Getting Started

The basic way to use cpymad is to create a Madx instance that can be used to control and access the state of a MAD-X process:

from cpymad.madx import Madx
madx = Madx()

This spawns a MAD-X process in the background and opens a communication channel to it.

Running MAD-X in a separate process is necessary to create multiple instances with independent interpreter state. This is useful for resetting MAD-X to a clean initial state by simply creating a new instance; and is a requirement for parallelization (because MAD-X is not thread safe).

Basic MAD-X commands

Now that you started MAD-X, most MAD-X commands can be executed using the corresponding method on the Madx instance (except in the case where the name of the command conflicts with another property, in this case, see the command() property). For example:

madx.option(echo=True)

madx.call(file='/path/to/some/input_file.madx')

madx.twiss(
    sequence='LEBT',
    betx=0.1, bety=0.1,
    alfx=0.1, alfy=0.1)

In general, parameters must be passed by their name, exactly as for the MAD-X command. Flags should be passed as python bools, deferred expressions (:=) as strings, and direct expressions (=) as floats using eval().

Most of the command methods are auto-generated by introspecting the corresponding MAD-X command object. Only a few of the methods provide additional functionality over the raw MAD-X commands:

call() allows to temporarily change the directory to the one of the executed file by setting the optional chdir parameter to True:

# change directory to `/path/to/your/` during CALL:
madx.call('/path/to/your/file.madx', chdir=True)

twiss() returns the resulting twiss table, which can be used conveniently for your own analysis, e.g.:

twiss = madx.twiss(sequence='LEBT', betx=1, bety=1)

import matplotlib.pyplot as plt
plt.plot(twiss.s, twiss.betx)
plt.show()

survey() returns the resulting survey table, similar to twiss().

Controlling MAD-X

The Madx class works by feeding commands in the form of textual input to the MAD-X process. This means that you can execute all MAD-X commands, even if they are not explicitly defined on the python class level.

input()

The method responsible for feeding textual input to MAD-X is input() method. It is called with a single string argument that will be forwarded as input to the MAD-X interpreter. For example:

madx.input('CALL, FILE="fodo.madx";')

Do not input anything but simple single line commands, no comments.

command()

While it can be necessary to use input() for some constructs like macros or loops, most of the time your most favorable option is to use the command attribute. It provides syntactic sugar for composing regular MAD-X commands from python variables and feeding the generated command string to input():

madx.command.beam(sequence='fodo', particle='PROTON')

If you need to override how command generates the command string (argument order/formatting), you can pass strings as positional arguments. For example:

madx.command.beam('sequence=fodo', particle='PROTON')

Note that positional and keyword parameters can be mixed.

A single trailing underscore will be stripped from the attribute name. This is useful for MAD-X commands that are python keywords:

madx.command.global_(sequence='cassps', Q1=26.58)

In order to clone a command or element (colon syntax in MAD-X), use the clone() method:

madx.command.quadrupole.clone('QP', AT=2, L=1)

which translates to the MAD-X command:

QP: QUADRUPOLE, AT=2, L=1;

chdir()

chdir() changes the directory of the MAD-X process (not the current python process). If the return value can be used as a context manager, that reverts to the original directory upon leaving the context.

Others

At this point, you should be able to execute arbitrary MAD-X commands via cpymad.

All other methods for controlling MAD-X are just syntactic sugar for input(). Among others, this has the following main benefits:

  • every modification of the MAD-X state is transparent from the command_log file

  • the session should be reproducible using the official madx command line client by the commands in the command_log file.

  • reduces the need for special implementations on the cython binding by always going through the same interface.

More methods for changing state:

Accessing MAD-X

In contrast to how cpymad is controlling the MAD-X state, when accessing state it does not use MAD-X commands, but rather directly retrieves the data from the C variables in the MAD-X process memory!

This means that data retrieval is relatively fast because it does not:

  • parse command in the MAD-X interpreter

  • use a file on disk or the network

  • parse resulting data on python side

  • to potentially modify the MAD-X interpreter state by executing a command

Apart from this major advantage, another important implication is that the command_log file will not be cluttered by data-retrieval commands but only show actions.

version

Access the MAD-X version:

print(madx.version)
# individual parts
print(madx.version.date)
print(madx.version.release)
# or as tuple:
print(madx.version.info >= (5, 3, 6))

elements

Access to global elements:

# list of element names:
print(list(madx.elements))

# check whether an element is defined:
print('qp1' in madx.elements)

# get element properties:
elem = madx.elements.qp1
print(elem.k1)
print(elem.l)

Note that elements support dict-like item access to retrieve properties (i.e. elem['k1']) besides attribute access. The same is true in other places.

table

View of MAD-X tables:

# list of existing table names
print(list(madx.table)):

# get table as dict-like object:
twiss = madx.table.twiss

# get columns as numpy arrays:
alfx = twiss.alfx
betx = twiss.alfy

# get all twiss variables for 10th element:
row = twiss[10]

By default a table provides access to all available rows and columns. In order to restrict to the selection from a previous SELECT command, you can use the the table.selection() method:

twiss = madx.table.twiss.selection()

# only selected elements:
twiss.betx

# only selected columns:
list(twiss)

globals

Dictionary-like view of the MAD-X global variables:

# list of variable names
print(list(madx.globals))

# value of a builtin variable
print(madx.globals.PI)

Evaluate an expression in the MAD-X interpreter:

print(madx.eval('sb->angle / pi * 180'))

sequence

Dictionary like view of all defined sequences:

# list of sequence names
print(list(madx.sequence))

# get a proxy object for the sequence
fodo = madx.sequence.fodo

beam = fodo.beam
print(beam.ex, beam.ey)

# ordered dict-like object of explicitly defined elements:
elements = fodo.elements

# OR: including implicit drifts:
expanded = fodo.expanded_elements

Logging commands

For the purpose of debugging, reproducibility and transparency in general, it is important to be able to get a listing of the user input sent to MAD-X. This can be controlled using the command_log parameter. It accepts file names, arbitrary callables and file-like objects as follows:

madx = Madx(command_log="log.madx")
madx = Madx(command_log=print)
madx = Madx(command_log=CommandLog(sys.stderr))

Redirecting output

The output of the MAD-X interpreter can be controlled using the redirect parameter of the Madx constructor. It allows to disable the output completely:

madx = Madx(stdout=False)

redirect it to a file:

with open('madx_output.log', 'w') as f:
    madx = Madx(stdout=f)

or send the MAD-X output directly to an in-memory pipe without going through the filesystem:

madx = Madx(stdout=subprocess.PIPE)
pipe = m._process.stdout