Skip to content

Commit 429c570

Browse files
committed
improve dtmin and unstable detection
1 parent 2df59ce commit 429c570

File tree

1 file changed

+41
-26
lines changed

1 file changed

+41
-26
lines changed

src/integrator_interface.jl

Lines changed: 41 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -584,53 +584,68 @@ function check_error(integrator::DEIntegrator)
584584
if integrator.sol.retcode (ReturnCode.Success, ReturnCode.Default)
585585
return integrator.sol.retcode
586586
end
587+
opts = integrator.opts
588+
verbose = opts.verbose
587589
# This implementation is intended to be used for ODEIntegrator and
588590
# SDEIntegrator.
589591
if isnan(integrator.dt)
590-
if integrator.opts.verbose
592+
if verbose
591593
@warn("NaN dt detected. Likely a NaN value in the state, parameters, or derivative value caused this outcome.")
592594
end
593595
return ReturnCode.DtNaN
594596
end
595-
if integrator.iter > integrator.opts.maxiters
596-
if integrator.opts.verbose
597+
if integrator.iter > opts.maxiters
598+
if verbose
597599
@warn("Interrupted. Larger maxiters is needed. If you are using an integrator for non-stiff ODEs or an automatic switching algorithm (the default), you may want to consider using a method for stiff equations. See the solver pages for more details (e.g. https://docs.sciml.ai/DiffEqDocs/stable/solvers/ode_solve/#Stiff-Problems).")
598600
end
599601
return ReturnCode.MaxIters
600602
end
601603

602604
# The last part:
603-
# If you are close to the end, don't exit: let the user hit the end!
604-
# However, if we try that and the step fails, exit instead of infinite loop
605-
if !integrator.opts.force_dtmin && integrator.opts.adaptive &&
606-
abs(integrator.dt) <= abs(integrator.opts.dtmin) &&
607-
(((hasproperty(integrator, :opts) && hasproperty(integrator.opts, :tstops)) ?
608-
integrator.t + integrator.dt < integrator.tdir * first(integrator.opts.tstops) :
609-
true) || (hasproperty(integrator, :accept_step) && !integrator.accept_step))
610-
if integrator.opts.verbose
611-
if isdefined(integrator, :EEst)
612-
EEst = ", and step error estimate = $(integrator.EEst)"
613-
else
614-
EEst = ""
605+
# Bail out if we take a step with dt less than the minimum value (which may be time dependent)
606+
# except if we are sucessfully taking such a small timestep is to hit a tstop exactly
607+
# We also exit if the ODE is unstable (by default this is the same as nonfinite u)
608+
# but only consider the ODE unstable if the error is somewhat controlled
609+
# (to prevent from bailing out as unstable when we just took way too big a step)
610+
if !opts.force_dtmin && opts.adaptive
611+
if abs(integrator.dt) <= abs(opts.dtmin) &&
612+
(((hasproperty(integrator, :opts) && hasproperty(opts, :tstops)) ?
613+
integrator.t + integrator.dt < integrator.tdir * first(opts.tstops) :
614+
true) || (hasproperty(integrator, :accept_step) && !integrator.accept_step))
615+
if verbose
616+
if isdefined(integrator, :EEst)
617+
EEst = ", and step error estimate = $(integrator.EEst)"
618+
else
619+
EEst = ""
620+
end
621+
@warn("dt($(integrator.dt)) <= dtmin($(opts.dtmin)) at t=$(integrator.t)$EEst. Aborting. There is either an error in your model specification or the true solution is unstable.")
622+
end
623+
return ReturnCode.DtLessThanMin
624+
elseif abs(integrator.dt) <= abs(eps(integrator.t)) &&
625+
hasproperty(integrator, :accept_step) && !integrator.accept_step
626+
if verbose
627+
if isdefined(integrator, :EEst)
628+
EEst = ", and step error estimate = $(integrator.EEst)"
629+
else
630+
EEst = ""
631+
end
632+
@warn("dt($(integrator.dt)) <= eps(t)($(integrator.t)) $EEst. Aborting. There is either an error in your model specification or the true solution is unstable (or the true solution can not be represented in the precision of $(eltype(integrator.u))).")
615633
end
616-
@warn("dt($(integrator.dt)) <= dtmin($(integrator.opts.dtmin)) at t=$(integrator.t)$EEst. Aborting. There is either an error in your model specification or the true solution is unstable.")
634+
return ReturnCode.Unstable
617635
end
618-
return ReturnCode.DtLessThanMin
619-
end
620-
if integrator.opts.unstable_check(integrator.dt, integrator.u, integrator.p,
621-
integrator.t)
622-
bigtol = max(integrator.opts.reltol, integrator.opts.abstol)
623-
# only declare instability if the dt is very small
624-
# or we have at least one digit of accuracy in the solution
625-
if integrator.dt<eps(integrator.t) || isdefined(integrator, :EEst) && integrator.EEst * bigtol < .1
626-
if integrator.opts.verbose
636+
end
637+
bigtol = max(opts.reltol, opts.abstol)
638+
if isdefined(integrator, :EEst) && integrator.EEst * bigtol < .1
639+
if opts.unstable_check(integrator.dt, integrator.u, integrator.p,
640+
integrator.t)
641+
if verbose
627642
@warn("Instability detected. Aborting")
628643
end
629644
return ReturnCode.Unstable
630645
end
631646
end
632647
if last_step_failed(integrator)
633-
if integrator.opts.verbose
648+
if verbose
634649
@warn("Newton steps could not converge and algorithm is not adaptive. Use a lower dt.")
635650
end
636651
return ReturnCode.ConvergenceFailure

0 commit comments

Comments
 (0)