こんにちは(@t_kun_kamakiri

この記事では、OpenFOAMで計算した時刻歴温度分布をFrontISTRへ渡し、各時刻の熱膨張を計算する仕組みを、フォルダ構成、メッシュ生成、座標変換、温度補間、FrontISTR入力の順に説明します。

【徹底解説】chtMultiRegionFoamの設定ファイルを一つずつ読む(輻射・発熱面つき) こんにちは(@t_kun_kamakiri) 金属のかたまりの側面にヒーター(ヒートマット)を貼り付けて温めると、ブロッ...

chtMultiRegionFoamの設定ファイルを一つずつ読む(輻射・発熱面つき)」で説明しています。本記事では重複を避け、OpenFOAMの計算が完了した後からFrontISTRへ渡す部分を中心にします。

最初に押さえるポイント
  • OpenFOAMのメッシュファイルをFrontISTR形式へ直接変換しているわけではない
  • 同じ寸法・同じ分割数のFrontISTRメッシュをPythonで新しく作る
  • OpenFOAMのセル中心温度をFrontISTRの節点温度へ補間する
  • FrontISTR自身はOpenFOAMファイルを読まず、変換後の!TEMPERATUREを読む
  • OpenFOAMの1保存時刻につき、FrontISTRの線形静解析を1回行う

この連成で行っていること

今回の連成はOpenFOAMからFrontISTRへの一方向・準静的連成です。熱流体計算で得た温度だけを構造解析へ渡します。FrontISTRの変形をOpenFOAMへ戻して流路形状を更新する反復計算は行いません。

ここでいう「準静的」とは、温度はOpenFOAMの時間変化を使いますが、構造解析では慣性や前時刻の応力履歴を引き継がず、その瞬間の温度で釣り合う静的な変形を時刻ごとに求める、という意味です。

101_1のフォルダ構成

101_1_frontistr_cht_box_thermal_expansion直下は次の構成です。

場所何をする場所か通常編集するか
README.mdケースの目的、実行方法、主要結果、GitHubへ含める範囲を説明する入口最初に読む
config/ヤング率、ポアソン比、密度、線膨張係数、基準温度をYAMLで管理する実材料に合わせて編集
python/FEMメッシュ生成、OpenFOAM温度読込み、温度写像、FrontISTR入力生成、実行、後処理を行う連成方法を変更するときに編集
case/時刻ごとのFrontISTR入力と計算結果を保存する自動生成。直接編集しない
data/時刻歴CSV、グラフ、ParaView用PVD、要約YAML、公開用GIFを保存する自動生成された結果を確認
ani/ParaViewから出したPNG連番と元GIFを保存する可視化を作り直すときに使用
docs/連成手順、FrontISTRキーワード、本記事などの説明資料通常は参照
tests/OpenFOAMフィールド読込み、座標変換、温度補間、材料YAMLを検証する単体テストコード変更後に実行
requirements.txtNumPy、Matplotlib、PyYAML、PillowなどPython依存関係初回環境構築で使用

python/の中は役割ごとに分けています。

ファイル役割
box_mesh.py直方体のFrontISTR六面体メッシュ、節点群、要素群を作る
openfoam_temperature.pyOpenFOAMのCTを読み、座標整合と節点補間を行う
fistr_case.py.msh.cnthecmw_ctrl.datを作り、fistr1を実行して結果を読む
run_thermal_expansion.py1時刻分の一連の処理をまとめる
run_thermal_expansion_timehistory.pyOpenFOAMの全保存時刻を並べ、1時刻処理を繰り返してPVDとCSVを作る
compress_preview_gif.py公開用GIFを縮小・減色する

どれがmainプログラムか

通常の実行入口(main)はpython/run_thermal_expansion_timehistory.pyです。OpenFOAMの全保存時刻を処理し、FrontISTR解析、CSV、グラフ、PVDまで作ります。

python/run_thermal_expansion.pymain()を持ちますが、これは1時刻だけを確認するための入口です。変換方法を変更したときのデバッグや、まず300秒だけ試したい場合に使います。

box_mesh.pyopenfoam_temperature.pyfistr_case.pyはmainから呼ばれる部品です。単独実行するものではありません。compress_preview_gif.pyだけは解析後に必要に応じて単独実行する補助ツールです。

各プログラムの関数と意味

プログラム主な関数何をしているか
box_mesh.pybuild_box_mesh()節点座標、六面体要素の接続、NALL・BOTTOM・TOP・EALLのID一覧を作る
cell_centers()理想的な一様格子のセル中心を作る検証用関数。実際の温度写像ではOpenFOAMのCを直接読む
openfoam_temperature.py_read_of_scalar_field()OpenFOAMのTを読み、uniform/nonuniformの両形式を数値配列へ変える
_read_of_vector_field()OpenFOAMのセル中心CをN×3の座標配列へ変える
load_solid_cell_temperatures()solidとheaterMatのC・Tを読み、1つの128セル点群へ結合する
align_cell_centers_to_node_mesh()OpenFOAM点群とFrontISTR点群の外接直方体中心を一致させる
interpolate_to_nodes()8近傍IDWでセル中心温度からFrontISTR節点温度を作る
fistr_case.pywrite_mesh()FrontISTRの.mshへ節点、要素、グループ、材料、断面を書く
write_hecmw_ctrl()メッシュ・制御・結果ファイル名をhecmw_ctrl.datへ書く
write_cnt()解析種別、底面拘束、225節点温度、材料、ステップ、VTK出力を.cntへ書く
run_fistr()対象時刻フォルダでfistr1を実行し、ログを保存する
add_temperature_to_visualization()FrontISTR標準VTUへTEMPERATUREとOpenFOAM実時刻を追加する
read_displacement()荷重適用後の.res.0.1から節点変位を読む
run_thermal_expansion.pyensure_cell_centres()1時刻のCがなければOpenFOAMのpostProcessで生成する
run_one_time()読込み、メッシュ生成、補間、入力生成、実行、結果集計を1本につなぐ中核関数
main()コマンドライン引数とYAMLを読み、指定した1時刻を処理する
run_thermal_expansion_timehistory.pylist_time_dirs()OpenFOAMの数値名フォルダを時間順に並べる
ensure_cell_centres_for_times()不足している全時刻のCをリージョン単位で一括生成する
write_pvd_collection()時刻別PVTUをOpenFOAM時刻付きPVDへまとめる
main()全時刻でrun_one_time()を繰り返し、summary.yaml、CSV、PNG、PVDを作る通常の入口

「メッシュ変換」ではなく「同等メッシュの再生成」

今回の実装では、OpenFOAMのpointsfacesownerneighbourを読み、FrontISTRの節点・要素へ一般的に変換しているわけではありません。OpenFOAM側の固体が単純な直方体・一様格子なので、同じ外形寸法と同じ分割数の構造格子をFrontISTR側で再生成しています。

項目OpenFOAMFrontISTR
固体外形200 × 200 × 400 mm200 × 200 × 400 mm
分割数4 × 4 × 8セル4 × 4 × 8要素
温度の位置128セルの中心225節点
原点流体領域中央のためx,y=0.4 m付近(0, 0, 0)
要素有限体積セル1次六面体要素 TYPE=361

OpenFOAMではsolidが120セル、heaterMatが8セルで、合わせて128セルです。FrontISTRでは両者を分けず、128個の六面体要素からなる1つの連続体として扱います。既定値では節点数は

\begin{align*}
N_{\mathrm{node}}
= (n_x+1)(n_y+1)(n_z+1)
= 5\times5\times9
=225
\end{align*}

要素数は

\begin{align*}
N_{\mathrm{elem}}
= n_x n_y n_z
=4\times4\times8
=128
\end{align*}

です。box_mesh.pyは次の式で節点座標を作ります。

\begin{align*}
x_i=\frac{iL_x}{n_x},\qquad
y_j=\frac{jL_y}{n_y},\qquad
z_k=\frac{kL_z}{n_z}
\end{align*}

8節点を[n0,n1,n2,n3,n4,n5,n6,n7]の順に結び、FrontISTRの1次六面体要素TYPE=361を作ります。さらに、全節点NALL、底面節点BOTTOM、上面節点TOP、全要素EALLを自動作成します。底面は5×5=25節点で、後ほど全固定に使います。

この方法が成立する条件

OpenFOAMとFrontISTRで、外形寸法、軸方向、分割数が一致している必要があります。回転、拡大縮小、曲面、非構造格子にはそのまま使えません。一般形状では、メッシュ変換ツールまたは空間探索・面投影を含む専用マッピングが必要です。

OpenFOAMから読み出す値

OpenFOAMから必要なのは、各保存時刻・各固体セルの中心座標Cと温度Tです。たとえば300秒では次の4ファイルを使います。

Tはソルバー結果に含まれますが、Cがない場合は次のOpenFOAM標準処理で生成します。

全時刻処理ではensure_cell_centres_for_times()が不足している時刻だけをまとめて生成します。CTは同じリージョン・同じメッシュのinternalFieldなので、同じセル順序です。プログラムはuniformnonuniform List<scalar>の両形式に対応し、宣言セル数と実データ数が一致するかも確認します。

温度単位はOpenFOAMもFrontISTRもKを使うため、ここでは℃への変換は行いません。

座標系を合わせる

OpenFOAMの固体は1 m角の流体領域中央にあり、外形はx,y=0.4~0.6 m、z=0~0.4 mです。FrontISTRメッシュは原点始まりで、x,y=0~0.2 m、z=0~0.4 mです。このまま距離を計算すると別の場所にある点群と判断されるため、補間前に原点を合わせます。

OpenFOAMセル中心群の外接直方体中心を$\mathbf{c}_{\mathrm{OF}}$、FrontISTR節点群の外接直方体中心を$\mathbf{c}_{\mathrm{FEM}}$とすると、平行移動量は

\begin{align*}
\mathbf{t}
=\mathbf{c}_{\mathrm{FEM}}-\mathbf{c}_{\mathrm{OF}},\qquad
\mathbf{x}’_{\mathrm{cell}}
=\mathbf{x}_{\mathrm{cell}}+\mathbf{t}
\end{align*}

です。本ケースでは

\begin{align*}
\mathbf{t}=(-0.4,-0.4,0)\ \mathrm{m}
\end{align*}

この処理は平行移動だけです。回転やスケーリングはしません。したがって、移動後の外形寸法と軸が一致していることを確認する必要があります。適用した移動量は時刻ごとのsummary.yamlにも保存します。

セル中心温度を節点温度へ変換する

OpenFOAMは有限体積法なので温度はセル中心にあります。一方、今回FrontISTRへ与える!TEMPERATUREは節点値です。4×4×8個のセル値128点を、その格子境界にある225節点へ移す必要があります。

各FrontISTR節点$\mathbf{x}_n$について、移動後のOpenFOAMセル中心から近い順に$k=8$個を選びます。距離$d_i$、重み$w_i$、節点温度$T_n$を次式で求めます。

\begin{align*}
d_i&=\left\|\mathbf{x}_n-\mathbf{x}_{c,i}\right\|,\\
w_i&=\frac{1}{d_i},\\
T_n&=\frac{\displaystyle\sum_{i=1}^{k}w_iT_i}{\displaystyle\sum_{i=1}^{k}w_i},\qquad k=8
\end{align*}

近いセルほど強く、遠いセルほど弱く反映する平均です。同じ一様格子の内部節点では、周囲8セルまでの距離がほぼ等しいため、8セルの平均に近くなります。節点とセル中心がほぼ一致して$d_i<10^{-9}$ mとなった場合は、ゼロ除算を避けてそのセル温度を直接使います。

IDW補間の注意

IDWは実装が単純で、今回の整った格子には使いやすい一方、エネルギー保存を保証する補間ではありません。局所的な最高温度は周囲との平均で低くなります。300秒ではOpenFOAMセル最高温度293.7681 Kに対し、補間後の節点最高温度は293.6414 Kでした。高温部を厳密に保持したい場合は、近傍数、形状関数補間、保存型マッピングを検討します。

1時刻分の処理をコードで追う

run_thermal_expansion.pyrun_one_time()が、1時刻分の変換と解析をまとめています。処理順は次の通りです。

300秒を処理すると、次のようなフォルダができます。

FrontISTRの材料設定YAML

人が変更する材料値はconfig/material_properties_steel.yamlへ集約しています。

項目意味今回の値
young_modulus_Pa弾性変形の硬さ。熱変形を拘束したときの応力へ影響205 GPa
poisson_ratio軸方向ひずみに対する横方向ひずみ0.3
density_kg_m3密度。今回の慣性なし静解析では結果への直接影響はない7850 kg/m³
thermal_expansion_coeff_per_K1 K上昇したときの自由熱ひずみ12×10-6/K
reference_temperature_K熱ひずみを0とする基準温度293.15 K

YAMLがFrontISTR入力になるまで

FrontISTRはYAMLを直接読みません。mainプログラムがPyYAMLのsafe_load()で辞書へ変換し、その値をwrite_mesh()write_cnt()へ渡します。

run_one_time()では、同じ辞書から必要なキーを明示して渡します。

YAMLキー.mshで使う場所.cntで使う場所
young_modulus_Pa!MATERIAL / !ITEM=1!ELASTICの1番目
poisson_ratio!MATERIAL / !ITEM=1!ELASTICの2番目
density_kg_m3!MATERIAL / !ITEM=2今回の.cntには書かない
thermal_expansion_coeff_per_K!MATERIAL / !ITEM=3!EXPANSION_COEFF
reference_temperature_K使わない!REFTEMP!INITIAL_CONDITION
sourcesnote人が根拠と注意点を読むための情報。計算には使わない

つまり、YAMLは人が編集しやすい材料設定の原本で、PythonがFrontISTRの2種類の記法へ展開します。材料値を変更した後は既存のcase/t_*を直接直すのではなく、YAMLを変更してmainプログラムを再実行します。

OpenFOAMではsolidheaterMatを別リージョンとして扱いますが、今回のFrontISTRモデルは両者を1つの鋼材相当ブロックとして扱います。実際のヒートマットと母材で弾性率や線膨張係数が異なる場合は、要素群と材料を分け、接着層や接触条件も追加する必要があります。

.mshを一つずつ読む

!NODE:節点番号と座標

形式は節点ID, x, y, zです。座標単位はmです。節点IDは1から始まり、x方向、y方向、z方向の順に増えます。

!ELEMENT, TYPE=361:六面体要素

TYPE=361は8節点1次六面体ソリッド要素です。節点順序が不正だと負体積や反転要素になるため、box_mesh.pyでは下面4点、対応する上面4点の順に一定の接続規則で作ります。

!NGROUP!EGROUP:名前付き集合

BOTTOMは拘束、TOPは上面変位の集計、NALLは初期温度、EALLは材料割当てに使います。

BOTTOM

TOP

このようにNOTEセットグループを作っておくことで境界条件の設定に紐づけることができます。
今回は後ほどBOTTOMに固定条件を与えます。

!MATERIAL!SECTION

ITEM=1はヤング率とポアソン比、ITEM=2は密度、ITEM=3は線膨張係数です。!SECTIONは全要素EALLを3次元ソリッドとし、材料STEELを割り当てます。

FrontISTRは!SECTIONが参照する材料名をメッシュ読込み時に解決するため、.mshにも材料定義が必要でした。.mshでは!ELASTICではなくHEC-MWの!ITEM形式を使います。同じ値を.cntにも書くので、両方が食い違わないようYAMLから生成します。

hecmw_ctrl.datを読む

FrontISTR本体fistr1が、どのファイルをメッシュ、解析制御、数値結果、可視化結果として使うかを対応付けるファイルです。TYPE=HECMW-ENTIREは単一のHEC-MWメッシュファイルを読む指定です。

.cntを一つずつ読む

!SOLUTION, TYPE=STATIC

入力形式バージョン3、線形静解析を指定します。OpenFOAMの300秒をFrontISTR内部で300秒間積分する指定ではありません。300秒の温度場を1つの静的熱荷重として解きます。

!WRITE:出力指定

節点変位・応力などの結果ファイルと、ParaView向け可視化ファイルを出力します。

!SOLVER:連立方程式の解法

  • METHOD=CG:共役勾配法
  • PRECOND=1:前処理を使用
  • 5000:最大反復回数
  • 1.0e-08:収束判定値
  • ITERLOG=NOTIMELOG=NO:詳細ログを抑制

このソルバーが最終的に解く基本形は、熱ひずみから作られる等価節点荷重$\mathbf{f}_{\mathrm{th}}$を使った

\begin{align*}
\mathbf{K}\mathbf{u}=\mathbf{f}_{\mathrm{th}}
\end{align*}

です。$\mathbf{K}$は剛性行列、$\mathbf{u}$は節点変位です。

!REFTEMPと初期温度

!REFTEMPは熱ひずみが0になる温度です。OpenFOAMの初期温度と同じ293.15 Kにします。等方材料の自由熱ひずみは

\begin{align*}
\boldsymbol{\varepsilon}_{\mathrm{th}}
=\alpha\left(T-T_{\mathrm{ref}}\right)\mathbf{I}
\end{align*}

です。$\alpha$は線膨張係数、$\mathbf{I}$は単位テンソルです。応力は全ひずみから熱ひずみを差し引いて

\begin{align*}
\boldsymbol{\sigma}
=\mathbf{D}\left(\boldsymbol{\varepsilon}(\mathbf{u})-\boldsymbol{\varepsilon}_{\mathrm{th}}\right)
\end{align*}

と評価されます。!INITIAL_CONDITIONは全節点の初期値を設定し、次の!TEMPERATUREが各節点の実際の熱荷重を与えます。

!BOUNDARY:底面固定

形式は節点群, 最初の自由度, 最後の自由度です。自由度1、2、3はX、Y、Z変位なので、z=0の底面25節点をXYZ全方向に固定します。

この拘束は剛体移動を防ぎますが、実物の支持が滑り、ボルト締結、接触などの場合は結果が変わります。特に熱応力は拘束条件に敏感なので、実機評価では支持方法をモデル化し直す必要があります。

!TEMPERATURE:OpenFOAMから渡された値

左がFrontISTR節点ID、右がIDW補間後の温度[K]です。FrontISTRが認識するのはこの値であり、OpenFOAMのTファイルではありません。PythonがOpenFOAM形式からFrontISTR形式への橋渡しをしています。

!MATERIAL:弾性と線膨張

!ELASTICはヤング率[Pa]とポアソン比、!EXPANSION_COEFFは線膨張係数です。温度依存性は入れておらず、全温度範囲で一定です。

!STEP:拘束と温度荷重を有効化

SUBSTEPS=1で温度荷重を1段階で与えます。BOUNDARY,1GRPID=1の底面拘束、LOAD,1GRPID=1の節点温度を有効にします。

!VISUAL:ParaView出力

表面結果をVTK形式で出力します。FrontISTR標準出力にはDISPLACEMENTNodalSTRESSNodalMISESが含まれますが、熱荷重として使ったTEMPERATUREは標準VTKに含まれません。

そこでadd_temperature_to_visualization()が解析後のVTU節点座標を入力メッシュへ照合し、TEMPERATURE配列を追加します。同時にTimeValueをOpenFOAMの実時刻へ置き換えます。これによりParaView上で温度、変位、応力を同じ時刻で切り替えられます。

全時刻をFrontISTRへ渡す仕組み

run_thermal_expansion_timehistory.pyはOpenFOAMケース直下の数値名フォルダを探し、数値として並べ替えます。

そして各時刻についてcase/t_<time>を作り、run_one_time()を呼びます。既定条件では121回のFrontISTR静解析です。

最後に、各時刻のPVTUをdata/thermal_expansion_timehistory.pvdへまとめます。

このPVDをParaViewで開くと、OpenFOAMと同じ時刻値でFrontISTR結果を連番表示できます。ただし、PVDが時系列に見せているだけで、FrontISTR解析同士が内部状態を受け渡しているわけではありません。

実行コマンド

単一時刻だけ確認する場合は次のコマンドです。

時刻を間引く場合は--every 2、範囲を限定する場合は--start-time--end-timeを使います。

FrontISTRで使っている単位

FrontISTRの入力ファイルには「このモデルはm単位」といった単位情報がありません。すべての数値を一貫した単位系で入力する必要があります。本ケースはSI単位系です。

本ケースの単位
座標mブロックは0.2 × 0.2 × 0.4 m
変位m.resとVTUのDISPLACEMENTはm
N今回の構造解析では外力なし
応力・ヤング率Pa = N/m²E=2.05×1011 Pa
密度kg/m³7850 kg/m³
温度K基準温度293.15 K
線膨張係数1/K1.2×10-5/K
OpenFOAM時刻sPVDの0~600 s。FrontISTR静解析の時間積分値ではない

data/timehistory.csvでは読みやすくするため、変位だけmからmmへ変換しています。一方、PVTU/VTUの変位はmのままです。ParaViewで変形倍率を設定するときはこの点に注意します。

EasyISTRでよく使われるmm・N・MPa系へ数値を変えて作り直す場合は、座標を200、200、400 mm、ヤング率を205000 MPa、変位をmmとして全体を統一します。線膨張係数と温度差の数値は変わりません。密度は採用する質量・時間単位まで含めて整合させる必要があるため、動解析では特に注意が必要です。今回の静的熱膨張では密度は結果へ直接影響しません。

caseフォルダをEasyISTRで読めるか

結論からいうと、FrontISTR入力としては正しいのでfistr1で直接実行できますが、既存の.msh.cntをEasyISTRで完全に読み戻して編集できるかはEasyISTRの版と操作経路に依存します。FrontISTR公式資料ではEasyISTRは対応プリポストとして案内され、EasyISTRが.msh.cntを生成する流れが示されています。しかし、既存の任意の.cntを全キーワード込みでプロジェクトへ逆読込みすることまでは保証されていません。

本ケースにはEasyISTRで扱う際に特に注意する点があります。

  • case直下ではなく、case/t_300など1時刻のフォルダを対象にする
  • box_thermal_expansion.mshはHEC-MW単一領域メッシュで、FrontISTR自身は読める
  • box_thermal_expansion.cntには225節点分の!TEMPERATUREがあり、EasyISTRで設定を保存し直すと失われる可能性がある
  • .msh.cntの材料定義が重複しているため、GUI側の再生成で記法が変わる可能性がある
  • 単位メタデータがないため、EasyISTR上でも0.2をmとして扱う必要がある

試す場合は元のcase/t_300を変更せず、別フォルダへ複製します。

EasyISTRへメッシュを読み込めた場合も、保存前後で少なくとも次を比較してください。

!TEMPERATURE!REFTEMP!EXPANSION_COEFFBOTTOM拘束が残っていることを確認します。単に形状と結果を見たい場合は、EasyISTRへ戻すよりdata/thermal_expansion_timehistory.pvdまたは時刻別PVTUをParaViewで開く方が確実です。

参考:FrontISTR公式FAQではEasyISTRを対応プリポストとして紹介しています。また、FrontISTR公式の逐次実行手順では、本ケースと同じhecmw_ctrl.dat、単一領域.msh、解析制御.cntの構成が説明されています。

変換が正しいか確認する

確認項目既定値・確認方法
OpenFOAM固体セル数solid 120 + heaterMat 8 = 128
FrontISTR節点・要素数225節点、128要素
座標移動量(-0.4, -0.4, 0) m
温度単位OpenFOAM、YAML、!TEMPERATUREの全てK
基準温度OpenFOAM初期温度と!REFTEMPが293.15 K
底面拘束BOTTOMの25節点、自由度1~3
300秒の温度範囲セル293.1531~293.7681 K、節点293.1575~293.6414 K
ParaViewPVDに121個のPVTU、各VTUにTEMPERATUREとDISPLACEMENT

単体テストは次のコマンドで実行します。

解析エラーではcase/t_<time>/log.fistr1FSTR.msgFSTR.staを確認します。値がおかしい場合はsummary.yamlのセル温度範囲、節点温度範囲、座標移動量、基準温度を確認します。

まとめ

  • OpenFOAMメッシュを直接変換せず、同じ寸法・分割数のFrontISTRメッシュを再生成する
  • OpenFOAMのCTを読み、solidとheaterMatを128セルの点群として結合する
  • 座標を(-0.4, -0.4, 0) m平行移動し、8近傍IDWで225節点の温度を作る
  • 節点温度を.cnt!TEMPERATUREへ書き、底面全固定の線形静解析を行う
  • OpenFOAMの各保存時刻についてFrontISTRを独立に実行し、PVDで1つの時刻歴として表示する
  • 材料値はYAML、処理はPython、生成結果はcasedataに分けて管理する

OpenFOAM側の設定内容は別記事、計算結果と熱変形の読み方は概要記事と分けることで、本記事は「どの値を、どのコードで、どのFrontISTR入力へ渡しているか」を調べるための技術資料として使える構成にしています。