Skip to content

DtLessThanMin when callable tstop includes tspan[1], differs from OrdinaryDiffEq behavior #679

@isaacsas

Description

@isaacsas

@ChrisRackauckas this is to just have an issue recording what we discussed by Slack.

When a callable tstop evaluates to include tspan[1], the SDE solver returns DtLessThanMin at t=0.0. The equivalent ODE setup handles this correctly.

The root cause is that StochasticDiffEq calls modify_dt_for_tstops! during __init (after evaluating callable tstops), which sets dt=0 when the first tstop equals the current time. OrdinaryDiffEqCore does not call modify_dt_for_tstops! at init time — it uses initialize_tstops with a strict tdir_t0 < tdir_t filter that excludes tstops at t0, avoiding this issue entirely.

MWE:

using OrdinaryDiffEqTsit5, StochasticDiffEq, SciMLBase

# Callable tstop that returns times including tspan[1]
tstops_callable = (p, tspan) -> [tspan[1], 1.0, 2.0]

# --- ODE: works fine ---
ode_f(u, p, t) = -0.1 * u
oprob = ODEProblem(ode_f, [1.0], (0.0, 3.0))
oprob = remake(oprob; kwargs = (; tstops = tstops_callable))
sol_ode = solve(oprob, Tsit5())
println("ODE: retcode = $(sol_ode.retcode)")  # Success

# --- SDE with t0 in tstops: fails ---
sde_f(u, p, t) = -0.1 * u
sde_g(u, p, t) = 0.01 * u
sprob = SDEProblem(sde_f, sde_g, [1.0], (0.0, 3.0))
sprob = remake(sprob; kwargs = (; tstops = tstops_callable))
sol_sde = solve(sprob, SOSRI())
println("SDE (with t0): retcode = $(sol_sde.retcode)")  # DtLessThanMin

# --- SDE without t0 in tstops: works fine ---
tstops_no_t0 = (p, tspan) -> [1.0, 2.0]
sprob2 = remake(sprob; kwargs = (; tstops = tstops_no_t0))
sol_sde2 = solve(sprob2, SOSRI())
println("SDE (without t0): retcode = $(sol_sde2.retcode)")  # Success

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions