I have a made a solver which can interchange between scipy.integrate.ode
and scipy.integrate.odeint
. Here is the code.
def f(y,s,C,u,v):
y0 = y[0] # u
y1 = y[1] # u'
y2 = y[2] # v
y3 = y[3] # v'
dy = np.zeros_like(y)
dy[0] = y1
dy[2] = y3
C = C.subs({u:y0,v:y2})
dy[1] = -C[0,0][0]*dy[0]**2\
-2*C[0,0][1]*dy[0]*dy[2]\
-C[0,1][1]*dy[2]**2
dy[3] = -C[1,0][0]*dy[0]**2\
-2*C[1,0][1]*dy[0]*dy[2]\
-C[1,1][1]*dy[2]**2
return dy
def solve(C,u0,s0,s1,ds,solver=None):
from sympy.abc import u,v
if solver == None: # use lsoda from scipy.integrate.odeint
s = np.arange(s0,s1+ds,ds)
print 'Running solver ...'
return sc.odeint(f,u0,s,args=(C,u,v))
else: # use any other solver from scipy.integrate.ode
r = sc.ode(f).set_integrator(solver) # vode,zvode,lsoda,dopri5,dop853
r.set_f_params(C,u,v)
r.set_initial_value(u0)
#t = []
y = []
print 'Running solver ...'
while r.successful() and r.t <= s1:
r.integrate(r.t + ds)
y.append(r.y)#; t.append(r.t)
return np.array(y)
The problem I experience is as following. If I decide to use the solver from scipy.integrate.odeint
then the parameters of f
have to be specified in the order as they are in the code. However, if I decide to use the solvers from scipy.integrate.ode
I have to change the order of the parameters of the function f(y,s,C,u,v)
to f(s,y,C,u,v)
, otherwise I get the error
TypeError: 'float' object has no attribute '__getitem__'
If I do this, then scipy.integrate.odeint
generates the same error for f
defined as f(s,y,C,u,v)
. How can I operate with a unified f
regardless of the order of the parameters ?
Edit :
To sum the problem up :
scipy.integrate.ode
solvers work if the function f is defined as f(s,y,C,u,v)
, and scipy.integrate.odeint
solver works if the function f is defined as f(y,s,C,u,v)
. Why is this occurring, and how I can I fix this?
Edit :
Scipy -- version 0.16.0
Why is this occurring, and how I can I fix this?
It is occurring because of an unfortunate API design decision made years ago. odeint
and the ode
class require different signatures for the system to be solved.
You can fix it by adding a wrapper that changes the order of the first two arguments when you use, say, the ode
class. For example, you could change this:
r = sc.ode(f).set_integrator(solver)
to
r = sc.ode(lambda t, x, *args: f(x, t, *args)).set_integrator(solver)
Update: In SciPy 1.1.0, the argument tfirst
was added to scipy.integrate.odeint
. The default, tfirst=False
, maintains the old behavior. With tfirst=True
, odeint
expects the first argument of
func
to be t
(i.e. the independent variable). By using tfirst=True
, the same func
can be used with ode
, odeint
and the newer solver_ivp
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With