Skip to content

Commit ac575c1

Browse files
committed
Add public methods to expose useful (up-to-now) private attributes of IDS nodes.
1 parent 067764d commit ac575c1

File tree

2 files changed

+196
-4
lines changed

2 files changed

+196
-4
lines changed

imaspy/test/test_util.py

Lines changed: 74 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,20 @@
1+
import pytest
2+
13
import imaspy
4+
from imaspy.db_entry import DBEntry
5+
from imaspy.ids_defs import MEMORY_BACKEND
26
from imaspy.test.test_helpers import fill_consistent
37
from imaspy.training import get_training_db_entry
48
from imaspy.util import (
59
find_paths,
10+
get_data_dictionary_version,
11+
get_full_path,
12+
get_parent,
13+
get_time_mode,
14+
get_toplevel,
615
idsdiffgen,
716
inspect,
17+
is_lazy_loaded,
818
print_metadata_tree,
919
print_tree,
1020
tree_iter,
@@ -133,5 +143,67 @@ def test_idsdiffgen():
133143

134144
def test_idsdiff():
135145
# Test the diff rendering for two sample IDSs
136-
entry = imaspy.training.get_training_db_entry()
137-
imaspy.util.idsdiff(entry.get("core_profiles"), entry.get("equilibrium"))
146+
with get_training_db_entry() as entry:
147+
imaspy.util.idsdiff(entry.get("core_profiles"), entry.get("equilibrium"))
148+
149+
150+
def test_get_parent():
151+
cp = imaspy.IDSFactory("3.39.0").core_profiles()
152+
cp.profiles_1d.resize(2)
153+
assert (
154+
get_parent(cp.profiles_1d[0].electrons.temperature)
155+
is cp.profiles_1d[0].electrons
156+
)
157+
assert get_parent(cp.profiles_1d[0].electrons) is cp.profiles_1d[0]
158+
assert get_parent(cp.profiles_1d[0]) is cp.profiles_1d
159+
assert get_parent(cp.profiles_1d) is cp
160+
assert get_parent(cp) is None
161+
162+
163+
def test_get_time_mode():
164+
cp = imaspy.IDSFactory("3.39.0").core_profiles()
165+
cp.profiles_1d.resize(2)
166+
assert (
167+
get_time_mode(cp.profiles_1d[0].electrons.temperature)
168+
is cp.ids_properties.homogeneous_time
169+
)
170+
171+
172+
def test_get_toplevel():
173+
cp = imaspy.IDSFactory("3.39.0").core_profiles()
174+
cp.profiles_1d.resize(2)
175+
assert get_toplevel(cp.profiles_1d[0].electrons.temperature) is cp
176+
assert get_toplevel(cp.profiles_1d[0].electrons) is cp
177+
assert get_toplevel(cp.profiles_1d[0]) is cp
178+
assert get_toplevel(cp.profiles_1d) is cp
179+
assert get_toplevel(cp) is cp
180+
181+
182+
def test_is_lazy_loaded():
183+
with get_training_db_entry() as entry:
184+
assert is_lazy_loaded(entry.get("core_profiles")) is False
185+
assert is_lazy_loaded(entry.get("core_profiles", lazy=True)) is True
186+
187+
188+
def test_get_full_path():
189+
cp = imaspy.IDSFactory("3.39.0").core_profiles()
190+
cp.profiles_1d.resize(2)
191+
assert (
192+
get_full_path(cp.profiles_1d[1].electrons.temperature)
193+
== "profiles_1d[1]/electrons/temperature"
194+
)
195+
196+
197+
@pytest.mark.parametrize("version", ["3.31.0", "3.39.0"])
198+
def test_get_dd_version(version):
199+
entry = DBEntry(MEMORY_BACKEND, "test", 0, 0, dd_version=version)
200+
assert get_data_dictionary_version(entry) == version
201+
assert get_data_dictionary_version(entry.factory) == version
202+
203+
cp = entry.factory.core_profiles()
204+
cp.profiles_1d.resize(2)
205+
assert get_data_dictionary_version(cp.profiles_1d[0].electrons) == version
206+
assert get_data_dictionary_version(cp.profiles_1d[0]) == version
207+
assert get_data_dictionary_version(cp.profiles_1d) == version
208+
assert get_data_dictionary_version(cp) == version
209+
assert get_data_dictionary_version(cp.time) == version

imaspy/util.py

Lines changed: 122 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,18 @@
66

77
import logging
88
import re
9-
from typing import Any, Callable, Iterator, List, Tuple, Union
9+
from typing import Any, Callable, Iterator, List, Optional, Tuple, Union
1010

1111
import numpy
1212

13+
from imaspy.db_entry import DBEntry
1314
from imaspy.ids_base import IDSBase
15+
from imaspy.ids_factory import IDSFactory
1416
from imaspy.ids_metadata import IDSMetadata
15-
from imaspy.ids_primitive import IDSPrimitive
17+
from imaspy.ids_primitive import IDSInt0D, IDSPrimitive
1618
from imaspy.ids_struct_array import IDSStructArray
1719
from imaspy.ids_structure import IDSStructure
20+
from imaspy.ids_toplevel import IDSToplevel
1821

1922
logger = logging.getLogger(__name__)
2023

@@ -404,3 +407,120 @@ def calc_hash(node: IDSBase) -> bytes:
404407
print(imaspy.util.calc_hash(cp).hex()) # 3b9b929756a242fd
405408
"""
406409
return node._xxhash()
410+
411+
412+
def get_parent(node: IDSBase) -> Optional[IDSBase]:
413+
"""Get the parent of any IDS node.
414+
415+
Args:
416+
node: Any node (structure, array of structures, data node) of an IDS.
417+
418+
Returns:
419+
The parent node of the provided node, or None if the node is an IDS toplevel.
420+
421+
Example:
422+
.. code-block:: python
423+
424+
>>> cp = imaspy.IDSFactory().core_profiles()
425+
>>> cp.profiles_1d.resize(2)
426+
>>> imaspy.util.get_parent(cp.profiles_1d[0].electrons.temperature)
427+
<IDSStructure (IDS:core_profiles, profiles_1d[0]/electrons)>
428+
>>> imaspy.util.get_parent(cp.profiles_1d[0].electrons)
429+
<IDSStructure (IDS:core_profiles, profiles_1d[0])>
430+
>>> imaspy.util.get_parent(cp.profiles_1d[0])
431+
<IDSStructArray (IDS:core_profiles, profiles_1d with 2 items)>
432+
>>> imaspy.util.get_parent(cp.profiles_1d)
433+
<IDSToplevel (IDS:core_profiles)>
434+
>>> imaspy.util.get_parent(cp)
435+
>>>
436+
"""
437+
if isinstance(node, IDSToplevel):
438+
return None
439+
return node._parent
440+
441+
442+
def get_time_mode(node: IDSBase) -> IDSInt0D:
443+
"""Retrieve ``ids_properties/homogeneous_time`` for any node in the IDS.
444+
445+
Args:
446+
node: Any node (structure, array of structures, data node) of an IDS.
447+
448+
Returns:
449+
``ids_properties/homogeneous_time``.
450+
451+
Example:
452+
.. code-block:: python
453+
454+
>>> cp = imaspy.IDSFactory().core_profiles()
455+
>>> cp.ids_properties.homogeneous_time = 0
456+
>>> cp.profiles_1d.resize(2)
457+
>>> imaspy.util.get_time_mode(cp.profiles_1d[0].electrons.temperature)
458+
<IDSInt0D (IDS:core_profiles, ids_properties/homogeneous_time, INT_0D)>
459+
int(0)
460+
"""
461+
return node._time_mode
462+
463+
464+
def get_toplevel(node: IDSBase) -> IDSToplevel:
465+
"""Retrieve the toplevel IDS object for any node in the IDS.
466+
467+
Args:
468+
node: Any node (structure, array of structures, data node) of an IDS.
469+
470+
Returns:
471+
The toplevel IDS object.
472+
473+
Example:
474+
.. code-block:: python
475+
476+
>>> cp = imaspy.IDSFactory().core_profiles()
477+
>>> cp.profiles_1d.resize(2)
478+
>>> imaspy.util.get_toplevel(cp.profiles_1d[0].electrons.temperature)
479+
<IDSToplevel (IDS:core_profiles)>
480+
"""
481+
return node._toplevel
482+
483+
484+
def is_lazy_loaded(node: IDSBase) -> bool:
485+
"""Find out if the provided (node of an) IDS is lazy loaded.
486+
487+
Args:
488+
node: Any node (structure, array of structures, data node) of an IDS.
489+
"""
490+
return node._lazy
491+
492+
493+
def get_full_path(node: IDSBase) -> str:
494+
"""Get the full path (relative to the IDS toplevel) of the provided node.
495+
496+
Caution:
497+
Determining the path is relatively expensive in large, nested Arrays of
498+
Structures: the calculation of the index suffix is O(N) in the size of the AoS.
499+
500+
Using this function may result in a performance bottleneck for your application.
501+
502+
Example:
503+
.. code-block:: python
504+
505+
>>> cp = imaspy.IDSFactory().core_profiles()
506+
>>> cp.profiles_1d.resize(2)
507+
>>> imaspy.util.get_full_path(cp.profiles_1d[1].electrons.temperature)
508+
'profiles_1d[1]/electrons/temperature'
509+
"""
510+
return node._path
511+
512+
513+
def get_data_dictionary_version(obj: Union[IDSBase, DBEntry, IDSFactory]) -> str:
514+
"""Find out the version of the data dictionary definitions that this object uses.
515+
516+
Args:
517+
obj: Any IMASPy object that is data-dictionary dependent.
518+
519+
Returns:
520+
The data dictionary version, e.g. ``"3.38.1"``.
521+
"""
522+
if isinstance(obj, (DBEntry, IDSFactory)):
523+
return obj.dd_version
524+
if isinstance(obj, IDSBase):
525+
return obj._version
526+
raise TypeError(f"Cannot get data dictionary version of '{type(obj)}'")

0 commit comments

Comments
 (0)