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.

Files changed (108) hide show
  1. imas/__init__.py +34 -0
  2. imas/__main__.py +17 -0
  3. imas/_util.py +260 -0
  4. imas/_version.py +16 -0
  5. imas/assets/IDSDef2MDSpreTree.xsl +161 -0
  6. imas/assets/IDS_fake_toplevel.xml +318 -0
  7. imas/assets/IDS_minimal.xml +37 -0
  8. imas/assets/IDS_minimal_2.xml +37 -0
  9. imas/assets/IDS_minimal_struct_array.xml +43 -0
  10. imas/assets/IDS_minimal_types.xml +169 -0
  11. imas/assets/ITER_134173_106_core_profiles.ids +2438 -0
  12. imas/assets/ITER_134173_106_equilibrium.ids +3172 -0
  13. imas/assets/README.md +5 -0
  14. imas/assets/core_profiles.ids +2438 -0
  15. imas/assets/equilibrium.ids +3172 -0
  16. imas/backends/__init__.py +12 -0
  17. imas/backends/db_entry_impl.py +122 -0
  18. imas/backends/imas_core/__init__.py +4 -0
  19. imas/backends/imas_core/al_context.py +391 -0
  20. imas/backends/imas_core/db_entry_al.py +405 -0
  21. imas/backends/imas_core/db_entry_helpers.py +200 -0
  22. imas/backends/imas_core/imas_interface.py +216 -0
  23. imas/backends/imas_core/mdsplus_model.py +367 -0
  24. imas/backends/imas_core/uda_support.py +56 -0
  25. imas/backends/netcdf/__init__.py +4 -0
  26. imas/backends/netcdf/db_entry_nc.py +176 -0
  27. imas/backends/netcdf/ids2nc.py +306 -0
  28. imas/backends/netcdf/nc2ids.py +302 -0
  29. imas/backends/netcdf/nc_metadata.py +357 -0
  30. imas/backends/netcdf/nc_validate.py +53 -0
  31. imas/command/cli.py +239 -0
  32. imas/command/db_analysis.py +290 -0
  33. imas/command/helpers.py +45 -0
  34. imas/command/timer.py +77 -0
  35. imas/db_entry.py +802 -0
  36. imas/dd_helpers.py +300 -0
  37. imas/dd_zip.py +297 -0
  38. imas/exception.py +107 -0
  39. imas/ids_base.py +149 -0
  40. imas/ids_convert.py +920 -0
  41. imas/ids_coordinates.py +400 -0
  42. imas/ids_data_type.py +115 -0
  43. imas/ids_defs.py +171 -0
  44. imas/ids_factory.py +108 -0
  45. imas/ids_identifiers.py +125 -0
  46. imas/ids_metadata.py +307 -0
  47. imas/ids_path.py +254 -0
  48. imas/ids_primitive.py +510 -0
  49. imas/ids_struct_array.py +233 -0
  50. imas/ids_structure.py +331 -0
  51. imas/ids_toplevel.py +381 -0
  52. imas/setup_logging.py +64 -0
  53. imas/test/test_all_dd_versions.py +28 -0
  54. imas/test/test_cli.py +44 -0
  55. imas/test/test_dbentry.py +104 -0
  56. imas/test/test_dd_helpers.py +134 -0
  57. imas/test/test_dd_zip.py +29 -0
  58. imas/test/test_deepcopy.py +25 -0
  59. imas/test/test_exception.py +12 -0
  60. imas/test/test_get_sample.py +388 -0
  61. imas/test/test_hash.py +188 -0
  62. imas/test/test_helpers.py +354 -0
  63. imas/test/test_identifiers.py +105 -0
  64. imas/test/test_ids2nc.py +166 -0
  65. imas/test/test_ids_ascii_data.py +39 -0
  66. imas/test/test_ids_convert.py +415 -0
  67. imas/test/test_ids_coordinates.py +165 -0
  68. imas/test/test_ids_data_type.py +40 -0
  69. imas/test/test_ids_factory.py +35 -0
  70. imas/test/test_ids_metadata.py +66 -0
  71. imas/test/test_ids_mixin.py +20 -0
  72. imas/test/test_ids_path.py +192 -0
  73. imas/test/test_ids_primitive.py +109 -0
  74. imas/test/test_ids_struct_array.py +89 -0
  75. imas/test/test_ids_structure.py +70 -0
  76. imas/test/test_ids_toplevel.py +55 -0
  77. imas/test/test_ids_validate.py +343 -0
  78. imas/test/test_latest_dd_autofill.py +74 -0
  79. imas/test/test_latest_dd_manual.py +38 -0
  80. imas/test/test_latest_dd_resample.py +102 -0
  81. imas/test/test_lazy_loading.py +201 -0
  82. imas/test/test_list_occurrences.py +82 -0
  83. imas/test/test_minimal.py +37 -0
  84. imas/test/test_minimal_conversion.py +44 -0
  85. imas/test/test_minimal_io.py +21 -0
  86. imas/test/test_minimal_struct_array_io.py +60 -0
  87. imas/test/test_minimal_types.py +436 -0
  88. imas/test/test_minimal_types_io.py +70 -0
  89. imas/test/test_minimal_types_io_automatic.py +66 -0
  90. imas/test/test_nbc_change.py +408 -0
  91. imas/test/test_nc_autofill.py +13 -0
  92. imas/test/test_nc_entry.py +28 -0
  93. imas/test/test_nc_metadata.py +153 -0
  94. imas/test/test_nc_validation.py +197 -0
  95. imas/test/test_snippets.py +25 -0
  96. imas/test/test_static_ids.py +39 -0
  97. imas/test/test_str_1d.py +99 -0
  98. imas/test/test_time_slicing.py +182 -0
  99. imas/test/test_uda_support.py +12 -0
  100. imas/test/test_util.py +209 -0
  101. imas/training.py +45 -0
  102. imas/util.py +526 -0
  103. imas_python-0.1.dev1618.dist-info/LICENSE.txt +165 -0
  104. imas_python-0.1.dev1618.dist-info/METADATA +288 -0
  105. imas_python-0.1.dev1618.dist-info/RECORD +108 -0
  106. imas_python-0.1.dev1618.dist-info/WHEEL +5 -0
  107. imas_python-0.1.dev1618.dist-info/entry_points.txt +3 -0
  108. 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
@@ -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