【OpenFOAMとOpenMoelica連携(3)】chtMultiRegionFoamによるマルチリージョンソルバ
updateMOS_and_Run.py
updateMOS_and_Run.pyの全文を載せときます。
updateMOS_and_Run.py
import re
import subprocess
from pathlib import Path
import os
# === ここから別ファイルを import して利用 ===
from sync_to_table import parse_boundary_sync, append_output_row
# ===== パス設定 =====
PWD = os.getcwd()
base = Path(PWD)
input_file = base / "constant/outletWeightAreaTempFile"
mos_file = base / "HVACSystem002.mos"
sync_file = base / "constant/fluid/boundarySyncFile"
# ===== outletWeightAreaTempFile の読み込み =====
text = input_file.read_text(encoding="utf-8")
def get_value(key):
"""Tout,293.094; のような行から値を取り出す"""
m = re.search(rf"{key},([0-9eE\.\-]+);", text)
return float(m.group(1)) if m else None
Tout = get_value("Tout")
Tprobe = get_value("Tprobe")
TimeVal = get_value("Time")
print(f"[INFO] Read from {input_file.name}: Tout={Tout}, Tprobe={Tprobe}, Time={TimeVal}")
# ===== MOSファイルを自動生成 =====
mos_content = f"""
//loadModel(Modelica);
//loadFile("{base}/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={Tout},TSensor={Tprobe} -r={base}/HVACSystemResult.mat");
t := {TimeVal};
tout_val := val(Tout, t, fileName="{base}/HVACSystemResult.mat");
mout_val := val(mout, t, fileName="{base}/HVACSystemResult.mat");
heatFlowSensor_Q_flow_val := val(heatFlowSensor.Q_flow, t, fileName="{base}/HVACSystemResult.mat");
TSensor_val := val(TSensor, t, fileName="{base}/HVACSystemResult.mat");
TTarget_val := val(ramp.y, t, fileName="{base}/HVACSystemResult.mat");
// ===== OpenFOAM形式のヘッダを書き出す =====
writeFile("{sync_file}",
"FoamFile\\n"
+"{{\\n"
+" version 2.0;\\n"
+" format ascii;\\n"
+" class dictionary;\\n"
+" location constant;\\n"
+" object boundarySyncFile;\\n"
+"}}\\n"
);
// ===== データ行を書き出す =====
writeFile("{sync_file}", "Time\\t" + String(t) + ";\\n", append=true);
writeFile("{sync_file}", "mout\\t" + String(mout_val) + ";\\n", append=true);
writeFile("{sync_file}", "Tout\\t" + String(tout_val) + ";\\n", append=true);
writeFile("{sync_file}", "heatFlowSensor.Q_flow\\t" + String(heatFlowSensor_Q_flow_val) + ";\\n", append=true);
writeFile("{sync_file}", "TSensor\\t" + String(TSensor_val) + ";\\n", append=true);
writeFile("{sync_file}", "ramp.y\\t" + String(TTarget_val) + ";\\n", append=true);
"""
mos_file.write_text(mos_content, encoding="utf-8")
print(f"[INFO] Wrote updated {mos_file.name}")
# ===== runMos.sh 経由で実行 =====
cmd = ["./runMos.sh", str(mos_file.name)]
print(f"[INFO] Running: {' '.join(cmd)} in {base}")
subprocess.run(cmd, cwd=base)
# ===== 処理完了後にロック削除 =====
lock = base / "constant/updateMOS.lock"
if lock.exists():
lock.unlink()
print("OK [INFO] Lock file removed, OpenFOAM can continue.")
else:
print("[INFO] No lock file found.")
# ===== output.dat の初期化(Time = 0 のとき) =====
out_path_init = base / "output.dat"
if TimeVal is not None and abs(TimeVal) < 1e-9:
if out_path_init.exists():
out_path_init.unlink()
print(f"[INFO] {out_path_init.name} removed (Time={TimeVal}), will be re-created.")
# ===== boundarySyncFile を読み込み output.dat に追記 =====
vals = parse_boundary_sync(sync_file)
out_path, row = append_output_row(
base,
vals,
headers=["Time", "mout", "Tout", "heatFlowSensor.Q_flow", "TSensor", "ramp.y"],
sep="\t"
)
print(f"[INFO] Appended to {out_path}: {row}")
ここで行っているのは以下です。
- HVACSystem002.mosというOpenModelicaをスクリプト実行するためのファイルを作成
- 最後にrunMos.shによりHVACSystem002.mosを実行
- constant/updateMOS.lockが削除し、Python実行が終了したことをOpenFOAMに知らせる
- output.datにOpenModelicaで得られた結果を出力する
HVACSystem002.mos
mosファイルの全文を載せておきます。
//loadModel(Modelica);
//loadFile("/mnt/d/work/openfoam/20250516_coconala_thermal/20250825_cht_heatroom/run003_model3_OpenModelica/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.153,TSensor=297.223 -r=/mnt/d/work/openfoam/20250516_coconala_thermal/20250825_cht_heatroom/run003_model3_OpenModelica/HVACSystemResult.mat");
t := 1.21601;
tout_val := val(Tout, t, fileName="/mnt/d/work/openfoam/20250516_coconala_thermal/20250825_cht_heatroom/run003_model3_OpenModelica/HVACSystemResult.mat");
mout_val := val(mout, t, fileName="/mnt/d/work/openfoam/20250516_coconala_thermal/20250825_cht_heatroom/run003_model3_OpenModelica/HVACSystemResult.mat");
heatFlowSensor_Q_flow_val := val(heatFlowSensor.Q_flow, t, fileName="/mnt/d/work/openfoam/20250516_coconala_thermal/20250825_cht_heatroom/run003_model3_OpenModelica/HVACSystemResult.mat");
TSensor_val := val(TSensor, t, fileName="/mnt/d/work/openfoam/20250516_coconala_thermal/20250825_cht_heatroom/run003_model3_OpenModelica/HVACSystemResult.mat");
TTarget_val := val(ramp.y, t, fileName="/mnt/d/work/openfoam/20250516_coconala_thermal/20250825_cht_heatroom/run003_model3_OpenModelica/HVACSystemResult.mat");
// ===== OpenFOAM形式のヘッダを書き出す =====
writeFile("/mnt/d/work/openfoam/20250516_coconala_thermal/20250825_cht_heatroom/run003_model3_OpenModelica/constant/fluid/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/20250516_coconala_thermal/20250825_cht_heatroom/run003_model3_OpenModelica/constant/fluid/boundarySyncFile", "Time\t" + String(t) + ";\n", append=true);
writeFile("/mnt/d/work/openfoam/20250516_coconala_thermal/20250825_cht_heatroom/run003_model3_OpenModelica/constant/fluid/boundarySyncFile", "mout\t" + String(mout_val) + ";\n", append=true);
writeFile("/mnt/d/work/openfoam/20250516_coconala_thermal/20250825_cht_heatroom/run003_model3_OpenModelica/constant/fluid/boundarySyncFile", "Tout\t" + String(tout_val) + ";\n", append=true);
writeFile("/mnt/d/work/openfoam/20250516_coconala_thermal/20250825_cht_heatroom/run003_model3_OpenModelica/constant/fluid/boundarySyncFile", "heatFlowSensor.Q_flow\t" + String(heatFlowSensor_Q_flow_val) + ";\n", append=true);
writeFile("/mnt/d/work/openfoam/20250516_coconala_thermal/20250825_cht_heatroom/run003_model3_OpenModelica/constant/fluid/boundarySyncFile", "TSensor\t" + String(TSensor_val) + ";\n", append=true);
writeFile("/mnt/d/work/openfoam/20250516_coconala_thermal/20250825_cht_heatroom/run003_model3_OpenModelica/constant/fluid/boundarySyncFile", "ramp.y\t" + String(TTarget_val) + ";\n", append=true);
- やっていることをまとめると以下です。
- Modelica標準ライブラリを読み込む
loadModel(Modelica); - 指定されたモデルファイルを読み込む
/mnt/d/work/openfoam/20251017_OpenCAE2025/heatedRoom/HVACSystem001.mo - モデルをビルド(コンパイル)
モデル名:HVACSystem001
解析条件:
- 開始時刻:0
- 終了時刻:300
- ステップ数:1000
- 許容誤差:1e-6
- 数値積分法:dassl
- 出力形式:.mat
- 出力プレフィックス:HVACSystem - システムコマンドでシミュレーションを実行(ここで値を更新している)
パラメータ上書き
-Tinlet=293.0
-TSensor=310.0
結果出力:
/mnt/d/work/openfoam/20251017_OpenCAE2025/heatedRoom/HVACSystemResult.mat - 指定時刻
t=300.0における変数値を読み取る
-Tout(出口温度)
-mout(質量流量)
-heatFlowSensor.Q_flow(熱流量)
-TSensor(センサー温度)
-ramp.y(ターゲット温度) - OpenFOAM形式のヘッダを
constant/boundarySyncFileに書き出す
FoamFileブロックとして、
- バージョン、フォーマット、クラス、場所、オブジェクト名を定義。 - 取得したデータ行を追記モードで出力
各行に以下の項目を書き出す
-Time(時刻)
-mout(質量流量)
-Tout(出口温度)
-heatFlowSensor.Q_flow(熱流量)
-TSensor(センサー温度)
-ramp.y(ターゲット温度)
OpenModelicaではOpenFOAMで計算したTSensorがOpenModelicaでしているターゲット温度波形(ramp.y:下図一番左オレンジ線)になるようにPID制御を行っています。

この記述をupdateMOS_and_Run.py内で行っているというのが重要なところで、TinletとTSensorはOpenFOAMでの結果で得られた値になります。
つまり、OpenFOAMにとってのoutletの温度$\overline{T} = \frac{\sum_i \rho v_iA_i C_p T_i}{\sum_i \rho v_iA_i C_p }= \frac{\sum_i \rho Q_i C_p T_i}{\sum_i \rho Q_i C_p }$はOpenModelicaではTinletとしてインプットされているということです。

constant/fluid/boundarySyncFileに結果を出力します。
constant/fluid/boundarySyncFile
FoamFile
{
version 2.0;
format ascii;
class dictionary;
location constant;
object boundarySyncFile;
}
Time 1.21601;
mout 1.00134;
Tout 395.992;
heatFlowSensor.Q_flow 92086;
TSensor 297.223;
ramp.y 298;
今度はOpenFOAMがこのファイルを読み込み境界名inletに与えれることになります。
OpenFOAMの境界条件の話に行く前に、先にOpenModelicaの設定の話をしておきます。
HVACSystem001.mo
OpenModelicaもUbuntuから起動させモデルを読み込みます。

以下のようにモデル読み込みの警告が出るかもしれません。
気にせず読み込みます(Unload all &なんとかをクリック)

このmosファイルを使ってOpenModelicaを動かしているわけですが、今回のOpenModelicaのモデルは以下のように変更しました。

- 電気回路による発熱量に上限180000Wを設けています。
こうすることでOpenFOAMの境界温度を大きくなりすぎないようにしています。 - 質量流の上限も$1.2\times 0.06\times 2\times 10$にしました。
空気の密度$1.2\, [\text{kg}/\text{m}^3]$、 流入面積$0.06\, [\text{m}^2]$、 流速の上限$2.0\, [\text{m}/\text{s}]$にしています。
あとは適当に係数を10倍にして上限を調整。

