Add Boozer/SFL chart gate tests and signed chartmap Jacobian - #398
Conversation
0758d54 to
56a8b65
Compare
There was a problem hiding this comment.
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:
-
minor
test/source/test_chartmap_gate_reconstruction.f90:39—B_POL = 0.3183099_dpis hardcoded as1/πbut the fieldline test uses the same constant asiota_geom = B_POL/B_TOR. This is a reasonable test value, but consider deriving it from a named constant or documenting it as1/πrather than leaving the numeric magic value. No fix required. -
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 thechartmap_solve_cartcall that usesierr_try. A reader must trace through to understand thatierr_try(solver exit) is intentionally not the accept gate. Consider moving the comment above thechartmap_solve_cartcall so the rationale is visible before the code it explains. No fix required. -
minor (process) Build verification could not be completed locally: CMake Fortran compiler identification failed and
FindPythoninsrc/efit_to_boozer/CMakeLists.txtfailed. 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
left a comment
There was a problem hiding this comment.
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.
Follow-up to #397 (the intended base branch
fix/395-boozer-cold-inversewas 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_tensorreturnssqrtg = sqrt(abs(det g)), which cannot distinguish a consistent chart orientation from a sign flip across a seam. New type-boundcs%jacobian_det(u)returns the signeddet(dx/du)from the analytic covariant basis;metric_tensoris 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| == sqrtgto 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 productionneo_circular_tokamak_fieldlie exactly on circular tori and are straight in the geometric angles; a tangency self-check verifies the closed-form trace againstcompute_bfieldat 1e-12 first. 27 launches x 50 toroidal transits x 12 samples are pulled back through the warminvert_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_torin 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_cartcomputed a seed-refinement residual and error code (ierr_seed) that nothing read;chartmap_refine_cart_seedloses 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_fieldon 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 writerchartmap_gate_fixtures, self-contained NetCDF in the style oftest_chartmap_invert_cart). Full localctest: 98/98 pass (Debug preset, gfortran), including all existing chartmap/Boozer inverse tests against the cleaned-up cold inverse.