ケースの統一的設定の試み

2017年6月22日

はじめに

OpenFOAM の計算ケースデータは、複数のディレクトリおよびファイルからなっている。ふつうはテキストエディタで設定ファイルを編集するが、設定項目が多くのファイルに散らばっているため、設定やその確認に手間がかかる。特に境界条件は境界ごとではなくフィールドごとの設定となっているため、境界ごとの設定・確認が非常にやりにくい。

ということで、OpenFOAM の機能の範囲内で、設定方法の改善を試みる。

使用バージョン

OpenFOAM 4.1

OpenFOAM の設定ファイルの書式のおさらい

OpenFOAM の設定ファイルは、辞書とキーワードからなる。

dict1
{
    key1 value1;

    dict2
    {
        key2 value2;
        key3 value3;
    }
}

"$" でキーワードの値を参照することができる (これをマクロという)。

key1 value1;

dict1
{
    key2 $key1;
}

同様に辞書の中身を参照することもできる。

dict1
{
    dict2
    {
        key1 value1;
        key2 value2;
    }

    dict3
    {
        $dict2;
    }
}

辞書の中のキーワードを参照することもできる。

dict1
{
    dict2
    {
        key1 value1;
        key2 value2;
    }

    key3 $dict2.key1;
}

上の例では、"$dict2.key1" で dict2 の key1 の値を参照している。ただし、"$" で辞書やキーワードを参照する場合、参照可能な範囲 (スコープ) がある。"." を用いない形式では、上の階層のものは参照可能なようである。

dict1
{
    key1 value1;

    dict2
    {
        key2 $key1;
    }
}

"." を使う形式の場合、階層の相対的な位置を指定する必要がある。 "$.名前" だと同じ階層を意味する。"$..名前" だとひとつ上、"$...名前" だとふたつ上の階層を意味する。

dict1
{
    key1 value1;
}

dict2
{
    dict3
    {
        key2 value2;
    }

    dict4
    {
        key3 $..dict3.key2;
        key4 $...dict1.key1; // $:dict1.key1 でもよい
    }
}

"$:名前" でトップの階層のものを参照できる。

インクルードファイル内のものも参照できる。たとえば、"settings" というファイルの中身が次のようになっていたとする。

settings

dict1
{
    key1 value1;
}

この場合、settings の中身を次のように参照できる。

#include "$FOAM_CASE/settings"

dict2
{
    dict3
    {
        key2 $...dict1.key1;
    }
}

OpenFOAM の設定ファイルは、ソルバーからいろいろ要求されるままに設定することになるので、つい必要な項目以外は無視されていると思いがちだが、実はすべての項目が読み込まれている上、ユーザーは自由に辞書を定義でき、マクロを使ってそれらを利用することもできる。

ケースの統一的設定のアイデア

以上の設定ファイルの書式をふまえて、ケースの統一的設定を試みる。辞書は自由に定義できるので、境界条件を境界ごとに書くために、以下のような辞書を定義する。

setting/boundaries

boundaries
{
    ".*"
    {
        U
        {
            type            noSlip;
        }
        ".*"
        {
            type            zeroGradient;
        }
    }
    inlet
    {
        U
        {
            type            fixedValue;
            value           uniform (10 0 0);
        }
        ".*"
        {
            type            zeroGradient;
        }
    }
    outlet
    {
        p
        {
            type            fixedValue;
            value           uniform 0;
        }
        ".*"
        {
            type            zeroGradient;
        }
    }
    frontAndBack
    {
        ".*"
        {
            type            empty;
        }
    }
}

これをフィールドファイルに反映させる必要があるが、それにはマクロを用いる。以下のように書ける。

0/U

#include "$FOAM_CASE/settings/boundaries"

dimensions      [0 1 -1 0 0 0 0];

internalField   uniform (0 0 0);

boundaryField
{
    inlet
    {
        $...boundaries.inlet.U;
    }
    outlet
    {
        $...boundaries.outlet.U;
    }
    upperWall
    {
        $...boundaries.upperWall.U;
    }
    lowerWall
    {
        $...boundaries.lowerWall.U;
    }
    frontAndBack
    {
        $...boundaries.frontAndBack.U;
    }
}

0/p

#include "$FOAM_CASE/settings/boundaries"

dimensions      [0 2 -2 0 0 0 0];

internalField   uniform 0;

boundaryField
{
    inlet
    {
        $...boundaries.inlet.p;
    }
    outlet
    {
        $...boundaries.outlet.p;
    }
    upperWall
    {
        $...boundaries.upperWall.p;
    }
    lowerWall
    {
        $...boundaries.lowerWall.p;
    }
    frontAndBack
    {
        $...boundaries.frontAndBack.p;
    }
}

このようにしておけば、境界条件をまとめておくことができる。

せっかくなので、内部データ (internalField) なども含めたフィールド情報もまとめておく。

setting/fields

class
{
    U      volVectorField;
    p      volScalarField;
}

dimensions
{
    U      [0 1 -1 0 0 0 0];
    p      [0 2 -2 0 0 0 0];
}

internalField
{
    U      uniform (0 0 0);
    p      uniform 0;
}

0/U

#include "$FOAM_CASE/settings/fields"
#include "$FOAM_CASE/settings/boundaries"

dimensions      $dimensions.U;

internalField   $internalField.U;

0/p

#include "$FOAM_CASE/settings/fields"
#include "$FOAM_CASE/settings/boundaries"

dimensions      $dimensions.p;

internalField   $internalField.p;

計算継続時の条件反映

マクロを用いて境界条件を設定しても、計算結果は数値になってしまうので、settings/boundaries の内容を変更しても計算継続時には変更が反映されない。そこで、境界条件のみ時刻 0 の条件で上書きすることを考える。その際、内部データ (internalField) はそのままにしておけば、結果は変更せずに境界条件のみ変更することができる。

境界条件の上書きには、changeDictionary を用いる。設定は以下のようにする。

system/changeDictionaryDict

FoamFile
{
    version     2.0;
    format      ascii;
    class       dictionary;
    object      changeDictionaryDict;
}

U
{
    #include "$FOAM_CASE/0/U"
}

p
{
    #include "$FOAM_CASE/0/p"
}

dictionaryReplacement
{
    U
    {
        boundaryField
        {
            $....U.boundaryField;
        }
    }
    p
    {
        boundaryField
        {
            $....p.boundaryField;
        }
    }
}

次のように実行する。

$ changeDictionary -latestTime -subDict dictionaryReplacement -enableFunctionEntries

オプション "-latestTime" で最後の結果に変更を加えるように指示している。"-subDict dictionaryReplacement" で辞書 "dictionaryReplacement" の中を処理するように指定し、"-enableFunctionEntries" で "#include" を扱えるようにしている。

注意

  • フィールドファイルで "setConstraintTypes" をインクルードしていると、中身が展開されてしまうので、変なことになる。
  • 最終時刻に含まれていないフィールドの情報が含まれているとうまくいかない。

設定の自動化

上記の方法で境界条件を統一的に設定できるが、ケースごとにこの仕組みを整えるのはめんどうである。そこで、設定の自動化を考える。

  • まず、フィールド情報 (settings/fields) は自分で用意する。サンプルファイルを用意しておいて、それをコピーして修正すればよい。これと境界情報 (constant/polyMesh/boundary) があれば、フィールドファイル (0 ディレクトリの中身) を作ることができる。
  • 境界情報 (settings/boundaries) についても、サンプルファイルを用意しておくことが考えられるが、入口、出口、壁などである程度境界の種類ごとに設定をテンプレート化できるので、それを反映できれば設定が少し楽になる。たとえば、入口も出口も境界タイプは同じ "patch" になるが、そこに入口 "inlet"、出口 "outlet" などといったタグをつけておく (これは手動でやる)。その情報を用いて、"inlet"、"outlet" それぞれ用意したデフォルト設定を適用して境界情報を生成すればよい。

ケースのテンプレートの作成

一般的に、ソルバーごとにフィールド情報は異なるし、境界条件のデフォルト設定も異なるだろう。これに対応するため、ソルバーごとのケースのテンプレートを用意することを考える。

OpenFOAM にはケースのテンプレートを管理する仕組みがある。foamNewCase というものを使うと、ソルバーに対応するケースディレクトリをテンプレートから作成することができる。

まず、~/.OpenFOAM/appTemplates/(バージョン番号) の中にソルバーに対応するケースディレクトリを置いておく。

$ mkdir -p ~/.OpenFOAM/appTemplates/4.1
$ cp -r $FOAM_TUTORIALS/incompressible/simpleFoam/pitzDaily ~/.OpenFOAM/appTemplates/4.1/simpleFoam

"foamNewCase -list" で利用可能なソルバーのリストが得られる。

$ foamNewCase -list

applications available:
    simpleFoam

simpleFoam が指定できるようになっている。foamNewCase はカレントディレクトリにテンプレートケースディレクトリの中身を展開するので、注意が必要である。まず空のディレクトリを作り、その中に移動する。

$ mkdir test
$ cd test

foamNewCase をソルバーを指定して実行する。

$ foamNewCase -app simpleFoam

この場合、test の中に pitzDaily の中身が展開される。

この仕組みを使えば、ソルバーごとのケースのテンプレートを利用することができる。