こんにちは(@t_kun_kamakiri)
本記事では、「OpenFOAMとOpenModelicaをFMUで連携する方法」 を初心者向けに詳しく解説します。インストールから設定、実際のシミュレーションの実行まで、エラー対策も含めて解説するので、この記事を読めばスムーズに統合解析ができるようになります!
といっても今回はOpenFOAM v2412でFMU4FOAMのビルドを行い、Temperature Control in a Flangeの例題を試みましたが、うまく動作していない様子でした。
この記事を読んで解決した方がいらしたらご連絡いただければ幸いです。
ただし、別の例題のHeated roomは正常に動作していることを確認しました。
OpenModelicaの温度差による発熱源のスイッチの切り替えを行うシステムをモデル化します。そして、その温度差をOpenFOAMで計算したフランジモデルとの温度差を取得するシステムを構築します。
解説するのはこちらの例題です。
OpenFOAMとOpenModelicaとの連携を行うことで、どのようなことができるのかは以下の記事を読むと良いでしょう。
- FMU4FOAMのexamples
- OpenCAE_Simpo2022_竹とんぼ飛行シミュレーション
- 第38回Modelicaライブラリ勉強会「FMU4FOAMによる竹とんぼシミュレーション(その2)」
- FMU4FOAM:OpenFOAMとOpenModelicaの連成シミュレーションの紹介
それでは、さっそくOpenFOAMとOpenModelicaを連携する方法を見ていきましょう!
- WSL2(OpenFOAM v2412が使用できる状態)
- gitがインストールされている状態(gitコマンドが使用できる)
- OpenModelica 1.25.0をLinux版をインストール
FMU4FOAMのビルド
こちらの記事にOpenFOAMv2412を元にビルドする方法を解説しました。
まだの方はこちらをお読みください。
OpenModelicaのLinux版のインストール
Linuxでのfmuを動かす必要があったためOpenModelicaはLinuxにインストールしました。
OpenModelicaでの温度スイッチのモデル作成
今回は使うOpenModelicaのひな型になるモデルの作り方は以下で解説をしています。
フォルダ構成
FMU4FOAMのビルドができたらフォルダ構成を確認しましょう。
以下がTemperature Control in a Flangeのフォルダのフォルダ構成です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
. ├── 0 ├── Allclean ├── Allplot.py ├── Allrun ├── Allrun-parallel ├── ControlledTemperatureCoupled.fmu ├── ControlledTemperatureCoupled.mo ├── FMU.json ├── FMUGen.mos ├── README.md ├── __pycache__ ├── constant ├── controlledTemperature.py ├── results ├── system └── test.ipyn |
FMU4FOAMに関連するのは以下の部分になるでしょう。
ControlledTemperatureCoupled.fmu
:Modelicaで作成した温度制御モデルをFMU形式で書き出した実行ファイル。FMUGen.mos
:ModelicaモデルからFMUを生成するためのOpenModelica用スクリプト。fmuエクスポートをOpenModelicaのGUIで行う場合は使用しない。FMU.json
:OpenFOAMとFMUの間で変数や実行設定を接続するための構成ファイル。controlledTemperature.py
:シミュレーション結果を読み込んで温度などを可視化するPythonスクリプト。ControlledTemperatureCoupled.mo
:OpenModelicaのモデルファイル
詳しくはこちらをご確認ください。
system/controlDict
system/controlDictには以下のようにfunction objectが設定されています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
libs ( externalComm ); functions { FMUSimulator { type FMUSimulator; libs (pyFMUSim); pyClassName controlledTemperature; pyFileName controlledTemperature; } } |
- pyFileNameでcontrolledTemperature.pyのファイル名を指定
- pyClassは
class controlledTemperature(FMU4FOAM.FMUBase):
の部分のクラス名
controlledTemperature.py
controlledTemperature.py
は以下となっています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
import FMU4FOAM from OMSimulator import OMSimulator class controlledTemperature(FMU4FOAM.FMUBase): def __init__(self,endTime,filename): super().__init__(endTime,filename) self.oms = OMSimulator() self.oms.setTempDirectory("./temp/") self.oms.newModel("model") self.oms.addSystem("model.root", self.oms.system_wc) # instantiate FMUs self.oms.addSubModel("model.root.system1", "ControlledTemperatureCoupled.fmu") # simulation settings self.oms.setResultFile("model", "controlledTemperature.csv") self.oms.setStopTime("model", endTime) # self.oms.setFixedStepSize("model.root", 1e-7) self.oms.instantiate("model") self.oms.setReal("model.root.system1.Tin", 298) self.oms.setReal("model.root.system1.dTin", 0) self.oms.initialize("model") def setVar(self, key: str, val: float) -> None: return self.oms.setReal(key,val) def getVar(self, key: str) -> float: return self.oms.getReal(key)[0] def stepUntil(self,t): self.oms.setReal("model.root.system1.dTin", 0) self.oms.stepUntil("model",t) def __del__(self): self.oms.terminate("model") self.oms.delete("model") |
新たにOpenModelicaでfmuエクスポートした際には以下の部分を修正することになります。
1 2 |
# instantiate FMUs self.oms.addSubModel("model.root.system1", "ControlledTemperatureCoupled.fmu") |
OpenModelicaの確認
ではOpenModelicaで既に用意されているモデルを確認しましょう。
ControlledTemperatureCoupled.moをOpenModelicaで開きます。
OpenModelicaでモデルを開くと、OpenModelica 1.25.0では一部表示エラーになっていました。
おそらく用意されているOpenModelicaのバージョンが1.19.0とかだからでしょう。
OpenModelicaの表示エラーを解消
デフォルトで用意されているOpenModelicaのモデルでは、バージョンが古く電気抵抗がエラーでしたので、以下のように現在のOpenModelicaのバージョンで動かせるようにしました。
以下の単位を表すクラスのパスも変わっていたので変更。古いバージョンをコメントアウトし、新たに書き直しました。
1 2 3 4 5 6 |
//parameter Modelica.SIunits.Temperature TAmb(displayUnit = "degC") = 293.15 "Ambient Temperature"; //parameter Modelica.SIunits.TemperatureDifference TDif = 2 "Error in Temperature"; //output Modelica.SIunits.Temperature TRes(displayUnit = "degC") = heatingResistor.T_heatPort "Resulting Temperature"; parameter Modelica.Units.SI.Temperature TAmb(displayUnit = "degC") = 293.15; //Ambient Temperature; parameter Modelica.Units.SI.TemperatureDifference TDif = 2; //Error in Temperature; output Modelica.Units.SI.Temperature TRes(displayUnit = "degC") = heatingResistor.T_heatPort; //Resulting Temperature; |
作り直したモデルが正常に動作するか確認
いきなりOpenFOAMとOpenModelicaを連携させると失敗に気付かないことや、OpenModelicaのモデル設定が間違っていても気付かないことがあります。
ですので、まずはOpenModelica単体で正しく動作しているかを確認する必要があります。
※Tinに値を入れないといけないので、適当な固定値を入れました。

onOffControllerはrampと同じ動き(赤線)をしているので、正常に動作していそうです。

正常に動作していそうでしたら以下のモデルに戻して続きを進めます。

FMIオプションの設定
左のツリーのFMIから必要に応じて修正してください。
Move FMUはデフォルトでfmuをエクスポートする設定です。

FMUのエクスポート
Fileから手動でFMUを出力することができます。

正常にビルドされエクスポートされていることを確認してください。

出力した結果がこちらになります。
- 1がエクスポートしたファイル(.fmuファイル)
- 2が新たに作り直したOpenModelicaのモデル(.moファイル)
ControlledTemperatureCoupled.moとは区別してControlledTemperatureCoupled001.moという名前で作成しています。
controlledTemperature.pyを変更
controlledTemperature.py
は以下に変更します。
1 2 |
# instantiate FMUs self.oms.addSubModel("model.root.system1", "ControlledTemperatureCoupled001.fmu") |
lalpacianFoamを実行
以上の設定を終えたらあとはOpenFOAMのソルバを実行するだけです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
#!/bin/sh cd "${0%/*}" || exit # Run from this directory . ${WM_PROJECT_DIR:?}/bin/tools/RunFunctions # Tutorial run functions #------------------------------------------------------------------------------ touch flange.foam echo ${FOAM_TUTORIALS:?} runApplication ansysToFoam \ ${FOAM_TUTORIALS:?}/resources/geometry/flange.ans -scale 0.001 #runApplication $(getApplication) laplacianFoam #------------------------------------------------------------------------------ |
以上で正常に動作するはず….だったがうまくいかず。
ターゲット温度が切り替わる2秒以降は以下のようなエラーがでます。
1 2 3 4 5 |
error: [fmi2logger] /mnt/c/work/001_CAE/openfoam/20250315_FMU4FOAM/FMU4FOAM/examples/TempControlFlange/temp/model-2xgm1zzt/temp/0001_system1 (logStatusError): fmi2SetReal: Illegal co-simulation call sequence. fmi2SetReal is not allowed in model_state_error state. error: [fmi2logger] /mnt/c/work/001_CAE/openfoam/20250315_FMU4FOAM/FMU4FOAM/examples/TempControlFlange/temp/model-2xgm1zzt/temp/0001_system1 (logStatusError): fmi2DoStep: Illegal co-simulation call sequence. fmi2DoStep is not allowed in model_state_error state. error: [stepUntil] fmi2_doStep failed for FMU "model.root.system1" error: [fmi2logger] /mnt/c/work/001_CAE/openfoam/20250315_FMU4FOAM/FMU4FOAM/examples/TempControlFlange/temp/model-2xgm1zzt/temp/0001_system1 (logStatusError): fmi2SetReal: Illegal co-simulation call sequence. fmi2SetReal is not allowed in model_state_error state. error: [fmi2logger] /mnt/c/work/001_CAE/openfoam/20250315_FMU4FOAM/FMU4FOAM/examples/TempControlFlange/temp/model-2xgm1zzt/temp/0001_system1 (logStatusError): fmi2SetReal: Illegal co-simulation call sequence. fmi2SetReal is not allowed in model_state_error state. |
具体的にはOpenModelicaのrampの挙動がおかしいということです。
テスト1:rampのモデル
例えばrampを以下の設定します。

Pythonでグラフ作成します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
""" Plot results from csv file and arrange graphs horizontally with larger fonts and grid. """ #==============================================================================* # Imports import pandas as pd import matplotlib.pyplot as plt #==============================================================================* # フォントサイズを全体的に大きく plt.rcParams.update({ "font.size": 20, "axes.labelsize": 20, "axes.titlesize": 20, "xtick.labelsize": 20, "ytick.labelsize": 20, "legend.fontsize": 20 }) # データ読み込み T = pd.read_csv("controlledTemperature.csv") T["diff"] = abs(T["model.root.system1.Tin"] - T["model.root.system1.ramp.y"]) # T["diff"] = abs(T["model.root.system1.Tin"] - T["model.root.system1.sine.y"]) print(f"Max Tdiff = {T['diff'].max()} K") # グラフ作成(横並び) fig, axes = plt.subplots(nrows=1, ncols=3, figsize=(20, 6)) # --- グラフ1: 温度 --- axes[0].plot(T["time"], T["model.root.system1.Tin"], label="Tin") axes[0].plot(T["time"], T["model.root.system1.ramp.y"], label="Target") # axes[0].plot(T["time"], T["model.root.system1.sine.y"], label="Target") axes[0].set_title("Temperature") axes[0].set_ylabel("Temperature [K]") axes[0].set_xlabel("Time [s]") axes[0].legend() axes[0].grid(True) # --- グラフ2: 温度差 --- axes[1].plot(T["time"], T["diff"], label="Tin - ramp") axes[1].set_title("Temperature Difference") axes[1].set_ylabel("Temperature difference [K]") axes[1].set_xlabel("Time [s]") axes[1].legend() axes[1].grid(True) # --- グラフ3: Qout --- axes[2].plot(T["time"], T["model.root.system1.Qout"], label="Qout") axes[2].set_title("Heat Output") axes[2].set_ylabel("Power [W]") axes[2].set_xlabel("Time [s]") axes[2].legend() axes[2].grid(True) # レイアウトと保存 fig.tight_layout() fig.savefig("results/summary.png") # plt.show() # 必要に応じて表示 #==============================================================================* |
結果はこうなってしまいます(一番左のグラフを比較)
TinとTargetの動きがOpenModelicaで設定したrampの動きになっていません。
以下の挙動を示すのが正しいはずですが・・・・

OpenModelicaのテキストビューでプログラムを確認しても正しく設定されているようです。
1 |
Modelica.Blocks.Sources.Ramp ramp(height = 25, duration = 10, offset = 298.15, startTime = 2) |
テスト2:rampのheigthを2500
rampのモデルを以下に変更します。
1 |
Modelica.Blocks.Sources.Ramp ramp(height = 2500, duration = 10, offset = 298.15, startTime = 2) |
height = 2500,にすると2.5℃上昇した。
duration = 10にすると10秒間でheight の値だけ値を増やす意味ですが、値を変更してもまったく動かない。
疑問
- rampが思った挙動をしてくれない
- Tinの値も初期からずっと変わっていないのもおかしい Qoutは思った挙動をしてくれているようにみえる
やったのは、
- OpenModelicaのモデルを1.25.0のバージョンで作り替え
controlledTemperature.py
のControlledTemperatureCoupled001.fmuに変えた
ということです。
もしくするとOpenModelicaのバージョンが新しすぎる可能性があるので、1.19.0にバージョンダウンすると上手く行く可能性があります。
おまけ(Heated roomの例題はうまくいった)
ただし、別の例題のHeated roomは正常に動作していることを確認しました。
OpenModelica1.25.0に作り替え
FMU4FOAMに同封されているモデルをOpenModelicaで開くと電気抵抗のアイコンが表示されていない(エラー)ます。

ですのでOpenModelica1.25.0のバージョンで作り直します。

PIDでもエラーがでます。
1 2 3 |
[3] 19:38:38 Translation Error [HVACSystem001: 49:121-49:140]: Modified element limitsAtInit not found in class LimPID. |
1 2 3 |
// Modelica.Blocks.Continuous.LimPID pid( Td = 0, Ti = 10,initType = Modelica.Blocks.Types.InitPID.InitialState, k = 10, limitsAtInit = true, xd_start = 0, xi_start = 0, yMax = 1000, yMin = 0.0001, y_start = 10000) Modelica.Blocks.Continuous.LimPID pid( Td = 0, Ti = 10,initType = Modelica.Blocks.Types.InitPID.InitialState, k = 10, xd_start = 0, xi_start = 0, yMax = 1000, yMin = 0.0001, y_start = 10000) |
PID制御のモデルも作り直し

単位のクラスのパスも変更します。
1 2 3 |
//parameter Modelica.SIunits.Temperature TAmb(displayUnit="degC")=293.15 parameter Modelica.Units.SI.Temperature TAmb(displayUnit="degC")=293.15 "Ambient temperature"; |
これでOpenModelica側のエラーは無くなりました。
HVACSystem.fmuをエクスポートします。

HVACSystem.pyの一部を変更
1 2 3 4 |
# instantiate FMUs #self.oms.addSubModel("model.root.system1", "HVACSystem.fmu") self.oms.addSubModel("model.root.system1", "HVACSystem001.fmu") |
計算実行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
#!/bin/sh cd "${0%/*}" || exit # Run from this directory . ${WM_PROJECT_DIR:?}/bin/tools/RunFunctions # Tutorial run functions #------------------------------------------------------------------------------ touch heatRoom.foam runApplication blockMesh # runApplication refineMesh -overwrite runApplication topoSet # create the inlet/outlet and AMI patches runApplication createPatch -overwrite # runApplication -s 1 refineMesh -overwrite restore0Dir # runApplication $(getApplication) buoyantPimpleFoam #------------------------------------------------------------------------------ |
特にエラーもなく計算が進みます。
Pythonでグラフ作成
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
import pandas as pd import matplotlib.pyplot as plt # CSV読み込み T = pd.read_csv("HVACSystem.csv", skiprows=0) # カラム名をクリーンアップ T.columns = T.columns.str.strip().str.replace('"', '').str.replace('model.root.system1.', '') # データのフィルタリング(時間 > 0.01) T = T[T["time"] > 0.01] # カラム名一覧(デバッグ用に表示) print("Available columns:", T.columns.tolist()) # 横並びで4つのグラフを作成 fig, axes = plt.subplots(1, 4, figsize=(22, 5)) # グラフ1: Target vs Measured temperature axes[0].plot(T["time"], T["TSensor"], label="Tsensor") axes[0].plot(T["time"], T["LimPID.u_s"], label="Target") axes[0].set_title("Target and Measured T") axes[0].set_xlabel("time [s]") axes[0].set_ylabel("T [K]") axes[0].legend() # グラフ2: Mass flow rate axes[1].plot(T["time"], T["mout"], label="mout") axes[1].set_title("Mass Flux") axes[1].set_xlabel("time [s]") axes[1].set_ylabel("mdot [kg/s]") axes[1].legend() # グラフ3: Inlet temperature axes[2].plot(T["time"], T["Tout"], label="Tinlet") axes[2].set_title("Inlet Temperature") axes[2].set_xlabel("time [s]") axes[2].set_ylabel("T [K]") axes[2].legend() # グラフ4: Heat flow axes[3].plot(T["time"], T["heatFlowSensor.Q_flow"], label="Q_flow") axes[3].set_title("Heat Flow") axes[3].set_xlabel("time [s]") axes[3].set_ylabel("Q [W]") axes[3].legend() # 全体のレイアウト調整と保存 plt.tight_layout() plt.savefig("results/summary_horizontal.png") plt.show() |
結果はこちらです。
正常に動作しているようです。


まとめ
本記事では、OpenModelicaでの温度制御モデルをOpenFOAMと連携させる手順を解説しました。
しかし、OpenFOAM v2412でFMU4FOAMのビルドを行い、Temperature Control in a Flangeの例題を試みましたが、うまく動作していない様子でした。
しかし、別の例題のHeated roomは正常に動作していることを確認しました。
OpenFOAMに関する技術書を販売中!
OpenFOAMを自宅で学べるシリーズを販売中です。
OpenFOAM初学者から中級者向けの技術書となっていますので、ぜひよろしくお願いいたします。
【電子書籍】OpenFOAM(自宅で深める流体解析)流れと熱移動(1)はOpenFOAMを用いた熱流体解析の基本を、3つの代表的な解析課題(自然対流の解析、円管内の熱流体、円柱周りの熱流体)と通して丁寧に解説しています。
シミュレーションの結果と理論式・実験データとを比較しながら、計算結果の妥当性を自分で判断できる力を養うことを目指しています。

計算力学技術者のための問題アプリ
計算力学技術者熱流体2級、1級対策アプリをリリースしました。
- 下記をクリックしてホームページでダウンロードできます。
- LINE公式に登録すると無料で問題の一部を閲覧できます。
※LINEの仕様で数式がずれていますが、アプリでは問題ありません。
- 計算力学技術者の熱流体2級問題アプリ作成
リリース後も試行錯誤をしながら改善に努め日々アップデートしています。
備忘録として作成の過程を綴っています。
おすすめ参考図書
☟こちらは、OpenFOAMの日本語書籍が無い中唯一わかりやすい参考書だと思います。
☟こちらもOpenFOMの古いバージョンでの和訳になります。さすがにこちらはバージョンに対する日本語でのケアはしていないので、OpenFOAMに慣れている方は購入しても良いかと思います。僕は「日本語でまとまっている内容」なので少し重宝しています。
☟以下に、もっと初心者向けの同人誌を紹介しておきます。
初心者は「ってか、まずどうやってOpenFOAMをインストールするの?」というところからつまずきがちです。
そんな時は、以下の書籍をおすすめします。
改訂新版 OpenFOAMの歩き方 (技術の泉シリーズ(NextPublishing))
インストール方法とチュートリアルで流体解析を体験・・・ちょっと高度な解析まで解説があります。著者曰くOpenFOAMのバージョンの追跡を行いながら、書籍をアップデートするようなので安心ですね。