Skip to content

Solve Boozer chartmap cold inverse on the full torus#397

Merged
krystophny merged 3 commits into
mainfrom
fix/395-boozer-cold-inverse
Jul 16, 2026
Merged

Solve Boozer chartmap cold inverse on the full torus#397
krystophny merged 3 commits into
mainfrom
fix/395-boozer-cold-inverse

Conversation

@krystophny

Copy link
Copy Markdown
Member

Fixes #395.

Problem

chartmap_from_cyl / from_cart on a rotating-frame (zeta_convention="boozer") chartmap failed an exact forward-map roundtrip with chartmap_from_cyl_err_max_iter for interior points. The cold inverse constrained the logical toroidal angle to the stored field-period wedge in three places (seed scan, line-search wrap, final modulo(zeta, zeta_period)), but the lab-frame position of the co-rotating cart chart satisfies x(rho, theta, zeta + P) = Rot_z(P) x(rho, theta, zeta) — it is only 2pi-periodic in zeta. Any target whose toroidal angle lies outside the first field-period sector could therefore never converge. The warm invert_cart had the mirror defect: it located the root, then wedge-wrapped the returned zeta, silently reporting CHARTMAP_LOCATED with a position hundreds of cm off.

Fix

  • Cold inverse: rotate the target back by the whole field periods of its geometric angle (exact chart symmetry), run the existing wedge-resolution seed scan and Newton solve on the rotated target with zeta iterating unwrapped, then shift the solved zeta back and return it modulo 2pi.
  • Accept the cold root on its final Cartesian residual against chartmap_invert_accept_tol, not on the solver exit path: the Newton step-size exit fires at a Cartesian residual of order |dx/du|*tol_step, above tol_res on reactor-size charts, which both rejected converged roots and (via the old seed-quality branch) accepted stalled ones.
  • Scale-aware floor on the Newton residual tolerance (100*eps*|x_target|).
  • Fix the axis-degenerate gauge in chartmap_newton_delta_xy (cth = xw/rho is 0/eps at an exact axis seed, freezing the iteration; use cos(atan2(yw, xw)) and floor the 1/rho amplification).
  • Warm invert_cart: return zeta modulo 2pi for co-rotating charts; the rz chart stays wedge-native.

Tests (written first, RED on main)

  • test_chartmap_boozer_coordinates: reactor-scale (R0 = 1000 cm, nfp = 5) endpoint-excluded rotating-frame fixture; 120 deterministic interior cold from_cyl roundtrips including rho = 0.5, angular seams, full-torus zeta, and inputs three full turns outside the stored wedge (80 of 120 failed before the fix); negative cases for past-edge targets and near-axis non-masquerade.
  • test_chartmap_invert_cart: warm full-torus roundtrip asserting zeta mod 2pi and evaluate_cart(u) reproducing the target (silently wrong by a field-period rotation before the fix).
  • test_boozer_chartmap_roundtrip: the issue's exact probe sequence on the exported LandremanPaul2021 QA chartmap, plus bit-for-bit invariance of the inverse across load_boozer_from_chartmap.

fo full pipeline passes (the Fmt WARN on the two touched files pre-exists on main; per repo policy the sweep is left to the dedicated format pass).

Downstream integration is tracked in itpplasma/SIMPLE#493.

The cold Cartesian inverse of the co-rotating cart chart constrained the
logical toroidal angle to the stored field-period wedge (seed scan, line
search wrap, and final reduction), but the lab-frame position of that
chart is only 2pi-periodic in zeta, so any target outside the first
field-period sector could never converge and an exact forward-map
roundtrip failed with err_max_iter.

Rotate the target back by the whole field periods of its geometric
angle (an exact chart symmetry), iterate zeta unwrapped, and shift the
solved zeta back, returning it modulo 2pi. Accept the cold root on its
final Cartesian residual against the documented accept tolerance
instead of the solver exit path, whose step-size exit sits above the
reachable residual on reactor-size charts. Give the Newton residual
tolerance a scale-aware floor, fix the axis-degenerate gauge in the
pseudo-Cartesian step, and stop wedge-wrapping the warm inverse result
for co-rotating charts.

Fixes #395
Review findings: the fixed cold accept tolerance could sit below the
Newton's own convergence target for coarse tol_newton or very large
charts, rejecting a legitimately converged root; carry the same floors
in the acceptance. The warm seam test compared zeta modulo one field
period and never re-evaluated the Cartesian point, masking exactly the
wrong-wedge regression it guards; compare modulo 2pi and assert the
reconstructed position.
@krystophny
krystophny enabled auto-merge (squash) July 16, 2026 13:34
@krystophny
krystophny merged commit e0437ad into main Jul 16, 2026
6 checks passed
@krystophny
krystophny deleted the fix/395-boozer-cold-inverse branch July 16, 2026 13:43
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.

Boozer chartmap cold inverse fails an exact forward-map roundtrip

1 participant