辞書

2018年4月14日

はじめに

OpenFOAM の入力形式である辞書ファイルを扱ってみる。

バージョン

OpenFOAM v1712

辞書

OpenFOAM の入力形式は「辞書」(dictionary) という次のようなものである。

dict

subDict
{
    keyword value;

    subDict2
    {
        keyword2 value2;
    }
}

"subDict { ... }" のような形式は「辞書」と呼ばれ、その中にキーワードと値の組みを書くことができる。キーワード/値の組みはセミコロンで区切られる。辞書は "subDict2 { ... }" のように入れ子にすることもできる。C++ と同様のコメントを書くこともできる。ファイル全体がひとつの辞書なので、上の "subDict" はサブ辞書である。

マクロが使えたり、正規表現が使えたり、インクルードできたり、コードまで書けたりと何かと便利であるが、なにより構造が単純で人間に読みやすく、コメントも使えるのがよい。欠点といえば、これを十全に処理するプログラムを自力で書くのがちょっと大変なことである。じゃあ OpenFOAM で処理すればいいやん、という話で、ソルバーを書かなくてもデータの処理だけでもできないかなあと思い、調べてみた。

辞書の読み込み

上の辞書 "dict" を読み込んで、サブ辞書 "subDict" のキーワード "keyword" の値を表示させるプログラムは以下のようになる。

dictionary.C

#include "messageStream.H"
#include "Enum.H"
#include "IFstream.H"

using namespace Foam;


void printDictionary(const dictionary& dict, int n=0)
{
    forAllConstIter(IDLList<entry>, dict, iter)
    {
        for (int i = 0; i < n; i++)
        {
            Info<< " ";
        }

        if (iter().isDict())
        {
            Info<< iter().keyword() << endl;
            printDictionary(iter().dict(), n+1);
        }
        else
        {
            Info<< iter().keyword() << " "
                << dict.lookup(iter().keyword()) << endl;
        }
    }
}


int main(int argc, char *argv[])
{
    IFstream is("dict");
    dictionary dict("dict");
    dict.read(is);

    Info<< dict.subDict("subDict").lookup("keyword") << endl;

    printDictionary(dict);

    return 0;
}

IFstream でファイル読み込み用の入力ストリームを作り、それを辞書 dict の read() 関数に渡して辞書ファイルを読み込んでいる。サブ辞書には dict の subDict() 関数でアクセスし、さらに lookup() 関数でキーワードの値を取り出して Info で出力させている。printDictionary() 関数では、辞書の項目を再帰的に巡回して出力している。なお、本プログラムのコンパイルには特に必要なオプションはない。

もう少し OpenFOAM らしい書き方も考えてみた。

dictionary2.C

#include "argList.H"
#include "Time.H"

using namespace Foam;


int main(int argc, char *argv[])
{
    argList::noBanner();
    argList args(argc, argv);

    Time runTime(Time::controlDictName, args);

    IOdictionary dict
    (
        IOobject
        (
            "dict",
            "",
            runTime,
            IOobject::MUST_READ
        )
    );

    Info<< dict.subDict("subDict").lookup("keyword") << endl;

    return 0;
}

argList のオブジェクトを作って Time のオブジェクトを作り、IOobject のオブジェクトを作って辞書の読み込みをする。ソルバーで見るパターンである。IOobject が要求しているのは、辞書の名前、パス (ソルバーでは "system" や "constant"、時刻ディレクトリなどになる)、objectRegistry オブジェクト (ソルバーでは runTime や mesh が渡される)、入出力オプションである。

この方法では、辞書の最初に以下のヘッダーが必要である。

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

ヘッダーにはクラスやオブジェクト名 (ファイル名と合わせる) を記述する。

さらに問題は、Time が system/controlDict を要求することである。

system/controlDict

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

deltaT          1;
writeInterval   1;

Time オブジェクトが最低限必要とするものは上記のものだけのようである。system/controlDict が出てきたことで、急に OpenFOAM らしくなってしまった。

OpenFOAM ソルバーの本体は argList と Time だと言っても過言ではない。ソルバーを実行したときに表示されるバナーを表示しているのは、argList のコンストラクタである (Time オブジェクトの作成に必要)。上のコードでは、バナー表示を抑制するために argList の static 関数 noBanner() をオブジェクト作成に先んじて実行している。これで (controlDict については目をつぶれば) 見た目はふつうのプログラムのように動作する。

辞書の書き出し

辞書の書き出しは、次のようにする。

dictionary3.C

#include "messageStream.H"
#include "Enum.H"
#include "IFstream.H"
#include "OFstream.H"

using namespace Foam;


int main(int argc, char *argv[])
{
    IFstream is("dict");
    dictionary dict("dict");
    dict.read(is);

    OFstream os("dict2");
    dictionary dict2(dict);

    dict2.subDict("subDict").add(keyType("keyword3"), "value3");
    dict2.subDict("subDict")
        .subDict("subDict2").set(keyType("keyword2"), "value4");

    dict2.write(os, false);

    return 0;
}

IFstream の代わりに OFstream を使って、write() 関数で辞書をファイルに書き出すことができる。上の例では、キーワードを追加したり、値を変更したりしている。write() 関数の第2引数を指定しない場合は true を指定したのと同じになるが、その場合は dict2 の内容全体が "{ ... }" で囲まれる。

プログラムを実行すると、次のような内容のファイルが出力される。

dict2

subDict
{
    keyword         value;
    subDict2
    {
        keyword2        value4;
    }
    keyword3        value3;
}

もっと! OpenFOAM たんけんたい 目次へ