Skip to content

Commit f262071

Browse files
committed
Add example for em_coupling conversion
Fixes iterorganization#22.
1 parent 540033f commit f262071

File tree

4 files changed

+196
-0
lines changed

4 files changed

+196
-0
lines changed

docs/source/examples.rst

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
.. _`IMAS-Python Examples`:
2+
3+
IMAS-Python Examples
4+
====================
5+
6+
Most IMAS-Python usage examples can be found throughout the documentation pages. On this
7+
page we collect some examples that are too big or too generic to include in specific
8+
pages. Currently this is a short list, but we expect that it will grow over time.
9+
10+
.. toctree::
11+
:caption: IMAS-Python examples
12+
:maxdepth: 1
13+
14+
examples/custom_conversion_em_coupling
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
"""IMAS-Python example for custom conversion logic.
2+
3+
This example script loads a Data Entry (in Data Dictionary 3.38.1) created by
4+
DINA and converts the em_coupling IDS to DD 4.0.0.
5+
"""
6+
7+
import imas
8+
from imas.ids_defs import IDS_TIME_MODE_INDEPENDENT
9+
10+
input_uri = "imas:hdf5?path=/work/imas/shared/imasdb/ITER_SCENARIOS/3/105013/1"
11+
# An error is reported when there's already data at the output_uri!
12+
output_uri = "imas:hdf5?path=105013-1-converted"
13+
target_dd_version = "4.0.0"
14+
15+
16+
# Mapping of DD 3.38.1 em_coupling data to DD 4.0.0
17+
# Map the name of the matrix in DD 3.38.1 to the identifier and coordinate URIs
18+
COUPLING_MAPS = {
19+
"field_probes_active": dict(
20+
coupling_quantity=2,
21+
rows_uri="#magnetics/b_field_pol_probe",
22+
columns_uri="#pf_active/coil",
23+
),
24+
"field_probes_grid": dict(
25+
coupling_quantity=2,
26+
rows_uri="#magnetics/b_field_pol_probe",
27+
columns_uri="#pf_plasma/element",
28+
),
29+
"field_probes_passive": dict(
30+
coupling_quantity=2,
31+
rows_uri="#magnetics/b_field_pol_probe",
32+
columns_uri="#pf_passive/loop",
33+
),
34+
"mutual_active_active": dict(
35+
coupling_quantity=1,
36+
rows_uri="#pf_active/coil",
37+
columns_uri="#pf_active/coil",
38+
),
39+
"mutual_grid_active": dict(
40+
coupling_quantity=1,
41+
rows_uri="#pf_plasma/element",
42+
columns_uri="#pf_active/coil",
43+
),
44+
"mutual_grid_grid": dict(
45+
coupling_quantity=1,
46+
rows_uri="#pf_plasma/element",
47+
columns_uri="#pf_plasma/element",
48+
),
49+
"mutual_grid_passive": dict(
50+
coupling_quantity=1,
51+
rows_uri="#pf_plasma/element",
52+
columns_uri="#pf_passive/loop",
53+
),
54+
"mutual_loops_active": dict(
55+
coupling_quantity=1,
56+
rows_uri="#magnetics/flux_loop",
57+
columns_uri="#pf_active/coil",
58+
),
59+
"mutual_loops_passive": dict(
60+
coupling_quantity=1,
61+
rows_uri="#magnetics/flux_loop",
62+
columns_uri="#pf_passive/loop",
63+
),
64+
"mutual_loops_grid": dict(
65+
coupling_quantity=1,
66+
rows_uri="#magnetics/flux_loop",
67+
columns_uri="#pf_plasma/element",
68+
),
69+
"mutual_passive_active": dict(
70+
coupling_quantity=1,
71+
rows_uri="#pf_passive/loop",
72+
columns_uri="#pf_active/coil",
73+
),
74+
"mutual_passive_passive": dict(
75+
coupling_quantity=1,
76+
rows_uri="#pf_passive/loop",
77+
columns_uri="#pf_passive/loop",
78+
),
79+
}
80+
81+
82+
with (
83+
imas.DBEntry(input_uri, "r") as entry,
84+
imas.DBEntry(output_uri, "x", dd_version=target_dd_version) as out,
85+
):
86+
print("Loaded IMAS Data Entry:", input_uri)
87+
88+
print("This data entry contains the following IDSs:")
89+
filled_idss = []
90+
for idsname in entry.factory.ids_names():
91+
occurrences = entry.list_all_occurrences(idsname)
92+
if occurrences:
93+
filled_idss.append(idsname)
94+
print(f"- {idsname}, occurrences: {occurrences}")
95+
print("")
96+
97+
# Load and convert all IDSs (except em_coupling) with imas.convert_ids()
98+
# N.B. we know that the input URI doesn't have multiple occurrences, so
99+
# we do not need to worry about them:
100+
for idsname in filled_idss:
101+
if idsname == "em_coupling":
102+
continue
103+
104+
print(f"Loading IDS: {idsname}...")
105+
ids = entry.get(idsname, autoconvert=False)
106+
print(f"Converting IDS {idsname} to DD {target_dd_version}...")
107+
ids4 = imas.convert_ids(
108+
ids,
109+
target_dd_version,
110+
provenance_origin_uri=input_uri,
111+
)
112+
print(f"Storing IDS {idsname} to output data entry...")
113+
out.put(ids4)
114+
115+
print("Conversion for em_coupling:")
116+
emc = entry.get("em_coupling", autoconvert=False)
117+
print("Using standard convert, this may log warnings about discarding data")
118+
emc4 = imas.convert_ids(
119+
emc,
120+
target_dd_version,
121+
provenance_origin_uri=input_uri,
122+
)
123+
124+
print("Starting custom conversion of the coupling matrices")
125+
for matrix_name, mapping in COUPLING_MAPS.items():
126+
# Skip empty matrices
127+
if not emc[matrix_name].has_value:
128+
continue
129+
130+
# Allocate a new coupling_matrix AoS element
131+
emc4.coupling_matrix.resize(len(emc4.coupling_matrix) + 1, keep=True)
132+
# And fill it
133+
134+
emc4.coupling_matrix[-1].name = matrix_name
135+
# Assigning an integer to the identifier will automatically fill the
136+
# index/name/description. See documentation:
137+
# https://imas-python.readthedocs.io/en/latest/identifiers.html
138+
emc4.coupling_matrix[-1].quantity = mapping["coupling_quantity"]
139+
emc4.coupling_matrix[-1].rows_uri = [mapping["rows_uri"]]
140+
emc4.coupling_matrix[-1].columns_uri = [mapping["columns_uri"]]
141+
emc4.coupling_matrix[-1].data = emc[matrix_name].value
142+
# N.B. the original data has no error_upper/error_lower so we skip these
143+
# Store em_coupling IDS
144+
out.put(emc4)
145+
146+
print("Generating pf_plasma IDS...")
147+
# N.B. This logic is specific to DINA
148+
# Create a new pf_plasma IDS and set basic properties
149+
pf_plasma = out.factory.pf_plasma()
150+
pf_plasma.ids_properties.homogeneous_time = IDS_TIME_MODE_INDEPENDENT
151+
pf_plasma.ids_properties.comment = "PF Plasma generated from equilibrium"
152+
153+
equilibrium = entry.get("equilibrium", lazy=True, autoconvert=False)
154+
r = equilibrium.time_slice[0].profiles_2d[0].grid.dim1
155+
z = equilibrium.time_slice[0].profiles_2d[0].grid.dim2
156+
nr, nz = len(r), len(z)
157+
# Generate a pf_plasma element for each grid point:
158+
pf_plasma.element.resize(nr * nz)
159+
for ir, rval in enumerate(r):
160+
for iz, zval in enumerate(z):
161+
element = pf_plasma.element[ir * nr + iz]
162+
element.geometry.geometry_type = 2 # rectangle
163+
element.geometry.rectangle.r = rval
164+
element.geometry.rectangle.z = zval
165+
# Store pf_plasma IDS
166+
out.put(pf_plasma)
167+
168+
print("Conversion finished")
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
Custom conversion of the ``em_coupling`` IDS
2+
============================================
3+
4+
The ``em_coupling`` IDS has had a big change between Data Dictionary 3.x and Data
5+
Dictionary 4.x. These changes are not covered by the automatic conversions of
6+
:py:meth:`imas.convert_ids <imas.ids_convert.convert_ids>` because these are too
7+
code-specific.
8+
9+
Instead we show on this page an example to convert a DINA dataset from DD 3.38.1 to DD
10+
4.0.0, which can be used as a starting point for converting output data from other codes
11+
as well.
12+
13+
.. literalinclude:: custom_conversion_em_coupling.py

docs/source/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ Manual
5151
cli
5252
netcdf
5353
changelog
54+
examples
5455

5556
.. toctree::
5657
:caption: IMAS-Python training courses

0 commit comments

Comments
 (0)