こんにちは(@t_kun_kamakiri

金属のかたまりの側面にヒーター(ヒートマット)を貼り付けて温めると、ブロックの中はどんな温度分布になり、どのくらい変形(熱膨張)するでしょうか。しかも、ヒーターを途中で切ったら変形はどう戻っていくでしょうか。このシリーズでは、その一部始終をOpenFOAM(熱・流れ・輻射)とFrontISTR(構造)を組み合わせて計算しています。計算対象は、1000mm角の空気の中に置いた200×200×400mmの金属ブロックで、側面の一部に貼ったヒートマットを0〜5分ON(20W)、5〜10分OFFにします。

計算対象:空気の中に置いた金属ブロックと、側面のヒートマット

↑計算対象のモデルです。左がOpenFOAMの流体領域(空気)、右がFrontISTRの構造モデルで、いずれも計算開始時(0秒、一様20℃)の状態です。

実際に計算すると、次のように動きます。

OpenFOAMの流体・温度場とFrontISTRの構造モデル(変形)の時刻歴アニメーション

左がOpenFOAMで解いた流体(空気)の温度場、右がその温度を受け取ったFrontISTRの構造モデル(変形)です。ヒートマット側から温度が上がって空気が対流し、ブロックがじわじわ反っていく様子が見えます。

本記事はそのシリーズの中でも、OpenFOAM側の設定ファイルを一つずつ読み解く「徹底解説編」です。chtMultiRegionFoam(共役熱伝達+輻射+発熱面)のケースについて、各設定ファイルの中身をブロック単位で「このキーワードは何をしているのか」まで潰していきます。自分のケースへ改造するときの辞書として使ってください(計算の全体像と結果は概要編、温度の受け渡しプログラムは連成の詳細編にまとめています)。

この記事でわかること
  • caseSettings/blockMeshDict/topoSetDictの各キーワードの意味
  • thermophysicalProperties(thermoType)の読み方と、流体(空気)と固体(鋼材)の違い
  • radiationProperties(fvDOM)とturbulenceProperties、controlDictの主要設定
  • fvSchemes/fvSolutionで何を指定しているか(輻射項の離散化・線形ソルバー・PIMPLE)
  • changeDictionaryDictでの結合境界条件(turbulentTemperatureRadCoupledMixed)と発熱面の設定

caseSettings:寸法と条件を一元管理する

まずsystem/include/caseSettingsです。ここはOpenFOAMの標準ファイルではなく、寸法や発熱条件をまとめておくために自分で用意したファイルです。#includeで他の辞書から読み込み、#eval #{ ... #}でその場で数式評価します。

  • #eval #{ round(...) #}:辞書の中で四則演算や関数を使える。ここでは寸法÷セルサイズで格子数を出している
  • $domainLx:同じファイル内や#include元で定義した変数を参照する記法

これで、cellSizeを変えるだけで格子数が、solidLxを変えるだけで固体の位置が自動追従します。

blockMeshDict:背景メッシュ

最初は固体も流体も区別しない、1つの直方体メッシュだけを作ります。

  • #include "include/caseSettings"先ほどのcaseSettingsファイルをここで読み込む行。これにより、$domainLx$nxといった変数がこのblockMeshDict内でも使えるようになる(寸法をcaseSettings側で一元管理できるのはこのおかげ)
  • convertToMeters 1:頂点座標をそのままメートルとして扱う(mmで書くなら0.001にする)
  • hex (0..7):8頂点で1つの六面体ブロックを定義
  • ($nx $ny $nz):各方向の分割数(caseSettingsから)
  • simpleGrading (1 1 1):格子の粗密比。1 1 1で等間隔

blockMeshを実行しParaViewで確認すると以下となります。

topoSetDict:3つのcellZoneへ分ける

1つのメッシュを、位置で指定した箱の中のセルごとにcellZoneへ登録します。ここが今回のケースの肝です。

  • boxToCell:指定した直方体box (最小点) (最大点)の中に中心があるセルを選ぶ
  • cellZoneSet + action new:選んだセル集合を新しいcellZoneにする
  • action subtract:既存ゾーンから引く。3ゾーンが重ならないようにするための必須処理

このあとsplitMeshRegions -cellZones -overwriteで本当に別リージョンへ分割すると、solid_to_fluidheaterMat_to_solidといった結合パッチが自動生成されます。

変数はすべて#include “include/caseSettings”でセットされているので、寸法を変更したい場合はこちらの数値を変更します。

topoSetコマンドを実行すると以下のようにsetsフォルダができており、こちらにセルゾーンなどが作られています。

うまくセルゾーンができているかをParaViewで確認することができます。

このtopoSetを実行することで各流体領域と固体領域の下地となるセルゾーンができました。
ただ、このままでは領域は分割されておらず(blockMeshのまま)次のsplitMeshにより領域分割する必要があります。

regionProperties:どのゾーンを固体/流体として解くか

splitMeshRegionsで分けた各cellZoneを、流体ソルバーで解くか固体ソルバーで解くかを振り分けるファイルです。fluidグループにはfluidを、solidグループにはsolidheaterMatを入れています。ヒートマットは発熱する部品ですが、扱いとしては熱伝導だけの固体なので、固体側に含めています。

境界条件の設定と領域分割

マルチリージョンソルバの境界条件の設定手順はとても面倒ですが、こちらの方法をお勧めします。

まずは、0フォルダに以下のファイルを用意し

中身をすべて以下とします。

それをコピーして0フォルダを作ります。

splitMeshRegions :領域分割

領域分割には以下のコマンドを実行します。

これによりセルゾーンから完全にメッシュの領域分割ができました。

さらに0フォルダの境界条件ファイルも自動で作ってくれます。

境界条件の設定

次にsystem/<領域>/changeDictionaryDictにそれぞれの境界条件をしておきます。
例えば

※各領域で適切な境界条件設定しておいてください。

このようにしつつ、以下のコマンドを実行します。
ひとつずつ実行してもいいですし、

for分にしても良いです。

設定のまととめ

以下のコマンドをスクリプトにしておくと便利でしょう。

Allrun.pre

次回からは以下のコマンドでメッシュ作成から解析設定まで(境界条件の設定まで)を行ってくれます。

g:重力の向き(自然対流を駆動する)

今回は空気の温度差による浮力(自然対流)を計算するため、重力が必要です。ヒートマットで温められた空気は軽くなって上昇し、周囲の冷たい空気が入れ替わりに降りてくる、という流れがこのgによって生まれます。高さ方向を$z$にとっているので、下向きの(0 0 -9.81)を与えています。もし重力を与えないと浮力が働かず、空気の対流が起きない(熱が伝導と輻射だけで伝わる)計算になってしまいます。

thermophysicalProperties:熱物性の指定方法

熱物性はthermoTypeで「どんなモデルの組み合わせで熱を計算するか」を指定し、mixtureで具体的な数値を与えます。まず流体(空気)です。

ポイントはequationOfState perfectGasです。理想気体にすることで温度によって密度が変わり、自然対流(浮力)が生まれます。次に固体(鋼材)です。

流体との違いは、heSolidThermo(固体用)、constIso(等方熱伝導率kappaで指定)、rhoConst(密度一定)です。ヒートマットも同じ鋼材相当にしています(constant/heaterMat/thermophysicalProperties)。

turbulenceProperties:乱流モデル

乱流は今回simulationType laminar(層流)としています。

乱流モデルの設定はすべての流量領域に設定する必要があります。
固体領域には設定は不要です。

radiationProperties:輻射(fvDOM)

  • nPhi × nTheta:輻射の方向を何本の離散方向で近似するか。増やすほど正確だが重くなる
  • solverFreq:毎ステップ輻射を解くと重いので、何回かに1回にする間引き
  • absorptivity/emissivityを0.01:空気は輻射をほぼ吸収・放射しない(面同士の輻射交換が主役)

各壁面の輻射率(グレー拡散面としての扱い)はconstant/fluid/boundaryRadiationPropertiesで個別に、固体表面0.85、外周壁0.9などと設定しています。固体側はradiation off(内部では輻射を解かない)です。

fvDOMは何を解いているのか(数式)

fvDOMは放射伝達方程式(RTE: Radiative Transfer Equation)を解いています。ある位置 $\mathbf{r}$ で、ある方向 $\mathbf{s}$ に進む放射強度を $I(\mathbf{r},\mathbf{s})$ とすると、散乱の無いグレー媒体では次の式が成り立ちます。

\begin{align*}
\mathbf{s}\cdot\nabla I(\mathbf{r},\mathbf{s})
=
\kappa_a
\left(
\frac{\sigma T^4}{\pi}

I(\mathbf{r},\mathbf{s})
\right)
\end{align*}

左辺は「その方向へ進むと強度がどれだけ変化するか」、右辺は「その場の温度で放射される分($\sigma T^4/\pi$、$\sigma$はステファン・ボルツマン定数)から、吸収される分($I$)を引いたもの」です。$\kappa_a$ は吸収係数で、radiationPropertiesabsorptivityに対応します。

この式は「方向 $\mathbf{s}$」という連続的な向きについての方程式なので、そのままでは解けません。そこで向きを有限個の代表方向 $\mathbf{s}_i$ に離散化するのがDOM(離散座標法)です。各方向について、強度 $I_i$ の移流方程式を1本ずつ解きます。

\begin{align*}
\mathbf{s}_i\cdot\nabla I_i
=
\kappa_a
\left(
\frac{\sigma T^4}{\pi}

I_i
\right),
\qquad
i=1,\dots,N
\end{align*}

この方向の本数 $N$ が、まさにfvDOMCoeffsnPhinThetaで決まります。OpenFOAMでは

\begin{align*}
N
=
4\,n_\phi\,n_\theta
\end{align*}

で、今回は $n_\phi=3$、$n_\theta=5$ なので $N=4\times3\times5=60$ 方向です(実際、実行ログにも「fvDOM : Allocated 60 rays」と出ます)。1方向あたり1本の移流方程式なので、この左辺の離散化がfvSchemesdiv(Ji,Ii_h)、各 $I_i$ を解く線形ソルバーがfvSolutionIiに対応します。

全方向の強度を足し合わせた(立体角 $\omega_i$ で重み付き和を取った)ものが入射放射 $G$ です。これがfvSolutionに出てきたGフィールドの正体です。

\begin{align*}
G
=
\int_{4\pi} I\, d\Omega
\;\approx\;
\sum_{i=1}^{N} \omega_i I_i
\end{align*}

最終的に、エネルギー方程式(温度の式)へ加わる輻射による発熱・吸熱は、放射で出ていく分 $4\sigma T^4$ と、入ってくる分 $G$ の差で決まります。

\begin{align*}
\nabla\cdot\mathbf{q}_r
=
\kappa_a
\left(
4\sigma T^4

G
\right)
\end{align*}

壁面では、面から出ていく強度が「自分が温度で放射する分」と「入ってきた放射を反射する分」の和になります。グレー拡散不透明面(opaqueDiffusive)なら、放射率 $\epsilon$ と反射率 $1-\epsilon$ を使って次のように書けます($q_{in}$ は入射熱流束)。

\begin{align*}
I_w
=
\frac{1}{\pi}
\left[
\epsilon\,\sigma T_w^4
+
(1-\epsilon)\,q_{in}
\right]
\end{align*}

この $\epsilon$ がboundaryRadiationPropertiesで面ごとに設定した放射率(固体表面0.85、外周壁0.9)です。なお、これらの輻射方程式を毎ステップ解くと重いので、solverFreq 10で「10回の流れ反復ごとに1回だけ輻射を解く」と間引いています。まとめると、radiationPropertiesfvSchemesdiv(Ji,Ii_h)fvSolutionIiGboundaryRadiationPropertiesは、すべてこの一連のRTE計算のための設定というわけです。

controlDict

時間や保存の設定はcontrolDictです。

adjustTimeStep yesにすると、maxCo(クーラン数)とmaxDi(拡散数)を超えないようdeltaTが自動で調整されます。安定性を気にせず回せる一方、保存時刻を揃えたいのでwriteControladjustableRunTime(5秒ちょうどで保存)にしています。この5秒刻みの保存時刻が、そのままFrontISTRへ渡す時刻になります。

fvSchemes:離散化スキーム

各項をどう離散化するかの指定です。流体側の要点だけ抜き出します。

div(Ji,Ii_h)が輻射(fvDOM)を使うときに必要な項です。これが無いと輻射計算でエラーになります。固体側のfvSchemesはもっと単純で、熱伝導に相当するlaplacian(alpha,h)だけを指定します(移流が無いため)。

fvSolution:線形ソルバーとPIMPLE

各変数を解く線形ソルバーと、反復制御(PIMPLE)を指定します。

  • p_rgh(浮力を分離した圧力)はGAMG(マルチグリッド)で解く
  • IiGは輻射(fvDOM)の変数。輻射を使うときに必要
  • pRefCellpRefValue:外に開いていない閉じた領域では圧力の基準点が必要

固体側は温度(エンタルピーh)だけを解くので、fvSolutionhのソルバー1つとPIMPLEの非直交補正だけの、とても短い内容です。

【補足】changeDictionaryDict:境界条件を確定させる

splitMeshRegions直後の境界条件は仮の状態なので、リージョンごとにchangeDictionaryで本来の条件へ書き換えます。まず流体側の温度Tです。

結合面に使うcompressible::turbulentTemperatureRadCoupledMixedが、共役熱伝達の中核です。両側の温度・熱流束が釣り合うように解かれます。主なキーワードは次の通りです。

  • Tnbr T:相手リージョンの温度フィールド名(隣と温度をやり取り)
  • kappaMethod:熱伝導率をどこから取るか(fluidThermosolidThermo
  • qrqrNbr:輻射熱流束を相手と受け渡すためのフィールド名

固体側は、底面などはzeroGradient(断熱)、結合面は同じくturbulentTemperatureRadCoupledMixedです。そしてヒートマットの外側面にだけ、時間変化する熱量境界条件を与えます。

externalWallHeatFluxTemperaturemode powerで使うと、面に総熱量[W]を与えられます。Q(時刻 値)のテーブルにすることで、矩形波状のON/OFFが表現できます。

温度測定

温度測定用にprobeを設定しておきます。

probeの座標もinclude/caseSettingsで設定しています。
座標位置はParaViewで確認すると良いでしょう。

solid/probes

これらの温度時刻歴をグラフにすると以下になります。

グラフの読み方

  • 上段は温度そのもの、下段は全領域共通の初期条件293.15 K(20℃)からの温度変化です。
  • P0は発熱面なので加熱中に最も高温になり、300秒のヒーター停止後に低下します。
  • P1は固体側なのでP0より遅れて上昇し、停止直後もしばらく上昇します。
  • P2・P3の変化が小さいことから、今回の時間範囲では熱が反対側や上面まで十分に広がっていないと読めます。
  • 300秒直後のP0の急な温度低下は、発熱量を20 Wから0 Wへ段階的に切り替えた条件に対応します。

P0だけ約0.5 ℃高い位置から始まる理由

P0はヒートマットのセル内部ではなく、externalWallHeatFluxTemperatureで20 Wを与える外側境界面の温度です。初期内部温度は全領域で293.15 Kですが、境界条件は最初の計算時刻から熱流束を満たす壁面温度を作ります。ヒーター面積は$0.10\times0.20=0.020\,\mathrm{m^2}$なので、熱流束は$q”=20/0.020=1000\,\mathrm{W/m^2}$です。境界面から隣接セル中心までの距離$d=0.025\,\mathrm{m}$、熱伝導率$k=50\,\mathrm{W/(m\,K)}$より、

$$T_{wall}-T_{cell}\approx\frac{q”d}{k}=\frac{1000\times0.025}{50}=0.5\,\mathrm{K}$$

となります。このため最初の記録はP0が約293.65 K、その他が約293.15 Kです。これは初期条件をP0だけ高く設定したためではなく、20 Wの熱流束境界条件により壁面とセル中心の間に必要な温度勾配によりセル間の保管温度として境界面温度を出力しているためです。P0をヒートマットの代表温度として使う場合は、境界面温度であることに注意してください

まとめ

chtMultiRegionFoamのケースを、設定ファイル単位で読み解いてきました。改造の勘所を整理すると次の通りです。

  • 寸法・条件はcaseSettingsに集約し、blockMeshDicttopoSetDictから#includeで共有する
  • 領域はtopoSetboxToCellsubtractで非重複)→splitMeshRegionsで分割する
  • 浮力を出すため流体はperfectGas、輻射はfvDOMdiv(Ji,Ii_h)Ii/Gソルバーがセット
  • 結合面はturbulentTemperatureRadCoupledMixed、発熱面はexternalWallHeatFluxTemperature(mode=power+テーブル)

OpenFOAMの温度分布をFrontISTRへ渡す部分(座標合わせ・逆距離加重補間・入力生成)は、連成の詳細編にまとめています。あわせて読んでいただければと思います。