More Related Content Similar to OpenFOAM -空間の離散化と係数行列の取り扱い(Spatial Discretization and Coefficient Matrix)- Similar to OpenFOAM -空間の離散化と係数行列の取り扱い(Spatial Discretization and Coefficient Matrix)- (20) More from Fumiya Nozaki (13) OpenFOAM -空間の離散化と係数行列の取り扱い(Spatial Discretization and Coefficient Matrix)-2. 2
Disclaimer
“This offering is not approved or endorsed by
OpenCFD Limited, the producer of the
OpenFOAM software and owner of the
OPENFOAM® and OpenCFD® trade marks.”
4. 4
目次
Chapter 1:離散化の概要
Chapter 2:係数行列の表示(ソルバーのカスタマイズ)
Chapter 3:係数行列の表示(gdbOF の使用)
Chapter 4:空間の離散化
Chapter 5:境界条件の離散化
Chapter 6:simpleFoam の解説
Chapter 7:pUCoupledFoam の解説
参考文献
6. 6
非圧縮性流体の運動方程式
非圧縮性流体の運動は,次の方程式で記述されます.
• 質量保存則(連続の式)
𝛻 ∙ 𝒖 = 0
• 運動量保存則
𝜕𝒖
𝜕𝑡
+ 𝒖 ∙ 𝛻 𝒖 = −
1
𝜌
𝛻𝑝 + 𝛻 ∙ 𝜈𝛻𝒖
Navier–Stokes 方程式と呼ばれるこの方程式を解析的に解くことができれば,
各時刻 𝑡,各場所 𝒙 での流速 𝒖 𝒙, 𝑡 ,圧力 𝑝 𝒙, 𝑡 の値を知ることができます
が,
これを数学的に解く問題は,アメリカのクレイ数学研究所からその解決に100
万ドルの懸賞金がかけられている数学上の未解決問題 [1] の一つであり,非常
に難しい問題です.
14. 14
係数行列の性質
1 2 3
6 5 4
7 8 9
2番目のCVと3番目のCVが共有するフェイスを見てみます.
注目するフェイスが離散化に使われるのはこの2ケースです.
2 3 2 3
(ⅰ)2番目のCVで離散化 (ⅱ)3番目のCVで離散化
18. 18
簡単な例で見てみましょう!
-1- -2- -3-
-6- -5- -4-
-7- -8- -9-
(数字はセル番号)
2番目のセルでの離散化式における5番目のセルの係数
𝐴 𝑙𝑜𝑤𝑒𝑟𝐴𝑑𝑑𝑟 𝑘 𝑢𝑝𝑝𝑒𝑟𝐴𝑑𝑑𝑟 𝑘 = 𝑢𝑝𝑝𝑒𝑟 𝑘
5番目のセルでの離散化式における2番目のセルの係数
𝐴 𝑢𝑝𝑝𝑒𝑟𝐴𝑑𝑑𝑟 𝑘 𝑙𝑜𝑤𝑒𝑟𝐴𝑑𝑑𝑟 𝑘 = 𝑙𝑜𝑤𝑒𝑟 𝑘
3×3のメッシュ 係数行列
このフェイスの
番号が 𝑘 だと
すると
19. 19
owner セルと neighbour セル
OpenFOAMでは,内部フェイスの両側にある2つのセル(CV)の内,
• 番号が小さい方をそのフェイスの owner セル
• 番号が大きい方をそのフェイスの neighbour セル
と定義しています.
内部フェイスに関して,lduAddressing クラスでは,
𝐮𝐩𝐩𝐞𝐫𝐀𝐝𝐝𝐫 k > 𝐥𝐨𝐰𝐞𝐫𝐀𝐝𝐝𝐫 k
と定義されているため,
• 𝐥𝐨𝐰𝐞𝐫𝐀𝐝𝐝𝐫 k : 𝑘 番目のフェイスの owner セル番号
• 𝐮𝐩𝐩𝐞𝐫𝐀𝐝𝐝𝐫 k : 𝑘 番目のフェイスの neighbour セル番号
という関係が成り立ちます.
23. 23
参考にしたテキスト
H.Versteeg, W.Malalasekera 著
『An Introduction to Computational Fluid Dynamics:
The Finite Volume Method』
有限体積法について,理論から計算例
の紹介までとても内容が豊富です.
記述も丁寧なので,学生さんにもおす
すめです.
著者のホームページ
• Henk Versteeg先生
http://www.lboro.ac.uk/departments/mechman/staff/henk-versteeg.html
• W.Malalasekera先生
http://lupo.lboro.ac.uk/staff/malalasekera.html
お二人とも英国のラフバラー大学の先生です.
27. laplacianFoam ソルバーのフォルダをコピーして,mylaplacianFoam フォ
ルダを作成します.
mylaplacianFoam フォルダに移動し,laplacianFoam.C ファイルの名前を
変更します.
Make フォルダ内の files ファイルの内容を変更します.
27
作業1:準備
$ sol
$ cd basic
$ cp –r laplacianFoam mylaplacianFoam
$ cd mylaplacianFoam
$ mv laplacianFoam.C mylaplacianFoam.C
laplacianFoam.C
EXE = $(FOAM_APPBIN)/laplacianFoam
mylaplacianFoam.C
EXE = $(FOAM_USER_APPBIN)/mylaplacianFoam
変更前
変更後
28. 28
作業2:mylaplacianFoam.C の変更
#include "fvCFD.H"
#include "simpleControl.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
int main(int argc, char *argv[])
{
#include "setRootCase.H"
#include "createTime.H"
#include "createMesh.H"
#include "createFields.H"
simpleControl simple(mesh);
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
Info<< "¥nCalculating temperature distribution¥n" << endl;
while (simple.loop())
{
Info<< "Time = " << runTime.timeName() << nl << endl;
while (simple.correctNonOrthogonal())
{
solve
(
fvm::ddt(T) - fvm::laplacian(DT, T)
);
}
#include "write.H"
Info<< "ExecutionTime = " << runTime.elapsedCpuTime() << " s"
<< " ClockTime = " << runTime.elapsedClockTime() << " s"
<< nl << endl;
}
Info<< "End¥n" << endl;
return 0;
}
// ************************************************************************* //
mylaplacianFoam.C の内容(下記)を次ページのように変更します.
変更前
29. 29
作業2:mylaplacianFoam.C の変更
#include "fvCFD.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
int main(int argc, char *argv[])
{
#include "setRootCase.H"
#include "createTime.H"
#include "createMesh.H"
#include "createFields.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
Info<< "¥nCalculating temperature distribution¥n" << endl;
while (runTime.loop())
{
fvScalarMatrix TEqn(fvm::laplacian(k, T));
TEqn.solve();
forAll(T, cellI)
{
Info<< "X = " << mesh.C()[cellI].component(vector::X)
<< ", T = " << T[cellI] << endl;
}
runTime.writeAndEnd();
}
Info<< "End¥n" << endl;
return 0;
}
// ************************************************************************* //
変更後の mylaplacianFoam.C ファイルです.
𝑑
𝑑𝑥
𝑘
𝑑𝑇
𝑑𝑥
= 0 を離散化して解く部分です.
各セル中心の X 座標と
そこでの温度 T を
書き出します.
変更後
30. 30
作業3:createFields.H の変更
Info<< "Reading field T¥n" << endl;
volScalarField T
(
IOobject
(
"T",
runTime.timeName(),
mesh,
IOobject::MUST_READ,
IOobject::AUTO_WRITE
),
mesh
);
Info<< "Reading transportProperties¥n" << endl;
IOdictionary transportProperties
(
IOobject
(
"transportProperties",
runTime.constant(),
mesh,
IOobject::MUST_READ_IF_MODIFIED,
IOobject::NO_WRITE
)
);
Info<< "Reading diffusivity DT¥n" << endl;
dimensionedScalar DT
(
transportProperties.lookup("DT")
);
createFields.H の内容(下記)を次ページのように変更します.
変更前
31. 31
作業3:createFields.H の変更
Info<< "Reading field T¥n" << endl;
volScalarField T
(
IOobject
(
"T",
runTime.timeName(),
mesh,
IOobject::MUST_READ,
IOobject::AUTO_WRITE
),
mesh
);
Info<< "Reading transportProperties¥n" << endl;
IOdictionary transportProperties
(
IOobject
(
"transportProperties",
runTime.constant(),
mesh,
IOobject::MUST_READ_IF_MODIFIED,
IOobject::NO_WRITE
)
);
Info<< "Reading diffusivity k¥n" << endl;
dimensionedScalar k
(
transportProperties.lookup(“k")
);
初期・境界条件を
ファイルから読み
込み,変数Tを定
義しています.
熱伝導率 k を
transportProperties ファイルから
読み込みます.
問題設定に合わせて,
記号を DT から k に
変更しています.
変更後の createFields.H ファイルです.
変更後
34. 34
作業4:確認計算
Create time
Create mesh for time = 0
Reading field T
Reading transportProperties
Reading diffusivity k
Calculating temperature distribution
DICPCG: Solving for T, Initial residual = 1, Final residual = 1.21266e-16, No Iterations 1
X = 0.05, T = 140
X = 0.15, T = 220
X = 0.25, T = 300
X = 0.35, T = 380
X = 0.45, T = 460
End
計算はすぐに終了し,ターミナル画面に次のような出力があります.
セル中心の X 座標とそこでの温度 T の計算結果です.
ターミナル画面への出力
36. 36
作業5:mylaplacianFoam.C の変更
mylaplacianFoam.C を下記のように変更します.
#include "fvCFD.H“
#include "simpleMatrix.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
int main(int argc, char *argv[])
{
#include "setRootCase.H"
#include "createTime.H"
#include "createMesh.H"
#include "createFields.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
Info<< "¥nCalculating temperature distribution¥n" << endl;
while (runTime.loop())
{
fvScalarMatrix TEqn(fvm::laplacian(k, T));
TEqn.solve();
forAll(T, cellI)
{
Info<< "X = " << mesh.C()[cellI].component(vector::X)
<< ", T = " << T[cellI] << endl;
}
#include "matrixWrite.H"
runTime.writeAndEnd();
}
Info<< "End¥n" << endl;
return 0;
}
// ************************************************************************* //
赤字の2行を追加します.
37. 37
作業6:matrixWrite.H ファイルの作成
matrixWrite.H の内容は下記の通りです(あと2ページ続きます).
// Investigate the Coefficient Matrix
label Nc = mesh.nCells(); //Total number of cells
simpleMatrix<scalar> A(Nc); //Coefficient matrix
// Initialization of matrix
for(label i=0; i<Nc; i++)
{
A.source()[i] = 0.0;
for(label j=0; j<Nc; j++)
{
A[i][j] = 0.0;
}
}
行列Aを作成して,初期化しています.
matrixWrite.H Part1
38. 38
作業6:matrixWrite.H ファイルの作成
// Assigning diagonal coefficients
for(label i=0; i<Nc; i++)
{
A[i][i] = TEqn.diag()[i];
}
// Assigning off-diagonal coefficients
for(label faceI=0; faceI<TEqn.lduAddr().lowerAddr().size(); faceI++)
{
label l = TEqn.lduAddr().lowerAddr()[faceI];
label u = TEqn.lduAddr().upperAddr()[faceI];
A[l][u] = TEqn.upper()[faceI];
A[u][l] = TEqn.upper()[faceI];
}
// Assigning contribution from BC
forAll(T.boundaryField(), patchI)
{
const fvPatch &pp = T.boundaryField()[patchI].patch();
forAll(pp, faceI)
{
label cellI = pp.faceCells()[faceI];
A[cellI][cellI] += TEqn.internalCoeffs()[patchI][faceI];
A.source()[cellI] += TEqn.boundaryCoeffs()[patchI][faceI];
}
}
matrixWrite.H Part2行列Aに対角成分
を代入しています.
行列Aに非対角成分
を代入しています.
境界条件から対角成
分への寄与とソース
項への寄与分を行列
Aに代入しています.
39. 39
作業6:matrixWrite.H ファイルの作成
Info<< "¥n==Coefficient Matrix==" << endl;
for(label i=0; i<Nc; i++)
{
for(label j=0; j<Nc; j++)
{
Info<< A[i][j] << " ";
}
Info<< A.source()[i] << endl;
}
//Info<< A << endl;
Info<< "Solution: " << A.solve() << endl;
行列Aの各成分とソース項
(右辺ベクトル)の各成
分を書き出します.
『AT=右辺ベクトル』
を解いています.
matrixWrite.H Part3
この解が,作業4の確認計算の解と一致すれば,
fvScalarMatrix TEqn(fvm::laplacian(k, T))
の離散化で作成される係数行列を正しく行列Aで再
現できたと言えます.
41. 41
作業7:最終計算
計算を実行すると,ターミナル画面には次の出力があります.
DICPCG: Solving for T, Initial residual = 1, Final residual = 1.21266e-16, No Iterations 1
X = 0.05, T = 140
X = 0.15, T = 220
X = 0.25, T = 300
X = 0.35, T = 380
X = 0.45, T = 460
==Coefficient Matrix==
-300 100 0 0 0 -20000
100 -200 100 0 0 0
0 100 -200 100 0 0
0 0 100 -200 100 0
0 0 0 100 -300 -100000
Solution: 5(140 220 300 380 460)
End
無事解が一致しています!
−300
100
0
0
0
100
−200
100
0
0
0
100
−200
100
0
0
0
100
−200
100
0
0
0
100
−300
𝑇1
𝑇2
𝑇3
𝑇4
𝑇5
=
−20000
0
0
0
−100000
以上から,fvScalarMatrix TEqn(fvm::laplacian(k, T)) により作成される
係数行列と右辺ベクトルは以下のようになることがわかりました.
係数行列
右辺ベクトル
43. 43
gdbOF について
gdbOF とは
GDB(GNU Project Debugger)と共に用いて,OpenFOAM のアプリケー
ションのデバッグ作業を手助けしてくれるツールです.
何ができるの?
• 離散化により得られた係数行列の表示 や
• セル中心値や境界値の表示
など他にもいろいろできるようです.詳細は,こちらの資料 [8] をご覧くだ
さい.
ダウンロードサイト
http://openfoamwiki.net/index.php/Contrib_gdbOF
44. 44
インストール
自分の環境(Ubuntu 12.04,OpenFOAM v2.3.0)では,以下の手順でインス
トールができました.
1. (事前準備) OpenFOAM v2.3.0 をデバッグオプションでコンパイル
2. (事前準備) 必要なライブラリのインストール
3. ダウンロードしたファイル GdbOF_v1.01a.tar.gz を解凍
4. 解凍したフォルダ内のインストール用のスクリプトを実行
#- Optimised, debug, profiling:
# WM_COMPILE_OPTION = Opt | Debug | Prof
export WM_COMPILE_OPTION=Debug
etc/bashrc ファイル
(必要があれば)実行権限の付与
$ chmod +x installgdbOF.sh
スクリプトを実行
$ ./installgdbOF.sh
$ sudo apt-get install gawk python gdb
46. 46
作業1,2
作業1:mylaplacianFoam をデバッグオプションでコンパイルし直します.
作業2:ケースフォルダに移動し,GDB を起動します.
$ cd testCase1
$ gdb mylaplacianFoam
GNU gdb (Ubuntu/Linaro 7.4-2012.04-0ubuntu2.1) 7.4-2012.04
Copyright (C) 2012 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
For bug reporting instructions, please see:
<http://bugs.launchpad.net/gdb-linaro/>...
Reading symbols from ~/platforms/linux64GccDPDebug/bin/mylaplacianFoam...done.
(gdb)
GDB が起動します.
47. 作業3:ブレークポイント(プログラムを強制的に一時停止させるポイン
ト)を設定します.
fvSclarMatrix.C ファイルの 170行目で一時停止させるという設定です.
47
作業3
(gdb) break fvScalarMatrix.C:170
No source file named fvScalarMatrix.C.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 1 (fvScalarMatrix.C:170) pending.
(gdb)
160 // Solver call
161 solverPerformance solverPerf = lduMatrix::solver::New
162 (
163 psi.name(),
164 *this,
165 boundaryCoeffs_,
166 internalCoeffs_,
167 psi.boundaryField().scalarInterfaces(),
168 solverControls
169 )->solve(psi.internalField(), totalSource);
170
171 if (solverPerformance::debug)
172 {
173 solverPerf.print(Info.masterStream(mesh().comm()));
174 }
STOP!
48. 48
作業4
作業4:ソルバーを実行します.
Breakpoint 1 (fvScalarMatrix.C:170) pending.
(gdb) run
・・・
Reading diffusivity k
Calculating temperature distribution
Breakpoint 1, Foam::fvMatrix<double>::solveSegregated (this=0x7fffffffcaf0,
solverControls=...) at fvMatrices/fvScalarMatrix/fvScalarMatrix.C:171
171 if (solverPerformance::debug)
(gdb)
作業3で指定したブレークポイントでソルバーが停止します.
50. 50
作業6
作業6:ファイルの中身を見てみましょう.
𝐴00 𝐴01 𝐴02 𝐴03 𝐴04
𝐴10 𝐴11 𝐴12 𝐴13 𝐴14
𝐴20 𝐴21 𝐴22 𝐴23 𝐴24
𝐴30 𝐴31 𝐴32 𝐴33 𝐴34
𝐴40 𝐴41 𝐴42 𝐴43 𝐴44
(gdb) pfvmatrixfull this matrix.txt 1 1 3 3
とすると赤枠内のみファイルに出力されます.
小さな誤差はありますが,40ページと同じ係数行列が得られています.
-300.0 100.0 0 0 0
100.0 -200.00000000000006 100.00000000000006 0 0
0 100.00000000000006 -200.0000000000001 100.00000000000006 0
0 0 100.00000000000006 -199.99999999999991 99.99999999999986
0 0 0 99.99999999999986 -300.00000000000057
係数行列の出力フォーマット
56. 56
勾配項の離散化|ソースコード
075 const labelUList& owner = mesh.owner();
076 const labelUList& neighbour = mesh.neighbour();
077 const vectorField& Sf = mesh.Sf();
078
079 Field<GradType>& igGrad = gGrad;
080 const Field<Type>& issf = ssf;
081
082 forAll(owner, facei)
083 {
084 GradType Sfssf = Sf[facei]*issf[facei];
085
086 igGrad[owner[facei]] += Sfssf;
087 igGrad[neighbour[facei]] -= Sfssf;
088 }
089
①内部フェイスでループ
• ①の部分で,内部フェイスをループし,各フェイスにおいて 𝑺 𝑓 𝜙 𝑓 を計算し,その値を
そのフェイスを界面とするセルに加えています.
• 087 行で負符号が付いているのは,neighbour セルに関しては,その外向きベクトル
が面積ベクトル 𝑺 𝑓 の逆向きのためです(𝑺 𝑓 の向きは owner ⇒ neighbour セルと定
義されています).
gaussGrad.C
57. 57
勾配項の離散化|ソースコード
090 forAll(mesh.boundary(), patchi)
091 {
092 const labelUList& pFaceCells =
093 mesh.boundary()[patchi].faceCells();
094
095 const vectorField& pSf = mesh.Sf().boundaryField()[patchi];
096
097 const fvsPatchField<Type>& pssf = ssf.boundaryField()[patchi];
098
099 forAll(mesh.boundary()[patchi], facei)
100 {
101 igGrad[pFaceCells[facei]] += pSf[facei]*pssf[facei];
102 }
103 }
104
105 igGrad /= mesh.V();
106
107 gGrad.correctBoundaryConditions();
②境界フェイスでループ
境界上のフェイスを界面に
もつセルの番号のリスト
• ②の部分で,境界フェイスに対して①と同様の計算をしています.
• 境界上のフェイスの法線ベクトルはセル外向きなので,101 行では正の符号.
• 105 行で各セルでの体積積分値をセル体積で除し,勾配を計算しています.
61. 61
対流項の係数行列
補間の重み w = 1 (F > 0の場合),w = 0 (F ≦0の場合) と定義することで,
F の符号に関係なく,係数行列の成分は次のように表現できます.
i
ji
j
𝑘𝑖
𝑘𝑗
⋮
<
1 − 𝑤 ∙ 𝐹
−𝑤 ∙ 𝐹
𝑤 ∙ 𝐹
upwind
− 1 − 𝑤
∙ 𝐹
62. 62
対流項の離散化|ソースコード
093 fvMatrix<Type>& fvm = tfvm();
094
095 fvm.lower() = -weights.internalField()*faceFlux.internalField();
096 fvm.upper() = fvm.lower() + faceFlux.internalField();
097 fvm.negSumDiag();
098
099 forAll(vf.boundaryField(), patchI)
100 {
101 const fvPatchField<Type>& psf = vf.boundaryField()[patchI];
102 const fvsPatchScalarField& patchFlux = faceFlux.boundaryField()[patchI];
103 const fvsPatchScalarField& pw = weights.boundaryField()[patchI];
104
105 fvm.internalCoeffs()[patchI] = patchFlux*psf.valueInternalCoeffs(pw);
106 fvm.boundaryCoeffs()[patchI] = -patchFlux*psf.valueBoundaryCoeffs(pw);
107 }
108
109 if (tinterpScheme_().corrected())
110 {
111 fvm += fvc::surfaceIntegrate(faceFlux*tinterpScheme_().correction(vf));
112 }
113
114 return tfvm;
gaussConvectionScheme.C
①内部フェイスでの離散化
②境界上のフェイスでの離散化
③補間の補正項の陽的な処理
upwind 以外の場合も同様にして,対流項の係数行列の各成分は,フェイス
における流束 faceFlux と補間スキームの重み weights から以下のように計
算されます.
63. 例えば,fvSchemes ファイルにおいて “div(phi,k) Gauss upwind;” と設定した場合
には,
• ”faceFlux” が “phi” を,
• ”weights” が “upwind” 補間スキームで定義されている重みを,
• ”tinterpScheme_()” が 補間スキーム “upwind” を
それぞれ表します.
補正 (correction) を伴う補間スキームについては,111 行でその補正項を陽的に評価
しています.
例えば,“linearUpwind” スキームの場合は,
𝛻 ∙ 𝑼𝑘 𝑑𝑉
𝑉
= 𝐹𝑓 ∙ 𝑘 upstream cell + correction 𝑓
𝑓
= 𝐹𝑓 ∙ 𝑘 upstream cell + 𝐹𝑓 ∙ correction 𝑓
𝑓𝑓
63
対流項の離散化|ソースコード
109 if (tinterpScheme_().corrected())
110 {
111 fvm += fvc::surfaceIntegrate(faceFlux*tinterpScheme_().correction(vf));
112 }
陰的に処理 ⇒ 係数行列へ 陽的に処理 ⇒ ソース項 (右辺ベクトル)へ
111 行で
加えている項
65. 65
対流項の離散化|bounded オプション
0067 template<class Type>
0068 tmp<fvMatrix<Type> >
0069 boundedConvectionScheme<Type>::fvmDiv
0070 (
0071 const surfaceScalarField& faceFlux,
0072 const GeometricField<Type, fvPatchField, volMesh>& vf
0073 ) const
0074 {
0075 return
0076 scheme_().fvmDiv(faceFlux, vf)
0077 - fvm::Sp(fvc::surfaceIntegrate(faceFlux), vf);
0078 }
boundedConvectionScheme.C
divSchemes
{
div(phi,k) bounded Gauss upwind;
}
𝛻 ∙ 𝑼𝑘 を 𝛻 ∙ 𝑼𝑘 − 𝛻 ∙ 𝑼 𝑘 と計算します.
の場合
66. 66
対流項の離散化|bounded オプション
bounded オプションを使用した場合には,対流項は次のように離散化され
ます.
𝛻 ∙ 𝑼𝑘 − 𝛻 ∙ 𝑼 𝑘 𝑑𝑉
𝑉
= 𝑑𝑺 ∙ 𝑼𝑘
𝑆
− 𝛻 ∙ 𝑼 𝑘 𝑑𝑉
𝑉
= 𝑺 𝑓 ∙ 𝑼 𝑓 𝑘 𝑓
𝑓
− 𝑉𝑝 ∙
𝑺 𝑓 ∙ 𝑼 𝑓𝑓
𝑉𝑝
∙ 𝑘 𝑝
= 𝐹𝑓 𝑘 𝑓 − 𝑘 𝑝 𝐹𝑓
𝑓𝑓 連続の式から,非圧縮流れでは
解の収束に伴って 0 に収束します.
Sp (61ページ)surfaceIntegrate (62ページ)
67. 67
対流項の離散化|bounded オプション
0098 template<class Type>
0099 Foam::tmp<Foam::fvMatrix<Type> >
0100 Foam::fvm::Sp
0101 (
0102 const DimensionedField<scalar, volMesh>& sp,
0103 const GeometricField<Type, fvPatchField, volMesh>& vf
0104 )
0105 {
0106 const fvMesh& mesh = vf.mesh();
0107
0108 tmp<fvMatrix<Type> > tfvm
0109 (
0110 new fvMatrix<Type>
0111 (
0112 vf,
0113 dimVol*sp.dimensions()*vf.dimensions()
0114 )
0115 );
0116 fvMatrix<Type>& fvm = tfvm();
0117
0118 fvm.diag() += mesh.V()*sp.field();
0119
0120 return tfvm;
0121 }
fvmSup.C
係数行列の
対角成分に加えています.
fvm::Sp(fvc::surfaceIntegrate(faceFlux), vf) の計算
68. 68
対流項の離散化|bounded オプション
0042 template<class Type>
0043 void surfaceIntegrate
0044 (
0045 Field<Type>& ivf,
0046 const GeometricField<Type, fvsPatchField, surfaceMesh>& ssf
0047 )
0048 {
0049 const fvMesh& mesh = ssf.mesh();
0050
0051 const labelUList& owner = mesh.owner();
0052 const labelUList& neighbour = mesh.neighbour();
0053
0054 const Field<Type>& issf = ssf;
0055
0056 forAll(owner, facei)
0057 {
0058 ivf[owner[facei]] += issf[facei];
0059 ivf[neighbour[facei]] -= issf[facei];
0060 }
0061
0062 forAll(mesh.boundary(), patchi)
0063 {
0064 const labelUList& pFaceCells =
0065 mesh.boundary()[patchi].faceCells();
0066
0067 const fvsPatchField<Type>& pssf = ssf.boundaryField()[patchi];
0068
0069 forAll(mesh.boundary()[patchi], facei)
0070 {
0071 ivf[pFaceCells[facei]] += pssf[facei];
0072 }
0073 }
0074
0075 ivf /= mesh.V();
0076 }
内部フェイスでループ
境界フェイスでループ
fvcSurfaceIntegrate.C
fvm::Sp(fvc::surfaceIntegrate(faceFlux), vf) の計算
71. 71
ラプラシアン項の離散化
𝛻 ∙ Γ𝛻𝜙 𝑑𝑉
𝑉
= 𝑑𝑺 ∙ Γ𝛻𝜙
𝑆
= Γ𝑓 𝑺 𝑓 ∙ 𝛻𝜙 𝑓
𝑓
𝑺 𝑓
P N
セル中心間を結ぶ線分が
その間のフェイスと直交する場合
𝑺 𝑓
𝜙 𝑁 − 𝜙 𝑃
𝒅
簡単に離散化できます.
が・・・
73. 73
ラプラシアン項の離散化
フェイスの面積ベクトル 𝑺 𝑓 を分解 ⇒ 𝑺 𝑓 = ∆ + 𝒌
Γ𝑓 𝑺 𝑓 ∙ 𝛻𝜙 𝑓 = Γ𝑓∆ ∙ 𝛻𝜙 𝑓 + Γ𝑓 𝒌 ∙ 𝛻𝜙 𝑓
• 右辺第一項: 陰的に処理 ⇒ 係数行列
• 右辺第二項: 陽的に処理 ⇒ ソース項
= Γ𝑓 ∆
𝜙 𝑁 − 𝜙 𝑃
𝒅
+ Γ𝑓 𝒌 ∙ 𝛻𝜙 𝑓
非直交補正
74. 74
ラプラシアン項の離散化
∆=
𝑺 2
𝒅 ∙ 𝑺
𝒅𝑺 = ∆ + 𝒌
𝑺 ∙ 𝒌 = 0
OpenFOAMでは,Jasak [10] に紹介されている3つの手法の内の
『over-relaxed approach』により非直交補正を行っています.
𝜃
77. 77
ラプラシアン項の離散化|非直交補正の大きさのコントロール
0051 template<class Type>
0052 tmp<GeometricField<Type, fvsPatchField, surfaceMesh> >
0053 limitedSnGrad<Type>::correction
0054 (
0055 const GeometricField<Type, fvPatchField, volMesh>& vf
0056 ) const
0057 {
0058 const GeometricField<Type, fvsPatchField, surfaceMesh> corr
0059 (
0060 correctedScheme_().correction(vf)
0061 );
0062
0063 const surfaceScalarField limiter
0064 (
0065 min
0066 (
0067 limitCoeff_
0068 *mag(snGradScheme<Type>::snGrad(vf, deltaCoeffs(vf), "SndGrad"))
0069 /(
0070 (1 - limitCoeff_)*mag(corr)
0071 + dimensionedScalar("small", corr.dimensions(), SMALL)
0072 ),
0073 dimensionedScalar("one", dimless, 1.0)
0074 )
0075 );
0076
0077 if (fv::debug)
0078 {
0079 Info<< "limitedSnGrad :: limiter min: " << min(limiter.internalField())
0080 << " max: "<< max(limiter.internalField())
0081 << " avg: " << average(limiter.internalField()) << endl;
0082 }
0083
0084 return limiter*corr;
0085 }
correctedSnGrads.C の Foam::fv::correctedSnGrad
<Foam::vector>::correction が呼ばれます.
limiter(𝜓) は以下の limiter に対応しています.
𝜓 の値
78. 78
ラプラシアン項の離散化
0042 template<class Type>
0043 Foam::tmp<Foam::GeometricField<Type, Foam::fvsPatchField, Foam::surfaceMesh> >
0044 Foam::fv::correctedSnGrad<Type>::fullGradCorrection
0045 (
0046 const GeometricField<Type, fvPatchField, volMesh>& vf
0047 ) const
0048 {
0049 const fvMesh& mesh = this->mesh();
0050
0051 // construct GeometricField<Type, fvsPatchField, surfaceMesh>
0052 tmp<GeometricField<Type, fvsPatchField, surfaceMesh> > tssf =
0053 mesh.nonOrthCorrectionVectors()
0054 & linear<typename outerProduct<vector, Type>::type>(mesh).interpolate
0055 (
0056 gradScheme<Type>::New
0057 (
0058 mesh,
0059 mesh.gradScheme("grad(" + vf.name() + ')')
0060 )().grad(vf, "grad(" + vf.name() + ')')
0061 );
0062 tssf().rename("snGradCorr(" + vf.name() + ')');
0063
0064 return tssf;
0065 }
非直交補正中の 𝛻𝜙 𝑓 の補間には,線形補間が使用されます.
correctedSnGrad.C
79. 79
発散項の離散化
ガウスの発散定理を使用します.
𝛻 ∙ 𝜈 𝛻𝒖 𝑇 𝑑𝑉
𝑉
= 𝑑𝑺 ∙ 𝜈 𝛻𝒖 𝑇
𝑆
= 𝑺 𝑓 ∙ 𝜈 𝛻𝒖 𝑇
𝑓
𝑓
divSchemes
{
div(nu*T(grad(U))) Gauss linear;
}
セル中心からフェイスへの
補間スキーム
ガウスの発散定理
81. 81
ソース項の離散化
0098 template<class Type>
0099 Foam::tmp<Foam::fvMatrix<Type> >
0100 Foam::fvm::Sp
0101 (
0102 const DimensionedField<scalar, volMesh>& sp,
0103 const GeometricField<Type, fvPatchField, volMesh>& vf
0104 )
0105 {
0106 const fvMesh& mesh = vf.mesh();
0107
0108 tmp<fvMatrix<Type> > tfvm
0109 (
0110 new fvMatrix<Type>
0111 (
0112 vf,
0113 dimVol*sp.dimensions()*vf.dimensions()
0114 )
0115 );
0116 fvMatrix<Type>& fvm = tfvm();
0117
0118 fvm.diag() += mesh.V()*sp.field();
0119
0120 return tfvm;
0121 }
fvmSup.C
2
𝜌𝜙 𝑑𝑉
𝑉
= 𝑉𝑃 𝜌 𝑃 𝜙 𝑃
ソース項を陰的に扱います.
係数行列の対角成分に加えています.
82. 82
ソース項の離散化
0190 template<class Type>
0191 Foam::tmp<Foam::fvMatrix<Type> >
0192 Foam::fvm::SuSp
0193 (
0194 const DimensionedField<scalar, volMesh>& susp,
0195 const GeometricField<Type, fvPatchField, volMesh>& vf
0196 )
0197 {
0198 const fvMesh& mesh = vf.mesh();
0199
0200 tmp<fvMatrix<Type> > tfvm
0201 (
0202 new fvMatrix<Type>
0203 (
0204 vf,
0205 dimVol*susp.dimensions()*vf.dimensions()
0206 )
0207 );
0208 fvMatrix<Type>& fvm = tfvm();
0209
0210 fvm.diag() += mesh.V()*max(susp.field(), scalar(0));
0211
0212 fvm.source() -= mesh.V()*min(susp.field(), scalar(0))
0213 *vf.internalField();
0214
0215 return tfvm;
0216 }
3
fvmSup.C
ソース項の処理を符号で
場合分けします.
𝜌𝜙 𝑑𝑉
𝑉
= 𝑉𝑃 𝜌 𝑃 𝜙 𝑃
𝝆 𝑷 > 𝟎 の場合: の場合と同様に陰的に処理2
𝝆 𝑷 < 𝟎 の場合: の場合と同様に陽的に処理1
84. 84
境界条件の分類
境界条件は,大きく以下の4つのタイプに分類できます.
• ディリクレ (Dirichlet) 境界条件,第1種境界条件
境界上で関数値そのものを規定します.𝒙を境界上の点,𝑡を時間として,
次式の条件で表されます.
𝜙 𝒙, 𝑡 = 𝑓 𝒙, 𝑡
• ノイマン (Neumann) 境界条件,第2種境界条件
境界上で関数の勾配(gradient)を規定します.𝒙を境界上の点,𝑡を時間,
𝒏を境界の法線方向の単位ベクトルとして,次式で表されます.
𝜕𝜙
𝜕𝒏
𝒙, 𝑡 = 𝑔 𝒙, 𝑡
• ロビン (Robin) 境界条件,第3種境界条件
上記の2つの条件を組み合わせた条件です.
𝛼 𝒙, 𝑡 𝜙 𝒙, 𝑡 + 𝛽 𝒙, 𝑡
𝜕𝜙
𝜕𝒏
𝒙, 𝑡 = ℎ 𝒙, 𝑡
• 周期境界条件
境界法線方向の𝜙の勾配
85. 85
対応する OpenFOAM の境界条件
定常 (Steady) 非定常 (Time-Dependent)
ディリクレ条件
Dirichlet
ノイマン条件
Neumann
ロビン条件
Robin
Not
Implemented
周期境界条件
Periodic
fixedValue uniformFixedValue
fixedGradient uniformFixedGradient
zeroGradient
mixed
cyclic
𝜙 𝒙 = 𝑓 𝒙 𝜙 𝒙, 𝑡 = 𝑓 𝒙, 𝑡
𝜕𝜙
𝜕𝒏
𝒙 = 𝑔 𝒙
𝜕𝜙
𝜕𝒏
𝒙, 𝑡 = 𝑔 𝒙, 𝑡
𝜕𝜙
𝜕𝒏
𝒙 = 0
𝛼 𝒙 𝜙 𝒙 + 𝛽 𝒙
𝜕𝜙
𝜕𝒏
𝒙 = ℎ 𝒙
cyclicAMI
90. ロビン条件(mixed) の場合
Γ𝑓 𝑺 𝑓 ∙ 𝛻𝜙 𝑓 = Γ𝑏 𝑺 𝑏
𝜙 𝑏 − 𝜙 𝑃
𝒅
= Γ𝑏 𝑺 𝑏
−𝑓
𝒅
𝜙 𝑃
𝑎11 𝑎1𝑁
𝑎 𝑁1 𝑎 𝑁𝑁
𝜙1
𝜙2
⋮
𝜙 𝑁
=
𝑠1
𝑠2
⋮
𝑠 𝑁
90
境界フェイスでのラプラシアンの離散化|mixed 境界条件
+Γ𝑏 𝑺 𝑏
𝑓 ∙ 𝑟𝑒𝑓𝑉𝑎𝑙𝑢𝑒 + 1 − 𝑓 ∙ 𝑟𝑒𝑓𝐺𝑟𝑎𝑑 ∙ 𝒅
𝒅
…
…
…
…
係数行列の対角成分と
右辺ベクトルの両方に
対して寄与があります.
91. 91
gradientInternalCoeffs と gradientBoundaryCoeffs
具体的に見てきたように,ラプラシアンを境界フェイスで離散化する際の
• 係数行列の対角成分
• 右辺ベクトル
への寄与はディリクレ,ノイマン,ロビン境界条件により異なります.
OpenFOAMではこの違いを取り扱うために,
それぞれの境界条件のクラスごとに次の2つの関数が定義されています.
• gradientInternalCoeffs()
係数行列の対角成分への寄与
• gradientBoundaryCoeffs()
右辺ベクトルへの寄与
次のスライドからOpenFOAMでの実装を見ていきましょう.
92. 92
境界フェイスでのラプラシアンの離散化|zeroGradient の実装
template<class Type>
tmp<Field<Type> > zeroGradientFvPatchField<Type>::gradientInternalCoeffs() const
{
return tmp<Field<Type> >
(
new Field<Type>(this->size(), pTraits<Type>::zero)
);
}
template<class Type>
tmp<Field<Type> > zeroGradientFvPatchField<Type>::gradientBoundaryCoeffs()
const
{
return tmp<Field<Type> >
(
new Field<Type>(this->size(), pTraits<Type>::zero)
);
}
係数行列の対角成分と
右辺ベクトルの両方に
対して寄与がありません.
zeroGradientFvPatchField.C
93. 93
境界フェイスでのラプラシアンの離散化|fixedGradient の実装
template<class Type>
tmp<Field<Type> > fixedGradientFvPatchField<Type>::
gradientInternalCoeffs() const
{
return tmp<Field<Type> >
(
new Field<Type>(this->size(), pTraits<Type>::zero)
);
}
template<class Type>
tmp<Field<Type> > fixedGradientFvPatchField<Type>::
gradientBoundaryCoeffs() const
{
return gradient();
}
fixedGradientFvPatchField.C
Γ𝑓 𝑺 𝑓 ∙ 𝛻𝜙 𝑓 = Γ𝑏 𝑺 𝑏
𝜕𝜙
𝜕𝒏 𝑏
係数行列の対角成分
への寄与はありません.
右辺ベクトルへの
寄与があります.
94. 94
境界フェイスでのラプラシアンの離散化|fixedValue の実装
template<class Type>
tmp<Field<Type> > fixedValueFvPatchField<Type>::gradientInternalCoeffs() const
{
return -pTraits<Type>::one*this->patch().deltaCoeffs();
}
Γ𝑓 𝑺 𝑓 ∙ 𝛻𝜙 𝑓 = Γ𝑏 𝑺 𝑏
−1
𝒅
𝜙 𝑃 + Γ𝑏 𝑺 𝑏
1
𝒅
𝜙 𝑏
template<class Type>
tmp<Field<Type> > fixedValueFvPatchField<Type>::gradientBoundaryCoeffs() const
{
return this->patch().deltaCoeffs()*(*this);
}
係数行列の対角成分と
右辺ベクトルの両方に
対して寄与があります.
境界フェイスにおいて,
deltaCoeffs() は,フェイス中心点と隣接セル中心点間の距離の逆数を返します.
fixedValueFvPatchField.C
95. 95
境界フェイスでのラプラシアンの離散化|mixed の実装
template<class Type>
tmp<Field<Type> > mixedFvPatchField<Type>::gradientInternalCoeffs() const
{
return -Type(pTraits<Type>::one)*valueFraction_*this->patch().deltaCoeffs();
}
template<class Type>
tmp<Field<Type> > mixedFvPatchField<Type>::gradientBoundaryCoeffs() const
{
return
valueFraction_*this->patch().deltaCoeffs()*refValue_
+ (1.0 - valueFraction_)*refGrad_;
}
mixedFvPatchField.C
Γ𝑓 𝑺 𝑓 ∙ 𝛻𝜙 𝑓 = Γ𝑏 𝑺 𝑏
−𝑓
𝒅
𝜙 𝑃
+Γ𝑏 𝑺 𝑏
𝑓 ∙ 𝑟𝑒𝑓𝑉𝑎𝑙𝑢𝑒 + 1 − 𝑓 ∙ 𝑟𝑒𝑓𝐺𝑟𝑎𝑑 ∙ 𝒅
𝒅
96. 96
境界フェイスでのラプラシアンの離散化
forAll(vf.boundaryField(), patchi)
{
const fvPatchField<Type>& pvf = vf.boundaryField()[patchi];
const fvsPatchScalarField& pGamma = gammaMagSf.boundaryField()[patchi];
const fvsPatchScalarField& pDeltaCoeffs =
deltaCoeffs.boundaryField()[patchi];
if (pvf.coupled())
{
//省略 cyclic境界条件などの場合の処理
}
else
{
fvm.internalCoeffs()[patchi] = pGamma*pvf.gradientInternalCoeffs();
fvm.boundaryCoeffs()[patchi] = -pGamma*pvf.gradientBoundaryCoeffs();
}
}
ラプラシアンの離散化のコードを確認してみましょう.
Γ𝑓 𝑺 𝑓 ∙ 𝛻𝜙 𝑓 = Γ𝑏 𝑺 𝑏
−1
𝒅
𝜙 𝑃 + Γ𝑏 𝑺 𝑏
1
𝒅
𝜙 𝑏
cyclic境界以外の境界での離散化
gaussLaplacianScheme.C
101. 101
valueInternalCoeffs と valueBoundaryCoeffs
具体的に見てきたように,境界フェイスにおける対流項の流束からの
• 係数行列の対角成分
• 右辺ベクトル
への寄与は,ディリクレ,ノイマン,ロビン境界条件によって異なります.
OpenFOAMではこの違いを取り扱うために,
それぞれの境界条件のクラスごとに次の2つの関数が定義されています.
• valueInternalCoeffs()
係数行列の対角成分への寄与
• valueBoundaryCoeffs()
右辺ベクトルへの寄与
次のスライドからOpenFOAMでの実装を見ていきましょう.
102. 102
境界フェイスでの対流項の離散化|zeroGradient の実装
template<class Type>
tmp<Field<Type> > zeroGradientFvPatchField<Type>::valueInternalCoeffs
(
const tmp<scalarField>&
) const
{
return tmp<Field<Type> >
(
new Field<Type>(this->size(), pTraits<Type>::one)
);
}
template<class Type>
tmp<Field<Type> > zeroGradientFvPatchField<Type>::valueBoundaryCoeffs
(
const tmp<scalarField>&
) const
{
return tmp<Field<Type> >
(
new Field<Type>(this->size(), pTraits<Type>::zero)
);
}
zeroGradientFvPatchField.C
係数行列の対角成分
に加わります.
右辺ベクトルへの
寄与はありません.
𝐹𝜙 𝑏 = 𝐹𝜙 𝑃
103. template<class Type>
tmp<Field<Type> > fixedGradientFvPatchField<Type>::valueInternalCoeffs
(
const tmp<scalarField>&
) const
{
return tmp<Field<Type> >(new Field<Type>(this->size(), pTraits<Type>::one));
}
template<class Type>
tmp<Field<Type> > fixedGradientFvPatchField<Type>::valueBoundaryCoeffs
(
const tmp<scalarField>&
) const
{
return gradient()/this->patch().deltaCoeffs();
}
103
境界フェイスでの対流項の離散化|fixedGradient の実装
𝐹𝜙 𝑏 = 𝐹𝜙 𝑃 + 𝐹𝑔 𝑏 𝒅
fixedGradientFvPatchField.C
係数行列の対角成分と
右辺ベクトルの両方に
対して寄与があります.
104. 104
境界フェイスでの対流項の離散化|fixedValue の実装
template<class Type>
tmp<Field<Type> > fixedValueFvPatchField<Type>::valueInternalCoeffs
(
const tmp<scalarField>&
) const
{
return tmp<Field<Type> >
(
new Field<Type>(this->size(), pTraits<Type>::zero)
);
}
template<class Type>
tmp<Field<Type> > fixedValueFvPatchField<Type>::valueBoundaryCoeffs
(
const tmp<scalarField>&
) const
{
return *this;
}
fixedValueFvPatchField.C
境界値を返しています.
係数行列の対角成分
への寄与はありません.
105. 105
境界フェイスでの対流項の離散化
対流項の離散化のコードを確認してみます.
𝐹 𝜙 𝑏
forAll(vf.boundaryField(), patchI)
{
const fvPatchField<Type>& psf = vf.boundaryField()[patchI];
const fvsPatchScalarField& patchFlux = faceFlux.boundaryField()[patchI];
const fvsPatchScalarField& pw = weights.boundaryField()[patchI];
fvm.internalCoeffs()[patchI] = patchFlux*psf.valueInternalCoeffs(pw);
fvm.boundaryCoeffs()[patchI] = -patchFlux*psf.valueBoundaryCoeffs(pw);
}
gaussConvectionScheme.C
108. 108
simpleFoam について
simpleFoam とは
• 定常非圧縮性流れ計算のためのソルバーです.
• RANS乱流モデルを使用できます.
支配方程式
𝛻 ∙ 𝒖 𝒖 = −𝛻𝑝 + 𝛻 ∙ 𝜈 𝑒𝑓𝑓 𝛻𝒖 + 𝛻𝒖 𝑇
𝛻 ∙ 𝒖 = 0
+ 乱流変数の輸送方程式(乱流モデルにより異なります)
流速 𝑚/𝑠
密度で割った圧力 𝑚2/𝑠2
渦動粘性係数と分子動粘性係数の和 𝑚2
/𝑠
運動方程式
連続の式
109. 109
対流項の表現
対流項をベクトルの成分で書き下すと
𝛻 ∙ 𝒖 𝒖 = 𝛻 ∙
𝑢1 𝑢1 𝑢1 𝑢2 𝑢1 𝑢3
𝑢2 𝑢1 𝑢2 𝑢2 𝑢2 𝑢3
𝑢3 𝑢1 𝑢3 𝑢2 𝑢3 𝑢3
=
𝜕 𝑢1 𝑢1
𝜕𝑥1
+
𝜕 𝑢2 𝑢1
𝜕𝑥2
+
𝜕 𝑢3 𝑢1
𝜕𝑥3
𝜕 𝑢1 𝑢2
𝜕𝑥1
+
𝜕 𝑢2 𝑢2
𝜕𝑥2
+
𝜕 𝑢3 𝑢2
𝜕𝑥3
𝜕 𝑢1 𝑢3
𝜕𝑥1
+
𝜕 𝑢2 𝑢3
𝜕𝑥2
+
𝜕 𝑢3 𝑢3
𝜕𝑥3
110. 110
対流項の表現
対流項は次式のように式変形できます.
𝛻 ∙ 𝒖 𝒖 = 𝛻 ∙ 𝒖 𝒖 + 𝒖 ∙ 𝛻 𝒖
第4章で見た対流項の離散化の ”bounded” オプションを使用した場合には,
収束までの過程で,非保存形 (non-conservative form) の表現を使用する
ことを意味します.
𝛻 ∙ 𝒖 𝒖 − 𝛻 ∙ 𝒖 𝒖 = 𝒖 ∙ 𝛻 𝒖
計算が収束して,連続の式が満たされれば,次式が成り立ちます.
𝛻 ∙ 𝒖 𝒖 = 𝒖 ∙ 𝛻 𝒖
“bounded” オプションを
使用した場合の対流項の表現
非保存形
111. 111
simpleFoam のソースコード
simpleFoam のソースコードの場所
$WM_PROJECT_DIR/applications/solvers/incompressible/simpleFoam
simpleFoam のソースファイル
• simpleFoam.C:メイン処理をまとめたファイル
• createFields.H:流速 U や圧力 p 等の変数を定義します.
• UEqn.H:運動方程式を解きます.
• pEqn.H:圧力ポアソン方程式を解きます.
“simpleFoam” の名前の由来にもなっている SIMPLE アルゴリズムの実装の
中核を担っている “UEqn.H” と “pEqn.H” のコードの詳細を見ていきましょ
う.
112. 計算の流れに沿って,UEqn.H ファイルから見ていきます.
UEqn.H ファイルでは運動方程式を離散化して解きます.
次のページからより詳しく見ていきましょう.
112
UEqn.H|概要
1 // Momentum predictor
2
3 tmp<fvVectorMatrix> UEqn
4 (
5 fvm::div(phi, U)
6 + turbulence->divDevReff(U)
7 ==
8 fvOptions(U)
9 );
10
11 UEqn().relax();
12
13 fvOptions.constrain(UEqn());
14
15 solve(UEqn() == -fvc::grad(p));
16
17 fvOptions.correct(U);
運動方程式を離散化しています.
係数行列の不足緩和を行っています.
圧力勾配項を前の時間ステップの圧力から
計算して,流速場を計算します.
114. 114
UEqn.H|不足緩和(under relaxation)
離散化 (3~9行) により得られる方程式は,次式のように書き表せます.
𝐴 𝑃 𝒖 𝑃 + 𝐴 𝑁 𝒖 𝑁
𝑁
= 𝒔
11行目で行っている不足緩和は,係数行列の対角成分を大きくするための操
作です.これにより連立方程式が解きやすくなります.
𝐴 𝑃
𝛼
𝒖 𝑝 + 𝐴 𝑁 𝒖 𝑁
𝑁
= 𝒔 +
𝐴 𝑃
𝛼
− 𝐴 𝑃 𝒖 𝑃
0
これを少し整理すると,
𝐴 𝑃
𝛼
𝒖 𝑃 + 𝐴 𝑁 𝒖 𝑁
𝑁
= 𝒔 +
1 − 𝛼
𝛼
𝐴 𝑃 𝒖 𝑃
0
641 // ... then relax
642 D /= alpha;
671 // Finally add the relaxation contribution to the source.
672 S += (D - D0)*psi_.internalField();
fvMatrix.C
0 < 𝛼 < 1 なので,もともとの係数 𝐴 𝑝 よりも大きな値をとります.
1
2
𝛼:緩和係数
𝒖 𝑃
0
:前の時間ステップの解
115. 115
UEqn.H|不足緩和(under relaxation)
前ページの 式と 式の関係を見てみます.
𝐴 𝑃 𝒖 𝑃 +
1 − 𝛼
𝛼
𝐴 𝑃 𝒖 𝑃 + 𝐴 𝑁 𝒖 𝑁
𝑁
= 𝒔 +
1 − 𝛼
𝛼
𝐴 𝑃 𝒖 𝑃
0
式の両辺に,青枠の項が足された形になっているのが分かります.
この連立方程式を,反復解法により解いて収束解が得られる場合には,青枠
の項は同じ値に収束するので, 式と 式とで同じ解が得られることが
分かります.
以上説明してきた緩和係数 𝛼 の値は,fvSolution ファイルで設定します.
1 2
式を変形すると,2
1
1 2
relaxationFactors
{
equations
{
U 0.7;
k 0.7;
epsilon 0.7;
}
}
連立方程式を解くそれぞれの変数ごとに
緩和係数の値を設定します.
116. 116
UEqn.H|流速場の予測値
15行目で得られた解 𝒖 𝑃
∗
について次の関係が成り立っています.
𝐴 𝑃 𝒖 𝑃
∗
+ 𝐴 𝑁 𝒖 𝑁
∗
𝑁
= 𝒔 − 𝑉𝑃 ∙ 𝛻 𝑝 𝑛−1
これを変形すると,
𝒖 𝑃
∗
=
1
𝐴 𝑃
𝒔 − 𝐴 𝑁 𝒖 𝑁
∗
𝑁
−
𝑉𝑃
𝐴 𝑃
𝛻𝑝 𝑛−1
ここで,𝐴 𝑃,𝐴 𝑁,𝒔 をセル体積で除したものを再び同じ記号で表すと,次式の
ように書き表せます.
𝒖 𝑃
∗
=
1
𝐴 𝑃
𝒔 − 𝐴 𝑁 𝒖 𝑁
∗
𝑁
−
1
𝐴 𝑃
𝛻𝑝 𝑛−1
3
前のステップの圧力
流速場の予測値
セル体積
117. 117
この後の計算の流れ
SIMPLE 法では,流速場の修正量を次式から見積もります.
𝒖 𝑃
′
= −
1
𝐴 𝑃
𝛻𝑝′
修正後の流速場 𝒖 𝑃
𝑛
が,連続の式を満たすように修正量を決定します.
𝛻 ∙ 𝒖 𝑃
𝑛
= 𝛻 ∙ 𝒖 𝑃
∗
+ 𝒖 𝑃
′
= 0
𝛻 ∙
1
𝐴 𝑃
𝒔 − 𝐴 𝑁 𝒖 𝑁
∗
𝑁
−
1
𝐴 𝑃
𝛻𝑝 𝑛−1 −
1
𝐴 𝑃
𝛻𝑝′ = 0
𝛻 ∙
1
𝐴 𝑃
𝛻𝑝 𝑛 = 𝛻 ∙
1
𝐴 𝑃
𝒔 − 𝐴 𝑁 𝒖 𝑁
∗
𝑁
4
5
3 と を使って変形4
6
118. 118
pEqn.H (Part1)
1 {
2 volScalarField rAU(1.0/UEqn().A());
3 volVectorField HbyA("HbyA", U);
4 HbyA = rAU*UEqn().H();
5 UEqn.clear();
6
7 surfaceScalarField phiHbyA("phiHbyA", fvc::interpolate(HbyA) & mesh.Sf());
8
9 fvOptions.makeRelative(phiHbyA);
10
11 adjustPhi(phiHbyA, U, p);
前のスライドの内容をどのように実装しているのか見ていきましょう.
pEqn.H
2~4行目
𝒖 𝑃
∗
=
1
𝐴 𝑃
𝒔 − 𝐴 𝑁 𝒖 𝑁
∗
𝑁
−
1
𝐴 𝑃
𝛻𝑝 𝑛−1
(再掲)
4行目で,HbyA という変数に代入しています.
rAU
UEqn().H()
3
119. 119
pEqn.H (Part1) |UEqn().A()
このスライドの赤字の部分の処理に対応して,UEqn().A() は係数行列の対
角成分をセル体積で割った値を返します.
template<class Type>
Foam::tmp<Foam::volScalarField> Foam::fvMatrix<Type>::A() const
{
tmp<volScalarField> tAphi
(
new volScalarField
(
IOobject
(
"A("+psi_.name()+')',
psi_.instance(),
psi_.mesh(),
IOobject::NO_READ,
IOobject::NO_WRITE
),
psi_.mesh(),
dimensions_/psi_.dimensions()/dimVol,
zeroGradientFvPatchScalarField::typeName
)
);
tAphi().internalField() = D()/psi_.mesh().V();
tAphi().correctBoundaryConditions();
return tAphi;
}
fvMatrix.C
120. 120
pEqn.H (Part1) |UEqn().H()
このスライドの赤字の部分の処理に対応して,UEqn().H() は係数行列の非
対角成分と右辺ベクトルをセル体積で割った値で計算されます.
template<class Type>
Foam::tmp<Foam::GeometricField<Type, Foam::fvPatchField, Foam::volMesh> >
Foam::fvMatrix<Type>::H() const
{
tmp<GeometricField<Type, fvPatchField, volMesh> > tHphi
(
new GeometricField<Type, fvPatchField, volMesh>
(
IOobject
(
"H("+psi_.name()+')',
psi_.instance(),
psi_.mesh(),
IOobject::NO_READ,
IOobject::NO_WRITE
),
psi_.mesh(),
dimensions_/dimVol,
zeroGradientFvPatchScalarField::typeName
)
);
GeometricField<Type, fvPatchField, volMesh>& Hphi = tHphi();
(中略)
Hphi.internalField() += lduMatrix::H(psi_.internalField()) + source_;
addBoundarySource(Hphi.internalField());
Hphi.internalField() /= psi_.mesh().V();
Hphi.correctBoundaryConditions();
fvMatrix.C
121. 121
pEqn.H (Part1) |補足
1 // Momentum predictor
2
3 tmp<fvVectorMatrix> UEqn
4 (
5 fvm::div(phi, U)
6 + turbulence->divDevReff(U)
7 ==
8 fvOptions(U)
9 );
10
11 UEqn().relax();
12
13 fvOptions.constrain(UEqn());
14
15 solve(UEqn() == -fvc::grad(p));
16
17 fvOptions.correct(U);
方程式の離散化に際して,UEqn() (3~9行)の定義の方に圧力勾配項
-fvc::grad(p) を含めないことで,UEqn().H() はこの項を含まずに計算して
います.
-fvc::grad(p) を UEqn()
の定義に含めていない
122. 122
pEqn.H (Part1)|体積流束 (volumetric flux) の計算
7行目:HbyA から体積流束を計算しています.
fvc::interpolate(HbyA) & mesh.Sf()
11行目:
どの境界でも圧力を規定していない場合に,計算領域に流入出する流量の収
支が満たされるように流束を修正します.
流量の収支 = massIn - fixedMassOut - massCorr * adjustableMassOut
と書き表した時に,流量の収支が満たされるように adjustableMassOut を
調整します.
ここで,
• massIn:計算領域に流入する全流量
• fixedMassOut:流速が規定されている境界から流出する流量
• adjustableMassOut:流速が規定されていない境界から流出する流量
• massCorr = (massIn - fixedMassOut)/adjustableMassOut
HbyA をセル中心からフェイスへ補間 フェイスの法線ベクトル
(大きさ;フェイスの面積)
123. 123
pEqn.H (Part2)|概要
13 // Non-orthogonal pressure corrector loop
14 while (simple.correctNonOrthogonal())
15 {
16 fvScalarMatrix pEqn
17 (
18 fvm::laplacian(rAU, p) == fvc::div(phiHbyA)
19 );
20
21 pEqn.setReference(pRefCell, pRefValue);
22
23 pEqn.solve();
24
25 if (simple.finalNonOrthogonalIter())
26 {
27 phi = phiHbyA - pEqn.flux();
28 }
29 }
pEqn.H
圧力に関するポアソン方程式 を解いて,連続の式を満たすように圧力を求
め,流束の修正を行っています.次のページで詳細を見ていきます.
6
125. メッシュの non-orthogonality が大きい場合には,非直交補正の処理をルー
プで行います.
非直交補正のループの回数は,”fvSolution” ファイルに設定します.
ループで圧力値が更新されるのに伴い非直交補正の量が更新されます.
1
𝐴 𝑃 𝑓
∆
𝑝 𝑁 − 𝑝 𝑃
𝒅
+
1
𝐴 𝑃 𝑓
𝒌 ∙ 𝛻𝑝 𝑓
𝑓
=
𝑯
𝐴 𝑃 𝑓
∙ 𝑺 𝑓
𝑓
125
pEqn.H (Part2)|非直交補正ループ
13 // Non-orthogonal pressure corrector loop
14 while (simple.correctNonOrthogonal())
(省略)
29 }
SIMPLE
{
nNonOrthogonalCorrectors 5;
}
126. 126
pEqn.H (Part3)
31 #include "continuityErrs.H"
32
33 // Explicitly relax pressure for momentum corrector
34 p.relax();
35
36 // Momentum corrector
37 U = HbyA - rAU*fvc::grad(p);
38 U.correctBoundaryConditions();
39 fvOptions.correct(U);
40 }
pEqn.H
34行目:圧力場の不足緩和
SIMPLE法では,圧力の修正量 𝑝 𝑛 − 𝑝 𝑛−1 (𝑝 𝑛 : ポアソン方程式の解,
𝑝 𝑛−1 : 前のステップの解) が過大に評価され,計算の収束性に悪影響を与え
る傾向があるため,それを防ぐために不足緩和が行われます.
𝑝 𝑛−1 + 𝛼 𝑝 𝑝 𝑛 − 𝑝 𝑛−1
緩和係数 𝛼 𝑝 の値は,fvSolution ファイルに設定します.
relaxationFactors
{
fields
{
p 0.3;
}
}
127. 圧力場に対する不足緩和は次のコードで行われます.
127
pEqn.H (Part3)|圧力場の不足緩和
873 template<class Type, template<class> class PatchField, class GeoMesh>
874 void Foam::GeometricField<Type, PatchField, GeoMesh>::relax(const scalar alpha)
875 {
876 if (debug)
877 {
878 InfoIn
879 (
880 "GeometricField<Type, PatchField, GeoMesh>::relax"
881 "(const scalar alpha)"
882 ) << "Relaxing" << endl << this->info() << " by " << alpha << endl;
883 }
884
885 operator==(prevIter() + alpha*(*this - prevIter()));
886 }
GeometricField.C
130. pUCoupledFoam は,foam のバージョン3.1に実装されています.
foam のインストール方法はこちら
ソースコードのヘッダー部の説明を見てみましょう.
支配方程式は,simpleFoam の場合と同じですが,この解法が異なります.
130
pUCoupledFoam の概要
Application
pUCoupledFoam
Description
Steady-state solver for incompressible, turbulent flow, with implicit
coupling between pressure and velocity achieved by BlockLduMatrix
Turbulence is in this version solved using the existing turbulence
structure.
Authors
Klas Jareteg, Chalmers University of Technology,
Vuko Vukcevic, FMENA Zagreb.
131. 131
pUCoupledFoam の参考資料
開発者 Klas Jareteg さんの資料 (pdf)
• pUCoupledFoam の開発者自身による説明資料です.
• OpenFOAMへの実装方法が詳しく説明されておりとても参考になります.
私のスライドも上記の資料を参考にしていますが,この資料に記載のない内
容までカバーできるように理解を深めていきたいと思います.
133. 133
解ベクトル Up
Info << "Creating field Up¥n" << endl;
volVector4Field Up
(
IOobject
(
"Up",
runTime.timeName(),
mesh,
IOobject::NO_READ,
IOobject::AUTO_WRITE
),
mesh,
dimensionedVector4("zero", dimless, vector4::zero)
);
流速 𝑈 の3つの方向成分と圧力 𝑝 の計4成分をもつベクトルを vector4
タイプの変数で表します.
𝑈 𝑥, 𝑈 𝑌, 𝑈 𝑍, 𝑝 𝑇
4成分
135. 135
参考文献
[1] http://www.claymath.org/millennium-problems (accessed 03/16/2014)
[2] ファーツィガー,ペリッチ,『コンピュータによる流体力学』
[3] http://www.openfoam.org/features/numerical-method.php
(accessed 03/16/2014)
[4] http://www.geocities.jp/penguinitis2002/index.html (accessed 03/16/2014)
[5] https://www.simonsfoundation.org/quanta/20140224-a-fluid-new-path-in-
grand-math-challenge/ (accessed 03/16/2014)
[6] http://www.math.kz/images/journal/2013-4/Otelbaev_N-S_21_12_2013.pdf
(accessed 03/16/2014)
[7] http://arxiv.org/pdf/1402.0290v2.pdf (accessed 03/16/2014)
138. 138
解析的なアプローチ:最近の動向
Navier-Stokes 方程式の解の存在の証明に関しては,2014年に入ってから
多くのニュースがあります [5].
• カザフスタンの Mukhtarbay Otelbaev 教授が,1月に解の存在証明に成
功したと宣言した論文 [6] については現在世界中の数学者による検証中で
すが,既に証明に問題があることが判明し,教授がその解決に取り組んで
いるそうです.
The most recent attempt to garner serious attention, by
Mukhtarbay Otelbaev of the Eurasian National University in Astana,
Kazakhstan, is still under review, but mathematicians have already
uncovered significant problems with the proof, which Otelbaev is
trying to solve [5].
• カリフォルニア大学の Terence Tao 教授が2月に論文 [7] を発表しました.
139. 139
This work is licensed under the Creative Commons
Attribution-ShareAlike 4.0 International License.
To view a copy of this license, visit
http://creativecommons.org/licenses/by-sa/4.0/