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}")

ここで行っているのは以下です。

  1. HVACSystem002.mosというOpenModelicaをスクリプト実行するためのファイルを作成
  2. 最後にrunMos.shによりHVACSystem002.mosを実行
  3. constant/updateMOS.lockが削除し、Python実行が終了したことをOpenFOAMに知らせる
  4. 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);
  1. やっていることをまとめると以下です。
  2. Modelica標準ライブラリを読み込む
     loadModel(Modelica);
  3. 指定されたモデルファイルを読み込む
     /mnt/d/work/openfoam/20251017_OpenCAE2025/heatedRoom/HVACSystem001.mo
  4. モデルをビルド(コンパイル)
     モデル名:HVACSystem001
     解析条件:
     - 開始時刻:0
     - 終了時刻:300
     - ステップ数:1000
     - 許容誤差:1e-6
     - 数値積分法:dassl
     - 出力形式:.mat
     - 出力プレフィックス:HVACSystem
  5. システムコマンドでシミュレーションを実行(ここで値を更新している)
     パラメータ上書き
     - Tinlet=293.0
     - TSensor=310.0
     結果出力:
     /mnt/d/work/openfoam/20251017_OpenCAE2025/heatedRoom/HVACSystemResult.mat
  6. 指定時刻 t=300.0 における変数値を読み取る
     - Tout(出口温度)
     - mout(質量流量)
     - heatFlowSensor.Q_flow(熱流量)
     - TSensor(センサー温度)
     - ramp.y(ターゲット温度)
  7. OpenFOAM形式のヘッダを constant/boundarySyncFile に書き出す
     FoamFile ブロックとして、
     - バージョン、フォーマット、クラス、場所、オブジェクト名を定義。
  8. 取得したデータ行を追記モードで出力
     各行に以下の項目を書き出す
     - 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倍にして上限を調整。
1 2 3 4 5