Serializing lattices¶
We can serialize lattices into MADX scripts using the following functions from the build
module:
create_script
- Creates a full MADX script including beam command and optionally error definitions as well as particle tracking.sequence_script
- Serializes a lattice into a correspondingSEQUENCE; ENDSEQUENCE;
block.track_script
- Serializes particle coordinates, plus some additional configuration, into a corrspondingTRACK; ENDTRACK;
block.error_script
- Parses error definitions from a given lattice and serializes them into a list ofSELECT
andEALIGN
statements.
For example:
[1]:
from dipas.build import Lattice, create_script, sequence_script, track_script, error_script
from dipas.elements import Segment
with Lattice(dict(particle='proton', gamma=1.25)) as lattice:
lattice.Quadrupole(k1=0.125, l=1, label='qf')
lattice.SBend(angle=0.05, l=6, label='s1')
lattice.Quadrupole(k1=-0.125, l=1, label='qd')
lattice.SBend(angle=0.05, l=6, label='s2')
lattice = Segment(lattice)
print(sequence_script(lattice))
seq: sequence, l = 14.0, refer = entry;
qf: quadrupole, k1 = 0.125, l = 1.0, at = 0.0;
s1: sbend, angle = 0.05, e1 = 0.0, e2 = 0.0, fint = 0.0, fintx = 0.0, h1 = 0.0, h2 = 0.0, hgap = 0.0, l = 6.0, at = 1.0;
qd: quadrupole, k1 = -0.125, l = 1.0, at = 7.0;
s2: sbend, angle = 0.05, e1 = 0.0, e2 = 0.0, fint = 0.0, fintx = 0.0, h1 = 0.0, h2 = 0.0, hgap = 0.0, l = 6.0, at = 8.0;
endsequence;
Now let’s create the TRACK
block:
[2]:
import torch
particles = torch.rand(6, 10)
print(track_script(particles, observe=['qf', 'qd'], aperture=True, recloss=True, turns=1, maxaper=[1]*6))
track, aperture = true, recloss = true, onepass = true, dump = true, onetable = true;
start, x = 0.2014635703501506, px = 0.2662095882206157, y = 0.8166112346953612, py = 0.23180824594831628, t = 0.39202893025542407, pt = 0.5399201490740829;
start, x = 0.14669278350197978, px = 0.24835279589455972, y = 0.27761962358694936, py = 0.8467406897590475, t = 0.9640333584721231, pt = 0.2787731815670358;
start, x = 0.40343846523065574, px = 0.8326952974366683, y = 0.30562559574588954, py = 0.6813336597554878, t = 0.03341727991931742, pt = 0.830850622929301;
start, x = 0.5470678008425934, px = 0.7779247137419353, y = 0.9958333764630148, py = 0.29537177766723244, t = 0.11764775607056943, pt = 0.3208520680352198;
start, x = 0.1049864797528981, px = 0.3752285141312387, y = 0.7203908988777403, py = 0.63130467146256, t = 0.9473415670404751, pt = 0.10299125216411009;
start, x = 0.39960444505659465, px = 0.3735462666225813, y = 0.7160529579941585, py = 0.1100296996131459, t = 0.6683040540297257, pt = 0.5498750540523619;
start, x = 0.0228851551084277, px = 0.876894016227671, y = 0.9587570774187312, py = 0.6097960547963239, t = 0.833647004978768, pt = 0.6247855455798537;
start, x = 0.9209046996988439, px = 0.11623483047061944, y = 0.8216139551556022, py = 0.6395245166612197, t = 0.15343851365337768, pt = 0.9395649827597847;
start, x = 0.20836412057646447, px = 0.32546809192785775, y = 0.7780847178261877, py = 0.9400753865683356, t = 0.35463141128309483, pt = 0.10358923206153958;
start, x = 0.4458752860950078, px = 0.19209207672465944, y = 0.43257782895074914, py = 0.027717813984675876, t = 0.5167697678230143, pt = 0.4922091373466405;
observe, place = qf;
observe, place = qd;
run, turns = 1, maxaper = {1, 1, 1, 1, 1, 1};
write, table = trackloss, file;
endtrack;
Let’s introduce some alignment errors to the defocusing quadrupole:
[3]:
from dipas.elements import LongitudinalRoll, Offset, Tilt
lattice['qd'] = Tilt(lattice['qd'], psi=0.78) # Technically this is not an alignment error, but it does modify the element.
lattice['qd'] = LongitudinalRoll(lattice['qd'], psi=0.35)
lattice['qd'] = Offset(lattice['qd'], dx=0.01, dy=0.02)
print(sequence_script(lattice))
seq: sequence, l = 14.0, refer = entry;
qf: quadrupole, k1 = 0.125, l = 1.0, at = 0.0;
s1: sbend, angle = 0.05, e1 = 0.0, e2 = 0.0, fint = 0.0, fintx = 0.0, h1 = 0.0, h2 = 0.0, hgap = 0.0, l = 6.0, at = 1.0;
qd: quadrupole, k1 = -0.125, l = 1.0, tilt = 0.78, at = 7.0;
s2: sbend, angle = 0.05, e1 = 0.0, e2 = 0.0, fint = 0.0, fintx = 0.0, h1 = 0.0, h2 = 0.0, hgap = 0.0, l = 6.0, at = 8.0;
endsequence;
Here we can see that the output from sequence_script
now contains the tilt
for the "qd"
quadrupole and the alignment errors are summarized and assigned in the part coming from error_script
.
Now let’s build the complete MADX script:
[4]:
print(create_script(
dict(particle='proton', gamma=1.25),
sequence=lattice,
errors=True, # Extracts the errors from the provided `sequence`.
track=track_script(particles, ['qf', 'qd'])
))
beam, particle = proton, gamma = 1.25;
seq: sequence, l = 14.0, refer = entry;
qf: quadrupole, k1 = 0.125, l = 1.0, at = 0.0;
s1: sbend, angle = 0.05, e1 = 0.0, e2 = 0.0, fint = 0.0, fintx = 0.0, h1 = 0.0, h2 = 0.0, hgap = 0.0, l = 6.0, at = 1.0;
qd: quadrupole, k1 = -0.125, l = 1.0, tilt = 0.78, at = 7.0;
s2: sbend, angle = 0.05, e1 = 0.0, e2 = 0.0, fint = 0.0, fintx = 0.0, h1 = 0.0, h2 = 0.0, hgap = 0.0, l = 6.0, at = 8.0;
endsequence;
use, sequence = seq;
eoption, add = true;
select, flag = error, clear = true;
select, flag = error, range = "qd";
ealign, dx = 0.01, dy = 0.02;
ealign, dpsi = 0.35;
track, aperture = true, recloss = true, onepass = true, dump = true, onetable = true;
start, x = 0.2014635703501506, px = 0.2662095882206157, y = 0.8166112346953612, py = 0.23180824594831628, t = 0.39202893025542407, pt = 0.5399201490740829;
start, x = 0.14669278350197978, px = 0.24835279589455972, y = 0.27761962358694936, py = 0.8467406897590475, t = 0.9640333584721231, pt = 0.2787731815670358;
start, x = 0.40343846523065574, px = 0.8326952974366683, y = 0.30562559574588954, py = 0.6813336597554878, t = 0.03341727991931742, pt = 0.830850622929301;
start, x = 0.5470678008425934, px = 0.7779247137419353, y = 0.9958333764630148, py = 0.29537177766723244, t = 0.11764775607056943, pt = 0.3208520680352198;
start, x = 0.1049864797528981, px = 0.3752285141312387, y = 0.7203908988777403, py = 0.63130467146256, t = 0.9473415670404751, pt = 0.10299125216411009;
start, x = 0.39960444505659465, px = 0.3735462666225813, y = 0.7160529579941585, py = 0.1100296996131459, t = 0.6683040540297257, pt = 0.5498750540523619;
start, x = 0.0228851551084277, px = 0.876894016227671, y = 0.9587570774187312, py = 0.6097960547963239, t = 0.833647004978768, pt = 0.6247855455798537;
start, x = 0.9209046996988439, px = 0.11623483047061944, y = 0.8216139551556022, py = 0.6395245166612197, t = 0.15343851365337768, pt = 0.9395649827597847;
start, x = 0.20836412057646447, px = 0.32546809192785775, y = 0.7780847178261877, py = 0.9400753865683356, t = 0.35463141128309483, pt = 0.10358923206153958;
start, x = 0.4458752860950078, px = 0.19209207672465944, y = 0.43257782895074914, py = 0.027717813984675876, t = 0.5167697678230143, pt = 0.4922091373466405;
observe, place = qf;
observe, place = qd;
run, turns = 1, maxaper = {0.1, 0.01, 0.1, 0.01, 1.0, 0.1};
write, table = trackloss, file;
endtrack;
In case we wanted to add optics calculations via TWISS
we can just append the relevant command manually:
[5]:
script = create_script(dict(particle='proton', gamma=1.25), sequence=sequence_script(lattice))
script += '\nselect, flag = twiss, full;\ntwiss, save, file = "twiss";'
print(script)
beam, particle = proton, gamma = 1.25;
seq: sequence, l = 14.0, refer = entry;
qf: quadrupole, k1 = 0.125, l = 1.0, at = 0.0;
s1: sbend, angle = 0.05, e1 = 0.0, e2 = 0.0, fint = 0.0, fintx = 0.0, h1 = 0.0, h2 = 0.0, hgap = 0.0, l = 6.0, at = 1.0;
qd: quadrupole, k1 = -0.125, l = 1.0, tilt = 0.78, at = 7.0;
s2: sbend, angle = 0.05, e1 = 0.0, e2 = 0.0, fint = 0.0, fintx = 0.0, h1 = 0.0, h2 = 0.0, hgap = 0.0, l = 6.0, at = 8.0;
endsequence;
use, sequence = seq;
select, flag = twiss, full;
twiss, save, file = "twiss";