計算の確認と計算結果の処理2021年6月20日 | |
はじめに計算の確認と計算結果の処理について。 使用バージョンOpenFOAM v2012、v8 残差の確認function object を用いる方法残差を出力するには function object という機能を用いる。OpenCFD 版 v3.0+ 以降では、system/controlDict で次のように設定する。
functions
{
#includeFunc solverInfo
}
デフォルトでは U と p の残差だけ表示するようになっている。残差を表示する変数を指定するには、次のように書けばよい。
functions
{
#includeFunc solverInfo(U,p,k,epsilon)
}
あるいは、次のように書いてもよい。
functions
{
residuals
{
type solverInfo;
libs ("libutilityFunctionObjects.so");
fields (U p k epsilon);
}
}
後者が本来の function object の書き方で、"#includeFunc" のほうは短縮系のようなものである。実際には、$FOAM_ETC/caseDicts/postProcessing/numerical/solverInfo を読み込んでいる。 libs は、OpenCFD 版では "libutilityFunctionObjects.so" の代わりに、引用符なしで utilityFunctionObjects と書いてもよい。 すべてのフィールドを指定するには次のように書ける。
functions
{
residuals
{
type solverInfo;
libs ("libutilityFunctionObjects.so");
fields (".*");
}
}
ソルバーを実行すると、"postProcessing" ディレクトリの中にテキストデータが出力される。グラフを表示するには、foamMonitor を用いる (gnuplot が必要)。 $ foamMonitor -l postProcessing/solverInfo/0/solverInfo.dat ただし、残差以外の余計な情報も表示される。気に入らなければ gnuplot などで自分でグラフを描けばよい。 writeResidualFields を有効にすると、残差の分布を書き出すことができる。
functions
{
residuals
{
type solverInfo;
libs ("libutilityFunctionObjects.so");
fields (".*");
writeResidualFields yes;
writeControl outputTime;
}
}
計算結果の中に "initialResidual:Ux" といったフィールドができる。 OpenCFD 版 v1812 以前および Foundation 版バージョン 2.4.0 から、残差を出力する function object が使える。system/controlDict で次のように設定する。 system/controlDict
functions
{
#includeFunc residuals
}
デフォルトでは p と U の残差だけ表示するようになっている。残差を表示する変数を指定するには、次のようにする。 system/controlDict
functions
{
#includeFunc residuals(U,p,k,epsilon)
}
あるいは、次のように書いてもよい。 system/controlDict
functions
{
residuals
{
type residuals;
libs ("libutilityFunctionObjects.so");
fields (U p);
}
}
ソルバーを実行すると、"postProcessing" ディレクトリの中にテキストデータが出力される。グラフを表示するには、foamMonitor を用いる。 $ foamMonitor -l postProcessing/residuals/0/residuals.dat バージョン 3.0.x 以前バージョン 3.0.x 以前では、以下のようにする。
functions
{
residuals
{
type residuals;
functionObjectLibs ("libutilityFunctionObjects.so");
fields (U p);
}
}
計算ログから残差を得る方法ソルバーの出力を log ファイルとして保存してあれば、つぎのようにして残差の情報を取り出すことができる。 $ foamLog log logs というディレクトリが作成され、中に各変数の残差の情報が入る。これらを gnuplot などでプロットすれば、残差の傾向がわかる。たとえば、つぎのようなスクリプトを用意しておく。 plot #!/usr/bin/gnuplot -persist set title 'Residuals' set xlabel 'Time' set ylabel 'Initial residual' set logscale y set format y "%.0e" set grid plot 'logs/Ux_0' title 'Ux' w l replot 'logs/Uy_0' title 'Uy' w l replot 'logs/Uz_0' title 'Uz' w l replot 'logs/p_0' title 'p' w l replot 'logs/p_rgh_0' title 'p_rgh' w l replot 'logs/T_0' title 'T' w l replot 'logs/h_0' title 'h' w l replot 'logs/k_0' title 'k' w l replot 'logs/epsilon_0' title 'epsilon' w l replot 'logs/omega_0' title 'omega' w l replot 'logs/Rxx_0' title 'Rxx' w l replot 'logs/Rxy_0' title 'Rxy' w l replot 'logs/Rxz_0' title 'Rxz' w l replot 'logs/Ryy_0' title 'Ryy' w l replot 'logs/Ryz_0' title 'Ryz' w l replot 'logs/Rzz_0' title 'Rzz' w l set terminal png set output 'residual.png' replot これは単純だがあまりスマートではない。つぎのように存在するファイルだけ表示させたらよいかもしれない。
#!/bin/sh
VARS=`ls logs | grep FinalRes | sed -e 's/FinalRes_0//'`
if [ "$VARS" = "" ] ; then
VARS=`ls logs | sed -e 's/_0//'`
fi
DIR=logs
PLOT=""
HEAD=1
for VAR in $VARS ; do
if [ -e "logs/${VAR}_0" ] ; then
if [ $HEAD -eq 1 ] ; then
PLOT="plot 'logs/${VAR}_0' title '$VAR' w l"
HEAD=0
else
PLOT="$PLOT, 'logs/${VAR}_0' title '$VAR' w l"
fi
fi
done
gnuplot -p <<EOF
set title 'Residuals'
set xlabel 'Time'
set ylabel 'Initial residual'
set logscale y
set format y "%.0e"
set grid
$PLOT
set terminal png
set output 'residual.png'
replot
EOF
マルチリージョンでは余計なものが表示されるので、少し修正がいる。
#!/bin/sh
VARS=`ls logs | grep FinalRes | sed -e 's/FinalRes_0//'`
if [ "$VARS" = "" ] ; then
VARS=`ls logs | sed -e 's/_0//'`
fi
DIR=logs
PLOT=""
HEAD=1
for VAR in $VARS ; do
if [ -e "logs/${VAR}_0" ] ; then
V=`head -1 logs/${VAR}_0 | awk '{print $2}'`
if [ $V != "region" ] ; then
I=0
for FILE in logs/${VAR}_* ; do
if [ $HEAD -eq 1 ] ; then
PLOT="plot '$FILE' title '${VAR}_$I' w l"
HEAD=0
else
PLOT="$PLOT, '$FILE' title '${VAR}_$I' w l"
fi
I=`expr $I + 1`
done
fi
fi
done
gnuplot -p <<EOF
set terminal x11
set title 'Residuals'
set xlabel 'Time'
set ylabel 'Initial residual'
set logscale y
set format y "%.0e"
set grid
$PLOT
set terminal png
set output 'residual.png'
replot
EOF
また、輻射モデルに fvDOM を用いた場合は輻射モデルに関する残差が大量に表示されてしまうので、表示から外してしまった方が良い。 VARS=`ls logs | grep FinalRes | sed -e 's/FinalRes_0//' | grep -v ILambda` 実行権限を設定する。 $ chmod +x plot つぎのように実行する。 $ ./plot これは各変数の代数方程式の初期残差をプロットする。プロットを見て、残差が順調に下がっているかチェックする。もし残差が上下して落ち着かないようであれば、関係しそうな緩和係数を小さくしてみたり、境界条件などを見直したりしてみる。 上記のスクリプトの場合、非定常解析で各時間ステップにおいて反復計算を行うときは、各反復計算の最初の初期残差が表示される。各反復計算のすべての初期残差を foamLog の出力データからプロットするのは難しいので、つぎのようなスクリプトを用意する。 myFoamLog
#!/bin/sh
if [ $# -lt 1 ] ; then
echo "usage: myFoamLog <log>"
exit 0
fi
VARS="Ux Uy Uz T h"
#VARS="$VARS p p_rgh"
#VARS="$VARS k epsilon omega Rxx Rxy Rxz Ryy Ryz Rzz"
DIR=logs
mkdir -p $DIR
for VAR in $VARS ; do
cat log | grep "Solving for $VAR" | grep -v "solution singularity" | sed s/,//g | awk '{print $8}' > $DIR/${VAR}_0
SIZE=`wc $DIR/${VAR}_0 | awk '{print $1}'`
if [ ! $SIZE -eq 0 ] ; then
echo $VAR
else
rm $DIR/${VAR}_0
fi
done
圧力や乱流統計量は、速度などと計算回数が一致しない可能性がある。 実行権限を設定する。 $ chmod +x myFoamLog つぎのように実行する。 $ ./myFoamLog log このスクリプトの出力データをプロットすれば、すべての反復計算の残差が表示される。 流量の確認ソルバーのオプションを用いて、境界面の流量を計算することができる。 $ simpleFoam -postProcess -func 'flowRatePatch(name=outlet)' -latestTime "-postProcess" は function object を実行するためのオプションである。"-func" は function object の関数を指定するためのオプションで、flowRatePatch は境界面 (パッチ) の流量を計算する関数である。"-latestTime" オプションは最後の結果のみ計算させるものである。計算には流束 phi が用いられている。phi は、非圧縮性流体ソルバーではセル界面の流速ベクトルと面積ベクトルの内積をとったもので、圧縮性流体ソルバーではそれに密度がかかる。それの総和をとることで体積流量か質量流量が計算される。これにより想定した流量が入っているかどうかをチェックできる。また、すべての入口・出口でそれぞれの流量を計算することで収支が合っているかどうかを見て、計算が十分に収束しているか確認できる。 計算中に流量を計算させたい場合は、system/controlDict で次のように設定する。
functions
{
#includeFunc flowRatePatch(name=outlet);
}
ソルバーを実行すると、"postProcessing" ディレクトリの中にテキストデータが出力される。 計算結果をログに表示させたい場合はつぎのようにする。
functions
{
#includeFunc flowRatePatch(name=outlet,log=true);
}
流量収支の計算2 つの境界面の流量収支を計算するには、次のようにする。
functions
{
massBalance
{
type fieldValueDelta;
libs ("libfieldFunctionObjects.so");
writeControl timeStep;
writeInterval 1;
operation add;
region1
{
type surfaceFieldValue;
libs ("libfieldFunctionObjects.so");
operation sum;
fields (phi);
writeFields false;
regionType patch;
name inlet;
}
region2
{
type surfaceFieldValue;
libs (fieldFunctionObjects);
operation sum;
fields (phi);
writeFields false;
regionType patch;
name outlet;
}
}
}
fieldValueDelta は 2 つの値に演算を施すためのものである。region1、region2 でそれぞれ surfaceFieldValue によって面の流束 phi の総和をとって流量を計算している (flowRatePatches の中身はこの surfaceFieldValue である)。ここでは境界面 (パッチ) inlet と outlet の流量収支を計算している。OpenFOAM では面の法線ベクトルは外側を向いており、その関係で流量は流入が負、流出が正になっている。そのため、2 つの差ではなく和をとっている (operation の add)。 上記の方法では 2 つの面の収支しかとれない。入口と出口が複数ある場合は、1 つの面にまとめる。OpenFOAM には計算メッシュのフェイスやセルをまとめるセットやゾーンというものがある。それらを編集するためのプログラムとして setSet がある。setSet ですべての境界面 (パッチ) をひとつにまとめるには、次のようにする。 $ setSet readline>faceSet allPatches new patchToFace inlet readline>faceSet allPatches add patchToFace outlet ... readline>faceZoneSet allPacthes new setToFaceZone allPatches readline>quit 最初に境界面 inlet により allPatches というフェイスセットを作り、それに outlet などを追加している。最後にフェイスセットからフェイスゾーンを作成している。 flowRateFaceZone を用いると、ゾーンの流量を計算できる。 $ simpleFoam -postProcess -func 'flowRateFaceZone(name=allPatches)' これにより全境界面の収支が得られる。 バージョン 3.0.x 以前バージョン 3.0.x 以前では、ユーティリティの patchIntegrate を用いる。 $ patchIntegrate -latestTime phi inlet 計算中に流量を計算させたい場合は system/controlDict で次のように設定する。
functions
{
flowRate
{
type faceSource;
functionObjectLibs ("libfieldFunctionObjects.so");
fields (phi);
source patch;
operation sum;
sourceName outlet;
valueOutput false;
}
}
熱量の確認壁面の熱量は、計算結果に対して次のように計算させることができる。 $ buoyantSimpleFoam -postProcess -func wallHeatFlux -latestTime ただし、圧縮性流体のケースにのみ対応する (buoyantBoussinesqSimpleFoam などでは使えない)。想定の熱量が入っているか、収支が合っているか確認できる。 計算中の熱量を得たい場合は、system/controlDict で次のようにする。
functions
{
#includeFunc wallHeatFlux(writeControl=timeSt
}
ソルバーを実行すると、"postProcessing" ディレクトリの中にテキストデータが出力される。また、時刻ディレクトリにフィールドファイルが書き出される。 流量同様にして、熱量収支を計算することができる。 バージョン 3.0.x 以前バージョン 3.0.x 以前では、ユーティリティの wallHeatFlux で壁面の熱量を計算させることができる。 $ wallHeatFlux -latestTime ただし、constant/turbulenceProperties を要求するので、バージョン 2.4.x 以前なら定常解析の場合は仮に置いておけばよい。 y+ の確認ソルバーのオプションを用いて、計算結果の y+ を計算することができる。 $ simpleFoam -postProcess -func yPlus -latestTime ソルバーを実行すると、"postProcessing" ディレクトリの中にテキストデータが出力される。また、時刻ディレクトリにフィールドファイルが書き出される。y+ が想定の範囲に入っているか確認できる。 バージョン 3.0.xバージョン 3.0.x では、ユーティリティの yPlus を用いる。 $ yPlus -latestTime バージョン 2.xバージョン 2.x 以前では、ユーティリティの yPlusRAS/yPlusLES を用いる。 $ yPlusRAS -latestTime 圧縮性流体のケースではオプション "-compressible" を付ける。 面平均値の確認計算結果について物理量の面における平均値を確認するには、function object の patchAverage を用いる。 $ simpleFoam -postProcess -func 'patchAverage(name=outlet,U)' -latestTime この場合は、postProcess ユーティリティを用いてもよい。 $ postProcess -func 'patchAverage(name=outlet,U)' -latestTime ただし、常にソルバーを postProcess に置き換えられるわけではない。 計算中の結果が欲しい場合は、system/controlDict で次のようにする。
functions
{
#includeFunc patchAverage(name=outlet,U)
}
ソルバーを実行すると、"postProcessing" ディレクトリの中にテキストデータが出力される。 バージョン 3.0.x 以前バージョン 3.0.x 以前では、ユーティリティの patchAverage を用いる。 $ patchAverage -latestTime T outlet 体積平均値の計算定常解析の場合、計算の収束性を見るために、物理量の体積平均値の推移もチェックしたほうがよい。物理量の体積平均値を得るために、function object をつぎのように設定する。 system/controlDict
functions
{
#includeFunc mag(U,executeControl=timeStep)
volAvgU
{
type volFieldValue;
libs ("libfieldFunctionObjects.so");
fields (mag(U));
writeFields false;
regionType all;
operation volAverage;
writeControl timeStep;
writeInterval 1;
}
}
この場合は、ベクトルである U をスカラーに変換してから体積平均値を計算するようにしている。計算結果は "postProcessing" ディレクトリの中にテキストデータとして出力される。この計算は計算時に行うほうがよいだろう。 マルチリージョンのケースでは、"region" によるリージョンが必要である。 バージョン 4.xバージョン 4.x では、以下のようにする。 system/controlDict
functions
{
#includeFunc mag(U,executeControl=timeStep)
volAvgU
{
type volRegion;
libs ("libfieldFunctionObjects.so");
fields (mag(U));
writeFields false;
regionType all;
operation volAverage;
wirteControl timeStep;
writeInterval 1;
}
}
バージョン 3.0.x 以前バージョン 3.0.x 以前では、以下のようにする。 system/controlDict
functions
{
magU
{
type calcMag;
functionObjectLibs ("libFVFunctionObjects.so");
fieldName U;
resultName magU;
outputControl outputTime;
outputInteval 1;
}
volAvgU
{
type cellSource;
functionObjectLibs ("libfieldFunctionObjects.so");
fields (magU);
valueOutput false;
source all;
operation volAverage;
outputControl timeStep;
outputInterval 1;
}
}
計算結果の時間平均化非定常解析の計算結果を時間平均化する必要がある場合は、system/controlDict で以下のように設定する。 system/controlDict
functions
{
fieldAverage
{
type fieldAverage;
libs ("libfieldFunctionObjects.so");
fields
(
U
{
mean on;
prime2Mean on;
base time;
}
p
{
mean on;
prime2Mean on;
base time;
}
);
writeControl writeTime;
writeInterval 1;
}
}
ソルバーの "-postProcess" オプションで function object を実行する。 $ pimpleFoam -postProcess フィールドデータとして "UMean"、"pMean" などが出力される。 上の例では計算後に処理しているが、計算前に設定しておけば、計算時に function object が実行され、平均値が出力される。 バージョン 3.0.x 以前バージョン 3.0.x 以前では、以下のようにする。
functions
{
fieldAverage
{
type fieldAverage;
functionObjectLibs ("libfieldFunctionObjects.so");
fields
(
U
{
mean on;
prime2Mean on;
base time;
}
p
{
mean on;
prime2Mean on;
base time;
}
);
outputControl outputTime;
outputInterval 1;
}
}
execFlowFunctionObjects で function object を実行する。 $ execFlowFunctionObjects せん断応力の計算壁面のせん断応力は、計算結果に対して次のように計算させることができる。 $ simpleFoam -postProcess -func wallShearStress -latestTime ソルバーを実行すると、"postProcessing" ディレクトリの中にテキストデータが出力される。また、時刻ディレクトリにフィールドファイルが書き出される。 バージョン 3.0.x 以前バージョン 3.0.x 以前では、ユーティリティの wallShearStress で壁面の熱量を計算させることができる。 $ wallShareStress -latestTime 圧力損失の計算圧力損失を計算するには、全圧の差をとればよい。全圧については、function object のtotalPressureIncompressible/totalPressureCompressible を用いる。差をとるには、流量収支のところで用いた fieldValueDelta で operation を subtract にする。
functions
{
#includeFunc totalPressureIncompressible(rhoInf=1.3,executeControl=timeStep)
pressureLoss
{
type fieldValueDelta;
libs ("libfieldFunctionObjects.so");
writeControl timeStep;
writeInterval 1;
operation subtract;
region1
{
type surfaceFieldValue;
libs ("libfieldFunctionObjects.so");
operation areaAverage;
fields (total(p));
writeFields false;
regionType patch;
name inlet;
}
region2
{
type surfaceFieldValue;
libs (fieldFunctionObjects);
operation areaAverage;
fields (total(p));
writeFields false;
regionType patch;
name outlet;
}
}
}
totalPressureIncompressible の場合は、rhoInf で密度を指定する必要がある。上の例では、境界面 inlet と outlet の間で圧力損失を計算している。 もし、境界ではない面から圧力損失を求めたい場合は、たとえばつぎのようにする。
functions
{
#includeFunc totalPressureIncompressible(rhoInf=1.3,executeControl=timeStep)
pressureLoss
{
type fieldValueDelta;
libs ("libfieldFunctionObjects.so");
writeControl timeStep;
writeInterval 1;
operation subtract;
region1
{
type surfaceFieldValue;
libs ("libfieldFunctionObjects.so");
operation areaAverage;
fields (total(p));
writeFields false;
regionType sampledSurface;
name x=0.1m;
sampledSurfaceDict
{
type plane;
planeType pointAndNormal;
pointAndNormalDict
{
point (0.1 0 0);
normal (1 0 0);
}
}
}
region2
{
type surfaceFieldValue;
libs (fieldFunctionObjects);
operation areaAverage;
fields (total(p));
writeFields false;
regionType patch;
name outlet;
}
}
}
上の例では、x = 0.1 m の断面と outlet で全圧差を計算している。 残差などのモニタリングバージョン 2.4.0 からは、foamMonitor により残差など function object から出力されたデータのモニタリングが可能である。たとえば、残差と流速の体積平均値をモニターするとする。 ※OpenCFD 版 v1906 以降は residuals の代わりに solverInfo を用いる。 system/controlDict
functions
{
#includeFunc residuals
#includeFunc mag(U,executeControl=timeStep)
volAvgU
{
type volFieldValue;
libs ("libfieldFunctionObjects.so");
fields (mag(U));
writeFields false;
regionType all;
operation volAverage;
writeControl timeStep;
writeInterval 1;
}
}
残差をモニターするには、計算中につぎのように実行する。 $ foamMonitor -r 1 -l postProcessing/residuals/0/residuals.dat & 流速の体積平均値の場合は、つぎのように実行する。 $ foamMonitor -r 1 postProcessing/volAvgU/0/cellSource.dat & 計算実行時に自動的に実行されるようにするとよいだろう。つぎのようなスクリプトを用意しておくとよいかもしれない。 run
#!/bin/sh
rm -rf postProcessing/residuals
rm -rf postProcessing/volAvgU
. $WM_PROJECT_DIR/bin/tools/RunFunctions
SOLVER=`getApplication`
foamJob $SOLVER
sleep 1
TIME=`cat log | awk '/Create mesh for time = /{print $6; exit}'`
FILE_RESIDUALS="postProcessing/residuals/$TIME/residuals.dat"
FILE_AVG_U="postProcessing/volAvgU/$TIME/volRegion.dat"
foamMonitor -r 1 -i 5 -l $FILE_RESIDUALS &
foamMonitor -r 1 -i 5 $FILE_AVG_U &
xterm -e "tail -f log" &
バージョン 4.xバージョン 4.x では、以下のようにする。
functions
{
#includeFunc residuals
#includeFunc mag(U,executeControl=timeStep)
volAvgU
{
type volRegion;
libs ("libfieldFunctionObjects.so");
fields (mag(U));
writeFields false;
regionType all;
operation volAverage;
wirteControl timeStep;
writeInterval 1;
}
}
バージョン 3.0.x 以前バージョン 3.0.x 以前では、以下のようにする。 system/controlDict
functions
{
residuals
{
type residuals;
functionObjectLibs ("libutilityFunctionObjects.so");
fields (U p);
}
magU
{
type calcMag;
functionObjectLibs ("libFVFunctionObjects.so");
fieldName U;
resultName magU;
outputControl outputTime;
outputInteval 1;
}
volAvgU
{
type cellSource;
functionObjectLibs ("libfieldFunctionObjects.so");
fields (magU);
valueOutput false;
source all;
operation volAverage;
outputControl timeStep;
outputInterval 1;
}
}
計算結果の確認と処理計算結果の詳細な確認および処理は ParaView で行う。ケースディレクトリで paraFoam を実行すると、ParaView が起動して計算結果が読み込まれる。 $ paraFoam 計算結果の処理の前に、計算結果が物理的に妥当かどうか確認する。 並列計算の結果を処理する場合は、あらかじめ reconstructPar を実行しておく必要がある。 マルチリージョンのケースでは、次のように領域ごとに実行する。 $ paraFoam -region fluid -touch $ paraFoam -region solid -touch 領域ごとに "*.OpenFOAM" ("*" にはケース名などが入る)というファイルができるので、それらを paraFoam ではなく ParaView を起動して読み込む。 paraFoam 自体は、"*.OpenFOAM" ファイル (中身は空でよい)を作成して ParaView で読み込んでいる。この場合、OpenFOAM が用意している OpenFOAM ケースリーダーが使われる。この方法とは別に、"*.foam" という空ファイルを用意して読み込む方法がある。この場合は、ParaView にはじめから用意されている OpenFOAM ケースリーダーが使われる。こちらの場合は、並列計算結果を結合 (reconstructPar) しなくても結果を読み込める特徴がある。ただし、使っている OpenFOAM のバージョンを完全にはサポートしていない可能性があり得る。 フォーマットの変換計算結果ファイルの ascii/binary の変換や compressed/uncompressed の変換を行いたい場合は、system/controlDict を望みのフォーマットに設定したあと、foamFormatConvert を実行する。 $ foamFormatConvert このユーティリティは、結果を読み込み、system/controlDict の設定に従って結果を書き出す。 ステップ数のカウント非定常計算で計算ステップ数を数えるには、"^Time =" を数えればよい。以下のようなスクリプトを作っておけばよいかもしれない。 count_steps
#!/bin/sh
if [ $# -lt 1 ] ; then
echo "count_steps <log>"
exit 0
fi
cat $1 | grep "^Time =" | awk 'BEGIN{i=1} {print i, $3; i++}'
cat $1 | grep "^Time =" | wc | awk '{print $1}'
ログファイルを指定する。 $ ./count_steps log | |
| PENGUINITIS | |