test: cap dt in the marginal continuous-callback crossing-time tests - #4866
Merged
AayushSabharwal merged 1 commit intoAug 4, 2026
Conversation
The three `solve(prob, Tsit5())` calls in "Additional SymbolicContinuousCallback options" assert event times to 1.0e-4 while integrating at the default `reltol = 1.0e-3`. For `c2 = cos(3t)` that leaves no room: the numerical trajectory carries ~2e-4 of error, and since `dc2/dt = ±3` at the crossings the recorded event time inherits ~7e-5 of it -- a 1.4x margin against the assertion. The rootfind itself is exact (the condition value at the recorded time is ~1e-16), so what these tests actually measured was default-tolerance quadrature error, not event handling. OrdinaryDiffEqCore 4.12.0 (SciML/OrdinaryDiffEq.jl#3720) reinitializes the step-size controller after any continuous callback with `maybe_discontinuity = true`, which is correct -- a PI controller's history is meaningless across a discontinuity -- but it changes the step sequence, and the c2 error grew to ~8e-4, pushing the crossing times to 2.6e-4 and over the threshold. Pass `dtmax = 0.01` as the other four solves in the same testset already do. Crossing times land within ~1e-12, so the assertions now test the rootfind rather than the controller. Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com> Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TNqmRywBbj51czPQgoiEC7
AayushSabharwal
marked this pull request as ready for review
August 4, 2026 15:19
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.
Fixes the three
Additional SymbolicContinuousCallback optionsfailures currently onmaster(e.g. https://github.com/SciML/ModelingToolkit.jl/actions/runs/30746352714/job/91493644801, reproducing onjulia 1,ltsandpre):Why these tests were failing
They were marginal by construction. All three assert event times to
1.0e-4while integratingsolve(prob, Tsit5())at the defaultreltol = 1.0e-3. Forc2 = cos(3t)that is not enough headroom: the numerical trajectory carries ~2e-4 of error, and sincedc2/dt = ±3at the crossings the recorded event time inheritserror/3≈ 7.3e-5 — a 1.4× margin against the assertion.The rootfind was never the problem. The condition value recorded at the event time is ~1e-16, i.e. the interpolant's zero is found exactly; what these assertions were actually measuring was default-tolerance quadrature error, not event handling.
What tipped them over
SciML/OrdinaryDiffEq.jl#3720, released in OrdinaryDiffEqCore 4.12.0, reinitializes the step-size controller after any continuous callback with
maybe_discontinuity = true:That is the right thing to do — a PI controller's error history assumes the dynamics before and after the step are the same, which is exactly what a discontinuity breaks — but it changes the accepted step sequence. On this problem the
c2error grew from ~2.2e-4 to ~7.9e-4, so the crossing times moved from 7.3e-5 to 2.6e-4 and crossed the threshold.Bisected on a minimal, MTK-free reproducer (plain
ODEProblem+VectorContinuousCallbackwith the same settings MTK generates):c2event-time error7.3234e-5✅2.6350432675e-4❌ (bit-identical to CI)maybe_discontinuity = false7.3234e-5✅The change
Pass
dtmax = 0.01, exactly as the other foursolvecalls in the same testset already do — those blocks land within ~1e-12 and were completely unaffected by the upstream change. The1.0e-4assertions are left alone; withdtmaxcapped the crossing times come out at ~1e-12, so they now test the rootfind instead of the controller.Testing
Ran the full file locally against this branch with
ModelingToolkitloaded (mirroring the failingInterfaceIjob), OrdinaryDiffEqCore 4.12.0 / DiffEqBase 7.10.0 / SciMLBase 3.41.0, Julia 1.12.6:31 testsets, no failures, no errors. On the same environment before the change, the three assertions fail with the exact CI values. Runic is clean.