pythonmatlabodeode45

Python ODE45 IndexError: list assignment out of range


I am trying to duplicate an ODE script I have running in Matlab to Python. Here is the Matlab script:

t0 = 0;
tfinal = 25;
q1 = 1;
q2 = 1;
q1dot = 0;
q2dot = 0;

% ODE variables
times = [t0 tfinal];
stateVars=[q1 q1dot q2 q2dot];

% ODE45
options = odeset('reltol',1E-12,'abstol',1E-12);
[t,xx]=ode45('hw1SS',times,stateVars,options);

hw1SS Function:

function [xdot] = hw1SS(t,x)
    % Given Parameters
    m = 1;
    K = 49;
    k = 1;
    C = 2;
    c = 0.5;
    k = 1;

    % Derivative variables solved for in part b)
    xdot = zeros(4,1);
    xdot(1) = x(2);
    xdot(2) = (1 / m) * (-c * x(2) - k * x(1) + C * (x(4) - x(2)) + K * (x(3) - x(1)));
    xdot(3) = x(4);
    xdot(4) = (1 / m) * (-c * x(4) - k * x(3) + C * (x(2) - x(4)) + K * (x(1) - x(3)));
end

The Matlab code works perfectly fine. When I try to replicate this in Python, I receive the following error: IndexError: list assignment index out of range

Here is my attempted solution in Python:

import numpy as np
from scipy.integrate import odeint
import matplotlib.pyplot as plt

def model(x,t):
    m, K, C, c, k = 1, 49, 2, 0.5, 1
    xdot = [[],[]]
    xdot[0] = x[1]
    xdot[1]  = (1/m)*(-c*x[1]-k*x[0]+C*(x[3]-x[1])+k*(x[2]-x[0]))
    xdot[2] = x[3]
    xdot[3]  = (1/m)*(-c*x[3]-k*x[2]+C*(x[1]-x[3])+k*(x[0]-x[2]))
    return xdot
q1, q1dot, q2, q2dot = 1, 0, 1, 0
t0, tf = 0, 25
t = np.linspace(t0, tf, 100)
y0 = [q1, q1dot, q2, q2dot]
y = odeint(model, t, y0)

plt.plot(t, y)
plt.show()

Error message:

---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
<ipython-input-61-1038b9266c62> in <module>()
      3 y0 = [q1, q1dot, q2, q2dot]
      4 
----> 5 y = odeint(model, t, y0)
      6 
      7 plt.plot(t, y)

~\Anaconda3\lib\site-packages\scipy\integrate\odepack.py in odeint(func, y0, t, args, Dfun, col_deriv, full_output, ml, mu, rtol, atol, tcrit, h0, hmax, hmin, ixpr, mxstep, mxhnil, mxordn, mxords, printmessg, tfirst)
    231                              full_output, rtol, atol, tcrit, h0, hmax, hmin,
    232                              ixpr, mxstep, mxhnil, mxordn, mxords,
--> 233                              int(bool(tfirst)))
    234     if output[-1] < 0:
    235         warning_msg = _msgs[output[-1]] + " Run with full_output = 1 to get quantitative information."

<ipython-input-53-3e8f74e5a043> in model(x, t)
      5     xdot[0] = x[1]
      6     xdot[1]  = (1/m)*(-c*x[1]-k*x[0]+C*(x[3]-x[1])+k*(x[2]-x[0]))
----> 7     xdot[2] = x[3]
      8     xdot[3]  = (1/m)*(-c*x[3]-k*x[2]+C*(x[1]-x[3])+k*(x[0]-x[2]))
      9 

IndexError: list assignment index out of range

Solution

  • First the function ode45 is different to the function odeint, in the question of the position of the arguments:

    scipy.integrate.odeint(func, y0, t, args=(), ...)
    

    As we see that the second argument is the initial value, not the time range.

    Another mistake is:

    xdot = [[],[]]
    

    That is a list list, what you should do is create a list of 4 elements with any value that will be overwritten (typically those values are 0s)

    import numpy as np
    from scipy.integrate import odeint
    import matplotlib.pyplot as plt
    
    
    def model(x, t):
        m, K, C, c, k = 1, 49, 2, 0.5, 1
        xdot = [0, 0, 0, 0]
        xdot[0] = x[1]
        xdot[1]  = (1/m)*(-c*x[1]-k*x[0]+C*(x[3]-x[1])+k*(x[2]-x[0]))
        xdot[2] = x[3]
        xdot[3]  = (1/m)*(-c*x[3]-k*x[2]+C*(x[1]-x[3])+k*(x[0]-x[2]))
        return xdot
    
    q1, q1dot, q2, q2dot = 1, 0, 1, 0
    t0, tf = 0, 25
    t = np.linspace(t0, tf, 100)
    y0 = [q1, q1dot, q2, q2dot]
    y = odeint(model, y0, t)
    plt.plot(t, y)
    plt.show()
    

    enter image description here