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
filethe session should be reproducible using the official
madx
command line client by the commands in thecommand_log
file.reduces the need for special implementations on the cython binding by always going through the same interface.
More methods for changing state:
verbose()
: switch on or off verbose mode.
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