MADX utilities

Several utilities for interfacing with the MADX program exist in the module madx.utils. The following functions can be used to run MADX scripts:

  • run_file - Runs a bunch of dependent MADX script files together with possibilities for further configuration (see the API docs for more details).

  • run_script - Runs a single script with the possibility for further configuration (see the API docs for more details).

  • run_orm - Compute the orbit response matrix for a given MADX sequence definition.

The resulting files are returned as pandas data frames by default but the functions can be configured to return the raw file contents instead. The stdout and stderr is returned as well.

These functions can also be imported from the particleflow.madx sub-package directly.

Running MADX scripts

For example we can run TWISS computations on one of the example scripts:

>>> from importlib import resources
>>> import os.path
>>> from particleflow.madx import run, run_script, run_orm
>>> import particleflow.test.sequences
>>>
>>> with resources.path(particleflow.test.sequences, 'cryring.seq') as path:
...     result = run_file(path, [], madx=os.path.expanduser('~/bin/madx'))
...
>>> list(result)
['stdout', 'stderr']
>>> result['stderr']
''

result['stdout'] contains the whole echo from the script, which is pretty long, so we won’t print it here. The empty list which was passed to run_file is a list of resulting files that should be retrieved, but since that example script just contained the sequence definition, no files were created anyway. Let’s change that and also remove the echo:

>>> script = resources.read_text(particleflow.test.sequences, 'cryring.seq')
>>> script = 'option, -echo;\n' + script
>>> script += 'twiss, save, file = "twiss";'
>>> result = run_script(script, ['twiss'], madx=os.path.expanduser('~/bin/madx'))

Here we added a TWISS command to the script, which generates the file “twiss” which we then specified as a result in the call to run_script. Let’s check the result now:

>>> list(result)
['stdout', 'stderr', 'twiss']
>>> type(result['twiss'])
<class 'pandas.core.frame.DataFrame'>
>>> result['twiss'].columns
Index(['NAME', 'KEYWORD', 'S', 'BETX', 'ALFX', 'MUX', 'BETY', 'ALFY', 'MUY',
       'X',
       ...
       'SIG54', 'SIG55', 'SIG56', 'SIG61', 'SIG62', 'SIG63', 'SIG64', 'SIG65',
       'SIG66', 'N1'],
      dtype='object', length=256)

The result['twiss'] is a pandas data frame, containing exactly the information from the generates “twiss” file.

Retrieving meta data from output files

If we wanted the meta information, at the beginning of the “twiss” file and prefixed by “@”, as well we can specify the resulting files that we want to retrieve as a dict instead: keys are file names and values are bools, indicating whether we want the meta information for that particular file or not:

>>> result = run_script(script, {'twiss': True}, madx=os.path.expanduser('~/bin/madx'))
>>> type(result['twiss'])
<class 'tuple'>
>>> type(result['twiss'][0])
<class 'pandas.core.frame.DataFrame'>
>>> type(result['twiss'][1])
<class 'dict'>
>>>
>>> from pprint import pprint
>>> pprint(result['twiss'][1])
{'ALFA': 0.1884508489,
 'BCURRENT': 0.0,
 'BETXMAX': 7.14827132,
 'BETYMAX': 7.958073519,
 'BV_FLAG': 1.0,
 'CHARGE': 1.0,
 'DATE': '05/09/19',
 'DELTAP': 0.0,
 'DQ1': -4.394427712,
 'DQ2': -10.92147539,
 'DXMAX': 5.852905623,
 'DXRMS': 4.993097628,
 'DYMAX': 0.0,
 'DYRMS': 0.0,
 'ENERGY': 1.0,
 'ET': 0.001,
 'EX': 1.0,
 'EY': 1.0,
 'GAMMA': 1.065788933,
 'GAMMATR': 2.303567544,
 'KBUNCH': 1.0,
 'LENGTH': 54.17782237,
 'MASS': 0.9382720813,
 'NAME': 'TWISS',
 'NPART': 0.0,
 'ORBIT5': -0.0,
 'ORIGIN': '5.05.02 Linux 64',
 'PARTICLE': 'PROTON',
 'PC': 0.3458981085,
 'Q1': 2.42,
 'Q2': 2.419999999,
 'SEQUENCE': 'CRYRING',
 'SIGE': 0.001,
 'SIGT': 1.0,
 'SYNCH_1': 0.0,
 'SYNCH_2': 0.0,
 'SYNCH_3': 0.0,
 'SYNCH_4': 0.0,
 'SYNCH_5': 0.0,
 'TIME': '13.48.43',
 'TITLE': 'no-title',
 'TYPE': 'TWISS',
 'XCOMAX': 0.0,
 'XCORMS': 0.0,
 'YCOMAX': 0.0,
 'YCORMS': 0.0}

If meta information is requested, the result is a tuple containing the actual file content as a data frame and the meta data as a dict.

Configure scripts before running

Now let’s inspect the resulting data frame, for example the orbit:

>>> twiss = result['twiss'][0]
>>> twiss[['X', 'Y']].describe()
           X      Y
count  184.0  184.0
mean     0.0    0.0
std      0.0    0.0
min      0.0    0.0
25%      0.0    0.0
50%      0.0    0.0
75%      0.0    0.0
max      0.0    0.0

Since the lattice does not contain any zeroth order kicks the orbit is just zero. We can change that by modifying (configuring) the script while we run it. For that purpose we can use the variables parameter. This parameter allows for replacing in variable definition of the form name :?= value; the value with a new_value.

>>> result = run_script(script, ['twiss'], variables={'k02kh': 0.005}, madx=os.path.expanduser('~/bin/madx'))
>>> result['twiss'][['X', 'Y']].describe()
                X      Y
count  184.000000  184.0
mean     0.002166    0.0
std      0.009515    0.0
min     -0.016486    0.0
25%     -0.008191    0.0
50%      0.005374    0.0
75%      0.008827    0.0
max      0.016840    0.0

Since we configured one of the horizontal kickers to have a non-zero kick strength the horizontal orbit changed but the vertical orbit remained zero (no coupling in the lattice).

Compute the Orbit Resonse Matrix

Using the madx.utils.run_orm function we can compute the orbit response matrix for the given lattice, by specifying a list of kicker and monitor labels. The ORM will be computed using these kickers and measured at these monitors:

>>> kickers = ['yr04kh', 'yr06kh', 'yr08kh', 'yr10kh']
>>> monitors = ['yr02dx1', 'yr03dx1', 'yr03dx4']
>>> orm = run_orm(script, kickers=kickers, monitors=monitors, madx=os.path.expanduser('~/bin/madx'))
>>> orm
               X                           Y
         yr02dx1   yr03dx1   yr03dx4 yr02dx1 yr03dx1 yr03dx4
yr04kh  1.092090  1.808234  1.640022     0.0     0.0     0.0
yr06kh -2.951312 -1.395442 -0.751784     0.0     0.0     0.0
yr08kh  3.234832  0.589074 -0.167347     0.0     0.0     0.0
yr10kh -2.705921  0.474549  1.159677     0.0     0.0     0.0
>>>
>>> orm['X']
         yr02dx1   yr03dx1   yr03dx4
yr04kh  1.092090  1.808234  1.640022
yr06kh -2.951312 -1.395442 -0.751784
yr08kh  3.234832  0.589074 -0.167347
yr10kh -2.705921  0.474549  1.159677

Since we chose only horizontal kickers, the vertical ORM is zero (no coupling).