見出し画像

Python : 非線形(連立)方程式の解法

pythonで数値的に非線形連立方程式の解法のメモです。

問題

以下の非線形連立方程式を例に解き方を整理します。

$$
\left\{ \ \begin{aligned}
& 2x^2 - y^2 = 0 \\
& y = 0.5 \left(  {\rm sin}(x) + {\rm cos}(y) \right) 
\end{aligned} \right.
$$

以下の関数にして、関数のゼロ(root)を探すことで解を得えます。


$$
\left\{ \ \begin{aligned}
& f_0 \left(x,y \right) = 2x^2 - y^2  \\
& f_1 \left(x, y \right) = y - 0.5 \left(  {\rm sin} (x) + {\rm cos} (y) \right) 
\end{aligned} \right.
$$

解法

今回は、Scipy の optimize パッケージにあるrootで解いてみます。以下は、マニュアルです。(マニュアルの中に例題もあります。)

methodは、hybr としておきます。
MINPACK に実装されているものを利用しているようです。
MINPACKの詳細は以下にのリファレンスがありました。
More, Jorge J., Burton S. Garbow, and Kenneth E. Hillstrom. 1980. User Guide for MINPACK-1.
検索すると、PDFがでてきます。ここまでは読んでいません。

 $${ x }$$ -> x[0],  $${ y }$$ ->x[1]です。$${ f_0(x,y) }$$ -> f[0], $${ f_1(x,y) }$$ -> f[1] と置いています。

import numpy as np
import scipy as sp
def res(x):
    f = np.zeros(2, dtype=np.float64)
    f[0] = 2*x[0]**2 - x[1]**2
    f[1] = x[1] - 0.5 * (np.sin(x[0]) + np.cos(x[1]))
    return f
x0 = np.array([0.0, 0.0])
ans = sp.optimize.root(res, x0, method='hybr')
print(ans)
print('--------')

以下が、出力結果

    fjac: array([[-0.95931118,  0.28235096],
       [-0.28235096, -0.95931118]])
     fun: array([-7.21644966e-15, -1.11022302e-16])
 message: 'The solution converged.'
    nfev: 18
     qtf: array([-1.41162713e-11, -4.36299709e-12])
       r: array([-1.6134865 ,  1.02076612, -1.052239  ])
  status: 1
 success: True
       x: array([0.43781417, 0.61916273])

一番最後の行が答えです。この値の時の関数の値は、
fun: array([-7.21644966e-15, -1.11022302e-16]) です。ほぼゼロで、収束しているようです。

例題:状態方程式を解く

連立ではないですが、化学工学で出てくる非線形方程式を例にといてみます。
Soave-Redlich-Kwong(SRK)equation of state を解いてある状態のモル体積を求めてみます。

SRK EOSは、

$$
P = \frac{RT}{\hat{V} - b} - \frac{\alpha a}{ \hat{V} (\hat{V} + b ) }   
$$

$$
a = 0.42747 \frac{(RT_c)^2}{P_c} \\
b = 0.08664 \frac{R T_c}{P_c} \\
m = 0.48505 + 1.55171 \omega - 0.1561 \omega^2 \\
\alpha = \left[ 1 + m \left( 1 - \sqrt{T / T_c} \right) \right]^2 
$$

と定義されています。
$${ P }$$ : 圧力[atm]、$${T}$$ : 絶対温度[K]、$${\hat{V}}$$ : モル体積[L/mol]、$${T_c}$$ : 臨界圧力[K]、$${P_c}$$ : 臨界圧力[atm]、$${\omega}$$ : 偏心因子[-]、$${R}$$ : 気体定数[L atm/(mol-K)]です。

例として、プロパン(臨界圧力 41.93437[atm]、臨界温度 369.82[K]、偏心因子 0.152[-])の350K, 15 atm におけるモル体積を求めてみます。状態方程式を移行して、以下の関数がゼロになる$${ \hat{V} }$$を探します。

$$
f(\hat{V}) = P - \frac{RT}{\hat{V} - b} + \frac{\alpha a}{ \hat{V} (\hat{V} + b ) }
$$

import numpy as np
import scipy as sp
def f(V):
    # Properties of Propane
    T = 350 # K
    P = 15 # atm
    Tc = 369.82 # K
    Pc = 41.93437 # atm
    w = 0.152 # Acentric factor for CO
    R = 0.08206 # L atm / (mol K)
    a = 0.042747*(R*Tc)**2 / (Pc)
    b = 0.08664*(R*Tc/Pc)
    m = 0.48508 + 1.55171*w - 0.1561*w**2
    alpha = (1 + m*(1 - np.sqrt(T/Tc)))**2
    term1 = R*T/(V-b)
    term2 = alpha*a/(V*(V+b))
    return P - term1 + term2

V = 2.0 # L/mol, initial guess
V = sp.optimize.root(f, V) #, maxiter=100, f_tol=1e-6)
print(V)
結果は、
    fjac: array([[-1.]])
     fun: array([5.55111512e-17])
 message: 'The solution converged.'
    nfev: 7
     qtf: array([6.03536665e-12])
       r: array([-7.84436269])
  status: 1
 success: True
       x: array([1.94609414])

答えは、x: array([1.94609414]) です。fの値もほぼゼロなので問題なさそうです。
1.946 L/mol と求まりました。

まとめ

Scipy.optimize.rootを使って非線形方程式を解いてみました。
root関数の中身までは終えていません。

所感

MINPACKは、1980年にまとめられたプロジェクトのようです。研究者たちの蓄積のおかげで簡単に方程式が扱えるのが、有難です。

よろしければサポートをお願いします。講習会、有料情報の取得などにあてたいと考えています。やっぱり、単純にうれしいです。