Line
Creating a Line object
An Xsuite Line can be created by providing beam line element objects and the corresponding names, as illustrated in the following example:
import numpy as np
import xtrack as xt
# Define elements
pi = np.pi
lbend = 3
elements = {
'mqf.1': xt.Quadrupole(length=0.3, k1=0.1),
'd1.1': xt.Drift(length=1),
'mb1.1': xt.Bend(length=lbend, k0=pi / 2 / lbend, h=pi / 2 / lbend),
'd2.1': xt.Drift(length=1),
'mqd.1': xt.Quadrupole(length=0.3, k1=-0.7),
'd3.1': xt.Drift(length=1),
'mb2.1': xt.Bend(length=lbend, k0=pi / 2 / lbend, h=pi / 2 / lbend),
'd4.1': xt.Drift(length=1),
'mqf.2': xt.Quadrupole(length=0.3, k1=0.1),
'd1.2': xt.Drift(length=1),
'mb1.2': xt.Bend(length=lbend, k0=pi / 2 / lbend, h=pi / 2 / lbend),
'd2.2': xt.Drift(length=1),
'mqd.2': xt.Quadrupole(length=0.3, k1=-0.7),
'd3.2': xt.Drift(length=1),
'mb2.2': xt.Bend(length=lbend, k0=pi / 2 / lbend, h=pi / 2 / lbend),
'd4.2': xt.Drift(length=1),
}
# Build the ring
line = xt.Line(elements=elements,
element_names=['mqf.1', 'd1.1', 'mb1.1', 'd2.1', # defines the order
'mqd.1', 'd3.1', 'mb2.1', 'd4.1',
'mqf.2', 'd1.2', 'mb1.2', 'd2.2',
'mqd.2', 'd3.2', 'mb2.2', 'd4.2'])
# Define reference particle
line.particle_ref = xt.Particles(p0c=1.2e9, mass0=xt.PROTON_MASS_EV)
# Complete source: xtrack/examples/toy_ring/000_toy_ring.py
Importing a line from MAD-X
An Xsuite Line object can be imported from an existing MAD-X model, through the
cpymad interface of MAD-X, using the method
xtrack.Line.from_madx_sequence()
. The import of certain features of the MAD-X
model (dererred expressions, apertures, thick elements, alignment errors, field
errors, etc.) can be controlled by the user. This is illustrated in the following
example:
import numpy as np
from cpymad.madx import Madx
import xtrack as xt
import matplotlib.pyplot as plt
from cpymad.madx import Madx
mad = Madx()
# Load mad model and apply element shifts
mad.input('''
call, file = '../../test_data/psb_chicane/psb.seq';
call, file = '../../test_data/psb_chicane/psb_fb_lhc.str';
beam, particle=PROTON, pc=0.5708301551893517;
use, sequence=psb1;
select,flag=error,clear;
select,flag=error,pattern=bi1.bsw1l1.1*;
ealign, dx=-0.0057;
select,flag=error,clear;
select,flag=error,pattern=bi1.bsw1l1.2*;
select,flag=error,pattern=bi1.bsw1l1.3*;
select,flag=error,pattern=bi1.bsw1l1.4*;
ealign, dx=-0.0442;
twiss;
''')
line = xt.Line.from_madx_sequence(
sequence=mad.sequence.psb1,
allow_thick=True,
enable_align_errors=True,
deferred_expressions=True,
)
line.particle_ref = xt.Particles(mass0=xt.PROTON_MASS_EV,
gamma0=mad.sequence.psb1.beam.gamma)
# Complete source: xtrack/examples/psb/000a_all_xsuite_import_model.py
Define a line through a sequence
A line can also be defined through a “sequence”, providing the element s positions instead of explicit drifts, as show in the example below:
import numpy as np
from xtrack import Line, Node, Multipole
# Or from a sequence definition:
elements = {
'quad': Multipole(length=0.3, knl=[0, +0.50]),
'bend': Multipole(length=0.5, knl=[np.pi / 12], hxl=[np.pi / 12]),
}
sequences = {
'arc': [Node(1.0, 'quad'), Node(4.0, 'bend', from_='quad')],
}
line = Line.from_sequence([
Node( 0.0, 'arc'),
Node(10.0, 'arc', name='section2'),
Node( 3.0, Multipole(knl=[0, 0, 0.1]), from_='section2', name='sext'),
Node( 3.0, 'quad', name='quad_5', from_='sext'),
], length=20,
elements=elements, sequences=sequences,
auto_reorder=True, copy_elements=False,
)
line.get_table().show()
# prints:
# #
# name s element_type isthick isreplica parent_name iscollective
# arc 0 Marker False False None False
# drift 0 Drift True False None False
# arcquad 1 Multipole False False None False
# drift1 1 Drift True False None False
# arcbend 5 Multipole False False None False
# drift2 5 Drift True False None False
# section2 10 Marker False False None False
# drift3 10 Drift True False None False
# section2quad 11 Multipole False False None False
# drift4 11 Drift True False None False
# sext 13 Multipole False False None False
# drift5 13 Drift True False None False
# section2bend 15 Multipole False False None False
# drift6 15 Drift True False None False
# quad_5 16 Multipole False False None False
# drift7 16 Drift True False None False
# _end_point 20 False False None False
# Complete source: xtrack/examples/sequence/000_sequence.py
Line inspection, Line.get_table()
, Line.attr[...]
The following example illustrates how to inspect the properties of a line and its elements:
import numpy as np
import xtrack as xt
pi = np.pi
lbend = 3
elements = {
'mqf.1': xt.Quadrupole(length=0.3, k1=0.1),
'd1.1': xt.Drift(length=1),
'mb1.1': xt.Bend(length=lbend, k0=pi / 2 / lbend, h=pi / 2 / lbend),
'd2.1': xt.Drift(length=1),
'mqd.1': xt.Quadrupole(length=0.3, k1=-0.7),
'd3.1': xt.Drift(length=1),
'mb2.1': xt.Bend(length=lbend, k0=pi / 2 / lbend, h=pi / 2 / lbend),
'd4.1': xt.Drift(length=1),
'mqf.2': xt.Quadrupole(length=0.3, k1=0.1),
'd1.2': xt.Drift(length=1),
'mb1.2': xt.Bend(length=lbend, k0=pi / 2 / lbend, h=pi / 2 / lbend),
'd2.2': xt.Drift(length=1),
'mqd.2': xt.Quadrupole(length=0.3, k1=-0.7),
'd3.2': xt.Drift(length=1),
'mb2.2': xt.Bend(length=lbend, k0=pi / 2 / lbend, h=pi / 2 / lbend),
'd4.2': xt.Drift(length=1),
}
# Build the ring
line = xt.Line(elements=elements, element_names=list(elements.keys()))
line.particle_ref = xt.Particles(p0c=1.2e9, mass0=xt.PROTON_MASS_EV)
line.build_tracker()
# Quick access to an element and its attributes (by name)
line['mqf.1'] # is Quadrupole(length=0.3, k1=0.1, ...)
line['mqf.1'].k1 # is 0.1
line['mqf.1'].length # is 0.3
# Quick access to an element and its attributes (by index)
line[0] # is Quadrupole(length=0.3, k1=0.1, ...)
line[0].k1 # is 0.1
line[0].length # is 0.3
# Tuple with all element names
line.element_names # is ('mqf.1', 'd1.1', 'mb1.1', 'd2.1', 'mqd.1', ...
# Tuple with all element objects
line.elements # is (Quadrupole(length=0.3, k1=0.1, ...), Drift(length=1), ...
# `line.attr[...]` can be used for efficient extraction of a given attribute for
# all elements. For example:
line.attr['length'] # is (0.3, 1, 3, 1, 0.3, 1, 3, 1, 0.3, 1, 3, 1, 0.3, 1, 3, 1)
line.attr['k1l'] # is ('0.03, 0.0, 0.0, 0.0, -0.21, 0.0, 0.0, 0.0, 0.03, ... )
# The list of all attributes can be found in
line.attr.keys() # is ('length', 'k1', 'k1l', 'k2', 'k2l', 'k3', 'k3l', 'k4', ...
# `line.get_table()`` can be used to get a table with information about the line
# elements. For example:
tab = line.get_table()
# The table can be printed
tab.show()
# prints:
#
# name s element_type isthick isreplica parent_name iscollective
# mqf.1 0 Quadrupole True False None False
# d1.1 0.3 Drift True False None False
# mb1.1 1.3 Bend True False None False
# d2.1 4.3 Drift True False None False
# mqd.1 5.3 Quadrupole True False None False
# d3.1 5.6 Drift True False None False
# mb2.1 6.6 Bend True False None False
# d4.1 9.6 Drift True False None False
# mqf.2 10.6 Quadrupole True False None False
# d1.2 10.9 Drift True False None False
# mb1.2 11.9 Bend True False None False
# d2.2 14.9 Drift True False None False
# mqd.2 15.9 Quadrupole True False None False
# d3.2 16.2 Drift True False None False
# mb2.2 17.2 Bend True False None False
# d4.2 20.2 Drift True False None False
# _end_point 21.2 False False None False
# Access to a single element of the table
tab['s', 'mb2.1'] # is 6.6
# Access to a single column of the table
tab['s'] # is [0.0, 0.3, 1.3, 4.3, 5.3, 5.6, 6.6, 9.6, 10.6, 10.9, 11.9, ...
# Regular expressions can be used to select elements by name
tab.rows['mb.*']
# returns:
#
# Table: 4 rows, 94 cols
# name s element_type isthick isreplica parent_name iscollective
# mb1.1 1.3 Bend True False None False
# mb2.1 6.6 Bend True False None False
# mb1.2 11.9 Bend True False None False
# mb2.2 17.2 Bend True False None False
# Elements can be selected by type
tab.rows[tab.element_type == 'Quadrupole']
# returns:
#
# Table: 4 rows, 94 cols
# name s element_type isthick isreplica parent_name iscollective
# mqf.1 0 Quadrupole True False None False
# mqd.1 5.3 Quadrupole True False None False
# mqf.2 10.6 Quadrupole True False None False
# mqd.2 15.9 Quadrupole True False None False
# A section of the ring can be selected using names
tab.rows['mqd.1':'mqd.2']
# returns:
#
# Table: 9 rows, 94 cols
# name s element_type isthick isreplica parent_name iscollective
# mqd.1 5.3 Quadrupole True False None False
# d3.1 5.6 Drift True False None False
# mb2.1 6.6 Bend True False None False
# d4.1 9.6 Drift True False None False
# mqf.2 10.6 Quadrupole True False None False
# d1.2 10.9 Drift True False None False
# mb1.2 11.9 Bend True False None False
# d2.2 14.9 Drift True False None False
# mqd.2 15.9 Quadrupole True False None False
# A section of the ring can be selected using the s coordinate
tab.rows[3.0:7.0:'s']
# returns:
#
# Table: 4 rows, 94 cols
# name s element_type isthick isreplica parent_name iscollective
# d2.1 4.3 Drift True False None False
# mqd.1 5.3 Quadrupole True False None False
# d3.1 5.6 Drift True False None False
# mb2.1 6.6 Bend True False None False
# A section of the ring can be selected using indexes relative one element
# (e.g. to get from three elements upstream of 'mqd.1' to two elements
# downstream of 'mb2.1')
tab.rows['mqd.1%%-3':'mb2.1%%2']
# returns:
#
# Table: 8 rows, 94 cols
# name s element_type isthick isreplica parent_name iscollective
# d1.1 0.3 Drift True False None False
# mb1.1 1.3 Bend True False None False
# d2.1 4.3 Drift True False None False
# mqd.1 5.3 Quadrupole True False None False
# d3.1 5.6 Drift True False None False
# mb2.1 6.6 Bend True False None False
# d4.1 9.6 Drift True False None False
# mqf.2 10.6 Quadrupole True False None False
# Each of the selection methods above returns a valid table, hence selections
# can be chained. For example:
tab.rows[0:10:'s'].rows['mb.*']
# returns:
#
# Table: 2 rows, 94 cols
# name s element_type isthick isreplica parent_name iscollective
# mb1.1 1.3 Bend True False None False
# mb2.1 6.6 Bend True False None False
# All attributes extracted by `line.attr[...]` can be included in the table
# using `attr=True`. For example, using `tab.cols[...]` to select columns, we
# we can get the focusing strength of all quadrupoles in the ring:
tab = line.get_table(attr=True)
tab.rows[tab.element_type=='Quadrupole'].cols['s length k1l']
# returns:
#
# Table: 4 rows, 4 cols
# name s length k1l
# mqf.1 0 0.3 0.03
# mqd.1 5.3 0.3 -0.21
# mqf.2 10.6 0.3 0.03
# mqd.2 15.9 0.3 -0.21
# Complete source: xtrack/examples/toy_ring/004_inspect.py
References and deferred expressions
Accelerators and beam lines have complex control paterns. For example, a single high-level parameter can be used to control groups of accelerator components (e.g., sets of magnets in series, groups of RF cavities, etc.) following complex dependency relations. Xsuite allows including these dependencies in the simulation model so that changes in the high-level parameters are automatically propagated down to the line elements properties. Furthermore, the dependency relations can be created, inspected and modified at run time, as illustrated in the following example:
import numpy as np
import xtrack as xt
# We build a simple ring
pi = np.pi
lbend = 3
lquad = 0.3
elements = {
'mqf.1': xt.Quadrupole(length=lquad, k1=0.1),
'd1.1': xt.Drift(length=1),
'mb1.1': xt.Bend(length=lbend, k0=pi / 2 / lbend, h=pi / 2 / lbend),
'd2.1': xt.Drift(length=1),
'mqd.1': xt.Quadrupole(length=lquad, k1=-0.7),
'd3.1': xt.Drift(length=1),
'mb2.1': xt.Bend(length=lbend, k0=pi / 2 / lbend, h=pi / 2 / lbend),
'd4.1': xt.Drift(length=1),
'mqf.2': xt.Quadrupole(length=lquad, k1=0.1),
'd1.2': xt.Drift(length=1),
'mb1.2': xt.Bend(length=lbend, k0=pi / 2 / lbend, h=pi / 2 / lbend),
'd2.2': xt.Drift(length=1),
'mqd.2': xt.Quadrupole(length=lquad, k1=-0.7),
'd3.2': xt.Drift(length=1),
'mb2.2': xt.Bend(length=lbend, k0=pi / 2 / lbend, h=pi / 2 / lbend),
'd4.2': xt.Drift(length=1),
}
line = xt.Line(elements=elements, element_names=list(elements.keys()))
line.particle_ref = xt.Particles(p0c=1.2e9, mass0=xt.PROTON_MASS_EV)
# For each quadrupole we create a variable controlling its integrated strength.
# Expressions can be associated to any beam element property, using the `element_refs`
# attribute of the line. For example:
line.vars['k1l.qf.1'] = 0
line.element_refs['mqf.1'].k1 = line.vars['k1l.qf.1'] / lquad
line.vars['k1l.qd.1'] = 0
line.element_refs['mqd.1'].k1 = line.vars['k1l.qd.1'] / lquad
line.vars['k1l.qf.2'] = 0
line.element_refs['mqf.2'].k1 = line.vars['k1l.qf.2'] / lquad
line.vars['k1l.qd.2'] = 0
line.element_refs['mqd.2'].k1 = line.vars['k1l.qd.2'] / lquad
# When a variable is changed, the corresponding element property is automatically
# updated:
line.vars['k1l.qf.1'] = 0.1
line['mqf.1'].k1 # is 0.333, i.e. 0.1 / lquad
# We can create a variable controlling the integrated strength of the two
# focusing quadrupoles
line.vars['k1lf'] = 0.1
line.vars['k1l.qf.1'] = line.vars['k1lf']
line.vars['k1l.qf.2'] = line.vars['k1lf']
# and a variable controlling the integrated strength of the two defocusing quadrupoles
line.vars['k1ld'] = -0.7
line.vars['k1l.qd.1'] = line.vars['k1ld']
line.vars['k1l.qd.2'] = line.vars['k1ld']
# Changes on the controlling variable are propagated to the two controlled ones and
# to the corresponding element properties:
line.vars['k1lf'] = 0.2
line.vars['k1l.qf.1']._get_value() # is 0.2
line.vars['k1l.qf.2']._get_value() # is 0.2
line['mqf.1'].k1 # is 0.666, i.e. 0.2 / lquad
line['mqf.2'].k1 # is 0.666, i.e. 0.2 / lquad
# The `_info()` method of a variable provides information on the existing relations
# between the variables. For example:
line.vars['k1l.qf.1']._info()
# prints:
## vars['k1l.qf.1']._get_value()
# vars['k1l.qf.1'] = 0.2
#
## vars['k1l.qf.1']._expr
# vars['k1l.qf.1'] = vars['k1lf']
#
## vars['k1l.qf.1']._expr._get_dependencies()
# vars['k1lf'] = 0.2
#
## vars['k1l.qf.1']._find_dependant_targets()
# element_refs['mqf.1'].k1
line.vars['k1lf']._info()
# prints:
## vars['k1lf']._get_value()
# vars['k1lf'] = 0.2
#
## vars['k1lf']._expr is None
#
## vars['k1lf']._find_dependant_targets()
# vars['k1l.qf.2']
# element_refs['mqf.2'].k1
# vars['k1l.qf.1']
# element_refs['mqf.1'].k1
line.element_refs['mqf.1'].k1._info()
# prints:
## element_refs['mqf.1'].k1._get_value()
# element_refs['mqf.1'].k1 = 0.6666666666666667
#
## element_refs['mqf.1'].k1._expr
# element_refs['mqf.1'].k1 = (vars['k1l.qf.1'] / 0.3)
#
## element_refs['mqf.1'].k1._expr._get_dependencies()
# vars['k1l.qf.1'] = 0.2
#
## element_refs['mqf.1'].k1 does not influence any target
# Expressions can include multiple variables and mathematical operations. For example
line.vars['a'] = 3 * line.functions.sqrt(line.vars['k1lf']) + 2 * line.vars['k1ld']
# As seen above, line.vars['varname'] returns a reference object that
# can be used to build further references, or to inspect its properties.
# To get the current value of the variable, one needs to use `._get_value()`
# For quick access to the current value of a variable, one can use the `line.varval`
# attribute or its shortcut `line.vv`:
line.varval['k1lf'] # is 0.2
line.vv['k1lf'] # is 0.2
# Note an important difference when using `line.vars` or `line.varval` in building
# expressions. For example:
line.vars['a'] = 3.
line.vars['b'] = 2 * line.vars['a']
# In this case the reference to the quantity `line.vars['a']` is stored in the
# expression, and the value of `line.vars['b']` is updated when `line.vars['a']`
# changes:
line.vars['a'] = 4.
line.vv['b'] # is 8.
# On the contrary, when using `line.varval` or `line.vv` in building expressions,
# the current value of the variable is stored in the expression:
line.vv['a'] = 3.
line.vv['b'] = 2 * line.vv['a']
line.vv['b'] # is 6.
line.vv['a'] = 4.
line.vv['b'] # is still 6.
# The `line.vars.get_table()` method returns a table with the value of all the
# existing variables:
line.vars.get_table()
# returns:
#
# Table: 9 rows, 2 cols
# name value
# t_turn_s 0
# k1l.qf.1 0.2
# k1l.qd.1 -0.7
# k1l.qf.2 0.2
# k1l.qd.2 -0.7
# k1lf 0.2
# k1ld -0.7
# a 4
# b 6
# Regular expressions can be used to select variables. For example we can select all
# the variables containing `qf` using the following:
var_tab = line.vars.get_table()
var_tab.rows['.*qf.*']
# returns:
#
# Table: 2 rows, 2 cols
# name value
# k1l.qf.1 0.2
# k1l.qf.2 0.2
# Complete source: xtrack/examples/toy_ring/002_expressions.py
When importing a MAD-X model, the dependency relations from MAD-X deferred expressions are automatically imported as well. The following example illustrates how to inspect the dependency relations in a line imported from MAD-X:
import xtrack as xt
import xobjects as xo
from cpymad.madx import Madx
# Load sequence in MAD-X
mad = Madx()
mad.call('../../test_data/hllhc15_noerrors_nobb/sequence.madx')
mad.use(sequence="lhcb1")
# Build Xtrack line importing MAD-X expressions
line = xt.Line.from_madx_sequence(mad.sequence['lhcb1'],
deferred_expressions=True # <--
)
line.particle_ref = xt.Particles(mass0=xt.PROTON_MASS_EV, q0=1,
gamma0=mad.sequence.lhcb1.beam.gamma)
line.build_tracker()
# MAD-X variables can be found in `line.vars` or, equivalently, in #
# `line.vars`. They can be used to change properties in the beamline. #
# For example, we consider the MAD-X variable `on_x1` that controls #
# the beam angle in the interaction point 1 (IP1). It is defined in #
# microrad units. #
# Inspect the value of the variable
print(line.vars['on_x1']._value)
# ---> returns 1 (as defined in the import)
# Measure vertical angle at the interaction point 1 (IP1)
print(line.twiss(at_elements=['ip1'])['px'])
# ---> returns 1e-6
# Set crossing angle using the variable
line.vars['on_x1'] = 300
# Measure vertical angle at the interaction point 1 (IP1)
print(line.twiss(at_elements=['ip1'])['px'])
# ---> returns 0.00030035
# Complete source: xtrack/examples/knobs/001_lhc.py
Save and reload lines
An Xtrack Line object can be transformed into a dictionary or saved to a json file, as illustrated in the following example:
import json
import numpy as np
import xtrack as xt
import xobjects as xo
# Build a beam line
line = xt.Line(
elements=[
xt.Multipole(knl=np.array([1.,2.,3.])),
xt.Drift(length=2.),
xt.Cavity(frequency=400e9, voltage=1e6),
xt.Multipole(knl=np.array([1.,2.,3.])),
xt.Drift(length=2.),
],
element_names=['m1', 'd1', 'c1', 'm2', 'c2']
)
# Save to json
line.to_json('line.json')
# Load from json
line_2 = xt.Line.from_json('line.json')
# Alternatively the to dict method can be used, which is more flexible for
# example to save additional information in the json file
#Save
dct = line.to_dict()
dct['my_additional_info'] = 'Important information'
with open('line.json', 'w') as fid:
json.dump(dct, fid, cls=xo.JEncoder)
# Load
with open('line.json', 'r') as fid:
loaded_dct = json.load(fid)
line_2 = xt.Line.from_dict(loaded_dct)
# loaded_dct['my_additional_info'] contains "Important information"
# Complete source: xtrack/examples/to_json/000_lattice_to_json.py
Element insertion
import numpy as np
import xtrack as xt
pi = np.pi
lbend = 3
elements = {
'mqf.1': xt.Quadrupole(length=0.3, k1=0.1),
'd1.1': xt.Drift(length=1),
'mb1.1': xt.Bend(length=lbend, k0=pi / 2 / lbend, h=pi / 2 / lbend),
'd2.1': xt.Drift(length=1),
'mqd.1': xt.Quadrupole(length=0.3, k1=-0.7),
'd3.1': xt.Drift(length=1),
'mb2.1': xt.Bend(length=lbend, k0=pi / 2 / lbend, h=pi / 2 / lbend),
'd4.1': xt.Drift(length=1),
'mqf.2': xt.Quadrupole(length=0.3, k1=0.1),
'd1.2': xt.Drift(length=1),
'mb1.2': xt.Bend(length=lbend, k0=pi / 2 / lbend, h=pi / 2 / lbend),
'd2.2': xt.Drift(length=1),
'mqd.2': xt.Quadrupole(length=0.3, k1=-0.7),
'd3.2': xt.Drift(length=1),
'mb2.2': xt.Bend(length=lbend, k0=pi / 2 / lbend, h=pi / 2 / lbend),
'd4.2': xt.Drift(length=1),
}
# Build the ring
line = xt.Line(elements=elements, element_names=list(elements.keys()))
line.particle_ref = xt.Particles(p0c=1.2e9, mass0=xt.PROTON_MASS_EV)
line.build_tracker()
# Inspect the line
tab = line.get_table()
tab.show()
# prints:
#
# name s element_type isthick isreplica parent_name iscollective
# mqf.1 0 Quadrupole True False None False
# d1.1 0.3 Drift True False None False
# mb1.1 1.3 Bend True False None False
# d2.1 4.3 Drift True False None False
# mqd.1 5.3 Quadrupole True False None False
# d3.1 5.6 Drift True False None False
# mb2.1 6.6 Bend True False None False
# d4.1 9.6 Drift True False None False
# mqf.2 10.6 Quadrupole True False None False
# d1.2 10.9 Drift True False None False
# mb1.2 11.9 Bend True False None False
# d2.2 14.9 Drift True False None False
# mqd.2 15.9 Quadrupole True False None False
# d3.2 16.2 Drift True False None False
# mb2.2 17.2 Bend True False None False
# d4.2 20.2 Drift True False None False
# _end_point 21.2 False False None False
# Define a sextupole
my_sext = xt.Sextupole(length=0.1, k2=0.1)
# Insert copies of the defined sextupole downstream of the quadrupoles
line.discard_tracker() # needed to modify the line structure
line.insert_element('msf.1', my_sext.copy(), at_s=tab['s', 'mqf.1'] + 0.4)
line.insert_element('msd.1', my_sext.copy(), at_s=tab['s', 'mqd.1'] + 0.4)
line.insert_element('msf.2', my_sext.copy(), at_s=tab['s', 'mqf.2'] + 0.4)
line.insert_element('msd.2', my_sext.copy(), at_s=tab['s', 'mqd.2'] + 0.4)
# Define a rectangular aperture
my_aper = xt.LimitRect(min_x=-0.02, max_x=0.02, min_y=-0.01, max_y=0.01)
# Insert the aperture upstream of the first bending magnet
line.insert_element('aper', my_aper, index='mb1.1')
line.get_table().show()
# prints:
#
# name s element_type isthick isreplica parent_name iscollective
# mqf.1 0 Quadrupole True False None False
# d1.1..0 0.3 DriftSlice True False d1.1 False
# msf.1 0.4 Sextupole True False None False
# d1.1..2 0.5 DriftSlice True False d1.1 False
# aper 1.3 LimitRect False False None False
# mb1.1 1.3 Bend True False None False
# d2.1 4.3 Drift True False None False
# mqd.1 5.3 Quadrupole True False None False
# d3.1..0 5.6 DriftSlice True False d3.1 False
# msd.1 5.7 Sextupole True False None False
# d3.1..2 5.8 DriftSlice True False d3.1 False
# mb2.1 6.6 Bend True False None False
# d4.1 9.6 Drift True False None False
# mqf.2 10.6 Quadrupole True False None False
# d1.2..0 10.9 DriftSlice True False d1.2 False
# msf.2 11 Sextupole True False None False
# d1.2..2 11.1 DriftSlice True False d1.2 False
# mb1.2 11.9 Bend True False None False
# d2.2 14.9 Drift True False None False
# mqd.2 15.9 Quadrupole True False None False
# d3.2..0 16.2 DriftSlice True False d3.2 False
# msd.2 16.3 Sextupole True False None False
# d3.2..2 16.4 DriftSlice True False d3.2 False
# mb2.2 17.2 Bend True False None False
# d4.2 20.2 Drift True False None False
# _end_point 21.2 False False None False
# Complete source: xtrack/examples/toy_ring/005_insert_element.py
Element slicing
It is possible to slice thick element with thin or thick slices, using the Uniform or the Teapot scheme. This is illustrated in the following example:
import numpy as np
import xtrack as xt
# We build a simple ring
pi = np.pi
lbend = 3
lquad = 0.3
elements = {
'mqf.1': xt.Quadrupole(length=lquad, k1=0.1),
'msf.1': xt.Sextupole(length=0.1, k2=0.02),
'd1.1': xt.Drift(length=0.9),
'mb1.1': xt.Bend(length=lbend, k0=pi / 2 / lbend, h=pi / 2 / lbend),
'd2.1': xt.Drift(length=1),
'mqd.1': xt.Quadrupole(length=lquad, k1=-0.7),
'd3.1': xt.Drift(length=1),
'mb2.1': xt.Bend(length=lbend, k0=pi / 2 / lbend, h=pi / 2 / lbend),
'd4.1': xt.Drift(length=1),
'mqf.2': xt.Quadrupole(length=lquad, k1=0.1),
'd1.2': xt.Drift(length=1),
'mb1.2': xt.Bend(length=lbend, k0=pi / 2 / lbend, h=pi / 2 / lbend),
'd2.2': xt.Drift(length=1),
'mqd.2': xt.Quadrupole(length=lquad, k1=-0.7),
'd3.2': xt.Drift(length=1),
'mb2.2': xt.Bend(length=lbend, k0=pi / 2 / lbend, h=pi / 2 / lbend),
'd4.2': xt.Drift(length=1),
}
line = xt.Line(elements=elements, element_names=list(elements.keys()))
line.particle_ref = xt.Particles(p0c=1.2e9, mass0=xt.PROTON_MASS_EV)
line_before_slicing = line.copy() # Keep for comparison
# Slice different elements with different strategies (in case multiple strategies
# apply to the same element, the last one takes precedence)
line.slice_thick_elements(
slicing_strategies=[
# Slicing with thin elements
xt.Strategy(slicing=xt.Teapot(1)), # (1) Default applied to all elements
xt.Strategy(slicing=xt.Uniform(2), element_type=xt.Bend), # (2) Selection by element type
xt.Strategy(slicing=xt.Teapot(3), element_type=xt.Quadrupole), # (4) Selection by element type
xt.Strategy(slicing=xt.Teapot(4), name='mb1.*'), # (5) Selection by name pattern
# Slicing with thick elements
xt.Strategy(slicing=xt.Uniform(2, mode='thick'), name='mqf.*'), # (6) Selection by name pattern
# Do not slice (leave untouched)
xt.Strategy(slicing=None, name='mqd.1') # (7) Selection by name
])
line.build_tracker()
line_before_slicing.build_tracker()
# Ispect the result:
ltable = line.get_table(attr=True).cols['s', 'isthick', 'element_type',
'parent_name', 'k0l', 'k1l', 'k2l']
# The sextupole msf.1 has one thin slice, as default strategy (1) is applied.
ltable.rows['msf.1_entry':'msf.1_exit']
# returns:
#
# Table: 5 rows, 8 cols
# name s isthick element_type parent_name k0l k1l k2l
# msf.1_entry 0.3 False Marker None 0 0 0
# drift_msf.1..0 0.3 True DriftSliceSextupole msf.1 0 0 0
# msf.1..0 0.35 False ThinSliceSextupole msf.1 0 0 0.002
# drift_msf.1..1 0.35 True DriftSliceSextupole msf.1 0 0 0
# msf.1_exit 0.4 False Marker None 0 0 0
# The bend mb2.1 has three thin slices, as strategy (2) is applied.
ltable.rows['mb2.1_entry':'mb2.1_exit']
# returns:
#
# Table: 9 rows, 8 cols
# name s isthick element_type parent_name k0l k1l k2l
# mb2.1_entry 6.6 False Marker None 0 0 0
# mb2.1..entry_map 6.6 False ThinSliceBendEntry mb2.1 0 0 0
# drift_mb2.1..0 6.6 True DriftSliceBend mb2.1 0 0 0
# mb2.1..0 7.6 False ThinSliceBend mb2.1 0.785398 0 0
# drift_mb2.1..1 7.6 True DriftSliceBend mb2.1 0 0 0
# mb2.1..1 8.6 False ThinSliceBend mb2.1 0.785398 0 0
# drift_mb2.1..2 8.6 True DriftSliceBend mb2.1 0 0 0
# mb2.1..exit_map 9.6 False ThinSliceBendExit mb2.1 0 0 0
# mb2.1_exit 9.6 False Marker None 0 0 0
# The quadrupole mqd.2 has four thin slices, as strategy (3) is applied.
ltable.rows['mqd.2_entry':'mqd.2_exit']
# returns:
#
# Table: 9 rows, 8 cols
# name s isthick element_type parent_name k0l k1l ...
# mqd.2_entry 15.9 False Marker None 0 0
# drift_mqd.2..0 15.9 True DriftSliceQuadrupole mqd.2 0 0
# mqd.2..0 15.9375 False ThinSliceQuadrupole mqd.2 0 -0.07
# drift_mqd.2..1 15.9375 True DriftSliceQuadrupole mqd.2 0 0
# mqd.2..1 16.05 False ThinSliceQuadrupole mqd.2 0 -0.07
# drift_mqd.2..2 16.05 True DriftSliceQuadrupole mqd.2 0 0
# mqd.2..2 16.1625 False ThinSliceQuadrupole mqd.2 0 -0.07
# drift_mqd.2..3 16.1625 True DriftSliceQuadrupole mqd.2 0 0
# mqd.2_exit 16.2 False Marker None 0 0
# The quadrupole mqf.1 has two thick slices, as strategy (6) is applied.
ltable.rows['mqf.1_entry':'mqf.1_exit']
# returns:
#
# Table: 4 rows, 8 cols
# name s isthick element_type parent_name k0l k1l k2l
# mqf.1_entry 0 False Marker None 0 0 0
# mqf.1..0 0 True ThickSliceQuadrupole mqf.1 0 0.015 0
# mqf.1..1 0.15 True ThickSliceQuadrupole mqf.1 0 0.015 0
# mqf.1_exit 0.3 False Marker None 0 0 0
# The quadrupole mqd.1 is left untouched, as strategy (7) is applied.
ltable.rows['mqd.1']
# returns:
#
# Table: 1 row, 8 cols
# name s isthick element_type parent_name k0l k1l k2l
# mqd.1 5.3 True Quadrupole None 0 -0.21 0
########################################
# Change properties of sliced elements #
########################################
# Sliced elements are updated whenever their parent is changed. For example:
# Inspect a quadrupole:
ltable.rows['mqf.1.*']
# returns:
#
# Table: 4 rows, 8 cols
# name s isthick element_type parent_name k0l k1l k2l
# mqf.1_entry 0 False Marker None 0 0 0
# mqf.1..0 0 True ThickSliceQuadrupole mqf.1 0 0.015 0
# mqf.1..1 0.15 True ThickSliceQuadrupole mqf.1 0 0.015 0
# mqf.1_exit 0.3 False Marker None 0 0 0
# Change the the strength of the parent
line['mqf.1'].k1 = 0.2
# Inspect
ltable = line.get_table(attr=True).cols['s', 'isthick', 'element_type',
'parent_name', 'k0l', 'k1l', 'k2l']
ltable.rows['mqf.1.*']
# returns (the strength of the slices has changed):
#
# Table: 4 rows, 8 cols
# name s isthick element_type parent_name k0l k1l k2l
# mqf.1_entry 0 False Marker None 0 0 0
# mqf.1..0 0 True ThickSliceQuadrupole mqf.1 0 0.03 0
# mqf.1..1 0.15 True ThickSliceQuadrupole mqf.1 0 0.03 0
# mqf.1_exit 0.3 False Marker None 0 0 0
# Complete source: xtrack/examples/toy_ring/003_slicing.py
Simulation of small rings: drifts, bends, fringe fields
The modeling of the body of bending magnets in automatically adapted depending on the bending radius, hence no special setting is required for this purpose when simulating small rings with large bending angles.
However, the modeling of the fringe fields and the drifts is not automatically adapted and appropriate settings need to be provided by the user.
The following example illustrates how to switch to the full model for the fringe fields and the drifts and compares the effect of different models on the optics functions and the chromatic properties of the CERN ELENA ring:
import numpy as np
from cpymad.madx import Madx
import xtrack as xt
# We get the model from MAD-X
mad = Madx()
folder = ('../../test_data/elena')
mad.call(folder + '/elena.seq')
mad.call(folder + '/highenergy.str')
mad.call(folder + '/highenergy.beam')
mad.use('elena')
# Build xsuite line
seq = mad.sequence.elena
line = xt.Line.from_madx_sequence(seq)
line.particle_ref = xt.Particles(gamma0=seq.beam.gamma,
mass0=seq.beam.mass * 1e9,
q0=seq.beam.charge)
# Inspect one bend
line['lnr.mbhek.0135']
# returns:
#
# Bend(length=0.971, k0=1.08, k1=0, h=1.08, model='adaptive',
# knl=array([0., 0., 0., 0., 0.]), ksl=array([0., 0., 0., 0., 0.]),
# edge_entry_active=1, edge_exit_active=1,
# edge_entry_model='linear', edge_exit_model='linear',
# edge_entry_angle=0.287, edge_exit_angle=0.287,
# edge_entry_angle_fdown=0, edge_exit_angle_fdown=0,
# edge_entry_fint=0.424, edge_exit_fint=0.424,
# edge_entry_hgap=0.038, edge_exit_hgap=0.038,
# shift_x=0, shift_y=0, rot_s_rad=0)
# By default the adaptive model is used for the core and the linearized model for the edge
line['lnr.mbhek.0135'].model # is 'adaptive'
line['lnr.mbhek.0135'].edge_entry_model # is 'linear'
line['lnr.mbhek.0135'].edge_exit_model # is 'linear'
# For small machines (bends with large bending angles) it is more appropriate to
# switch to the `full` model for the edge
line.configure_bend_model(core='adaptive', edge='full')
# It is also possible to switch from the expanded drift to the exact one
line.config.XTRACK_USE_EXACT_DRIFTS = True
line['lnr.mbhek.0135'].model # is 'adaptive'
line['lnr.mbhek.0135'].edge_entry_model # is 'full'
line['lnr.mbhek.0135'].edge_exit_model # is 'full'
# Slice the bends to see the behavior of the optics functions within them
line.slice_thick_elements(
slicing_strategies=[
xt.Strategy(slicing=None), # don't touch other elements
xt.Strategy(slicing=xt.Uniform(10, mode='thick'), element_type=xt.Bend)
])
# Twiss
tw = line.twiss(method='4d')
# Switch to a simplified model
line.configure_bend_model(core='expanded', edge='linear')
line.config.XTRACK_USE_EXACT_DRIFTS = False
# Twiss with the default model
tw_simpl = line.twiss(method='4d')
# Compare beta functions and chromatic properties
import matplotlib.pyplot as plt
plt.close('all')
plt.figure(1, figsize=(6.4, 4.8 * 1.5))
ax1 = plt.subplot(4,1,1)
plt.plot(tw.s, tw.betx, label='adaptive')
plt.plot(tw_simpl.s, tw_simpl.betx, '--', label='simplified')
plt.ylabel(r'$\beta_x$')
plt.legend(loc='best')
ax2 = plt.subplot(4,1,2, sharex=ax1)
plt.plot(tw.s, tw.bety)
plt.plot(tw_simpl.s, tw_simpl.bety, '--')
plt.ylabel(r'$\beta_y$')
ax3 = plt.subplot(4,1,3, sharex=ax1)
plt.plot(tw.s, tw.wx_chrom)
plt.plot(tw_simpl.s, tw_simpl.wx_chrom, '--')
plt.ylabel(r'$W_x$')
ax4 = plt.subplot(4,1,4, sharex=ax1)
plt.plot(tw.s, tw.wy_chrom)
plt.plot(tw_simpl.s, tw_simpl.wy_chrom, '--')
plt.ylabel(r'$W_y$')
plt.xlabel('s [m]')
# Highlight the bends
tt_sliced = line.get_table()
tbends = tt_sliced.rows[tt_sliced.element_type == 'ThickSliceBend']
for ax in [ax1, ax2, ax3, ax4]:
for nn in tbends.name:
ax.axvspan(tbends['s', nn], tbends['s', nn] + line[nn].length,
color='b', alpha=0.2, linewidth=0)
plt.show()
# Complete source: xtrack/examples/small_rings/000_elena_chromatic_functions.py
Extraction of second order transfer maps
The method xtrack.Line.get_line_with_second_order_maps()
allows modeling portions
of a beam line with second order transfer maps. This is illustrated in the
following example.
See also xtrack.SecondOrderTaylorMap.from_line()
.
import numpy as np
import xtrack as xt
# Get a line and build a tracker
line = xt.Line.from_json('../../test_data/hllhc15_thick/lhc_thick_with_knobs.json')
line.build_tracker()
# Switch RF on
line.vars['vrf400'] = 16
line.vars['lagrf400.b1'] = 0.5
# Enable crossing angle orbit bumps
line.vars['on_x1'] = 10
line.vars['on_x2'] = 20
line.vars['on_x5'] = 10
line.vars['on_x8'] = 30
# Generate line made on maps (splitting at defined markers)
ele_cut = ['ip1', 'ip2', 'ip5', 'ip8'] # markers where to split the line
line_maps = line.get_line_with_second_order_maps(split_at=ele_cut)
line_maps.build_tracker()
line_maps.get_table().show()
# prints:
#
# name s element_type isthick
# ip7 0 Marker False
# map_0 0 SecondOrderTaylorMap True
# ip8 3321.22 Marker False
# map_1 3321.22 SecondOrderTaylorMap True
# ip1 6664.72 Marker False
# map_2 6664.72 SecondOrderTaylorMap True
# ip2 9997.16 Marker False
# map_3 9997.16 SecondOrderTaylorMap True
# ip5 19994 Marker False
# map_4 19994 SecondOrderTaylorMap True
# lhcb1ip7_p_ 26658.9 Marker False
# _end_point 26658.9 False
# Compare twiss of the two lines
tw = line.twiss()
tw_map = line_maps.twiss()
tw.qx # is 62.3099999
tw_map.qx # is 0.3099999
tw.dqx # is 1.9135
tw_map.dqx # is 1.9137
tw.rows[['ip1', 'ip2', 'ip5', 'ip8']].cols['x px y py betx bety'].show()
# prints
#
# name x px y py betx bety
# ip8 9.78388e-09 3.00018e-05 6.25324e-09 5.47624e-09 1.50001 1.49999
# ip1 1.92278e-10 1.00117e-05 2.64869e-09 1.2723e-08 0.15 0.15
# ip2 1.47983e-08 2.11146e-09 -2.1955e-08 2.00015e-05 10 9.99997
# ip5 -3.03593e-09 5.75413e-09 5.13184e-10 1.0012e-05 0.15 0.15
tw_map.rows[['ip1', 'ip2', 'ip5', 'ip8']].cols['x px y py betx bety']
# prints
#
# name x px y py betx bety
# ip8 9.78388e-09 3.00018e-05 6.25324e-09 5.47624e-09 1.50001 1.49999
# ip1 1.92278e-10 1.00117e-05 2.64869e-09 1.2723e-08 0.15 0.15
# ip2 1.47983e-08 2.11146e-09 -2.1955e-08 2.00015e-05 10 9.99997
# ip5 -3.03593e-09 5.75413e-09 5.13184e-10 1.0012e-05 0.15 0.15
# Complete source: xtrack/examples/taylor_map/000_line_with_maps.py
Apply transformations (tilt, shift) to elements
Tilt and shifts transformations can be applied to beam elements, as illustrated in the following example:
import xtrack as xt
from cpymad.madx import Madx
# Load a very simple sequence from MAD-X
mad = Madx()
mad.input("""
seq: sequence, l=4;
b1: sbend, at=0.5, angle=0.2, l=1;
b2: sbend, at=2.5, angle=0.3, l=1;
endsequence;
beam;
use,sequence=seq;
""")
line = xt.Line.from_madx_sequence(mad.sequence.seq)
line.build_tracker()
print('The line as imported from MAD-X:')
print(line.get_table())
# Shift and tilt selected elements
line['b1'].shift_x = -0.1
line['b1'].rot_s_rad = 0.8
line['b2'].shift_y = 0.2
line['b2'].rot_s_rad = -0.8
tt = line.get_table(attr=True)
tt.cols['s', 'element_type', 'isthick', 'shift_x', 'shift_y', 'rot_s_rad']
# returns:
#
# name s element_type isthick shift_x shift_y rot_s_rad
# seq$start 0 Marker False 0 0 0
# b1 0 Bend True -0.1 0 0.8
# drift_0 1 Drift True 0 0 0
# b2 2 Bend True 0 0.2 -0.8
# drift_1 3 Drift True 0 0 0
# seq$end 4 Marker False 0 0 0
# _end_point 4 False 0 0 0
# Complete source: xtrack/examples/element_transformations/000_element_transform.py
Tranfromations are propagated when the elements are sliced and can be updated also after the slicing by acting on the parent element. This is illustrated in the following example:
import xtrack as xt
from cpymad.madx import Madx
# Load a very simple sequence from MAD-X
mad = Madx()
mad.input("""
seq: sequence, l=4;
b1: sbend, at=0.5, angle=0.2, l=1;
b2: sbend, at=2.5, angle=0.3, l=1;
endsequence;
beam;
use,sequence=seq;
""")
line = xt.Line.from_madx_sequence(mad.sequence.seq)
line.build_tracker()
print('The line as imported from MAD-X:')
print(line.get_table())
# Shift and tilt selected elements
line['b1'].shift_x = -0.1
line['b1'].rot_s_rad = 0.8
line['b2'].shift_y = 0.2
line['b2'].rot_s_rad = -0.8
tt = line.get_table(attr=True)
tt.cols['s', 'element_type', 'isthick', 'shift_x', 'shift_y', 'rot_s_rad']
# returns:
#
# name s element_type isthick shift_x shift_y rot_s_rad
# seq$start 0 Marker False 0 0 0
# b1 0 Bend True -0.1 0 0.8
# drift_0 1 Drift True 0 0 0
# b2 2 Bend True 0 0.2 -0.8
# drift_1 3 Drift True 0 0 0
# seq$end 4 Marker False 0 0 0
# _end_point 4 False 0 0 0
# Slice the line
slicing_strategies = [
xt.Strategy(slicing=None), # Default catch-all
xt.Strategy(slicing=xt.Teapot(2), element_type=xt.Bend),
]
line.slice_thick_elements(slicing_strategies)
line.build_tracker()
# Inspect
tt = line.get_table(attr=True)
tt.cols['s', 'element_type', 'isthick', 'parent_name', 'shift_x', 'shift_y', 'rot_s_rad']
# returns:
#
# Table: 23 rows, 8 cols
# name s element_type isthick parent_name shift_x shift_y rot_s_rad
# seq$start 0 Marker False None 0 0 0
# b1_entry 0 Marker False None 0 0 0
# b1..entry_map 0 ThinSliceBendEntry False b1 -0.1 0 0.8
# drift_b1..0 0 DriftSliceBend True b1 0 0 0
# b1..0 0.166667 ThinSliceBend False b1 -0.1 0 0.8
# drift_b1..1 0.166667 DriftSliceBend True b1 0 0 0
# b1..1 0.833333 ThinSliceBend False b1 -0.1 0 0.8
# drift_b1..2 0.833333 DriftSliceBend True b1 0 0 0
# b1..exit_map 1 ThinSliceBendExit False b1 -0.1 0 0.8
# b1_exit 1 Marker False None 0 0 0
# drift_0 1 Drift True None 0 0 0
# b2_entry 2 Marker False None 0 0 0
# b2..entry_map 2 ThinSliceBendEntry False b2 0 0.2 -0.8
# drift_b2..0 2 DriftSliceBend True b2 0 0 -0
# b2..0 2.16667 ThinSliceBend False b2 0 0.2 -0.8
# drift_b2..1 2.16667 DriftSliceBend True b2 0 0 -0
# b2..1 2.83333 ThinSliceBend False b2 0 0.2 -0.8
# drift_b2..2 2.83333 DriftSliceBend True b2 0 0 -0
# b2..exit_map 3 ThinSliceBendExit False b2 0 0.2 -0.8
# b2_exit 3 Marker False None 0 0 0
# drift_1 3 Drift True None 0 0 0
# seq$end 4 Marker False None 0 0 0
# _end_point 4 False None 0 0 0
# Update misalignment for one element. We act on the parent and the effect is
# propagated to the slices.
line['b2'].s_rotation = 0.3
line['b2'].shift_x = 2e-3
# Inspect
tt = line.get_table(attr=True)
tt.cols['s', 'element_type', 'isthick', 'parent_name', 'shift_x', 'shift_y', 'rot_s_rad']
# returns:
#
# Table: 23 rows, 8 cols
# name s element_type isthick parent_name shift_x shift_y rot_s_rad
# seq$start 0 Marker False None 0 0 0
# b1_entry 0 Marker False None 0 0 0
# b1..entry_map 0 ThinSliceBendEntry False b1 -0.1 0 0.8
# drift_b1..0 0 DriftSliceBend True b1 0 0 0
# b1..0 0.166667 ThinSliceBend False b1 -0.1 0 0.8
# drift_b1..1 0.166667 DriftSliceBend True b1 0 0 0
# b1..1 0.833333 ThinSliceBend False b1 -0.1 0 0.8
# drift_b1..2 0.833333 DriftSliceBend True b1 0 0 0
# b1..exit_map 1 ThinSliceBendExit False b1 -0.1 0 0.8
# b1_exit 1 Marker False None 0 0 0
# drift_0 1 Drift True None 0 0 0
# b2_entry 2 Marker False None 0 0 0
# b2..entry_map 2 ThinSliceBendEntry False b2 0.002 0.2 -0.8
# drift_b2..0 2 DriftSliceBend True b2 0 0 -0
# b2..0 2.16667 ThinSliceBend False b2 0.002 0.2 -0.8
# drift_b2..1 2.16667 DriftSliceBend True b2 0 0 -0
# b2..1 2.83333 ThinSliceBend False b2 0.002 0.2 -0.8
# drift_b2..2 2.83333 DriftSliceBend True b2 0 0 -0
# b2..exit_map 3 ThinSliceBendExit False b2 0.002 0.2 -0.8
# b2_exit 3 Marker False None 0 0 0
# drift_1 3 Drift True None 0 0 0
# seq$end 4 Marker False None 0 0 0
# _end_point 4 False None 0 0 0
# Complete source: xtrack/examples/element_transformations/001_sliced_element_transform.py
Cut line elements at given s positions
The method xtrack.Line.cut_at_s()
allows for cutting the line elements at the
specified s positions. In the example before we take the same toy ring introduced
in the earlier example and we cut it into 100 equal length slices:
hundred_cuts = np.linspace(0, line.get_length(), num=100)
print(f'Make cuts every {hundred_cuts[1]} metres') # => 21 centimetres
line.cut_at_s(s=list(hundred_cuts))
line.get_table()
# returns:
#
# Table: 139 rows, 7 cols
# name s element_type isthick isreplica parent_name ...
# mqf.1_entry 0 Marker False False None
# mqf.1..0 0 ThickSliceQuadrupole True False mqf.1
# mqf.1..1 0.214141 ThickSliceQuadrupole True False mqf.1
# mqf.1_exit 0.3 Marker False False None
# d1.1..0 0.3 DriftSlice True False d1.1
# d1.1..1 0.428283 DriftSlice True False d1.1
# d1.1..2 0.642424 DriftSlice True False d1.1
# d1.1..3 0.856566 DriftSlice True False d1.1
# d1.1..4 1.07071 DriftSlice True False d1.1
# d1.1..5 1.28485 DriftSlice True False d1.1
# mb1.1_entry 1.3 Marker False False None
# mb1.1..entry_map 1.3 ThinSliceBendEntry False False mb1.1
# mb1.1..0 1.3 ThickSliceBend True False mb1.1
# mb1.1..1 1.49899 ThickSliceBend True False mb1.1
# mb1.1..2 1.71313 ThickSliceBend True False mb1.1
# mb1.1..3 1.92727 ThickSliceBend True False mb1.1
# mb1.1..4 2.14141 ThickSliceBend True False mb1.1
# etc...
# Complete source: xtrack/examples/toy_ring/007_cut_at_s.py