Repository navigation
Conversation
CorrectorPass integrates KickAngle and ignores PolynomA/PolynomB, so a strength set on an at.Corrector element round-tripped through the accessor but never moved the orbit. The simulator accessors now address the dipole component of such an element through KickAngle, keeping a polynom the element carries in step with it. Correctors defined without polynoms are supported, and a kick already present in the lattice is read back. Tests compare the closed orbit against a KickAngle reference.
The multipole pass methods add KickAngle to the polynom. An element that carries one read back only its polynom part, and a write added to the kick instead of replacing it. The accessors now read the sum and, on a write, store the value in the polynom and reset the KickAngle component. A KickAngle on a pass method that ignores it is not read.
|
@thellert Thanks for the contribution. Normally, we've discussed limiting pyAML elements to PolynomA/PolynomB, no kick angle, as the maintaining lab of AT is not using KickAngles much. But I think now there's enough demand to also have KickAngle in pyAML. So, this is a good addition. |
|
I think here you need to test also if it works with a combined function magnet, having a sextupole with dipolar correctors as KickAngles. I think @gupichon did something similar a while ago, but maybe it was never merged. |
|
Thank for the contribution. |
|
@thellert Thanks again for the contribution. We've discussed in the maintainer meeting that KickAngle can be implemented and allow a bridge to CorrectorPass to cover the use case. I will try to review this net week (and @JeanLucPons if he has time [not guaranteed] can guide you). |
gubaidulinvadim
left a comment
There was a problem hiding this comment.
For me the PR looks mostly OK. I will have a closer look on Monday. The one thing that is strange is that one test fails for you. This is definitely not normal. Could you please make an issue detailing why test_tune_hardware fails for you on the main branch @thellert ?
| AT holds the dipole kick of an element in two places. ``CorrectorPass`` integrates ``KickAngle`` only and ignores | ||
| ``PolynomA``/``PolynomB``; the multipole pass methods integrate the polynom *and* add ``KickAngle`` to it. This |
There was a problem hiding this comment.
For now, we would be happy to even have only KickAngle for CorrectorPass without integration. But if it's already done, it's good.
|
|
||
| AT holds the dipole kick of an element in two places. ``CorrectorPass`` integrates ``KickAngle`` only and ignores | ||
| ``PolynomA``/``PolynomB``; the multipole pass methods integrate the polynom *and* add ``KickAngle`` to it. This | ||
| view exposes the total at index 0 with the polynom convention (``PolynomB[0] * L = -KickAngle[0]``, |
There was a problem hiding this comment.
For me, it's not immediately clear what "index 0" means in a docstring here
GamelinAl
left a comment
There was a problem hiding this comment.
Some pass method seem to be missing.
Otherwise, it's good for me
| def _applies_kick_angle(element: at.Element) -> bool: | ||
| """ | ||
| Return True when the pass method of ``element`` applies its ``KickAngle``. | ||
|
|
||
| That is ``CorrectorPass`` and the multipole integrators (``StrMPole*``, ``BndMPole*``, ``ThinMPolePass``, | ||
| ``ExactMultipole*``). Other pass methods ignore the attribute. | ||
| """ | ||
| pass_method = element.PassMethod | ||
| return pass_method == "CorrectorPass" or "MPole" in pass_method or "Multipole" in pass_method |
There was a problem hiding this comment.
The list of pass methods is incomplete, those are missing:
- ExactSectorBendPass
- ExactRectangularBendPass and ExactRectBendPass
- the *Rad variants of all three
- the legacy StrOldRadPass / BndOldRadPass
|
FYI. In a future pyAT release, |
@gubaidulinvadim |
Hm, then I suspect that it fails on all macOS machines. @thellert @kparasch an issue should be made on AT side then.
That's good news, but the next release is in ~6 months. We could use @thellert solution in the meantime. |
Absolutely. I would propose a test is included here to catch this and make sure we don't forget about it whenever/if this change happens and is released in AT. |
This is already done in AT and is part of the release 0.9.0, see: atcollab/at#1158 |
|
So we can just cancel this PR ? |
|
@swhite2401 I see |
|
In any case, in pyAML angles are accessible through [H,V]Corrector.angle.set() and loads PolynomA/B. |
It also handles cases where an existing lattice has a Edit: In fact with |
|
As I understand, I think KickAngle is expected to be 0 in lattice file. |
Yes, the purpose is mainly to support CorrectorPass, which is present in many lattices and is used at many facilities for simulated commissioning studies. |
|
I would strongly suggest to use HKick and VKick, this is the simplest solution for you, it is always present in any passmethod and allows to use any magnet as corrector. KickAngle still has legacy behavior, it is added on top of what is in PolynomA/B -> there are by the way different things, one is the deflection angle the other the polynomial expansion of the field (be careful with sign!) |
|
So I don't understand this: def _applies_kick_angle(element: at.Element) -> bool:
"""
Return True when the pass method of ``element`` applies its ``KickAngle``.
That is ``CorrectorPass`` and the multipole integrators (``StrMPole*``, ``BndMPole*``, ``ThinMPolePass``,
``ExactMultipole*``). Other pass methods ignore the attribute.
"""
pass_method = element.PassMethod
return pass_method == "CorrectorPass" or "MPole" in pass_method or "Multipole" in pass_methodIf I understand well this PR affect all the passmethod that match. |
There are cases where the design can have non-zero kick angles, e.g. canting in light sources with correctors, modeling of shifted quadrupoles (without touching dx), colliders for crossing angles between beams, e.t.c. |
Exactly and this is why we suggest to use HKick and VKick in the future, this is much cleaner, KickAngle will only be kept for backward compatibility and eventually be derived from the same quatity |
|
OK so, this PR should:
|
Description
AT keeps the dipole kick of an element in the polynom and in
KickAngle; the simulator's magnet accessors read and write the polynom only. As a result a strength set on aCorrectorPasselement never reaches the beam, and aKickAnglecarried by a multipole element is not read and is doubled by a write. Details, measurements and a script are in the issue.Related Issue
Features/issues described there are:
pyaml/lattice/abstract_impl.py(_KickAngleCoefficients) presents the dipole strength of an element that carries aKickAngleas a single polynom coefficient. The scalar and array accessors use it wherever they looked up the polynom, so their arithmetic is unchanged. This keeps the fix in one place instead of branching in each accessor.Changes to existing functionality
CorrectorPasselements: the strength is written to and read fromKickAngle(PolynomB[0] * L = -KickAngle[0],PolynomA[0] * L = KickAngle[1]), because that pass method ignores the polynoms. A polynom the element carries is kept in step, so the existing accessor tests hold unchanged.at.Correctorelements defined withoutPolynomA/Bnow work; building the accessor raisedAttributeError.KickAngle(ThinMPolePass,StrMPole*,BndMPole*,ExactMultipole*): the strength reads polynom +KickAngle; a write stores the value in the polynom and resets that plane'sKickAngle, so it is not applied twice. This changes an attribute of the user's lattice on write — the alternative is to keepKickAngleand store the difference in the polynom; say which you prefer.KickAngle, or whose pass method ignores it (QuadLinearPass,DriftPass), are untouched.Testing
The following tests (compatible with pytest) were added to
tests/lattice/test_magnet_accessors.py; each compares the closed orbit of a small ring against aKickAnglereference:test_corrector_pass_strength_moves_the_orbit— thin and thickCorrectorPass, both planestest_corrector_pass_without_polynoms_is_supportedtest_corrector_pass_reads_the_kick_of_the_latticetest_corrector_pass_combined_function_strengths_move_the_orbit— strength and hardware arraystest_split_corrector_pass_shares_the_kick_by_lengthtest_multipole_corrector_agrees_with_corrector_passtest_multipole_kick_angle_is_part_of_the_strength— thin multipole, multipole, dipoletest_kick_angle_ignored_by_the_pass_method_is_not_readThe orbit tests fail on
mainand pass here.Verify that your checklist complies with the project
tests/tuning_tools/test_tune_hardware.py::test_tune(4.1e-8 against a 1e-8 tolerance), which fails with the same value on unmodifiedmainon my machine (macOS, Python 3.12, AT 0.8.0)