boozer chartmap: fix rho-grid fields and start sampling#360
Merged
Conversation
The Boozer chartmap stores A_phi, B_theta, B_phi against the file's rho grid, but splint_boozer_coord read A_phi off a synthetic uniform-s grid while reading B_theta/B_phi/Bmod on rho. rho is uniform, so s = rho^2 is not, so the mismatched abscissa corrupted dA_phi/ds and therefore iota = -dA_phi_ds/dA_theta_ds, shifting the trapped/passing boundary for GVEC-exported chartmaps. Read A_phi on the rho grid and chain-rule its s-derivatives, matching the existing Bmod/B_theta treatment in splint_boozer_coord; gate by aphi_over_rho so the VMEC-derived direct Boozer path keeps A_phi on VMEC's native uniform-s grid. Sample A_phi natively on the rho grid in compute_boozer_data (aphi_rho) and write that from export_boozer_chartmap, so the exported profile shares the abscissa of B_theta/B_phi without resampling. Add test_chartmap_aphi_abscissa: analytic A_phi nonlinear in s, fails on the old uniform-s reader, passes now. Document the profile contract in DOC/coordinates-and-fields.md.
The chartmap A_phi branch in splint_boozer_coord returned d3A_phi/ds3 = 0. It is unused by the Boozer symplectic path today, but the other s-derivatives are chain-ruled from rho, so complete the third one too: f'''(s) = g'''(rho) rho'^3 + 3 g''(rho) rho' rho'' + g'(rho) rho''', rho = sqrt(s), rho' = 1/(2 rho), rho'' = -1/(4 rho^3), rho''' = 3/(8 rho^5). Extend test_chartmap_aphi_abscissa to check d2A_phi/ds2 and d3A_phi/ds3 against the analytic profile; both match to ~1e-9 relative (a wrong term would show order-unity error since d3 is O(100) at the test points).
…ify readers
A VMEC-free Boozer chartmap run diverged from the equivalent VMEC run in
two ways that Robert reported.
rmajor was never restored on the chartmap path. load_boozer_from_chartmap
set nper but not new_vmec_stuff_mod::rmajor, so stevvo returned R0 = 1 m and
params_init produced dtaumin about ten times too small (the reported
dtaumin/ntau 2.45/528 against the VMEC 24.90/52). export_boozer_chartmap now
writes the rmajor attribute and both readers restore it.
boozer_chartmap_field_t read Bmod on the endpoint-excluded geometry grid
(theta/zeta) instead of the endpoint-included field grid
(theta_field/zeta_field), so its periodic spline period was one cell short.
It now reads the field grid and spans the full 2*pi and 2*pi/nfp period.
The two chartmap readers (boozer_chartmap_field_t and
load_boozer_from_chartmap) duplicated the whole NetCDF parse and disagreed on
scaling. Both now go through one parser, boozer_chartmap_io.read_boozer_chartmap,
and apply vmec_B_scale/vmec_RZ_scale consistently. Dead read_3d_reordered and
check_nc helpers are removed.
The reported ~0.8% bmin/bmax gap is not a conversion defect: the Boozer
reconstruction of |B| matches raw VMEC to ~1e-6 on a flux surface and along a
field line, and for the same equilibrium the VMEC and chartmap runs give
identical confined fractions. The gap comes from comparing different field
sources, not from the chartmap code.
Tests: unit test_boozer_chartmap_io (field-grid dims, periodic spans, rmajor),
regression test_boozer_vmec_bfield_match (|B| extrema match VMEC), and
test_e2e_boozer_chartmap now asserts dtaumin/ntau agree between the two runs.
## Verification
Before (chartmap run, rmajor unset):
tau: 1294.87 dtaumin=2.452 ntau=528
After (rmajor restored from file):
tau: 1294.87 dtaumin=24.901 ntau=52 (== VMEC run)
test_boozer_chartmap_io: PASS
test_boozer_vmec_bfield_match: surface bmax rel 3.3e-6, bmin 9.8e-7;
field-line bmax 1.6e-7, bmin 2.3e-6; PASS
test_boozer_chartmap_roundtrip: Bmod 7.9e-5, orbit < 1e-6; PASS
test_e2e_boozer_chartmap: QA and NCSX PASS, dtaumin/ntau match
21/21 chartmap+field tests, 16/16 unit tests: PASS
Pre-existing, unrelated: golden_record_classifier_fast diverges from main on
this branch independently of this change (identical failure with these edits
stashed). Its VMEC-direct A_phi path is unchanged here; the divergence tracks
the NetCDF/HDF5 toolchain commit and needs a maintainer golden-record review.
Chartmap Boozer startmode=1 samples the starting field line in integrator coordinates (s, theta_B, phi_B) but stores zstart in reference coordinates. Convert those sampled starts with integ_to_ref before writing start.dat on the chartmap path, so sbeg remains flux s while chartmap reference rho is sqrt(s). Apply the same conversion to grid starts and fix the grid linear index stride. Fold the parser-only chartmap unit test into the analytic A_phi/rho test, drop duplicate slow diagnostics, and tighten chartmap tolerances to the measured interpolation floors. The startmode regression uses sbeg=0.5 to catch s-vs-rho confusion directly.
4ea46a0 to
b77fe0e
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fix the Boozer chartmap radial contract end to end.
Changes:
A_phi,B_theta,B_phi, andBmodare read on the chartmaprhogrid. Radial derivatives are converted tos = rho^2by chain rule.rmajor; both chartmap field consumers useboozer_chartmap_io.read_boozer_chartmap.Bmodis read on the endpoint-includedtheta_field/zeta_fieldgrid.(s, theta_B, phi_B); chartmapzstartstores(rho, theta_B, phi_B), sosbeg=0.5now writesrho=sqrt(0.5).generate_start_onlynow stops after writing corrected starts instead of tracing orbits.test_chartmap_aphi_abscissanow covers reader metadata and the analyticA_phi(rho)chain-rule contract.Bmodfloor is the current export resolution, about7.90e-5; the orbit floor is about1.68e-9.Closes #358 and fixes the chartmap
sbegissue tracked in #359.Stack
Base:
main.Merge order:
mainand before this fix.Already on
main: #329, #332, #333, #338, #340, #346, #348, #355, and the Boozer flux-sign doc commit.Verification
Test fails before the start-sampling fix
Tests pass after the fix
External W7X/GVEC reruns
Run with
OMP_NUM_THREADS=1using the rebasedfix/chartmap-aphi-rho-abscissabuild. These cases do not usegenerate_start_only, so they were not rerun aftercf3ff9e.