Search code examples
pythonalgorithmnumerical-methodsdifferential-equations

How are 2nd order ODEs solved in python? With two variables in each of two second order differentials?


I have been given two second order ODEs and I've been asked to solve them with odeint in python.

These are the equations:

d^x(t)/dt^2 = 10dy(t)/dt + x(t) - (k + 1)(x(t))/z^3

d^2y(t)/dt^2 = - 10dy(t)/dt + y(t) - ((k+1)(y(t) + k))/z^3

where z = np.sqrt((y+k)^2+x^2))

I've been given initial variables (x, y, dxdt, dydt) I know their values but I'm not stuck on typing them so I won't put them here.

def function(init, time, k):
    xt = init[0]
    yt = init[1]
    z = np.sqrt((init[1]+k)^2+init[0]^2))
    dxdt = init[2]
    dydt = init[3]
    ddxddt = 10*dydt + xt - ((k+1)(xt))/z^3
    ddyddt = -10*dxdt + xt - ((k+1)(yt + k))/z^3
    return(dxdt, ddxddt, dydt, ddyddt)

init = [0.921, 0, 0, 3.0]
values = odeint(function, initial, time, args(k,))

After this I define initial, and define time, k, and put them in the odeint.

But I can see that I am doing something really wrong with my actual set up function. I don't understand how to split up 2nd order odes.


Solution

  • You have a few mistakes here.

    First: z^3 is not a power, it's the exclusive-or operation. In Python powers are done using the ** operator, so you'll want to be writing z**3.

    Second: You've misnamed the arguments of your function. Instead of:

    def function(init, time, k):
    

    you should have

    def function(state, time, k):
    

    since state evolves according to the derivatives the function returns. It will only have the initial values in the first timestep.

    Third: your state interpretation and state deltas are inconsistent. You write:

    xt   = init[0]
    yt   = init[1]
    dxdt = init[2]
    dydt = init[3]
    

    But later

    return dxdt, ddxddt, dydt, ddyddt
    

    This implies, among other things, that dydt=ddxddt. You should instead write:

    xt, yt, dxdt, dydt = state
    [....]
    return dxdt, dydt, ddxddt, ddyddt
    

    Note that you then have to ensure your initial conditions agree with the way you've ordered your state.

    A minimum working example of a correct implementation might look like this:

    import numpy as np
    import scipy.integrate
    import matplotlib.pyplot as plt
    
    def function(state, time, k):
      xt,yt,dxdt,dydt = state
      z               = np.sqrt((yt+k)**2+xt**2)
      ddxddt          = 10*dxdt + xt - ((k+1)*(xt    ))/z**3
      ddyddt          = -10*dydt + yt - ((k+1)*(yt + k))/z**3
      return dxdt, dydt, ddxddt, ddyddt
    
    init = [
      0.921, #x[0]
      0,     #y[0]
      0,     #x'[0]
      3.0    #y'[0]
    ]
    
    k = 1
    
    times  = np.linspace(0,1,1000)
    values = scipy.integrate.odeint(function, init, times, args=(k,), tfirst=False)
    
    plt.plot(values)
    plt.show()
    

    and gives this output:

    Output of odeint