5
votes

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

1
If I interchange the s and y parameters, the solver tries to access a float as if it were a list. This is causing the error message above. - imranal
But why do you need the odeint in general? The odeint uses lsoda solver which is avaliable in ode as well. - Igor Markelov
Sorry for the late reply. Originally I was using odeint, but then I wanted to add the possibility to use other solvers as well (besides Lsoda). The code shown in this example is only a fraction of the whole code. I guess I was being lazy by not wanting to change the code. But I also thought that the question seemed relevant, since it was odd that these two solvers had different variable maps. - imranal

1 Answers

6
votes

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.