差分法の重み係数を出してくれる
超ありがたいPythonコード
出川智啓
いきなりですが
2019/07/27オープンCAE勉強会@関西2
微分の差分近似式
何個暗記してますか?
1階微分の差分近似式
2019/07/27オープンCAE勉強会@関西3
 𝑓𝑖
′
=
𝑓 𝑖+1−𝑓 𝑖
Δ𝑥
+ 𝑂 Δ𝑥
 𝑓𝑖
′
=
𝑓 𝑖−𝑓 𝑖−1
Δ𝑥
+ 𝑂 Δ𝑥
 𝑓𝑖
′
=
𝑓 𝑖+1−𝑓 𝑖−1
2Δ𝑥
+ 𝑂 Δ𝑥2
 𝑓𝑖
′
=
3𝑓 𝑖−4𝑓 𝑖−1+𝑓 𝑖−2
2Δ𝑥
+ 𝑂 Δ𝑥2
 𝑓𝑖
′
=
−3𝑓 𝑖+4𝑓 𝑖+1−𝑓𝑖+2
2Δ𝑥
+ 𝑂 Δ𝑥2
 𝑓𝑖
′
=
−𝑓 𝑖+2+8?𝑓 𝑖+1−8?𝑓 𝑖−1+𝑓 𝑖−2
12?Δ𝑥
+ 𝑂 Δ𝑥4
2階微分の差分近似式
2019/07/27オープンCAE勉強会@関西4
 𝑓𝑖
′′
=
𝑓 𝑖+1−2𝑓 𝑖+𝑓𝑖−1
Δ𝑥2 + 𝑂 Δ𝑥2
 𝑓𝑖
′′
=
𝑓 𝑖−2𝑓 𝑖+1+𝑓𝑖+2
Δ𝑥2 + 𝑂 Δ𝑥
 𝑓𝑖
′′
=
𝑓 𝑖−2𝑓 𝑖−1+𝑓𝑖−2
Δ𝑥2 + 𝑂 Δ𝑥
 𝑓𝑖
′′
=
2?𝑓 𝑖−5?𝑓 𝑖+1+4?𝑓 𝑖+2−?𝑓 𝑖+3
Δ𝑥2 + 𝑂 Δ𝑥2
 𝑓𝑖
′′
=
2?𝑓 𝑖−5?𝑓 𝑖−1+4?𝑓 𝑖−2−?𝑓 𝑖−3
Δ𝑥2 + 𝑂 Δ𝑥2
重み係数の情報源
2019/07/27オープンCAE勉強会@関西5
 手作業で導出
 4次精度が精一杯
 ネット
 中心差分
 https://en.wikipedia.org/wiki/Finite_difference_coefficient#Central_finite_difference
 片側差分
 https://en.wikipedia.org/wiki/Finite_difference_coefficient#Forward_finite_difference
 https://en.wikipedia.org/wiki/Finite_difference_coefficient#Backward_finite_difference
 任意階数,任意精度の式はない
 それに近いWebアプリはある
 http://web.media.mit.edu/~crtaylor/calculator.html
 自分に都合のよい導出プログラムを作ってみよう
差分法の公式の導出
2019/07/27オープンCAE勉強会@関西6
 Taylor展開
 微分の公式を直接導出
 ステンシルと精度の関係がわかりやすい
 連立方程式を解く必要が生じる
 Lagrange多項式
 補間多項式を経由して間接的に導出
 ステンシルと精度の関係は明示されない
 1階微分は打切精度=ステンシル幅-1という関係を暗喩
 記号的に微分ができれば非常に楽
戦略
2019/07/27オープンCAE勉強会@関西7
1. Lagrange基底多項式を作る
2. 物理量と基底多項式をかけてLagrange多項式を作る
3. Lagrange多項式を記号的に微分する
Lagrange多項式(補間多項式)
2019/07/27オープンCAE勉強会@関西8
 重複のないn+1個の座標値と物理量の組 𝑥𝑖, 𝑓𝑖
 n+1点全てを通るn次多項式を作る
 n次式多項式𝑃 𝑥 = 𝑎0 + 𝑎1 𝑥 + 𝑎2 𝑥2
+ ⋯ + 𝑎 𝑛 𝑥 𝑛
の係数
𝑎0, 𝑎𝑖, … , 𝑎 𝑛を求めるのはよい手段ではない
 𝑓𝑖 = 𝑃 𝑥𝑖 = 𝑎0 + 𝑎1 𝑥𝑖 + 𝑎2 𝑥𝑖
2
+ ⋯ + 𝑎 𝑛 𝑥𝑖
𝑛
𝑖 = 0,1, … , 𝑛
 連立方程式を解く必要がある
 nが大きいときは悪条件になりやすい
 直接的な方法が望ましい
 Lagrange(補間)多項式
Lagrange多項式(補間多項式)
2019/07/27オープンCAE勉強会@関西9
 Lagrange(補間)多項式
=Lagrange基底多項式の線形結合
 𝐿 𝑥 = ∑ 𝑓𝑖ℓ𝑖(𝑥)𝑛
𝑖=0
 ℓ𝑖(𝑥) Lagrange基底多項式
 ℓ𝑖 𝑥 = ∏
𝑥−𝑥 𝑗
𝑥 𝑖−𝑥 𝑗
0≤𝑗≤𝑛
𝑖≠𝑗
=
𝑥−𝑥0
𝑥 𝑖−𝑥0
⋯
𝑥−𝑥 𝑖−1
𝑥 𝑖−𝑥𝑖−1
𝑥−𝑥 𝑖+1
𝑥 𝑖−𝑥 𝑖+1
⋯
𝑥−𝑥 𝑛
𝑥 𝑖−𝑥 𝑛
例題
2019/07/27オープンCAE勉強会@関西10
 (-0.5, 1.5625),(0, 1.0625),(0.5, 1.0625)が
与えられているとき,x=0.25におけるfを求めよ
 ℓ0 0.25 =
0.25−0
−0.5−0
0.25−0.5
−0.5−0.5
= −
0.0625
0.5
= −0.125
 ℓ1 0.25 =
0.25− −0.5
0− −0.5
0.25−0.5
0−0.5
= −
0.1875
−0.25
= 0.75
 ℓ2 0.25 =
0.25− −0.5
0.5− −0.5
0.25−0
0.5−0
=
0.1875
0.5
= 0.375

𝑓 0.25 = 𝐿 0.25
= 𝑓0ℓ0 0.25 + 𝑓1ℓ1 0.25 + 𝑓2ℓ2 0.25
= −0.125 ⋅ 1.5625 + 0.75 ⋅ 1.0625 + 0.375 ⋅ 1.0625
= 1
Lagrange基底多項式を作る関数
2019/07/27オープンCAE勉強会@関西11
 Sympyを使ってシンボリックに表現
import sympy as sp
def LagrangeBasis(x,degreeOfPolynomial,pointAt,xSet=None):
import sympy as sp
numDataSet = degreeOfPolynomial+1
if xSet==None:
xSet = sp.symbols('x0:{:d}'.format(numDataSet))
index = list(range(numDataSet))
index.remove(pointAt)
return sp.prod([(x-xSet[j])/(xSet[pointAt]-xSet[j])
for j in index])
Lagrange基底多項式を作る関数
2019/07/27オープンCAE勉強会@関西12
 Sympyを使ってシンボリックに表現
x=sp.symbols('x')
LagrangeBasis(x,degreeOfPolynomial=5,pointAt=0)
Lagrange基底多項式を作る関数の説明
2019/07/27オープンCAE勉強会@関西13
 引数
 x :物理量を補間したい座標
 degreeOfPolynomial :補間多項式の次数(=データ数-1)
 pointAt :基底多項式
 xSet :物理量が定義された座標の集合
(差分法でいうステンシル)
def LagrangeBasis(x,degreeOfPolynomial,pointAt,xSet=None):
import sympy as sp
numDataSet = degreeOfPolynomial+1
if xSet==None:
xSet = sp.symbols('x0:{:d}'.format(numDataSet))
index = list(range(numDataSet))
index.remove(pointAt)
return sp.prod([(x-xSet[j])/(xSet[pointAt]-xSet[j])
for j in index])
Lagrange基底多項式を作る関数の説明
2019/07/27オープンCAE勉強会@関西14
 補間多項式の次数とデータ数の対応付け
 ステンシルの準備
 引数として与えられていないときは作成
 Sympyシンボルの一括作成
numDataSet = degreeOfPolynomial+1
if xSet==None:
xSet = sp.symbols('x0:{:d}'.format(numDataSet))
'x0:{:d}'.format(6)
'x0:6'
xSet=sp.symbols('x0:{:d}'.format(6))
xSet
(x0, x1, x2, x3, x4, x5)
Lagrange基底多項式を作る関数の説明
2019/07/27オープンCAE勉強会@関西15
 配列添字を参照するインデックスの作成
 listとして作成
 Lagrange基底多項式を作る点はremoveで取り除く
 Lagrange多項式の仮定を満たすため
 インデックスに基づいて座標値を参照し,基底多項式を
作成
index = list(range(numDataSet))
index.remove(pointAt)
return sp.prod([(x-xSet[j])/(xSet[pointAt]-xSet[j])
for j in index])
Lagrange多項式
2019/07/27オープンCAE勉強会@関西16
 計算したLagrange基底多項式ℓ𝑖(𝑥)と物理量𝑓𝑖の線形
結合を計算
 𝐿 𝑥 = ∑ 𝑓𝑖ℓ𝑖(𝑥)𝑛
𝑖=0
def LagrangePoly(x,xSet,fSet):
if len(xSet) != len(fSet):
raise ValueError(
"The number of elements of xSet({:d}) and fSet({:d}) does not match."¥
.format(len(xSet),len(fSet)))
return
numDataSet = len(xSet)
degreeOfPolynomial = numDataSet-1
return sum([LagrangeBasis(x,degreeOfPolynomial,i,xSet)*fSet[i]
for i in range(numDataSet)])
Lagrange多項式を作る関数の説明
2019/07/27オープンCAE勉強会@関西17
 引数
 x :物理量を補間したい座標
 xSet :物理量が定義された座標の集合
(差分法でいうステンシル)
 fSet :物理量の集合
def LagrangePoly(x,xSet,fSet):
if len(xSet) != len(fSet):
raise ValueError(
"The number of elements of xSet({:d}) and fSet({:d}) does not match."¥
.format(len(xSet),len(fSet)))
return
numDataSet = len(xSet)
degreeOfPolynomial = numDataSet-1
return sum([LagrangeBasis(x,degreeOfPolynomial,i,xSet)*fSet[i]
for i in range(numDataSet)])
Lagrange多項式を作る関数の説明
2019/07/27オープンCAE勉強会@関西18
 エラー処理
 物理量が定義された座標と物理量の要素数が異なれば
ValueErrorを発生させる
 Lagrange多項式の作成
 基底多項式ℓ𝑖(𝑥)と物理量𝑓𝑖の積の線形結合をreturn
if len(xSet) != len(fSet):
raise ValueError(
"The number of elements of xSet({:d}) and fSet({:d}) do not match."¥
.format(len(xSet),len(fSet)))
return
numDataSet = len(xSet)
degreeOfPolynomial = numDataSet-1
return sum([LagrangeBasis(x,degreeOfPolynomial,i,xSet)*fSet[i]
for i in range(numDataSet)])
Lagrange多項式を作ってみる
2019/07/27オープンCAE勉強会@関西19
 座標値
 -Δx, 0, Δxの3点を通るLagrange多項式
 微分を計算したい点を0とした相対座標系とみなせる
 物理量の集合
 Sympyシンボルとして作成
 Lagrange多項式の作成
x=sp.symbols('x')
Δx=sp.symbols('Δx')
xi=[-Δx,0,Δx]
fi=sp.symbols('f0:{:d}'.format(len(xi)))
LagrangePoly(x,xi,fi)
Lagrange多項式の微分
2019/07/27オープンCAE勉強会@関西20
 sympy.diff(式, 微分変数[,階数])
 1階微分
 2階微分
sp.diff(LagrangePoly(x,xi,fi),x)
sp.diff(LagrangePoly(x,xi,fi),x,2)
Lagrange多項式の微分
2019/07/27オープンCAE勉強会@関西21
 xの設定
 -Δx, 0, Δxの3点を通るLagrange多項式
 微分を計算したい点を0とした相対座標系とみなせる
 Lagrange多項式を微分して得られた式のxに0を代入
 subsメソッドによるSympyシンボルへの値の代入
 (ついでに)simplify関数で整理
sp.diff(LagrangePoly(x,xi,fi),x).subs([(x,0)])
sp.simplify(sp.diff(LagrangePoly(x,xi,fi),x).subs([(x,0)]))
Lagrange多項式の微分
2019/07/27オープンCAE勉強会@関西22
 一連の作業をまとめて関数化
 function :微分する式
LagrangePoly()の戻り値を与える
 x :微分する記号
 orderOfDifference :微分の階数
def Derivative(function,x,orderOfDifference=1):
import sympy as sp
return sp.simplify(
sp.diff(function,x,orderOfDifference).subs([(x,0)]))
xi=[-Δx,0,Δx]
fi=sp.symbols('f0:{:d}'.format(len(xi)))
Derivative(LagrangePoly(x,xi,fi), x, orderOfDifference=1)
高次精度差分にも対応
2019/07/27オープンCAE勉強会@関西23
xi=[-5*Δx, -4*Δx, -3*Δx, -2*Δx, -Δx, 0, Δx, 2*Δx, 3*Δx, 4*Δx, 5*Δx]
fi=sp.symbols('f0:{:d}'.format(len(xi)))
Derivative(LagrangePoly(x,xi,fi), x, orderOfDifference=1)
xi=[-5*Δx, -4*Δx, -3*Δx, -2*Δx, -Δx, 0, Δx, 2*Δx, 3*Δx, 4*Δx, 5*Δx]
fi=sp.symbols('f0:{:d}'.format(len(xi)))
Derivative(LagrangePoly(x,xi,fi), x, orderOfDifference=2)
xi=[-5*Δx, -4*Δx, -3*Δx, -2*Δx, -Δx, 0, Δx, 2*Δx, 3*Δx, 4*Δx, 5*Δx]
fi=sp.symbols('f0:{:d}'.format(len(xi)))
Derivative(LagrangePoly(x,xi,fi), x, orderOfDifference=10)
片側に重みを付けた差分にも対応
2019/07/27オープンCAE勉強会@関西24
xi=[0, Δx, 2*Δx]
fi=sp.symbols('f0:{:d}'.format(len(xi)))
Derivative(LagrangePoly(x,xi,fi), x, orderOfDifference=1)
xi=[-Δx, 0, Δx, 2*Δx]
fi=sp.symbols('f0:{:d}'.format(len(xi)))
Derivative(LagrangePoly(x,xi,fi), x, orderOfDifference=1)
xi=[-3*Δx, -2*Δx, -Δx, 0, Δx]
fi=sp.symbols('f0:{:d}'.format(len(xi)))
Derivative(LagrangePoly(x,xi,fi), x, orderOfDifference=2)
スタガード格子もいけるっぽいけど要検証
2019/07/27オープンCAE勉強会@関西25
xi=[-Δx/2, Δx/2]
fi=sp.symbols('f0:{:d}'.format(len(xi)))
Derivative(LagrangePoly(x,xi,fi), x, orderOfDifference=1)
xi=[-5*Δx/2,-3*Δx/2,-Δx/2]
fi=sp.symbols('f0:{:d}'.format(len(xi)))
Derivative(LagrangePoly(x,xi,fi), x, orderOfDifference=1)
xi=[-5*Δx/2,-3*Δx/2,-Δx/2,Δx/2]
fi=sp.symbols('f0:{:d}'.format(len(xi)))
Derivative(LagrangePoly(x,xi,fi), x, orderOfDifference=2)
プログラムの改善点
2019/07/27オープンCAE勉強会@関西26
 座標値を数字だけで指定したい
 [-3*Δx,-2*Δx,-Δx,0,Δx]->[-3,-2,-1,0,1]
 これは簡単にできそう
 だがスタガード格子との親和性に難あり
 差分を計算する点をf0としたい
 今は数直線上の一番左が0
 ついでに数字を下付きにしたい
 [-3*Δx,-2*Δx,-Δx,0,Δx]に対応する物理量は
(f0,f1,f2,f3,f4)->(f-3,f-2,f-1,f0,f1)
 関数をもう少しわかりやすく
 微分記号のxと値を計算する座標値xの区別とか

Very helpful python code to find coefficients of the finite difference method