Repository: iterorganization/IMAS-Python

Issue:
Conversion issue between DDv4 and DDv3.42.0 for p' and ff' 1D profiles in equilibrium IDS
I have found a potential issue in the conversion from DDv4 to DDv3.42.0 and vice versa for dp_dpsi and f_df_dpsi profiles in equilibrium IDS.  

When starting from an equilibrium IDS in DDv3.42.0 and converting in to DDv4, the psi profile is expected to get a minus sign when going from COCOS 11 to COCOS 17. This is also observed. It is also expected that profiles of derivatives w.r.t. psi get a minus sign.

However, I noticed that the sign is not changed for the dp_dpsi and f_df_dpsi profiles during conversion.

Hints from issue comments:
@maarten-ic: Thanks @Rick-Fusion for reporting this! It looks like we missed this case in the DD3-4 conversion logic.

We have implemented the sign change in the DD conversion logic for all quantities which have a `cocos_label_transformation='psi_like'`. I just checked the `time_slice/profiles_1d/f_df_dpsi` and `time_slice/profiles_1d/dpressure_dpsi` in equilbirium: these have `cocos_label_transformation='dodpsi_like'`.

@imbeauf: I think we should extend the sign flip when converting between DD3 and DD4 to include both `psi_like` and `dodpsi_like`, do you agree with that?@imbeauf: Yes, this is correct !

Solution patch:
diff --git a/imas/ids_convert.py b/imas/ids_convert.py
index a52db52..e5dc091 100644
--- a/imas/ids_convert.py
+++ b/imas/ids_convert.py
@@ -1,7 +1,6 @@
 # This file is part of IMAS-Python.
 # You should have received the IMAS-Python LICENSE file with this project.
-"""Functionality for converting IDSToplevels between DD versions.
-"""
+"""Functionality for converting IDSToplevels between DD versions."""
 
 import copy
 import datetime
@@ -334,12 +333,13 @@ class DDVersionMap:
         # Additional conversion rules for DDv3 to DDv4
         if self.version_old.major == 3 and new_version and new_version.major == 4:
             # Postprocessing for COCOS definition change:
-            xpath_query = ".//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
+            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
             # Definition change for pf_active circuit/connections
             if self.ids_name == "pf_active":
                 path = "circuit/connections"


Test patch:
diff --git a/imas/test/test_ids_convert.py b/imas/test/test_ids_convert.py
index 750c44e..55045bb 100644
--- a/imas/test/test_ids_convert.py
+++ b/imas/test/test_ids_convert.py
@@ -368,6 +368,26 @@ def test_3to4_cocos_change(dd4factory):
     cp3 = convert_ids(cp4, "3.39.0")
     compare_children(cp, cp3)
 
+    eq = IDSFactory("3.39.0").equilibrium()
+    eq.ids_properties.homogeneous_time = IDS_TIME_MODE_HOMOGENEOUS
+    eq.time = [1.0]
+    eq.time_slice.resize(1)
+    eq.time_slice[0].profiles_1d.psi = numpy.linspace(0, 1, 11)
+    eq.time_slice[0].profiles_1d.dpressure_dpsi = numpy.linspace(1, 2, 11)
+
+    eq4 = convert_ids(eq, None, factory=dd4factory)
+    assert numpy.array_equal(
+        eq4.time_slice[0].profiles_1d.psi,
+        -eq.time_slice[0].profiles_1d.psi,
+    )
+    assert numpy.array_equal(
+        eq4.time_slice[0].profiles_1d.dpressure_dpsi,
+        -eq.time_slice[0].profiles_1d.dpressure_dpsi,
+    )
+
+    eq3 = convert_ids(eq4, "3.39.0")
+    compare_children(eq, eq3)
+
 
 def test_3to4_circuit_connections(dd4factory, caplog):
     pfa = IDSFactory("3.39.0").pf_active()