Ordinary Differential Equations#


import numpy as np
import matplotlib.pyplot as plt

Solving ODEs using Euler’s Method#

\[ \large \begin{align*} & & \frac{\partial y(x)}{\partial x} &= F(x, y(x)) \end{align*} \]
\[\begin{split} \begin{align*} \text{discretised:}& & \frac{\partial y(x_i)}{\partial x_i} &= F(x_i, y(x_i)) \\ \text{approximated:}& & \frac{y(x_{i+1}) - y(x_i)}{\Delta x} &\approx F(x_i, y(x_i)) \\ \end{align*} \end{split}\]
\[ \large \begin{align*} \Rightarrow y(x_{i+1}) &\approx F(x_i, y(x_i)) \, \Delta x + y(x_i) \end{align*} \]

1. Example: \(y'(x) = y\)#

\[\begin{split} \begin{align*} \frac{\partial y(x)}{\partial x} &= y(x) \quad \Rightarrow \quad y(x) = e^x + b \\ y(0) &= 1 \quad \Rightarrow \quad y(x) = e^x \end{align*} \end{split}\]
N = 100
x = np.linspace(0, 5, N)
y = np.empty(N)
dx = x[1] - x[0]

y[0] = 1    # initial value

def F(x, y):
    return y
    
# integrate "by hand"
for i in range(N-1):
    
    y[i+1] = y[i] + dx * F(x[i], y[i])

# plot it
plt.figure(1)
plt.plot(x, y, 'o', label='numerical')
plt.plot(x, np.exp(x), "b--", lw=3, label='analytic')
plt.xlabel('$x$')
plt.ylabel('$y(x)$')
plt.legend()

plt.show()
../_images/86c9078e9ca9cf82cacb81502fb73668b6ef89a39ba5d41a28b3ac1d3fd3b87d.png

2. Example: \(y'(x) = x\)#

\[\begin{split} \begin{align*} \frac{\partial y(x)}{\partial x} &= x \quad \Leftrightarrow y(x) = \int x \, dx \quad = \frac{1}{2} x^2 + b \\ y(0) &= 1 \quad \Rightarrow \quad y(x) = \frac{1}{2} x^2 + 1 \end{align*} \end{split}\]
N = 100
x = np.linspace(0, 5, N)
y = np.empty(N)
dx = x[1] - x[0]

y[0] = 1    # initial value

def F(x, y):
    return x
    
for i in range(N-1):
    y[i+1] = y[i] + dx * F(x[i], y[i])

plt.figure(1)
plt.plot(x, y, 'o', lw=3, label='numerical')
plt.plot(x, 0.5 * x**2.0 + 1, "r--", lw=3, label='analytic')
plt.xlabel('$x$')
plt.ylabel('$y(x)$')
plt.legend()

plt.show()
../_images/60b56c398a857fdf1ae150fb2ce32b0c60c57208d721022c9cb9029638676e21.png

3. Example: \(y'(x) = sin(x)\)#

\[\begin{split} \begin{align*} \frac{\partial y(x)}{\partial x} &= sin(x) &\quad \Rightarrow \quad y(x) &= -cos(x) + b \\ y(0) &= 1 &\quad \Rightarrow \quad y(x) &= -cos(x) + 2 \end{align*} \end{split}\]
N = 100
x = np.linspace(0, 4*np.pi, N)
y = np.empty(N)
dx = x[1] - x[0]

y[0] = 1    # initial value

def F(x, y):
    return np.sin(x)
    
for i in range(N-1):
    y[i+1] = y[i] + dx * F(x[i], y[i])

plt.figure(1)
plt.plot(x, y, 'o', lw=3, label='numerical')
plt.plot(x, -np.cos(x) + 2, "r--", lw=3, label='analytic')
plt.legend()
plt.show()
../_images/327fe030af913731801d5b541da76fe6f7af145465499fb3236f53e600086a3a.png

4. Example: Pendulum#

Setup

Second-oder ODE for the angle \(\varphi\):

\[\large \frac{\partial^2 \varphi}{\partial t^2} = - \frac{g}{l} \sin{\varphi}\]

Coupled system of first-order ODEs:

\[\begin{split} \large \begin{pmatrix} \dot{\varphi} \\ \dot{\omega} \end{pmatrix} = \begin{pmatrix} \omega \\ - \frac{g}{l} \sin{\varphi} \end{pmatrix} \end{split}\]
N = 10000
g = 9.81    # m / s^2
l = 10.0    # m

time  = np.linspace(0, 50, N)
phi   = np.zeros(N)
omega = np.zeros(N)
dt    = time[1] - time[0]

phi[0]   = 0.1    # initial values
omega[0] = 0.0

def F(time, phi, omega):
    return [omega, 
            -(g/l) * np.sin(phi)]

for i in range(N-1):
    
    phi[i+1]   = phi[i]   + dt * F(time[i], 
                                   phi[i], 
                                   omega[i])[0]
    
    omega[i+1] = omega[i] + dt * F(time[i], 
                                   phi[i], 
                                   omega[i])[1]

plt.figure(1)
plt.plot(time, phi)
plt.xlabel('$t$')
plt.ylabel('$\\varphi(t)$')
plt.show()
../_images/5b71e4011059ad6a2ea7f0e65f0ba723546eec163da22a181c2ea9d1a3dec99d.png

Solving ODEs using Predictor-Corrector Method#

Setup

N = 1000
g = 9.8
l = 10.0

time  = np.linspace(0, 50, N)
phi   = np.zeros(N)
omega = np.zeros(N)
dt    = time[1] - time[0]

phi[0]   = 0.1    # initial values
omega[0] = 0.0

def F(time, phi, omega):
    return [omega, 
            -(g/l) * np.sin(phi)]

for i in range(N-1):
    
    phi_mid   = phi[i]   + 0.5*dt * F(time[i], 
                                      phi[i], 
                                      omega[i])[0]
    
    omega_mid = omega[i] + 0.5*dt * F(time[i], 
                                      phi[i], 
                                      omega[i])[1]

    phi[i+1]   = phi[i]   + dt * F(time[i], 
                                   phi_mid, 
                                   omega_mid)[0]
    
    omega[i+1] = omega[i] + dt * F(time[i], 
                                   phi_mid, 
                                   omega_mid)[1]

plt.figure(1)
plt.plot(time, phi)
plt.xlabel('$t$')
plt.ylabel('$\\varphi(t)$')
plt.show()
../_images/146af4363705e4cf3b3298ae9c0acd360dd73be0562b923d1acaecd5eb2a3415.png

Solving ODEs using SciPy’s Runge-Kutta Method integrate.solve_ivp()#

1. Example: Pendulum – done right#

\[\begin{split} \large \begin{align*} \begin{pmatrix} \dot{\varphi} \\ \dot{\omega} \end{pmatrix} & = \begin{pmatrix} \omega \\ - \frac{g}{l} \sin{\varphi} \end{pmatrix} \end{align*} \end{split}\]

needs to be reacast into the following form:

\[\begin{split} \large \begin{align*} \frac{\partial \vec{y}(t)}{\partial t} &= \vec{F}(t, \vec{y}(t))\\ \vec{y}(t) &= \begin{pmatrix} y_0(t) \\ y_1(t) \end{pmatrix} = \begin{pmatrix} \varphi(t) \\ \omega(t) \end{pmatrix} \\ \vec{F}_\text{Pendulum}(t, \vec{y}) &= \begin{pmatrix} y_1(t) \\ -\frac{g}{l} \sin{y_0(t)} \end{pmatrix} \end{align*} \end{split}\]
import scipy.integrate as integrate
N = 1000
g = 9.8
l = 10.0

tMin = 0
tMax = 50
time = np.linspace(tMin, tMax, N)

y_init = [0, 0.1]    # initial values

def FPendulum(t, y):
    F    = np.zeros(2)
    F[0] = y[1]
    F[1] = -(g/l) * np.sin(y[0])
    return F

sol = integrate.solve_ivp(FPendulum, 
                          (tMin, tMax), 
                          y_init, t_eval=time)

print(sol)
  message: The solver successfully reached the end of the integration interval.
  success: True
   status: 0
        t: [ 0.000e+00  5.005e-02 ...  4.995e+01  5.000e+01]
        y: [[ 0.000e+00  5.003e-03 ... -7.478e-02 -7.133e-02]
            [ 1.000e-01  9.988e-02 ...  6.715e-02  7.074e-02]]
      sol: None
 t_events: None
 y_events: None
     nfev: 326
     njev: 0
      nlu: 0
plt.figure(1)

plt.plot(sol.t, sol.y[0])
plt.plot(sol.t, sol.y[1])

plt.xlabel('$t$')
plt.ylabel('$\\varphi(t)$')
plt.show()
../_images/90cb5d4ae9334a7a5645a9f18f29473952e4a93845bb127c21a2360193673741.png

2. Example: \(y'(x) = x\)#

\[\begin{split} \begin{align*} \frac{\partial y(x)}{\partial x} &= x \quad \Leftrightarrow y(x) = \int x \, dx \quad = \frac{1}{2} x^2 + b \\ y(0) &= 1 \quad \Rightarrow \quad y(x) = \frac{1}{2} x^2 + 1 \end{align*} \end{split}\]
N = 10
x = np.linspace(0, 5, N)
y = np.empty(N)
dx = x[1] - x[0]

y0 = [1]    # initial value

def F(x, y):
    return x

sol = integrate.solve_ivp(F, (0, 5), y0, t_eval=x)

plt.figure(1)
plt.plot(x, sol.y[0, :], 'o', lw=3)
plt.plot(x, 0.5 * x**2.0 + 1, "r--", lw=3)
plt.xlabel('$x$')
plt.ylabel('$y(x)$')

plt.show()
../_images/7d4401b23d3c9ca99c78a28a2a5cd100024b6ab9d0af573b88d7b26d07a02d53.png

3. Example: Projectile motion#

Setup

$$\large \begin{align*} \begin{pmatrix} x(t) \\ y(t) \end{pmatrix} &= \begin{pmatrix} v_{0x} t \\ v_{0y} t - \frac{1}{2} g t^2 \end{pmatrix}\\ \Rightarrow \begin{pmatrix} \dot{x}(t) \\ \dot{y}(t) \end{pmatrix} &= \begin{pmatrix} v_{0x} \\ v_{0y} - g t\end{pmatrix} = \vec{F}(t, \vec{y})\\ \text{with } v_{0x} &= v_0 \cos(\alpha) \\ v_{0y} &= v_0 \sin(\alpha) \end{align*} $$
g     = 9.8
v0    = 6.0
alpha = 45.0 / 180.0 * np.pi

N    = 200
tMin = 0
tMax = 0.75
time = np.linspace(tMin, tMax, N)

y0 = [0, 0]    # initial values

def FProjectile(t, y):
    
    F    = np.zeros(2)
    F[0] =  v0 * np.cos(alpha)
    F[1] =  v0 * np.sin(alpha) - g*t
    
    return F

sol = integrate.solve_ivp(FProjectile, 
                          (tMin, tMax), 
                          y0, t_eval=time)

plt.figure(1)

plt.plot(sol.y[0, :], sol.y[1, :])


#plt.ylim([0, 0.21])
#plt.xlim([0, 0.15])
plt.show()
../_images/a2980bf49a185c83354f6715d1cefbd49025ade5793e2cf40dfd814bcaec9a3a.png

3. Example: Multidimensional#

\[\begin{split} \begin{align*} \frac{\partial \vec{y}(t)}{\partial t} = \vec{F}(t, \vec{y}) = \begin{pmatrix} y_1 \\ y_2 \\ y_0^2 \end{pmatrix} \end{align*} \end{split}\]
def F(t, y):
    myF    = np.zeros(3)
    myF[0] = y[1]
    myF[1] = y[2]
    myF[2] = y[0]**2.0
    return myF

N    = 200
tMin = 0
tMax = 3.0
time = np.linspace(tMin, tMax, N)

y0 = [-1, 0, 0]   # initial values

sol = integrate.solve_ivp(F, (tMin, tMax), y0, t_eval=time)

plt.figure(1)
plt.plot(sol.t, sol.y[0,:], label='$y_0(t)$')
plt.plot(sol.t, sol.y[1,:], label='$y_1(t)$')
plt.plot(sol.t, sol.y[2,:], label='$y_2(t)$')
plt.xlabel('$t$')
plt.legend()
plt.show()
../_images/a000ddc94341455cb9a92af6de0a086257084d87e9fb388f3b2cfec2c10c49c9.png

4. Example: Circle / Spiral#

\[\begin{split} \begin{pmatrix} y \\ x \end{pmatrix} = \begin{pmatrix} \sin{\varphi} \\ \cos{\varphi} \end{pmatrix} \Leftrightarrow \begin{pmatrix} \dot{x} \\ \dot{y} \end{pmatrix} = \begin{pmatrix} -y \\ x \end{pmatrix} \end{split}\]
def F(phi, y):
    myF    = np.zeros(2)
    myF[0] =  -y[1] - 0.1 * y[0]
    myF[1] = y[0] - 0.1 * y[1]
    return myF

N      = 2000
phiMin = 0.01
phiMax = 48.0 * np.pi
phi    = np.linspace(phiMin, phiMax, N)

y0 = [1, 1]     # initial values

sol = integrate.solve_ivp(F, (phiMin, phiMax), y0, t_eval=phi)

plt.figure(1)
plt.plot(sol.y[0,:], sol.y[1,:])
plt.axis('equal')
plt.show()
../_images/ea8b669cb2ee414aa38db9ab95a93d2b69bbce3b64feef053122a87c700a720b.png

5. Example: Lotka–Volterra equations / predator–prey equations#

\[\begin{split} \begin{pmatrix} \dot{r} \\ \dot{f} \end{pmatrix} = \begin{pmatrix} \alpha r - \beta r f \\ \delta r f - \gamma f\end{pmatrix} \begin{matrix} \text{rabbits} \\ \text{foxes} \end{matrix} \end{split}\]
a = 0.5
b = 0.1
g = 0.7
d = 0.1

N    = 200
tMin = 0
tMax = 20.0
time = np.linspace(tMin, tMax, N)

y0 = [10, 2]     # initial values

def FLVE(t, y):
    
    r, f = y
    
    rDot = a * r     - b * r * f
    fDot = d * r * f - g * f 
    
    return rDot, fDot

sol = integrate.solve_ivp(FLVE, (tMin, tMax), y0, t_eval=time)

plt.figure(1)
plt.plot(time, sol.y[0, :], label="rabits")
plt.plot(time, sol.y[1, :], label="foxes")
plt.legend()
plt.xlabel('time')
plt.show()
../_images/4b95f240ae64db607547ff62b64482f5dc3759f8538ec2f2cc0a297f75da4ff3.png