diff --git a/imas/ids_convert.py b/imas/ids_convert.py index 75359f8..559e8a3 100644 --- a/imas/ids_convert.py +++ b/imas/ids_convert.py @@ -336,13 +336,18 @@ def add_rename(old_path: str, new_path: str): def _apply_3to4_conversion(self, old: Element, new: Element) -> None: # Postprocessing for COCOS definition change: + cocos_paths = [] for psi_like in ["psi_like", "dodpsi_like"]: xpath_query = f".//field[@cocos_label_transformation='{psi_like}']" for old_item in old.iterfind(xpath_query): - old_path = old_item.get("path") - new_path = self.old_to_new.path.get(old_path, old_path) - self.new_to_old.post_process[new_path] = _cocos_change - self.old_to_new.post_process[old_path] = _cocos_change + cocos_paths.append(old_item.get("path")) + # Sign flips not covered by the generic rule: + cocos_paths.extend(_3to4_sign_flip_paths.get(self.ids_name, [])) + for old_path in cocos_paths: + new_path = self.old_to_new.path.get(old_path, old_path) + self.new_to_old.post_process[new_path] = _cocos_change + self.old_to_new.post_process[old_path] = _cocos_change + # Definition change for pf_active circuit/connections if self.ids_name == "pf_active": path = "circuit/connections" @@ -676,6 +681,113 @@ def _copy_structure( callback(item, target_item) +_3to4_sign_flip_paths = { + "core_instant_changes": [ + "change/profiles_1d/grid/psi_magnetic_axis", + "change/profiles_1d/grid/psi_boundary", + ], + "core_profiles": [ + "profiles_1d/grid/psi_magnetic_axis", + "profiles_1d/grid/psi_boundary", + ], + "core_sources": [ + "source/profiles_1d/grid/psi_magnetic_axis", + "source/profiles_1d/grid/psi_boundary", + ], + "core_transport": [ + "model/profiles_1d/grid_d/psi_magnetic_axis", + "model/profiles_1d/grid_d/psi_boundary", + "model/profiles_1d/grid_v/psi_magnetic_axis", + "model/profiles_1d/grid_v/psi_boundary", + "model/profiles_1d/grid_flux/psi_magnetic_axis", + "model/profiles_1d/grid_flux/psi_boundary", + ], + "disruption": [ + "global_quantities/psi_halo_boundary", + "profiles_1d/grid/psi_magnetic_axis", + "profiles_1d/grid/psi_boundary", + ], + "ece": [ + "channel/beam_tracing/beam/position/psi", + "psi_normalization/psi_magnetic_axis", + "psi_normalization/psi_boundary", + ], + "edge_profiles": [ + "profiles_1d/grid/psi", + "profiles_1d/grid/psi_magnetic_axis", + "profiles_1d/grid/psi_boundary", + ], + "equilibrium": [ + "time_slice/boundary/psi", + "time_slice/global_quantities/q_min/psi", + "time_slice/ggd/psi/values", + ], + "mhd": ["ggd/psi/values"], + "pellets": ["time_slice/pellet/path_profiles/psi"], + "plasma_profiles": [ + "profiles_1d/grid/psi", + "profiles_1d/grid/psi_magnetic_axis", + "profiles_1d/grid/psi_boundary", + "ggd/psi/values", + ], + "plasma_sources": [ + "source/profiles_1d/grid/psi", + "source/profiles_1d/grid/psi_magnetic_axis", + "source/profiles_1d/grid/psi_boundary", + ], + "plasma_transport": [ + "model/profiles_1d/grid_d/psi", + "model/profiles_1d/grid_d/psi_magnetic_axis", + "model/profiles_1d/grid_d/psi_boundary", + "model/profiles_1d/grid_v/psi", + "model/profiles_1d/grid_v/psi_magnetic_axis", + "model/profiles_1d/grid_v/psi_boundary", + "model/profiles_1d/grid_flux/psi", + "model/profiles_1d/grid_flux/psi_magnetic_axis", + "model/profiles_1d/grid_flux/psi_boundary", + ], + "radiation": [ + "process/profiles_1d/grid/psi_magnetic_axis", + "process/profiles_1d/grid/psi_boundary", + ], + "reflectometer_profile": [ + "psi_normalization/psi_magnetic_axis", + "psi_normalization/psi_boundary", + ], + "reflectometer_fluctuation": [ + "psi_normalization/psi_magnetic_axis", + "psi_normalization/psi_boundary", + ], + "runaway_electrons": [ + "profiles_1d/grid/psi_magnetic_axis", + "profiles_1d/grid/psi_boundary", + ], + "sawteeth": [ + "profiles_1d/grid/psi_magnetic_axis", + "profiles_1d/grid/psi_boundary", + ], + "summary": [ + "global_quantities/psi_external_average/value", + "local/magnetic_axis/position/psi", + ], + "transport_solver_numerics": [ + "solver_1d/grid/psi_magnetic_axis", + "solver_1d/grid/psi_boundary", + "derivatives_1d/grid/psi_magnetic_axis", + "derivatives_1d/grid/psi_boundary", + ], + "wall": ["description_ggd/ggd/psi/values"], + "waves": [ + "coherent_wave/profiles_1d/grid/psi_magnetic_axis", + "coherent_wave/profiles_1d/grid/psi_boundary", + "coherent_wave/profiles_2d/grid/psi", + "coherent_wave/beam_tracing/beam/position/psi", + ], +} +"""List of paths per IDS that require a COCOS sign change, but aren't covered by the +generic rule.""" + + ######################################################################################## # Type changed handlers and post-processing functions # ######################################################################################## diff --git a/imas/test/test_ids_convert.py b/imas/test/test_ids_convert.py index 826a797..af6f3a5 100644 --- a/imas/test/test_ids_convert.py +++ b/imas/test/test_ids_convert.py @@ -12,12 +12,14 @@ from imas import identifiers from imas.ids_convert import ( + _3to4_sign_flip_paths, _get_ctxpath, _get_tbp, convert_ids, dd_version_map_from_factories, iter_parents, ) +from imas.ids_data_type import IDSDataType from imas.ids_defs import ( ASCII_BACKEND, IDS_TIME_MODE_HETEROGENEOUS, @@ -529,3 +531,22 @@ def test_3to4_migrate_deprecated_fields(): # GH#55 del cp342.profiles_1d[0].ion[0].label cp4 = convert_ids(cp342, "4.0.0") assert cp4.profiles_1d[0].ion[0].name == "y" + + +def test_3to4_cocos_hardcoded_paths(): + # Check for existence in 3.42.0 + factory = IDSFactory("3.42.0") + for ids_name, paths in _3to4_sign_flip_paths.items(): + ids = factory.new(ids_name) + for path in paths: + # Check path exists and is not a FLT + metadata = ids.metadata[path] + assert metadata.data_type is IDSDataType.FLT + + # Test a conversion + eq = factory.equilibrium() + eq.time_slice.resize(1) + eq.time_slice[0].boundary.psi = 3.141 + + eq4 = convert_ids(eq, "4.0.0") + assert eq4.time_slice[0].boundary.psi == -3.141