Skip to content

Commit deb2c17

Browse files
author
Deepak Mewar
committed
[GH#59] Convert name/identifier(DD3) to description/name(DD4)
Signed-off-by: Deepak Mewar <deepak.mewar@iter.org>
1 parent bcd47fe commit deb2c17

File tree

2 files changed

+116
-0
lines changed

2 files changed

+116
-0
lines changed

imas/ids_convert.py

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,85 @@ def _apply_3to4_conversion(self, old: Element, new: Element) -> None:
386386
to_update[p] = v
387387
self.old_to_new.path.update(to_update)
388388

389+
# GH#59: To improve further the conversion of DD3 to DD4, especially the
390+
# Machine Description part of the IDSs, we would like to add a 3to4 specific
391+
# rule to convert any siblings name + identifier (that are not part of an
392+
# identifier structure, meaning that there is no index sibling) into
393+
# description + name. Meaning:
394+
# parent/name (DD3) -> parent/description (DD4)
395+
# parent/identifier (DD3) -> parent/name (DD4)
396+
# Only perform the mapping if the corresponding target fields exist in the
397+
# new DD and if we don't already have a mapping for the involved paths.
398+
try:
399+
old_paths = {f.get("path"): f for f in old.iterfind(".//field")}
400+
new_paths = {f.get("path"): f for f in new.iterfind(".//field")}
401+
except Exception:
402+
old_paths = {}
403+
new_paths = {}
404+
405+
for p in list(old_paths.keys()):
406+
# look for name children
407+
if not p.endswith("/name"):
408+
continue
409+
parent = p.rsplit("/", 1)[0]
410+
name_path = f"{parent}/name"
411+
id_path = f"{parent}/identifier"
412+
index_path = f"{parent}/index"
413+
desc_path = f"{parent}/description"
414+
new_name_path = f"{parent}/name"
415+
416+
if name_path not in old_paths or id_path not in old_paths:
417+
continue
418+
# exclude identifier-structure (has index sibling)
419+
if index_path in old_paths:
420+
continue
421+
422+
# Ensure the candidate target fields exist in the new DD
423+
if desc_path not in new_paths or new_name_path not in new_paths:
424+
continue
425+
426+
# Map DD3 name -> DD4 description
427+
if name_path not in self.old_to_new.path:
428+
old_item = old_paths[name_path]
429+
new_item = new_paths[desc_path]
430+
self.old_to_new.path[name_path] = desc_path
431+
self.old_to_new.tbp[name_path] = _get_tbp(new_item, new_paths)
432+
self.old_to_new.ctxpath[name_path] = _get_ctxpath(desc_path, new_paths)
433+
# reciprocal mapping
434+
if desc_path not in self.new_to_old.path:
435+
self.new_to_old.path[desc_path] = name_path
436+
self.new_to_old.tbp[desc_path] = _get_tbp(old_item, old_paths)
437+
self.new_to_old.ctxpath[desc_path] = _get_ctxpath(
438+
name_path, old_paths
439+
)
440+
logger.info(
441+
"Renamed DD3 name to DD4 description %s,",
442+
old_item.get("path"),
443+
)
444+
445+
# Map DD3 identifier -> DD4 name
446+
if id_path in self.old_to_new.path:
447+
old_item_id = old_paths[id_path]
448+
new_item_name = new_paths[new_name_path]
449+
self.old_to_new.path[id_path] = new_name_path
450+
self.old_to_new.tbp[id_path] = _get_tbp(new_item_name, new_paths)
451+
self.old_to_new.ctxpath[id_path] = _get_ctxpath(
452+
new_name_path, new_paths
453+
)
454+
# reciprocal mapping
455+
if new_name_path not in self.new_to_old.path:
456+
self.new_to_old.path[new_name_path] = id_path
457+
self.new_to_old.tbp[new_name_path] = _get_tbp(
458+
old_item_id, old_paths
459+
)
460+
self.new_to_old.ctxpath[new_name_path] = _get_ctxpath(
461+
id_path, old_paths
462+
)
463+
logger.info(
464+
"Renamed DD3 identifier to DD4 name %s,",
465+
old_item_id.get("path"),
466+
)
467+
389468
def _map_missing(self, is_new: bool, missing_paths: Set[str]):
390469
rename_map = self.new_to_old if is_new else self.old_to_new
391470
# Find all structures which have a renamed sub-item

imas/test/test_ids_convert.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -529,3 +529,40 @@ def test_3to4_migrate_deprecated_fields(): # GH#55
529529
del cp342.profiles_1d[0].ion[0].label
530530
cp4 = convert_ids(cp342, "4.0.0")
531531
assert cp4.profiles_1d[0].ion[0].name == "y"
532+
533+
534+
def test_3to4_name_identifier_mapping_cores_non_axisymmetric():
535+
# Create source IDS using DD 3.39.0
536+
pfa = IDSFactory("3.39.0")
537+
if "cores_non_axisymmetric" not in pfa.ids_names():
538+
pytest.skip("core_non_axisymmetric not present in DD 3.39.0 on this system")
539+
src = pfa.cores_non_axisymmetric()
540+
# Populate a parent that has name + identifier (no 'index' sibling)
541+
src.coil.resize(1)
542+
src.coil[0].name = "TEST_NAME"
543+
src.coil[0].identifier = "TEST_IDENTIFIER"
544+
545+
# Convert to DD 4.0.0
546+
dst = convert_ids(src, "4.0.0")
547+
548+
# Locate the destination node and assert mapping:
549+
try:
550+
dst4 = dst.core[0].name
551+
except Exception:
552+
pytest.skip("destination IDS structure does not contain core/name")
553+
554+
# DD3 name -> DD4 description
555+
if hasattr(dst4, "description"):
556+
assert dst4.description == "TEST_NAME"
557+
else:
558+
pytest.skip(
559+
"DD4 destination does not have 'description' on this node - cannot verify name->description mapping"
560+
)
561+
562+
# DD3 identifier -> DD4 name
563+
if hasattr(dst4, "name"):
564+
assert dst4.name == "TEST_IDENTIFIER"
565+
else:
566+
pytest.skip(
567+
"DD4 destination does not have 'name' on this node - cannot verify identifier->name mapping"
568+
)

0 commit comments

Comments
 (0)