Add rho^|m| near-axis regularization for VMEC harmonics#306
Conversation
VMEC full-grid Fourier amplitudes near the axis violate the analytic regularity c_m(rho) ~ rho^|m| with rho = sqrt(s). The existing healing (determine_nheal_for_axis, 30% tolerance) is a smoothness test and lets the smooth parity violation and sub-tolerance grid noise through. On the W7-X high-mirror case that noise drives SIMPLE's symplectic integrator into secular radial transport (r_negative ~ 600 in a 0.01 s trace). s_to_rho_power_law continues each harmonic to the axis as c(rho) = c_anchor * (rho/rho_anchor)^|m| below rho_axis_heal, with the spline built only from surfaces at or outside the anchor. This is the booz_xform_to_boozer_chartmap continuation applied at the VMEC harmonic level, so the Boozer and canonical transforms inherit a clean near-axis field from one place. New flags in new_vmec_stuff_mod: axis_healing_power_law (default .true.) and rho_axis_heal (default 0.1). Legacy healing stays behind axis_healing_power_law = .false. Verified on W7-X high mirror: internal Boozer field, symplectic Euler1, 0.01 s trace, r_negative 606 -> 2.
The power-law near-axis continuation changed the VMEC field for every consumer when defaulted on. On the coarse Landreman-Paul QA fixture (ns=50) the anchor lands at surface 2 and the c/rho**|m| division amplifies the radial startup layer, distorting the near-axis geometry so the chartmap inversion Newton solve diverges (test_chartmap_matches_vmec, test_chartmap_vmec_mapping). Make axis_healing_power_law default .False.: legacy healing for existing chartmap/field consumers, explicit namelist opt-in for SIMPLE near-axis symplectic tracing. The W7-X benchmark runs already set the flag, so the near-axis defect fix is unchanged there.
The near-axis continuation in s_to_rho_power_law multiplied the reduced coefficient splcoe(0,1) = arr_in(i_anchor)/rho_anchor**|m| by (rho/rho_anchor)**|m|, leaving a spurious 1/rho_anchor**|m| factor. This contradicts the routine's own documented form c(rho) = c_anchor*(rho/rho_anchor)**|m| and introduces a discontinuity at the anchor surface (factor ~10 for m=1, ~99 for m=2 at the default rho_axis_heal=0.1), corrupting the near-axis field the regularization is meant to clean. Use splcoe(0,1)*rho**|m|, which equals arr_in(i_anchor)*(rho/rho_anchor)**|m| and is continuous across the anchor. Add test_axis_power_law_regularization pinning: axis vanishing for m>0, exact reproduction of an amp*rho**m harmonic on the full rho grid, anchor continuity, and the i_anchor index formula.
Added: fix for the near-axis continuation + regression coverageWhile adding the regression test that pins this regularization (it had no coverage), the test caught a correctness bug in The continuation is documented as VerificationThe new test pins: axis vanishing for m>0, exact reproduction of an |
Problem
VMEC full-grid Fourier amplitudes near the axis violate the analytic regularity condition
c_m(rho) ~ rho^|m|(withrho = sqrt(s)). The existing axis healing (determine_nheal_for_axis, 30% tolerance) is a smoothness test: it is structurally blind to the smooth parity violation and lets sub-tolerance grid noise through. On the W7-X high-mirror equilibrium the measuredrmnc/rho^mquotient of the dominant m=3 mode flips sign at the innermost surface and the m=4 quotient is 6.3x its asymptote, the textbook near-axis VMEC signature.Downstream, SIMPLE's symplectic integrator converts that near-axis noise into secular radial transport:
r_negative ~ 600in a 0.01 s internal-Boozer trace, with a creeping non-prompt loss tail absent from RK and from external booz_xform chartmaps on the same equilibrium.Fix
s_to_rho_power_lawcontinues each harmonic to the axis with the analytic power law:Below the anchor the surfaces are discarded; outside it the spline acts on
c/rho^|m|, an even analytic function of rho, built only from reliable surfaces.mis clamped at 50. This is thebooz_xform_to_boozer_chartmap.pycontinuation (which mirrors booz_xform's ownrmnc/sqrt(s)extrapolation) applied at the VMEC harmonic level, so both the Boozer and the canonical transforms inherit a clean near-axis field from one shared evaluator chain (vmec_field -> splint_vmec_data). Neither transform needs its own change.The regularity
c_m ~ rho^|m|is standard (Garren-Boozer 1991; Landreman-Sengupta 2018/2019; DESC and booz_xform both enforce it).Interface
New flags in
new_vmec_stuff_mod:axis_healing_power_law(default.false.): opt in to the power-law continuation, bypassingdetermine_nheal_for_axis.rho_axis_heal(default0.1): anchor radius. At ns=501 this replaces the inner 5 of 501 surfaces (inner 1% of flux); the field is unchanged beyondrho ~ 0.14.The default keeps the legacy healing, so existing chartmap and field consumers are byte-for-byte unchanged. Consumers that need the regularization (SIMPLE near-axis symplectic tracing) set the flag from their own namelist.
Verification
W7-X high mirror, internal Boozer field, symplectic Euler1, 0.01 s, 1000 fixed-start alphas:
The flag is opt-in because the continuation changes the near-axis field for every consumer. On the low-resolution CI fixture (ns=50) the
c/rho^|m|division at the second surface amplifies the startup-layer noise instead of removing it. That distorts the near-axis geometry enough to diverge the chartmap Newton inversion (chartmap from_cyl failed ierr=1), which is what broketest_chartmap_matches_vmecandtest_chartmap_vmec_mappingwhen the flag defaulted on. With the default.false.both chartmap tests pass unchanged; the regularization is exercised only by consumers that request it.1 s benchmark runs (symplectic Boozer and canonical on the regularized field) are in progress in the orbit-benchmark repo.
Note for maintainers
The default
.false.leaves the legacy near-axis field bit-reproducible, so existing golden records are unaffected. Enabling the flag shifts the near-axis harmonics by design; golden records generated under it differ from the legacy ones, and regenerating them is the intended follow-up for consumers that adopt it.