仮眠プログラマーのつぶやき

自分がプログラムやっていて、思いついたことをつぶやいていきます。 2025年からzennに移行

自作ゲームやツール、ソースなどを公開しております。
①ポンコツ自動車シュライシュラー
DOWNLOAD
②流体力学ソース付き
汚いほうDOWNLOAD
綺麗なほうDOWNLOAD
③ミニスタヲズ
DOWNLOAD
④地下鉄でGO
DOWNLOAD
⑤ババドン
DOWNLOAD
⑥圧縮拳(ツール)
DOWNLOAD
⑦複写拳
DOWNLOAD
⑧布シミュレーション
DOWNLOAD
⑨minecraft巨大電卓地形データ
DOWNLOAD
⑩フリュードランダー
デジゲー博頒布α版
DOWNLOAD
⑪パズドラルート解析GPGPU版
DOWNLOAD
⑫ゲーム「流体de月面着陸」
DOWNLOAD

流体力学

流体力学のプログラムを作りたい!補足記事 ナブラ、ベクトル場について

この記事は格子法を説明した流体力学のプログラムを作りたい!その1記事の補足になります。
ナビエ・ストークス方程式の難しさの一つに、次元が増えるごとに速度情報とその偏微分成分が加速度的に増えることが挙げられる。
二次元で考えれば速度情報はx方向速度、y方向速度はもちろん、その2つのx軸方向偏微分、y軸方向偏微分で2*2=4つの成分を考えることになる。それが三次元ではx速度、y速度、z速度がさらにx,y,z軸方向偏微分で3*3=9の成分を考えなくてはいけなくなる。
普通に頭がこんがらがる。

だが避けては通れないので自分用のメモとしても、ここに整理しておこうと思う。

スカラー場とベクトル場

ベクトル場

ベクトル場で分かりやすい例として天気予報等ででてくる風速図
vec
ある地点の風速がベクトルで表示されている。これがベクトル場。

ベクトル表記ようにx方向成分とy方向成分に分離することができる。
vec

x速度成分

vecx

y速度成分

vecy

スカラー場

一方スカラー場は、ある地点の情報が1つの変数で表せられるもの。例えばモノクロ色画像
lena

1ピクセルあたり濃淡の1変数しか情報を持たない。
さきほどの分離した成分
vecx
これもスカラー場と言える。

でここからがややこしいところだが、このスカラー場に∇を作用させるとベクトル場になったりベクトル場に∇を作用させるとスカラー場になったりする。


∇演算子の作用について

作用3パターン

∇(ナブラ)は表記の仕方によって3種類にわかれる。
勾配発散回転
表記∇f∇⋅A∇×A
∇の右に
くるもの
スカラー場ベクトル場ベクトル場
計算結果ベクトル場スカラー場ベクトル場
https://physics-school.com/grad-div-rot/
まずこれを知っていないと理解できない。
ナビエ・ストークス方程式を理解するのに必要なのは左二つの∇fと∇⋅A

∇f


早速∇fという操作をやってみる。

lenasiki0
スカラー場 fは例によってlena画像を使う。

lenasiki1

ここでは、全ピクセルの色情報についてx方向、y方向に差をとっている。具体的にはピクセル位置(x+1,y)から(x,y)の色を引いた値がx方向偏微分の値、(x,y+1)から(x,y)の色を引いた値がy方向偏微分の値ということになる。

そして、結果をみればわかるよう∇fの結果はベクトル場になった。(スカラー場を作用させたらベクトル場になった=情報量が増えている)

∇・A

次に∇⋅Aという操作をしてみる。ここでAがベクトル場でないといけないので
lenasiki2

として計算してみる。
lenasiki3

lenasiki4

ということで結果がスカラー場になった。色の加減算では、灰色(r,g,b=127,127,127)が±0として、白がプラス、黒がマイナスの値として計算している。

改めて2次元平面での∇演算子の定義をみてみると
teigi

とあり、これはベクトルの形である、ということのようだ。
確かに上の表にあるよう ベクトル*スカラー=ベクトルになるし、ベクトル・ベクトル=スカラー になる。


2Dナビエ・ストークス式の移流項をひも解く

ns

自分がしばらく理解できなかったのはこのうち
nabiekao
移流項の部分。


まずカッコ内のところからひも解いてみる。
nabiekaokakko

これは速度場vが∇の右ではなく左にある形だ。なので∇は作用せず、単にベクトルの内積という計算をすればよい。
ここで速度場vのイメージは
v=vecimg2=(vecimg2x,vecimg2y)

こんな感じ。これをまずはv=(vx,vy)とし式を書くと

nabie1
こうなる。もちろんこのvx,vyはスカラー場だ。

そして次にカッコの右側のvを作用させると
nabie2

こんな感じのイメージになる。

ここで速度場の偏微分がでてきた。まずはx方向の偏微分をみてみると
nabie3

こうなる。
ベクトル場の偏微分なので答えもベクトル場になる。
ここでは「速度場のx速度x方向偏微分」と「速度場のy速度x方向偏微分」を要素に持つベクトルを求めているわけだ。

同じように
nabie4

ここでは「速度場のx速度y方向偏微分」と「速度場のy速度y方向偏微分」を要素に持つベクトルを求めている。

くどいようだが重要なのでここを間違いないようにしたい。
    • x速度x方向偏微分
    • y速度x方向偏微分
    • x速度y方向偏微分
    • y速度y方向偏微分
というように4パターンのスカラー場がでてくるのが分かると思う。
これが3次元だと9パターン・・・結構大変ではある。

それでは話を進め、これら4パターンの実際の偏微分の値が求まったとする。

nabie5

これを上の式に代入すると

nabie6


これを成分ごとにまとめ整理すると


nabie7
こうなる。
ここでvx,vyは思い出してもらうと

vecimg2x,vecimg2y

こんな感じのスカラー場であったので、これも結局は1変数の数値。
矢印の長さを数値化すると
vx,vy
こうなる。


したがって上の式は最終的に

nabie8


という計算をすることで求まることが分かった!


一次風上差分のおさらい

流体力学のプログラムを作りたい!その1
で移流項のコードを載せているが、これも上記の計算と同じことをやっていることが分かる。

vx,vyはそれぞれx方向速度、y方向速度が格納されていて、スタガード格子という前提がある。
vx_after,vy_afterには移流項計算後の値が入る。
vxの更新のコード

u=vx[i][j]
v=( vy[i-1][j] + vy[i][j] + vy[i-1][j+1] + vy[i][j+1] )/4

if (u>=0.0)&(v>=0.0){
	vx_after[i][j] = vx[i][j] - u*(vx[i][j] - vx[i-1][j])*Δt - v*(vx[i][j]-vx[i][j-1])*Δt
}

if (u<0 .0)&(v>=0.0){
	vx_after[i][j] = vx[i][j] - u*(vx[i+1][j] - vx[i][j])*Δt - v*(vx[i][j]-vx[i][j-1])*Δt
}

if (u>=0.0)&(v<0 .0){
	vx_after[i][j] = vx[i][j] - u*(vx[i][j] - vx[i-1][j])*Δt - v*(vx[i][j+1]-vx[i][j])*Δt
}

if (u<0.0)&(v<0.0){
	vx_after[i][j] = vx[i][j] - u*(vx[i+1][j] - vx[i][j])*Δt - v*(vx[i][j+1]-vx[i][j])*Δt
}


vy更新のコード

u=( vx[i][j-1] + vx[i+1][j-1] + vx[i+1][j] + vx[i][j] )/4
v=vy[i][j]

if (u>=0.0)&(v>=0.0){
	vy_after[i][j] = vy[i][j] - u*(vy[i][j] - vy[i-1][j])*Δt - v*(vy[i][j]-vy[i][j-1])*Δt
}

if (u<0 .0)&(v>=0.0){
	vy_after[i][j] = vy[i][j] - u*(vy[i+1][j] - vy[i][j])*Δt - v*(vy[i][j]-vy[i][j-1])*Δt
}

if (u>=0.0)&(v<0 .0){
	vy_after[i][j] = vy[i][j] - u*(vy[i][j] - vy[i-1][j])*Δt - v*(vy[i][j+1]-vy[i][j])*Δt
}

if (u<0.0)&(v<0.0){
	vy_after[i][j] = vy[i][j] - u*(vy[i+1][j] - vy[i][j])*Δt - v*(vy[i][j+1]-vy[i][j])*Δt
}

if文でごちゃごちゃしているが、偏微分の計算のところは単に差をとっており、あとはさっきの画像の通りになっていることが分かるはずだ。

流体力学のゲーム完成!「流体 de 月面着陸」

ナビエ・ストークスの方程式を使ったガチ流体ゲーム
「流体de 月面着陸」がついに完成!
ダウンロード



ニコニコ動画

【物理演算】流体 de 月面着陸【ニコニコ自作ゲームフェス5】

smile
(このゲームはニコニコ自作ゲームフェス5に参加しています。)


物理的な正確性はもちろんのこと、今回はゲームとしてもかなり遊べる内容だと思っている!
去年のHSPコンテストでは、ゲームとは呼べないような内容で出してしまったが、あれからかなり進化させている!
ステージ数は1から25面まで増えたし、BGMもいいのを選んだつもり・・。BGMのビートに合わせて出てくるカラフルな粒子は見てるだけでも飽きさせない!
ストーリーもちゃんとある(一応)!。今回はゲーム内容からプロ生ちゃんがかなり役どころとしてしっくりハマってくれた。

新しいビットマップ イメージ
新しいビットマップ イメージ2

ゲームを作っていなければ普通は作らないようなユーザーインターフェースも久しぶりに頑張って作ったが、凝ったものを作るのにやはり非常に時間がかかった。
普通ゲーム製作において多分作ってて楽しい部分でもあるのだろうけど、自分にとっては苦痛でしかない・・。そんな性格だからいつまでたってもゲームが完成してこなかったのだが・・・
ただやはり完成したときのこの達成感は、今までの小物ツールとは段違い!
 
ずっとこんなゲームを作りたかったので本当に完成してよかった!

以下、激流ステージのスクショ






3
2
無題


流体力学のプログラムを作りたい!その3

流体力学に関する記事はかなり久しぶりである。

約束通りCIP法の解説をしたいと思ったが、解説に使う労力があまりに多いため、また最近多忙なため、CIP法のソースの公開と、参考になったリンクを貼るのみとさせて頂く。

今までネット上に落ちているソースを元に独学で流体力学を学んできたが、ついにCIP法の難しさにより本を購入。

CIP法とJavaによるCGシミュレーション 単行本(ソフトカバー) – 2007/2/17
矢部 孝  (著), 尾形 陽一  (著), 滝沢 研二  (著)

といっても私でも理解できたのでかなりやさしいのでは。CIP法のソースが最初から最後まで乗っている解説本。
今回紹介する公開サンプルはそれのHSP移植かつ模倣にとどまる。

以下のスクリーンショットがHSP上で実行したものだ。
cip法流体.rar

cip有限音速
これは、音速が有限で、混合密度性流体である。
「cipcup格子法低音速、混合密度.hsp」の実行画面である。

画面の意味するものは、明るい部分が密度の高いもの、暗い部分が密度の低いもので、重力なし、境界は反対側につながっていて、初期条件は左半分が高密度で真下に向かう速度を持ち、右半分が低密度で真上に向かう速度を持つ。
流体を妨げる障害物はない。


そして以下のものは、本のソースを参考に自分で改変し、音速を無限、密度一定で非圧縮性流体にしたものだ。
当然CIP法なので、数値拡散がとても少なく、サンプル内のレンダリング動画を見てもらえれば分かる通り、160×120格子でも細かい渦が消えずに残っているのが分かる。
(もしCIP法でないと細かい渦が拡散してしまう)
これは障害物(壁)を3枚、左側に配置してある。
cip無限音速

壁の境界条件をどの段階で設定すればいいのかわからず、移流項計算、圧力項計算の前後に無駄に多くの壁境界条件設定をしているが、こんなにする意味は無い・・お陰で汚いソースである・・・・。



最後にお世話になりまくったサイト or PDFのご紹介。
特に参考になったリストは 
以下、その他参考になったサイト or PDF
http://www.4gamer.net/games/000/G000000/20070926041/
http://cfdsim.web.fc2.com/download_source.html
http://www.civil.hokudai.ac.jp/yasu/home.htm
http://www.exocortex.com/plugins/slipstreamvx
http://grantkot.com/Multiphase/Liquid.html
http://grantkot.com/MPM/Liquid.html
http://ja.wikipedia.org/wiki/CIP%E6%B3%95
http://news.mynavi.jp/articles/2007/10/09/cedec01/001.html
http://art-science.org/journal/v3n2/v3n2pp159/artsci-v3n2pp159.pdf
http://www.csp-consortium.org/action/csp_200910/nakane_200910.pdf

流体力学のプログラムを作りたい!その2

「その1」で流体の挙動を最低限シミュレートできたはずである。
次は少しステップアップして、流体の可視化と、流体の中に存在する「動く障害物」に対してどう処理するかというところを見ていこう。


【可視化 
これはかなり独学なところもあったが、基本は「渦度」を画面に出力すればらしい映像が得られる、という認識で間違いないと思う。

百聞は一見に如かず
画像左半分が128*128格子の渦度の可視化画面
o0512025611361324083

ちなみに右は圧力の可視化。

渦度を求める方法はいろいろすっ飛ばした結果以下のとおり。

a3a

渦度=(A+B+C+D)/4


AとCは、x速度の定義点におけるy速度、BとDはy速度の定義点におけるx速度を表している。
それぞれの速度は、定義点から外れているため、近傍4点から平均を取ってくるなどの方法をとってうまく計算する。
渦度の画面出力は、マイナス値を赤、プラス値を緑などとし、値の大きさと色をフィーリングで比例させればだいたい↑のようなレンダリング結果が得られるだろう。


あと計算高速化のアイディアとして、細かいことをきにしなくていいなら、平均処理をしないで渦度を求めることだってできる。
渦度=周囲4点の時計(又は反時計) 回りへ の速度を平均したもの
とラフに考えれば、格子の「角」における渦度はどうなるか。



a3b

これならx速度、y速度の値をそのまま使えるため計算コストが低く抑えられる。



次に、渦度だけでは画面が寂しいというのなら、幾万ものドット粒子を浮遊させるというのもよくある良い可視化手段である。
srtnh


粒子の移動速度は当然流体の速度と同じで、近傍の速度定義点から求めてやればいい。
a3c


ちなみに3次補間と比べ精度の低い線形補間(バイリニア補間)で求めても粒子の動きは見た目問題ないほど非常になめらかであった。


【流体の中で動く障害物  
流体の計算の醍醐味は、障害物の存在下で流体がどのような挙動を取るのかを見て楽しむことにある。

asdfgv

このように、移動する障害物を流体力学の計算にどのように組み込んだらいいのか。
ここでもスタガード格子ということで考えてみる。

まず格子ごとに、この格子が障害物か流体かを識別するための配列変数「kabe」、を作る。
可動障害物でも静止障害物でも、ある時刻tにおけるある格子が障害物であるときkabeは0の値を取り、流体であるとき1の値を取る。

次に、流体内に、移動しない障害物があるときの処理をおさらいする。
このとき、障害物の境界で強制的に流速を0にすれば良かったはずだ。
一方、障害物が移動することを考えると。
ここのサイトによると、このときの境界の条件は、流速=障害物の移動速度、となる。


最初に、一番単純化して格子の長さが1m×1mとして、1格子からなる最小単位の障害物がx方向に1m/sで動くプログラムを考える。
またΔt=0.2sとする。

最初のt=0.0sの時点では当然こうなる。
a5a
濃い灰色と薄い灰色の速度は固定である。そのため移流や圧力の影響を受けない。
また変数kabeは1と0だけで定義できる状態だ。


さて次にt=0.2sではどうなるか。
a5b
理想ではこうしたいところではあるが、障害物の側面が速度定義点から外れる上、1格子内に壁成分と流体成分が混じっているため、 変数kabeは1と0だけで定義できない状態となる。 

つまり正解はこうなる。
a5a
これはt=0.0sの状況と全く同じである。
これでいいのかと思うかもしれないが、これで良い!


そしてt=1.0sまでタイムステップを進めたとき、↓のようにすれば良い。
a5c


ということはt=0.8sの時点で一番誤差が大きくなり、1m^2の面積の格子に0.8m^2分の誤差流体が入っていることになる。
こんな言い方をしたくないが、こればかりは仕方がない。


そしてつぎに斜め移動の場合を考える。例えば斜め右下30°に1.0m/sの速度を持つ壁としたら、境界条件はx速度=0.87m/s 、y速度=0.5m/s
に固定すれば良い。
そこら辺はさっきのリンク ここ の下の方の説明画像を見ればわかると思う。



ところで話はかわり、タコマ橋という有名な橋があるのだが、これは完成後すぐに横風によるカルマン渦が発生して上下に振動し、ついには崩壊してしまったアメリカの橋だ。
このように、障害物(壁)が流体の力を受けて移動する場合の計算は、ポアソン方程式で圧力計算が終わったあとの障害物周囲の圧力を障害物に働く力として計算すればよい。


これで可動障害物の話は終わり。





最後に。

iPhoneアプリで面白い物を見つけた。
「wind tunnel lite」という格子法で計算されているCFDアプリだ。
指で流体を動かせる機能も付いている。
当然フルにスペックを使って計算されているはずなので電池消耗アプリとしていいかもしれない・・・(そんな馬鹿な)

そして風上差分の大欠点である数値拡散を抑えるため、CIP法の勉強を最近始めたので 
次の記事にはCIP法についてわかったことを書いていこうと思う。

今回お世話になった参考文献。
http://news.mynavi.jp/articles/2007/10/09/cedec01/006.html
(やはり西川善司さんの講座は参考になる)


次回:流体力学のプログラムを作りたい!その3

流体力学のプログラムを作りたい!その1

素人が独学でやって書いた記事です。適宜参考文献リスト等も参照下さい。

対象の読者

対象は個人ゲーム製作やCG関係で、流体の精度よりまずは動くプログラムが欲しい、学びたいという方。なので今回は一番単純で簡単な動く流体力学プログラムをつくろうと言うのが目標だ。
最終的に
ezgif-3-116efdef2c5f
こんな感じのプログラムが動くところまでやる。

導入

まず2Dでも3Dでも流体力学で避けて通れないのはナビエストークスの式(NS式)([1]wikipedia より)

ns


(画像はwikipediaより)
左辺 - 第1項 : 時間微分項、第2項 : 移流項(対流項)
右辺 - 第1項 : 圧力項、第2項 : 粘性項(拡散項)、第3項 : 外力項


ちなみにこれでも簡単なほうらしい。これだけだと未知数が1つおおいままなので、∇・v = 0 という連続の式を連立して解けということだ(vは速度ベクトル場)

nsdiv0

(画像はwikipediaより)
三角形の逆みたいな記号があるけどなんだこれは?内積の掛け算で使う「・」があるけど、ベクトルの計算式とかあるのか?と、私は最初見た時いろいろわからないとこだらけで、結局理解するのに2ヶ月くらいかかった。

実際のとこ∇をよく理解せずプログラムを組んで動いてしまったので、この記事を書いていた当時は「∇とかわからなくても四則演算でプログラムは作れる」「今はとりあえず動くプログラムを作ることが最優先」とか思っていた。(やっぱそれじゃダメだろと思ったので補足記事書きました)

粒子法か格子法か

今回の記事では格子法についてのみ解説を行う。
流体計算は大きく粒子法と格子法の2つがあり、前者は流体を粒子として計算、後者は流体の入った空間を何百という格子で分割して各々の格子の圧力や速度を計算するというもの。

どうも格子法の方が簡単だということがわかった。(粒子法専門の方には大変失礼ですが・・・)
ちなみに粒子法もたまにゲームやCGに組み込まれているのを見る。Position Based Fluidsというものらしい。

格子法で流体力学の完成形

[13]http://www.youtube.com/watch?v=4Ov6tguRr8E

格子法では、2Dの場合、各々の格子の中の圧力、x速度、y速度を時間ステップごとに更新していくものだ。

格子の種類

さて格子法でやると決めた後はどの種類の格子にするかを決める必要がある。大きく分けて通常格子、コロケート格子とスタガード格子がある。

通常格子

tuujou

コロケート格子

a2

スタガード格子

a3

スタガード格子の方が少し複雑で難しい。が、プログラムを作っている途中でわかったが最終的にこっちの方が楽になる。

というわけでスタガード格子でやることに決定!

これは東工大のサイトが参考になった。
[2.1]http://www.eto.titech.ac.jp/contents/sub03/chapter08-1.html

で、次にスタガード格子の速度と圧力をNS式でどのように離散化して時間ステップを進めるかということを考える。

全体像と変数

a4

流体格子の分割は10*10格子とする。
壁を含めると全計算領域は12*12格子である。
必要な変数はまずは圧力変数 「p」 、これは12*12格子

次にx速度変数 「vx」 、これは13*12
逆にy速度変数 「vy」12*13
また後々、ダイバージェンス(∇・v=0)の計算で使う各格子での「発散」値を格納するデータ 「s」 も必要。これは圧力と同じく12*12
「p」「vx」「vy」の一時的な記憶変数として「p_after」「vx_after」「vy_after」もそれぞれ同じ配列要素で用意。


そして、以降話を分り易くするためにΔxやΔyは1.0として考える (少し衝撃的な決断だが)なので以降の式にΔxΔyが省略されているがそこはご了承を。
まともにプログラムが動いてからΔxやΔyをいじってみればいい。またΔtは1.0にすると計算が発散するので、今は0.1~0.2くらいだと思ってもらえればいい。

フローチャート

フローチャートはこうなる

ふろーちゃーtp

上から順にみていこう

①移流粘性項

b0

最初に移流と粘性を分けて考える。

移流項

移流とは、流れる前の速度変数 vx vy をつかって一定時間流れた後の速度を求める、ということだ。(と思ってましたがこれはあくまで陽解法の説明です。陰解法では...略)
流れ後の速度変数をそれぞれvx_after   vy_afterとすると

(i,jは配列の要素、整数)
u=vx[i][j]
v=( vy[i-1][j] + vy[i][j] + vy[i-1][j+1] + vy[i][j+1] )/4

if (u>=0.0)&(v>=0.0){
	vx_after[i][j] = vx[i][j] - u*(vx[i][j] - vx[i-1][j])*Δt - v*(vx[i][j]-vx[i][j-1])*Δt
}

if (u<0 .0)&(v>=0.0){
	vx_after[i][j] = vx[i][j] - u*(vx[i+1][j] - vx[i][j])*Δt - v*(vx[i][j]-vx[i][j-1])*Δt
}

if (u>=0.0)&(v<0 .0){
	vx_after[i][j] = vx[i][j] - u*(vx[i][j] - vx[i-1][j])*Δt - v*(vx[i][j+1]-vx[i][j])*Δt
}

if (u<0.0)&(v<0.0){
	vx_after[i][j] = vx[i][j] - u*(vx[i+1][j] - vx[i][j])*Δt - v*(vx[i][j+1]-vx[i][j])*Δt
}

ここでuvは、代入が行なわれる変数vx_after[i][j]の位置にあるx速度、y速度を意味する。よってu=vx[i][j]でいいがは、上のスタガード格子の性質上、x速度の定義点とy速度の定義点がずれているため
v=( vy[i-1][j] + vy[i][j] + vy[i-1][j+1] + vy[i][j+1] )/4

と、4つの近くの格子点の情報を足して4で割る必要がある。


ここまででvxの移流の話が終わり、次はvyの移流である。
上の計算でvxvyを置き換えるだけでいいが

u=( vx[i][j-1] + vx[i+1][j-1] + vx[i+1][j] + vx[i][j] )/4
v=vy[i][j]
とする。
したがって

u=( vx[i][j-1] + vx[i+1][j-1] + vx[i+1][j] + vx[i][j] )/4
v=vy[i][j]

if (u>=0.0)&(v>=0.0){
	vy_after[i][j] = vy[i][j] - u*(vy[i][j] - vy[i-1][j])*Δt - v*(vy[i][j]-vy[i][j-1])*Δt
}

if (u<0 .0)&(v>=0.0){
	vy_after[i][j] = vy[i][j] - u*(vy[i+1][j] - vy[i][j])*Δt - v*(vy[i][j]-vy[i][j-1])*Δt
}

if (u>=0.0)&(v<0 .0){
	vy_after[i][j] = vy[i][j] - u*(vy[i][j] - vy[i-1][j])*Δt - v*(vy[i][j+1]-vy[i][j])*Δt
}

if (u<0.0)&(v<0.0){
	vy_after[i][j] = vy[i][j] - u*(vy[i+1][j] - vy[i][j])*Δt - v*(vy[i][j+1]-vy[i][j])*Δt
}

これを計算領域内すべてのi jで計算する。
全計算が終わったならvx vyvx_after vy_afterを代入する。計算領域はイメージ図1で言う緑の部分と接するか重なるところだけ。
つまり最外層の1層だけは参照されるのみで代入は行なわれない。

またまた東工大のサイトが参考になった。
[2.2]http://www.eto.titech.ac.jp/contents/sub03/chapter03.html

移流のイメージ

移流のイメージとしては例えば以下の画像の黄緑の部分の速度が(1.0,1.0)で白の部分が(0.0,0.0)、話を分り易くするためにΔt = 1.0だとしたらこうなる。

移流


しかし実際はΔtや速度の値が 0.148 とか0.009とか不規則な数であり、これにより移流計算するごとに速度の情報が崩れまくる。
特に一次風上差分法はとてもその形が崩れやすい。黄緑色の領域がじわじわ薄くなって広がってしまうイメージだ。

iryuu2

数値流体力学ではこのプロファイルの崩壊をいかに防いで移流計算するかということに、そのシミュレーション精度がかかっている。
精度を上げるためには、Cubic セミ・ラグランジアン法やCIP法を用いると良いようだ。
今回は一番簡単な一次風上差分を採用した。

このサイトの座標のところがすごく理解の助けになった。(リンク切れ)
[3]http://www.eco.zaq.jp/env_univ/euler.html

粘性項

次に粘性であるがこれはただ単に、1つの点の速度が周囲4つ点に拡散するという意味である。
どのくらいの割合で拡散するかはRe(レイノルズ数)で決まる。
vx_after[i][j]=vx[i][j]-1/Re*(vx[i+1][j]+vx[i][j+1]+vx[i-1][j]+vx[i][j-1])*Δt
vy_after[i][j]=vy[i][j]-1/Re*(vy[i+1][j]+vy[i][j+1]+vy[i-1][j]+vy[i][j-1])*Δt

全計算が終わったならvx vyvx_after vy_afterを代入する。

②外力項

b1
マウス操作等で流体に速度をつけたいのなら、ここで速度に加算する。

また、壁と接する速度定義点は本来 速度=0.0でないといけないはずだが、前行程の移流粘性項で近隣の速度定義点から影響を受け0でなくなってしまった定義点があった場合に、ここで0に戻す

そして流出境界などもここで設定する。

a5

またまた東工大のサイトが参考になった!
[2.3]http://www.eto.titech.ac.jp/contents/sub03/chapter08-4.html

③ダイバージェンス計算

b2

次に速度からダイバージェンス=発散量を求める。

「s」はダイバージェンスを格納する配列変数。

s[i][j]=( -vx[i][j] -vy[i][j] +vx[i+1][j] +vy[i][j+1] )/Δt

ここでは1格子の空間から出ていく(or入ってくる)水の量をすべてのi,jで計算している。

divsamp

スタガード格子を採用したので、この矢印を足し引きすれば発散は簡単に求まる。


この発散量を求めていく意味なのだが、記事の上の方で∇・v = 0 を連立するという話につながる。
∇・v = 0 つまり、どのi,jでも s[i][j]0.0になる必要があると言っている。

移流や粘性、外力項などで速度情報が何度も書き換えられたため、今のままでは∇・v = 0 を満たしていないと考えられる。これでは非圧縮の条件が満たされない。

これをいっきに解決するのが次の④である。

④圧力のポアソン方程式

b3


ここが山場だった。
上で求めた各格子のダイバージェンスから圧力を算出したい。ダイバージェンスから圧力を求めるには、以下の反復法のSOR法を使うといい。

SOR法

p[i][j]=(1.0-ω)*p[i][j] + ω/4*(p[i-1][j] + p[i+1][j] + p[i][j-1] + p[i][j+1] - s[i][j])

ωは加速係数といって自分の経験則では1.7~1.9で一番収束が早くなる。格子数が少なめなら1.7、多めなら1.9が良い(気がする)。
ΔxΔyを変えたい場合は
[2.5]http://www.eto.titech.ac.jp/contents/sub03/chapter07.html
の(7-4)式を参照のこと

これを計算領域内のi,jで、何回も反復することで収束していく。だいたい100回とか1000回とか?
当然初期p値が解から遠ければ、収束までの反復回数は増える。ここが一番計算コストのかかる部分なので、ここをいかに効率良く解くかで、全体の処理速度が決まってくるといってもいい。
反復回数は多分計算領域の大きさや許容できる誤差によるところもある。

さて、ここで2つ問題がある。

1.障害物の扱いについて

最外層以外にも任意の場所に障害物を置きたいことがある。ある流体格子の圧力を求めるとき隣の格子が壁の場合、SORの計算の前にその壁に自分の圧力をコピーしておく

poasson

薄茶色が壁、白が流体の存在するところである(さっきと色が逆ですみません)。

このとき圧力定義点の白丸はSOR法で代入も参照も行なわれる。
黄緑色の点は参照のみされる。
黒丸は参照すら行なわれない。

Bの圧力をSOR法で計算しようとする時、Bの上下左右の圧力が参照されるため、Cの圧力が参照されることになるが、Cは壁内にあるためここにはBの圧力を使う。よってBの圧力計算をする前に、CにBの圧力を代入しておく。次にAの圧力をSOR法で計算しようとする時、同じようにCにAの圧力を代入してから計算する。

結果的に黄緑色の部分の本来の圧力というものは無視される。まぁ壁の中に圧力なんてないわけなんだが。

以上の作業はSORの「ループ内」で行う必要がある。「最初に1回だけ壁に圧力をコピーしておいてあとはループ計算を流す」ではうまくいかないはず。

ここでも東工大のサイトが一番参考になった。
[2.6]http://www.eto.titech.ac.jp/contents/sub03/chapter08-2.html

この「壁」を考慮したやり方は他のサイトや書籍など調べてもあまり書いてないものの、発散から圧力を求めるときの連立一次方程式を考えると、このやり方が一番合理的だと思うので自分はこの方式を支持する。


さらにこの東工大の記事によると、格子の壁情報を格納する変数(trueなら壁 falseなら流体とか)を作ると便利だということだ。確かにそうすることで、今自分が壁なら圧力計算をskipという処理が書きやすい。

また余談になるが、SOR法は参照する4つの圧力情報のうち、計算後の情報と計算前の情報の2種類が混じり合っているように見えるがそれで良い。(ガウス・ザイデル法のアレと同じ話)

2.SOR法を何回反復するか

「p_afterに現在計算中の圧力情報を、「p」に1ループ前の圧力情報を格納しているとしてp[i][j] - p_after[i][j]絶対値が、すべてのi,jで一定値以下なら「収束した」と判断する。

ただこんな作業を毎ループやっていると、計算量2倍になり非効率なので、10ループに1回にしたり、i,jを1つ飛ばしでやるなど工夫する。

また残差計算が面倒ならループ数を固定してしまっても良いと思われる。計算の最初のフレームだけ誤差が大きそうだから最初だけ1000回ループとして、それ以降はレンダリングやゲーム等のフレームレート維持のため10ループ固定、などという案も現実的だ。

⑤修正項

b4


圧力により速度が修正される。ここで、修正されるというのは、∇・v = 0 になるように速度が調節されるということである。
rhs2

スタガード格子の利点はここにも。圧力定義点がx速度点、y速度点を挟み込むように配置されているので修正はとても簡単だ。

vx[i][j]-=( p[i][j]-p[i-1][j] )*Δt
vy[i][j]-=( p[i][j]-p[i][j-1] )*Δt


⑤はこれだけ

これでやっと全ての処理が終わり。
vx vy p の値全てが次のタイムステップに進んだことになる。修正項のあとは、可視化ルーチンに回すなり、ランダムに配置した粒子の移動ルーチンに回すなり好きにすればいい。
そして処理は①へ

まとめ

こうして①~⑤を更新してSTEPを進めていくのだが、一発でプログラムが予想した挙動を示してくれるとは限らない。最後に注意すべき点がいくつかある。

まずコーディングミス。実装量的にそこそこな量になるし、一つでもミスると結果がアベコベな値になる。かなり慎重に書いていく必要がある。特にxとyの取違い

次に移流項。この記事で紹介した方法ではΔtを0.1~0.2と適度に細かくしておけば発散しない。(詳しくはCFL条件で検索)

次に圧力のポアソン方程式の項。圧力pを毎STEPごとに初期化せず、前STEPのを使いまわすほうが結果的に早く収束することが分かっている(warm startingとかいう手法だった気がする)。ただしその場合、何STEPか計算していくと数値誤差でpの値が正か負に発散していくことがある。pの値は前STEPのpにのみ依存するため誤差の蓄積が原因となる。pは結局その「xy方向差分」だけを計算に使うのでpが発散するまで気づかない。なのでpを使いまわすのであれば「ガス抜き」のような処理がどこかに必要。平均値が0.0になるような処理を。

もう少し突っ込んで話すと、x,yで周期境界条件を作ったとき、圧力の逃げ場がないので数値誤差が積み重なりpが正か負に発散する。境界条件にノイマン条件がどこか1か所でもあればそこが圧力の逃げ場になり発散はしない(と自分では理解している)。


ちなみに、この流体力学のサンプルソースを配布している。
コッチ(Python版)からどうぞ。
ezgif-3-116efdef2c5f

おしまい

とりあえずNS式の意味を深く考えず、ここをこうすればいいと計算の手順の部分だけ書いてきたが、一応これで動くはずである!正直、最初に流体力学をやろうと思ったときは「∇」とか「ベクトル場」とか「ラプラシアン」とか全く分からなかったが、見よう見まねでプログラムを動かそうとしているうちにだんだんと式や単語の意味が分かってきた。

私は、まずは動くプログラムを作ってから式や単語の意味を考えるのが一番効率がいいと思う。なのでとにかく動かすことを最優先で書いてきた。
2012年当時、ネットで参考になる資料はいくつかあったものの自分の理解できるレベルのものは少なく、東工大のサイトに出会わなかったらずっとCFDプログラムが作れずにいただろう。本当に感謝

参考文献

参考にしたサイトやらなにらや
[1]http://ja.wikipedia.org/wiki/%E3%83%8A%E3%83%93%E3%82%A8-%E3%82%B9%E3%83%88%E3%83%BC%E3%82%AF%E3%82%B9%E6%96%B9%E7%A8%8B%E5%BC%8F
[2]http://www.eto.titech.ac.jp/contents/
[3]http://www.eco.zaq.jp/env_univ/euler.html
[4]http://d.hatena.ne.jp/etopirika5/20110425/1303740299
[5]http://www.bee-www.com/log.htm
[6]http://journal.mycom.co.jp/articles/2007/10/09/cedec01/001.html
[7]http://www.4gamer.net/games/000/G000000/20070926041/screenshot.html?num=012
[8]http://fluid.me.seikei.ac.jp/lecture/cfd/cfd-08-NS-algorithm.pdf
[9]http://staff.aist.go.jp/kogaki.t/Dissertation/PDF_files/chap3.pdf
[10]http://homepage.mac.com/catincat/java/index.html
[11]http://oshiro.bpe.es.osaka-u.ac.jp/people/staff/imura/lecture/2007cg/fire071220.pdf
[12]http://www.youtube.com/watch?v=6rPL-QkUFf8&feature=related
[13]http://www.youtube.com/watch?v=4Ov6tguRr8E

他、個人ブログの方が書いたゲームetcに格子法流体を実装するのに参考になりそうなサイト(2020/6更新)
http://el-ement.com/blog/2015/03/16/gpu-fluid/
https://edom18.hateblo.jp/entry/2020/02/13/104744

2018/10追記
東工大の流体のページがいつからか閉鎖させておりアクセスできない状態になっていた。ただWARPという国内の教育機関などに特化したwebアーカイブサイトに保存されていた!!
見たい方はコチラで「コンピュータシミュレーションによる流体解析(発展編)」と検索
アーカイブ自体は私も保存しているのでもしほしい方はコメントを(教育目的のサイトだし著作権的に個人所有は問題ないはず・・)


次回:流体力学のプログラムを作りたい!その2
プロフィール

toropippi

記事検索
アクセスカウンター

    QRコード
    QRコード
    • ライブドアブログ