Prechádzať zdrojové kódy

test(thermal): add verify_enable_thermal.py + TEST-061 end-to-end PASS

Verified the executor enable_thermal plumbing on real Motor-CAD: RobustMotorCADSolver(enable_thermal=True) runs EM + steady-state thermal, merges 7 thermal metrics, and writes them to scan_results.csv. temp_rise +27.57, thermal_resistance +5.657 (ambient=25).
carlin 21 hodín pred
rodič
commit
f5749a94a7
2 zmenil súbory, kde vykonal 104 pridanie a 0 odobranie
  1. 19 0
      docs/TEST_RECORDS.md
  2. 85 0
      scripts/verify_enable_thermal.py

+ 19 - 0
docs/TEST_RECORDS.md

@@ -2194,3 +2194,22 @@ P5-M6 引入的 `enable_thermal` 开关使用了两个**不存在的 API**,从
 | 总损耗 | 49.92W | 42.57W |
 
 **结论**:磁热耦合更准确(温度反馈迭代)但更慢。"省时间"选分离式(enable_thermal 已实现)。
+
+---
+
+## TEST-061:执行器 enable_thermal 端到端真实验证
+
+| 项目 | 内容 |
+|---|---|
+| 测试日期 | 2026-09-04 |
+| 测试目的 | 验证执行器 enable_thermal 全链路(RobustMotorCADSolver→热求解→热指标落盘 CSV),此前仅代码透传 + 单元测试 |
+
+### 结果(scripts/verify_enable_thermal.py)
+
+- `RobustMotorCADSolver(enable_thermal=True)` 跑单点:电磁 + 稳态热求解均成功(status=OK)。
+- 7 项热指标全部合并到 point result 并落盘 `scan_results.csv`(header 含全部热指标列)。
+- 环境温度覆盖 25°C 后:温升 +27.57°C、热阻 +5.657 K/W(物理合理正值)。
+
+### 结论
+
+执行器 enable_thermal 链路端到端 **PASS**。热仿真已完整接入生产链路(配置开关 → adapter → solver → 热指标落盘)。

+ 85 - 0
scripts/verify_enable_thermal.py

@@ -0,0 +1,85 @@
+"""Verify the executor enable_thermal plumbing end-to-end on real Motor-CAD.
+
+Runs RobustMotorCADSolver with enable_thermal=True for a single point and
+checks that:
+    1. The EM and thermal solves both complete.
+    2. Thermal metrics are merged into the point result.
+    3. Thermal metric columns are written to scan_results.csv.
+
+Ambient temperature is overridden to 25 C in-memory (the MARS model ships an
+abnormal 125 C) so the temperature-rise / thermal-resistance metrics come out
+physically positive.
+
+Run with the venv python that has ansys-motorcad-core installed, e.g.:
+    <venv>/Scripts/python.exe scripts/verify_enable_thermal.py
+
+All source is ASCII only.
+"""
+from __future__ import annotations
+
+import os
+import sys
+import time
+from pathlib import Path
+
+_ROOT = Path(__file__).resolve().parent.parent
+sys.path.insert(0, str(_ROOT / "src"))
+sys.path.insert(0, str(_ROOT / "scripts"))
+
+from robust_motorcad import RobustMotorCADSolver  # noqa: E402
+
+MODEL = str(_ROOT / "models" / "MARS-12S10P_SSSR_D76-C150_V5.0-0819.mot")
+
+THERMAL_KEYS = [
+    "winding_temp_c", "winding_hotspot_temp_c", "magnet_temp_c",
+    "stator_temp_c", "bearing_temp_c", "temp_rise_c",
+    "thermal_resistance_k_w",
+]
+
+
+def main() -> int:
+    output_dir = str(_ROOT / "output" / (
+        "verify_thermal_" + time.strftime("%Y%m%d_%H%M%S")
+    ))
+    solver = RobustMotorCADSolver(
+        model_path=MODEL, output_dir=output_dir, enable_thermal=True
+    )
+    solver.connect()
+    try:
+        result = solver.run_single_point(
+            {"RMSCurrent": 21.0, "Shaft_Speed": 5000.0,
+             "Ambient_Temperature": 25.0},
+            point_index=0, point_label="verify",
+        )
+        print("status: %s" % result["status"], flush=True)
+        print("=== thermal metrics in point result ===", flush=True)
+        for key in THERMAL_KEYS:
+            print("  %s = %s" % (key, result.get("metrics", {}).get(key)),
+                  flush=True)
+
+        csv_path = os.path.join(output_dir, "scan_results.csv")
+        csv_ok = os.path.exists(csv_path)
+        print("scan_results.csv exists: %s" % csv_ok, flush=True)
+        thermal_in_csv = False
+        if csv_ok:
+            header = open(csv_path, encoding="utf-8").readline().rstrip()
+            thermal_in_csv = all(k in header for k in THERMAL_KEYS)
+            print("thermal columns present in CSV header: %s" % thermal_in_csv,
+                  flush=True)
+
+        merged = all(
+            key in result.get("metrics", {}) for key in THERMAL_KEYS
+        )
+        ok = (
+            result["status"] == "OK" and merged and csv_ok and thermal_in_csv
+        )
+        print("")
+        print("ENABLE_THERMAL END-TO-END: %s" % ("PASS" if ok else "FAIL"),
+              flush=True)
+        return 0 if ok else 1
+    finally:
+        solver.disconnect()
+
+
+if __name__ == "__main__":
+    sys.exit(main())