Skip to content

Commit 0aeeb22

Browse files
committed
Add conversion logic for core/edge -> plasma IDS
1 parent 72d7ea3 commit 0aeeb22

File tree

3 files changed

+153
-0
lines changed

3 files changed

+153
-0
lines changed

imas/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@
1515
from .db_entry import DBEntry
1616
from .ids_factory import IDSFactory
1717
from .ids_convert import convert_ids
18+
from .convert_core_edge_plasma import (
19+
convert_to_plasma_profiles,
20+
convert_to_plasma_transport,
21+
convert_to_plasma_sources,
22+
)
1823
from .ids_identifiers import identifiers
1924

2025
# Load the IMAS-Python IMAS AL/DD core

imas/convert_core_edge_plasma.py

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
# This file is part of IMAS-Python.
2+
# You should have received the IMAS-Python LICENSE file with this project.
3+
"""Logic to convert core/edge IDSs to their corresponding plasma ID."""
4+
5+
from packaging.version import Version
6+
7+
from imas.ids_toplevel import IDSToplevel
8+
from imas.ids_factory import IDSFactory
9+
from imas.exception import IDSNameError
10+
from imas.ids_convert import DDVersionMap, NBCPathMap, _copy_structure
11+
12+
13+
def convert_to_plasma_profiles(
14+
core_or_edge_profiles: IDSToplevel, *, deepcopy: bool = False
15+
) -> IDSToplevel:
16+
return _convert_to_plasma(core_or_edge_profiles, "profiles", deepcopy)
17+
18+
19+
def convert_to_plasma_sources(
20+
core_or_edge_sources: IDSToplevel, *, deepcopy: bool = False
21+
) -> IDSToplevel:
22+
return _convert_to_plasma(core_or_edge_sources, "sources", deepcopy)
23+
24+
25+
def convert_to_plasma_transport(
26+
core_or_edge_transport: IDSToplevel, *, deepcopy: bool = False
27+
) -> IDSToplevel:
28+
return _convert_to_plasma(core_or_edge_transport, "transport", deepcopy)
29+
30+
31+
class CoreEdgePlasmaMap(DDVersionMap):
32+
"""Subclass of DDVersionMap to generate an NBCPathMap that is suitable to copy
33+
between a core/edge IDS and the corresponding plasma IDS."""
34+
35+
def __init__(self, source, target, factory):
36+
self.ids_name = source
37+
self.old_version = factory._etree
38+
self.new_version = factory._etree
39+
self.version_old = Version(factory.version)
40+
41+
self.old_to_new = NBCPathMap()
42+
self.new_to_old = NBCPathMap()
43+
44+
old_ids_object = factory._etree.find(f"IDS[@name='{source}']")
45+
new_ids_object = factory._etree.find(f"IDS[@name='{target}']")
46+
self._build_map(old_ids_object, new_ids_object)
47+
48+
49+
def _convert_to_plasma(source: IDSToplevel, suffix: str, deepcopy: bool) -> IDSToplevel:
50+
# Sanity checks for input data
51+
if not isinstance(source, IDSToplevel):
52+
raise TypeError(
53+
f"First argument to convert_to_plasma_{suffix} must be a core_{suffix} or "
54+
f"edge_{suffix} of type IDSToplevel. Got a type {type(source)} instead."
55+
)
56+
if source.metadata.name not in [f"core_{suffix}", f"edge_{suffix}"]:
57+
raise ValueError(
58+
f"First argument to convert_to_plasma_{suffix} must be a core_{suffix} or "
59+
f"edge_{suffix} IDS. Got a {source.metadata.name} IDS instead."
60+
)
61+
62+
# Construct target plasma_{suffix} IDS
63+
factory: IDSFactory = source._parent
64+
try:
65+
target = factory.new(f"plasma_{suffix}")
66+
except IDSNameError:
67+
raise ValueError(
68+
f"Cannot convert {source.metadata.name} IDS to plasma_{suffix}: the source "
69+
f"IDS uses Data Dictionary version {factory.dd_version} which doesn't have "
70+
f"a plasma_{suffix} IDS. Please convert the source IDS to a supported Data "
71+
"Dictionary version using `imas.convert_ids` and try again."
72+
) from None
73+
74+
# Leverage existing logic from ids_convert to do the copying
75+
# First construct a map (to handle missing items in the target IDS)
76+
data_map = CoreEdgePlasmaMap(source.metadata.name, target.metadata.name, factory)
77+
path_map = data_map.old_to_new # old = core/edge, new = plasma IDS
78+
_copy_structure(source, target, deepcopy, path_map)
79+
80+
return target
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import pytest
2+
3+
import imas.training
4+
from imas.util import idsdiffgen
5+
from imas.test.test_helpers import fill_with_random_data
6+
7+
8+
def assert_equal(core_edge, plasma):
9+
# We only expect the IDS name to be different:
10+
difflist = list(idsdiffgen(core_edge, plasma))
11+
assert difflist == [("IDS name", core_edge.metadata.name, plasma.metadata.name)]
12+
13+
14+
def test_convert_training_core_profiles():
15+
with imas.training.get_training_db_entry() as entry:
16+
cp = entry.get("core_profiles")
17+
18+
pp = imas.convert_to_plasma_profiles(cp)
19+
assert_equal(cp, pp)
20+
21+
22+
def test_convert_missing_qty():
23+
cp = imas.IDSFactory("4.1.0").core_profiles()
24+
cp.profiles_1d.resize(1)
25+
cp.profiles_1d[0].ion.resize(1)
26+
cp.profiles_1d[0].ion[0].state.resize(1)
27+
cp.profiles_1d[0].ion[0].state[0].ionization_potential = 0.5
28+
29+
pp = imas.convert_to_plasma_profiles(cp)
30+
# check that state[0] is copied, but that it's empty
31+
assert not pp.profiles_1d[0].ion[0].state[0].has_value
32+
33+
34+
@pytest.mark.parametrize("idsname", ["core_profiles", "edge_profiles"])
35+
def test_convert_randomly_filled_profiles(idsname):
36+
ids = imas.IDSFactory("4.1.0").new(idsname)
37+
fill_with_random_data(ids)
38+
39+
if idsname == "core_profiles":
40+
# ionization_potential doesn't exist in plasma_profiles in DD 4.1.0. This case
41+
# is tested in test_convert_missing_qty. Unset these variables to avoid a diff:
42+
for profiles in list(ids.profiles_1d) + list(ids.profiles_2d):
43+
for ion in profiles.ion:
44+
for state in ion.state:
45+
del state.ionization_potential
46+
del state.ionization_potential_error_upper
47+
del state.ionization_potential_error_lower
48+
49+
plasma = imas.convert_to_plasma_profiles(ids)
50+
assert_equal(ids, plasma)
51+
52+
53+
@pytest.mark.parametrize("idsname", ["core_sources", "edge_sources"])
54+
def test_convert_randomly_filled_sources(idsname):
55+
ids = imas.IDSFactory("4.1.0").new(idsname)
56+
fill_with_random_data(ids)
57+
58+
plasma = imas.convert_to_plasma_sources(ids)
59+
assert_equal(ids, plasma)
60+
61+
62+
@pytest.mark.parametrize("idsname", ["core_transport", "edge_transport"])
63+
def test_convert_randomly_filled_transport(idsname):
64+
ids = imas.IDSFactory("4.1.0").new(idsname)
65+
fill_with_random_data(ids)
66+
67+
plasma = imas.convert_to_plasma_transport(ids)
68+
assert_equal(ids, plasma)

0 commit comments

Comments
 (0)