常微分方程式

モジュールのインポート

import numpy as np
from scipy.integrate import solve_ivp
# from scipy.integrate import odeint

オイラー法

# x0, y0: 初期条件,y0 = y'(x0)
# xs: yを求めるxの値
# dfunc: 導関数
# n: 区間数
# y: 数値解,y = y(xs)
def euler(dfunc, x0, y0, xs, n): # オイラー法
    h = (xs-x0)/n
    y = y0
    for i in range(n):
        x = x0+h*i
        y = y+h*dfunc(x,y)
    return y

修正オイラー法(2次のルンゲ・クッタ法)

# x0, y0: 初期条件,y0 = y'(x0)
# xs: yを求めるxの値
# dfunc: 導関数
# n: 区間数
# y: 数値解,y = y(xs)
def rk2(dfunc, x0, y0, xs, n): # 修正オイラー法(2次のルンゲ・クッタ法)
    h = (xs-x0)/n
    y = y0
    for i in range(n):
       x = x0+h*i
       k1 = h*dfunc(x,y)
       k2 = h*dfunc(x+h,y+k1)
       y = y+(k1+k2)*0.5
    return y

4次のルンゲ・クッタ法

# x0, y0: 初期条件,y0 = y'(x0)
# xs: yを求めるxの値
# dfunc: 導関数
# n: 区間数
# y: 数値解,y = y(xs)
def rk4(dfunc, x0, y0, xs, n): # 4次のルンゲ・クッタ法
    h = (xs-x0)/n
    h2 = h*0.5
    y = y0
    for i in range(n):
       x = x0+h*i
       k1 = h*dfunc(x,y)
       k2 = h*dfunc(x+h2,y+k1*0.5)
       k3 = h*dfunc(x+h2,y+k2*0.5)
       k4 = h*dfunc(x+h,y+k3)
       y = y+(k1+2.0*(k2+k3)+k4)/6.0
    return y
Scipy の常微分方程式の関数の一つは,
https://docs.scipy.org/doc/scipy/reference/generated/scipy.integrate.solve_ivp.html
まず,次のような関数を考える.
def dfunc7(x,y): # 微分方程式 y' = dy/dx = f(x,y)
    dydx = -4.0*(x-1.0)*y
    return dydx
これは解析的に解け,次のような関数で表される.
def func7(x): # 微分方程式を解析的に解いて得られた関数
    y = np.exp(-2.0*(x-1.0)*(x-1.0))
    return y
これらより常微分方程式を数値的に解いてみよう.
eps = 1.0e-9
n, m = 5, 11
x00, y00 = 0.0, np.exp(-2.0)# 初期条件 for func7
xe = 2.0
dx = (xe-x00)/(m-1)
x0 = x00
y0a, y0b, y0c = y00, y00, y00
y_ini = np.array([y00])
print(f"{'x':>8s} {'solve_ivp':>10s} {'er1':>10s} {'ek2':>10s}"\
      f"{'er2':>10s} {'rk4':>10s} {'er4':>10s}")
for i in range(m):
    x = x00+dx*i
    y = func7(x)
    y1 = euler(dfunc7, x0, y0a, x, n)
    y2 = rk2(dfunc7, x0, y0b, x, n)
    y4 = rk4(dfunc7, x0, y0c, x, n)
    solver = solve_ivp(dfunc7, [x0, x], y_ini, dense_output=True)
    y5 = solver.sol(x)
    print(f"{x:8.3f} {y5[0]:10.6f} {y5[0]-y:10.6f} {y2:10.6f}"\
          f"{y2-y:10.6f} {y4:10.6f} {y4-y:10.6f}")
    x0 = x
    y0a, y0b, y0c = y1, y2, y4
    y_ini = y5.copy()
x  solve_ivp        er1        ek2       er2        rk4        er4
0.000   0.135335   0.000000   0.135335  0.000000   0.135335   0.000000
0.200   0.278038   0.000000   0.277404 -0.000633   0.278037  -0.000001
0.400   0.486753   0.000001   0.485100 -0.001652   0.486751  -0.000002
0.600   0.726150   0.000001   0.723366 -0.002783   0.726147  -0.000002
0.800   0.923118   0.000001   0.919466 -0.003651   0.923113  -0.000003
1.000   1.000002   0.000002   0.996014 -0.003986   0.999997  -0.000003
1.200   0.923118   0.000001   0.919419 -0.003697   0.923113  -0.000003
1.400   0.726150   0.000001   0.723298 -0.002851   0.726147  -0.000002
1.600   0.486753   0.000001   0.485056 -0.001696   0.486751  -0.000002
1.800   0.278038   0.000001   0.277421 -0.000616   0.278037  -0.000001
2.000   0.135337   0.000001   0.135411  0.000076   0.135335   0.000000