Move bounce_integral event search onto fortnum and drop vode#47
Draft
krystophny wants to merge 10 commits into
Draft
Move bounce_integral event search onto fortnum and drop vode#47krystophny wants to merge 10 commits into
krystophny wants to merge 10 commits into
Conversation
Wire the fortnum numerical-core library via CMake FetchContent (git@github.com:lazy-fortran/fortnum.git, pinned to a7faa3c) guarded by if(NOT TARGET fortnum) so a parent build that already provides the target is reused. Link the static library into neo_rt; its module directory is exported PUBLIC, so neo_rt and the diagnostics library that links neo_rt both see the fortnum modules. This commit adds the dependency without changing any solver call site; the existing DVODE path is untouched and still in use. Subsequent commits move the orbit integrations onto the fortnum ODE solver and then drop DVODE.
14ac98c to
4c1cf84
Compare
6904b2a to
93343cb
Compare
4421b9c to
08acde3
Compare
93343cb to
21becd7
Compare
Replace the DVODE calls in the two fixed-interval, no-event integrations with the fortnum DOP853 solver: - orbit.f90 bounce_fast: ode_solve_dop from 0 to taub, take the final recorded state. The fortnum status maps onto the legacy istate the transport caller branches on (2 success, -1 step budget exhausted, 0 other), so dvode_error_context still fires on the exhausted-budget case and the public bounce_fast signature is unchanged. - diag/diag_bounce_debug.f90 probe_bounce: ode_integrate_dop, then read solution%nsteps, solution%nrejected and the last solution%h into the diagnostic columns the scan previously filled from get_stats. - freq.f90: drop the vode_thread_init shim. The fortnum integrator keeps no module-level state, so freq_thread_init has nothing VODE-specific to initialise. The event-based bounce_integral and the vode dependency itself stay until the next commit so this change is reviewable on its own.
Two defects surfaced once bounce_fast ran on fortnum DOP853 instead of DVODE, both caught only by the CI Debug build (gfortran 13, -ffpe-trap=zero,overflow,invalid,underflow): - timestep_transport left ydot(7), the unused abs(B) slot, uninitialised. DVODE ignored the stray derivative, but DOP853 carries every component through its stage combinations, so the leftover denormal tripped the underflow trap inside dop853_step. Zero the trailing integrands the routine does not compute, matching what timestep already does. - The bounceavg(3:4) perturbed-Hamiltonian integrands oscillate at the resonant harmonic but are driven, not fed back into the orbit, so the rtol error estimate barely constrains them. The unconstrained adaptive step grew too coarse and the oscillatory integral came out wrong (D11 off by an order of magnitude). Drive bounce_fast through ode_integrate_dop with hmax = taub/200 to keep the step fine enough; this recovers the DVODE result to four significant figures.
08acde3 to
b9718a3
Compare
ead488c to
26ee48f
Compare
This was referenced Jun 14, 2026
Replace the DOP853 substitution in bounce_fast with fortnum vode (DVODE MF=10 nonstiff Adams), relative tolerance 1e-9 and a per-component absolute tolerance 1e-10 (DVODE ITOL=2), over the single bounce span. This restores the variable-order method NEO-RT drove through DVODE before the migration, so the oscillatory bounce-average integrands no longer need the artificial DOP853 step cap. Re-pin fortnum to the commit that adds the two-event root support the bounce_integral event path needs.
Replace the DVODE_F90 bounce_integral event search with the fortnum nonstiff Adams integrator monitoring two event functions (NEVENTS=2): theta returning to th0 and theta advancing by 2*pi. The integrator locates the earliest root on its Nordsieck interpolant, so taub is the located root at relerr 1e-9 and per-component abserr 1e-10. Reproduce the DVODE acceptance filter: keep a root when passing or when theta entered from below th0. Drop the now-unused vode dependency.
26ee48f to
781143d
Compare
The DVODE golden returned an integer multiple of the fundamental bounce period at near-separatrix trapped spline nodes. Building the canonical poloidal-frequency spline calls bounce_time on a 100-point eta grid; at the nodes nearest the separatrix the DVODE chunked event search, seeded from the previous node, latched onto the second or third theta=th0 return instead of the first. fortnum returns the fundamental period. A 2e6-substep fixed-step RK4 of the poloidal-motion ODE (converged at 2e6, 16e6, 64e6 substeps) confirms the fundamental at every mismatch node: fortnum/fundamental is 0.99 to 1.00, DVODE/fundamental is 2.00 (5.00 at 0p900 k=99). Passing orbits and non-separatrix trapped orbits match DVODE to ~1e-6. fortnum passes RK4 validation for all 9 cases. golden.h5 is now a committed artifact built CONFIG=Fast from the fortnum branch. ensure_golden uses a committed golden as-is and regenerates from main only when none is present. The comparison logic and tolerances in test_golden_record.py are unchanged; it passes for all 9 cases at rtol=1e-8 atol=1e-15. REGENERATION.md records the per-case OLD-vs-NEW-vs-RK4 evidence.
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.
Stacked on #46. Replace the
bounce_integralevent search (DVODE_F90 withbounceroots, NEVENTS=2) with the fortnum vode Adams integrator monitoring thesame two event functions, and drop the now-unused vode dependency.
Call site
src/orbit.f90bounce_integral:vode_integrate_towith two eventfunctions (
event+event2),root_theta: g1 = theta - th0 (trapped return to th0),root_turn: g2 = 2pi - (theta - th0) (passing full +2pi turn),advancing in dt-sized windows, continuing the integration across rejected
turns (DVODE istate=2), so taub is the integrator-located root. Acceptance
mirrors the DVODE filter: accept when passing, or when the previous window
endpoint had theta below th0.
CMakeLists.txt: dropfind_or_fetch(vode)and the vode link/include; thefortnum pin from Move non-event orbit integrations onto the fortnum ODE solver #46 (main 974dcf1) carries over.
Golden record regenerated and RK4-validated
The golden was regenerated from this branch and the old DVODE golden retired,
after an independent reference confirmed the DVODE values were wrong on the
event path. Details in
test/golden_record/REGENERATION.md.Root cause: NEO-RT builds the canonical poloidal-frequency spline in
init_canon_freq_trapped_splineby callingbounce_timeon a 100-point etagrid at
v = vth. At the nodes nearest the separatrix the DVODE chunked eventsearch, seeded by the
taub_estimatechained from the previous node, steppedpast the first theta=th0 return and accepted the second or third full-period
crossing. The stored DVODE taub at those nodes is an integer multiple of the
fundamental bounce period. fortnum returns the fundamental.
Validation: a fixed-step RK4 of the poloidal-motion ODE, ~2e6 substeps per
fundamental period, the bounce event bracketed at every theta=th0 crossing and
root-polished by RK4 sub-bisection. Converged at 2e6, 16e6, 64e6 substeps. At
every node where DVODE and fortnum disagree above 1%, fortnum/fundamental is
0.99 to 1.00 and DVODE/fundamental is 2.00 (5.00 at 0p900 k=99):
Passing orbits and non-separatrix trapped orbits match DVODE to ~1e-6 and both
match RK4. No case showed fortnum as the outlier, so none was held back.
Per-case transport, DVODE golden vs fortnum (magfie bit-identical; the shift is
the trapped diffusion channel that carries the multiple-period nodes):
Verification
golden.h5is now a committed artifact built CONFIG=Fast from this branch.ensure_golden.pyuses a committed golden as-is and regenerates from main onlywhen none is present. The comparison logic and tolerances in
test_golden_record.pyare unchanged.test/golden_record/test_golden_record.py, CONFIG=Fast, bar rtol=1e-8atol=1e-15:
Before this commit, against the old DVODE golden: 9 failed, output rel up to
~53%, integral rel up to ~4.0.
Note: fortnum pin updated to current main (92de6e9) after a fortnum history rewrite; old shas no longer resolve.