Skip to content

Commit ffa8b92

Browse files
committed
Merge branch 'develop' into bugfix/IMAS-5560
2 parents 31d739f + 239491b commit ffa8b92

File tree

5 files changed

+130
-19
lines changed

5 files changed

+130
-19
lines changed

ci/run_benchmark.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,9 @@ asv run --skip-existing-successful main^!
5252
# Compare results
5353
if [ `git rev-parse --abbrev-ref HEAD` == develop ]
5454
then
55-
asv compare main develop
55+
asv compare main develop --machine $(hostname) || echo "asv compare failed"
5656
else
57-
asv compare develop HEAD
57+
asv compare develop HEAD --machine $(hostname) || echo "asv compare failed"
5858
fi
5959

6060
# Publish results

conftest.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424

2525
from imaspy.backends.imas_core.imas_interface import has_imas as _has_imas
2626
from imaspy.backends.imas_core.imas_interface import ll_interface, lowlevel
27-
from imaspy.dd_zip import dd_etree, latest_dd_version
27+
from imaspy.dd_zip import dd_etree, dd_xml_versions, latest_dd_version
2828
from imaspy.ids_defs import (
2929
ASCII_BACKEND,
3030
HDF5_BACKEND,
@@ -126,6 +126,14 @@ def latest_factory():
126126
return IDSFactory(latest_version)
127127

128128

129+
@pytest.fixture()
130+
def latest_factory3():
131+
"""Get most recent DDv3 version."""
132+
for version in reversed(dd_xml_versions()):
133+
if version.startswith("3."):
134+
return IDSFactory(version)
135+
136+
129137
# Fixtures for various assets
130138
@pytest.fixture()
131139
def imaspy_assets():

imaspy/ids_convert.py

Lines changed: 62 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,12 @@
2020
from imaspy.ids_data_type import IDSDataType
2121
from imaspy.ids_factory import IDSFactory
2222
from imaspy.ids_path import IDSPath
23-
from imaspy.ids_primitive import IDSNumeric0D, IDSPrimitive, IDSString0D
23+
from imaspy.ids_primitive import (
24+
IDSNumeric0D,
25+
IDSNumericArray,
26+
IDSPrimitive,
27+
IDSString0D,
28+
)
2429
from imaspy.ids_struct_array import IDSStructArray
2530
from imaspy.ids_structure import IDSStructure
2631
from imaspy.ids_toplevel import IDSToplevel
@@ -302,6 +307,12 @@ def add_rename(old_path: str, new_path: str):
302307
self.old_to_new.type_change[old_path] = _repeat_first_point_conditional
303308
closed_path = Path(old_path).parent / "closed"
304309
self.old_to_new.ignore_missing_paths.add(str(closed_path))
310+
elif nbc_description == "remove_last_point_if_open_annular_centreline":
311+
old_path = process_parent_renames(new_path)
312+
self.new_to_old.type_change[new_path] = _repeat_last_point_centreline
313+
self.old_to_new.type_change[old_path] = _remove_last_point_centreline
314+
closed_path = Path(old_path) / "closed"
315+
self.old_to_new.ignore_missing_paths.add(str(closed_path))
305316
else: # Ignore unknown NBC changes
306317
log_args = (nbc_description, new_path)
307318
logger.error("Ignoring unsupported NBC change: %r for %r.", *log_args)
@@ -775,7 +786,11 @@ def _remove_last_point_conditional(
775786
for source, target in iterator:
776787
for child in source.iter_nonempty_():
777788
value = child.value
778-
if closed and child.metadata.name != "time":
789+
if (
790+
closed
791+
and child.metadata.name != "time"
792+
and isinstance(child, IDSNumericArray)
793+
):
779794
# repeat first point:
780795
value = value[:-1]
781796
target[child.metadata.name] = value
@@ -801,14 +816,57 @@ def _repeat_first_point_conditional(
801816
iterator = [(source_node, target_node)]
802817
for source, target in iterator:
803818
for child in source.iter_nonempty_():
804-
if child.metadata.name != "closed":
819+
if child.metadata.name != "closed" and not child.metadata.name.endswith(
820+
"_error_index"
821+
):
805822
value = child.value
806-
if closed and child.metadata.name != "time":
823+
if (
824+
closed
825+
and child.metadata.name != "time"
826+
and isinstance(child, IDSNumericArray)
827+
):
807828
# repeat first point:
808829
value = numpy.concatenate((value, [value[0]]))
809830
target[child.metadata.name] = value
810831

811832

833+
def _remove_last_point_centreline(source_node: IDSBase, target_node: IDSBase) -> None:
834+
"""Type change method for
835+
nbc_description=repeat_children_first_point_conditional_centreline.
836+
837+
This method handles converting from old (DDv3) to new (DDv4).
838+
839+
If a centreline is a closed contour, we should do nothing.
840+
If it is an open contour the thickness variable had too many entries,
841+
and we'll drop the last one.
842+
"""
843+
closed = bool(source_node._parent.centreline.closed)
844+
845+
if closed:
846+
target_node.value = source_node.value
847+
else:
848+
target_node.value = source_node.value[:-1]
849+
850+
851+
def _repeat_last_point_centreline(source_node: IDSBase, target_node: IDSBase) -> None:
852+
"""Type change method for
853+
nbc_description=repeat_children_first_point_conditional_centreline.
854+
855+
This method handles converting from new (DDv4) to old (DDv3).
856+
857+
If a centreline is a closed contour, we should do nothing.
858+
If it is an open contour the thickness variable in the older
859+
dd has one extra entry, so repeat the last one.
860+
"""
861+
closed = bool(target_node._parent.centreline.closed)
862+
if closed:
863+
target_node.value = source_node.value
864+
else:
865+
target_node.value = numpy.concatenate(
866+
(source_node.value, [source_node.value[-1]])
867+
)
868+
869+
812870
def _cocos_change(node: IDSBase) -> None:
813871
"""Handle COCOS definition change: multiply values by -1."""
814872
if not isinstance(node, IDSPrimitive):

imaspy/test/test_ids_convert.py

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
import logging
55
import re
6-
from datetime import UTC, datetime, timedelta
6+
from datetime import datetime, timedelta, timezone
77
from unittest.mock import MagicMock
88

99
import numpy
@@ -29,6 +29,8 @@
2929
from imaspy.ids_structure import IDSStructure
3030
from imaspy.test.test_helpers import compare_children, open_dbentry
3131

32+
UTC = timezone.utc
33+
3234

3335
def test_iter_parents():
3436
assert list(iter_parents("a/b/c/d/e")) == ["a", "a/b", "a/b/c", "a/b/c/d"]
@@ -232,10 +234,10 @@ def test_3to4_ggd_space_identifier(dd4factory):
232234

233235

234236
def test_3to4_repeat_children_first_point_conditional(dd4factory):
235-
# The wall IDS contains all (three!) cases with conditional repeats
237+
# The wall IDS contains all (four!) cases with conditional repeats
236238
wall = IDSFactory("3.39.0").wall()
237239
wall.ids_properties.homogeneous_time = IDS_TIME_MODE_HETEROGENEOUS
238-
wall.description_2d.resize(1)
240+
wall.description_2d.resize(2)
239241

240242
# Case 1: repeat_children_first_point_conditional
241243
wall.description_2d[0].vessel.unit.resize(2)
@@ -264,8 +266,20 @@ def test_3to4_repeat_children_first_point_conditional(dd4factory):
264266
unit.outline[j].z = [-1.0, -2.0, -3.0]
265267
unit.outline[j].time = j / 5
266268

269+
# Case 4: repeat_children_first_point_conditional_centreline
270+
# (see https://jira.iter.org/browse/IMAS-5541)
271+
wall.description_2d[1].vessel.unit.resize(2)
272+
for i in range(2):
273+
centreline = wall.description_2d[1].vessel.unit[i].annular.centreline
274+
centreline.closed = i # first is open, second is closed
275+
centreline.r = [1.0, 2.0, 3.0]
276+
centreline.z = [-1.0, -2.0, -3.0]
277+
# if it was open there were too many thickness values!
278+
# The last one will be dropped and repeated
279+
wall.description_2d[1].vessel.unit[i].annular.thickness = [1, 0.9, 0.9]
280+
267281
wall4 = convert_ids(wall, None, factory=dd4factory)
268-
assert len(wall4.description_2d) == 1
282+
assert len(wall4.description_2d) == 2
269283

270284
# Test conversion for case 1:
271285
assert len(wall4.description_2d[0].vessel.unit) == 2
@@ -302,6 +316,15 @@ def test_3to4_repeat_children_first_point_conditional(dd4factory):
302316
assert numpy.array_equal(unit.outline[j].z, [-1.0, -2.0, -3.0, -1.0])
303317
assert unit.outline[j].time == pytest.approx(j / 5)
304318

319+
# Test conversion for case 4:
320+
assert len(wall4.description_2d[1].vessel.unit) == 2
321+
for i in range(2):
322+
thickness = wall4.description_2d[1].vessel.unit[i].annular.thickness
323+
if i == 0: # open outline, there was one value too many, drop the last one
324+
assert numpy.array_equal(thickness, [1, 0.9])
325+
else: # closed outline, thickness values kept
326+
assert numpy.array_equal(thickness, [1, 0.9, 0.9])
327+
305328
# Test conversion back
306329
wall3 = convert_ids(wall4, "3.39.0")
307330
compare_children(wall, wall3)

imaspy/test/test_nbc_change.py

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -362,25 +362,47 @@ def test_autofill_save_newer(ids_name, backend, worker_id, tmp_path):
362362
dbentry2.close()
363363

364364

365-
def test_convert_min_to_max(ids_name, latest_factory):
365+
def test_convert_min_to_max_v3(ids_name, latest_factory3):
366+
"""Convert from DD 3.22.0 to the last DDv3 release."""
366367
factory = IDSFactory("3.22.0")
367368
if not factory.exists(ids_name):
368369
pytest.skip("IDS %s not defined for version 3.22.0" % (ids_name,))
369-
if not latest_factory.exists(ids_name):
370-
pytest.skip(f"IDS {ids_name} not defined for version {latest_factory.version}")
370+
if not latest_factory3.exists(ids_name):
371+
pytest.skip(f"IDS {ids_name} not defined for version {latest_factory3.version}")
371372

372373
ids = factory.new(ids_name)
373374
fill_with_random_data(ids)
374-
convert_ids(ids, latest_factory.version)
375+
convert_ids(ids, latest_factory3.version)
375376

376377

377-
def test_convert_max_to_min(ids_name, latest_factory):
378+
def test_convert_max_to_min_v3(ids_name, latest_factory3):
379+
"""Convert from the last DDv3 release to DD 3.22.0."""
378380
factory = IDSFactory("3.22.0")
379381
if not factory.exists(ids_name):
380382
pytest.skip(f"IDS {ids_name} not defined for version 3.22.0")
381-
if not latest_factory.exists(ids_name):
382-
pytest.skip(f"IDS {ids_name} not defined for version {latest_factory.version}")
383+
if not latest_factory3.exists(ids_name):
384+
pytest.skip(f"IDS {ids_name} not defined for version {latest_factory3.version}")
383385

384-
ids = latest_factory.new(ids_name)
386+
ids = latest_factory3.new(ids_name)
385387
fill_with_random_data(ids)
386388
convert_ids(ids, None, factory=factory)
389+
390+
391+
def test_convert_3_to_newest(ids_name, latest_factory3, latest_factory):
392+
"""Convert from the last DDv3 release to the last released DD."""
393+
if not latest_factory3.exists(ids_name) or not latest_factory.exists(ids_name):
394+
pytest.skip(f"IDS {ids_name} not defined for both versions.")
395+
396+
ids = latest_factory3.new(ids_name)
397+
fill_with_random_data(ids)
398+
convert_ids(ids, None, factory=latest_factory)
399+
400+
401+
def test_convert_newest_to_3(ids_name, latest_factory3, latest_factory):
402+
"""Convert from the last released DD to the last DDv3 release."""
403+
if not latest_factory3.exists(ids_name) or not latest_factory.exists(ids_name):
404+
pytest.skip(f"IDS {ids_name} not defined for both versions.")
405+
406+
ids = latest_factory.new(ids_name)
407+
fill_with_random_data(ids)
408+
convert_ids(ids, None, factory=latest_factory3)

0 commit comments

Comments
 (0)