連立常微分方程式

モジュールのインポート

import numpy as np  
from scipy.integrate import solve_ivp
from scipy.integrate import odeint
import matplotlib.pyplot as plt
import scienceplots
#Warning : As of version 2.0.0, you need to add import scienceplots before setting the style (plt.style.use('science')).
plt.style.use(['science', 'notebook'])
次のような連立微分方程式,初期条件に対して,$0 < x \le 1/2$の範囲で,数値的に$y$,$z$の値を計算しよう. $$ y' = \frac{dy}{dx} = 3y+4z, \ \ \ \ \ z' = \frac{dz}{dx} = 2y+5z $$ ここで,初期条件は, $$ x_0 = 0, \ \ \ \ \ y_0 = 3, \ \ \ \ \ z_0 = 0 $$ 微分方程式を解析的に解くと[数学演習,p.272 [11](4)], $$ y = 2c_1 e^x + c_2 e^{7x}, \ \ \ \ \ z = -c_1 e^x + c_2 e^{7x} $$ ただし,$x=0$のとき, $$ c_1 = \frac{y(0)-z(0)}{3}, \ \ \ \ \ c_2 = \frac{y(0)+2z(0)}{3} $$ より,$y(0)=3$,$z(0)=0$とおくと,$c_1 =1$,$c_2=1$となるので, $$ y = 2 e^x + e^{7x}, \ \ \ \ \ z = -e^x + e^{7x} $$ まず,この関数は,
def func7(x):
    return [2*np.exp(x)+np.exp(7*x), -np.exp(x)+np.exp(7*x)]
微分方程式は,
def dfunc7(x, s): # Coupled Second order ODEs
    y, z = s
    return [3*y+4*z, 2*y+5*z]

関数 odeint

Scipy の 関数 odeint
https://docs.scipy.org/doc/scipy/reference/generated/scipy.integrate.odeint.html
より,
x00, y00, z00 = 0.0, 3.0, 0.0 # 初期条件
s00 = (y00, z00)
m = 11
xe = 0.5
xx = np.linspace(x00, xe, m)
ode1 = odeint(dfunc7, s00, xx,  tfirst=True)
yy1, zz1 = ode1.T[0], ode1.T[1]
解析的に得られた式から誤差を求めると,
print(f"{'x':>8s} {'odeint y':>10s} {'error':>10s} {'odeint z':>10s} {'error':>10s}")
yy, zz = func7(xx)
for i,x in enumerate(xx):
    print(f"{x:8.3f} {yy1[i]:10.6f} {yy1[i]-yy[i]:10.6f} {zz1[i]:10.6f} {zz1[i]-zz[i]:10.6f}")
x   odeint y      error   odeint z      error
0.000   3.000000   0.000000   0.000000   0.000000
0.050   3.521610  -0.000000   0.367796  -0.000000
0.100   4.224095   0.000000   0.908582   0.000000
0.150   5.181320   0.000000   1.695817   0.000000
0.200   6.498006   0.000000   2.833797   0.000000
0.250   8.322654   0.000000   4.470577   0.000000
0.300  10.865888   0.000000   6.816311   0.000000
0.350  14.426482   0.000000  10.169279   0.000000
0.400  19.428297   0.000001  14.952823   0.000001
0.450  26.472690   0.000001  21.767753   0.000001
0.500  36.412896   0.000001  31.466732   0.000001
プロットすると,
xxx = np.linspace(x00, xe, m*10)

fig = plt.figure() # グラフ領域の作成
yyy, zzz = func7(xxx)
plt.plot(xxx, yyy, xxx, zzz)
plt.plot(xx, yy1, "o")
plt.plot(xx, zz1, "o")
fig.savefig('p1_ex07e_1.pdf')

関数 solve_ivp

Scipy の 関数 solve_ivp
https://docs.scipy.org/doc/scipy/reference/generated/scipy.integrate.solve_ivp.html
より,
x0 = x00
y_ini = s00
yy2 = np.empty(0)
zz2 = np.empty(0)
print(f"{'x':>8s} {'solve_ivp y':>12s} {'error':>12s} {'solve_ivp z':>12s} {'error':>12s}")
for i,x in enumerate(xx):
    ode2 = solve_ivp(dfunc7, [x0, x], y_ini,  method='DOP853', dense_output=True)
    ss = ode2.sol(x)
    print(f"{x:8.3f} {ss[0]:12.8f} {ss[0]-yy[i]:12.8f} {ss[1]:12.8f} {ss[1]-zz[i]:12.8f}")
    x0 = x
    y_ini = ss.copy()
    yy2 = np.append(yy2, ss[0])
    zz2 = np.append(zz2, ss[1])
x  solve_ivp y        error  solve_ivp z        error
0.000   3.00000000   0.00000000   0.00000000   0.00000000
0.050   3.52160974  -0.00000000   0.36779645  -0.00000000
0.100   4.22409454  -0.00000000   0.90858179  -0.00000000
0.150   5.18131960  -0.00000000   1.69581688  -0.00000000
0.200   6.49800548  -0.00000000   2.83379721  -0.00000000
0.250   8.32265351  -0.00000000   4.47057726  -0.00000000
0.300  10.86588753  -0.00000000   6.81631110  -0.00000000
0.350  14.42648182  -0.00000000  10.16927917  -0.00000000
0.400  19.42829617  -0.00000000  14.95282207  -0.00000000
0.450  26.47268895  -0.00000000  21.76775239  -0.00000000
0.500  36.41289450  -0.00000000  31.46673069  -0.00000000
プロットすると,
fig = plt.figure() # グラフ領域の作成
plt.plot(xxx, yyy, xxx, zzz)
plt.plot(xx, yy2, "o")
plt.plot(xx, zz2, "o")
fig.savefig('p1_ex07e_2.pdf')

yy1,yy2,yy
(array([ 3.        ,  3.52160974,  4.22409459,  5.18131964,  6.49800556,
         8.32265363, 10.86588772, 14.42648211, 19.42829669, 26.47268983,
        36.41289594]),
 array([ 3.        ,  3.52160974,  4.22409454,  5.1813196 ,  6.49800548,
         8.32265351, 10.86588753, 14.42648182, 19.42829617, 26.47268895,
        36.4128945 ]),
 array([ 3.        ,  3.52160974,  4.22409454,  5.1813196 ,  6.49800548,
         8.32265351, 10.86588753, 14.42648182, 19.42829617, 26.47268895,
        36.4128945 ]))
計算誤差をプロットして比較すると,
fig = plt.figure() # グラフ領域の作成
plt.plot(xx, yy1-yy, "o", label='odeint y')
plt.plot(xx, yy2-yy, "o", label='solve_int y')
plt.legend(ncol=2, loc='upper right', fancybox=False, frameon = True)
fig.savefig('p1_ex07e_3.pdf')

fig = plt.figure() # グラフ領域の作成
plt.plot(xx, zz1-zz, "o", label='odeint z')
plt.plot(xx, zz2-zz, "o", label='solve_int z')
plt.legend(ncol=2, loc='upper right', fancybox=False, frameon = True)
fig.savefig('p1_ex07e_4.pdf')