OpenFOAMで任意のファイル出力

# 使用環境
- Ubuntu 16.04
- OpenFOAM 3.0.1
- OpenFOAM 4.1

# 概要
OpenFOAMではcontrolDictのfunctions機能を使うことで平均値・合計値などを任意の時間ステップで出力できる。
しかしオリジナルソルバーのデバッグ中などでは、より詳細に値を出力したい場合もある。
OpenFOAMの関数だけを用いて任意のファイル・値・形式を出力する方法をまとめた。

## 作成したヘッダファイル（一例）
以下の二つのヘッダファイルを作成した。

- outputInitial.H
- output.H

タイムループ前に`outputInitial.H`でファイルを準備し、タイムループ毎に`output.H`をincludeしてファイルに書き込む

# 例1：laplacianFoamで最大/平均/最小温度をtxtファイルに出力する
簡単な例を交えて説明する。
ここでは温度の最大値・平均値・最小値をgnuplot向けに出力していく例を考える。

下みたいになることが目標。

```csv:case/postProcessing/temperature.txt
#time     max     mean    min
1         273     273     273
2         273.21614       273.001560      272.816514
3         273.41561       273.002146      272.745651
4         273.64815       273.003184      272.647846
...
```
- 項目間がtab区切り
- 1行目は#で始まる

という形式だとgnuplotでプロットできる。

作成したファイルの中身は以下の通り。

```c++:outputInitial.H
const fileName dPath = "postProcessing";  // postProcessingディレクトリを指定する
if(!isDir(dPath)) mkDir(dPath);           // ディレクトリ作成の関数もOFに備わっている

const fileName fPath = dPath / "temperature.txt";  // fileName型の"/"演算子で文字列"postProcessing/temperature.txt"を生成
OFstream os(fPath);                                // ここに出力していく

os << "#time" << tab << "max" << tab << "mean" << tab << "min" << endl;   // 一行目を#で始めるとgnuplotで読み込める
```

```c++:output.H
//volScalarField T                      // volScalarField型の温度Tについて考える
const word t = runTime.timeName();      // 時刻は.timeName()でword{aka string}型、.value()でscalar{aka double}型

os << t << tab << gMax(T) << tab << gAverage(T) << tab << gMin(T) << endl;  // tabでタブ、nlかendlで改行ができる
```

普通のファイル操作よりも簡単にできる。
ケースディレクトリがカレントになる。

これを例えば以下のようにincludeすればファイルに値が書き込まれていく。

```c++:laplacianFoam.C
...
int main(int argc, char *argv[])
{
    ....
    #include "outputInitial.H" // タイムループの前に呼び出し

    while (simple.loop())
    {
        ...
        #include "output.H" // 計算後に呼び出し
    }
    ...
    return 0;
}
```

# 例2：pimpleFoamでpatch毎のcsvファイルに平均圧力と質量流量をそれぞれ出力する
この例ではあまり需要がないかもしれないが、patch毎、phase毎、region毎などでファイルを分けたい場合があるかもしれない。
こういう場合はOFstreamのPtrListを作成すると操作しやすい。

```c++:outputInitial.H
... // ディレクトリ作成

PtrList<OFstream> os(mesh.boundary().size());                // ファイル数(patchの数)を指定する

forAll(mesh.boundary(), patchI) {                            // ここのpatchI(int型)がファイル番号に対応する
    const fvPatch& patch = mesh.boundary()[patchI];
    const fileName fPath = dPath / patch.name() + ".csv";    // patch名をファイル名にする

    os.set(
        patchI,                                              // patchI番目にOFstreamを生成
        new OFstream(fPath)
    );

    os[patchI] << "#time" << tab << "pressure(Pa)" << tab << "massFlow(kg/s)" << endl;   // 単位もつけてみた
}
```

```c++:output.H
//volScalarField p　絶対圧[Pa]
//surfaceScalarField phi　質量流量[kg/s]

const word t = runTime.timeName();

forAll(mesh.boundary(), patchI) {
    const scalarField& pp = p.boundaryField()[patchI];      // patch毎の値を取り出す。fvPatchScalarFieldでも可。
    const scalarField& pphi = phi.boundaryField()[patchI];  // patch毎の値を取り出す。fvsPatchScalarFieldでも可。

    os[patchI] << t << tab << gAverage(pp) << tab << gSum(pphi) << endl;
}
```

これで以下のようにwall.csv、inlet.csv、outlet.csvが生成された。

```csv:case/postProcessing/wall.csv
#time     pressure(Pa) massFlow(kg/s)
0.001     100000       0
0.002     100012.142            0
0.003     100031.344            0
0.004     100051.242            0
...
```
```csv:case/postProcessing/inlet.csv
#time     pressure(Pa) massFlow(kg/s)
0.001     100000       0
0.002     100034.161   0.221643
0.003     100058.015   0.351941
0.004     100075.169   0.461891
...
```
```csv:case/postProcessing/outlet.csv
#time     pressure(Pa) massFlow(kg/s)
0.001     100000       0
0.002     100000       -0.216546
0.003     100000       -0.348641
0.004     100000       -0.456116
...
```