Skip to content

Commit 8b16e6b

Browse files
authored
Adding backup solver Rodas5Pr if no states in model (#37)
* Use `Rodas5Pr` on a fixed grid if there are no state variables in the model.
1 parent 694e03e commit 8b16e6b

1 file changed

Lines changed: 14 additions & 17 deletions

File tree

src/simulate.jl

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -78,28 +78,25 @@ function run_simulate(ode_prob,
7878
# Redirect all library log output (including Symbolics/MTK warnings)
7979
# to the log file so they don't clutter stdout.
8080
sol = Logging.with_logger(logger) do
81-
# Overwrite saveat, always use dense output.
82-
# For stateless models (no unknowns) the adaptive solver takes no
83-
# internal steps and sol.t would be empty with saveat=[].
84-
# Supply explicit time points so observed variables can be evaluated.
81+
# Special handling for systems without states
8582
sys = ode_prob.f.sys
8683
n_unknowns = length(ModelingToolkit.unknowns(sys))
87-
88-
kwargs = if n_unknowns == 0
89-
# No unknowns at all (e.g. BusUsage):
90-
# Supply explicit time points so observed variables can be evaluated.
84+
eqs = ModelingToolkit.equations(sys)
85+
n_states = count(ModelingToolkitBase.isdiffeq, eqs)
86+
kwargs, active_solver = if n_states == 0
87+
# No state variables (e.g. BusUsage, Modelica.Electrical.Analog.Examples.OpAmps.Subtracter):
88+
# Use Rodas5Pr on a fixed grid so observed variables can be evaluated at each step.
89+
dt_s = (ode_prob.tspan[end] - ode_prob.tspan[1]) / (settings.saveat_n - 1)
9190
saveat_s = collect(range(ode_prob.tspan[1], ode_prob.tspan[end]; length = settings.saveat_n))
92-
(saveat = saveat_s, dense = true)
91+
(saveat = saveat_s, dt = dt_s, adaptive = false, dense = false),
92+
DifferentialEquations.Rodas5Pr()
9393
else
94-
(saveat = Float64[], dense = true)
94+
saveat_s = Float64[]
95+
(saveat = saveat_s, dense = true), solver
9596
end
9697

97-
# Log solver settings — init returns NullODEIntegrator (no .opts)
98-
# when the problem has no unknowns (u::Nothing), so only inspect
99-
# opts when a real integrator is returned.
100-
# Use our own `saveat` vector for the log: integ.opts.saveat is a
101-
# BinaryHeap which does not support iterate/minimum/maximum.
102-
integ = DifferentialEquations.init(ode_prob, solver; kwargs...)
98+
# Log solver settings
99+
integ = DifferentialEquations.init(ode_prob, active_solver; kwargs...)
103100
saveat_s = kwargs.saveat
104101
solver_settings_string = if hasproperty(integ, :opts)
105102
sv_str = isempty(saveat_s) ? "[]" : "$(length(saveat_s)) points in [$(first(saveat_s)), $(last(saveat_s))]"
@@ -119,7 +116,7 @@ function run_simulate(ode_prob,
119116
end
120117

121118
# Solve
122-
DifferentialEquations.solve(ode_prob, solver; kwargs...)
119+
DifferentialEquations.solve(ode_prob, active_solver; kwargs...)
123120
end
124121
sim_time = time() - t0
125122
if sol.retcode == DifferentialEquations.ReturnCode.Success

0 commit comments

Comments
 (0)