Skip to content

Address the dipole kick through KickAngle where the pass method uses it - #470

Open
thellert wants to merge 2 commits into
python-accelerator-middle-layer:mainfrom
als-apg:fix/corrector-pass-kick-angle
Open

thellert wants to merge 2 commits into
python-accelerator-middle-layer:mainfrom
als-apg:fix/corrector-pass-kick-angle

Conversation

@thellert

@thellert thellert commented Oct 10, 2026 •

Copy link
Copy Markdown
Member

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 a CorrectorPass element never reaches the beam, and a KickAngle carried 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:

  • bugfix: one small view class in pyaml/lattice/abstract_impl.py (_KickAngleCoefficients) presents the dipole strength of an element that carries a KickAngle as 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

  • CorrectorPass elements: the strength is written to and read from KickAngle (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.Corrector elements defined without PolynomA/B now work; building the accessor raised AttributeError.
  • Multipole elements that carry a KickAngle (ThinMPolePass, StrMPole*, BndMPole*, ExactMultipole*): the strength reads polynom + KickAngle; a write stores the value in the polynom and resets that plane's KickAngle, so it is not applied twice. This changes an attribute of the user's lattice on write — the alternative is to keep KickAngle and store the difference in the polynom; say which you prefer.
  • Elements without a 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 a KickAngle reference:

  • test_corrector_pass_strength_moves_the_orbit — thin and thick CorrectorPass, both planes
  • test_corrector_pass_without_polynoms_is_supported
  • test_corrector_pass_reads_the_kick_of_the_lattice
  • test_corrector_pass_combined_function_strengths_move_the_orbit — strength and hardware arrays
  • test_split_corrector_pass_shares_the_kick_by_length
  • test_multipole_corrector_agrees_with_corrector_pass
  • test_multipole_kick_angle_is_part_of_the_strength — thin multipole, multipole, dipole
  • test_kick_angle_ignored_by_the_pass_method_is_not_read

The orbit tests fail on main and pass here.

Verify that your checklist complies with the project

  • New and existing unit tests pass locally — 467 passed, 6 skipped, 1 failed: tests/tuning_tools/test_tune_hardware.py::test_tune (4.1e-8 against a 1e-8 tolerance), which fails with the same value on unmodified main on my machine (macOS, Python 3.12, AT 0.8.0)
  • Tests were added to prove that all features/changes are effective
  • The code is commented where appropriate
  • Any existing features are not broken (unless there is an explicit change to an existing functionality)

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 thellert changed the title Write corrector strength to KickAngle for CorrectorPass elements Address the dipole kick through KickAngle where the pass method uses it Oct 10, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Dipole kick held in KickAngle is not seen by the magnet accessors

1 participant