From feb0aecc1806cf1bc1544b9f54d3fb58e10be029 Mon Sep 17 00:00:00 2001 From: Felix Soubelet <19598248+fsoubelet@users.noreply.github.com> Date: Tue, 31 May 2022 14:00:09 +0000 Subject: [PATCH] Fix: MAD-X Variable Name Length Limit (#102) --- pylhc/__init__.py | 2 +- pylhc/lsa_to_madx.py | 34 +++++++++++++++++-- ..._05_04_B1_RigidWaitsShift_IP1pos_knob.madx | 34 +++++++++---------- ...S_2022_05_04_B1_RigidWaitsShift_IP1pos.tfs | 2 +- .../inputs/lsa_to_madx/knobs_definitions.txt | 1 + tests/unit/test_lsa_to_madx.py | 15 ++++++-- 6 files changed, 64 insertions(+), 24 deletions(-) diff --git a/pylhc/__init__.py b/pylhc/__init__.py index 2c82115..d36f743 100644 --- a/pylhc/__init__.py +++ b/pylhc/__init__.py @@ -10,7 +10,7 @@ __title__ = "pylhc" __description__ = "An accelerator physics script collection for the OMC team at CERN." __url__ = "https://github.com/pylhc/pylhc" -__version__ = "0.7.1" +__version__ = "0.7.2" __author__ = "pylhc" __author_email__ = "pylhc@github.com" __license__ = "MIT" diff --git a/pylhc/lsa_to_madx.py b/pylhc/lsa_to_madx.py index 56a98e8..d83b440 100644 --- a/pylhc/lsa_to_madx.py +++ b/pylhc/lsa_to_madx.py @@ -33,6 +33,17 @@ Two files, **LHCBEAM_MD_ATS_2022_05_04_B1_RigidWaitsShift_IP1pos_definition.tfs** and **LHCBEAM_MD_ATS_2022_05_04_B1_RigidWaitsShift_IP1pos_knob.madx** will be written to disk. +.. warning:: + In ``MAD-X``, variable names with 48 or more characters will cause an issue. + As a consequence, this script will automatically truncate the knob name if needed when created the trim variable name. + One should not be surprised if long ``LSA`` knob names appear slightly differently in the created ``MAD-X`` files, then functionality stays intact. + + For instance, the knob ``LHCBEAM/MD_ATS_2022_05_04_B1_RigidWaitsShift_IP1pos`` will lead to the following trim variable definition: + + .. code-block:: fortran + + trim_D_ATS_2022_05_04_B1_RigidWaitsShift_IP1pos = 1.0; + In order to reproduce a specific machine configuration at a given time, one can gather all knobs and their trim values for this configuration in a text file and feed this file to the script. In this file, each line should hold a knob name as it appears in LSA and its trim value. Lines starting with a ``#`` character will be ignored. @@ -143,8 +154,7 @@ def get_madx_script_from_definition_dataframe(deltas_df: tfs.TfsDataFrame, lsa_k change_commands = [f"! Start of change commands for knob: {lsa_knob}"] # Set this to 1 by default but can be changed by the user to reproduce a given trim - knob_itself = lsa_knob.split("/")[-1] # without the LHCBEAM[12]?/ part - trim_variable = f"{knob_itself}_trim" + trim_variable = _get_trim_variable(lsa_knob) change_commands.append("! Change this value to reproduce a different trim") change_commands.append(f"! Beware some knobs are not so linear in their trims") change_commands.append(f"{trim_variable} = {trim};") @@ -157,6 +167,26 @@ def get_madx_script_from_definition_dataframe(deltas_df: tfs.TfsDataFrame, lsa_k return "\n".join(change_commands) +def _get_trim_variable(lsa_knob: str) -> str: + """ + Generates the ``MAD-X`` trim variable name from an ``LSA`` knob. + Handles the variable name character limit of ``MAD-X``. + """ + knob_itself = lsa_knob.split("/")[-1] # without the LHCBEAM[12]?/ part + + # MAD-X will crash if the variable name is >48 characters or longer! It will also silently fail + # if the variable name starts with an underscore or a digit. Adding "trim_" at the start circumvents + # the latter two, and we make sure to truncate the knob so that the result is <=47 characters + if len(knob_itself) > 42: + LOG.info(f"Knob '{knob_itself}' is too long to be a MAD-X variable and will be truncated.") + knob_itself = knob_itself[-42:] + LOG.debug(f"Truncated knob name to '{knob_itself}'.") + + trim_variable = f"trim_{knob_itself.lstrip('_')}" + + return trim_variable + + # ----- Script Part ----- # diff --git a/tests/inputs/lsa_to_madx/LHCBEAM_MD_ATS_2022_05_04_B1_RigidWaitsShift_IP1pos_knob.madx b/tests/inputs/lsa_to_madx/LHCBEAM_MD_ATS_2022_05_04_B1_RigidWaitsShift_IP1pos_knob.madx index bb561e1..8e4be12 100644 --- a/tests/inputs/lsa_to_madx/LHCBEAM_MD_ATS_2022_05_04_B1_RigidWaitsShift_IP1pos_knob.madx +++ b/tests/inputs/lsa_to_madx/LHCBEAM_MD_ATS_2022_05_04_B1_RigidWaitsShift_IP1pos_knob.madx @@ -1,20 +1,20 @@ -! Start of change commands for knob: LHCBEAM/MD_ATS_2022_05_04_B1_RigidWaitsShift_IP1pos +! Start of change commands for knob: LHCBEAM/ATS_Test_Knob ! Change this value to reproduce a different trim ! Beware some knobs are not so linear in their trims -MD_ATS_2022_05_04_B1_RigidWaitsShift_IP1pos_trim = 1.0; +trim_ATS_Test_Knob = 1.0; ! Impacted variables -kq9.r1b1 = kq9.r1b1 + ( 0.000139519135701 ) * MD_ATS_2022_05_04_B1_RigidWaitsShift_IP1pos_trim; -kq5.r1b1 = kq5.r1b1 + ( -0.000208665238461 ) * MD_ATS_2022_05_04_B1_RigidWaitsShift_IP1pos_trim; -kq7.l1b1 = kq7.l1b1 + ( -1.65434776136e-05 ) * MD_ATS_2022_05_04_B1_RigidWaitsShift_IP1pos_trim; -kq8.r1b1 = kq8.r1b1 + ( -5.16630898346e-05 ) * MD_ATS_2022_05_04_B1_RigidWaitsShift_IP1pos_trim; -kq8.l1b1 = kq8.l1b1 + ( 4.70197155664e-05 ) * MD_ATS_2022_05_04_B1_RigidWaitsShift_IP1pos_trim; -kq10.r1b1 = kq10.r1b1 + ( -1.96085402422e-05 ) * MD_ATS_2022_05_04_B1_RigidWaitsShift_IP1pos_trim; -kq4.l1b1 = kq4.l1b1 + ( 7.35134890419e-05 ) * MD_ATS_2022_05_04_B1_RigidWaitsShift_IP1pos_trim; -kq4.r1b1 = kq4.r1b1 + ( 4.70408231195e-05 ) * MD_ATS_2022_05_04_B1_RigidWaitsShift_IP1pos_trim; -kq10.l1b1 = kq10.l1b1 + ( -2.47050375037e-05 ) * MD_ATS_2022_05_04_B1_RigidWaitsShift_IP1pos_trim; -kq7.r1b1 = kq7.r1b1 + ( -2.3806014724e-05 ) * MD_ATS_2022_05_04_B1_RigidWaitsShift_IP1pos_trim; -kq9.l1b1 = kq9.l1b1 + ( 0.000118330906844 ) * MD_ATS_2022_05_04_B1_RigidWaitsShift_IP1pos_trim; -kq5.l1b1 = kq5.l1b1 + ( -0.000214221465285 ) * MD_ATS_2022_05_04_B1_RigidWaitsShift_IP1pos_trim; -kq6.r1b1 = kq6.r1b1 + ( 0.000218127839616 ) * MD_ATS_2022_05_04_B1_RigidWaitsShift_IP1pos_trim; -kq6.l1b1 = kq6.l1b1 + ( 0.000125226986711 ) * MD_ATS_2022_05_04_B1_RigidWaitsShift_IP1pos_trim; -! End of change commands for knob: LHCBEAM/MD_ATS_2022_05_04_B1_RigidWaitsShift_IP1pos +kq9.r1b1 = kq9.r1b1 + ( 0.000139519135701 ) * trim_ATS_Test_Knob; +kq5.r1b1 = kq5.r1b1 + ( -0.000208665238461 ) * trim_ATS_Test_Knob; +kq7.l1b1 = kq7.l1b1 + ( -1.65434776136e-05 ) * trim_ATS_Test_Knob; +kq8.r1b1 = kq8.r1b1 + ( -5.16630898346e-05 ) * trim_ATS_Test_Knob; +kq8.l1b1 = kq8.l1b1 + ( 4.70197155664e-05 ) * trim_ATS_Test_Knob; +kq10.r1b1 = kq10.r1b1 + ( -1.96085402422e-05 ) * trim_ATS_Test_Knob; +kq4.l1b1 = kq4.l1b1 + ( 7.35134890419e-05 ) * trim_ATS_Test_Knob; +kq4.r1b1 = kq4.r1b1 + ( 4.70408231195e-05 ) * trim_ATS_Test_Knob; +kq10.l1b1 = kq10.l1b1 + ( -2.47050375037e-05 ) * trim_ATS_Test_Knob; +kq7.r1b1 = kq7.r1b1 + ( -2.3806014724e-05 ) * trim_ATS_Test_Knob; +kq9.l1b1 = kq9.l1b1 + ( 0.000118330906844 ) * trim_ATS_Test_Knob; +kq5.l1b1 = kq5.l1b1 + ( -0.000214221465285 ) * trim_ATS_Test_Knob; +kq6.r1b1 = kq6.r1b1 + ( 0.000218127839616 ) * trim_ATS_Test_Knob; +kq6.l1b1 = kq6.l1b1 + ( 0.000125226986711 ) * trim_ATS_Test_Knob; +! End of change commands for knob: LHCBEAM/ATS_Test_Knob diff --git a/tests/inputs/lsa_to_madx/MD_ATS_2022_05_04_B1_RigidWaitsShift_IP1pos.tfs b/tests/inputs/lsa_to_madx/MD_ATS_2022_05_04_B1_RigidWaitsShift_IP1pos.tfs index 997be6b..fb02165 100644 --- a/tests/inputs/lsa_to_madx/MD_ATS_2022_05_04_B1_RigidWaitsShift_IP1pos.tfs +++ b/tests/inputs/lsa_to_madx/MD_ATS_2022_05_04_B1_RigidWaitsShift_IP1pos.tfs @@ -1,4 +1,4 @@ -@ Knob %s "LHCBEAM/MD_ATS_2022_05_04_B1_RigidWaitsShift_IP1pos" +@ Knob %s "LHCBEAM/ATS_Test_Knob" @ Optics %s "R2022a_A30cmC30cmA10mL200cm" @ Info %s "In MAD-X it should be 'name = name + DELTA * knobValue'" * INDEX&&& CIRCUIT DELTA_K diff --git a/tests/inputs/lsa_to_madx/knobs_definitions.txt b/tests/inputs/lsa_to_madx/knobs_definitions.txt index e4679fc..fc3e24b 100644 --- a/tests/inputs/lsa_to_madx/knobs_definitions.txt +++ b/tests/inputs/lsa_to_madx/knobs_definitions.txt @@ -1,3 +1,4 @@ +# For optics R2017aT_A30C30A10mL300_CTPPS2 #Nonlinear LHCBEAM/2017_IRNL_IR1a4 0.5 LHCBEAM/2017_IRNL_IR1b3_couplFD 1.0 diff --git a/tests/unit/test_lsa_to_madx.py b/tests/unit/test_lsa_to_madx.py index 562d444..b80b565 100644 --- a/tests/unit/test_lsa_to_madx.py +++ b/tests/unit/test_lsa_to_madx.py @@ -10,6 +10,7 @@ from pandas._testing import assert_dict_equal from pylhc.lsa_to_madx import ( + _get_trim_variable, get_madx_script_from_definition_dataframe, parse_knobs_and_trim_values_from_file, ) @@ -26,10 +27,18 @@ def test_parse_knob_definition_file(self, knobs_file, parsed_definitions): class TestMADXWriting: def test_madx_script_writing_from_definition_df(self, knob_definition_df, correct_madx_script): - script = get_madx_script_from_definition_dataframe( - knob_definition_df, lsa_knob="LHCBEAM/MD_ATS_2022_05_04_B1_RigidWaitsShift_IP1pos" + script = get_madx_script_from_definition_dataframe(knob_definition_df, lsa_knob="LHCBEAM/ATS_Test_Knob") + assert script == correct_madx_script + + @pytest.mark.parametrize("lsa_knob", ["LHCBEAM/Super_Duper_Long_Name_For_A_Knob_Will_Be_Truncated_For_Sure", "ATS_Test_Knob"]) + def test_trim_variable_from_long_knob_name(self, lsa_knob): + """Testing that the generated trim variable is correctly truncated if too long.""" + assert ( + _get_trim_variable("ATS_2022_05_08_B1_arc_by_arc_coupling_133cm_30cm") + == "trim_22_05_08_B1_arc_by_arc_coupling_133cm_30cm" ) - assert script == correct_madx_script # TODO: figure why this doesn't work? + assert _get_trim_variable("___knob") == "trim_knob" # make sure we handle several underscores + assert len(_get_trim_variable(lsa_knob)) < 48 # ----- Fixtures ----- #