Converting thick to thin elements¶

Not all lattice elements support thick tracking and so converting these elements to thin slices is necessary before doing particle tracking or optics calculations. Elements can be converted to their thin representation using the makethin method:

[1]:

from dipas.build import Lattice

with Lattice({'particle': 'proton', 'beta': 0.6}) as lattice:
lattice.HKicker(kick=0.5, l=1.0, label='hk1')

print(kicker)
print(kicker.makethin(5))

HKicker(l=tensor(1.), hkick=tensor(0.5000), vkick=tensor(0.), kick=tensor(0.5000), label='hk1')
HKicker(l=tensor(1.), hkick=tensor(0.5000), vkick=tensor(0.), kick=tensor(0.5000), label='hk1')
> Drift(l=tensor(0.0833), label='hk1__d0')
> HKicker(l=tensor(0.), hkick=tensor(0.1000), vkick=tensor(0.), kick=tensor(0.1000), label='hk1__0')
> Drift(l=tensor(0.2083), label='hk1__d1')
> HKicker(l=tensor(0.), hkick=tensor(0.1000), vkick=tensor(0.), kick=tensor(0.1000), label='hk1__1')
> Drift(l=tensor(0.2083), label='hk1__d2')
> HKicker(l=tensor(0.), hkick=tensor(0.1000), vkick=tensor(0.), kick=tensor(0.1000), label='hk1__2')
> Drift(l=tensor(0.2083), label='hk1__d3')
> HKicker(l=tensor(0.), hkick=tensor(0.1000), vkick=tensor(0.), kick=tensor(0.1000), label='hk1__3')
> Drift(l=tensor(0.2083), label='hk1__d4')
> HKicker(l=tensor(0.), hkick=tensor(0.1000), vkick=tensor(0.), kick=tensor(0.1000), label='hk1__4')
> Drift(l=tensor(0.0833), label='hk1__d5')


The makethin method returns a elements.ThinElement object, a special version of a more general Segment. This ThinElement contains the thin kicker slices as well as the drift space before, between and after the slices. The distribution of drift space depends on the selected slicing style. By default the TEAPOT style is used. Other available slicing styles include SIMPLE and EDGE. For more details consider the documentation of the elements.ThinElement.create_thin_sequence method.

Let’s compare the SIMPLE and EDGE style for the quadrupole element:

[2]:

print('EDGE', end='\n\n')
print('SIMPLE', end='\n\n')

EDGE

> Drift(l=tensor(0.), label='q1__d0')
> Drift(l=tensor(1.2500), label='q1__d1')
> Drift(l=tensor(1.2500), label='q1__d2')
> Drift(l=tensor(1.2500), label='q1__d3')
> Drift(l=tensor(1.2500), label='q1__d4')
> Drift(l=tensor(0.), label='q1__d5')

SIMPLE

> Drift(l=tensor(0.5000), label='q1__d0')
> Drift(l=tensor(1.), label='q1__d1')
> Drift(l=tensor(1.), label='q1__d2')
> Drift(l=tensor(1.), label='q1__d3')
> Drift(l=tensor(1.), label='q1__d4')
> Drift(l=tensor(0.5000), label='q1__d5')



EDGE places the outermost slices directly at the edges of the thick element, while SIMPLE adds a margin that is half the in-between distance of slices.

We can also convert whole lattices represented by Segment objects to thin elements. Here we can choose the number of slices as well as the style via a dict which maps element identifiers to the particular values. The identifiers can be strings for comparing element labels, regex patterns for matching element labels or lattice element types, similar to element selection via lattice[identifier] (see inspecting lattices).

[3]:

from dipas.elements import HKicker, Quadrupole, Segment

lattice = Segment(lattice)
thin = lattice.makethin({HKicker: 2, 'q1': 5}, style={'hk1': 'edge', Quadrupole: 'simple'})
print(thin)

Segment(elements=[HKicker(l=tensor(1.), hkick=tensor(0.5000), vkick=tensor(0.), kick=tensor(0.5000), label='hk1')
> Drift(l=tensor(0.), label='hk1__d0')
> HKicker(l=tensor(0.), hkick=tensor(0.2500), vkick=tensor(0.), kick=tensor(0.2500), label='hk1__0')
> Drift(l=tensor(1.), label='hk1__d1')
> HKicker(l=tensor(0.), hkick=tensor(0.2500), vkick=tensor(0.), kick=tensor(0.2500), label='hk1__1')
> Drift(l=tensor(0.), label='hk1__d2'),
> Drift(l=tensor(0.5000), label='q1__d0')
> Drift(l=tensor(1.), label='q1__d1')
> Drift(l=tensor(1.), label='q1__d2')
> Drift(l=tensor(1.), label='q1__d3')
> Drift(l=tensor(1.), label='q1__d4')
> Drift(l=tensor(0.5000), label='q1__d5')])


The ThinElements represent their thick counterparts which are still accessible via the .base attribute. Also the base label is inherited (element access works as explained in inspecting lattices):

[4]:

print('q1.base: ', thin['q1'].base)
print('q1.label: ', thin[1].label)
for drift in thin['q1']['q1__d*']:
print(drift)

q1.base:  Quadrupole(l=tensor(5.), k1=tensor(0.6250), dk1=tensor(0.), label='q1')
q1.label:  q1
Drift(l=tensor(0.5000), label='q1__d0')
Drift(l=tensor(1.), label='q1__d1')
Drift(l=tensor(1.), label='q1__d2')
Drift(l=tensor(1.), label='q1__d3')
Drift(l=tensor(1.), label='q1__d4')
Drift(l=tensor(0.5000), label='q1__d5')


We can also flatten such a nested Segment, containing ThinElements, using the flat (or flatten) method:

[5]:

print(thin.flat())

Segment(elements=[Drift(l=tensor(0.), label='hk1__d0'),
HKicker(l=tensor(0.), hkick=tensor(0.2500), vkick=tensor(0.), kick=tensor(0.2500), label='hk1__0'),
Drift(l=tensor(1.), label='hk1__d1'),
HKicker(l=tensor(0.), hkick=tensor(0.2500), vkick=tensor(0.), kick=tensor(0.2500), label='hk1__1'),
Drift(l=tensor(0.), label='hk1__d2'),
Drift(l=tensor(0.5000), label='q1__d0'),

flatten() returns a generator over all the nested elements.