A package for the curious mathematicians and engineers who want to experiment the Runge-Kutta method with their own coefficients.
I decided to create this package to give mathematicians or engineers the freedom to create and use the Runge-Kutta method of their choice, as opposed to be being locked in to whatever a package provides you.
Runge-Kutta methods aim to numerically solve ordinary differential equations of the form:
with a given initial condition
We define a member of the Runge-Kutta family with a Butcher tableau:
The above tableau is often abbreviated to
The Butcher tableau presents the categories of coefficients that will be used in our Runge-Kutta method.
The
Before defining
where
We now have everything we need to calculate
The idea is to calculate various slopes at point
Most packages for the Runge-Kutta method usually have the coefficients
gerk
is an easy interface to allow the user to determine their own coefficient values for the Runge-Kutta method.
First you must install gerk
with the following command:
pip install gerk
We can then import gerk
in the following way:
from gerk import gerk
As seen in mathematics above, there is quite a bit information required to execute the Runge-Kutta method. This has been broken down into parameters to be passed into gerk()
:
-
a
The$A$ matrix in the Butcher tableau. This must be a lower triangular matrix that is formatted as a list of lists that contain floats, or integers -
b
The$b$ array. Must be a list of floats, decimals or integers -
c
The$c$ array. Must be a list of floats, decimals or integers -
initial
Atuple
that acts as the coordinate of the initial condition values$(x_0, y_0)$ -
terminal
The value of$x$ for which we terminate the Runge-Kutta method -
timesteps
The number of times steps you want to apply on the from the starting point$x_0$ tofinal
. Must be an integer -
func
The function expression to be numerically integrated -
enforce_rules
(Optional) A boolean to enforce conventional Runge-Kutta rules
The function will output the time steps and approximated y values as a tuple of lists of floats.
There is no consensus to what conditions must hold regarding the coefficients you choose for your method, however, some known Runge-Kutta methods do consistently conform to some known conditions.
These conditions are
which can be enforced by the enforce_rules
parameter, which is defaulted to False
.
Let us numerically solve the following initial value problem:
with the initial value
The
a = [
[1/3],
[-1/3, 1],
[1, -1, 1]
]
Now for the b
and c
vectors:
b = [1/8, 3/8, 3/8, 1/8]
c = [0, 1/3, 2/3, 1]
Finally, we can define the function with a lambda function:
func = lambda x, y: y
Putting it all together, we are now ready to run gerk()
and plot its output:
import matplotlib.pyplot as plt
from gerk import gerk
a = [
[1/3],
[-1/3, 1],
[1, -1, 1]
]
b = [1/8, 3/8, 3/8, 1/8]
c = [0, 1/3, 2/3, 1]
func = lambda x, y: y
x, y = gerk(
a=a,
b=b,
c=c,
initial=(0, 1),
timesteps=10000,
terminal=1,
func=func
)
plt.plot(x, y)
plt.show()
The code above yields the following graph:
There is an alternate way to utilise the Runge-Kutta method by employing an additional distinct
where
This method is not too disimilar to the original Runge-Kutta method. We calculate the
Although we do calculate
Note that the calculation for
For every time step, we calculate the following error:
Now we define the tolerance,
If
However, if
The value of
where
In this example, we will try to numerically integrate
with initial conditions
Here we will use the Bogacki–Shampine (BS23) method which has the following Butcher tableau:
For the adaptive Runge-Kutta method, we will use adaptive_gerk()
. This method's paramters vary slightly from gerk()
:
-
a
The$A$ matrix in the Butcher tableau. This must be a lower triangular matrix that is formatted as a list of lists that contain floats, or integers -
b_1
The$b_1$ array. Must be a list of floats, decimals or integers -
b_2
The$b_2$ array. Must be a list of floats, decimals or integers -
c
The$c$ array. Must be a list of floats, decimals or integers -
initial
Atuple
that acts as the coordinate of the initial condition values$(x_0, y_0)$ -
terminal
The value of$x$ for which we terminate the Runge-Kutta method -
timesteps
The number of times steps you want to apply on the from the starting point$x_0$ tofinal
. Must be an integer -
func
The function expression to be numerically integrated -
enforce_rules
(Optional) A boolean to enforce conventional Runge-Kutta rules. Defaulted toFalse
-
tolerance
(Optional) A float representing the maximum threshold of the adaptive step. Defaulted to1e-4
Similar to gerk()
, this function will output the time steps and approximated y values as a tuple of lists of floats.
from math import exp
import matplotlib.pyplot as plt
from gerk import adaptive_gerk
a = [
[1/2],
[0, 3/4],
[2/9, 1/3, 4/9]
]
b_1 = [2/9, 1/3, 4/9, 0]
b_2 = [7/24, 1/4, 1/3, 1/8]
c = [0, 1/2, 3/4, 1]
x, y = adaptive_gerk(
a=a,
b_1=b_1,
b_2=b_2,
c=c,
initial=(-1, exp(-1)),
timesteps=10000,
terminal=1,
func=lambda x, y: -2 * x * y,
)
plt.plot(x, y)
plt.show()
The code above yields the following graph:
- Further generalise the Runge-Kutta method with an indefinite number of
$b$ and$c$ arrays - Implement Cython for faster execution times
- Add unit tests
- Implement Stochastic Runge-Kutta method