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.

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

API

Package latticeconstructor

Top-level package for latticeconstructor.

Development

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.

v0.2.2 (2021-07-07)

Patching more positional data errors.

This package was created with package et-micc,

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

Indices and tables