Robust emittance minimisation¶
A worked recipe for HELIX's two newest matcher pieces:
MIN_EMIT_GROWTH plane weight— a one-sided residual that penalises end-of-line growth in one plane and ignores drops below the seed beam's emittance. Critical for the longitudinal plane, where coupling-resonance crossings can drop ε_z below input — recovery up to ε_z,in is free, only true growth is penalised.MIN_EMIT_4D_GROWTH weight tol_4d tol_z— emittance-exchange-aware variant. Combines the 4-D coupled transverse normalised emittanceε_4D · (βγ)²(computed fromsqrt(det(Σ_4×4_transverse))) with the normalised longitudinalε_z · (βγ), each clipped by a multiplicative tolerance band. Use when the lattice physically exchanges phase-space area between planes (e.g. solenoid-rotated transport, coupling resonances near sync-phase crossings): the strict per-axisMIN_EMIT_GROWTH X|Y|Zconstraint would force the matcher onto a structural cost floor it cannot escape. See § Emittance-exchange tolerance below for the recipe.SET_KE_OUT_MIN energy_MeV weight— a one-sided floor on the reference particle's exit kinetic energy. Prevents the matcher from silently buying lower emittance by detuning a cavity off-crest.--algorithm cmaes— Covariance Matrix Adaptation Evolution Strategy (Hansen). Robust global, gradient-free, bound-respecting. The right pick when the constraint family has flat regions that defeat scipy LM.
When to use this recipe¶
You have a lattice that grows transverse and/or longitudinal emittance
under nominal settings, and you want to tune solenoid B-fields,
cavity gradients (ke) and cavity synchronous phases to drive
the growth back down, subject to:
- Per-element bounds (cryomodule spec sheets) — already supported by
the existing
ADJUST … vmin vmaxsyntax. - An end-of-line energy floor — supplied by
SET_KE_OUT_MIN. - "No penalty for ε_z drops, only ε_z growth" — supplied by
MIN_EMIT_GROWTH Z.
Lattice syntax¶
ADJUST <family> <param_idx> <link_group> <vmin> <vmax> <start_step> 0
…
MIN_EMIT_GROWTH X 1.0
MIN_EMIT_GROWTH Y 1.0
MIN_EMIT_GROWTH Z 1.0
SET_KE_OUT_MIN 3.5 10.0 ; W_kin,out ≥ 3.5 MeV, weight 10
END
Parameter indices used for the tunables in this workflow
(see 02_set_adjust.md for the full table):
| Element | param_idx | Attribute | Unit |
|---|---|---|---|
Solenoid |
2 | field |
T |
FieldMap / FieldMap3D |
2 | phase |
deg |
FieldMap / FieldMap3D |
5 | kb (magnetic scale) |
dimensionless |
FieldMap / FieldMap3D |
6 | ke (electric scale) |
dimensionless |
RFGap (a.k.a. GAP) |
1 | voltage |
MV |
RFGap (a.k.a. GAP) |
2 | phase |
deg |
The
<family>in anADJUSTcard matches the prefix of the element name, not the .dat keyword: e.g.ADJUST SOL …forSolenoidelements (namedSOL_001,SOL_002, …) andADJUST FMAP …forFieldMap/FieldMap3D(namedFMAP_001,FMAP_002, …).RFGapparsed fromGAPcards is namedGAP_001etc., soADJUST GAP …is correct.
One-sided emittance residuals¶
The X and Y planes are compared in normalised emittance so adiabatic damping under acceleration does not trivially satisfy the constraint:
The Z plane uses the native emit_z (deg·MeV); that unit pair
already counts the conjugate longitudinal action and is invariant
under acceleration:
A drop in any plane contributes zero residual. Recovery up to the seed value is also free.
End-of-line energy floor¶
Pick the weight w_E roughly 10× the per-plane emittance weights;
the energy residual scales in MeV which is typically much larger than
the per-plane Δε in mm·mrad, so a smaller weight would let the matcher
trade away significant energy for minor emittance gains.
Running the matcher¶
python -m linac_gen.matching <lattice.dat> \
--algorithm cmaes \
--space-charge --current 5.0 \
--energy 2.5 --species proton --frequency 162.5 \
--alpha-x -1.2 --beta-x 0.32 --emit-x 0.30 \
--alpha-y 2.0 --beta-y 0.05 --emit-y 0.30 \
--emit-z 0.4 --beta-z 600.0 \
--max-iter 80 --cmaes-sigma 0.25 \
--report
The default refine=True chains a scipy.optimize.least_squares
polish from the best CMA-ES point. Pass --no-refine to skip that
polish (useful if the polish moves the solution out of an active-bound
basin).
Worked example¶
examples/emittance_min/min_test.dat ships as the canonical demo: 8
elements (drift–sol–drift–buncher–drift–sol–drift–accel–drift), 6
tunable knobs, 4 constraints, proton at 2.5 MeV with deliberate
transverse mismatch.
| Quantity | Baseline | After match | Change |
|---|---|---|---|
| ε_nx growth (mm·mrad) | +0.15 | +0.045 | −70 % |
| ε_ny growth | +0.15 | +0.045 | −70 % |
| ε_z growth | 0.00 | 0.00 | flat |
| W_kin,out (MeV) | 3.93 | ≥3.5 | floor respected |
| Wall time | — | ~1.3 s | 783 evals |
See examples/emittance_min/README.md for the exact invocation and
expected output. A PIP-II HWR cryomodule slice with 12 tunable knobs
ships alongside as pipii_hwr_cm_match.dat.
Algorithm notes¶
- CMA-ES population defaults to
4 + ⌊3·ln(N)⌋per Hansen, where N is the number of decision variables. Override with--cmaes-popsize. Larger populations are more robust against local minima at the cost of more forward passes. - The initial step size
--cmaes-sigma(default0.2) is in units of bound-box width. Larger values explore more aggressively; pick~0.3if the seed is far from a good point,~0.1to refine a near-matched lattice. - Coupling-resonance landscapes are genuinely multimodal — there is
no guarantee CMA-ES finds the global optimum. When budget allows,
run 3–5 different seeds (vary the RNG seed inside
cmaor perturb the initial values) and keep the best.
Sequential-scan algorithm (--algorithm sequential_scan)¶
For users who prefer the classical accelerator-physics matching recipe —
coordinate descent with direction reversal + physics-aware ordering —
HELIX exposes a sequential_scan algorithm in the matcher. Unlike the
other algorithms which vary all variables simultaneously, this walks
each tunable element in lattice order and brackets each ADJUST'd
parameter in a one-at-a-time sweep:
for pass in range(N_PASSES):
for element in lattice_order(selected):
for group in [(kb,), (ke,), (phase,)]: # FieldMap
direction = +1
for step in range(N_STEPS):
x_new = x_current + direction × step_frac × (vmax − vmin)
cost = evaluate(x_new)
if εnx_norm_out > seed AND εnz_norm_out > seed:
direction *= −1 # reverse on dual growth
track_best(x_new, cost)
x_current = x_new
Key properties:
- Element selection — runs over a user-specified subset of elements (default: all elements with at least one ADJUST card).
- Physics ordering — for FieldMap elements: scan
kb(solenoid focusing) first, thenke(cavity amplitude), thenphase(sync phase). Other element classes use the order of their ADJUST cards. - Direction reversal — defaults to "reverse only when both
transverse and longitudinal normalised emittance exceed the reference"
(
--seqscan-reversal both_grew); that is, accept emittance exchange between planes as physics (matches whatMIN_EMIT_4D_GROWTHalready does in the cost function). Stricter--seqscan-reversal any_grewreverses on either plane growing. - Reversal reference —
--seqscan-threshold input(default) compares trial exit ε to the beam input ε. Tight criterion; designed for the coupling-resonance case (ε_t and ε_z trade off but reversal fires only if both exceed input simultaneously).--seqscan-threshold seed_exitcompares to the nominal/unmatched lattice's exit ε instead; reversal fires only when the trial is genuinely WORSE than baseline. Natural for ε minimisation in lattices with intrinsic growth (most RF cryomodule chains). - Best-x tracking + Stop button — share the same machinery as the other algorithms; cancellation always leaves the lattice at the best-cost x seen so far.
GUI: pick Algorithm = sequential_scan in the Matching tab, click Match, fill in the Setup dialog (which elements, how many passes / steps, step size as a fraction of bound width, reversal criterion, reversal reference), click Start Scan. Live convergence plot updates per evaluation; Stop button works as for the other algorithms.
CLI — every seqscan_* kwarg is exposed:
python -m linac_gen.matching examples/emittance_min/pipii_hwr_cm_match_4d_relaxed.dat \
--algorithm sequential_scan \
--seqscan-passes 2 \
--seqscan-steps 7 \
--seqscan-step-frac 0.10 \
--seqscan-reversal both_grew \
--seqscan-threshold seed_exit \
--seqscan-element FMAP_001 --seqscan-element FMAP_002 \
--report
Repeat --seqscan-element NAME to restrict the walk to a subset of
ADJUST'd elements; omit it to scan all of them.
When to pick sequential_scan over cmaes:
- You want full transparency into which knob did what -- per-element scans are easier to interpret than CMA-ES population samples.
- The lattice has weak inter-knob coupling and a small number of variables (≤ 10) -- coordinate descent converges in fewer total evaluations.
- You're validating a hand-tuned baseline -- sequential_scan with
small
step_frac(e.g. 0.02) refines a near-matched lattice conservatively.
When to stay with cmaes:
- The problem is strongly coupled (12+ variables, HWR-style) -- CMA-ES learns the covariance matrix and moves diagonally; coordinate descent zigzags.
- You want a single number that captures algorithm confidence -- CMA-ES provides one via the population spread; sequential scan does not.
Emittance-exchange tolerance (MIN_EMIT_4D_GROWTH)¶
For lattices that naturally trade phase-space area between transverse
and longitudinal planes — solenoid-focused cryomodule sections,
coupling-resonance crossings near a sync-phase target — the strict
per-axis MIN_EMIT_GROWTH X|Y|Z 1.0 triple over-constrains the
matcher. Even after CMA-ES + LS polish the cost plateaus at a
non-zero structural floor because no setting of the tunable knobs can
drive all three axes below their entry emittance simultaneously.
MIN_EMIT_4D_GROWTH lets you encode the physics-aware version of
"don't grow total phase-space volume":
MIN_EMIT_4D_GROWTH 1.0 1.05 1.20
; | | |
; | | +-- tol_z: allow 20 % normalised εz growth
; | +------- tol_4d: allow 5 % 4-D transverse growth
; +----------- weight: same semantics as MIN_EMIT_GROWTH
The two residuals are clipped one-sided:
r_4d = max(0, ε_4D·(βγ)²|_out − tol_4d · ε_4D·(βγ)²|_in)
r_z = max(0, ε_z·(βγ) |_out − tol_z · ε_z·(βγ) |_in)
Key properties:
- εx ↔ εy exchange is free. ε_4D is
sqrt(det(Σ_4×4_transverse))— invariant under any orthogonal rotation of the transverse phase space (solenoid coupling, skew-quad twist). The matcher can shuffle area between x and y without paying any cost. - Transverse–longitudinal trade-off is allowed up to the tolerances.
A drop in εx at the price of εz growth is fine as long as the εz
growth stays under
tol_z. - Tolerances are multiplicative bounds —
1.0is strict (any growth penalised);1.1allows 10 % growth before the residual becomes positive. Negative or sub-1.0 values raise at parse time. - Drops are free (one-sided clip), same as
MIN_EMIT_GROWTH.
Use one MIN_EMIT_4D_GROWTH or three MIN_EMIT_GROWTH X|Y|Z lines,
not both — they over-constrain each other. Choose the strict form
when each plane has a hard independent target (collimator, foil
stripping section); choose the 4-D form when emittance exchange is
expected.
Working example: examples/emittance_min/pipii_hwr_cm_match_4d.dat
(strict) and pipii_hwr_cm_match_4d_relaxed.dat (5 % / 20 %
tolerance) — both target the PIP-II HWR cryomodule with identical
ADJUST cards and beam config as the original pipii_hwr_cm_match.dat.
Caveats¶
MIN_EMIT_GROWTHandSET_KE_OUT_MINare soft weighted residuals — the matcher minimises sum-of-squares. If the weight onSET_KE_OUT_MINis too small, the matcher can buy emittance at the cost of a few MeV of beam energy. Pick the weight deliberately.- CMA-ES bounds are penalty-based; transient out-of-spec excursions during exploration are normal and the final point is always inside the box.
- The Z-plane evaluator does not apply a
β γcorrection —emit_zin deg·MeV is already an action variable. Mixing units between planes is intentional.
Pre-equipped PIP-II lattices (examples/pipii_tunable/)¶
Ready-to-match copies of the canonical examples/pipii/ lattices, with an
ADJUST card already on every active element and an end-of-line
MIN_EMIT_4D_GROWTH + SET_KE_OUT_MIN objective — no hand-editing needed.
Each deck also carries a commented ;MIN_TRANSMISSION 99.9 50 line —
uncomment it when matching with the mp cost solver so the optimiser
cannot "improve" emittance by scraping halo (the engine refuses the
active card under the envelope cost solver, where it would be inert).
| Lattice | knobs (cav / sol / quad) |
|---|---|
btl_tunable.dat, btl_with_foil_tunable.dat |
53 (0 / 0 / 53) |
mebt_tunable.dat |
39 (4 / 0 / 31) |
mebt+hwr_tunable.dat |
63 (12 / 8 / 31) |
mebt+hwr+ssr1_tunable.dat |
103 (28 / 16 / 31) |
mebt+hwr+ssr1+ssr2_tunable.dat |
194 (63 / 37 / 31) |
mebt+hwr+ssr1+ssr2+lb650+hb650_tunable.dat |
350 (123 / 37 / 67) |
Bounds: cavity ke ±20 %, sync phase ±10°, solenoid kb ±10 %, quad
gradient ±20 %. Steerers are excluded (orbit, not emittance); BTL dipoles
stay fixed (geometry). Regenerate or re-tune bounds with
examples/pipii_tunable/make_tunable.py (bounds are CLI flags). Three
policy flags refine the knob set: --tune-from-s <metres> restricts
knobs to elements downstream of a given s; --fix-phase-maps gives the
matching cavity families a ke knob but no phase knob;
--cap-family-max caps each cavity/solenoid's strong-side bound at its
field-map family's max design |value| (hardware ceiling).
examples/pipii_hwr_ssr1_match/ combines the latter two — a 99-knob
MEBT+HWR+SSR1 match with the QWR buncher phases pinned at −90° and
family-max ke/kb limits — a cheaper section-sized match than the
full linac.
Equip → perturb → match
Two conditions must hold for the matcher to actually do something here:
- Run with space charge (
--space-charge, or thempcost solver). Under linear optics with no space charge, RMS emittance is exactly conserved — soMIN_EMIT_4D_GROWTHis trivially satisfied and the match is a no-op. - Start from a degraded state, not the design. These are the PIP-II design lattices — they already grow emittance < 1 % end-to-end, so a match started from design correctly changes nothing. Perturb first (an off-design input beam, applied errors, or a failed element via the Failure Study tab), then match.
Cross-references¶
- SET / ADJUST card syntax
- Matching CLI — full
python -m linac_gen.matchingflag list. - Recipes — Twiss-matching and gradient-matching examples.
examples/emittance_min/— runnable demos.examples/pipii_tunable/— fully-equipped PIP-II lattices (above).
← Gradient algorithm · Continue to Multi-objective matching →