merge/model-development-into-main #2

Merged
lujingze merged 126 commits from merge/model-development-into-main into main 2026-07-31 09:52:44 +08:00
3 changed files with 184 additions and 2 deletions
Showing only changes of commit 03a6db10ad - Show all commits

No files matched your search

+10 -2
View File
@@ -78,6 +78,14 @@
- `tests/test_test_mql_variables.py`
- 保护变量目录覆盖率、组件变量映射、连接变量映射、子模型计数和非法 `Data_Path` 报错。
- `PythonModels/systems/test_mql_lines.py`
- 把 AMESim `CONNECTION_SPECS` 中的管路连接装配成 Python 管路目录。
- 当前覆盖 `PNL0001` 20 条、`PNL0002` 8 条、`PNL0003` 8 条、`PNL00R` 4 条,共 40 条。
- 保留 `pneumatic_*` alias、源/目标组件端口、`C-R`/`R-C-R`/`C-R-C`/`R` 模式和对应 `Data_Path`。
- `tests/test_test_mql_lines.py`
- 保护管路数量、拓扑端口、管路模式、典型 `Data_Path` 和子模型分组。
## 物性约定
AMESim 模型中 `test_mql` 使用氦气,Python 侧当前通过 `HELIUM_PR` 使用 Peng-Robinson 状态方程计算气体压缩因子和密度。当前物性层先覆盖状态方程相关量,完整焓/内能偏差函数后续在接气室能量方程时再补。
@@ -124,12 +132,12 @@ AMESim 模型中 `test_mql` 使用氦气,Python 侧当前通过 `HELIUM_PR`
## 验证方式
```bash
python3 -m py_compile PythonModels/components/amesim_pneumatic.py PythonModels/core/peng_robinson.py PythonModels/reporting/amesim_results.py PythonModels/reporting/test_mql_comparison.py PythonModels/reporting/test_mql_variables.py PythonModels/systems/test_mql.py PythonModels/systems/test_mql_config.py PythonModels/systems/test_mql_pneumatic.py PythonModels/scripts/run_test_mql.py
python3 -m py_compile PythonModels/components/amesim_pneumatic.py PythonModels/core/peng_robinson.py PythonModels/reporting/amesim_results.py PythonModels/reporting/test_mql_comparison.py PythonModels/reporting/test_mql_variables.py PythonModels/systems/test_mql.py PythonModels/systems/test_mql_config.py PythonModels/systems/test_mql_lines.py PythonModels/systems/test_mql_pneumatic.py PythonModels/scripts/run_test_mql.py
python3 -m PythonModels.scripts.run_test_mql
python3 -m unittest discover -s tests -t .
```
当前测试覆盖的是结构、配置解析、气动原语、气动件装配、AMESim 结果读取、变量目录和 AMESim/Python 时序对齐工具,不代表已经完成 AMESim 物理结果复刻。
当前测试覆盖的是结构、配置解析、气动原语、气动件装配、管路连接装配、AMESim 结果读取、变量目录和 AMESim/Python 时序对齐工具,不代表已经完成 AMESim 物理结果复刻。
## 后续方向
+102
View File
@@ -0,0 +1,102 @@
from __future__ import annotations
import re
from collections import Counter
from dataclasses import dataclass
from PythonModels.reporting.amesim_results import AmesimResults
from PythonModels.reporting.test_mql_variables import (
TestMqlVariableCatalog,
build_test_mql_variable_catalog,
)
from PythonModels.systems.test_mql import CONNECTION_SPECS
TEST_MQL_PNEUMATIC_LINE_SUBMODELS = ("PNL0001", "PNL0002", "PNL0003", "PNL00R")
_LINE_PATTERN_RE = re.compile(r"\(([^()]+)\)\s*$")
@dataclass(frozen=True)
class TestMqlLineConnection:
index: int
alias: str
submodel: str
pattern: str
source_component: str
source_port: str
target_component: str
target_port: str
label: str
data_paths: tuple[str, ...]
signal_names: tuple[str, ...]
@property
def has_compliance(self) -> bool:
return "C" in self.pattern
@property
def has_resistance(self) -> bool:
return "R" in self.pattern
@dataclass(frozen=True)
class TestMqlLineAssembly:
lines: tuple[TestMqlLineConnection, ...]
@property
def line_count(self) -> int:
return len(self.lines)
def by_alias(self, alias: str) -> TestMqlLineConnection:
for line in self.lines:
if line.alias == alias:
return line
raise KeyError(alias)
def by_submodel(self, submodel: str) -> tuple[TestMqlLineConnection, ...]:
return tuple(line for line in self.lines if line.submodel == submodel)
def counts_by_submodel(self) -> dict[str, int]:
return dict(Counter(line.submodel for line in self.lines))
def aliases(self) -> tuple[str, ...]:
return tuple(line.alias for line in self.lines)
def build_test_mql_line_assembly(
amesim_results: AmesimResults,
variable_catalog: TestMqlVariableCatalog | None = None,
) -> TestMqlLineAssembly:
variable_catalog = variable_catalog or build_test_mql_variable_catalog(amesim_results)
lines = []
for spec in CONNECTION_SPECS:
submodel = str(spec["submodel"])
if submodel not in TEST_MQL_PNEUMATIC_LINE_SUBMODELS:
continue
data_paths = variable_catalog.data_paths_for_owner(str(spec["alias"]))
signal_names = tuple(path.rsplit("@", 1)[0] for path in data_paths)
lines.append(
TestMqlLineConnection(
index=int(spec["index"]),
alias=str(spec["alias"]),
submodel=submodel,
pattern=_line_pattern(str(spec["label"]), submodel),
source_component=str(spec["source_component"]),
source_port=str(spec["source_port"]),
target_component=str(spec["target_component"]),
target_port=str(spec["target_port"]),
label=str(spec["label"]),
data_paths=data_paths,
signal_names=signal_names,
)
)
return TestMqlLineAssembly(lines=tuple(lines))
def _line_pattern(label: str, submodel: str) -> str:
match = _LINE_PATTERN_RE.search(label)
if match is not None:
return match.group(1)
if submodel == "PNL00R":
return "R"
return submodel
+72
View File
@@ -0,0 +1,72 @@
from __future__ import annotations
import unittest
from pathlib import Path
from PythonModels.reporting.amesim_results import load_test_mql_amesim_results
from PythonModels.reporting.test_mql_variables import build_test_mql_variable_catalog
from PythonModels.systems.test_mql_lines import build_test_mql_line_assembly
REPO_ROOT = Path(__file__).resolve().parents[1]
TEST_MQL_AME = REPO_ROOT / "AmesimModels" / "test_mql.ame"
class TestMqlLineAssemblyTests(unittest.TestCase):
@classmethod
def setUpClass(cls) -> None:
cls.amesim_results = load_test_mql_amesim_results(TEST_MQL_AME)
cls.variable_catalog = build_test_mql_variable_catalog(cls.amesim_results)
cls.assembly = build_test_mql_line_assembly(
cls.amesim_results,
cls.variable_catalog,
)
def test_assembles_expected_line_counts(self) -> None:
self.assertEqual(self.assembly.line_count, 40)
self.assertEqual(
self.assembly.counts_by_submodel(),
{"PNL0001": 20, "PNL0003": 8, "PNL0002": 8, "PNL00R": 4},
)
self.assertIn("pneumatic_69", self.assembly.aliases())
self.assertIn("pneumatic_100", self.assembly.aliases())
def test_preserves_connection_topology(self) -> None:
line = self.assembly.by_alias("pneumatic_69")
self.assertEqual(line.index, 37)
self.assertEqual(line.submodel, "PNL0001")
self.assertEqual(line.pattern, "C-R")
self.assertEqual(line.source_component, "pnnode4_16")
self.assertEqual(line.source_port, "port_2")
self.assertEqual(line.target_component, "pn_c1_8")
self.assertEqual(line.target_port, "port_1")
self.assertTrue(line.has_compliance)
self.assertTrue(line.has_resistance)
def test_line_data_paths_are_attached_from_amesim_results(self) -> None:
line = self.assembly.by_alias("pneumatic_69")
self.assertIn("dh1@pneumatic_69", line.data_paths)
self.assertIn("dm1@pneumatic_69", line.data_paths)
self.assertIn("dh1", line.signal_names)
self.assertIn("dm1", line.signal_names)
def test_resistance_only_line_keeps_r_pattern(self) -> None:
line = self.assembly.by_alias("pneumatic_100")
self.assertEqual(line.submodel, "PNL00R")
self.assertEqual(line.pattern, "R")
self.assertFalse(line.has_compliance)
self.assertTrue(line.has_resistance)
self.assertIn("dm1@pneumatic_100", line.data_paths)
def test_rcr_and_crc_patterns_are_distinct(self) -> None:
self.assertEqual(self.assembly.by_alias("pneumatic_80").pattern, "R-C-R")
self.assertEqual(self.assembly.by_alias("pneumatic_75").pattern, "C-R-C")
self.assertEqual(len(self.assembly.by_submodel("PNL0002")), 8)
self.assertEqual(len(self.assembly.by_submodel("PNL0003")), 8)
if __name__ == "__main__":
unittest.main()