From 7dbc30181826aa26600f8d24c92b1587b31677c6 Mon Sep 17 00:00:00 2001 From: Ravi Kumar <119737193+ravi-ivar-7@users.noreply.github.com> Date: Sun, 15 Oct 2023 14:37:29 +0530 Subject: [PATCH] added rkf45 method (#10438) * added rkf45 method * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Updated rkf45.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Updated rkf45.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update rkf45.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update rkf45.py with suggestions * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Improved Code Quality rkf45.py * Added more test cases and exception rkf45.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update rkf45.py * corrected some spellings. rkf45.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update rkf45.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update rkf45.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Christian Clauss --- maths/rkf45.py | 112 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 maths/rkf45.py diff --git a/maths/rkf45.py b/maths/rkf45.py new file mode 100644 index 000000000..29fd447b6 --- /dev/null +++ b/maths/rkf45.py @@ -0,0 +1,112 @@ +""" +Use the Runge-Kutta-Fehlberg method to solve Ordinary Differential Equations. +""" + +from collections.abc import Callable + +import numpy as np + + +def runge_futta_fehlberg_45( + func: Callable, + x_initial: float, + y_initial: float, + step_size: float, + x_final: float, +) -> np.ndarray: + """ + Solve an Ordinary Differential Equations using Runge-Kutta-Fehlberg Method (rkf45) + of order 5. + + https://en.wikipedia.org/wiki/Runge%E2%80%93Kutta%E2%80%93Fehlberg_method + + args: + func: An ordinary differential equation (ODE) as function of x and y. + x_initial: The initial value of x. + y_initial: The initial value of y. + step_size: The increment value of x. + x_final: The final value of x. + + Returns: + Solution of y at each nodal point + + # exact value of y[1] is tan(0.2) = 0.2027100937470787 + >>> def f(x, y): + ... return 1 + y**2 + >>> y = runge_futta_fehlberg_45(f, 0, 0, 0.2, 1) + >>> y[1] + 0.2027100937470787 + >>> def f(x,y): + ... return x + >>> y = runge_futta_fehlberg_45(f, -1, 0, 0.2, 0) + >>> y[1] + -0.18000000000000002 + >>> y = runge_futta_fehlberg_45(5, 0, 0, 0.1, 1) + Traceback (most recent call last): + ... + TypeError: 'int' object is not callable + >>> def f(x, y): + ... return x + y + >>> y = runge_futta_fehlberg_45(f, 0, 0, 0.2, -1) + Traceback (most recent call last): + ... + ValueError: The final value x must be greater than initial value of x. + >>> def f(x, y): + ... return x + >>> y = runge_futta_fehlberg_45(f, -1, 0, -0.2, 0) + Traceback (most recent call last): + ... + ValueError: Step size must be positive. + """ + if x_initial >= x_final: + raise ValueError("The final value x must be greater than initial value of x.") + + if step_size <= 0: + raise ValueError("Step size must be positive.") + + n = int((x_final - x_initial) / step_size) + y = np.zeros( + (n + 1), + ) + x = np.zeros(n + 1) + y[0] = y_initial + x[0] = x_initial + for i in range(n): + k1 = step_size * func(x[i], y[i]) + k2 = step_size * func(x[i] + step_size / 4, y[i] + k1 / 4) + k3 = step_size * func( + x[i] + (3 / 8) * step_size, y[i] + (3 / 32) * k1 + (9 / 32) * k2 + ) + k4 = step_size * func( + x[i] + (12 / 13) * step_size, + y[i] + (1932 / 2197) * k1 - (7200 / 2197) * k2 + (7296 / 2197) * k3, + ) + k5 = step_size * func( + x[i] + step_size, + y[i] + (439 / 216) * k1 - 8 * k2 + (3680 / 513) * k3 - (845 / 4104) * k4, + ) + k6 = step_size * func( + x[i] + step_size / 2, + y[i] + - (8 / 27) * k1 + + 2 * k2 + - (3544 / 2565) * k3 + + (1859 / 4104) * k4 + - (11 / 40) * k5, + ) + y[i + 1] = ( + y[i] + + (16 / 135) * k1 + + (6656 / 12825) * k3 + + (28561 / 56430) * k4 + - (9 / 50) * k5 + + (2 / 55) * k6 + ) + x[i + 1] = step_size + x[i] + return y + + +if __name__ == "__main__": + import doctest + + doctest.testmod()