merge/model-development-into-main #2
No files matched your search
@@ -63,6 +63,14 @@
|
||||
- `tests/test_test_mql_comparison.py`
|
||||
- 保护 AMESim 原始序列零误差对齐、粗时间步插值、偏移误差统计、缺失变量报错和 CSV 输出。
|
||||
|
||||
- `PythonModels/systems/test_mql_pneumatic.py`
|
||||
- 把 AMESim 配置中的 `PNCH023`、`PNCH012`、`PNOR001`、`PNVO001` 装配成 Python 气动对象。
|
||||
- 当前覆盖 4 个固定气室、8 个变容气室、8 个固定孔口、8 个可变孔口,alias 沿用 AMESim 原名。
|
||||
- 初始压力按 AMESim 全局参数 `P0=153 bar` 和大气压 `1.013 bar` 转成 `15198700 Pa`。
|
||||
|
||||
- `tests/test_test_mql_pneumatic.py`
|
||||
- 保护气动装配数量、alias、氦气工质、气室体积、孔口面积/流量系数和基础质量流量计算。
|
||||
|
||||
## 物性约定
|
||||
|
||||
AMESim 模型中 `test_mql` 使用氦气,Python 侧当前通过 `HELIUM_PR` 使用 Peng-Robinson 状态方程计算气体压缩因子和密度。当前物性层先覆盖状态方程相关量,完整焓/内能偏差函数后续在接气室能量方程时再补。
|
||||
@@ -109,12 +117,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/systems/test_mql.py PythonModels/systems/test_mql_config.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/systems/test_mql.py PythonModels/systems/test_mql_config.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 物理结果复刻。
|
||||
|
||||
## 后续方向
|
||||
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
|
||||
from PythonModels.components.amesim_pneumatic import (
|
||||
HELIUM_PNEUMATIC_GAS,
|
||||
AmesimPneumaticGas,
|
||||
AmesimPneumaticOrifice,
|
||||
AmesimPneumaticVolume,
|
||||
)
|
||||
from PythonModels.systems.test_mql_config import TestMqlConfig, TestMqlResolvedComponent
|
||||
|
||||
|
||||
ATMOSPHERIC_PRESSURE_BAR = 1.013
|
||||
BAR_TO_PA = 1.0e5
|
||||
DEFAULT_TEST_MQL_TEMPERATURE_K = 293.15
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class TestMqlPneumaticAssembly:
|
||||
fixed_chambers: dict[str, AmesimPneumaticVolume]
|
||||
variable_chambers: dict[str, AmesimPneumaticVolume]
|
||||
fixed_orifices: dict[str, AmesimPneumaticOrifice]
|
||||
variable_orifices: dict[str, AmesimPneumaticOrifice]
|
||||
initial_pressure_pa: float
|
||||
|
||||
@property
|
||||
def chamber_count(self) -> int:
|
||||
return len(self.fixed_chambers) + len(self.variable_chambers)
|
||||
|
||||
@property
|
||||
def orifice_count(self) -> int:
|
||||
return len(self.fixed_orifices) + len(self.variable_orifices)
|
||||
|
||||
@property
|
||||
def component_count(self) -> int:
|
||||
return self.chamber_count + self.orifice_count
|
||||
|
||||
@property
|
||||
def aliases(self) -> tuple[str, ...]:
|
||||
return tuple(
|
||||
[
|
||||
*self.fixed_chambers,
|
||||
*self.variable_chambers,
|
||||
*self.fixed_orifices,
|
||||
*self.variable_orifices,
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
def build_test_mql_pneumatic_assembly(
|
||||
config: TestMqlConfig | None = None,
|
||||
gas: AmesimPneumaticGas = HELIUM_PNEUMATIC_GAS,
|
||||
) -> TestMqlPneumaticAssembly:
|
||||
config = config or TestMqlConfig.from_amesim_specs()
|
||||
initial_pressure_pa = pressure_from_amesim_bar_parameter(config.global_parameters["P0"])
|
||||
fixed_chambers = {
|
||||
component.alias: _build_chamber(
|
||||
component,
|
||||
volume_parameter="cvol",
|
||||
gas=gas,
|
||||
initial_pressure_pa=initial_pressure_pa,
|
||||
)
|
||||
for component in config.components_by_submodel("PNCH023")
|
||||
}
|
||||
variable_chambers = {
|
||||
component.alias: _build_chamber(
|
||||
component,
|
||||
volume_parameter="cvol0",
|
||||
gas=gas,
|
||||
initial_pressure_pa=initial_pressure_pa,
|
||||
)
|
||||
for component in config.components_by_submodel("PNCH012")
|
||||
}
|
||||
fixed_orifices = {
|
||||
component.alias: _build_orifice(
|
||||
component,
|
||||
area_parameter="area",
|
||||
gas=gas,
|
||||
opening=1.0,
|
||||
)
|
||||
for component in config.components_by_submodel("PNOR001")
|
||||
}
|
||||
variable_orifices = {
|
||||
component.alias: _build_orifice(
|
||||
component,
|
||||
area_parameter="area0",
|
||||
gas=gas,
|
||||
opening=1.0,
|
||||
)
|
||||
for component in config.components_by_submodel("PNVO001")
|
||||
}
|
||||
return TestMqlPneumaticAssembly(
|
||||
fixed_chambers=fixed_chambers,
|
||||
variable_chambers=variable_chambers,
|
||||
fixed_orifices=fixed_orifices,
|
||||
variable_orifices=variable_orifices,
|
||||
initial_pressure_pa=initial_pressure_pa,
|
||||
)
|
||||
|
||||
|
||||
def pressure_from_amesim_bar_parameter(pressure_bar: float) -> float:
|
||||
return (pressure_bar - ATMOSPHERIC_PRESSURE_BAR) * BAR_TO_PA
|
||||
|
||||
|
||||
def _build_chamber(
|
||||
component: TestMqlResolvedComponent,
|
||||
*,
|
||||
volume_parameter: str,
|
||||
gas: AmesimPneumaticGas,
|
||||
initial_pressure_pa: float,
|
||||
) -> AmesimPneumaticVolume:
|
||||
return AmesimPneumaticVolume.from_liters(
|
||||
name=component.alias,
|
||||
volume_liters=component.parameter_value(volume_parameter),
|
||||
gas=gas,
|
||||
p0=initial_pressure_pa,
|
||||
T0=_component_temperature(component),
|
||||
)
|
||||
|
||||
|
||||
def _build_orifice(
|
||||
component: TestMqlResolvedComponent,
|
||||
*,
|
||||
area_parameter: str,
|
||||
gas: AmesimPneumaticGas,
|
||||
opening: float,
|
||||
) -> AmesimPneumaticOrifice:
|
||||
return AmesimPneumaticOrifice.from_mm2(
|
||||
name=component.alias,
|
||||
area_mm2=component.parameter_value(area_parameter),
|
||||
flow_coefficient=component.parameter_value("cq"),
|
||||
gas=gas,
|
||||
opening=opening,
|
||||
)
|
||||
|
||||
|
||||
def _component_temperature(component: TestMqlResolvedComponent) -> float:
|
||||
parameter = component.parameters.get("extemp")
|
||||
if parameter is None or parameter.value is None:
|
||||
return DEFAULT_TEST_MQL_TEMPERATURE_K
|
||||
return parameter.value
|
||||
@@ -0,0 +1,68 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import unittest
|
||||
|
||||
from PythonModels.components.amesim_pneumatic import HELIUM_PNEUMATIC_GAS
|
||||
from PythonModels.systems.test_mql_pneumatic import (
|
||||
build_test_mql_pneumatic_assembly,
|
||||
pressure_from_amesim_bar_parameter,
|
||||
)
|
||||
|
||||
|
||||
class TestMqlPneumaticAssemblyTests(unittest.TestCase):
|
||||
def test_assembles_expected_amesim_pneumatic_component_counts(self) -> None:
|
||||
assembly = build_test_mql_pneumatic_assembly()
|
||||
|
||||
self.assertEqual(len(assembly.fixed_chambers), 4)
|
||||
self.assertEqual(len(assembly.variable_chambers), 8)
|
||||
self.assertEqual(len(assembly.fixed_orifices), 8)
|
||||
self.assertEqual(len(assembly.variable_orifices), 8)
|
||||
self.assertEqual(assembly.component_count, 28)
|
||||
self.assertIn("pn_general_chamber", assembly.aliases)
|
||||
self.assertIn("pn_c1_8", assembly.aliases)
|
||||
self.assertIn("pn_orifice_18", assembly.aliases)
|
||||
self.assertIn("pn_morifice_11", assembly.aliases)
|
||||
|
||||
def test_uses_amesim_global_initial_pressure_convention(self) -> None:
|
||||
assembly = build_test_mql_pneumatic_assembly()
|
||||
|
||||
self.assertAlmostEqual(
|
||||
pressure_from_amesim_bar_parameter(153.0),
|
||||
15198700.0,
|
||||
)
|
||||
self.assertAlmostEqual(assembly.initial_pressure_pa, 15198700.0)
|
||||
|
||||
def test_chamber_parameters_are_resolved_in_si_units(self) -> None:
|
||||
assembly = build_test_mql_pneumatic_assembly()
|
||||
fixed_chamber = assembly.fixed_chambers["pn_general_chamber"]
|
||||
variable_chamber = assembly.variable_chambers["pn_c1_8"]
|
||||
|
||||
self.assertAlmostEqual(fixed_chamber.volume, 0.057)
|
||||
self.assertAlmostEqual(variable_chamber.volume, 0.015)
|
||||
self.assertIs(fixed_chamber.gas, HELIUM_PNEUMATIC_GAS)
|
||||
self.assertIs(variable_chamber.gas, HELIUM_PNEUMATIC_GAS)
|
||||
self.assertAlmostEqual(fixed_chamber.properties().T, 293.15)
|
||||
self.assertAlmostEqual(variable_chamber.properties().T, 293.15)
|
||||
|
||||
def test_orifice_parameters_are_resolved_in_si_units(self) -> None:
|
||||
assembly = build_test_mql_pneumatic_assembly()
|
||||
fixed_orifice = assembly.fixed_orifices["pn_orifice_18"]
|
||||
variable_orifice = assembly.variable_orifices["pn_morifice_11"]
|
||||
|
||||
self.assertAlmostEqual(fixed_orifice.area, 78.5e-6)
|
||||
self.assertAlmostEqual(fixed_orifice.flow_coefficient, 0.9)
|
||||
self.assertAlmostEqual(variable_orifice.area, 78.5e-6)
|
||||
self.assertAlmostEqual(variable_orifice.flow_coefficient, 0.45)
|
||||
self.assertAlmostEqual(variable_orifice.opening, 1.0)
|
||||
|
||||
def test_assembled_orifice_can_compute_helium_mass_flow(self) -> None:
|
||||
assembly = build_test_mql_pneumatic_assembly()
|
||||
fixed_orifice = assembly.fixed_orifices["pn_orifice_18"]
|
||||
|
||||
flow = fixed_orifice.mass_flow(15.0e6, 10.0e6, 293.15)
|
||||
|
||||
self.assertGreater(flow, 0.0)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in new issue
Block a user