Skip to content

Decouple integration microstep dtaumin from trace_time#433

Closed
krystophny wants to merge 1 commit into
mainfrom
fix/dtaumin-trace-time-decouple
Closed

Decouple integration microstep dtaumin from trace_time#433
krystophny wants to merge 1 commit into
mainfrom
fix/dtaumin-trace-time-decouple

Conversation

@krystophny

Copy link
Copy Markdown
Member

Risk tier

  • T3: physics, output behavior, coordinate convention

Correctness contract

Intended behavior change

The integration microstep dtaumin no longer depends on trace_time. It was
overwritten with dtau/ntau (dtau = trace_time*v0/(ntimstep-1)), so the
physical resolution was tied to the total trace length: two runs differing only
in trace_time integrated at slightly different microsteps (an off-by-one in
ntau = ceiling(dtau/dtaumin)), and confined-fraction curves of different
length were not comparable. On the W7-X reactor case this shifted the confined
fraction ~11% at 100 ms between a 0.1 s and a 1 s run with identical start.dat.

dtaumin is now pinned to 2*pi*rbig/npoiper2 (resolution from npoiper2
alone). The total microsteps are derived from it (nint(tau/dtaumin)) and
distributed over the ntimstep output points by cumulative rounding, in both
the linear and log macrostep schedules (the log branch already did this).

Behavior that must not change

Macrostep/output count (ntimstep), log-grid 10:1 spacing, and the microstep
resolution in the asymptotic regime (dtau >> dtaumin, i.e. large ntau):
there the change is < 1 part in ntau.

Coordinate / unit conventions

Unchanged.

Numerical invariants

dtaumin is a function of (rbig, npoiper2) only. Two runs differing only in
trace_time integrate at identical resolution.

Tests added

  • unit:
  • integration: test_dtaumin_invariance - dtaumin is identical across a 10x
    trace_time change and equals 2*pi*rbig/npoiper2.
  • system:
  • golden record: regeneration pending review (see impact).

Golden-record impact

  • changed

Loss times shift. Measured against run_main:

  • realistic fields (boozer, canonical, ...): max|dt| ~ 9.4e-5 - the
    total-trace-time rounding on confined markers' final time. Negligible.
  • analytic TEST tokamak (test_tokamak, *_classifier): large
    (max|dt| ~ 2.5e2). Those configs request a coarse npoiper2 whose microstep
    the old code silently refined for short traces; the orbits are chaotic, so
    loss times decorrelate. The new resolution is exactly what npoiper2 asks for.

All 12 golden records change for the corrected (now trace_time-independent)
behavior and need regeneration.

Failure modes considered

Degenerate short traces (tau < nintv*dtaumin) are guarded by
n_microsteps_total = max(nintv, nint(tau/dtaumin)) so every macrostep keeps at
least one microstep. init_sympl is called with (dtaumin, dtaumin), so its
dtau/dtaumin integer check is unaffected.

Manual validation

10/10 orbit/timestep/macrostep ctest pass (test_macrostep_log_grid,
orbit_netcdf_output, test_orbit_chartmap_comparison,
test_orbit_refcoords_rk45, ...).

Verification

Failing-before (old dtaumin = dtau/ntau), TEST field npoiper2=256:

assert 0.0202020202020202 == 0.02405002405002405 +/- 1.0e-12   # trace_time 10 vs 100
FAILED test_dtaumin_invariance.py::test_dtaumin_independent_of_trace_time

Passing-after (pinned dtaumin):

test/python/test_dtaumin_invariance.py::test_dtaumin_independent_of_trace_time PASSED

dtaumin was overwritten with dtau/ntau, tying the physical resolution to
the total trace length: two runs differing only in trace_time integrated
at slightly different microsteps (off-by-one in ntau), so confined-fraction
curves of different length were not comparable and chaotic near-axis orbits
flipped fate (an ~11% shift at 100 ms on the W7-X reactor case).

Pin dtaumin = 2*pi*rbig/npoiper2 (resolution from npoiper2 only); derive the
total microsteps from it and distribute them over the ntimstep output points
by cumulative rounding in both the linear and log macrostep schedules. ntau
is kept as a representative substep count for diagnostics.

Add test_dtaumin_invariance: dtaumin is identical across a 10x trace_time
change and equals the npoiper2 resolution target (was ~19% apart before).
@krystophny krystophny added tier/T3 physics or output behavior size/M review size up to 300 changed lines labels Jun 26, 2026
@krystophny krystophny closed this Jun 27, 2026
@krystophny krystophny deleted the fix/dtaumin-trace-time-decouple branch June 27, 2026 07:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/M review size up to 300 changed lines tier/T3 physics or output behavior

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant