Skip to content

Add Boozer/SFL chart gate tests and signed chartmap Jacobian - #398

Merged
krystophny merged 7 commits into
mainfrom
feat/boozer-chart-gates
Jul 26, 2026
Merged

Add Boozer/SFL chart gate tests and signed chartmap Jacobian#398
krystophny merged 7 commits into
mainfrom
feat/boozer-chart-gates

Conversation

@krystophny

Copy link
Copy Markdown
Member

Follow-up to #397 (the intended base branch fix/395-boozer-cold-inverse was merged to main and deleted, so this stacks directly on main). Implements the ROADMAP straight-field-line/Boozer gauge gates as executable tests, ahead of the JOREK-sourced chart builder.

What

Signed Jacobian accessor. chartmap_metric_tensor returns sqrtg = sqrt(abs(det g)), which cannot distinguish a consistent chart orientation from a sign flip across a seam. New type-bound cs%jacobian_det(u) returns the signed det(dx/du) from the analytic covariant basis; metric_tensor is unchanged.

Gate (a) — signed periodic Jacobian positivity (test_chartmap_gate_jacobian): full-torus scan of a right-handed nfp = 3 rotating-frame fixture with probe pairs straddling every field-period seam, the theta seam, and the 2pi wrap; requires det > 0 everywhere (rho > 0), seam-pair continuity at solver-noise level, exact 2pi periodicity, and |jacobian_det| == sqrtg to 1e-9. A mirrored fixture must give det < 0 everywhere, proving the accessor is genuinely signed. Same positivity scan on the circular nfp = 1 fixture.

Gate (b) — bounded flux-label drift (test_chartmap_gate_fieldline): field lines of the production neo_circular_tokamak_field lie exactly on circular tori and are straight in the geometric angles; a tangency self-check verifies the closed-form trace against compute_bfield at 1e-12 first. 27 launches x 50 toroidal transits x 12 samples are pulled back through the warm invert_cart (orbit-pusher pattern, guess chained along the line). All 16 227 pullbacks must report LOCATED; max flux-label drift gate 1e-5 (measured 7.9e-7).

Gate (c) — launch-independent rotational transform (same test): endpoint transform estimate from unwrapped chart angles for the 3 radii x 3 poloidal x 3 toroidal launch ensemble; ensemble spread and deviation from the analytic transform (-B_pol/B_tor in the right-handed chart) gated at 5e-4, the analytic worst-case bound from the bounded periodic angle offsets (measured error ~9e-6).

Gate (d) — converged field reconstruction against the native evaluator (test_chartmap_gate_reconstruction): the consumer pipeline — native field sampled on chart nodes, periodic batch splines of (B_R, B_phi, B_Z, |B|), physical probe -> invert_cart -> spline evaluation — compared against the native evaluator at three resolutions (9,16,8) -> (17,32,16) -> (33,64,32). Gates: monotone error decrease, contraction >= 4 per refinement (measured 33x/15x vector, 7.7x/31x |B|), finest max relative error < 8e-5, the WP3 analytic |B| gate bound (measured 3.2e-8 vector, 5.3e-9 |B|).

Cleanup. chartmap_invert_cart computed a seed-refinement residual and error code (ierr_seed) that nothing read; chartmap_refine_cart_seed loses the two dead outputs.

Scope (honest limits)

Gate (d) runs against the analytic circular fixture, not JOREK: libneo has no JOREK-to-chartmap builder yet, so a JOREK-native gate (d) (against jorek_field on an AUG restart) must land together with that builder. The fixtures are smooth nested-surface charts; shaped/X-point geometry, non-nested regions, toroidal ripple, and the Fourier Boozer-transform pipeline are not covered. The constant-transform field makes gate (c) a launch-independence and absolute-value check, not a radial-profile-ordering check.

Tests

New: test_chartmap_gate_jacobian, test_chartmap_gate_fieldline, test_chartmap_gate_reconstruction (shared fixture writer chartmap_gate_fixtures, self-contained NetCDF in the style of test_chartmap_invert_cart). Full local ctest: 98/98 pass (Debug preset, gfortran), including all existing chartmap/Boozer inverse tests against the cleaned-up cold inverse.

@krystophny
krystophny force-pushed the feat/boozer-chart-gates branch from 0758d54 to 56a8b65 Compare July 17, 2026 11:28

@slopqueue slopqueue Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review verdict: Approve

Based on my thorough review of the complete diff, the source changes, all test files, the CMakeLists changes, the abstract base class, and the CI configuration, here is my review:


Summary: This PR adds a signed Jacobian determinant accessor (jacobian_det) to the chartmap coordinate system, removes dead output parameters from chartmap_refine_cart_seed, and adds four new test files (fixtures + three gate tests) covering chart orientation, field-line drift, rotational transform, and field reconstruction. The math is sound, the dead-code removal is safe (single caller verified), and the tests are well-structured with clear pass/fail criteria.

Findings:

  1. minor test/source/test_chartmap_gate_reconstruction.f90:39B_POL = 0.3183099_dp is hardcoded as 1/π but the fieldline test uses the same constant as iota_geom = B_POL/B_TOR. This is a reasonable test value, but consider deriving it from a named constant or documenting it as 1/π rather than leaving the numeric magic value. No fix required.

  2. minor src/coordinates/libneo_coordinates_chartmap.f90:1162 — The comment "Accept on the final residual, not on the solver exit path" (line ~1162–1170) is excellent documentation, but it appears after the chartmap_solve_cart call that uses ierr_try. A reader must trace through to understand that ierr_try (solver exit) is intentionally not the accept gate. Consider moving the comment above the chartmap_solve_cart call so the rationale is visible before the code it explains. No fix required.

  3. minor (process) Build verification could not be completed locally: CMake Fortran compiler identification failed and FindPython in src/efit_to_boozer/CMakeLists.txt failed. This is an environment issue (missing dev dependencies), not a code issue. The CI workflow (main.yml) installs all system deps via apt-get and should build correctly there. The tests' correctness depends on CI passing.

Verdict: Approve — The implementation is mathematically correct (standard cofactor expansion for det(E) where E is the covariant basis), the signature change to chartmap_refine_cart_seed is safe (verified single caller, removed outputs were dead code), jacobian_det is correctly placed on the concrete type (not the abstract base), and the tests use select type appropriately to access the chartmap-specific accessor. The local build failure is environmental; CI should confirm runtime behavior.

…at/boozer-chart-gates

# Conflicts:
#	src/coordinates/libneo_coordinates_chartmap.f90
#	test/source/test_chartmap_gate_fieldline.f90
#	test/source/test_chartmap_gate_reconstruction.f90

@krystophny krystophny left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed latest 1a6d50b5 after merging current main and the remote head. The two review nits are fixed: B_POL is expressed as 1/pi from TWOPI, and the final-residual rationale precedes the solver call. Native suite: 98/98 passed before the final ancestry merge; the three affected chart gates were rerun afterward and all 3 passed. No blocking finding.

@krystophny
krystophny merged commit 2e34486 into main Jul 26, 2026
6 checks passed
@krystophny
krystophny deleted the feat/boozer-chart-gates branch July 26, 2026 17:48
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.

1 participant