Skip to content

Move non-event orbit integrations onto the fortnum ODE solver#46

Draft
krystophny wants to merge 8 commits into
mainfrom
migrate/fortnum-ode-nonevent
Draft

Move non-event orbit integrations onto the fortnum ODE solver#46
krystophny wants to merge 8 commits into
mainfrom
migrate/fortnum-ode-nonevent

Conversation

@krystophny

@krystophny krystophny commented Jun 14, 2026

Copy link
Copy Markdown
Member

Move the non-event orbit integrations (bounce_fast) from DVODE_F90 onto the
fortnum nonstiff variable-order Adams integrator (fortnum_ode_vode, DVODE
MF=10 equivalent): single span [0, taub], scalar relerr 1e-9, per-component
abserr 1e-10 (ITOL=2). Re-pins the fortnum dependency from v0.1.0 to fortnum
main 974dcf1.

Call sites

  • src/orbit.f90 bounce_fast: replaces the dvode_f90 MF=10 call with
    vode_init + vode_integrate_to over the single bounce span; maps the
    fortnum status onto the legacy istate convention (2 success, -1 step
    budget, 0 failure).
  • CMakeLists.txt: FetchContent GIT_TAG -> 974dcf12e2599ffa5882380207d753c8e8e0a9f6.

Verification

Golden record test/golden_record/test_golden_record.py against the
DVODE_F90-generated golden.h5 (bar rtol=1e-8, atol=1e-15), CONFIG=Fast,
worst relative deviation per output:

The bounce_fast path on fortnum vode agrees with the DVODE reference far
better than the prior DOP853 substitution (which was ~1e-4 to ~15% on
output):

case output rel output abs
0p100 2.9e-9 7.5e-13
0p200 5.3e-9 5.6e-12
0p300 9.9e-9 3.7e-12
0p400 9.3e-9 1.2e-12
0p500 2.5e-7 3.1e-12
0p600 3.6e-7 1.1e-11
0p700 1.7e-7 2.9e-10
0p800 5.8e-8 1.5e-10
0p900 4.7e-8 2.2e-10

magfie matches exactly (rel 0). The bounce-averaged output (D11/D12)
agrees to 1e-9..3.5e-7.

The golden test still reports 9 failed on this branch, for two reasons:

  1. The remaining integral / torque_integral failures (rel ~1e-4) come from
    the event-path bounce_integral, which on this branch still uses the
    DOP853 chunked scan; its taub is not the DVODE root. That path is migrated
    in the stacked PR Move bounce_integral event search onto fortnum and drop vode #47.
  2. Even the bounce_fast agreement tops out at ~3.6e-7 (worst case 0p600),
    above the 1e-8 bar. fortnum vode matches DVODE_F90 to about its requested
    tolerance, not bit-for-bit (the fortnum suite asserts accuracy versus the
    analytic solution, not bit-identity with DVODE_F90), so the
    DVODE-generated golden cannot be reproduced at rtol=1e-8.

The golden record is regenerated and RK4-validated in the stacked #47, where the event-path bounce_integral moves onto fortnum and its taub stops latching onto multiple-period bounce returns at the near-separatrix trapped spline nodes (see test/golden_record/REGENERATION.md). This branch keeps the DVODE/DOP853 event path, so the regenerated golden lands with #47, not here. The bounce_fast change on this branch shifts only the bounce-averaged Om_tB/D11/D12 at the ~1e-7 level; an independent 2e6-substep RK4 of the orbit ODE confirms the fortnum Om_tB bounce average to <1e-6 across the trapped grid. No tolerances were weakened.

Note: fortnum pin updated to current main (92de6e9) after a fortnum history rewrite; old shas no longer resolve.

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.
@krystophny krystophny force-pushed the migrate/fortnum-ode-nonevent branch 2 times, most recently from 4421b9c to 08acde3 Compare June 14, 2026 10:17
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.
@krystophny krystophny force-pushed the migrate/fortnum-ode-nonevent branch from 08acde3 to b9718a3 Compare June 14, 2026 14:31
@krystophny krystophny changed the base branch from migrate/fortnum-ode-cmake to main June 14, 2026 18:26
@krystophny krystophny closed this Jun 14, 2026
@krystophny krystophny reopened this 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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant