Skip to content

Commit 5b172dc

Browse files
authored
Merge pull request #693 from oscardssmith/os/improve-dtmin-unstable-check
improve dtmin and unstable detection
2 parents f2f2358 + 761a8c7 commit 5b172dc

File tree

1 file changed

+39
-28
lines changed

1 file changed

+39
-28
lines changed

src/integrator_interface.jl

Lines changed: 39 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -584,53 +584,64 @@ 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 successfully taking such a small timestep is to hit a tstop exactly
607+
# We also exit if the ODE is unstable according to a user chosen callback
608+
# but only if we accepted the step to prevent from bailing out as unstable
609+
# when we just took way too big a step)
610+
step_accepted = !hasproperty(integrator, :accept_step) || integrator.accept_step
611+
if !opts.force_dtmin && opts.adaptive
612+
if abs(integrator.dt) <= abs(opts.dtmin) &&
613+
(!step_accepted || (hasproperty(opts, :tstops) ?
614+
integrator.t + integrator.dt < integrator.tdir * first(opts.tstops) :
615+
true))
616+
if verbose
617+
if isdefined(integrator, :EEst)
618+
EEst = ", and step error estimate = $(integrator.EEst)"
619+
else
620+
EEst = ""
621+
end
622+
@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.")
615623
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.")
617-
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
627-
@warn("Instability detected. Aborting")
624+
return ReturnCode.DtLessThanMin
625+
elseif !step_accepted && integrator.t isa AbstractFloat && abs(integrator.dt) <= abs(eps(integrator.t))
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))).")
628633
end
629634
return ReturnCode.Unstable
630635
end
631636
end
637+
if step_accepted && opts.unstable_check(integrator.dt, integrator.u, integrator.p, integrator.t)
638+
if verbose
639+
@warn("Instability detected. Aborting")
640+
end
641+
return ReturnCode.Unstable
642+
end
632643
if last_step_failed(integrator)
633-
if integrator.opts.verbose
644+
if verbose
634645
@warn("Newton steps could not converge and algorithm is not adaptive. Use a lower dt.")
635646
end
636647
return ReturnCode.ConvergenceFailure

0 commit comments

Comments
 (0)