I need an ODE-solver for a stiff problem similar to MATLAB ode15s.
For my problem I need to check how many steps (calculations) is needed for different initial values an
I'm seeing something similar; with the 'vode'
solver, changing methods between 'adams'
and 'bdf'
doesn't change the number of steps by very much. (By the way, there is no point in using order=15
; the maximum order of the 'bdf'
method of the 'vode'
solver is 5 (and the maximum order of the 'adams' solver is 12). If you leave the argument out, it should use the maximum by default.)
odeint
is a wrapper of LSODA. ode
also provides a wrapper of LSODA:
change 'vode'
to 'lsoda'
. Unfortunately the 'lsoda'
solver ignores
the step=True
argument of the integrate
method.
The 'lsoda'
solver does much better than 'vode'
with method='bdf'
.
You can get an upper bound on
the number of steps that were used by initializing tvals = []
,
and in func
, do tvals.append(t)
. When the solver completes, set
tvals = np.unique(tvals)
. The length of tvals
tells you the
number of time values at which your function was evaluated.
This is not exactly what you want, but it does show a huge difference
between using the 'lsoda'
solver and the 'vode'
solver with
method 'bdf'
. The number of steps used by the 'lsoda'
solver is
on the same order as you quoted for matlab in your comment. (I used mu=10000
, tf = 10
.)
Update: It turns out that, at least for a stiff problem, it make a huge difference for the 'vode'
solver if you provide a function to compute the Jacobian matrix.
The script below runs the 'vode'
solver with both methods, and it
runs the 'lsoda'
solver. In each case, it runs the solver with and without the Jacobian function. Here's the output it generates:
vode adams jac=None len(tvals) = 517992
vode adams jac=jac len(tvals) = 195
vode bdf jac=None len(tvals) = 516284
vode bdf jac=jac len(tvals) = 55
lsoda jac=None len(tvals) = 49
lsoda jac=jac len(tvals) = 49
The script:
from __future__ import print_function
import numpy as np
from scipy.integrate import ode
def func(t, u, mu):
tvals.append(t)
u1 = u[1]
u2 = mu*(1 - u[0]*u[0])*u[1] - u[0]
return np.array([u1, u2])
def jac(t, u, mu):
j = np.empty((2, 2))
j[0, 0] = 0.0
j[0, 1] = 1.0
j[1, 0] = -mu*2*u[0]*u[1] - 1
j[1, 1] = mu*(1 - u[0]*u[0])
return j
mu = 10000.0
u0 = [2, 0]
t0 = 0.0
tf = 10
for name, kwargs in [('vode', dict(method='adams')),
('vode', dict(method='bdf')),
('lsoda', {})]:
for j in [None, jac]:
solver = ode(func, jac=j)
solver.set_integrator(name, atol=1e-8, rtol=1e-6, **kwargs)
solver.set_f_params(mu)
solver.set_jac_params(mu)
solver.set_initial_value(u0, t0)
tvals = []
i = 0
while solver.successful() and solver.t < tf:
solver.integrate(tf, step=True)
i += 1
print("%-6s %-8s jac=%-5s " %
(name, kwargs.get('method', ''), j.func_name if j else None),
end='')
tvals = np.unique(tvals)
print("len(tvals) =", len(tvals))