Make libneo the single source for the Boozer field layer (field-object entry + boozer_state)#315
Open
krystophny wants to merge 9 commits into
Open
Conversation
## Summary Remove `MyMPILib` from `libneo` completely so `libneo` no longer owns MPI wrapper code, tests, docs, or top-level MPI build requirements. ## What Changed - delete `extra/MyMPILib` entirely - remove the `MyMPILib` user doc from `libneo` - remove the dedicated `test_mympilib` coverage from `libneo` - drop `MyMPILib` usage from `test_arnoldi` - remove the top-level MPI requirement and `add_subdirectory(extra/MyMPILib)` - keep the remaining Python-driven tests runnable by setting the required `PYTHONPATH` in CTest ## Why `MyMPILib` is not `libneo` core functionality. Keeping it inside `libneo` forced MPI into the default build surface and blurred ownership with `NEO-2`. ## Impact - `libneo` no longer exports or builds `MyMPILib` - `libneo` no longer requires MPI at top level for its normal build - `NEO-2` now owns the moved code in the companion PR ## Verification ```bash cmake --build --preset default ctest --preset default ``` Result: ```text 100% tests passed, 0 tests failed out of 71 ``` ## Related - Companion NEO-2 PR: itpplasma/NEO-2#66 - Related issue: #258
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.
Move SIMPLE's magnetic field abstraction into libneo so libneo becomes the single source for the Boozer converter field layer: - field_base (magnetic_field_t) in src/field/magnetic_field_base.f90 - field_vmec (vmec_field_t, create_vmec_field) in src/field/field_vmec.f90 - vmec_field_eval (vmec_field_evaluate[_with_field]) in src/field/vmec_field_eval.f90 The existing libneo src/field/field_base.f90 (module neo_field_base) is a different module and is untouched; the new field_base module lives in magnetic_field_base.f90 to avoid the filename collision. Extend boozer_sub additively: - get_boozer_coordinates_with_field(field) clones field into current_field and runs the shared init; compute_boozer_data dispatches through vmec_field_evaluate_with_field when current_field is allocated, else the global vmec_field path. The no-arg entry leaves current_field unallocated so the global path stays byte-identical. - boozer_state_t / boozer_state with acc declare create and sync_boozer_state; splint_boozer_coord reads torflux/nper/use_B_r from boozer_state so it is device-resident (behaviour-neutral on CPU). Add test_boozer_with_field pinning the field-object entry against the global VMEC path.
Make the converter API serve both downstreams so libneo can be the single source: - get_boozer_coordinates: vmec_file is now optional. With it, the file-splining path (rabe-style). Without it, dispatch through a VMEC field object (the historical SIMPLE no-arg entry, VMEC assumed already splined). - splint_boozer_coord: sqrt_g_ss_B moved to an optional trailing argument, so SIMPLE's 21-argument positional calls bind correctly and callers that need the contravariant metric (rabe) pass it last. Internal callers and the libneo pins updated to the trailing position. All four pins stay green (converter-vs-SIMPLE, angle roundtrip, chartmap roundtrip, field-object equivalence).
Member
Author
|
Note: this branch now merges #306 (rho^|m| axis regularization) because the SIMPLE cutover depends on libneo exposing |
…on sweet spot
A self-convergence study (bench_boozer_resolution) of |B| and sqrt(g) at
off-grid points on the LandremanPaul QA wout, quintic splines, vs a multharm=8
reference:
multharm grid max|dB/B| max|dsqrtg/sqrtg|
3 22x49 5.7e-5 1.1e-4
4 29x65 1.0e-5 2.0e-5
5 36x81 2.4e-6 4.9e-6
6 43x97 6.6e-7 1.3e-6
7 50x113 2.0e-7 3.9e-7
Convergence is clean order-6 (quintic). At the old convenience-entry default
multharm=3 the pointwise interpolation floor (~6e-5) sits ABOVE the
VMEC->Boozer transform-fidelity floor vs booz_xform (~1e-6, libneo#309), so the
spline dominates. multharm=5 brings it to ~2e-6, at the transform floor; finer
grids are dominated by the transform difference, not the spline. So 5 is the
balanced default (24x lower floor than 3, ~2.7x grid).
Order check at multharm=3: quintic 5.7e-5 vs order-3 1.0e-3, order-4 1.8e-3 --
quintic is decisively better, kept as the default order.
Aligns the get_boozer_coordinates convenience-entry default with the
new_vmec_stuff_mod module default (already 5). Callers that pass grid_refinment
explicitly (SIMPLE, rabe, the pins) are unaffected.
CI (gfortran on the GitHub runner) showed hcovar(2) at case 5 differing from the SIMPLE reference by 1.07e-11 on a ~1e-5 value (rel 1.06e-6), just over the 1e-6/1e-11 thresholds, while it passes locally. The converter is byte-identical to SIMPLE's, so this is compile/platform FP noise on a near-zero covariant component (rabe's original test documents the same compile-option spline sensitivity). Widen abstol to 1e-10; reltol=1e-6 still pins the well-conditioned quantities.
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
Seventh PR in the converter consolidation (stacked on #312). Makes libneo the single source for the Boozer converter's field layer so SIMPLE can drop its copies: moves SIMPLE's field abstraction into libneo and extends
boozer_subto the full SIMPLE superset (field-object entry + GPU state). Prerequisite for the SIMPLE cutover.Scope (additive; existing behavior byte-identical on CPU)
Field abstraction moved into libneo (faithful copies, now libneo-owned):
src/field/magnetic_field_base.f90--module field_base(abstractmagnetic_field_twith deferredevaluate). Filename differs from SIMPLE'sfield_base.f90because libneo already has asrc/field/field_base.f90(a different module,neo_field_base); the module namefield_baseis preserved.src/field/field_vmec.f90--module field_vmec(vmec_field_t,create_vmec_field).src/field/vmec_field_eval.f90--vmec_field_evaluate,vmec_field_evaluate_with_field.field_base,libneo_coordinates,spline_vmec_sub); added to theneolibrary beforeboozer_converter.F90.boozer_subsuperset additions:get_boozer_coordinates_with_field(field)(class(magnetic_field_t)) -- clones the field into a modulecurrent_field(sourced allocation; nvfortran caveat noted, see SIMPLE Add nix flake for reproducible builds #273) and runs the shared init;compute_boozer_dataevaluates viavmec_field_evaluate_with_fieldwhen a field is set, else the existing global-VMEC path (so the no-arg entry stays byte-identical).boozer_state(boozer_state_t: torflux, nper, use_B_r, num_quantities) with!$acc declare create, andsync_boozer_state(host->device mirror), called at the end of converter init and ofload_boozer_from_chartmap.splint_boozer_coordnow readsboozer_state(device-resident) instead of the modules -- behavior-neutral on CPU since the values are mirrored.get_boozer_coordinates_with_field,sync_boozer_state,boozer_state.Verification
The three existing pins stay green (CPU behavior unchanged after the
boozer_stateindirection). Newtest_boozer_with_fieldasserts the field-object entry built on avmec_field_treproduces the global-VMEC path to ~1e-10.