You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Build - Bump FastInterpolations.jl to v0.4.20 for PeriodicBC fix #531
FastInterpolations.jl versions before v0.4.20 build the wrong super-diagonal in the PeriodicBC cubic-spline system on non-uniform grids, so periodic splines on non-uniform knots are incorrect. Fixed upstream in ProjectTorreyPines/FastInterpolations.jl#220, released in v0.4.20.
On develop (44e0afc), GPEC is exposed in two ways:
the CI manifests (ci/manifests/Manifest-v1.11.toml, Manifest-v1.12.toml) lock v0.4.18;
the Project.toml compat bound (FastInterpolations = "0.4.10") still allows affected versions on a fresh install.
PeriodicBC is used throughout src/ (47 call sites), mostly in Equilibrium (EQUIL) and LocalStability (LS) along the poloidal angle. Any of these on a non-uniform grid would be affected.
Proposed fix: raise the compat bound to FastInterpolations = "0.4.20" and regenerate both CI manifests, then run the regression harness to check whether results move.
Thanks again for the this fix on FastInterpolations side :)
The followings are some additional info for Logan and reviewers:
this is a fairly small bug. Even on strongly non-uniform grids, the added interpolation error is around 1e-6, so it shouldn't have much impact on GPEC in practice. I also confirmed that the full test suite passes locally with v0.4.20.
A bit more detail:
Where it applies. Most PeriodicBC call sites use uniform θ grids (range / rzphi_ys). There h_{i-1} = h_i, so the fix changes nothing. The main exception is DirectEquilibrium.jl:534, which splines the field-line ODE step points onto the uniform θ grid. Those points are somewhat irregular, mostly because of a short final step.
DIII-D-like example: δW, Δ′ and the equilibrium scalars move by ~1e-5 relative with a fixed ψ-grid, and by ~1e-4 with the default auto grid. Most of the auto-grid shift comes from the grid now picking 320 surfaces instead of 598.
Solovev example: ~1e-4–1e-3 at the default etol = 1e-10. At the example's loose etol = 1e-7 (only ~20–30 ODE points per surface), a few near-marginal quantities such as et[1] and Δ′ shift by up to a few percent.
In all cases v0.4.20 is closer to a tight-tolerance reference, and the stability verdicts are unchanged.
v0.4.20 also brings other improvements, including performance work, so I also recommend updating the Project.toml to use FastInterpolations >= v0.4.20.
@logan-nc
This is just a minor/patch version bump under the same major version (v0.4.x), so there is no breaking changes for API at all.
Therefore, updating the Project.toml is sufficient.
@mgyoo86@jhalpern30 — I ran the full regression harness for the v0.4.20 bump (#532) and the shifts are much larger than the ~1e-5–1e-4 estimated above, so I bisected them. Summary: the PeriodicBC fix is correct and is the only part of v0.4.20 that matters, but its effect on Solovev is large, and it lands the DIII-D-like example's auto ψ-grid on a grid where the forward integrator breaks down.
Harness, develop @ 278057d, manifests differing only in FastInterpolations (0.4.18 vs 0.4.20): 165 tracked quantities changed across 15 of 18 cases. Headlines:
case
quantity
0.4.18
0.4.20
diiid_n1 (forward, auto grid)
Re(et[1])
0.8009
1.7814
Im(et[1])
7.9e-5
0.531
ODE steps (saved)
1441
822
diiid_n1_riccati (same eq. + grid)
Re(et[1])
0.8035
0.8032
solovev_n1
Re(et[1])
0.442
0.0475
equilibrium scalars (q0, q95, β, li)
~1e-6 rel.
Bisect (examples run directly, develop source):
FastInterpolations
Solovev et[1]
DIII-D-like et[1]
DIII-D ODE steps
0.4.18
0.44197
0.80087
1441
0.4.19
identical to 0.4.18
identical
1441
0.4.20
0.04754
1.7814 + 0.531i
822
0.4.20 with the du[i-1] line reverted
0.44197
0.80087
1422
0.4.18 with only the du[i-1] fix
0.04754
0.80062
822
So the one-line super-diagonal fix accounts for the shift; everything else in 0.4.19/0.4.20 is roundoff-level.
Solovev — the shift is real and v0.4.20 converges correctly. Fixed ldp grid, tightening the equilibrium etol:
etol
0.4.18
0.4.20
1e-7 (example default)
0.442
0.0475
1e-10
0.01606
0.01089
1e-12
0.01603
0.01090
et[1] here is a near-cancellation (ep ≈ −10.41, ev ≈ +10.43), which amplifies a ~5e-4 relative change in ep by ~1000×. 0.4.18 keeps a ~30% offset even at etol = 1e-12: the wrong super-diagonal is a systematic bias, not something that converges away.
DIII-D-like — the 1.78 + 0.53i is the auto grid, not the fix. On fixed grids v0.4.20 is benign:
grid
0.4.18
0.4.20
ldp 512
0.80073
0.80056
ldp 1024
0.80093
0.80062
On the auto grid, 0.4.18 + only the fix and real 0.4.20 take the identical 822-step path, yet give 0.8006 vs 1.78 + 0.53i — a roundoff-level difference (e.g. the rewritten cubic eval kernel) producing an O(1) change with a large imaginary part. That's the same amplification family as #347, now in the forward-integrator energy itself, and it's what #349/#370/#525 describe: the fix changes the curvature measure, the auto grid gets coarser (598 → 320 surfaces, as noted above), and the forward solution on that grid is ill-conditioned. Downstream DIII-D quantities (resonant field, NTV torque ×1700, island widths) inherit it.
Proposal: take the bump (it's a correctness fix), but sequence it with #525's knot-density floor so diiid_n1 doesn't re-pin the harness to a pathological value. Separately, a large Im(et[1])/Re(et[1]) seems worth a warning as a cheap detector for this failure mode.
Thanks @matt-pharr for the much more thorough investigation, this is very helpful.
I mostly looked at the base quantities, which is why I estimated at most a few percent, and I didn't realize how much the near-cancelled values matter here. Either way, the fix is clearly needed.
More generally, a few notes on sensitive systems like this, where ULP-level differences can get amplified:
Fundamentally, ULP-level results can't be trusted. Even the order of a simple sum changes the answer:
So ULP-level results can change at any time, even in a minor or patch release of a package (FastInterpolations included). Simply reordering a single operation is enough.
Even the exact same source code can give different ULP-level results on different hardware, OS or Julia versions, depending for example on whether the compiler fuses a*b - c into an FMA (0.1*3.0 - 0.3 gives 5.55e-17, fma(0.1, 3.0, -0.3) gives 2.78e-17) or how SIMD reorders a reduction. For example, running the same DIII-D-like example with FastInterpolations of v0.4.20 on my machine (macOS arm64, Julia 1.13.2) gives 0.80062 rather than 1.78 + 0.53i. In practice, identical behavior at the ULP level is not something we can expect.
Fundamentally, I think the direction should be to remove this kind of error amplification itself. A high-precision reference (e.g. BigFloat) would also help separate real errors from round-off amplification. FastInterpolations already supports BigFloat, but GPEC is largely hard-coded to Float64, so it would need a substantial refactor. Happy to help if there's interest.
Activity
mgyoo86 commentedon Oct 15, 2026
Hi @StuartBenjamin,
Thanks again for the this fix on FastInterpolations side :)
The followings are some additional info for Logan and reviewers:
this is a fairly small bug. Even on strongly non-uniform grids, the added interpolation error is around 1e-6, so it shouldn't have much impact on GPEC in practice. I also confirmed that the full test suite passes locally with v0.4.20.
A bit more detail:
PeriodicBCcall sites use uniform θ grids (range/rzphi_ys). Thereh_{i-1} = h_i, so the fix changes nothing. The main exception isDirectEquilibrium.jl:534, which splines the field-line ODE step points onto the uniform θ grid. Those points are somewhat irregular, mostly because of a short final step.etol = 1e-10. At the example's looseetol = 1e-7(only ~20–30 ODE points per surface), a few near-marginal quantities such aset[1]and Δ′ shift by up to a few percent.v0.4.20 also brings other improvements, including performance work, so I also recommend updating the Project.toml to use FastInterpolations >= v0.4.20.
logan-nc commentedon Oct 16, 2026
@mgyoo86 are there any API changes we need to be aware of? Or is it a fairly simple mater of just updating the
Project.toml?mgyoo86 commentedon Oct 16, 2026
@logan-nc
This is just a minor/patch version bump under the same major version (v0.4.x), so there is no breaking changes for API at all.
Therefore, updating the
Project.tomlis sufficient.matt-pharr commentedon Oct 16, 2026
@mgyoo86 @jhalpern30 — I ran the full regression harness for the v0.4.20 bump (#532) and the shifts are much larger than the ~1e-5–1e-4 estimated above, so I bisected them. Summary: the PeriodicBC fix is correct and is the only part of v0.4.20 that matters, but its effect on Solovev is large, and it lands the DIII-D-like example's auto ψ-grid on a grid where the forward integrator breaks down.
Harness, develop @ 278057d, manifests differing only in FastInterpolations (0.4.18 vs 0.4.20): 165 tracked quantities changed across 15 of 18 cases. Headlines:
diiid_n1(forward, auto grid)diiid_n1_riccati(same eq. + grid)solovev_n1Bisect (examples run directly, develop source):
du[i-1]line reverteddu[i-1]fixSo the one-line super-diagonal fix accounts for the shift; everything else in 0.4.19/0.4.20 is roundoff-level.
Solovev — the shift is real and v0.4.20 converges correctly. Fixed
ldpgrid, tightening the equilibriumetol:et[1] here is a near-cancellation (ep ≈ −10.41, ev ≈ +10.43), which amplifies a ~5e-4 relative change in ep by ~1000×. 0.4.18 keeps a ~30% offset even at etol = 1e-12: the wrong super-diagonal is a systematic bias, not something that converges away.
DIII-D-like — the 1.78 + 0.53i is the auto grid, not the fix. On fixed grids v0.4.20 is benign:
On the auto grid, 0.4.18 + only the fix and real 0.4.20 take the identical 822-step path, yet give 0.8006 vs 1.78 + 0.53i — a roundoff-level difference (e.g. the rewritten cubic eval kernel) producing an O(1) change with a large imaginary part. That's the same amplification family as #347, now in the forward-integrator energy itself, and it's what #349/#370/#525 describe: the fix changes the curvature measure, the auto grid gets coarser (598 →
320 surfaces, as noted above), and the forward solution on that grid is ill-conditioned. Downstream DIII-D quantities (resonant field, NTV torque ×1700, island widths) inherit it.Proposal: take the bump (it's a correctness fix), but sequence it with #525's knot-density floor so
diiid_n1doesn't re-pin the harness to a pathological value. Separately, a large Im(et[1])/Re(et[1]) seems worth a warning as a cheap detector for this failure mode.mgyoo86 commentedon Oct 16, 2026
Thanks @matt-pharr for the much more thorough investigation, this is very helpful.
I mostly looked at the base quantities, which is why I estimated at most a few percent, and I didn't realize how much the near-cancelled values matter here. Either way, the fix is clearly needed.
More generally, a few notes on sensitive systems like this, where ULP-level differences can get amplified:
a*b - cinto an FMA (0.1*3.0 - 0.3gives5.55e-17,fma(0.1, 3.0, -0.3)gives2.78e-17) or how SIMD reorders a reduction. For example, running the same DIII-D-like example with FastInterpolations ofv0.4.20on my machine (macOS arm64, Julia 1.13.2) gives0.80062rather than1.78 + 0.53i. In practice, identical behavior at the ULP level is not something we can expect.