imas-python 0.1.dev1618__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of imas-python might be problematic. Click here for more details.
- imas/__init__.py +34 -0
- imas/__main__.py +17 -0
- imas/_util.py +260 -0
- imas/_version.py +16 -0
- imas/assets/IDSDef2MDSpreTree.xsl +161 -0
- imas/assets/IDS_fake_toplevel.xml +318 -0
- imas/assets/IDS_minimal.xml +37 -0
- imas/assets/IDS_minimal_2.xml +37 -0
- imas/assets/IDS_minimal_struct_array.xml +43 -0
- imas/assets/IDS_minimal_types.xml +169 -0
- imas/assets/ITER_134173_106_core_profiles.ids +2438 -0
- imas/assets/ITER_134173_106_equilibrium.ids +3172 -0
- imas/assets/README.md +5 -0
- imas/assets/core_profiles.ids +2438 -0
- imas/assets/equilibrium.ids +3172 -0
- imas/backends/__init__.py +12 -0
- imas/backends/db_entry_impl.py +122 -0
- imas/backends/imas_core/__init__.py +4 -0
- imas/backends/imas_core/al_context.py +391 -0
- imas/backends/imas_core/db_entry_al.py +405 -0
- imas/backends/imas_core/db_entry_helpers.py +200 -0
- imas/backends/imas_core/imas_interface.py +216 -0
- imas/backends/imas_core/mdsplus_model.py +367 -0
- imas/backends/imas_core/uda_support.py +56 -0
- imas/backends/netcdf/__init__.py +4 -0
- imas/backends/netcdf/db_entry_nc.py +176 -0
- imas/backends/netcdf/ids2nc.py +306 -0
- imas/backends/netcdf/nc2ids.py +302 -0
- imas/backends/netcdf/nc_metadata.py +357 -0
- imas/backends/netcdf/nc_validate.py +53 -0
- imas/command/cli.py +239 -0
- imas/command/db_analysis.py +290 -0
- imas/command/helpers.py +45 -0
- imas/command/timer.py +77 -0
- imas/db_entry.py +802 -0
- imas/dd_helpers.py +300 -0
- imas/dd_zip.py +297 -0
- imas/exception.py +107 -0
- imas/ids_base.py +149 -0
- imas/ids_convert.py +920 -0
- imas/ids_coordinates.py +400 -0
- imas/ids_data_type.py +115 -0
- imas/ids_defs.py +171 -0
- imas/ids_factory.py +108 -0
- imas/ids_identifiers.py +125 -0
- imas/ids_metadata.py +307 -0
- imas/ids_path.py +254 -0
- imas/ids_primitive.py +510 -0
- imas/ids_struct_array.py +233 -0
- imas/ids_structure.py +331 -0
- imas/ids_toplevel.py +381 -0
- imas/setup_logging.py +64 -0
- imas/test/test_all_dd_versions.py +28 -0
- imas/test/test_cli.py +44 -0
- imas/test/test_dbentry.py +104 -0
- imas/test/test_dd_helpers.py +134 -0
- imas/test/test_dd_zip.py +29 -0
- imas/test/test_deepcopy.py +25 -0
- imas/test/test_exception.py +12 -0
- imas/test/test_get_sample.py +388 -0
- imas/test/test_hash.py +188 -0
- imas/test/test_helpers.py +354 -0
- imas/test/test_identifiers.py +105 -0
- imas/test/test_ids2nc.py +166 -0
- imas/test/test_ids_ascii_data.py +39 -0
- imas/test/test_ids_convert.py +415 -0
- imas/test/test_ids_coordinates.py +165 -0
- imas/test/test_ids_data_type.py +40 -0
- imas/test/test_ids_factory.py +35 -0
- imas/test/test_ids_metadata.py +66 -0
- imas/test/test_ids_mixin.py +20 -0
- imas/test/test_ids_path.py +192 -0
- imas/test/test_ids_primitive.py +109 -0
- imas/test/test_ids_struct_array.py +89 -0
- imas/test/test_ids_structure.py +70 -0
- imas/test/test_ids_toplevel.py +55 -0
- imas/test/test_ids_validate.py +343 -0
- imas/test/test_latest_dd_autofill.py +74 -0
- imas/test/test_latest_dd_manual.py +38 -0
- imas/test/test_latest_dd_resample.py +102 -0
- imas/test/test_lazy_loading.py +201 -0
- imas/test/test_list_occurrences.py +82 -0
- imas/test/test_minimal.py +37 -0
- imas/test/test_minimal_conversion.py +44 -0
- imas/test/test_minimal_io.py +21 -0
- imas/test/test_minimal_struct_array_io.py +60 -0
- imas/test/test_minimal_types.py +436 -0
- imas/test/test_minimal_types_io.py +70 -0
- imas/test/test_minimal_types_io_automatic.py +66 -0
- imas/test/test_nbc_change.py +408 -0
- imas/test/test_nc_autofill.py +13 -0
- imas/test/test_nc_entry.py +28 -0
- imas/test/test_nc_metadata.py +153 -0
- imas/test/test_nc_validation.py +197 -0
- imas/test/test_snippets.py +25 -0
- imas/test/test_static_ids.py +39 -0
- imas/test/test_str_1d.py +99 -0
- imas/test/test_time_slicing.py +182 -0
- imas/test/test_uda_support.py +12 -0
- imas/test/test_util.py +209 -0
- imas/training.py +45 -0
- imas/util.py +526 -0
- imas_python-0.1.dev1618.dist-info/LICENSE.txt +165 -0
- imas_python-0.1.dev1618.dist-info/METADATA +288 -0
- imas_python-0.1.dev1618.dist-info/RECORD +108 -0
- imas_python-0.1.dev1618.dist-info/WHEEL +5 -0
- imas_python-0.1.dev1618.dist-info/entry_points.txt +3 -0
- imas_python-0.1.dev1618.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import netCDF4
|
|
2
|
+
import numpy as np
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
from imas.backends.netcdf.ids2nc import IDS2NC
|
|
6
|
+
from imas.backends.netcdf.nc2ids import NC2IDS
|
|
7
|
+
from imas.backends.netcdf.nc_validate import validate_netcdf_file
|
|
8
|
+
from imas.exception import InvalidNetCDFEntry, UnknownDDVersion
|
|
9
|
+
from imas.ids_defs import IDS_TIME_MODE_HOMOGENEOUS
|
|
10
|
+
from imas.ids_factory import IDSFactory
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@pytest.fixture()
|
|
14
|
+
def memfile():
|
|
15
|
+
with netCDF4.Dataset("-", "w", diskless=True) as memfile:
|
|
16
|
+
yield memfile
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@pytest.fixture()
|
|
20
|
+
def factory():
|
|
21
|
+
return IDSFactory("4.0.0")
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@pytest.fixture()
|
|
25
|
+
def memfile_with_ids(memfile, factory):
|
|
26
|
+
ids = factory.core_profiles()
|
|
27
|
+
ids.ids_properties.homogeneous_time = IDS_TIME_MODE_HOMOGENEOUS
|
|
28
|
+
ids.time = [1.0, 2.0, 3.0]
|
|
29
|
+
ids.profiles_1d.resize(3)
|
|
30
|
+
for i in range(3):
|
|
31
|
+
ids.profiles_1d[i].grid.rho_tor_norm = [0.0, 0.2, 0.4, 0.6, 0.8, 1.0]
|
|
32
|
+
ids.profiles_1d[0].zeff = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0]
|
|
33
|
+
IDS2NC(ids, memfile).run()
|
|
34
|
+
# This one is valid:
|
|
35
|
+
NC2IDS(memfile, factory.core_profiles()).run()
|
|
36
|
+
return memfile
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def test_invalid_homogeneous_time(memfile, factory):
|
|
40
|
+
empty_group = memfile.createGroup("empty_group")
|
|
41
|
+
# Invalid dtype
|
|
42
|
+
invalid_dtype = memfile.createGroup("invalid_dtype")
|
|
43
|
+
invalid_dtype.createVariable("ids_properties.homogeneous_time", float, ())[()] = 0
|
|
44
|
+
# Invalid shape: 1D instead of 0D
|
|
45
|
+
invalid_shape = memfile.createGroup("invalid_shape")
|
|
46
|
+
invalid_shape.createDimension("dim")
|
|
47
|
+
invalid_shape.createVariable("ids_properties.homogeneous_time", "i4", ("dim",))
|
|
48
|
+
# Invalid value: not 0, 1 or 2
|
|
49
|
+
invalid_value = memfile.createGroup("invalid_value")
|
|
50
|
+
invalid_value.createVariable("ids_properties.homogeneous_time", "i4", ())
|
|
51
|
+
|
|
52
|
+
ids = factory.core_profiles()
|
|
53
|
+
with pytest.raises(InvalidNetCDFEntry):
|
|
54
|
+
NC2IDS(empty_group, ids) # ids_properties.homogeneous_time does not exist
|
|
55
|
+
with pytest.raises(InvalidNetCDFEntry):
|
|
56
|
+
NC2IDS(invalid_dtype, ids)
|
|
57
|
+
with pytest.raises(InvalidNetCDFEntry):
|
|
58
|
+
NC2IDS(invalid_shape, ids)
|
|
59
|
+
with pytest.raises(InvalidNetCDFEntry):
|
|
60
|
+
NC2IDS(invalid_value, ids)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def test_invalid_units(memfile_with_ids, factory):
|
|
64
|
+
memfile_with_ids["time"].units = "hours"
|
|
65
|
+
with pytest.raises(InvalidNetCDFEntry):
|
|
66
|
+
NC2IDS(memfile_with_ids, factory.core_profiles()).run()
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def test_invalid_documentation(memfile_with_ids, factory, caplog):
|
|
70
|
+
with caplog.at_level("WARNING"):
|
|
71
|
+
NC2IDS(memfile_with_ids, factory.core_profiles()).run()
|
|
72
|
+
assert not caplog.records
|
|
73
|
+
# Invalid docstring logs a warning
|
|
74
|
+
memfile_with_ids["time"].documentation = "https://en.wikipedia.org/wiki/Time"
|
|
75
|
+
with caplog.at_level("WARNING"):
|
|
76
|
+
NC2IDS(memfile_with_ids, factory.core_profiles()).run()
|
|
77
|
+
assert len(caplog.records) == 1
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def test_invalid_dimension_name(memfile_with_ids, factory):
|
|
81
|
+
memfile_with_ids.renameDimension("time", "T")
|
|
82
|
+
with pytest.raises(InvalidNetCDFEntry):
|
|
83
|
+
NC2IDS(memfile_with_ids, factory.core_profiles()).run()
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def test_invalid_coordinates(memfile_with_ids, factory):
|
|
87
|
+
memfile_with_ids["profiles_1d.grid.rho_tor_norm"].coordinates = "xyz"
|
|
88
|
+
with pytest.raises(InvalidNetCDFEntry):
|
|
89
|
+
NC2IDS(memfile_with_ids, factory.core_profiles()).run()
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def test_invalid_ancillary_variables(memfile_with_ids, factory):
|
|
93
|
+
memfile_with_ids["time"].ancillary_variables = "xyz"
|
|
94
|
+
with pytest.raises(InvalidNetCDFEntry):
|
|
95
|
+
NC2IDS(memfile_with_ids, factory.core_profiles()).run()
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def test_extra_attributes(memfile_with_ids, factory):
|
|
99
|
+
memfile_with_ids["time"].new_attribute = [1, 2, 3]
|
|
100
|
+
with pytest.raises(InvalidNetCDFEntry):
|
|
101
|
+
NC2IDS(memfile_with_ids, factory.core_profiles()).run()
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def test_shape_array_without_data(memfile_with_ids, factory):
|
|
105
|
+
memfile_with_ids.createVariable("profiles_1d.t_i_average:shape", int, ())
|
|
106
|
+
with pytest.raises(InvalidNetCDFEntry):
|
|
107
|
+
NC2IDS(memfile_with_ids, factory.core_profiles()).run()
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def test_shape_array_without_sparse_data(memfile_with_ids, factory):
|
|
111
|
+
memfile_with_ids.createVariable("profiles_1d.grid.rho_tor_norm:shape", int, ())
|
|
112
|
+
with pytest.raises(InvalidNetCDFEntry):
|
|
113
|
+
NC2IDS(memfile_with_ids, factory.core_profiles()).run()
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def test_shape_array_with_invalid_dimensions(memfile_with_ids, factory):
|
|
117
|
+
cp = factory.core_profiles()
|
|
118
|
+
t_i_average_meta = cp.metadata["profiles_1d.t_i_average"]
|
|
119
|
+
t_i_average = memfile_with_ids.createVariable(
|
|
120
|
+
"profiles_1d.t_i_average", float, ("time", "profiles_1d.grid.rho_tor_norm:i")
|
|
121
|
+
)
|
|
122
|
+
t_i_average.units = t_i_average_meta.units
|
|
123
|
+
t_i_average.documentation = t_i_average_meta.documentation
|
|
124
|
+
t_i_average.sparse = "Contents don't matter"
|
|
125
|
+
memfile_with_ids.createVariable(
|
|
126
|
+
"profiles_1d.t_i_average:shape",
|
|
127
|
+
np.int32,
|
|
128
|
+
("time", "profiles_1d.grid.rho_tor_norm:i"),
|
|
129
|
+
)
|
|
130
|
+
with pytest.raises(InvalidNetCDFEntry):
|
|
131
|
+
NC2IDS(memfile_with_ids, cp).run()
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def test_shape_array_with_invalid_dtype(memfile_with_ids, factory):
|
|
135
|
+
cp = factory.core_profiles()
|
|
136
|
+
t_i_average_meta = cp.metadata["profiles_1d.t_i_average"]
|
|
137
|
+
t_i_average = memfile_with_ids.createVariable(
|
|
138
|
+
"profiles_1d.t_i_average", float, ("time", "profiles_1d.grid.rho_tor_norm:i")
|
|
139
|
+
)
|
|
140
|
+
t_i_average.units = t_i_average_meta.units
|
|
141
|
+
t_i_average.documentation = t_i_average_meta.documentation
|
|
142
|
+
t_i_average.sparse = "Contents don't matter"
|
|
143
|
+
memfile_with_ids.createVariable(
|
|
144
|
+
"profiles_1d.t_i_average:shape", float, ("time", "1D")
|
|
145
|
+
)
|
|
146
|
+
with pytest.raises(InvalidNetCDFEntry):
|
|
147
|
+
NC2IDS(memfile_with_ids, cp).run()
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def test_validate_nc(tmpdir):
|
|
151
|
+
fname = str(tmpdir / "test.nc")
|
|
152
|
+
|
|
153
|
+
# Wrong extension
|
|
154
|
+
with pytest.raises(InvalidNetCDFEntry):
|
|
155
|
+
validate_netcdf_file("test.h5") # invalid extension
|
|
156
|
+
|
|
157
|
+
# Empty file
|
|
158
|
+
netCDF4.Dataset(fname, "w").close()
|
|
159
|
+
with pytest.raises(InvalidNetCDFEntry):
|
|
160
|
+
validate_netcdf_file(fname)
|
|
161
|
+
|
|
162
|
+
# Invalid DD version
|
|
163
|
+
with netCDF4.Dataset(fname, "w") as dataset:
|
|
164
|
+
dataset.data_dictionary_version = "invalid"
|
|
165
|
+
dataset.createGroup("core_profiles")
|
|
166
|
+
with pytest.raises(UnknownDDVersion):
|
|
167
|
+
validate_netcdf_file(fname)
|
|
168
|
+
|
|
169
|
+
# Invalid group
|
|
170
|
+
with netCDF4.Dataset(fname, "w") as dataset:
|
|
171
|
+
dataset.data_dictionary_version = "4.0.0"
|
|
172
|
+
dataset.createGroup("X")
|
|
173
|
+
with pytest.raises(InvalidNetCDFEntry):
|
|
174
|
+
validate_netcdf_file(fname)
|
|
175
|
+
|
|
176
|
+
# Invalid occurrence
|
|
177
|
+
with netCDF4.Dataset(fname, "w") as dataset:
|
|
178
|
+
dataset.data_dictionary_version = "4.0.0"
|
|
179
|
+
dataset.createGroup("core_profiles/a")
|
|
180
|
+
with pytest.raises(InvalidNetCDFEntry):
|
|
181
|
+
validate_netcdf_file(fname)
|
|
182
|
+
|
|
183
|
+
# Invalid variable in root group
|
|
184
|
+
with netCDF4.Dataset(fname, "w") as dataset:
|
|
185
|
+
dataset.data_dictionary_version = "4.0.0"
|
|
186
|
+
dataset.createVariable("core_profiles", int, ())
|
|
187
|
+
with pytest.raises(InvalidNetCDFEntry):
|
|
188
|
+
validate_netcdf_file(fname)
|
|
189
|
+
|
|
190
|
+
# Missing ids_properties.homogeneous_time
|
|
191
|
+
with netCDF4.Dataset(fname, "w") as dataset:
|
|
192
|
+
dataset.data_dictionary_version = "4.0.0"
|
|
193
|
+
dataset.createGroup("core_profiles/1")
|
|
194
|
+
with pytest.raises(InvalidNetCDFEntry):
|
|
195
|
+
validate_netcdf_file(fname)
|
|
196
|
+
|
|
197
|
+
# All other validations are handled by NC2IDS and tested above
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import runpy
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
courses = Path(__file__, "../../../", "docs/source/courses").resolve()
|
|
8
|
+
course_snippets = []
|
|
9
|
+
for course in ["basic", "advanced"]:
|
|
10
|
+
course_snippets.extend((courses / course).glob("*snippets/*.py"))
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@pytest.mark.skip(reason="skipping hli test")
|
|
14
|
+
@pytest.mark.filterwarnings("ignore:The input coordinates to pcolormesh:UserWarning")
|
|
15
|
+
@pytest.mark.parametrize("snippet", course_snippets)
|
|
16
|
+
def test_script_execution(snippet, monkeypatch, tmp_path, requires_imas):
|
|
17
|
+
monkeypatch.chdir(tmp_path)
|
|
18
|
+
# Prevent showing plots in a GUI
|
|
19
|
+
monkeypatch.delenv("DISPLAY", raising=False)
|
|
20
|
+
if "IMAS_HOME" not in os.environ:
|
|
21
|
+
# Only execute those snippets that don't need access to the public IMAS DB
|
|
22
|
+
script_text = snippet.read_text()
|
|
23
|
+
if '"public"' in script_text: # ugly hack :(
|
|
24
|
+
pytest.skip("Snippet requires the public IMAS DB, which is not available")
|
|
25
|
+
runpy.run_path(str(snippet))
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Testing static IDS behavior as defined in IMAS-3330
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
import imas
|
|
8
|
+
from imas.ids_defs import IDS_TIME_MODE_HETEROGENEOUS, IDS_TIME_MODE_INDEPENDENT
|
|
9
|
+
from imas.ids_metadata import IDSType
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def test_ids_valid_type():
|
|
13
|
+
factory = imas.IDSFactory()
|
|
14
|
+
ids_types = set()
|
|
15
|
+
for ids_name in factory:
|
|
16
|
+
ids = factory.new(ids_name)
|
|
17
|
+
ids_types.add(ids.metadata.type)
|
|
18
|
+
|
|
19
|
+
# For DD versions <4, `type` is never set at IDS top levels.
|
|
20
|
+
# For DD versions >=4, `type` may be CONSTANT (i.e. no root time node) or DYNAMIC
|
|
21
|
+
assert ids_types in ({IDSType.NONE}, {IDSType.CONSTANT, IDSType.DYNAMIC})
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def test_constant_ids(caplog):
|
|
25
|
+
ids = imas.IDSFactory().new("amns_data")
|
|
26
|
+
if ids.metadata.type is IDSType.NONE:
|
|
27
|
+
pytest.skip("IDS definition has no constant IDSs")
|
|
28
|
+
|
|
29
|
+
ids.ids_properties.homogeneous_time = IDS_TIME_MODE_HETEROGENEOUS
|
|
30
|
+
dbe = imas.DBEntry(imas.ids_defs.MEMORY_BACKEND, "test", 0, 0)
|
|
31
|
+
dbe.create()
|
|
32
|
+
|
|
33
|
+
caplog.clear()
|
|
34
|
+
with caplog.at_level(logging.WARNING, logger="imas"):
|
|
35
|
+
dbe.put(ids)
|
|
36
|
+
assert ids.ids_properties.homogeneous_time == IDS_TIME_MODE_INDEPENDENT
|
|
37
|
+
assert len(caplog.records) == 1
|
|
38
|
+
msg = caplog.records[0].message
|
|
39
|
+
assert "ids_properties/homogeneous_time has been set to 2" in msg
|
imas/test/test_str_1d.py
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# A minimal testcase loading an IDS file and checking that the structure built is ok
|
|
2
|
+
import string
|
|
3
|
+
|
|
4
|
+
from imas.ids_defs import IDS_TIME_MODE_INDEPENDENT, MEMORY_BACKEND
|
|
5
|
+
from imas.test.test_helpers import open_dbentry
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def test_str_1d_empty(backend, ids_minimal_types, worker_id, tmp_path):
|
|
9
|
+
"""Write and then read again a string on our minimal IDS."""
|
|
10
|
+
dbentry = open_dbentry(
|
|
11
|
+
backend, "w", worker_id, tmp_path, xml_path=ids_minimal_types
|
|
12
|
+
)
|
|
13
|
+
minimal = dbentry.factory.new("minimal")
|
|
14
|
+
minimal.str_1d = []
|
|
15
|
+
|
|
16
|
+
minimal.ids_properties.homogeneous_time = IDS_TIME_MODE_INDEPENDENT
|
|
17
|
+
dbentry.put(minimal)
|
|
18
|
+
|
|
19
|
+
dbentry2 = open_dbentry(
|
|
20
|
+
backend, "a", worker_id, tmp_path, xml_path=ids_minimal_types
|
|
21
|
+
)
|
|
22
|
+
minimal2 = dbentry2.get("minimal")
|
|
23
|
+
assert list(minimal2.str_1d.value) == []
|
|
24
|
+
|
|
25
|
+
dbentry.close()
|
|
26
|
+
if backend != MEMORY_BACKEND: # MEM backend already cleaned up, prevent SEGFAULT
|
|
27
|
+
dbentry2.close()
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def test_str_1d_long_single(backend, ids_minimal_types, worker_id, tmp_path):
|
|
31
|
+
"""Write and then read again a string on our minimal IDS."""
|
|
32
|
+
dbentry = open_dbentry(
|
|
33
|
+
backend, "w", worker_id, tmp_path, xml_path=ids_minimal_types
|
|
34
|
+
)
|
|
35
|
+
minimal = dbentry.factory.new("minimal")
|
|
36
|
+
minimal.str_1d = [string.ascii_uppercase * 100]
|
|
37
|
+
|
|
38
|
+
minimal.ids_properties.homogeneous_time = IDS_TIME_MODE_INDEPENDENT
|
|
39
|
+
dbentry.put(minimal)
|
|
40
|
+
|
|
41
|
+
dbentry2 = open_dbentry(
|
|
42
|
+
backend, "a", worker_id, tmp_path, xml_path=ids_minimal_types
|
|
43
|
+
)
|
|
44
|
+
minimal2 = dbentry2.get("minimal")
|
|
45
|
+
assert minimal2.str_1d.value == [string.ascii_uppercase * 100]
|
|
46
|
+
|
|
47
|
+
dbentry.close()
|
|
48
|
+
if backend != MEMORY_BACKEND: # MEM backend already cleaned up, prevent SEGFAULT
|
|
49
|
+
dbentry2.close()
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def test_str_1d_multiple(backend, ids_minimal_types, worker_id, tmp_path):
|
|
53
|
+
"""Write and then read again a string on our minimal IDS."""
|
|
54
|
+
dbentry = open_dbentry(
|
|
55
|
+
backend, "w", worker_id, tmp_path, xml_path=ids_minimal_types
|
|
56
|
+
)
|
|
57
|
+
minimal = dbentry.factory.new("minimal")
|
|
58
|
+
minimal.str_1d = [string.ascii_uppercase, string.ascii_lowercase]
|
|
59
|
+
|
|
60
|
+
minimal.ids_properties.homogeneous_time = IDS_TIME_MODE_INDEPENDENT
|
|
61
|
+
dbentry.put(minimal)
|
|
62
|
+
|
|
63
|
+
dbentry2 = open_dbentry(
|
|
64
|
+
backend, "a", worker_id, tmp_path, xml_path=ids_minimal_types
|
|
65
|
+
)
|
|
66
|
+
minimal2 = dbentry2.get("minimal")
|
|
67
|
+
assert minimal2.str_1d.value == [
|
|
68
|
+
string.ascii_uppercase,
|
|
69
|
+
string.ascii_lowercase,
|
|
70
|
+
]
|
|
71
|
+
|
|
72
|
+
dbentry.close()
|
|
73
|
+
if backend != MEMORY_BACKEND: # MEM backend already cleaned up, prevent SEGFAULT
|
|
74
|
+
dbentry2.close()
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def test_str_1d_long_multiple(backend, ids_minimal_types, worker_id, tmp_path):
|
|
78
|
+
"""Write and then read again a string on our minimal IDS."""
|
|
79
|
+
dbentry = open_dbentry(
|
|
80
|
+
backend, "w", worker_id, tmp_path, xml_path=ids_minimal_types
|
|
81
|
+
)
|
|
82
|
+
minimal = dbentry.factory.new("minimal")
|
|
83
|
+
minimal.str_1d = [string.ascii_uppercase * 100, string.ascii_lowercase * 100]
|
|
84
|
+
|
|
85
|
+
minimal.ids_properties.homogeneous_time = IDS_TIME_MODE_INDEPENDENT
|
|
86
|
+
dbentry.put(minimal)
|
|
87
|
+
|
|
88
|
+
dbentry2 = open_dbentry(
|
|
89
|
+
backend, "a", worker_id, tmp_path, xml_path=ids_minimal_types
|
|
90
|
+
)
|
|
91
|
+
minimal2 = dbentry2.get("minimal")
|
|
92
|
+
assert minimal2.str_1d.value == [
|
|
93
|
+
string.ascii_uppercase * 100,
|
|
94
|
+
string.ascii_lowercase * 100,
|
|
95
|
+
]
|
|
96
|
+
|
|
97
|
+
dbentry.close()
|
|
98
|
+
if backend != MEMORY_BACKEND: # MEM backend already cleaned up, prevent SEGFAULT
|
|
99
|
+
dbentry2.close()
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
"""A testcase checking if writing and then reading works for the latest full
|
|
2
|
+
data dictionary version.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
import os
|
|
7
|
+
|
|
8
|
+
import numpy as np
|
|
9
|
+
import pytest
|
|
10
|
+
|
|
11
|
+
from imas.backends.imas_core.mdsplus_model import ensure_data_dir, mdsplus_model_dir
|
|
12
|
+
from imas.ids_defs import (
|
|
13
|
+
ASCII_BACKEND,
|
|
14
|
+
CLOSEST_INTERP,
|
|
15
|
+
IDS_TIME_MODE_HETEROGENEOUS,
|
|
16
|
+
IDS_TIME_MODE_HOMOGENEOUS,
|
|
17
|
+
MDSPLUS_BACKEND,
|
|
18
|
+
)
|
|
19
|
+
from imas.ids_factory import IDSFactory
|
|
20
|
+
from imas.test.test_helpers import open_dbentry
|
|
21
|
+
|
|
22
|
+
# import IMAS HLI, skip module when this is an install without IMAS
|
|
23
|
+
imas = pytest.importorskip("imas")
|
|
24
|
+
|
|
25
|
+
logger = logging.getLogger(__name__)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@pytest.fixture(params=(IDS_TIME_MODE_HOMOGENEOUS, IDS_TIME_MODE_HETEROGENEOUS))
|
|
29
|
+
def time_mode(request):
|
|
30
|
+
return request.param
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def test_write_read_time(backend, worker_id, tmp_path, time_mode):
|
|
34
|
+
"""Write some data to an IDS and then check that all slices match."""
|
|
35
|
+
dbentry = open_dbentry(backend, "w", worker_id, tmp_path)
|
|
36
|
+
eq = IDSFactory().new("equilibrium")
|
|
37
|
+
eq.ids_properties.homogeneous_time = time_mode
|
|
38
|
+
|
|
39
|
+
eq.time = np.array([0.0, 0.1, 0.2])
|
|
40
|
+
dbentry.put(eq)
|
|
41
|
+
|
|
42
|
+
eq = dbentry.get("equilibrium")
|
|
43
|
+
assert np.array_equal(eq.time, [0.0, 0.1, 0.2])
|
|
44
|
+
dbentry.close()
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def test_time_slicing_get(backend, worker_id, tmp_path, time_mode):
|
|
48
|
+
"""Write some data to an IDS and then check that all slices match."""
|
|
49
|
+
if backend == ASCII_BACKEND:
|
|
50
|
+
pytest.skip("ASCII backend does not support slice mode")
|
|
51
|
+
dbentry = open_dbentry(backend, "w", worker_id, tmp_path)
|
|
52
|
+
eq = IDSFactory().new("equilibrium")
|
|
53
|
+
eq.ids_properties.homogeneous_time = time_mode
|
|
54
|
+
|
|
55
|
+
# eq.time is the time coordinate for b0 in heterogeneous mode as well
|
|
56
|
+
eq.time = np.array([0.0, 0.1, 0.2])
|
|
57
|
+
eq.vacuum_toroidal_field.b0 = np.array([3.0, 4.0, 5.0])
|
|
58
|
+
dbentry.put(eq)
|
|
59
|
+
|
|
60
|
+
for time in range(3):
|
|
61
|
+
eq = dbentry.get_slice("equilibrium", time * 0.1, CLOSEST_INTERP)
|
|
62
|
+
assert eq.vacuum_toroidal_field.b0.value == time + 3.0
|
|
63
|
+
dbentry.close()
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def test_time_slicing_put(backend, worker_id, tmp_path, request, time_mode):
|
|
67
|
+
"""Write some slices to an IDS and then check that they are all there"""
|
|
68
|
+
if backend == ASCII_BACKEND:
|
|
69
|
+
pytest.skip("ASCII backend does not support slice mode")
|
|
70
|
+
dbentry = open_dbentry(backend, "w", worker_id, tmp_path)
|
|
71
|
+
eq = IDSFactory().new("equilibrium")
|
|
72
|
+
eq.ids_properties.homogeneous_time = time_mode
|
|
73
|
+
|
|
74
|
+
for time in range(3):
|
|
75
|
+
# eq.time is the time coordinate for b0 in heterogeneous mode as well
|
|
76
|
+
eq.time = [time * 0.1]
|
|
77
|
+
eq.vacuum_toroidal_field.b0 = [time + 3.0]
|
|
78
|
+
dbentry.put_slice(eq)
|
|
79
|
+
|
|
80
|
+
eq = dbentry.get("equilibrium")
|
|
81
|
+
|
|
82
|
+
assert np.allclose(eq.vacuum_toroidal_field.b0.value, [3.0, 4.0, 5.0])
|
|
83
|
+
assert np.allclose(eq.time.value, [0.0, 0.1, 0.2])
|
|
84
|
+
dbentry.close()
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
@pytest.mark.skip(reason="skipping hli test")
|
|
88
|
+
def test_hli_time_slicing_put(backend, worker_id, tmp_path, time_mode):
|
|
89
|
+
"""Write some slices to an IDS and then check that they are all there"""
|
|
90
|
+
if backend == ASCII_BACKEND:
|
|
91
|
+
pytest.skip("ASCII backend does not support slice mode")
|
|
92
|
+
|
|
93
|
+
ids = imas.equilibrium()
|
|
94
|
+
|
|
95
|
+
if worker_id == "master":
|
|
96
|
+
pulse = 1
|
|
97
|
+
else:
|
|
98
|
+
pulse = int(worker_id[2:]) + 1
|
|
99
|
+
|
|
100
|
+
# ensure presence of mdsplus model dir
|
|
101
|
+
if backend == MDSPLUS_BACKEND:
|
|
102
|
+
os.environ["ids_path"] = mdsplus_model_dir(IDSFactory())
|
|
103
|
+
ensure_data_dir(str(tmp_path), "test", "3", 9999)
|
|
104
|
+
db_entry = imas.DBEntry(backend, "test", pulse, 9999, user_name=str(tmp_path))
|
|
105
|
+
status, ctx = db_entry.create()
|
|
106
|
+
if status != 0:
|
|
107
|
+
logger.error("Error opening db entry %s", status)
|
|
108
|
+
|
|
109
|
+
ids.ids_properties.homogeneous_time = time_mode
|
|
110
|
+
|
|
111
|
+
for time in range(3):
|
|
112
|
+
ids.vacuum_toroidal_field.b0 = np.asarray([time + 3.0])
|
|
113
|
+
# eq.time is the time coordinate for b0 in heterogeneous mode as well
|
|
114
|
+
ids.time = np.asarray([time * 0.1])
|
|
115
|
+
ids.putSlice(0, db_entry)
|
|
116
|
+
|
|
117
|
+
ids.get(0, db_entry)
|
|
118
|
+
|
|
119
|
+
db_entry.close()
|
|
120
|
+
|
|
121
|
+
assert np.allclose(ids.vacuum_toroidal_field.b0, [3.0, 4.0, 5.0])
|
|
122
|
+
assert np.allclose(ids.time, [0.0, 0.1, 0.2])
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def test_time_slicing_put_two(backend, worker_id, tmp_path, time_mode):
|
|
126
|
+
"""Write some slices to an IDS and then check that they are all there"""
|
|
127
|
+
if backend == ASCII_BACKEND:
|
|
128
|
+
pytest.skip("ASCII backend does not support slice mode")
|
|
129
|
+
dbentry = open_dbentry(backend, "w", worker_id, tmp_path)
|
|
130
|
+
eq = IDSFactory().new("equilibrium")
|
|
131
|
+
eq.ids_properties.homogeneous_time = time_mode
|
|
132
|
+
|
|
133
|
+
for time in range(3):
|
|
134
|
+
eq.vacuum_toroidal_field.b0 = [time + 3.0, time + 3.5]
|
|
135
|
+
# eq.time is the time coordinate for b0 in heterogeneous mode as well
|
|
136
|
+
eq.time = [time * 0.1, (time + 0.5) * 0.1]
|
|
137
|
+
dbentry.put_slice(eq)
|
|
138
|
+
|
|
139
|
+
eq = dbentry.get("equilibrium")
|
|
140
|
+
|
|
141
|
+
assert np.array_equal(
|
|
142
|
+
eq.vacuum_toroidal_field.b0.value, [3.0, 3.5, 4.0, 4.5, 5.0, 5.5]
|
|
143
|
+
)
|
|
144
|
+
# Use allclose instead of array_equal since 0.15000000000000002 != 0.15
|
|
145
|
+
assert np.allclose(eq.time.value, [0.0, 0.05, 0.1, 0.15, 0.2, 0.25])
|
|
146
|
+
dbentry.close()
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def test_time_slicing_time_mode(backend, worker_id, tmp_path, time_mode):
|
|
150
|
+
"""Write data to controllers IDS to test heterogeneous time mode."""
|
|
151
|
+
if backend == ASCII_BACKEND:
|
|
152
|
+
pytest.skip("ASCII backend does not support slice mode")
|
|
153
|
+
dbentry = open_dbentry(backend, "w", worker_id, tmp_path)
|
|
154
|
+
ctrls = IDSFactory().new("controllers")
|
|
155
|
+
ctrls.ids_properties.homogeneous_time = time_mode
|
|
156
|
+
|
|
157
|
+
ctrls.time = [0.1, 0.2, 0.3]
|
|
158
|
+
ctrls.linear_controller.resize(2)
|
|
159
|
+
# This time base is ignored for IDS_TIME_MODE_HOMOGENEOUS
|
|
160
|
+
ctrls.linear_controller[0].inputs.time = [0.0, 0.1, 0.2]
|
|
161
|
+
ctrls.linear_controller[0].inputs.data = [[0.0, 1.0, 2.0]]
|
|
162
|
+
# This time base is ignored for IDS_TIME_MODE_HOMOGENEOUS
|
|
163
|
+
ctrls.linear_controller[1].inputs.time = [0.2, 0.3, 0.4]
|
|
164
|
+
ctrls.linear_controller[1].inputs.data = [[0.0, 1.0, 2.0]]
|
|
165
|
+
# code.output_flag always uses /time as timebase
|
|
166
|
+
ctrls.code.output_flag = [0, 1, 2]
|
|
167
|
+
|
|
168
|
+
dbentry.put(ctrls)
|
|
169
|
+
ctrls = dbentry.get_slice("controllers", 0.2, CLOSEST_INTERP)
|
|
170
|
+
assert np.array_equal(ctrls.time.value, [0.2])
|
|
171
|
+
if time_mode == IDS_TIME_MODE_HETEROGENEOUS:
|
|
172
|
+
assert np.array_equal(ctrls.linear_controller[0].inputs.time.value, [0.2])
|
|
173
|
+
assert np.array_equal(ctrls.linear_controller[0].inputs.data.value, [[2.0]])
|
|
174
|
+
assert np.array_equal(ctrls.linear_controller[1].inputs.time.value, [0.2])
|
|
175
|
+
assert np.array_equal(ctrls.linear_controller[1].inputs.data.value, [[0.0]])
|
|
176
|
+
else: # HOMOGENEOUS_TIME
|
|
177
|
+
# Note: don't test time arrays of linear_controller inputs, as they shouldn't be
|
|
178
|
+
# used in homogeneous time mode
|
|
179
|
+
assert np.array_equal(ctrls.linear_controller[0].inputs.data.value, [[1.0]])
|
|
180
|
+
assert np.array_equal(ctrls.linear_controller[1].inputs.data.value, [[1.0]])
|
|
181
|
+
assert np.array_equal(ctrls.code.output_flag.value, [1])
|
|
182
|
+
dbentry.close()
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from zlib import crc32
|
|
3
|
+
|
|
4
|
+
from imas import dd_zip
|
|
5
|
+
from imas.backends.imas_core.uda_support import extract_idsdef
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def test_extract_idsdef():
|
|
9
|
+
fname = extract_idsdef("4.0.0")
|
|
10
|
+
expected_crc = dd_zip.get_dd_xml_crc("4.0.0")
|
|
11
|
+
actual_crc = crc32(Path(fname).read_bytes())
|
|
12
|
+
assert expected_crc == actual_crc
|