【OpenFOAM】手法を変えて処理を追加② : ソルバーに処理を追加する

公開:2020/12/05
更新:2020/12/06
12 min読了の目安(約10800字TECH技術記事

オープンCAEアドベントカレンダー 5日目のエントリーです。

はじめに

『【OpenFOAM】手法を変えて処理を追加① : codedFunctionObject』の続きです。
 OpenFOAMの計算に手法を変えて処理を追加していきます。前回はコンパイルなしで計算ファイルであるsystem/controlDictにC++で処理を記述しました。
今回はソルバーをコピーして処理を加えてコンパイルする方法を紹介します。

手法一覧

具体的には以下の方法で行う予定です。実行確認済みのファイルはGitHubで公開しています。

  • 例題を用意する ()(キーワード:pimpleFoam
  • 既存機能でやるとしたら ()(キーワード:functionObjects
  1. 計算ファイルに処理を書き込む ()(キーワード:codedFunctionObject
  2. ソルバーに処理を追加する () 今ここ
  3. クラスとして処理を追加する
  4. 継承クラスに処理を追加
  5. 設定項目を実行時に読み込む(キーワード:IOdictionary
  6. 継承クラスを実行時に選択する(キーワード:runTimeSelectionTable
  7. クラスを別でコンパイルする(キーワード:wmake libso
  8. 継承クラスの一つとしてcodedを追加(キーワード:codedTemplete

動作環境

  • OS : Ubuntu 20.04 LTS (WSL2 on Windows 10)
  • OpenFOAM : ESI版 v2006 (from source)
  • gcc : 9.3.0

2. ソルバーに処理を追加する

まず最初に説明する方法は、ソルバーに直接処理を書き込むという方法です。

main()関数に直接、各面の流量の総和を求めるコードを記述するという方法です。具体的に手順を見ていきます。

pimpleFoamをコピー

機能の追加の方針として、今回は『ソルバーをコピーして名前を変更してべつのそるばーとしてコンパイルする』という方法を用います。この手順について説明していきます。

まず以下のようにpimpleFoamをコピーます。

$ mkdir -p $FOAM_RUN/002.inSolver
$ cp -r $FOAM_SOLVERS/incompressible/pimpleFoam $FOAM_RUN/002.inSolver/scorePimpleFoam
$ cd $FOAM_RUN/002.inSolver/scorePimpleFoam
$ sed -i -e "s/APPBIN)\/pimpleFoam/USER\_APPBIN)\/scorePimpleFoam/g" Make/files

# 下2行はWSLの場合に必要 (windowsはファイル名の大文字小文字を区別できないのでCorrectPhi.HをcorrectPhi.Hと間違えてしまう)
$ cp $FOAM_SRC/finiteVolume/cfdTools/general/CorrectPhi/CorrectPhi.H cpCorrectPhi.H
$ sed -i -e "s/CorrectPhi.H/cpCorrectPhi.H/" pimpleFoam.C

現在のディレクトリ構成のうち重要なものは以下のファイルです。

$ cd $FOAM_RUN/002.inSolver/scorePimpleFoam
$ tree
scorePimpleFoam
├── Make
│   ├── files
│   └── options
├── UEqn.H
├── correctPhi.H
├── cpCorrectPhi.H
├── createFields.H
├── pEqn.H
└── pimpleFoam.C

このうちmain()関数はpimpleFoam.Cに、細かい処理が他の*.Hに記述されています。OpenFOAMではソルバーをコンパイルする際にはwmakeというコマンドを使用します。これはMakefileなどを毎回作成することなく、OpenFOAMの数多くあるライブラリを半自動的に効率的にリンクして実行ファイルを生成してくれるスクリプトです。

$ cd $FOAM_RUN/002.inSolver/scorePimpleFoam
$ wmake
Making dependency list for source file pimpleFoam.C
g++ -std=c++11 -m64 -pthread -DOPENFOAM=2006 -DWM_DP -DWM_LABEL_SIZE=32 -Wall -Wextra -Wold-style-cast -Wnon-virtual-dtor -Wno-unused-parameter -Wno-invalid-offsetof -Wno-attributes -Wno-unknown-pragmas  -O3  -DNoRepository -ftemplate-depth-100 -I/installDir/OpenFOAM-v2006/src/finiteVolume/lnInclude -I/installDir/OpenFOAM-v2006/src/meshTools/lnInclude -I/installDir/OpenFOAM-v2006/src/sampling/lnInclude -I/installDir/OpenFOAM-v2006/src/TurbulenceModels/turbulenceModels/lnInclude -I/installDir/OpenFOAM-v2006/src/TurbulenceModels/incompressible/lnInclude -I/installDir/OpenFOAM-v2006/src/transportModels -I/installDir/OpenFOAM-v2006/src/transportModels/incompressible/singlePhaseTransportModel -I/installDir/OpenFOAM-v2006/src/dynamicMesh/lnInclude -I/installDir/OpenFOAM-v2006/src/dynamicFvMesh/lnInclude -iquote. -IlnInclude -I/installDir/OpenFOAM-v2006/src/OpenFOAM/lnInclude -I/installDir/OpenFOAM-v2006/src/OSspecific/POSIX/lnInclude   -fPIC -c pimpleFoam.C -o Make/linux64GccDPInt32Opt/pimpleFoam.o
g++ -std=c++11 -m64 -pthread -DOPENFOAM=2006 -DWM_DP -DWM_LABEL_SIZE=32 -Wall -Wextra -Wold-style-cast -Wnon-virtual-dtor -Wno-unused-parameter -Wno-invalid-offsetof -Wno-attributes -Wno-unknown-pragmas  -O3  -DNoRepository -ftemplate-depth-100 -I/installDir/OpenFOAM-v2006/src/finiteVolume/lnInclude -I/installDir/OpenFOAM-v2006/src/meshTools/lnInclude -I/installDir/OpenFOAM-v2006/src/sampling/lnInclude -I/installDir/OpenFOAM-v2006/src/TurbulenceModels/turbulenceModels/lnInclude -I/installDir/OpenFOAM-v2006/src/TurbulenceModels/incompressible/lnInclude -I/installDir/OpenFOAM-v2006/src/transportModels -I/installDir/OpenFOAM-v2006/src/transportModels/incompressible/singlePhaseTransportModel -I/installDir/OpenFOAM-v2006/src/dynamicMesh/lnInclude -I/installDir/OpenFOAM-v2006/src/dynamicFvMesh/lnInclude -iquote. -IlnInclude -I/installDir/OpenFOAM-v2006/src/OpenFOAM/lnInclude -I/installDir/OpenFOAM-v2006/src/OSspecific/POSIX/lnInclude   -fPIC -Xlinker --add-needed -Xlinker --no-as-needed  Make/linux64GccDPInt32Opt/pimpleFoam.o -L/installDir/OpenFOAM-v2006/platforms/linux64GccDPInt32Opt/lib \
    -lfiniteVolume -lfvOptions -lmeshTools -lsampling -lturbulenceModels -lincompressibleTurbulenceModels -lincompressibleTransportModels -ldynamicMesh -ldynamicFvMesh -ltopoChangerFvMesh -latmosphericModels -lOpenFOAM -ldl  \
     -lm -o /installDir/inabower-v2006/platforms/linux64GccDPInt32Opt/bin/scorePimpleFoam

このようなメッセージが出ればコンパイル完了です。ちゃんとコンパイルできているかを以下のように確認してみましょう。

動作確認用のケースの準備

まずはこれでpimpleFoamと同じ機能を持ったscorePimpleFoamを実行できるようになりました。動作の確認のために以下のようにチュートリアルもコピーしソルバーを試す準備を行います。

$ cp -r $FOAM_TUTORIALS/incompressible/pimpleFoam/RAS/TJunction $FOAM_RUN/002.inSolver/plainCase
$ cd $FOAM_RUN/002.inSolver/plainCase

system/controlDict

system/controlDictの中のapplicationの項目を以下のように編集します。
※なおこの部分はソルバー自体は読み込みませんが、OpenFOAMで用意されているスクリプトやシェル関数で使用されます。

$FOAM_RUN/002.inSolver/plainCase/system/controlDict

/*--------------------------------*- C++ -*----------------------------------*\
| =========                 |                                                 |
| \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox           |
|  \\    /   O peration     | Version:  v2006                                 |
|   \\  /    A nd           | Website:  www.openfoam.com                      |
|    \\/     M anipulation  |                                                 |
\*---------------------------------------------------------------------------*/
FoamFile
{
    version     2.0;
    format      ascii;
    class       dictionary;
    location    "system";
    object      controlDict;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

// application    pimpleFoam; // before
application     scorePimpleFoam; // after

startFrom       startTime;

startTime       0;
(以下略)

foamRunTutorials

これにより、例えば以下のようにfoamRunTutorialsと実行するとメッシュ生成と計算の実行が行えるようになります。

$ cd $FOAM_RUN/002.inSolver/plainCase
$ foamRunTutorials

foamCleanTutorials

ちなみに実行結果を削除するためには、以下のようにfoamCleanTutorialsを実行します。

$ cd $FOAM_RUN/002.inSolver/plainCase
$ foamCleanTutorials

このように、foamRunTutorialsfoamCleanTutorialsを行うことでソルバーの挙動を確認できます。今後はソルバーをコンパイルする度に、これにより動作を確認します。

ソルバーに処理を記述する

それではいよいよ目的の処理を記述します。
今回追加する場所はpimpleFoam.Cに記載されているmain()関数の中です。
各イタレーションで速度と圧力を計算し終わった後に各流量を求めることとします。

$FOAM_RUN/002.inSolver/scorePimpleFoam/pimpleFoam.C (抜粋)

#include "~~"

int main(int argc, char *argv[])
{
    while (runTime.run())
    {
        // PIMPLE ループ
        while (pimple.loop())
        {
            // 速度と圧力の計算
        }

        // ここでスコアを計算する

        runTime.write();
    }
}

処理をmain()に直接記述する

具体的には以下のようにmain()関数の書かれているpimpleFoam.Cに処理を記述することで処理を追加することができます。

$FOAM_RUN/002.inSolver/scorePimpleFoam/pimpleFoam.C (抜粋)

            while (pimple.correct())
            {
                #include "pEqn.H"
            }

            if (pimple.turbCorr())
            {
                laminarTransport.correct();
                turbulence->correct();
            }
        }

        // 170行目:ここから追加
	{
	    Info << "Calculating score sum of phi" << endl;

	    scalar score = 0.0;

	    forAll(phi.boundaryField(), patchI)
	    {
		const fvPatch& patch = phi.boundaryField()[patchI].patch();
		const scalarField& pphi =  phi.boundaryField()[patchI];
		if (!(patch.name()=="inlet" || patch.name()=="outlet1" || patch.name()=="outlet2")) continue;
		Info << "    sum(" << patch.name() << ") of phi = " << gSum(pphi) << endl;
		score += gSum(pphi);
	    }
	    Info << nl << "score: " << runTime.value() << tab << score << nl << endl;
	}

        runTime.write();

        runTime.printExecutionTime(Info);

以下のようにコンパイルを行い、

$ cd $FOAM_RUN/002.inSolver/scorePimpleFoam
$ wmake

以下のように実行してみましょう。

$ cd $FOAM_RUN/002.inSolver/plainCase
$ foamCleanTutorials
$ foamRunTutorials

出力されたlog.scorePimpleFoamが、前回のcodedFunctionObjectの結果と同じであることを確認します。

処理部分を別ファイルに書きincludeする

このように、pimpleFoam.Cに直接処理を記載することもできますが、OpenFOAMでは慣例として、細かい処理を別の*.Hファイルに記述してそれを#includeするというスタイルが多く用いられています。コーディングルールで決まっているわけではないですし、この手法を避ける方は結構多いのですが、以下ではこの慣例に則って作成してみます。
 つまり、以下のようにpimpleFoam.Cには#include "getScore.H"とだけ記載し、詳しい処理の書かれたgetScore.Hを新たに作成します。

$FOAM_RUN/002.inSolver/scorePimpleFoam/pimpleFoam.C (抜粋)

            while (pimple.correct())
            {
                #include "pEqn.H"
            }

            if (pimple.turbCorr())
            {
                laminarTransport.correct();
                turbulence->correct();
            }
        }

        #include "getScore.H" // 170行目に挿入

        runTime.write();

        runTime.printExecutionTime(Info);

$FOAM_RUN/002.inSolver/scorePimpleFoam/getScore.H (新規作成)

{
    Info << "Calculating score sum of phi" << endl;

    scalar score = 0.0;

    forAll(phi.boundaryField(), patchI)
    {
        const fvPatch& patch = phi.boundaryField()[patchI].patch();
        const scalarField& pphi =  phi.boundaryField()[patchI];
        if (!(patch.name()=="inlet" || patch.name()=="outlet1" || patch.name()=="outlet2")) continue;
        Info << "    sum(" << patch.name() << ") of phi = " << gSum(pphi) << endl;
        score += gSum(pphi);
    }
    Info << nl << "score: " << runTime.value() << tab << score << nl << endl;
}

というわけで現在のファイル構成は以下のようになっていると思います。

$ cd $FOAM_RUN/002.inSolver/scorePimpleFoam
$ tree
scorePimpleFoam
├── Make
│   ├── files
│   └── options
├── UEqn.H
├── correctPhi.H
├── cpCorrectPhi.H
├── createFields.H
├── getScore.H  ## NEW!!
├── pEqn.H
└── pimpleFoam.C

それではコンパイルして動作を確認してみましょう

$ cd $FOAM_RUN/002.inSolver/scorePimpleFoam
$ wmake

$ cd $FOAM_RUN/002.inSolver/plainCase
$ foamCleanTutorials
$ foamRunTutorials

同じlog.scorePimpleFoamが生成されていることを確認します。

最後に

今回はソルバーのmain()関数にそのまま記述する方法を紹介しました。

この追加方法は最も単純な方法ですので、コードの作成に時間をかけたくない場合にとても有効です。一度きりのカスタマイズなどに多く用いられると思います。
 一方でデメリットとしては処理の内容を変更したい場合に毎回コンパイルをする必要があるという点や、処理が複雑になってくるとファイル構造が煩雑になり管理が難しくなってくるという点があります。
 次回以降では、複数の機能を選択できるようにしたり計算ごとに処理の内容を変更したり等をできるように、『処理をクラスや関数として実装する』という方法を見ていきます。