こんにちは(@t_kun_kamakiri)
以前にOpenFOAMとOpenMoelicaの連携を行うFMU4FOAMというインターフェースについて紹介する記事をアップしました。
以下の記事を1つずつ読み進めていけば、FMU4FOAMを使うことができます。
この記事はOpenFOAMのv2412でも動かせるようにしたものです。
※並列計算できるかなどは確認していません。
ただし、このFMU4FOAMは現時点(2025年11月)で調べた限りはOpenFOAMのマルチリージョンソルバには対応していないようでした。
これが少し不満でどうしてもマルチリージョンソルバに対応したOpenFOAMとOpenModelicaの連携がしたく、上記の勉強会でも報告しアドバイスを頂いましたが、解決にはいたらず。
マルチリージョンソルバは以下の図のように領域ごとに「0, constant, system」ファイルを構成するため、FMU4FOAMの境界条件がこのファイル構造を認識するようにプログラムされていないことが原因のようです。

OpenFOAMから外部ソルバ(今回はOpenModelida)を実行し、OpenFOAMとOpenModelicaの連携を行います。
本記事では、まずはOpenModelicaのコマンド実行ができるようにします。
計算の流れは以下のようになります。

右下のグラフに結果を載せています。
一番左のグラフが大事で、OpenFOAMのprobesで指定した温度センサの値がオレンジ色のターゲット温度に追従するよう、OpenModelicaのPID制御で入口温度境界に反映させる設定にしています。
OpenFOAMと外部ソルバ(OpenMoelica)を連携
FMU4FOAM
じゃっかん結果が違いますが、やりたいことは達成していますよね。
OpenFOAMの流出温度の評価方法とPID制御のパラメータが少し違うことが原因なのかな?
というわけで前段が長くなりましたが、本記事ではOpenModelicaをコマンド実行で計算させる方法についてまとめておきます。
- OpenFOAM v2412
- OpenModelica 1.25.0(Linux版)
- WSL2(Ubuntu-22.04)
OpenModelicaの計算実行
まずは普通にOpenModelicaのGUIを起動して計算実行する方法を示しておきます。
対象とするモデルはFMU4FOAMの例題(heatedRoom)になります。
Linux版のOpenModelicaは以下のコマンドを実行するとGUIが起動します。
|
1 |
OMEdit & |
&を付けることで、バックグラウンド実行をしています。OMEditのコマンドを実行してもコマンド動作を受け付けてくれるようにしています。
TinletとTSensorはinput情報なのでこのまま計算実行をしても適切な結果を出力はしてくれませんが、とりあえず実行します。

TinletとTSensorに値を入力して(適当な値を入力)、再度計算実行します。
すると、TSensorが300である場合にどのようなToutを設定すればターゲット温度(ramp.y)になるのかを計算してくれます。
TSensorがターゲット温度より低い60sくらいでToutが大きくないといけない設定になります。

コンパイルもそこそこ時間がかかるので、こうしておくと便利な場合が多いです。
シェルを起動して実行
ここからはシェルを起動してコマンド操作でOpenMoelicaを操ります。
以下のコマンドによりシェルが起動します。
|
1 |
OMShell & |
|
1 2 3 4 5 6 |
>>loadModel(Modelica) >>loadFile("/mnt/d/work/openfoam/20251017_OpenCAE2025/heatedRoom/HVACSystem001.mo") >>buildModel(HVACSystem001, startTime=0, stopTime=300, numberOfIntervals=1000, tolerance=1e-6, method="dassl", outputFormat="mat", fileNamePrefix="HVACSystem") >>system("/tmp/OpenModelica/HVACSystem -override Tinlet=319.401,TSensor=328.105 -r=/mnt/d/work/openfoam/20251017_OpenCAE2025/heatedRoom/HVACSystemResult.mat") |
🔧 OpenModelicaでモデルをビルドして外部入力で実行する手順
loadModel(Modelica)
標準ライブラリ(Modelica Standard Library)を読み込みます。
熱、流体、電気などの基本ブロックを使用するために最初に実行します。loadFile("HVACSystem001.mo")
ローカルのModelicaファイルを読み込んで、モデル定義をOpenModelicaに登録します。
ファイル内のモデル名(例:HVACSystem001)が使えるようになります。buildModel(HVACSystem001, … )
モデルをシミュレーション可能な実行ファイルにコンパイルします。
主なオプションの意味は次のとおりです。
startTime=0:シミュレーション開始時刻
stopTime=300:シミュレーション終了時刻
numberOfIntervals=1000:出力間隔(時間分割数)
tolerance=1e-6:収束誤差の許容範囲
method="dassl":DASSL法(陰解法)で安定的に解く
outputFormat="mat":結果をMATLAB形式で出力
fileNamePrefix="HVACSystem":出力ファイル名の接頭辞を指定system("/tmp/OpenModelica/HVACSystem -override Tinlet=319.401,TSensor=328.105 -r=...")
コンパイル済みの実行ファイルを、外部パラメータで実行します。
-override:TinletやTSensorなどのモデル内パラメータを上書き指定
-r=...:シミュレーション結果を保存する.matファイルの出力先を指定
実際の実行の様子はこちらです。
ここではコンパイル(bluidModel)と計算実行(system)を分けましたが、simulation()を使うとコンパイルと計算実行を同時に行ってくれます。
ただ、コンパイルは一度行うと実行ファイルが作成され、以後コンパイルしなくても良いので、コンパイルと計算実行は分けておく方が良いです。
値を変える
大事な点はsystemの中でTinlet=319.401,TSensor=328.105のように値を指定しているところです。これが、input値になっています。
再度、以下の値に変えて実行してみます。
|
1 |
system("/tmp/OpenModelica/HVACSystem -override Tinlet=293,TSensor=300 -r=/mnt/d/work/openfoam/20251017_OpenCAE2025/heatedRoom/HVACSystemResult.mat") |
値の取得
値を取得することができます。
|
1 |
val(Tout, t, fileName="/mnt/d/work/openfoam/20251017_OpenCAE2025/heatedRoom/HVACSystemResult.mat") |
変数にすることもできます。
|
1 |
tout_val := val(Tout, t, fileName="/mnt/d/work/openfoam/20251017_OpenCAE2025/heatedRoom/HVACSystemResult.mat"); |
グラフにする
シェルコマンドからグラフを作成することもできます。
|
1 |
plot({ramp.y,Tout, Tinlet, TSensor}, fileName="/mnt/d/work/openfoam/20251017_OpenCAE2025/heatedRoom/HVACSystemResult.mat"); |
データをファイルに書き出す
OpenModelicaで計算した結果をファイルに書き出すこともできます。
次回の記事でOpenMoelicaでの計算結果をOpenFOAM側で読み取りを行う場面があるため、ファイル出力させておく方法は知っておく必要があります。
|
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 |
t := 300.0; tout_val := val(Tout, t, fileName="/mnt/d/work/openfoam/20251017_OpenCAE2025/heatedRoom/HVACSystemResult.mat"); mout_val := val(mout, t, fileName="/mnt/d/work/openfoam/20251017_OpenCAE2025/heatedRoom/HVACSystemResult.mat"); heatFlowSensor_Q_flow_val := val(heatFlowSensor.Q_flow, t, fileName="/mnt/d/work/openfoam/20251017_OpenCAE2025/heatedRoom/HVACSystemResult.mat"); TSensor_val := val(TSensor, t, fileName="/mnt/d/work/openfoam/20251017_OpenCAE2025/heatedRoom/HVACSystemResult.mat"); TTarget_val := val(ramp.y, t, fileName="/mnt/d/work/openfoam/20251017_OpenCAE2025/heatedRoom/HVACSystemResult.mat"); // ===== OpenFOAM形式のヘッダを書き出す ===== writeFile("/mnt/d/work/openfoam/20251017_OpenCAE2025/heatedRoom/constant/boundarySyncFile", "FoamFile\n" +"{\n" +" version 2.0;\n" +" format ascii;\n" +" class dictionary;\n" +" location constant;\n" +" object boundarySyncFile;\n" +"}\n" ); // ===== データ行を書き出す ===== writeFile("/mnt/d/work/openfoam/20251017_OpenCAE2025/heatedRoom/constant/boundarySyncFile", "Time\t" + String(t) + ";\n", append=true); writeFile("/mnt/d/work/openfoam/20251017_OpenCAE2025/heatedRoom/constant/boundarySyncFile", "mout\t" + String(mout_val) + ";\n", append=true); writeFile("/mnt/d/work/openfoam/20251017_OpenCAE2025/heatedRoom/constant/boundarySyncFile", "Tout\t" + String(tout_val) + ";\n", append=true); writeFile("/mnt/d/work/openfoam/20251017_OpenCAE2025/heatedRoom/constant/boundarySyncFile", "heatFlowSensor.Q_flow\t" + String(heatFlowSensor_Q_flow_val) + ";\n", append=true); writeFile("/mnt/d/work/openfoam/20251017_OpenCAE2025/heatedRoom/constant/boundarySyncFile", "TSensor\t" + String(TSensor_val) + ";\n", append=true); writeFile("/mnt/d/work/openfoam/20251017_OpenCAE2025/heatedRoom/constant/boundarySyncFile", "ramp.y\t" + String(TTarget_val) + ";\n", append=true); |
上で変数定義をして置き、writeFileでファイルへ書き出します。
constant/boundarySyncFileに以下のように書き出されます。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
FoamFile { version 2.0; format ascii; class dictionary; location constant; object boundarySyncFile; } Time 300; mout 11.49; Tout 312.945; heatFlowSensor.Q_flow 230777; TSensor 310; ramp.y 328; |
一通りコマンド実行による操作の説明を終わります。
スクリプトで計算実行
今まで行ったコマンドをスクリプトとしてまとめておきます。
HVACSystem002.mos
|
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 |
//loadModel(Modelica); //loadFile("/mnt/d/work/openfoam/20251017_OpenCAE2025/heatedRoom/HVACSystem001.mo"); buildModel(HVACSystem001, startTime=0, stopTime=300, numberOfIntervals=1000, tolerance=1e-6, method="dassl", outputFormat="mat", fileNamePrefix="HVACSystem"); system("/tmp/OpenModelica/HVACSystem -override Tinlet=293.0,TSensor=310.0 -r=/mnt/d/work/openfoam/20251017_OpenCAE2025/heatedRoom/HVACSystemResult.mat"); t := 300.0; tout_val := val(Tout, t, fileName="/mnt/d/work/openfoam/20251017_OpenCAE2025/heatedRoom/HVACSystemResult.mat"); mout_val := val(mout, t, fileName="/mnt/d/work/openfoam/20251017_OpenCAE2025/heatedRoom/HVACSystemResult.mat"); heatFlowSensor_Q_flow_val := val(heatFlowSensor.Q_flow, t, fileName="/mnt/d/work/openfoam/20251017_OpenCAE2025/heatedRoom/HVACSystemResult.mat"); TSensor_val := val(TSensor, t, fileName="/mnt/d/work/openfoam/20251017_OpenCAE2025/heatedRoom/HVACSystemResult.mat"); TTarget_val := val(ramp.y, t, fileName="/mnt/d/work/openfoam/20251017_OpenCAE2025/heatedRoom/HVACSystemResult.mat"); // ===== OpenFOAM形式のヘッダを書き出す ===== writeFile("/mnt/d/work/openfoam/20251017_OpenCAE2025/heatedRoom/constant/boundarySyncFile", "FoamFile\n" +"{\n" +" version 2.0;\n" +" format ascii;\n" +" class dictionary;\n" +" location constant;\n" +" object boundarySyncFile;\n" +"}\n" ); // ===== データ行を書き出す ===== writeFile("/mnt/d/work/openfoam/20251017_OpenCAE2025/heatedRoom/constant/boundarySyncFile", "Time\t" + String(t) + ";\n", append=true); writeFile("/mnt/d/work/openfoam/20251017_OpenCAE2025/heatedRoom/constant/boundarySyncFile", "mout\t" + String(mout_val) + ";\n", append=true); writeFile("/mnt/d/work/openfoam/20251017_OpenCAE2025/heatedRoom/constant/boundarySyncFile", "Tout\t" + String(tout_val) + ";\n", append=true); writeFile("/mnt/d/work/openfoam/20251017_OpenCAE2025/heatedRoom/constant/boundarySyncFile", "heatFlowSensor.Q_flow\t" + String(heatFlowSensor_Q_flow_val) + ";\n", append=true); writeFile("/mnt/d/work/openfoam/20251017_OpenCAE2025/heatedRoom/constant/boundarySyncFile", "TSensor\t" + String(TSensor_val) + ";\n", append=true); writeFile("/mnt/d/work/openfoam/20251017_OpenCAE2025/heatedRoom/constant/boundarySyncFile", "ramp.y\t" + String(TTarget_val) + ";\n", append=true); |
最初の2行をコメントアウトしているのは、既にコンパイル済の場合は、再度コンパイルする必要がないからです。
はじめてコンパイルする場合はコメントアウトを外して実行してください。
以下のようにさらにスクリプトを書いて、HVACSystem002.mosを実行するようにします。
runMos.sh
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
#!/bin/bash # 使用方法: ./run_OM.sh Tank002.mos # ./run_OM.sh HVACSystem001.mos # 引数で .mos ファイル名を指定できるようにしています MOSFILE=$1 # 引数チェック if [ -z "$MOSFILE" ]; then echo "使用方法: $0 <mosファイル名>" exit 1 fi # 現在のディレクトリを基準に対象ファイルを指定 CURRENT_PATH=$(pwd) TARGET_PATH="$CURRENT_PATH/$MOSFILE" # 出力先ディレクトリ WORK_DIR="/tmp/OpenModelica" # ディレクトリ移動と実行 cd "$WORK_DIR" || { echo "ディレクトリ移動に失敗しました"; exit 1; } omc "$TARGET_PATH" |
パスの関係性を考えるのが面倒なので、このようにしておくと便利です。
では、mosファイルを実行してみましょう。
以下のように引数として実行したいmosファイルを指定します。
|
1 |
./runMos.sh HVACSystem002.mos |
こんな感じでログが出てくると思います。
今一度それぞれのファイルの関係性を図にしておきます。
以上でOpenModelicaによるスクリプト実行の説明を終わります。
まとめ
今回の記事では、OpenModelicaをコマンドラインで制御する方法を紹介しました。
GUI操作ではわかりにくいモデルの内部挙動を、スクリプトやシェル実行によって再現・自動化する手順を整理できたと思います。
ポイントを振り返ると以下の通りです。
OMShellからloadModel、loadFile、buildModel、systemを順に実行することで、
モデルの読み込み → ビルド → 外部入力を与えて実行という流れが構築できる。-overrideオプションを使うことで、OpenFOAMなど外部解析ツールから入力値をリアルタイムに渡すことができる。val()関数で計算結果を取得し、writeFile()によって OpenFOAM形式のファイル出力が可能。.mosファイルにまとめておくと、再実行時にコマンドを1行ずつ入力する必要がなく、./runMos.sh HVACSystem002.mosのように自動化・再利用が容易になる。
この仕組みを使えば、FMU4FOAMの例題のようなOpenFOAM×OpenModelica連携を自分の環境に合わせて柔軟に再現できます。
次回は、ここで出力した boundarySyncFile をOpenFOAM側から読み取り、双方向連携(1D-CAE × 3D-CFD)を実現する方法について解説します。
参考書
OpenModelicaのプログラムの文法を勉強したい方にはこちらがお勧めです。
はじめてのModelicaプログラミング -1日で読める わかる Modelica入門- (MBD Lab Series)
こちらはOpenModelicaの参考書の中でも数少ない純粋な日本語書籍です。
本記事で紹介したコマンド実行の方法も解説があり、全体的にわかりやすい参考書になっており、重宝しています。








