OpenFOAM+外部プログラムの連携方法について調査中。
連携の形には、いくつかの形式がある。
- 外部ソルバーから境界条件などを設定し、OFのケースを計算する。
- OFケースの一部として例えばタイムステップ毎に外部プログラムを呼び出す。
前者の例としては、DAKOTA(OpenMDAO)+OFの併用が挙げられ、外部ソルバー自体にOFの計算実行を呼び出す機能がある(そのように作られている)。
問題は、2番目で、
- OFのフレームワークの中で外部ソルバーの計算を実装してしまう。
- MPIなど、スレッド間通信を実装して並行計算を行う。
- OFのlibfunctionObject機能で、sysCall関数を使って外部ソルバー呼び出す。
などなどがある。どれが良いかは不明で、1,2なんかはC++やスレッド間通信の知識が必要。後者はいろいろ出来そうだが、柔軟性に欠ける気もする。また、1wayか2wayかによっても使い方を選択すべきかもしれない。
最近知ったことだが、OF23xに付属のtutorialsに外部ソルバーとの連携をやっているexternalCoupledCavityというのがあるそうで、ちょっとやってみた。
cp $FOAM_TUTORIAL/heatTransfer/buoyantSimpleFoam/externalCoupledCavity/ .
cd externalCoupledCavity
[externalCoupledCavity]$ tree .
.
|-- 0
| |-- T
| |-- U
| |-- alphat
| |-- epsilon
| |-- k
| |-- mut
| |-- omega
| |-- p
| `-- p_rgh
|-- Allclean
|-- Allrun
|-- Allrun-parallel
|-- Allrun.pre
|-- README
|-- constant
| |-- RASProperties
| |-- g
| |-- polyMesh
| | |-- blockMeshDict
| | `-- boundary
| `-- thermophysicalProperties
|-- externalSolver
`-- system
|-- controlDict
|-- decomposeParDict
|-- fvSchemes
`-- fvSolution
4 directories, 24 files
more README ---> buoyantCavity tutorial をベースにhot/cold patch設定温度を1度づつ外部ソルバーから変化させるらしい。
constant/systemフォルダには特別な記述なし。
0/Tのpatch境界条件に、以下の記述があるので、これは外部連携を想定した境界条件を作成してあるということか....。
hot
{
type externalCoupledTemperature;
commsDir "${FOAM_CASE}/comms";
fileName "data";
initByExternal yes;
log true;
value uniform 307.75; // 34.6 degC
}
cold
{
type externalCoupledTemperature;
commsDir "${FOAM_CASE}/comms";
fileName "data";
initByExternal yes;
log true;
value uniform 288.15; // 15 degC
}more Allrun ---> こちらも"externalSolver"を呼び出している以外はシンプルそのもの。
[externalCoupledCavity]$ more Allrun
#!/bin/sh
cd ${0%/*} || exit 1 # run from this directory
# Source tutorial run functions
. $WM_PROJECT_DIR/bin/tools/RunFunctions
./Allrun.pre
runApplication $(getApplication) &
./externalSolver
とりあえず実行させてみる。
externalCoupledCavity]$ ./Allrun
Running blockMesh on /home/waku/Latest/externalCoupledCavity
Running createExternalCoupledPatchGeometry on /home/waku/Latest/externalCoupledCavity
Executing dummy external solver
External: initialisation: creating comms/data.in
Running buoyantSimpleFoam on /home/waku/Latest/externalCoupledCavity
External: found lock file comms/OpenFOAM.lock - waiting
External: found lock file comms/OpenFOAM.lock - waiting
External: step 1
External: lock not present - taking control
External: sleeping for 1 secs to simulate external process
External: creating comms/data.in
External: creating lock file comms/OpenFOAM.lock
External: found lock file comms/OpenFOAM.lock - waiting
External: step 2
External: lock not present - taking control
External: sleeping for 1 secs to simulate external process
External: creating comms/data.in
External: creating lock file comms/OpenFOAM.lock
External: found lock file comms/OpenFOAM.lock - waiting
External: found lock file comms/OpenFOAM.lock - waiting
External: step 3
External: lock not present - taking control
External: sleeping for 1 secs to simulate external process:
という感じで、1ステップ毎にlockファイルを作成し、設定条件を更新しているようだ。
externalSolverは以下のような感じのシェルスクリプトで、いろいろhard codingされている。特殊な境界条件を前提としたもので、汎用性はいまいちかなと思われる。
[externalCoupledCavity]$ more externalSolver
#!/bin/sh
#
# Dummy external solver to communicate with OpenFOAM via externalCoupled
# boundary conditions
#
# Functionality is hard-coded for this particular test case
# - patch temperatures increased by 1K on each step
#
cd ${0%/*} || exit 1 # run from this directory
echo "Executing dummy external solver"
commsDir="comms"
lockFile="${commsDir}/OpenFOAM.lock"
dataFile="${commsDir}/data"
waitSec=1
timeOut=10
refGrad=0
valueFraction=1
log()
{
echo "External: $@"
}
init()
{
log "initialisation: creating ${dataFile}.in"
# Hard-coded for 2 patches of size 2250
n=2250
refCold=283
refHot=303
touch "${dataFile}.in"
for i in $(seq 1 $n); do
echo "$refHot $refGrad $valueFraction" >> "${dataFile}.in"
done
for i in $(seq 1 $n); do
echo "$refCold $refGrad $valueFraction" >> "${dataFile}.in"
done
# create lock file to pass control to OF
touch ${lockFile}
}
# tutorial case employs the 'initByExternalOption', so we need to provide
# the initial values
init
totalWait=0
step=0
while [ 1 ]; do
if [ -f $lockFile ]; then
log "found lock file ${lockFile} - waiting"
totalWait=$(expr $totalWait + $waitSec)
if [ $totalWait -gt $timeOut ]; then
log "timeout"
break
else
sleep $waitSec
fi
else
totalWait=0
step=$(expr $step + 1)
log "step $step"
log "lock not present - taking control"
log "sleeping for $waitSec secs to simulate external process"
sleep $waitSec
log "creating ${dataFile}.in"
awk '{if( $1 != "#" ){print $2+1 " 0 1"}}' ${dataFile}.out > ${dataFile}
.in
log "creating lock file ${lockFile}"
touch ${lockFile}
fi
done
log "done"
# ----------------------------------------------------------------- end-of-file