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')
lattice.Quadrupole(k1=0.625, l=5.0, label='q1')
kicker, quad = lattice
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(quad.makethin(5, style='edge'), end='\n\n')
print('SIMPLE', end='\n\n')
print(quad.makethin(5, style='simple'), end='\n\n')
EDGE
Quadrupole(l=tensor(5.), k1=tensor(0.6250), dk1=tensor(0.), label='q1')
> Drift(l=tensor(0.), label='q1__d0')
> ThinQuadrupole(l=tensor(0.), k1l=tensor(0.6250), dk1l=tensor(0.), label='q1__0')
> Drift(l=tensor(1.2500), label='q1__d1')
> ThinQuadrupole(l=tensor(0.), k1l=tensor(0.6250), dk1l=tensor(0.), label='q1__1')
> Drift(l=tensor(1.2500), label='q1__d2')
> ThinQuadrupole(l=tensor(0.), k1l=tensor(0.6250), dk1l=tensor(0.), label='q1__2')
> Drift(l=tensor(1.2500), label='q1__d3')
> ThinQuadrupole(l=tensor(0.), k1l=tensor(0.6250), dk1l=tensor(0.), label='q1__3')
> Drift(l=tensor(1.2500), label='q1__d4')
> ThinQuadrupole(l=tensor(0.), k1l=tensor(0.6250), dk1l=tensor(0.), label='q1__4')
> Drift(l=tensor(0.), label='q1__d5')
SIMPLE
Quadrupole(l=tensor(5.), k1=tensor(0.6250), dk1=tensor(0.), label='q1')
> Drift(l=tensor(0.5000), label='q1__d0')
> ThinQuadrupole(l=tensor(0.), k1l=tensor(0.6250), dk1l=tensor(0.), label='q1__0')
> Drift(l=tensor(1.), label='q1__d1')
> ThinQuadrupole(l=tensor(0.), k1l=tensor(0.6250), dk1l=tensor(0.), label='q1__1')
> Drift(l=tensor(1.), label='q1__d2')
> ThinQuadrupole(l=tensor(0.), k1l=tensor(0.6250), dk1l=tensor(0.), label='q1__2')
> Drift(l=tensor(1.), label='q1__d3')
> ThinQuadrupole(l=tensor(0.), k1l=tensor(0.6250), dk1l=tensor(0.), label='q1__3')
> Drift(l=tensor(1.), label='q1__d4')
> ThinQuadrupole(l=tensor(0.), k1l=tensor(0.6250), dk1l=tensor(0.), label='q1__4')
> 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'),
Quadrupole(l=tensor(5.), k1=tensor(0.6250), dk1=tensor(0.), label='q1')
> Drift(l=tensor(0.5000), label='q1__d0')
> ThinQuadrupole(l=tensor(0.), k1l=tensor(0.6250), dk1l=tensor(0.), label='q1__0')
> Drift(l=tensor(1.), label='q1__d1')
> ThinQuadrupole(l=tensor(0.), k1l=tensor(0.6250), dk1l=tensor(0.), label='q1__1')
> Drift(l=tensor(1.), label='q1__d2')
> ThinQuadrupole(l=tensor(0.), k1l=tensor(0.6250), dk1l=tensor(0.), label='q1__2')
> Drift(l=tensor(1.), label='q1__d3')
> ThinQuadrupole(l=tensor(0.), k1l=tensor(0.6250), dk1l=tensor(0.), label='q1__3')
> Drift(l=tensor(1.), label='q1__d4')
> ThinQuadrupole(l=tensor(0.), k1l=tensor(0.6250), dk1l=tensor(0.), label='q1__4')
> Drift(l=tensor(0.5000), label='q1__d5')])
The ThinElement
s 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 ThinElement
s, 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'),
ThinQuadrupole(l=tensor(0.), k1l=tensor(0.6250), dk1l=tensor(0.), label='q1__0'),
Drift(l=tensor(1.), label='q1__d1'),
ThinQuadrupole(l=tensor(0.), k1l=tensor(0.6250), dk1l=tensor(0.), label='q1__1'),
Drift(l=tensor(1.), label='q1__d2'),
ThinQuadrupole(l=tensor(0.), k1l=tensor(0.6250), dk1l=tensor(0.), label='q1__2'),
Drift(l=tensor(1.), label='q1__d3'),
ThinQuadrupole(l=tensor(0.), k1l=tensor(0.6250), dk1l=tensor(0.), label='q1__3'),
Drift(l=tensor(1.), label='q1__d4'),
ThinQuadrupole(l=tensor(0.), k1l=tensor(0.6250), dk1l=tensor(0.), label='q1__4'),
Drift(l=tensor(0.5000), label='q1__d5')])
flatten()
returns a generator over all the nested elements.