<Previous Page: Numerical Solution Methods for First-order Ordinary Differential Equations|Next Page: The Monte Carlo Method>
In this page, we will learn how to numerically solve second-order ordinary differential equations using Fortran.
If you are using a programming language other than Fortran, please also refer to the following pages:
Solving First-Order Ordinary Differential Equations in Python
Solving First-Order Ordinary Differential Equations in Julia
Second-order ordinary differential equations can generally be written in the following form: \[ \frac{d^2y(x)}{dx^2} =f\left (x,y(x), \frac{dy(x)}{dx} \right). \tag{1} \] Here, to solve second-order ordinary differential equations, we consider rewriting equation (1) as the following system of first-order differential equations: \[ v(x) = \frac{dy(x)}{dx}, \tag{2} \] \[ \frac{dv(x)}{dx} = f\left (x,y(x), v(x) \right). \tag{3} \] By introducing a new variable \(v(x)=dy(x)/dx\), we have rewritten the second-order differential equation as a system of two coupled differential equations. Such systems of first-order differential equations can be numerically solved using the methods learned in the previous page (Solving First-Order Ordinary Differential Equations). Furthermore, in general, an \(n\)th-order differential equation can be rewritten as a system of \(n\) coupled first-order ordinary differential equations.
Here, we first learn about solving second-order ordinary differential equations using Euler's method. The details of Euler's method are explained in the previous page (Solving First-Order Ordinary Differential Equations), so please refer to that for more information.
To learn about solving second-order ordinary differential equations using Euler's method, let's consider the Newtonian equation for a harmonic oscillator as an example. The Newtonian equation of motion for a harmonic oscillator can be written as follows: \[ m \frac{d^2x(t)}{dt^2}=-k x(t). \tag{4} \] Here \(m\) is the mass of the particle, and \(k\) is the spring constant. As discussed above, we consider rewriting this second-order differential equation into the following two coupled first-order ordinary differential equations: \[ v(t)=\frac{dx(t)}{dt}, \tag{5} \] \[ \frac{dv(t)}{dt} = -\frac{k}{m}x(t). \tag{6} \] Here, \(v(t)\) represents the velocity of the particle. Let's consider solving these coupled differential equations, equations (5) and (6), under the following initial conditions: \[ x(0)=0, \tag{7} \] \[ v(0)=1. \tag{8} \] The solution to these coupled differential equations can be found analytically and is given as follows: \[ x(t)=\sqrt{\frac{m}{k}} \sin \left [ \sqrt{\frac{k}{m}}t \right ], \tag{9} \] \[ v(t)=\cos \left [\sqrt{\frac{k}{m}}t \right ]. \tag{10} \]
The following program example solves the above differential equations (Newton's equations) using Euler's method.
program main
implicit none
real(8) :: k, m
real(8) :: x, v, f
real(8) :: x_new, v_new
real(8) :: x_exact, v_exact
real(8) :: t_fin, dt, t
integer :: n, i
n = 20
t_fin = 10d0
dt = t_fin/n
k = 1d0
m = 1d0
x = 0d0
v = 1d0
open(20,file="newton_euler.out")
t = 0d0
x_exact = sin(t)
v_exact = cos(t)
write(20,"(5e16.6e3)")t,x,v,x_exact,v_exact
do i = 0, n-1
f = -k*x
x_new = x + dt*v
v_new = v + dt*f/m
t = dt*(i+1)
x_exact = sin(t)
v_exact = cos(t)
write(20,"(5e16.6e3)")t,x_new,v_new,x_exact,v_exact
x = x_new
v = v_new
end do
close(20)
end program main
The following figure compares the numerical solution obtained by Euler's method as calculated by the above program with the analytical solution. You can see that the difference between the numerical and analytical solutions increases over time. This error is due to Euler's method being based on difference approximation, and it can be improved by reducing the time step size. However, the improvement in the accuracy of Euler's method by reducing the step size is very slow, so it is not often used in practical computations. In the next section, we will learn how to solve second-order differential equations (including Newton's equations) using the Predictor-Corrector method, which is an improvement over the Euler's method algorithm.
Here, we attempt to solve the Newton equation, equation (1), using the Predictor-Corrector method. For details about the Predictor-Corrector method, please refer to "Numerical Solutions to First-Order Ordinary Differential Equations".
The following program example solves the above Newton equation using the Predictor-Corrector method.
program main
implicit none
real(8) :: k, m
real(8) :: x, v, f
real(8) :: x_pred, v_pred, f_pred
real(8) :: x_new, v_new
real(8) :: x_exact, v_exact
real(8) :: t_fin, dt, t
integer :: n, i
n = 20
t_fin = 10d0
dt = t_fin/n
k = 1d0
m = 1d0
x = 0d0
v = 1d0
open(20,file="newton_PredCorr.out")
t = 0d0
x_exact = sin(t)
v_exact = cos(t)
write(20,"(5e16.6e3)")t,x,v,x_exact,v_exact
do i = 0, n-1
f = -k*x
x_pred = x + dt*v
v_pred = v + dt*f/m
f_pred = -k*x_pred
x_new = x + dt*0.5d0*(v+v_pred)
v_new = v + dt*0.5d0*(f+f_pred)/m
t = dt*(i+1)
x_exact = sin(t)
v_exact = cos(t)
write(20,"(5e16.6e3)")t,x_new,v_new,x_exact,v_exact
x = x_new
v = v_new
end do
close(20)
end program main
The figure below compares the numerical solution obtained by the Predictor-Corrector method using the above program with the numerical solution obtained by Euler's method, as well as with the analytical solution. From the figure, it can be seen that the accuracy of the Predictor-Corrector method is improved compared to the accuracy of Euler's method. Additionally, since the Predictor-Corrector method is also based on difference approximation, reducing the time step size can minimize the error.
Here, we will implement a program to solve the Newton equation, equation (1), using the Runge-Kutta method. For details about the Runge-Kutta method, please refer to the previous discussion (Lesson 3).
The following program example solves the Newton equation using the Runge-Kutta method.
program main
implicit none
real(8) :: k, m
real(8) :: x, v, f
real(8) :: kx1,kx2,kx3,kx4
real(8) :: kv1,kv2,kv3,kv4
real(8) :: x_new, v_new
real(8) :: x_tmp, v_tmp
real(8) :: x_exact, v_exact
real(8) :: t_fin, dt, t
integer :: n, i
n = 20
t_fin = 10d0
dt = t_fin/n
k = 1d0
m = 1d0
x = 0d0
v = 1d0
open(20,file="newton_RungeKutta.out")
t = 0d0
x_exact = sin(t)
v_exact = cos(t)
write(20,"(5e16.6e3)")t,x,v,x_exact,v_exact
do i = 0, n-1
f = -k*x
kx1 = v
kv1 = f/m
x_tmp = x + 0.5d0*dt*kx1
v_tmp = v + 0.5d0*dt*kv1
kx2 = v_tmp
f = -k*x_tmp
kv2 = f/m
x_tmp = x + 0.5d0*dt*kx2
v_tmp = v + 0.5d0*dt*kv2
kx3 = v_tmp
f = -k*x_tmp
kv3 = f/m
x_tmp = x + dt*kx3
v_tmp = v + dt*kv3
kx4 = v_tmp
f = -k*x_tmp
kv4 = f/m
x_new = x + (dt/6d0)*(kx1+2d0*kx2+2d0*kx3+kx4)
v_new = v + (dt/6d0)*(kv1+2d0*kv2+2d0*kv3+kv4)
t = dt*(i+1)
x_exact = sin(t)
v_exact = cos(t)
write(20,"(5e16.6e3)")t,x_new,v_new,x_exact,v_exact
x = x_new
v = v_new
end do
close(20)
end program main
The figure below compares the numerical solution obtained by the Runge-Kutta method as calculated by the above program with the numerical solutions obtained by Euler's method and the Predictor-Corrector method, as well as with the analytical solution. From the figure, it is evident that the Runge-Kutta method provides higher precision calculations than both the Predictor-Corrector method and Euler's method.
<Previous Page: Numerical Solution Methods for First-order Ordinary Differential Equations|Next Page: The Monte Carlo Method>