Search code examples
pythondifferential-equationsscientific-computing

Runge-Kutta 4 for solving systems of ODEs Python


I wrote code for Runge-Kutta 4 for solving system of ODEs.
It works fine for 1-D ODE but when I try to solve x'' + kx = 0 I have a problem trying to define a vectorial function:

Let u1 = x and u2 = x' = u1', then the system looks like:

u1' = u2
u2' = -k*u1

If u = (u1,u2) and f(u, t) = (u2, -k*u1), then we need to solve:

u' = f(u, t)
def f(u,t, omega=2):
    u, v = u
    return np.asarray([v, -omega**2*u])

My entire code is:

import numpy as np

def ode_RK4(f, X_0, dt, T):    
    N_t = int(round(T/dt))
    #  Create an array for the functions ui 
    u = np.zeros((len(X_0),N_t+1)) # Array u[j,:] corresponds to the j-solution
    t = np.linspace(0, N_t*dt, N_t + 1)
    # Initial conditions
    for j in range(len(X_0)):
        u[j,0] = X_0[j]
    # RK4
    for j in range(len(X_0)):
        for n in range(N_t):
            u1 = f(u[j,n] + 0.5*dt* f(u[j,n], t[n])[j], t[n] + 0.5*dt)[j]
            u2 = f(u[j,n] + 0.5*dt*u1, t[n] + 0.5*dt)[j]
            u3 = f(u[j,n] + dt*u2, t[n] + dt)[j]
            u[j, n+1] = u[j,n] + (1/6)*dt*( f(u[j,n], t[n])[j] + 2*u1 + 2*u2 + u3)
    
    return u, t

def demo_exp():
    import matplotlib.pyplot as plt
    
    def f(u,t):
        return np.asarray([u])

    u, t = ode_RK4(f, [1] , 0.1, 1.5)
    
    plt.plot(t, u[0,:],"b*", t, np.exp(t), "r-")
    plt.show()
    
def demo_osci():
    import matplotlib.pyplot as plt
    
    def f(u,t, omega=2):
        # u, v = u Here I've got a problem
        return np.asarray([v, -omega**2*u])
    
    u, t = ode_RK4(f, [2,0], 0.1, 2)
    
    for i in [1]:
        plt.plot(t, u[i,:], "b*")
    plt.show()
    

In advance, thank you.


Solution

  • You are on the right path, but when applying time-integration methods such as RK to vector valued ODEs, one essentially does the exact same thing as in the scalar case, just with vectors.

    Thus, you skip the for j in range(len(X_0)) loop and associated indexation and you make sure that you pass initial values as vectors (numpy arrays).

    Also cleaned up the indexation for t a little and stored the solution in a list.

    import numpy as np
    
    def ode_RK4(f, X_0, dt, T):    
        N_t = int(round(T/dt))
        # Initial conditions
        usol = [X_0]
        u = np.copy(X_0)
        
        tt = np.linspace(0, N_t*dt, N_t + 1)
        # RK4
        for t in tt[:-1]:
            u1 = f(u + 0.5*dt* f(u, t), t + 0.5*dt)
            u2 = f(u + 0.5*dt*u1, t + 0.5*dt)
            u3 = f(u + dt*u2, t + dt)
            u = u + (1/6)*dt*( f(u, t) + 2*u1 + 2*u2 + u3)
            usol.append(u)
        return usol, tt
    
    def demo_exp():
        import matplotlib.pyplot as plt
        
        def f(u,t):
            return np.asarray([u])
    
        u, t = ode_RK4(f, np.array([1]) , 0.1, 1.5)
        
        plt.plot(t, u, "b*", t, np.exp(t), "r-")
        plt.show()
        
    def demo_osci():
        import matplotlib.pyplot as plt
        
        def f(u,t, omega=2):
            u, v = u 
            return np.asarray([v, -omega**2*u])
        
        u, t = ode_RK4(f, np.array([2,0]), 0.1, 2)
        
        u1 = [a[0] for a in u]
        
        for i in [1]:
            plt.plot(t, u1, "b*")
        plt.show()