Here is a simple example code that implements the Fourth-Order Runge-Kutta:
SUBROUTINE rk4(y, dydx, x, h, yout, derivs)
USE nrtype; USE nrutil, ONLY : assert_eq
IMPLICIT NONE
REAL(sp), DIMENSION(:), INTENT(in) :: y, dydx
REAL(sp), INTENT(in) :: x, h
REAL(sp), DIMENSION(:), INTENT(out) :: yout
INTERFACE
SUBROUTINE derivs(x, y, dydx)
USE nrtype
IMPLICIT NONE
REAL(sp), INTENT(in) :: x
REAL(sp), DIMENSION(:), INTENT(in) :: y
REAL(sp), DIMENSION(:), INTENT(out) :: dydx
END SUBROUTINE derivs
END INTERFACE
INTEGER(i4b) :: ndum
REAL(sp) :: h6, hh, xh
REAL(sp), DIMENSION(SIZE(y)) :: dym, dyt, yt
ndum = assert_eq(SIZE(y), SIZE(dydx), SIZE(yout), 'rk4')
hh = h*0.5_sp
h6 = h/6.0_sp
xh = x + hh
yt = y + hh * dydx
CALL derivs(xh, yt, dyt)
yt = y + hh * dyt
CALL derivs(xh, yt, dym)
yt = y + h * dym
dym = dyt + dym
CALL derivs(x + h, yt, dyt)
yout = y + h6 * (dydx + dyt + 2.0_sp * dym)
END SUBROUTINE rk4
The subroutine applies Runge-Kutta solver to a system
of equations:
Note that here the lower index numbers the equations not time steps. Furthermore the notation itself is y(x) rather than x(t).
The arguments that must be passed to the subroutine are
The first operation simply checks if arrays
y, dydx, and yout are of matching sizes,
and, if not, an appropriate error message is written on
standard output. Otherwise the size is written on a dummy
variable ndum that is not used any more:
ndum = assert_eq(SIZE(y), SIZE(dydx), SIZE(yout), 'rk4')The next three statements:
hh = h*0.5_sp h6 = h/6.0_sp xh = x + hhevaluate
hh),
h6), and
xh.
And now we evaluate
yt.
This will go into k2 later:yt = y + hh * dydx
The next step is to evaluate
yt, and tn+1/2 is stored on xh. Observe
that subroutine derivs reverses the order of
arguments, compared with our notation, i.e., time (xh)
goes first, then position (yt), and the last argument
is used for the answer, once the subroutine returns:CALL derivs(xh, yt, dyt)What this function actually returns, is not k2, but
dyt
by anything yet.
This multiplication takes place in the next line, when we evaluate
yt = y + hh * dytBut remember that
hh is really
CALL derivs(xh, yt, dym)evaluates:
dym.
Now we evaluate xn + k3 thusly:
yt = y + h * dymRemember that whereas
hh stands for
h is just h * dym
is indeed plain k3, whereas x + h stands
for, in our notation,
CALL derivs(x + h, yt, dyt)What's returned in
dyt is
Observe
that just before calling derivs we have save
the previous value of
dyt + dym on dym. That previous
value was
.
So
this means that h6 * 2.0_sp * dym stands
for
h6 * (dydx + dyt), stands for
yout = y + h6 * (dydx + dyt + 2.0_sp * dym)evaluates