Tutorial 10 - Initial value problems
Contents
Tutorial 10 - Initial value problems¶
nitial value problems of ordinary differential equations, explicit and implicit Euler method, Runge-Kutta methods, Leap-frog, Adams-Bashford, Adams-Moulton, predictor-corrector, Bulirsch-Stoer algorithm, stiff equations.
import numpy as np
import matplotlib.pyplot as plt
from scipy import integrate, optimize
Runge-Kutta methods¶
The Runge–Kutta methods are a family of explicit and implicit iterative methods for the numerical solution of the initial value problem
Here \( y \) is an unknown function (scalar or vector) of \( x \). The function \( f \) and the initial conditions \( x_0 \), \( y_0 \) are given. The Runge–Kutta methods take the form
where
in the case of explicit methods and
in the case of implicit methods. Here, \( y_i \) and \( y_{i + 1} \) are the approximations of \( y(x_i)\) and \( y(x_{i + 1}) \), respectively, and \( h > 0 \) is a step size. To specify a particular method, one needs to provide the integer \( r \) (the number of stages), and the coefficients \( a_{ij} \), \( b_i \), and \( c_i \) (see the list of Runge-Kutta methods). The matrix \( (a_{ij}) \) is called the Runge–Kutta matrix, while the \( b_i \) and \( c_i \) are known as the weights and the nodes. These data can be arranged in a Butcher tableau:
Note: In the case of explicit methods the Runge-Kutta matrix is lower triangular.
Forward (explicit) Euler method¶
Exercise 10.1: Implement the forward Euler method.
def forward_euler(f, x_0, x_n, y_0, n): # Runge-Kutta 1st order method
# add your code here
You may evaluate the correctness of your implementation using the scipy.integrate.solve_ivp
function.
def f(t, y):
return -0.5 * y
try:
np.testing.assert_almost_equal(forward_euler(f, 0, 10, 2, 10000)[1][-1], integrate.solve_ivp(f, [0, 10], [2]).y[-1][-1], \
decimal=4)
except AssertionError as E:
print(E)
else:
print("The implementation is correct.")
Exercise 10.2: The fundamental law of radioactive decay can be mathematically expressed as
where \( N \) is the number of radioactive nuclei, \( t \) is time, \( \lambda = \ln(2) \, / \, t_{1/2} \) is the decay constant and \( t_{1/2} \) is the half-life of the decaying quantity. The analytical solution of the decay equation is
Solve the decay equation for Ra-226 using the forward Euler method and compare the numerical and analytical solutions.
# add your code here
Runge-Kutta \(2^{\mathrm{nd}}\) order method¶
Exercise 10.3: Implement the Runge-Kutta \( 2^{\mathrm{nd}} \) order method.
def runge_kutta_2(f, x_0, x_n, y_0, n):
# add your code here
You may evaluate the correctness of your implementation using the scipy.integrate.solve_ivp
function.
def f(t, y):
return -0.5 * y
try:
np.testing.assert_almost_equal(runge_kutta_2(f, 0, 10, 2, 100)[1][-1], integrate.solve_ivp(f, [0, 10], [2]).y[-1][-1], \
decimal=4)
except AssertionError as E:
print(E)
else:
print("The implementation is correct.")
Exercise 10.4: Solve the decay equation for Ra-226 using the Runge-Kutta \( 2^{\mathrm{nd}} \) order method and compare the numerical and analytical solutions.
# add your code here
Runge-Kutta \(3^{rd}\) order method¶
Exercise 10.5: Implement the Runge-Kutta \( 3^{\mathrm{rd}} \) order method.
def runge_kutta_3(f, x_0, x_n, y_0, n):
# add your code here
You may evaluate the correctness of your implementation using the scipy.integrate.solve_ivp
function.
def f(t, y):
return -0.5 * y
try:
np.testing.assert_almost_equal(runge_kutta_3(f, 0, 10, 2, 100)[1][-1], integrate.solve_ivp(f, [0, 10], [2]).y[-1][-1], \
decimal=4)
except AssertionError as E:
print(E)
else:
print("The implementation is correct.")
Exercise 10.6: Solve the decay equation for Ra-226 using the Runge-Kutta \( 3^{\mathrm{rd}} \) order method and compare the numerical and analytical solutions.
# add your code here
Runge-Kutta \(4^{th}\) order method¶
Exercise 10.7: Implement the Runge-Kutta \( 4^{\mathrm{th}} \) order method.
def runge_kutta_4(f, x_0, x_n, y_0, n):
# add your code here
You may evaluate the correctness of your implementation using the scipy.integrate.solve_ivp
function.
def f(t, y):
return -0.5 * y
try:
np.testing.assert_almost_equal(runge_kutta_4(f, 0, 10, 2, 10)[1][-1], integrate.solve_ivp(f, [0, 10], [2]).y[-1][-1], \
decimal=4)
except AssertionError as E:
print(E)
else:
print("The implementation is correct.")
Exercise 10.8: Solve the decay equation for Ra-226 using the Runge-Kutta \( 4^{\mathrm{th}} \) order method and compare the numerical and analytical solutions.
# add your code here
Backward (implicit) Euler method¶
Exercise 10.9: Implement the backward Euler method.
def backward_euler(f, x_0, x_n, y_0, n): # implicit method
# add your code here
You may evaluate the correctness of your implementation using the scipy.integrate.solve_ivp
function.
def f(t, y):
return -0.5 * y
try:
np.testing.assert_almost_equal(backward_euler(f, 0, 10, 2, 1000)[1][-1], integrate.solve_ivp(f, [0, 10], [2]).y[-1][-1], \
decimal=4)
except AssertionError as E:
print(E)
else:
print("The implementation is correct.")
Exercise 10.10: Solve the equation
using the backward Euler method. Compare the numerical solution with the analytical solution
and with the solution obtained by the forward Euler method.
# add your code here
Multistep methods¶
Leap-frog method¶
Exercise 10.11: Implement the Leap-frog method.
def leap_frog(f, x_0, x_n, y_0, n):
# add your code here
You may evaluate the correctness of your implementation using the scipy.integrate.solve_ivp
function.
def f(t, y):
return -0.5 * y
try:
np.testing.assert_almost_equal(leap_frog(f, 0, 10, 2, 10000)[1][-1], integrate.solve_ivp(f, [0, 10], [2]).y[-1][-1], \
decimal=4)
except AssertionError as E:
print(E)
else:
print("The implementation is correct.")
Exercise 10.12: Solve the decay equation for Ra-226 using the Leap-frog method and compare the numerical and analytical solutions.
# add your code here
Adams-Bashforth method¶
Exercise 10.13: Implement the Adams-Bashforth method.
def adams_bashforth(f, x_0, x_n, y_0, n):
# add your code here
You may evaluate the correctness of your implementation using the scipy.integrate.solve_ivp
function.
def f(t, y):
return -0.5 * y
try:
np.testing.assert_almost_equal(adams_bashforth(f, 0, 10, 2, 100)[1][-1], integrate.solve_ivp(f, [0, 10], [2]).y[-1][-1], \
decimal=4)
except AssertionError as E:
print(E)
else:
print("The implementation is correct.")
Exercise 10.14: Solve the decay equation for Ra-226 using the Adams-Bashforth method and compare the numerical and analytical solutions.
# add your code here
Adams-Moulton method¶
Exercise 10.15: Implement the Adams-Moulton method.
def adams_moulton(f, x_0, x_n, y_0, n):
# add your code here
You may evaluate the correctness of your implementation using the scipy.integrate.solve_ivp
function.
def f(t, y):
return -0.5 * y
try:
np.testing.assert_almost_equal(adams_moulton(f, 0, 10, 2, 100)[1][-1], integrate.solve_ivp(f, [0, 10], [2]).y[-1][-1], \
decimal=4)
except AssertionError as E:
print(E)
else:
print("The implementation is correct.")
Exercise 10.16: Solve the decay equation for Ra-226 using the Adams-Moulton method and compare the numerical and analytical solutions.
# add your code here
Predictor-corrector methods¶
The predictor-corrector method
Exercise 10.17: Implement the predictor-corrector method.
def predictor_corrector(f, x_0, x_n, y_0, n):
# add your code here
You may evaluate the correctness of your implementation using the scipy.integrate.solve_ivp
function.
def f(t, y):
return -0.5 * y
try:
np.testing.assert_almost_equal(predictor_corrector(f, 0, 10, 2, 100)[1][-1], integrate.solve_ivp(f, [0, 10], [2]).y[-1][-1], \
decimal=4)
except AssertionError as E:
print(E)
else:
print("The implementation is correct.")
Exercise 10.18: Solve the decay equation for Ra-226 using the predictor-corrector method and compare the numerical and analytical solutions.
# add your code here
Bulirsch-Stoer method¶
Exercise 10.19: Implement the Bulirsch-Stoer method.
def bulirsch_stoer(f, x_0, x_n, y_0, n, m=5):
# add your code here
You may evaluate the correctness of your implementation using the scipy.integrate.solve_ivp
function.
def f(t, y):
return -0.5 * y
try:
np.testing.assert_almost_equal(bulirsch_stoer(f, 0, 10, 2, 100)[1][-1], integrate.solve_ivp(f, [0, 10], [2]).y[-1][-1], \
decimal=4)
except AssertionError as E:
print(E)
else:
print("The implementation is correct.")
Exercise 10.20: Solve the decay equation for Ra-226 using the Bulirsch-Stoer method and compare the numerical and analytical solutions.
# add your code here