Parameters#
Parameters are objects of class Param which can be used instead of numeric values as Element attributes.
Parameters are initialised with a scalar numeric value. They have an optional name, used when printtig the parameter. Optionally they can be bounded.
total_length = Param(2.5, name="total_length")
dlength = Param(1.0, name="dlength", bounds=(0.0, 5.0))
print(f"{total_length}: {total_length!r}")
print(f"{dlength}: {dlength!r}")
total_length: 2.5
dlength: 1.0
The value of a parameter can be read or modified through its value property. set() and get() methods are also available:
total_length.value = 2.4
print(f"{total_length}: {total_length!r}")
total_length: 2.4
total_length.set(2.3)
print(total_length.get())
2.3
Arithmetic combinations of parameters create new read-only parameters of class ParamBase, whose value is permanently kept up-to-date:
qlength = total_length - dlength
print(f"{qlength}: {qlength!r}")
total_length-dlength: 1.2999999999999998
dlength.value = 0.9
print(f"{qlength}: {qlength!r}")
total_length-dlength: 1.4
Parameters may be assigned to Element attributes, for instance on initialisation:
dr1 = at.Drift('DR1', dlength)
qf1 = at.Quadrupole('QF1', qlength, 0.6)
print(dr1)
print(qf1)
Drift:
FamName: DR1
Length: dlength
PassMethod: DriftPass
Quadrupole:
FamName: QF1
Length: total_length-dlength
PassMethod: StrMPoleSymplectic4Pass
MaxOrder: 1
NumIntSteps: 10
PolynomA: [0. 0.]
PolynomB: [0. 0.6]
K: 0.6
The Element attributes keep their type so that all the processing of elements either in python functions or in C integrators is unchanged:
print(dr1.Length, type(dr1.Length))
0.9 <class 'float'>
Assigning parameters#
To a single element#
Parameters may be assigned to Element attributes in several ways:
At element creation:
dr2 = at.Drift('DR2', dlength)
By converting a numeric attribute into a parameter:
qd1 = at.Quadrupole('QD1', 0.5, -0.4)
ql = qd1.parameterise('Length')
vkick = qd1.parameterise("PolynomA", index=0, name="vkick")
print(f"{ql}: {ql!r}")
param3: 0.5
By normal assignment, only for scalar parameters:
qd1.Length = Param(0.2)
print(qd1)
Quadrupole:
FamName: QD1
Length: param5
PassMethod: StrMPoleSymplectic4Pass
MaxOrder: 1
NumIntSteps: 10
PolynomA: [vkick 0.0]
PolynomB: [ 0. -0.4]
K: -0.4
With the set_parameter() method:
qstrength = Param(-0.5, name='quad_strength')
qd1.set_parameter('PolynomB', qstrength, index=1)
print(qd1)
Quadrupole:
FamName: QD1
Length: param5
PassMethod: StrMPoleSymplectic4Pass
MaxOrder: 1
NumIntSteps: 10
PolynomA: [vkick 0.0]
PolynomB: [0.0 quad_strength]
K: -0.5
To selected elements of a Lattice#
To act on several elements in a single step, Lattice methods similar to Element methods are available.
Convert numeric attributes into parameters:
The attribute of all the selected elements is replaced by a single parameter whose initial value is the average of the original values.
kf1 = ring.parameterise('QF1[AE]', 'PolynomB', index=1, name='kf1')
print(f"{kf1}: {kf1!r}")
print(ring[5])
kf1: np.float64(2.5394599781303304)
Quadrupole:
FamName: QF1A
Length: 0.311896
PassMethod: StrMPoleSymplectic4Pass
FringeQuadEntrance: 1
FringeQuadExit: 1
MaxOrder: 1
NumIntSteps: 20
PolynomA: [0. 0.]
PolynomB: [0.0 kf1]
K: 2.5394599781303304
Use the set_parameter() method:
The attribute of all the selected elements is replaced by the provided parameter,
lf1 = Param(0.311896, name='lf1')
ring.set_parameter('QF1[AE]', 'Length', lf1)
print(ring[117])
Quadrupole:
FamName: QF1E
Length: lf1
PassMethod: StrMPoleSymplectic4Pass
FringeQuadEntrance: 1
FringeQuadExit: 1
MaxOrder: 1
NumIntSteps: 20
PolynomA: [0. 0.]
PolynomB: [0.0 kf1]
K: 2.5394599781303304
Once a Parameter is assigned to an attribute, it acquires the type and the constraints of the attribute. For instance:
num_int_steps = Param(14.4, name="num_int_steps")
ring.set_parameter(at.Multipole, 'NumIntSteps', num_int_steps)
print(ring[5].NumIntSteps, type(ring[5].NumIntSteps))
14 <class 'int'>
num_int_steps.value = -5
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
Cell In[17], line 1
----> 1 num_int_steps.value = -5
File ~/checkouts/readthedocs.org/user_builds/at/envs/parameters3/lib/python3.11/site-packages/at/lattice/parameters.py:70, in Param.value(self, value)
68 @value.setter
69 def value(self, value: Number):
---> 70 self.set(value)
File ~/checkouts/readthedocs.org/user_builds/at/envs/parameters3/lib/python3.11/site-packages/at/lattice/variables.py:360, in VariableBase.set(self, value, **setkw)
350 r"""Set the variable value.
351
352 Args:
(...) 357 They augment the keyword arguments given in the constructor.
358 """
359 self.check_bounds(value)
--> 360 self._setfun(value, *self.args, **(self.kwargs | setkw))
361 if np.isnan(self._initial):
362 self._initial = value
File ~/checkouts/readthedocs.org/user_builds/at/envs/parameters3/lib/python3.11/site-packages/at/lattice/parameters.py:59, in Param._setfun(self, value, **_)
58 def _setfun(self, value: Number, **_) -> None:
---> 59 self._evaluator = _Constant(self._conversion(value))
File ~/checkouts/readthedocs.org/user_builds/at/envs/parameters3/lib/python3.11/site-packages/at/lattice/elements/element_object.py:60, in Element.<lambda>(v)
42 """Base class for AT elements."""
44 _BUILD_ATTRIBUTES: ClassVar[list[str]] = ["FamName"]
45 _conversions: ClassVar[dict] = {
46 "FamName": str,
47 "PassMethod": str,
48 "Length": _float,
49 "R1": _array66,
50 "R2": _array66,
51 "T1": lambda v: _array(v, (6,)),
52 "T2": lambda v: _array(v, (6,)),
53 "RApertures": lambda v: _array(v, (4,)),
54 "EApertures": lambda v: _array(v, (2,)),
55 "KickAngle": lambda v: _array(v, (2,)),
56 "PolynomB": _array,
57 "PolynomA": _array,
58 "BendingAngle": _float,
59 "MaxOrder": _int,
---> 60 "NumIntSteps": lambda v: _int(v, vmin=0),
61 "Energy": _float,
62 }
63 _file_classname: ClassVar[str]
65 _entrance_fields: ClassVar[list[str]] = ["T1", "R1"]
File ~/checkouts/readthedocs.org/user_builds/at/envs/parameters3/lib/python3.11/site-packages/at/lattice/elements/conversions.py:23, in _int(value, vmin, vmax)
21 if vmin is not None and intv < vmin:
22 msg = f"Value must be greater of equal to {vmin}"
---> 23 raise ValueError(msg)
24 if vmax is not None and intv > vmax:
25 msg = f"Value must be smaller of equal to {vmax}"
ValueError: Value must be greater of equal to 0
The parameter behaves as the attribute.
Retrieving parameters#
Since the values of Element attributes keep their original type, they cannot be used to access the underlying parameter. The only way to retrieve it is to use the get_parameter() method. a TypeError is raised if the attribute is not
a Parameter.
ql = qf1.get_parameter('Length')
print(f"{ql}: {ql!r}")
print("ql is qlength:", ql is qlength)
total_length-dlength: 1.4
ql is qlength: True
This also works for items in array attributes:
qs = qd1.get_parameter('PolynomB', index=1)
print(f"{qs}: {qs!r}")
print("qs is qstrength:", qs is qstrength)
quad_strength: -0.5
qs is qstrength: True
Checking parametrisation#
The is_parameterised() method may be applied to:
a full element: it returns true is any of its attributes is a parameter,
an array attribute: it returns true if any of its items is a parameter,
a scalar attribute or an item of an array.
print(qd1.is_parameterised())
print(qd1.is_parameterised('PolynomB', index=1))
print(qd1.is_parameterised('PolynomB', index=0))
True
True
False
Removing parameters#
Removing the parameters will “freeze” the element at its current value. The unparameterise() method is defined for both Element and Lattice, and may be applied to:
a full element: all the parameters are replaced by their value,
an array attribute: the whole
ParamArrayis replaced by a numpy array,a scalar attribute or an item of an array.
In an Element#
print(qd1)
qd1.unparameterise('Length')
print(qd1)
Quadrupole:
FamName: QD1
Length: param5
PassMethod: StrMPoleSymplectic4Pass
MaxOrder: 1
NumIntSteps: 10
PolynomA: [vkick 0.0]
PolynomB: [0.0 quad_strength]
K: -0.5
Quadrupole:
FamName: QD1
Length: 0.2
PassMethod: StrMPoleSymplectic4Pass
MaxOrder: 1
NumIntSteps: 10
PolynomA: [vkick 0.0]
PolynomB: [0.0 quad_strength]
K: -0.5
qd1.unparameterise('PolynomB', 1)
print(qd1)
Quadrupole:
FamName: QD1
Length: 0.2
PassMethod: StrMPoleSymplectic4Pass
MaxOrder: 1
NumIntSteps: 10
PolynomA: [vkick 0.0]
PolynomB: [ 0. -0.5]
K: -0.5
In a Lattice#
print(ring[5])
ring.unparameterise('QF1[AE]', 'PolynomB', index=1)
print(ring[5])
Quadrupole:
FamName: QF1A
Length: lf1
PassMethod: StrMPoleSymplectic4Pass
FringeQuadEntrance: 1
FringeQuadExit: 1
MaxOrder: 1
NumIntSteps: num_int_steps
PolynomA: [0. 0.]
PolynomB: [0.0 kf1]
K: 2.5394599781303304
Quadrupole:
FamName: QF1A
Length: lf1
PassMethod: StrMPoleSymplectic4Pass
FringeQuadEntrance: 1
FringeQuadExit: 1
MaxOrder: 1
NumIntSteps: num_int_steps
PolynomA: [0. 0.]
PolynomB: [0. 2.53945998]
K: 2.5394599781303304
ring.unparameterise('QF1[AE]')
print(ring[117])
Quadrupole:
FamName: QF1E
Length: 0.311896
PassMethod: StrMPoleSymplectic4Pass
FringeQuadEntrance: 1
FringeQuadExit: 1
MaxOrder: 1
NumIntSteps: 14
PolynomA: [0. 0.]
PolynomB: [0. 2.53945998]
K: 2.5394599781303304
Parameter history#
Parameter values are kept in an history buffer. The properties initial_value, last_value and previous_value are also available:
dlength.value = 1.1
print(dlength.history)
print(dlength.initial_value)
print(dlength.previous_value)
[1.0, 0.9, 1.1]
1.0
0.9
After varying parameters, in matching for instance, the current status can be printed:
print(dlength.status())
Name Initial Final Variation
dlength 1.000000e+00 1.100000e+00 1.000000e-01
Parameters may be reset to a previous history value with the reset() and set_previous() methods. The history is shortened accordingly.
dlength.set_previous()
print(dlength, dlength.history)
dlength.reset()
print(dlength, dlength.history)
dlength [1.0, 0.9]
dlength [1.0]
Copying and Saving#
Parameters cannot be copied. A shallow or deep copy of a parameter returns the parameter itself.
So in a deep copy of an Element, the parameters are preserved.
When saving a lattice with parameterised elements, as a .mat, a .m or .repr file, all parametrisation is removed, and a “frozen” state of the lattice is saved.
In pickle dumps of an Element, parameters are preserved.