いわて駐在研究日誌

OpenCAE、電子工作、R/C等、徒然なるままに

OpenFOAMと外部プログラムの連携方法

OpenFOAM+外部プログラムの連携方法について調査中。

連携の形には、いくつかの形式がある。

  1. 外部ソルバーから境界条件などを設定し、OFのケースを計算する。
  2. OFケースの一部として例えばタイムステップ毎に外部プログラムを呼び出す。

前者の例としては、DAKOTA(OpenMDAO)+OFの併用が挙げられ、外部ソルバー自体にOFの計算実行を呼び出す機能がある(そのように作られている)。

問題は、2番目で、

  1. OFのフレームワークの中で外部ソルバーの計算を実装してしまう。
  2. MPIなど、スレッド間通信を実装して並行計算を行う。
  3. 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