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
imas/db_entry.py
ADDED
|
@@ -0,0 +1,802 @@
|
|
|
1
|
+
# This file is part of imas-python.
|
|
2
|
+
# You should have received the imas-python LICENSE file with this project.
|
|
3
|
+
"""Logic for interacting with IMAS Data Entries.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import logging
|
|
7
|
+
import os
|
|
8
|
+
from typing import Any, List, Optional, Tuple, Type, Union, overload
|
|
9
|
+
|
|
10
|
+
import numpy
|
|
11
|
+
|
|
12
|
+
import imas
|
|
13
|
+
from imas.backends.db_entry_impl import (
|
|
14
|
+
DBEntryImpl,
|
|
15
|
+
GetSampleParameters,
|
|
16
|
+
GetSliceParameters,
|
|
17
|
+
)
|
|
18
|
+
from imas.dd_zip import dd_xml_versions
|
|
19
|
+
from imas.exception import IDSNameError, UnknownDDVersion, ValidationError
|
|
20
|
+
from imas.ids_base import IDSBase
|
|
21
|
+
from imas.ids_convert import dd_version_map_from_factories
|
|
22
|
+
from imas.ids_defs import (
|
|
23
|
+
CREATE_PULSE,
|
|
24
|
+
FORCE_CREATE_PULSE,
|
|
25
|
+
FORCE_OPEN_PULSE,
|
|
26
|
+
IDS_TIME_MODE_INDEPENDENT,
|
|
27
|
+
IDS_TIME_MODES,
|
|
28
|
+
OPEN_PULSE,
|
|
29
|
+
)
|
|
30
|
+
from imas.ids_factory import IDSFactory
|
|
31
|
+
from imas.ids_metadata import IDSType
|
|
32
|
+
from imas.ids_toplevel import IDSToplevel
|
|
33
|
+
|
|
34
|
+
logger = logging.getLogger(__name__)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _get_uri_mode(uri, mode) -> Tuple[str, str]:
|
|
38
|
+
"""Helper method to parse arguments of DBEntry.__init__."""
|
|
39
|
+
return uri, mode
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _get_legacy_params(
|
|
43
|
+
backend_id, db_name, pulse, run, user_name=None, data_version=None
|
|
44
|
+
) -> Tuple[int, str, int, int, Optional[str], Optional[str]]:
|
|
45
|
+
"""Helper method to parse arguments of DBEntry.__init__."""
|
|
46
|
+
return backend_id, db_name, pulse, run, user_name, data_version
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class DBEntry:
|
|
50
|
+
"""Represents an IMAS database entry, which is a collection of stored IDSs.
|
|
51
|
+
|
|
52
|
+
A ``DBEntry`` can be used as a :external:ref:`context manager <context-managers>`:
|
|
53
|
+
|
|
54
|
+
.. code-block:: python
|
|
55
|
+
|
|
56
|
+
import imas
|
|
57
|
+
|
|
58
|
+
# AL4-style constructor:
|
|
59
|
+
with imas.DBEntry(imas.ids_defs.HDF5_BACKEND, "test", 1, 12) as dbentry:
|
|
60
|
+
# dbentry is now opened and can be used for reading data:
|
|
61
|
+
ids = dbentry.get(...)
|
|
62
|
+
# The dbentry is now closed
|
|
63
|
+
|
|
64
|
+
# AL5-style constructor also allows creating the Data Entry with the mode
|
|
65
|
+
# argument
|
|
66
|
+
with imas.DBEntry("imas:hdf5?path=testdb", "w") as dbentry:
|
|
67
|
+
# dbentry is now created and can be used for writing data:
|
|
68
|
+
dbentry.put(ids)
|
|
69
|
+
# The dbentry is now closed
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
@overload
|
|
73
|
+
def __init__(
|
|
74
|
+
self,
|
|
75
|
+
uri: str,
|
|
76
|
+
mode: str,
|
|
77
|
+
*,
|
|
78
|
+
dd_version: Optional[str] = None,
|
|
79
|
+
xml_path: Optional[str] = None,
|
|
80
|
+
) -> None: ...
|
|
81
|
+
|
|
82
|
+
@overload
|
|
83
|
+
def __init__(
|
|
84
|
+
self,
|
|
85
|
+
backend_id: int,
|
|
86
|
+
db_name: str,
|
|
87
|
+
pulse: int,
|
|
88
|
+
run: int,
|
|
89
|
+
user_name: Optional[str] = None,
|
|
90
|
+
data_version: Optional[str] = None,
|
|
91
|
+
*,
|
|
92
|
+
shot: Optional[int] = None,
|
|
93
|
+
dd_version: Optional[str] = None,
|
|
94
|
+
xml_path: Optional[str] = None,
|
|
95
|
+
) -> None: ...
|
|
96
|
+
|
|
97
|
+
def __init__(
|
|
98
|
+
self,
|
|
99
|
+
*args,
|
|
100
|
+
dd_version: Optional[str] = None,
|
|
101
|
+
xml_path: Optional[str] = None,
|
|
102
|
+
**kwargs,
|
|
103
|
+
):
|
|
104
|
+
"""Open or create a Data Entry based on the provided URI and mode, or prepare a
|
|
105
|
+
DBEntry using `legacy` parameters.
|
|
106
|
+
|
|
107
|
+
Note:
|
|
108
|
+
When using `legacy` parameters (:param:`backend_id`, :param:`db_name`,
|
|
109
|
+
:param:`pulse`, :param:`run`), the DBEntry is not opened.
|
|
110
|
+
You have to call :meth:`open` or :meth:`create` after creating the DBEntry
|
|
111
|
+
object before you can use it for reading or writing data.
|
|
112
|
+
|
|
113
|
+
Args:
|
|
114
|
+
uri: URI to the data entry, see explanation above.
|
|
115
|
+
mode: Mode to open the Data Entry in:
|
|
116
|
+
|
|
117
|
+
- ``"r"``: Open an existing data entry. Raises an error when the data
|
|
118
|
+
entry does not exist.
|
|
119
|
+
|
|
120
|
+
.. note:: The opened data entry is not read-only, it can be written to.
|
|
121
|
+
- ``"a"``: Open an existing data entry, create the data entry if it does
|
|
122
|
+
not exist.
|
|
123
|
+
- ``"w"``: Create a data entry, overwriting any existing.
|
|
124
|
+
|
|
125
|
+
.. caution:: This will irreversibly delete any existing data.
|
|
126
|
+
- ``"x"``: Create a data entry. Raises an error when a data entry already
|
|
127
|
+
exists.
|
|
128
|
+
|
|
129
|
+
backend_id: ID of the backend to use. See :ref:`Backend identifiers`.
|
|
130
|
+
db_name: Database name, e.g. "ITER".
|
|
131
|
+
pulse: Pulse number of the database entry.
|
|
132
|
+
run: Run number of the database entry.
|
|
133
|
+
user_name: User name of the database, retrieved from environment when not
|
|
134
|
+
supplied.
|
|
135
|
+
data_version: Major version of the DD used by the the access layer.
|
|
136
|
+
|
|
137
|
+
Keyword Args:
|
|
138
|
+
shot: Legacy alternative for :param:`pulse`.
|
|
139
|
+
dd_version: Use a specific Data Dictionary version instead of the default
|
|
140
|
+
one. See :ref:`multi-dd training`.
|
|
141
|
+
xml_path: Use a specific Data Dictionary build by pointing to the
|
|
142
|
+
IDSDef.xml. See :ref:`Using custom builds of the Data Dictionary`.
|
|
143
|
+
"""
|
|
144
|
+
try:
|
|
145
|
+
# Try to map *args and **kwargs to (uri, mode)
|
|
146
|
+
uri, mode = _get_uri_mode(*args, **kwargs)
|
|
147
|
+
legacy = False
|
|
148
|
+
|
|
149
|
+
except TypeError as exc1:
|
|
150
|
+
# map legacy `shot` to `pulse`
|
|
151
|
+
if "shot" in kwargs:
|
|
152
|
+
if "pulse" in kwargs:
|
|
153
|
+
raise ValueError("Cannot provide a value for both shot and pulse")
|
|
154
|
+
kwargs["pulse"] = kwargs.pop("shot")
|
|
155
|
+
# Try to map *args and **kwargs to legacy call pattern
|
|
156
|
+
try:
|
|
157
|
+
legacy_params = _get_legacy_params(*args, **kwargs)
|
|
158
|
+
legacy = True
|
|
159
|
+
except TypeError as exc2:
|
|
160
|
+
raise TypeError(
|
|
161
|
+
f"Incorrect arguments to {__class__.__name__}.__init__(): "
|
|
162
|
+
f"{exc1.args[0]}, {exc2.args[0]}"
|
|
163
|
+
) from None
|
|
164
|
+
|
|
165
|
+
# Actual intializiation
|
|
166
|
+
self._dbe_impl: Optional[DBEntryImpl] = None
|
|
167
|
+
self._dd_version = dd_version
|
|
168
|
+
self._xml_path = xml_path
|
|
169
|
+
self._ids_factory = IDSFactory(dd_version, xml_path)
|
|
170
|
+
|
|
171
|
+
if legacy:
|
|
172
|
+
# Unpack legacy params
|
|
173
|
+
(
|
|
174
|
+
self.backend_id,
|
|
175
|
+
self.db_name,
|
|
176
|
+
self.pulse,
|
|
177
|
+
self.run,
|
|
178
|
+
self.user_name,
|
|
179
|
+
self.data_version,
|
|
180
|
+
) = legacy_params
|
|
181
|
+
self.uri = None
|
|
182
|
+
self.mode = None
|
|
183
|
+
else:
|
|
184
|
+
self.uri = str(uri)
|
|
185
|
+
self.mode = mode
|
|
186
|
+
cls = self._select_implementation(self.uri)
|
|
187
|
+
self._dbe_impl = cls.from_uri(self.uri, mode, self._ids_factory)
|
|
188
|
+
|
|
189
|
+
@staticmethod
|
|
190
|
+
def _select_implementation(uri: Optional[str]) -> Type[DBEntryImpl]:
|
|
191
|
+
"""Select which DBEntry implementation to use based on the URI."""
|
|
192
|
+
if uri and uri.endswith(".nc") and not uri.startswith("imas:"):
|
|
193
|
+
from imas.backends.netcdf.db_entry_nc import NCDBEntryImpl as impl
|
|
194
|
+
else:
|
|
195
|
+
from imas.backends.imas_core.db_entry_al import ALDBEntryImpl as impl
|
|
196
|
+
return impl
|
|
197
|
+
|
|
198
|
+
def __enter__(self):
|
|
199
|
+
# Context manager protocol
|
|
200
|
+
if self._dbe_impl is None:
|
|
201
|
+
# Open if the DBEntry was not already opened or created
|
|
202
|
+
self.open()
|
|
203
|
+
return self
|
|
204
|
+
|
|
205
|
+
def __exit__(self, exc_type, exc_value, traceback):
|
|
206
|
+
# Context manager protocol
|
|
207
|
+
self.close()
|
|
208
|
+
|
|
209
|
+
@property
|
|
210
|
+
def factory(self) -> IDSFactory:
|
|
211
|
+
"""Get the IDS factory used by this DB entry."""
|
|
212
|
+
return self._ids_factory
|
|
213
|
+
|
|
214
|
+
@property
|
|
215
|
+
def dd_version(self) -> str:
|
|
216
|
+
"""Get the DD version used by this DB entry"""
|
|
217
|
+
return self._ids_factory.version
|
|
218
|
+
|
|
219
|
+
def close(self, *, erase=False):
|
|
220
|
+
"""Close this Database Entry.
|
|
221
|
+
|
|
222
|
+
Keyword Args:
|
|
223
|
+
erase: Remove the pulse file from the database. Note: this parameter may be
|
|
224
|
+
ignored by the backend. It is best to not use it.
|
|
225
|
+
"""
|
|
226
|
+
if self._dbe_impl is None:
|
|
227
|
+
return
|
|
228
|
+
self._dbe_impl.close(erase=erase)
|
|
229
|
+
self._dbe_impl = None
|
|
230
|
+
|
|
231
|
+
def create(self, *, options=None, force=True) -> None:
|
|
232
|
+
"""Create a new database entry.
|
|
233
|
+
|
|
234
|
+
This method may not be called when using the URI constructor of DBEntry.
|
|
235
|
+
|
|
236
|
+
Caution:
|
|
237
|
+
This method erases the previous entry if it existed!
|
|
238
|
+
|
|
239
|
+
Keyword Args:
|
|
240
|
+
options: Backend specific options.
|
|
241
|
+
force: Whether to force create the database entry.
|
|
242
|
+
|
|
243
|
+
Example:
|
|
244
|
+
.. code-block:: python
|
|
245
|
+
|
|
246
|
+
import imas
|
|
247
|
+
from imas.ids_defs import HDF5_BACKEND
|
|
248
|
+
|
|
249
|
+
imas_entry = imas.DBEntry(HDF5_BACKEND, "test", 1, 1234)
|
|
250
|
+
imas_entry.create()
|
|
251
|
+
"""
|
|
252
|
+
self._open_pulse(FORCE_CREATE_PULSE if force else CREATE_PULSE, options)
|
|
253
|
+
|
|
254
|
+
def open(self, mode=OPEN_PULSE, *, options=None, force=False) -> None:
|
|
255
|
+
"""Open an existing database entry.
|
|
256
|
+
|
|
257
|
+
This method may not be called when using the URI constructor of DBEntry.
|
|
258
|
+
|
|
259
|
+
Keyword Args:
|
|
260
|
+
options: Backend specific options.
|
|
261
|
+
force: Whether to force open the database entry.
|
|
262
|
+
|
|
263
|
+
Example:
|
|
264
|
+
.. code-block:: python
|
|
265
|
+
|
|
266
|
+
import imas
|
|
267
|
+
from imas.ids_defs import HDF5_BACKEND
|
|
268
|
+
|
|
269
|
+
imas_entry = imas.DBEntry(HDF5_BACKEND, "test", 1, 1234)
|
|
270
|
+
imas_entry.open()
|
|
271
|
+
"""
|
|
272
|
+
if force:
|
|
273
|
+
mode = FORCE_OPEN_PULSE
|
|
274
|
+
logger.warning(
|
|
275
|
+
"DBEntry.open(force=True) is deprecated, "
|
|
276
|
+
"use DBEntry.open(FORCE_OPEN_PULSE) instead"
|
|
277
|
+
)
|
|
278
|
+
self._open_pulse(mode, options)
|
|
279
|
+
|
|
280
|
+
def _open_pulse(self, mode: int, options: Any) -> None:
|
|
281
|
+
"""Internal method implementing open()/create()."""
|
|
282
|
+
if self._dbe_impl is not None:
|
|
283
|
+
raise RuntimeError("This DBEntry is already open")
|
|
284
|
+
if self.uri is not None:
|
|
285
|
+
raise RuntimeError(
|
|
286
|
+
"This DBEntry was opened using an URI: "
|
|
287
|
+
"DBEntry.open/create is not available."
|
|
288
|
+
)
|
|
289
|
+
|
|
290
|
+
cls = self._select_implementation(self.uri)
|
|
291
|
+
self._dbe_impl = cls.from_pulse_run(
|
|
292
|
+
self.backend_id,
|
|
293
|
+
self.db_name,
|
|
294
|
+
self.pulse,
|
|
295
|
+
self.run,
|
|
296
|
+
self.user_name,
|
|
297
|
+
self.data_version,
|
|
298
|
+
mode,
|
|
299
|
+
options,
|
|
300
|
+
self._ids_factory,
|
|
301
|
+
)
|
|
302
|
+
|
|
303
|
+
def get(
|
|
304
|
+
self,
|
|
305
|
+
ids_name: str,
|
|
306
|
+
occurrence: int = 0,
|
|
307
|
+
*,
|
|
308
|
+
lazy: bool = False,
|
|
309
|
+
autoconvert: bool = True,
|
|
310
|
+
ignore_unknown_dd_version: bool = False,
|
|
311
|
+
destination: Optional[IDSToplevel] = None,
|
|
312
|
+
) -> IDSToplevel:
|
|
313
|
+
"""Read the contents of an IDS into memory.
|
|
314
|
+
|
|
315
|
+
This method fetches an IDS in its entirety, with all time slices it may contain.
|
|
316
|
+
See :meth:`get_slice` for reading a specific time slice.
|
|
317
|
+
|
|
318
|
+
Args:
|
|
319
|
+
ids_name: Name of the IDS to read from the backend.
|
|
320
|
+
occurrence: Which occurrence of the IDS to read.
|
|
321
|
+
|
|
322
|
+
Keyword Args:
|
|
323
|
+
lazy: When set to ``True``, values in this IDS will be retrieved only when
|
|
324
|
+
needed (instead of getting the full IDS immediately). See :ref:`Lazy
|
|
325
|
+
loading` for more details.
|
|
326
|
+
|
|
327
|
+
.. note:: Lazy loading is not supported by the ASCII backend.
|
|
328
|
+
autoconvert: Automatically convert IDSs.
|
|
329
|
+
|
|
330
|
+
If enabled (default), a call to ``get()`` or ``get_slice()`` will return
|
|
331
|
+
an IDS from the Data Dictionary version attached to this Data Entry.
|
|
332
|
+
Data is automatically converted between the on-disk version and the
|
|
333
|
+
in-memory version.
|
|
334
|
+
|
|
335
|
+
When set to ``False``, the IDS will be returned in the DD version it was
|
|
336
|
+
stored in.
|
|
337
|
+
ignore_unknown_dd_version: When an IDS is stored with an unknown DD version,
|
|
338
|
+
do not attempt automatic conversion and fetch the data in the Data
|
|
339
|
+
Dictionary version attached to this Data Entry.
|
|
340
|
+
destination: Populate this IDSToplevel instead of creating an empty one.
|
|
341
|
+
|
|
342
|
+
Returns:
|
|
343
|
+
The loaded IDS.
|
|
344
|
+
|
|
345
|
+
Example:
|
|
346
|
+
.. code-block:: python
|
|
347
|
+
|
|
348
|
+
import imas
|
|
349
|
+
|
|
350
|
+
imas_entry = imas.DBEntry(imas.ids_defs.MDSPLUS_BACKEND, "ITER", 131024, 41, "public")
|
|
351
|
+
imas_entry.open()
|
|
352
|
+
core_profiles = imas_entry.get("core_profiles")
|
|
353
|
+
""" # noqa
|
|
354
|
+
return self._get(
|
|
355
|
+
ids_name,
|
|
356
|
+
occurrence,
|
|
357
|
+
None,
|
|
358
|
+
destination,
|
|
359
|
+
lazy,
|
|
360
|
+
autoconvert,
|
|
361
|
+
ignore_unknown_dd_version,
|
|
362
|
+
)
|
|
363
|
+
|
|
364
|
+
def get_slice(
|
|
365
|
+
self,
|
|
366
|
+
ids_name: str,
|
|
367
|
+
time_requested: float,
|
|
368
|
+
interpolation_method: int,
|
|
369
|
+
occurrence: int = 0,
|
|
370
|
+
*,
|
|
371
|
+
lazy: bool = False,
|
|
372
|
+
autoconvert: bool = True,
|
|
373
|
+
ignore_unknown_dd_version: bool = False,
|
|
374
|
+
destination: Optional[IDSToplevel] = None,
|
|
375
|
+
) -> IDSToplevel:
|
|
376
|
+
"""Read a single time slice from an IDS in this Database Entry.
|
|
377
|
+
|
|
378
|
+
This method returns an IDS object with all constant/static data filled. The
|
|
379
|
+
dynamic data is interpolated on the requested time slice. This means that the
|
|
380
|
+
size of the time dimension in the returned data is 1.
|
|
381
|
+
|
|
382
|
+
Args:
|
|
383
|
+
ids_name: Name of the IDS to read from the backend.
|
|
384
|
+
time_requested: Requested time slice
|
|
385
|
+
interpolation_method: Interpolation method to use. Available options:
|
|
386
|
+
|
|
387
|
+
- :const:`~imas.ids_defs.CLOSEST_INTERP`
|
|
388
|
+
- :const:`~imas.ids_defs.PREVIOUS_INTERP`
|
|
389
|
+
- :const:`~imas.ids_defs.LINEAR_INTERP`
|
|
390
|
+
|
|
391
|
+
occurrence: Which occurrence of the IDS to read.
|
|
392
|
+
|
|
393
|
+
Keyword Args:
|
|
394
|
+
lazy: When set to ``True``, values in this IDS will be retrieved only when
|
|
395
|
+
needed (instead of getting the full IDS immediately). See :ref:`Lazy
|
|
396
|
+
loading` for more details.
|
|
397
|
+
autoconvert: Automatically convert IDSs.
|
|
398
|
+
|
|
399
|
+
If enabled (default), a call to ``get()`` or ``get_slice()`` will return
|
|
400
|
+
an IDS from the Data Dictionary version attached to this Data Entry.
|
|
401
|
+
Data is automatically converted between the on-disk version and the
|
|
402
|
+
in-memory version.
|
|
403
|
+
|
|
404
|
+
When set to ``False``, the IDS will be returned in the DD version it was
|
|
405
|
+
stored in.
|
|
406
|
+
ignore_unknown_dd_version: When an IDS is stored with an unknown DD version,
|
|
407
|
+
do not attempt automatic conversion and fetch the data in the Data
|
|
408
|
+
Dictionary version attached to this Data Entry.
|
|
409
|
+
destination: Populate this IDSToplevel instead of creating an empty one.
|
|
410
|
+
|
|
411
|
+
Returns:
|
|
412
|
+
The loaded IDS.
|
|
413
|
+
|
|
414
|
+
Example:
|
|
415
|
+
.. code-block:: python
|
|
416
|
+
|
|
417
|
+
import imas
|
|
418
|
+
|
|
419
|
+
imas_entry = imas.DBEntry(imas.ids_defs.MDSPLUS_BACKEND, "ITER", 131024, 41, "public")
|
|
420
|
+
imas_entry.open()
|
|
421
|
+
core_profiles = imas_entry.get_slice("core_profiles", 370, imas.ids_defs.PREVIOUS_INTERP)
|
|
422
|
+
""" # noqa
|
|
423
|
+
return self._get(
|
|
424
|
+
ids_name,
|
|
425
|
+
occurrence,
|
|
426
|
+
GetSliceParameters(time_requested, interpolation_method),
|
|
427
|
+
destination,
|
|
428
|
+
lazy,
|
|
429
|
+
autoconvert,
|
|
430
|
+
ignore_unknown_dd_version,
|
|
431
|
+
)
|
|
432
|
+
|
|
433
|
+
def get_sample(
|
|
434
|
+
self,
|
|
435
|
+
ids_name: str,
|
|
436
|
+
tmin: float,
|
|
437
|
+
tmax: float,
|
|
438
|
+
dtime: Optional[Union[float, numpy.ndarray]] = None,
|
|
439
|
+
interpolation_method: Optional[int] = None,
|
|
440
|
+
occurrence: int = 0,
|
|
441
|
+
*,
|
|
442
|
+
lazy: bool = False,
|
|
443
|
+
autoconvert: bool = True,
|
|
444
|
+
ignore_unknown_dd_version: bool = False,
|
|
445
|
+
destination: Optional[IDSToplevel] = None,
|
|
446
|
+
) -> IDSToplevel:
|
|
447
|
+
"""Read a range of time slices from an IDS in this Database Entry.
|
|
448
|
+
|
|
449
|
+
This method has three different modes, depending on the provided arguments:
|
|
450
|
+
|
|
451
|
+
1. No interpolation. This method is selected when :param:`dtime` and
|
|
452
|
+
:param:`interpolation_method` are not provided.
|
|
453
|
+
|
|
454
|
+
This mode returns an IDS object with all constant/static data filled. The
|
|
455
|
+
dynamic data is retrieved for the provided time range [tmin, tmax].
|
|
456
|
+
|
|
457
|
+
2. Interpolate dynamic data on a uniform time base. This method is selected
|
|
458
|
+
when :param:`dtime` and :param:`interpolation_method` are provided.
|
|
459
|
+
:param:`dtime` must be a number or a numpy array of size 1.
|
|
460
|
+
|
|
461
|
+
This mode will generate an IDS with a homogeneous time vector ``[tmin, tmin
|
|
462
|
+
+ dtime, tmin + 2*dtime, ...`` up to ``tmax``. The chosen interpolation
|
|
463
|
+
method will have no effect on the time vector, but may have an impact on the
|
|
464
|
+
other dynamic values. The returned IDS always has
|
|
465
|
+
``ids_properties.homogeneous_time = 1``.
|
|
466
|
+
|
|
467
|
+
3. Interpolate dynamic data on an explicit time base. This method is selected
|
|
468
|
+
when :param:`dtime` and :param:`interpolation_method` are provided.
|
|
469
|
+
:param:`dtime` must be a numpy array of size larger than 1.
|
|
470
|
+
|
|
471
|
+
This mode will generate an IDS with a homogeneous time vector equal to
|
|
472
|
+
:param:`dtime`. :param:`tmin` and :param:`tmax` are ignored in this mode.
|
|
473
|
+
The chosen interpolation method will have no effect on the time vector, but
|
|
474
|
+
may have an impact on the other dynamic values. The returned IDS always has
|
|
475
|
+
``ids_properties.homogeneous_time = 1``.
|
|
476
|
+
|
|
477
|
+
Args:
|
|
478
|
+
ids_name: Name of the IDS to read from the backend
|
|
479
|
+
tmin: Lower bound of the requested time range
|
|
480
|
+
tmax: Upper bound of the requested time range, must be larger than or
|
|
481
|
+
equal to :param:`tmin`
|
|
482
|
+
dtime: Interval to use when interpolating, must be positive, or numpy array
|
|
483
|
+
containing an explicit time base to interpolate.
|
|
484
|
+
interpolation_method: Interpolation method to use. Available options:
|
|
485
|
+
|
|
486
|
+
- :const:`~imas.ids_defs.CLOSEST_INTERP`
|
|
487
|
+
- :const:`~imas.ids_defs.PREVIOUS_INTERP`
|
|
488
|
+
- :const:`~imas.ids_defs.LINEAR_INTERP`
|
|
489
|
+
|
|
490
|
+
occurrence: Which occurrence of the IDS to read.
|
|
491
|
+
|
|
492
|
+
Keyword Args:
|
|
493
|
+
lazy: When set to ``True``, values in this IDS will be retrieved only when
|
|
494
|
+
needed (instead of getting the full IDS immediately). See :ref:`Lazy
|
|
495
|
+
loading` for more details.
|
|
496
|
+
autoconvert: Automatically convert IDSs.
|
|
497
|
+
|
|
498
|
+
If enabled (default), a call to ``get_sample()`` will return
|
|
499
|
+
an IDS from the Data Dictionary version attached to this Data Entry.
|
|
500
|
+
Data is automatically converted between the on-disk version and the
|
|
501
|
+
in-memory version.
|
|
502
|
+
|
|
503
|
+
When set to ``False``, the IDS will be returned in the DD version it was
|
|
504
|
+
stored in.
|
|
505
|
+
ignore_unknown_dd_version: When an IDS is stored with an unknown DD version,
|
|
506
|
+
do not attempt automatic conversion and fetch the data in the Data
|
|
507
|
+
Dictionary version attached to this Data Entry.
|
|
508
|
+
destination: Populate this IDSToplevel instead of creating an empty one.
|
|
509
|
+
|
|
510
|
+
Returns:
|
|
511
|
+
The loaded IDS.
|
|
512
|
+
|
|
513
|
+
Example:
|
|
514
|
+
.. code-block:: python
|
|
515
|
+
|
|
516
|
+
import imas
|
|
517
|
+
import numpy
|
|
518
|
+
from imas import ids_defs
|
|
519
|
+
|
|
520
|
+
imas_entry = imas.DBEntry(
|
|
521
|
+
"imas:mdsplus?user=public;pulse=131024;run=41;database=ITER", "r")
|
|
522
|
+
|
|
523
|
+
# All time slices between t=200 and t=370
|
|
524
|
+
core_profiles = imas_entry.get_sample("core_profiles", 200, 370)
|
|
525
|
+
|
|
526
|
+
# Closest points to [0, 100, 200, ..., 1000]
|
|
527
|
+
core_profiles_interp = imas_entry.get_sample(
|
|
528
|
+
"core_profiles", 0, 1000, 100, ids_defs.CLOSEST_INTERP)
|
|
529
|
+
|
|
530
|
+
# Linear interpolation for [10, 11, 12, 14, 16, 20, 30, 40, 50]
|
|
531
|
+
times = numpy.array([10, 11, 12, 14, 16, 20, 30, 40, 50])
|
|
532
|
+
core_profiles_interp = imas_entry.get_sample(
|
|
533
|
+
"core_profiles", 0, 0, times, ids_defs.LINEAR_INTERP)
|
|
534
|
+
"""
|
|
535
|
+
if dtime is not None:
|
|
536
|
+
dtime = numpy.atleast_1d(dtime) # Convert floats and 0D arrays to 1D array
|
|
537
|
+
return self._get(
|
|
538
|
+
ids_name,
|
|
539
|
+
occurrence,
|
|
540
|
+
GetSampleParameters(tmin, tmax, dtime, interpolation_method),
|
|
541
|
+
destination,
|
|
542
|
+
lazy,
|
|
543
|
+
autoconvert,
|
|
544
|
+
ignore_unknown_dd_version,
|
|
545
|
+
)
|
|
546
|
+
|
|
547
|
+
def _get(
|
|
548
|
+
self,
|
|
549
|
+
ids_name: str,
|
|
550
|
+
occurrence: int,
|
|
551
|
+
parameters: Union[None, GetSliceParameters, GetSampleParameters],
|
|
552
|
+
destination: Optional[IDSToplevel],
|
|
553
|
+
lazy: bool,
|
|
554
|
+
autoconvert: bool,
|
|
555
|
+
ignore_unknown_dd_version: bool,
|
|
556
|
+
) -> IDSToplevel:
|
|
557
|
+
"""Actual implementation of get() and get_slice()"""
|
|
558
|
+
if self._dbe_impl is None:
|
|
559
|
+
raise RuntimeError("Database entry is not open.")
|
|
560
|
+
if lazy and destination:
|
|
561
|
+
raise ValueError("Cannot supply a destination IDS when lazy loading.")
|
|
562
|
+
if not self._ids_factory.exists(ids_name):
|
|
563
|
+
raise IDSNameError(ids_name, self._ids_factory)
|
|
564
|
+
|
|
565
|
+
# Note: this will raise an exception when the ids/occurrence is not filled:
|
|
566
|
+
dd_version = self._dbe_impl.read_dd_version(ids_name, occurrence)
|
|
567
|
+
|
|
568
|
+
# DD version sanity checks:
|
|
569
|
+
if not dd_version:
|
|
570
|
+
# No DD version stored in the IDS, load as if it was stored with
|
|
571
|
+
# self.dd_version
|
|
572
|
+
logger.warning(
|
|
573
|
+
"Loaded IDS (%s, occurrence %s) does not specify a data dictionary "
|
|
574
|
+
"version. Some data may not be loaded.",
|
|
575
|
+
ids_name,
|
|
576
|
+
occurrence,
|
|
577
|
+
)
|
|
578
|
+
elif dd_version != self.dd_version and dd_version not in dd_xml_versions():
|
|
579
|
+
# We don't know the DD version that this IDS was written with
|
|
580
|
+
if ignore_unknown_dd_version:
|
|
581
|
+
# User chooses to ignore this problem, load as if it was stored with
|
|
582
|
+
# self.dd_version
|
|
583
|
+
logger.info("Ignoring unknown data dictionary version %s", dd_version)
|
|
584
|
+
dd_version = None
|
|
585
|
+
else:
|
|
586
|
+
note = (
|
|
587
|
+
"\nYou may set the get/get_slice parameter "
|
|
588
|
+
"ignore_unknown_dd_version=True to ignore this and get an IDS in "
|
|
589
|
+
f"the default DD version ({self.dd_version})"
|
|
590
|
+
)
|
|
591
|
+
raise UnknownDDVersion(dd_version, dd_xml_versions(), note)
|
|
592
|
+
|
|
593
|
+
# Version conversion:
|
|
594
|
+
if not destination:
|
|
595
|
+
# Construct IDS object that the backend can store data in
|
|
596
|
+
if autoconvert or not dd_version:
|
|
597
|
+
# Store results in our DD version
|
|
598
|
+
destination = self._ids_factory.new(ids_name, _lazy=lazy)
|
|
599
|
+
else:
|
|
600
|
+
# Store results in the on-disk version
|
|
601
|
+
destination = IDSFactory(dd_version).new(ids_name, _lazy=lazy)
|
|
602
|
+
|
|
603
|
+
nbc_map = None
|
|
604
|
+
if dd_version and dd_version != destination._dd_version:
|
|
605
|
+
if dd_version.split(".")[0] != destination._dd_version.split(".")[0]:
|
|
606
|
+
logger.warning(
|
|
607
|
+
"On-disk data is stored in DD %s which has a different major "
|
|
608
|
+
"version than the requested DD version (%s). imas-python will "
|
|
609
|
+
"convert the data automatically, but this does not cover all"
|
|
610
|
+
"changes. "
|
|
611
|
+
"See %s/multi-dd.html#conversion-of-idss-between-dd-versions",
|
|
612
|
+
dd_version,
|
|
613
|
+
destination._dd_version,
|
|
614
|
+
imas.PUBLISHED_DOCUMENTATION_ROOT,
|
|
615
|
+
)
|
|
616
|
+
ddmap, source_is_older = dd_version_map_from_factories(
|
|
617
|
+
ids_name, IDSFactory(version=dd_version), self._ids_factory
|
|
618
|
+
)
|
|
619
|
+
nbc_map = ddmap.new_to_old if source_is_older else ddmap.old_to_new
|
|
620
|
+
|
|
621
|
+
# Pass on to the DBEntry implementation:
|
|
622
|
+
return self._dbe_impl.get(
|
|
623
|
+
ids_name,
|
|
624
|
+
occurrence,
|
|
625
|
+
parameters,
|
|
626
|
+
destination,
|
|
627
|
+
lazy,
|
|
628
|
+
nbc_map,
|
|
629
|
+
)
|
|
630
|
+
|
|
631
|
+
def put(self, ids: IDSToplevel, occurrence: int = 0) -> None:
|
|
632
|
+
"""Write the contents of an IDS into this Database Entry.
|
|
633
|
+
|
|
634
|
+
The IDS is written entirely, with all time slices it may contain.
|
|
635
|
+
|
|
636
|
+
Caution:
|
|
637
|
+
The put method deletes any previously existing data within the target IDS
|
|
638
|
+
occurrence in the Database Entry.
|
|
639
|
+
|
|
640
|
+
Args:
|
|
641
|
+
ids: IDS object to put.
|
|
642
|
+
occurrence: Which occurrence of the IDS to write to.
|
|
643
|
+
|
|
644
|
+
Example:
|
|
645
|
+
.. code-block:: python
|
|
646
|
+
|
|
647
|
+
ids = imas.IDSFactory().pf_active()
|
|
648
|
+
... # fill the pf_active IDS here
|
|
649
|
+
imas_entry.put(ids)
|
|
650
|
+
"""
|
|
651
|
+
self._put(ids, occurrence, False)
|
|
652
|
+
|
|
653
|
+
def put_slice(self, ids: IDSToplevel, occurrence: int = 0) -> None:
|
|
654
|
+
"""Append a time slice of the provided IDS to the Database Entry.
|
|
655
|
+
|
|
656
|
+
Time slices must be appended in strictly increasing time order, since the Access
|
|
657
|
+
Layer is not reordering time arrays. Doing otherwise will result in
|
|
658
|
+
non-monotonic time arrays, which will create confusion and make subsequent
|
|
659
|
+
:meth:`get_slice` commands to fail.
|
|
660
|
+
|
|
661
|
+
Although being put progressively time slice by time slice, the final IDS must be
|
|
662
|
+
compliant with the data dictionary. A typical error when constructing IDS
|
|
663
|
+
variables time slice by time slice is to change the size of the IDS fields
|
|
664
|
+
during the time loop, which is not allowed but for the children of an array of
|
|
665
|
+
structure which has time as its coordinate.
|
|
666
|
+
|
|
667
|
+
The :meth:`put_slice` command is appending data, so does not modify previously
|
|
668
|
+
existing data within the target IDS occurrence in the Data Entry.
|
|
669
|
+
|
|
670
|
+
It is possible possible to append several time slices to a node of the IDS in
|
|
671
|
+
one :meth:`put_slice` call, however the user must ensure that the size of the
|
|
672
|
+
time dimension of the node remains consistent with the size of its timebase.
|
|
673
|
+
|
|
674
|
+
Args:
|
|
675
|
+
ids: IDS object to put.
|
|
676
|
+
occurrence: Which occurrence of the IDS to write to.
|
|
677
|
+
|
|
678
|
+
Example:
|
|
679
|
+
A frequent use case is storing IMAS data progressively in a time loop. You
|
|
680
|
+
can fill the constant and static values only once and progressively append
|
|
681
|
+
the dynamic values calculated in each step of the time loop with
|
|
682
|
+
:meth:`put_slice`.
|
|
683
|
+
|
|
684
|
+
.. code-block:: python
|
|
685
|
+
|
|
686
|
+
ids = imas.IDSFactory().pf_active() ... # fill the static data of the
|
|
687
|
+
pf_active IDS here for i in range(N):
|
|
688
|
+
... # fill time slice of the pf_active IDS imas_entry.put_slice(ids)
|
|
689
|
+
"""
|
|
690
|
+
self._put(ids, occurrence, True)
|
|
691
|
+
|
|
692
|
+
def _put(self, ids: IDSToplevel, occurrence: int, is_slice: bool):
|
|
693
|
+
"""Actual implementation of put() and put_slice()"""
|
|
694
|
+
if self._dbe_impl is None:
|
|
695
|
+
raise RuntimeError("Database entry is not open.")
|
|
696
|
+
if ids._lazy:
|
|
697
|
+
raise ValueError("Lazy loaded IDSs cannot be used in put or put_slice.")
|
|
698
|
+
|
|
699
|
+
# Automatic validation
|
|
700
|
+
disable_validate = os.environ.get("IMAS_AL_DISABLE_VALIDATE")
|
|
701
|
+
if not disable_validate or disable_validate == "0":
|
|
702
|
+
try:
|
|
703
|
+
ids.validate()
|
|
704
|
+
except ValidationError:
|
|
705
|
+
logger.error(
|
|
706
|
+
"IDS %s is not valid. You can disable automatic IDS validation by "
|
|
707
|
+
"setting the environment variable IMAS_AL_DISABLE_VALIDATE=1.",
|
|
708
|
+
ids.metadata.name,
|
|
709
|
+
)
|
|
710
|
+
raise
|
|
711
|
+
|
|
712
|
+
ids_name = ids.metadata.name
|
|
713
|
+
# Verify homogeneous_time is set
|
|
714
|
+
time_mode = ids.ids_properties.homogeneous_time
|
|
715
|
+
# TODO: allow unset homogeneous_time and quit with no action?
|
|
716
|
+
if time_mode not in IDS_TIME_MODES:
|
|
717
|
+
raise ValueError("'ids_properties.homogeneous_time' is not set or invalid.")
|
|
718
|
+
# IMAS-3330: automatically set time mode to independent:
|
|
719
|
+
if ids.metadata.type is IDSType.CONSTANT:
|
|
720
|
+
if time_mode != IDS_TIME_MODE_INDEPENDENT:
|
|
721
|
+
logger.warning(
|
|
722
|
+
"ids_properties/homogeneous_time has been set to 2 for the constant"
|
|
723
|
+
" IDS %s/%d. Please check the program which has filled this IDS"
|
|
724
|
+
" since this is the mandatory value for a constant IDS",
|
|
725
|
+
ids_name,
|
|
726
|
+
occurrence,
|
|
727
|
+
)
|
|
728
|
+
ids.ids_properties.homogeneous_time = IDS_TIME_MODE_INDEPENDENT
|
|
729
|
+
if is_slice and time_mode == IDS_TIME_MODE_INDEPENDENT:
|
|
730
|
+
raise RuntimeError("Cannot use put_slice with IDS_TIME_MODE_INDEPENDENT.")
|
|
731
|
+
|
|
732
|
+
# Set version_put properties (version_put was added in DD 3.22)
|
|
733
|
+
if hasattr(ids.ids_properties, "version_put"):
|
|
734
|
+
version_put = ids.ids_properties.version_put
|
|
735
|
+
version_put.data_dictionary = self._ids_factory._version
|
|
736
|
+
version_put.access_layer = self._dbe_impl.access_layer_version()
|
|
737
|
+
version_put.access_layer_language = f"imas {imas.__version__}"
|
|
738
|
+
|
|
739
|
+
self._dbe_impl.put(ids, occurrence, is_slice)
|
|
740
|
+
|
|
741
|
+
def delete_data(self, ids_name: str, occurrence: int = 0) -> None:
|
|
742
|
+
"""Delete the provided IDS occurrence from this IMAS database entry.
|
|
743
|
+
|
|
744
|
+
Args:
|
|
745
|
+
ids_name: Name of the IDS to delete from the backend.
|
|
746
|
+
occurrence: Which occurrence of the IDS to delete.
|
|
747
|
+
"""
|
|
748
|
+
if self._dbe_impl is None:
|
|
749
|
+
raise RuntimeError("Database entry is not open.")
|
|
750
|
+
self._dbe_impl.delete_data(ids_name, occurrence)
|
|
751
|
+
|
|
752
|
+
@overload
|
|
753
|
+
def list_all_occurrences(
|
|
754
|
+
self, ids_name: str, node_path: None = None
|
|
755
|
+
) -> List[int]: ...
|
|
756
|
+
|
|
757
|
+
@overload
|
|
758
|
+
def list_all_occurrences(
|
|
759
|
+
self, ids_name: str, node_path: str
|
|
760
|
+
) -> Tuple[List[int], List[IDSBase]]: ...
|
|
761
|
+
|
|
762
|
+
def list_all_occurrences(self, ids_name, node_path=None):
|
|
763
|
+
"""List all non-empty occurrences of an IDS
|
|
764
|
+
|
|
765
|
+
Note: this is only available with Access Layer core version 5.1 or newer.
|
|
766
|
+
|
|
767
|
+
Args:
|
|
768
|
+
ids_name: name of the IDS (e.g. "magnetics", "core_profiles" or
|
|
769
|
+
"equilibrium")
|
|
770
|
+
node_path: path to a Data-Dictionary node (e.g. "ids_properties/comment",
|
|
771
|
+
"code/name", "ids_properties/provider").
|
|
772
|
+
|
|
773
|
+
Returns:
|
|
774
|
+
tuple or list:
|
|
775
|
+
When no ``node_path`` is supplied, a (sorted) list with non-empty
|
|
776
|
+
occurrence numbers is returned.
|
|
777
|
+
|
|
778
|
+
When ``node_path`` is supplied, a tuple ``(occurrence_list,
|
|
779
|
+
node_content_list)`` is returned. The ``occurrence_list`` is a (sorted)
|
|
780
|
+
list of non-empty occurrence numbers. The ``node_content_list`` contains
|
|
781
|
+
the contents of the node in the corresponding occurrences.
|
|
782
|
+
|
|
783
|
+
Example:
|
|
784
|
+
.. code-block:: python
|
|
785
|
+
|
|
786
|
+
dbentry = imas.DBEntry(uri, "r")
|
|
787
|
+
occurrence_list, node_content_list = \\
|
|
788
|
+
dbentry.list_all_occurrences("magnetics", "ids_properties/comment")
|
|
789
|
+
dbentry.close()
|
|
790
|
+
"""
|
|
791
|
+
if self._dbe_impl is None:
|
|
792
|
+
raise RuntimeError("Database entry is not open.")
|
|
793
|
+
|
|
794
|
+
occurrence_list = self._dbe_impl.list_all_occurrences(ids_name)
|
|
795
|
+
|
|
796
|
+
if node_path is None:
|
|
797
|
+
return occurrence_list
|
|
798
|
+
|
|
799
|
+
node_content_list = [
|
|
800
|
+
self.get(ids_name, occ, lazy=True)[node_path] for occ in occurrence_list
|
|
801
|
+
]
|
|
802
|
+
return occurrence_list, node_content_list
|