Welcome to latticeconstructor’s documentation!¶
latticeconstructor¶
Package to build accelerator lattice tables based on elements and their definitions. Supposed to be combined with the latticeadaptor Package.
Free software: MIT license
Documentation: https://latticeconstructor.readthedocs.io.
Features¶
Construct accelerator element table manually
Edit accelerator element tables
Load definitions and lattice from: Elegant, Madx sequence file, Madx Line definition file.
Non-standard Dependency¶
Installation¶
Stable release¶
To install latticeconstructor, run this command in your terminal:
$ pip install latticeconstructor
This is the preferred method to install latticeconstructor, as it will always install the most recent stable release.
If you don’t have pip installed, this Python installation guide can guide you through the process.
From sources¶
The sources for latticeconstructor can be downloaded from the Github repo.
You can either clone the public repository:
$ git clone git://github.com/tomerten/latticeconstructor
Or download the tarball:
$ curl -OL https://github.com/tomerten/latticeconstructor/tarball/master
Once you have a copy of the source, you can install it with:
$ python setup.py install
Development¶
Tom Mertens <your.email@whatev.er>
History¶
v0.0.0 (2021-05-08)¶
v0.1.0 (2021-05-16)¶
v0.2.0 (2021-05-21)¶
Switched over to using Lark in combination with LatticeJson
v0.2.1 (2021-05-21)¶
Patching some postional data errors.
Tutorial [Basic]¶
The goal of the package is to allow a quick and flexible way of constructing a table representing an accelerator lattice. This table should be used as input in the package latticeadaptor for generating accelerator lattice files in various formats and apply more advanced edits if necessary.
[1]:
from latticeconstructor.core import LatticeBuilderLine
lblfodo = LatticeBuilderLine()
Class properties¶
[2]:
lblfodo.__dict__
[2]:
{'lattice': [],
'definitions': {},
'table': None,
'positions': None,
'history': <queue.LifoQueue at 0x7fc2640e0f70>}
Class methods¶
[3]:
from types import FunctionType
[x for x, y in LatticeBuilderLine.__dict__.items() if (type(y) == FunctionType) and not x.startswith('_')]
[3]:
['add_def',
'add_element',
'replace_element',
'replace_list',
'insert_element_before',
'insert_element_after',
'remove_element',
'remove_from_to',
'get_idx',
'build_table',
'load_from_file',
'undo']
Example 1: FODO¶
[4]:
# Element definitions
lblfodo.add_def(
{
"QF": {"family" : "KQUAD", "L": 0.342, "K1": 0.4900, "N_KICKS": 16},
"QD": {"family" : "KQUAD", "L": 0.668, "K1": -0.4999, "N_KICKS": 16},
"D": {"family" : "DRIF" , "L": 3.5805},
"W1": {"family" : "WATCH", "L": 0, "filename":"\"%s-%03ld.w1\"","mode": "coordinates"}
}
)
# add elements in order
lblfodo.add_element(["W1","QF","D","QD","D","QF"])
# show lattice table
lblfodo.table
[4]:
family | L | filename | mode | name | K1 | N_KICKS | at | |
---|---|---|---|---|---|---|---|---|
0 | MARKER | 0.0000 | "%s-%03ld.w1" | coordinates | W1 | NaN | NaN | 0.00000 |
1 | QUADRUPOLE | 0.3420 | NaN | NaN | QF | 0.4900 | 16.0 | 0.17100 |
2 | DRIFT | 3.5805 | NaN | NaN | D | NaN | NaN | 2.13225 |
3 | QUADRUPOLE | 0.6680 | NaN | NaN | QD | -0.4999 | 16.0 | 4.25650 |
4 | DRIFT | 3.5805 | NaN | NaN | D | NaN | NaN | 6.38075 |
5 | QUADRUPOLE | 0.3420 | NaN | NaN | QF | 0.4900 | 16.0 | 8.34200 |
[5]:
# check the defintions
lblfodo.definitions
[5]:
{'QF': {'family': 'QUADRUPOLE',
'L': 0.342,
'K1': 0.49,
'N_KICKS': 16,
'name': 'QF'},
'QD': {'family': 'QUADRUPOLE',
'L': 0.668,
'K1': -0.4999,
'N_KICKS': 16,
'name': 'QD'},
'D': {'family': 'DRIFT', 'L': 3.5805, 'name': 'D'},
'W1': {'family': 'MARKER',
'L': 0,
'filename': '"%s-%03ld.w1"',
'mode': 'coordinates',
'name': 'W1'}}
[6]:
# check the lattice line definition (expanded)
lblfodo.lattice
[6]:
['W1', 'QF', 'D', 'QD', 'D', 'QF']
Example 2: Adding definitions and elements¶
Adding an element that is not defined will add it to the lattice but will not auto-update the table.
[7]:
lblfodo.add_element("END")
lblfodo.lattice
Table not updated - not all elements defined.
{'END'}
[7]:
['W1', 'QF', 'D', 'QD', 'D', 'QF', 'END']
[8]:
lblfodo.table
[8]:
family | L | filename | mode | name | K1 | N_KICKS | at | |
---|---|---|---|---|---|---|---|---|
0 | MARKER | 0.0000 | "%s-%03ld.w1" | coordinates | W1 | NaN | NaN | 0.00000 |
1 | QUADRUPOLE | 0.3420 | NaN | NaN | QF | 0.4900 | 16.0 | 0.17100 |
2 | DRIFT | 3.5805 | NaN | NaN | D | NaN | NaN | 2.13225 |
3 | QUADRUPOLE | 0.6680 | NaN | NaN | QD | -0.4999 | 16.0 | 4.25650 |
4 | DRIFT | 3.5805 | NaN | NaN | D | NaN | NaN | 6.38075 |
5 | QUADRUPOLE | 0.3420 | NaN | NaN | QF | 0.4900 | 16.0 | 8.34200 |
To fix this, add the defintion and manually update the table.
[9]:
# adding a marker
lblfodo.add_def({"END": {"family": "MARK", "L": 0.0 } })
# build table manually
lblfodo.build_table()
[10]:
lblfodo.lattice
[10]:
['W1', 'QF', 'D', 'QD', 'D', 'QF', 'END']
[11]:
# we highlight the change
lblfodo.table.style.apply(lambda x: ['background: lightgreen' if x.name in [6] else '' for i in x], axis=1)
[11]:
family | L | filename | mode | name | K1 | N_KICKS | at | |
---|---|---|---|---|---|---|---|---|
0 | MARKER | 0.000000 | "%s-%03ld.w1" | coordinates | W1 | nan | nan | 0.000000 |
1 | QUADRUPOLE | 0.342000 | nan | nan | QF | 0.490000 | 16.000000 | 0.171000 |
2 | DRIFT | 3.580500 | nan | nan | D | nan | nan | 2.132250 |
3 | QUADRUPOLE | 0.668000 | nan | nan | QD | -0.499900 | 16.000000 | 4.256500 |
4 | DRIFT | 3.580500 | nan | nan | D | nan | nan | 6.380750 |
5 | QUADRUPOLE | 0.342000 | nan | nan | QF | 0.490000 | 16.000000 | 8.342000 |
6 | MARKER | 0.000000 | nan | nan | END | nan | nan | 8.513000 |
Example 3: Replacing elements¶
Replace single element¶
[12]:
lblfodo.replace_element('END','W1')
[13]:
lblfodo.lattice
[13]:
['W1', 'QF', 'D', 'QD', 'D', 'QF', 'W1']
[14]:
# we again highlight the change
lblfodo.table.style.apply(lambda x: ['background: orange' if x.name in [6] else '' for i in x], axis=1)
[14]:
family | L | filename | mode | name | K1 | N_KICKS | at | |
---|---|---|---|---|---|---|---|---|
0 | MARKER | 0.000000 | "%s-%03ld.w1" | coordinates | W1 | nan | nan | 0.000000 |
1 | QUADRUPOLE | 0.342000 | nan | nan | QF | 0.490000 | 16.000000 | 0.171000 |
2 | DRIFT | 3.580500 | nan | nan | D | nan | nan | 2.132250 |
3 | QUADRUPOLE | 0.668000 | nan | nan | QD | -0.499900 | 16.000000 | 4.256500 |
4 | DRIFT | 3.580500 | nan | nan | D | nan | nan | 6.380750 |
5 | QUADRUPOLE | 0.342000 | nan | nan | QF | 0.490000 | 16.000000 | 8.342000 |
6 | MARKER | 0.000000 | "%s-%03ld.w1" | coordinates | W1 | nan | nan | 8.513000 |
[15]:
# undo to keep the previous table
lblfodo.undo()
[16]:
# check if undo worked
lblfodo.lattice
[16]:
['W1', 'QF', 'D', 'QD', 'D', 'QF', 'END']
[17]:
# check if undo worked
lblfodo.table.style.apply(lambda x: ['background: lightgreen' if x.name in [6] else '' for i in x], axis=1)
[17]:
family | L | filename | mode | name | K1 | N_KICKS | at | |
---|---|---|---|---|---|---|---|---|
0 | MARKER | 0.000000 | "%s-%03ld.w1" | coordinates | W1 | nan | nan | 0.000000 |
1 | QUADRUPOLE | 0.342000 | nan | nan | QF | 0.490000 | 16.000000 | 0.171000 |
2 | DRIFT | 3.580500 | nan | nan | D | nan | nan | 2.132250 |
3 | QUADRUPOLE | 0.668000 | nan | nan | QD | -0.499900 | 16.000000 | 4.256500 |
4 | DRIFT | 3.580500 | nan | nan | D | nan | nan | 6.380750 |
5 | QUADRUPOLE | 0.342000 | nan | nan | QF | 0.490000 | 16.000000 | 8.342000 |
6 | MARKER | 0.000000 | nan | nan | END | nan | nan | 8.513000 |
Replace a series of elements¶
[18]:
start_index = 5
stop_index = 6
lblfodo.replace_list(start_index,stop_index,'QF')
[19]:
lblfodo.lattice
[19]:
['W1', 'QF', 'D', 'QD', 'D', 'QF']
[20]:
lblfodo.table.style.apply(lambda x: ['background: orange' if x.name in [5] else '' for i in x], axis=1)
[20]:
family | L | filename | mode | name | K1 | N_KICKS | at | |
---|---|---|---|---|---|---|---|---|
0 | MARKER | 0.000000 | "%s-%03ld.w1" | coordinates | W1 | nan | nan | 0.000000 |
1 | QUADRUPOLE | 0.342000 | nan | nan | QF | 0.490000 | 16.000000 | 0.171000 |
2 | DRIFT | 3.580500 | nan | nan | D | nan | nan | 2.132250 |
3 | QUADRUPOLE | 0.668000 | nan | nan | QD | -0.499900 | 16.000000 | 4.256500 |
4 | DRIFT | 3.580500 | nan | nan | D | nan | nan | 6.380750 |
5 | QUADRUPOLE | 0.342000 | nan | nan | QF | 0.490000 | 16.000000 | 8.342000 |
Example 4: Inserting elements¶
Insert element before index¶
[21]:
lblfodo.insert_element_before("END",5)
[22]:
lblfodo.table.style.apply(lambda x: ['background: orange' if x.name in [5] else '' for i in x], axis=1)
[22]:
family | L | filename | mode | name | K1 | N_KICKS | at | |
---|---|---|---|---|---|---|---|---|
0 | MARKER | 0.000000 | "%s-%03ld.w1" | coordinates | W1 | nan | nan | 0.000000 |
1 | QUADRUPOLE | 0.342000 | nan | nan | QF | 0.490000 | 16.000000 | 0.171000 |
2 | DRIFT | 3.580500 | nan | nan | D | nan | nan | 2.132250 |
3 | QUADRUPOLE | 0.668000 | nan | nan | QD | -0.499900 | 16.000000 | 4.256500 |
4 | DRIFT | 3.580500 | nan | nan | D | nan | nan | 6.380750 |
5 | MARKER | 0.000000 | nan | nan | END | nan | nan | 8.171000 |
6 | QUADRUPOLE | 0.342000 | nan | nan | QF | 0.490000 | 16.000000 | 8.342000 |
[23]:
# undo to keep the table
lblfodo.undo()
Insert element after index¶
[24]:
lblfodo.insert_element_after("END",5)
[25]:
lblfodo.table.style.apply(lambda x: ['background: orange' if x.name in [6] else '' for i in x], axis=1)
[25]:
family | L | filename | mode | name | K1 | N_KICKS | at | |
---|---|---|---|---|---|---|---|---|
0 | MARKER | 0.000000 | "%s-%03ld.w1" | coordinates | W1 | nan | nan | 0.000000 |
1 | QUADRUPOLE | 0.342000 | nan | nan | QF | 0.490000 | 16.000000 | 0.171000 |
2 | DRIFT | 3.580500 | nan | nan | D | nan | nan | 2.132250 |
3 | QUADRUPOLE | 0.668000 | nan | nan | QD | -0.499900 | 16.000000 | 4.256500 |
4 | DRIFT | 3.580500 | nan | nan | D | nan | nan | 6.380750 |
5 | QUADRUPOLE | 0.342000 | nan | nan | QF | 0.490000 | 16.000000 | 8.342000 |
6 | MARKER | 0.000000 | nan | nan | END | nan | nan | 8.513000 |
[26]:
lblfodo.lattice
[26]:
['W1', 'QF', 'D', 'QD', 'D', 'QF', 'END']
[27]:
lblfodo.undo()
lblfodo.lattice
[27]:
['W1', 'QF', 'D', 'QD', 'D', 'QF']
Example 5: Removing elements¶
Remove at index¶
[28]:
lblfodo.remove_element(2)
[29]:
lblfodo.lattice
[29]:
['W1', 'QF', 'QD', 'D', 'QF']
[30]:
lblfodo.undo()
lblfodo.lattice
[30]:
['W1', 'QF', 'D', 'QD', 'D', 'QF']
Remove between indices¶
[31]:
lblfodo.remove_from_to(2,4)
lblfodo.lattice
[31]:
['W1', 'QF', 'QF']
[32]:
lblfodo.undo()
lblfodo.lattice
[32]:
['W1', 'QF', 'D', 'QD', 'D', 'QF']
Tutorial [advanced]¶
The more advanced tutorial shows how to read in definitions and lattice constructions from various standard formats and prepare them to be manipulated by the LatticeBuilderLine class.
Elegant¶
[34]:
# generate lte lattice file
elements ={
"QF": {"type" : "KQUAD", "L": 0.342, "K1": 0.4900, "N_KICKS": 16},
"QD": {"type" : "KQUAD", "L": 0.668, "K1": -0.4999, "N_KICKS": 16},
"D": {"type" : "DRIF" , "L": 3.5805},
"W1": {"type" : "WATCH", "filename":"\"%s-%03ld.w1\"","mode": "coordinates"}
}
FODOstr = "! FODO cell used for studying TRIBs\n\n"
stringlist = ["{:6}: {}".format(k,", ".join(["{}={:15.12f}".format(kk,vv)
if not isinstance(vv,str)
else "{}={}".format(kk,vv)
if kk!="type" else "{}".format(vv) for kk,vv in v.items()]))
for k,v in elements.items()]
line = ["W1","QF","D","QD","D","QF"]
linestr = "{:6}: LINE=({})".format("FODO",",".join(line))
FODOstr += "\n".join(stringlist)
FODOstr += "\n\n"
FODOstr += linestr
print(FODOstr)
with open("FODO_TRIB.lte","w") as f:
f.write(FODOstr)
! FODO cell used for studying TRIBs
QF : KQUAD, L= 0.342000000000, K1= 0.490000000000, N_KICKS=16.000000000000
QD : KQUAD, L= 0.668000000000, K1=-0.499900000000, N_KICKS=16.000000000000
D : DRIF, L= 3.580500000000
W1 : WATCH, filename="%s-%03ld.w1", mode=coordinates
FODO : LINE=(W1,QF,D,QD,D,QF)
[35]:
lbllte = LatticeBuilderLine()
lbllte.load_from_file("FODO_TRIB.lte",ftype='lte')
[36]:
lbllte.lattice
[36]:
['W1', 'QF', 'D', 'QD', 'D', 'QF']
[37]:
lbllte.definitions
[37]:
{'QF': {'family': 'QUADRUPOLE', 'L': 0.342, 'K1': 0.49, 'N_KICKS': 16.0},
'QD': {'family': 'QUADRUPOLE', 'L': 0.668, 'K1': -0.4999, 'N_KICKS': 16.0},
'D': {'family': 'DRIFT', 'L': 3.5805},
'W1': {'family': 'MARKER', 'FILENAME': '%s-%03ld.w1', 'MODE': 'coordinates'}}
[38]:
# the table needs to be updated manually
lbllte.table
[39]:
lbllte.build_table()
[40]:
lbllte.table
[40]:
family | FILENAME | MODE | name | L | K1 | N_KICKS | at | |
---|---|---|---|---|---|---|---|---|
0 | MARKER | %s-%03ld.w1 | coordinates | W1 | NaN | NaN | NaN | NaN |
1 | QUADRUPOLE | NaN | NaN | QF | 0.3420 | 0.4900 | 16.0 | 0.17100 |
2 | DRIFT | NaN | NaN | D | 3.5805 | NaN | NaN | 2.13225 |
3 | QUADRUPOLE | NaN | NaN | QD | 0.6680 | -0.4999 | 16.0 | 4.25650 |
4 | DRIFT | NaN | NaN | D | 3.5805 | NaN | NaN | 6.38075 |
5 | QUADRUPOLE | NaN | NaN | QF | 0.3420 | 0.4900 | 16.0 | 8.34200 |
Remember to delete the columns that are attribute names not recognized by MADX before writing it to a sequence file.
MADX - Sequence file¶
[41]:
FODOseqstr = """
QF : QUADRUPOLE, L := 0.50 , K1 := 1.00;
QD : QUADRUPOLE, L := 1.00 , K1 := -1.00;
D1 : DRIFT, L := 1.00;
D2 : DRIFT, L := 1.00;
FODO: SEQUENCE, L=4.00;
QF, at = 0.25;
D1, at = 1.00;
QD, at = 2.00;
D2, at = 3.00;
QF, at = 3.75;
ENDSEQUENCE;
"""
print(FODOseqstr)
with open("FODO_TRIB.seq","w") as f:
f.write(FODOseqstr)
QF : QUADRUPOLE, L := 0.50 , K1 := 1.00;
QD : QUADRUPOLE, L := 1.00 , K1 := -1.00;
D1 : DRIFT, L := 1.00;
D2 : DRIFT, L := 1.00;
FODO: SEQUENCE, L=4.00;
QF, at = 0.25;
D1, at = 1.00;
QD, at = 2.00;
D2, at = 3.00;
QF, at = 3.75;
ENDSEQUENCE;
[42]:
lblseq = LatticeBuilderLine()
lblseq.load_from_file("FODO_TRIB.seq",ftype='madx')
lblseq.lattice
[42]:
['QF', 'D1', 'QD', 'D2', 'QF']
[43]:
lblseq.definitions
[43]:
{'QF': {'family': 'QUADRUPOLE', 'L': 0.5, 'K1': 1.0},
'QD': {'family': 'QUADRUPOLE', 'L': 1.0, 'K1': -1.0},
'D1': {'family': 'DRIFT', 'L': 1.0},
'D2': {'family': 'DRIFT', 'L': 1.0}}
[44]:
lblseq.build_table()
lblseq.table
[44]:
family | L | K1 | name | at | |
---|---|---|---|---|---|
0 | QUADRUPOLE | 0.5 | 1.0 | QF | 0.25 |
1 | DRIFT | 1.0 | NaN | D1 | 1.00 |
2 | QUADRUPOLE | 1.0 | -1.0 | QD | 2.00 |
3 | DRIFT | 1.0 | NaN | D2 | 3.00 |
4 | QUADRUPOLE | 0.5 | 1.0 | QF | 3.75 |
[45]:
lblseq.positions
[45]:
name | at | |
---|---|---|
0 | QF | 0.25 |
1 | D1 | 1.00 |
2 | QD | 2.00 |
3 | D2 | 3.00 |
4 | QF | 3.75 |
Note that the if the madx input file does not contain position data, it will be calculated from the element lengths.
MADX Line def¶
[46]:
FODOlinestr = """
QF : QUADRUPOLE, L := 0.50 , K1 := 1.00;
QD : QUADRUPOLE, L := 1.00 , K1 := -1.00;
D1 : DRIFT, L := 1.00;
D2 : DRIFT, L := 1.00;
FODO: LINE = (QF,D1,QD,D2,QF);
"""
print(FODOlinestr)
with open("FODO_TRIB.madx","w") as f:
f.write(FODOlinestr)
QF : QUADRUPOLE, L := 0.50 , K1 := 1.00;
QD : QUADRUPOLE, L := 1.00 , K1 := -1.00;
D1 : DRIFT, L := 1.00;
D2 : DRIFT, L := 1.00;
FODO: LINE = (QF,D1,QD,D2,QF);
[47]:
lblmadx = LatticeBuilderLine()
lblmadx.load_from_file("FODO_TRIB.madx",ftype='madx')
lblmadx.lattice
[47]:
['QF', 'D1', 'QD', 'D2', 'QF']
[48]:
lblmadx.definitions
[48]:
{'QF': {'family': 'QUADRUPOLE', 'L': 0.5, 'K1': 1.0},
'QD': {'family': 'QUADRUPOLE', 'L': 1.0, 'K1': -1.0},
'D1': {'family': 'DRIFT', 'L': 1.0},
'D2': {'family': 'DRIFT', 'L': 1.0}}
[49]:
lblmadx.build_table()
lblmadx.table
[49]:
family | L | K1 | name | at | |
---|---|---|---|---|---|
0 | QUADRUPOLE | 0.5 | 1.0 | QF | 0.25 |
1 | DRIFT | 1.0 | NaN | D1 | 1.00 |
2 | QUADRUPOLE | 1.0 | -1.0 | QD | 2.00 |
3 | DRIFT | 1.0 | NaN | D2 | 3.00 |
4 | QUADRUPOLE | 0.5 | 1.0 | QF | 3.75 |