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']

Example 6: getting int index of elements

[33]:
lblfodo.get_idx('QF')
[33]:
[1, 5]

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