From ba377c719b06c4c2f20a81d5a26fafea5c6618b7 Mon Sep 17 00:00:00 2001
From: Yngve Levinsen <yngve.levinsen@ess.eu>
Date: Wed, 11 Nov 2020 07:32:44 +0100
Subject: [PATCH] ess.TraceWin.project added first rule

---
 ess/TraceWin.py                               |  37 +-
 .../tw_project_file_reverse_engineered.json   | 480 +++++++++---------
 2 files changed, 281 insertions(+), 236 deletions(-)

diff --git a/ess/TraceWin.py b/ess/TraceWin.py
index 0ee742e..5357b3a 100644
--- a/ess/TraceWin.py
+++ b/ess/TraceWin.py
@@ -1510,9 +1510,9 @@ class project:
         import pkg_resources
 
         if settings_fname is None:
-            self._refdict = json.loads(pkg_resources.resource_string(__name__, "data/tw_project_file_reverse_engineered.json"))
+            self._refdict, self._rules = json.loads(pkg_resources.resource_string(__name__, "data/tw_project_file_reverse_engineered.json"))
         else:
-            self._refdict = json.loads(open(self._settings_fname, "r").read())
+            self._refdict, self._rules = json.loads(open(self._settings_fname, "r").read())
 
         self._settings_fname = settings_fname
         self._project_fname = project_fname
@@ -1593,6 +1593,36 @@ class project:
                 raise ValueError(f"{parameter} should be one of {opts}")
         self._dict[parameter] = value
 
+    def _check_rule_same_sign(self, variables, explanation, fail_on_err):
+        import numpy
+
+        s1 = numpy.sign(self.get(variables[0]))
+        for i in range(1, len(variables)):
+            s2 = numpy.sign(self.get(variables[i]))
+            print("DBG", s1, s2)
+            if s1 != s2:
+                errmsg = f"{variables[i]} and {variables[0]} have opposite signs\nExplanation/logic: {explanation}"
+                if fail_on_err:
+                    raise ValueError(errmsg)
+                else:
+                    print(errmsg)
+
+    def check_rule(self, rule=None, fail_on_err=False):
+        """
+        Validate that we still obey the rule
+        if rule is not given, check all rules
+        """
+        if rule is None:
+            rules = self._rules
+        else:
+            rules = [rule]
+
+        for r in rules:
+            if r[0] == "same-sign":
+                self._check_rule_same_sign(r[1], r[2], fail_on_err)
+            else:
+                raise TypeError(f"Unknown rule {r[0]}")
+
     def save(self, fname=None):
         """
         Save the project file
@@ -1602,6 +1632,9 @@ class project:
         import struct
         from textwrap import wrap
 
+        for rule in self._rules:
+            self.check_rule(rule, fail_on_err=True)
+
         if fname is None:
             fname = self._project_fname
 
diff --git a/ess/data/tw_project_file_reverse_engineered.json b/ess/data/tw_project_file_reverse_engineered.json
index 5b9baf6..bc4a74a 100644
--- a/ess/data/tw_project_file_reverse_engineered.json
+++ b/ess/data/tw_project_file_reverse_engineered.json
@@ -1,238 +1,250 @@
-{
-  "partran": [
-    109,
-    "?",
-    1
-  ],
-  "toutatis": [
-    135,
-    "?",
-    1
-  ],
-  "match:input_matched_beam": [
-    128,
-    "?",
-    1
-  ],
-  "match:family_twiss": [
-    113,
-    "?",
-    1
-  ],
-  "match:diag": [
-    118,
-    "?",
-    1
-  ],
-  "match:limit_criterion": [
-    12260,
-    "d",
-    8
-  ],
-  "main:beam1_npart": [
-    11984,
-    "i",
-    4
-  ],
-  "main:beam2_npart": [
-    11988,
-    "i",
-    4
-  ],
-  "main:beam1_energy": [
-    12100,
-    "d",
-    8
-  ],
-  "main:beam1_freq": [
-    12068,
-    "d",
-    8
-  ],
-  "main:beam1_curr": [
-    12084,
-    "d",
-    8
-  ],
-  "main:beam1_dcycle": [
-    12648,
-    "d",
-    8
-  ],
-  "main:beam1_emit_xxp": [
-    12116,
-    "d",
-    8
-  ],
-  "main:beam1_emit_yyp": [
-    12132,
-    "d",
-    8
-  ],
-  "main:beam1_rms_energy_spread": [
-    22752,
-    "d",
-    8
-  ],
-  "main:beam1_twiss_alphaz": [
-    12572,
-    "d",
-    8
-  ],
-  "main:beam1_twiss_betaz": [
-    12580,
-    "d",
-    8
-  ],
-  "mp:partr_sc_opt_routine": [
-    [
-      119,
-      125,
-      12607,
-      22704
+[
+  {
+    "partran": [
+      109,
+      "?",
+      1
+    ],
+    "toutatis": [
+      135,
+      "?",
+      1
+    ],
+    "match:input_matched_beam": [
+      128,
+      "?",
+      1
+    ],
+    "match:family_twiss": [
+      113,
+      "?",
+      1
+    ],
+    "match:diag": [
+      118,
+      "?",
+      1
+    ],
+    "match:limit_criterion": [
+      12260,
+      "d",
+      8
+    ],
+    "main:beam1_npart": [
+      11984,
+      "i",
+      4
+    ],
+    "main:beam2_npart": [
+      11988,
+      "i",
+      4
+    ],
+    "main:beam1_energy": [
+      12100,
+      "d",
+      8
+    ],
+    "main:beam1_freq": [
+      12068,
+      "d",
+      8
+    ],
+    "main:beam1_curr": [
+      12084,
+      "d",
+      8
+    ],
+    "main:beam1_dcycle": [
+      12648,
+      "d",
+      8
+    ],
+    "main:beam1_emit_xxp": [
+      12116,
+      "d",
+      8
+    ],
+    "main:beam1_emit_yyp": [
+      12132,
+      "d",
+      8
+    ],
+    "main:beam1_rms_energy_spread": [
+      22752,
+      "d",
+      8
+    ],
+    "main:beam1_twiss_alphaz": [
+      12572,
+      "d",
+      8
+    ],
+    "main:beam1_twiss_betaz": [
+      12580,
+      "d",
+      8
+    ],
+    "mp:partr_sc_opt_routine": [
+      [
+        119,
+        125,
+        12607,
+        22704
+      ],
+      "bool:list",
+      1,
+      [
+        "picnic",
+        "picnir",
+        "ce_cyl",
+        "my_sc"
+      ]
+    ],
+    "mp:partr_sc_calcstep": [
+      12616,
+      "i",
+      4
+    ],
+    "mp:partr_sc_scstep": [
+      9876,
+      "i",
+      4
+    ],
+    "match:input_matched_beam_wpartran": [
+      20200,
+      "?",
+      1
+    ],
+    "match:family_twiss_wpartran": [
+      20201,
+      "?",
+      1
+    ],
+    "match:diag_wpartran": [
+      20202,
+      "?",
+      1
     ],
-    "bool:list",
-    1,
+    "mp:partr_sc_picnir_mesh_long": [
+      12032,
+      "i",
+      4
+    ],
+    "mp:partr_sc_picnir_mesh_transv": [
+      12028,
+      "i",
+      4
+    ],
+    "mp:partr_sc_picnic_mesh_transv": [
+      12036,
+      "i",
+      4
+    ],
+    "mp:partr_sc_picnic_mesh_long": [
+      12040,
+      "i",
+      4
+    ],
+    "mp:partr_sc_picnic_weight": [
+      12052,
+      "i",
+      4
+    ],
+    "mp:partr_sc_picnic_meshrms": [
+      12244,
+      "d",
+      8
+    ],
+    "mp:partr_sc_picnic_skip": [
+      12056,
+      "i",
+      4
+    ],
+    "err:study_envelope": [
+      114,
+      "?",
+      1
+    ],
+    "err:study_multipart": [
+      111,
+      "?",
+      1
+    ],
+    "err:beamref": [
+      8737,
+      "?",
+      1
+    ],
+    "mp:input_distr": [
+      8732,
+      "i",
+      4
+    ],
+    "mp:input_distr_mask_transv": [
+      8728,
+      "i",
+      4
+    ],
+    "mp:input_distr_mask_long": [
+      8724,
+      "i",
+      4
+    ],
+    "mp:partr_enerphase_lost_eesynchr": [
+      12632,
+      "d",
+      8
+    ],
+    "mp:partr_enerphase_lost_p-psynchr": [
+      12640,
+      "d",
+      8
+    ],
+    "mp:partr_enerphase_excl_e-esynchr": [
+      12672,
+      "d",
+      8
+    ],
+    "mp:partr_enerphase_excl_p-psynchr": [
+      12680,
+      "d",
+      8
+    ],
+    "mp:plt_includedistr_lastelmt": [
+      120,
+      "?",
+      1
+    ],
+    "mp:plt_includedistr_eachperiod": [
+      121,
+      "?",
+      1
+    ],
+    "mp:plt_includedistr_lossfile": [
+      22768,
+      "?",
+      1
+    ],
+    "mp:plt_includedistr_stoplost": [
+      23275,
+      "?",
+      1
+    ],
+    "mp:multithread": [
+      20248,
+      "?",
+      1
+    ]
+  },
+  [
     [
-      "picnic",
-      "picnir",
-      "ce_cyl",
-      "my_sc"
+      "same-sign",
+      [
+        "mp:partr_sc_scstep",
+        "mp:partr_sc_calcstep"
+      ],
+      "per beta/lambda if negative, per metre if positive"
     ]
-  ],
-  "mp:partr_sc_calcstep": [
-    12616,
-    "i",
-    4
-  ],
-  "mp:partr_sc_scstep": [
-    9876,
-    "i",
-    4
-  ],
-  "match:input_matched_beam_wpartran": [
-    20200,
-    "?",
-    1
-  ],
-  "match:family_twiss_wpartran": [
-    20201,
-    "?",
-    1
-  ],
-  "match:diag_wpartran": [
-    20202,
-    "?",
-    1
-  ],
-  "mp:partr_sc_picnir_mesh_long": [
-    12032,
-    "i",
-    4
-  ],
-  "mp:partr_sc_picnir_mesh_transv": [
-    12028,
-    "i",
-    4
-  ],
-  "mp:partr_sc_picnic_mesh_transv": [
-    12036,
-    "i",
-    4
-  ],
-  "mp:partr_sc_picnic_mesh_long": [
-    12040,
-    "i",
-    4
-  ],
-  "mp:partr_sc_picnic_weight": [
-    12052,
-    "i",
-    4
-  ],
-  "mp:partr_sc_picnic_meshrms": [
-    12244,
-    "d",
-    8
-  ],
-  "mp:partr_sc_picnic_skip": [
-    12056,
-    "i",
-    4
-  ],
-  "err:study_envelope": [
-    114,
-    "?",
-    1
-  ],
-  "err:study_multipart": [
-    111,
-    "?",
-    1
-  ],
-  "err:beamref": [
-    8737,
-    "?",
-    1
-  ],
-  "mp:input_distr": [
-    8732,
-    "i",
-    4
-  ],
-  "mp:input_distr_mask_transv": [
-    8728,
-    "i",
-    4
-  ],
-  "mp:input_distr_mask_long": [
-    8724,
-    "i",
-    4
-  ],
-  "mp:partr_enerphase_lost_eesynchr": [
-    12632,
-    "d",
-    8
-  ],
-  "mp:partr_enerphase_lost_p-psynchr": [
-    12640,
-    "d",
-    8
-  ],
-  "mp:partr_enerphase_excl_e-esynchr": [
-    12672,
-    "d",
-    8
-  ],
-  "mp:partr_enerphase_excl_p-psynchr": [
-    12680,
-    "d",
-    8
-  ],
-  "mp:plt_includedistr_lastelmt": [
-    120,
-    "?",
-    1
-  ],
-  "mp:plt_includedistr_eachperiod": [
-    121,
-    "?",
-    1
-  ],
-  "mp:plt_includedistr_lossfile": [
-    22768,
-    "?",
-    1
-  ],
-  "mp:plt_includedistr_stoplost": [
-    23275,
-    "?",
-    1
-  ],
-  "mp:multithread": [
-    20248,
-    "?",
-    1
   ]
-}
+]
-- 
GitLab