实现test_mql P4节点代数契约
This commit is contained in:
1 parent
5c3ad50090
commit
7569917924
3 files changed
+196
-1
No files matched your search
@@ -3863,6 +3863,7 @@ class TestMqlSystem:
|
|||||||
self.pnl0003_assembly = self._build_pnl0003_assembly()
|
self.pnl0003_assembly = self._build_pnl0003_assembly()
|
||||||
self.pnl00r_assembly = self._build_pnl00r_assembly()
|
self.pnl00r_assembly = self._build_pnl00r_assembly()
|
||||||
self.node3_assembly = self._build_node3_assembly()
|
self.node3_assembly = self._build_node3_assembly()
|
||||||
|
self.node4_assembly = self._build_node4_assembly()
|
||||||
self.pneumatic_assembly = self._build_pneumatic_assembly()
|
self.pneumatic_assembly = self._build_pneumatic_assembly()
|
||||||
pneumatic_components = self._pneumatic_components_by_alias()
|
pneumatic_components = self._pneumatic_components_by_alias()
|
||||||
for spec in COMPONENT_SPECS:
|
for spec in COMPONENT_SPECS:
|
||||||
@@ -3912,6 +3913,12 @@ class TestMqlSystem:
|
|||||||
|
|
||||||
return build_test_mql_node3_assembly()
|
return build_test_mql_node3_assembly()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _build_node4_assembly():
|
||||||
|
from PythonModels.systems.test_mql_nodes import build_test_mql_node4_assembly
|
||||||
|
|
||||||
|
return build_test_mql_node4_assembly()
|
||||||
|
|
||||||
def _pneumatic_components_by_alias(self) -> dict[str, Component]:
|
def _pneumatic_components_by_alias(self) -> dict[str, Component]:
|
||||||
return {
|
return {
|
||||||
**self.pneumatic_assembly.fixed_chambers,
|
**self.pneumatic_assembly.fixed_chambers,
|
||||||
@@ -3940,6 +3947,10 @@ class TestMqlSystem:
|
|||||||
def typed_node3_count(self) -> int:
|
def typed_node3_count(self) -> int:
|
||||||
return len(self.node3_assembly)
|
return len(self.node3_assembly)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def typed_node4_count(self) -> int:
|
||||||
|
return len(self.node4_assembly)
|
||||||
|
|
||||||
def _pnl0003_spec_for_node_port(self, node_alias: str, port_name: str):
|
def _pnl0003_spec_for_node_port(self, node_alias: str, port_name: str):
|
||||||
for spec in self.pnl0003_assembly.specs:
|
for spec in self.pnl0003_assembly.specs:
|
||||||
if spec.source_component == node_alias and spec.source_port == port_name:
|
if spec.source_component == node_alias and spec.source_port == port_name:
|
||||||
|
|||||||
@@ -74,9 +74,107 @@ class TestMqlPneumaticNode3:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class TestMqlPneumaticNode4Balance:
|
||||||
|
temperature_k: float
|
||||||
|
pressure_pa: float
|
||||||
|
port_1_enthalpy_flow_w: float
|
||||||
|
port_1_mass_flow_g_s: float
|
||||||
|
port_1_volume_derivative_l_min: float
|
||||||
|
port_1_volume_cm3: float
|
||||||
|
port_2_enthalpy_flow_w: float
|
||||||
|
port_2_mass_flow_g_s: float
|
||||||
|
port_2_volume_derivative_l_min: float
|
||||||
|
port_2_volume_cm3: float
|
||||||
|
port_3_enthalpy_flow_w: float
|
||||||
|
port_3_mass_flow_g_s: float
|
||||||
|
port_3_volume_derivative_l_min: float
|
||||||
|
port_3_volume_cm3: float
|
||||||
|
port_4_enthalpy_flow_w: float
|
||||||
|
port_4_mass_flow_g_s: float
|
||||||
|
port_4_volume_derivative_l_min: float
|
||||||
|
port_4_volume_cm3: float
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class TestMqlPneumaticNode4:
|
||||||
|
"""Exact algebraic contract of AMESim ``P4NODE2``.
|
||||||
|
|
||||||
|
Pressure and temperature are fixed by port 2 and duplicated to ports 1, 3,
|
||||||
|
and 4. Flow and volume signals at port 2 are the sums of ports 1, 3, and
|
||||||
|
4, matching the saved AMESim variables for ``pnnode4_*`` instances.
|
||||||
|
"""
|
||||||
|
|
||||||
|
alias: str
|
||||||
|
|
||||||
|
def balance(
|
||||||
|
self,
|
||||||
|
*,
|
||||||
|
port_2_temperature_k: float,
|
||||||
|
port_2_pressure_pa: float,
|
||||||
|
port_1_enthalpy_flow_w: float,
|
||||||
|
port_1_mass_flow_g_s: float,
|
||||||
|
port_3_enthalpy_flow_w: float,
|
||||||
|
port_3_mass_flow_g_s: float,
|
||||||
|
port_4_enthalpy_flow_w: float,
|
||||||
|
port_4_mass_flow_g_s: float,
|
||||||
|
port_1_volume_derivative_l_min: float = 0.0,
|
||||||
|
port_1_volume_cm3: float = 0.0,
|
||||||
|
port_3_volume_derivative_l_min: float = 0.0,
|
||||||
|
port_3_volume_cm3: float = 0.0,
|
||||||
|
port_4_volume_derivative_l_min: float = 0.0,
|
||||||
|
port_4_volume_cm3: float = 0.0,
|
||||||
|
) -> TestMqlPneumaticNode4Balance:
|
||||||
|
if port_2_temperature_k <= 0.0:
|
||||||
|
raise ValueError("port_2_temperature_k must be positive")
|
||||||
|
if port_2_pressure_pa <= 0.0:
|
||||||
|
raise ValueError("port_2_pressure_pa must be positive")
|
||||||
|
return TestMqlPneumaticNode4Balance(
|
||||||
|
temperature_k=port_2_temperature_k,
|
||||||
|
pressure_pa=port_2_pressure_pa,
|
||||||
|
port_1_enthalpy_flow_w=port_1_enthalpy_flow_w,
|
||||||
|
port_1_mass_flow_g_s=port_1_mass_flow_g_s,
|
||||||
|
port_1_volume_derivative_l_min=port_1_volume_derivative_l_min,
|
||||||
|
port_1_volume_cm3=port_1_volume_cm3,
|
||||||
|
port_2_enthalpy_flow_w=(
|
||||||
|
port_1_enthalpy_flow_w
|
||||||
|
+ port_3_enthalpy_flow_w
|
||||||
|
+ port_4_enthalpy_flow_w
|
||||||
|
),
|
||||||
|
port_2_mass_flow_g_s=(
|
||||||
|
port_1_mass_flow_g_s
|
||||||
|
+ port_3_mass_flow_g_s
|
||||||
|
+ port_4_mass_flow_g_s
|
||||||
|
),
|
||||||
|
port_2_volume_derivative_l_min=(
|
||||||
|
port_1_volume_derivative_l_min
|
||||||
|
+ port_3_volume_derivative_l_min
|
||||||
|
+ port_4_volume_derivative_l_min
|
||||||
|
),
|
||||||
|
port_2_volume_cm3=(
|
||||||
|
port_1_volume_cm3 + port_3_volume_cm3 + port_4_volume_cm3
|
||||||
|
),
|
||||||
|
port_3_enthalpy_flow_w=port_3_enthalpy_flow_w,
|
||||||
|
port_3_mass_flow_g_s=port_3_mass_flow_g_s,
|
||||||
|
port_3_volume_derivative_l_min=port_3_volume_derivative_l_min,
|
||||||
|
port_3_volume_cm3=port_3_volume_cm3,
|
||||||
|
port_4_enthalpy_flow_w=port_4_enthalpy_flow_w,
|
||||||
|
port_4_mass_flow_g_s=port_4_mass_flow_g_s,
|
||||||
|
port_4_volume_derivative_l_min=port_4_volume_derivative_l_min,
|
||||||
|
port_4_volume_cm3=port_4_volume_cm3,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def build_test_mql_node3_assembly() -> dict[str, TestMqlPneumaticNode3]:
|
def build_test_mql_node3_assembly() -> dict[str, TestMqlPneumaticNode3]:
|
||||||
return {
|
return {
|
||||||
str(spec["alias"]): TestMqlPneumaticNode3(alias=str(spec["alias"]))
|
str(spec["alias"]): TestMqlPneumaticNode3(alias=str(spec["alias"]))
|
||||||
for spec in COMPONENT_SPECS
|
for spec in COMPONENT_SPECS
|
||||||
if spec["submodel"] == "PN3NODE2"
|
if spec["submodel"] == "PN3NODE2"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def build_test_mql_node4_assembly() -> dict[str, TestMqlPneumaticNode4]:
|
||||||
|
return {
|
||||||
|
str(spec["alias"]): TestMqlPneumaticNode4(alias=str(spec["alias"]))
|
||||||
|
for spec in COMPONENT_SPECS
|
||||||
|
if spec["submodel"] == "P4NODE2"
|
||||||
|
}
|
||||||
@@ -5,7 +5,10 @@ from pathlib import Path
|
|||||||
|
|
||||||
from PythonModels.reporting.amesim_results import load_test_mql_amesim_results
|
from PythonModels.reporting.amesim_results import load_test_mql_amesim_results
|
||||||
from PythonModels.systems.test_mql import TestMqlSystem
|
from PythonModels.systems.test_mql import TestMqlSystem
|
||||||
from PythonModels.systems.test_mql_nodes import build_test_mql_node3_assembly
|
from PythonModels.systems.test_mql_nodes import (
|
||||||
|
build_test_mql_node3_assembly,
|
||||||
|
build_test_mql_node4_assembly,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
REPO_ROOT = Path(__file__).resolve().parents[1]
|
REPO_ROOT = Path(__file__).resolve().parents[1]
|
||||||
@@ -83,5 +86,88 @@ class TestMqlPneumaticNode3Tests(unittest.TestCase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class TestMqlPneumaticNode4Tests(unittest.TestCase):
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls) -> None:
|
||||||
|
cls.nodes = build_test_mql_node4_assembly()
|
||||||
|
cls.results = load_test_mql_amesim_results(TEST_MQL_AME)
|
||||||
|
|
||||||
|
def test_builds_all_eight_p4node2_instances(self) -> None:
|
||||||
|
self.assertEqual(len(self.nodes), 8)
|
||||||
|
self.assertIn("pnnode4_17", self.nodes)
|
||||||
|
system = TestMqlSystem()
|
||||||
|
self.assertEqual(system.typed_node4_count, 8)
|
||||||
|
self.assertEqual(system.node4_assembly["pnnode4_17"].alias, "pnnode4_17")
|
||||||
|
|
||||||
|
def test_port_two_flow_is_exact_sum_of_ports_one_three_and_four(self) -> None:
|
||||||
|
balance = self.nodes["pnnode4_17"].balance(
|
||||||
|
port_2_temperature_k=281.0,
|
||||||
|
port_2_pressure_pa=4.4e6,
|
||||||
|
port_1_enthalpy_flow_w=2.0,
|
||||||
|
port_1_mass_flow_g_s=3.0,
|
||||||
|
port_3_enthalpy_flow_w=-0.5,
|
||||||
|
port_3_mass_flow_g_s=-1.0,
|
||||||
|
port_4_enthalpy_flow_w=4.0,
|
||||||
|
port_4_mass_flow_g_s=5.0,
|
||||||
|
port_1_volume_derivative_l_min=4.0,
|
||||||
|
port_1_volume_cm3=5.0,
|
||||||
|
port_3_volume_derivative_l_min=-1.0,
|
||||||
|
port_3_volume_cm3=6.0,
|
||||||
|
port_4_volume_derivative_l_min=2.0,
|
||||||
|
port_4_volume_cm3=7.0,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(balance.temperature_k, 281.0)
|
||||||
|
self.assertEqual(balance.pressure_pa, 4.4e6)
|
||||||
|
self.assertEqual(balance.port_2_enthalpy_flow_w, 5.5)
|
||||||
|
self.assertEqual(balance.port_2_mass_flow_g_s, 7.0)
|
||||||
|
self.assertEqual(balance.port_2_volume_derivative_l_min, 5.0)
|
||||||
|
self.assertEqual(balance.port_2_volume_cm3, 18.0)
|
||||||
|
|
||||||
|
def test_pnnode4_17_baseline_matches_adjacent_flow_sum(self) -> None:
|
||||||
|
index = 500
|
||||||
|
balance = self.nodes["pnnode4_17"].balance(
|
||||||
|
port_2_temperature_k=self.results.series("temp1@pnnode4_17")[index],
|
||||||
|
port_2_pressure_pa=(
|
||||||
|
self.results.series("press1@pnnode4_17")[index] + 101_300.0
|
||||||
|
),
|
||||||
|
port_1_enthalpy_flow_w=self.results.series("dh1@pneumatic_86")[index],
|
||||||
|
port_1_mass_flow_g_s=self.results.series("dm1@pneumatic_86")[index],
|
||||||
|
port_3_enthalpy_flow_w=self.results.series("dh2@pneumatic_83")[index],
|
||||||
|
port_3_mass_flow_g_s=self.results.series("dm2@pneumatic_83")[index],
|
||||||
|
port_4_enthalpy_flow_w=self.results.series("dh2@pn_morifice_9")[index],
|
||||||
|
port_4_mass_flow_g_s=self.results.series("dm2@pn_morifice_9")[index],
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertAlmostEqual(
|
||||||
|
balance.port_2_mass_flow_g_s,
|
||||||
|
self.results.series("dm2@pnnode4_17")[index],
|
||||||
|
)
|
||||||
|
self.assertAlmostEqual(
|
||||||
|
balance.port_2_enthalpy_flow_w,
|
||||||
|
self.results.series("dh2@pnnode4_17")[index],
|
||||||
|
)
|
||||||
|
self.assertEqual(self.results.series("vol2@pnnode4_17")[index], 0.0)
|
||||||
|
self.assertEqual(self.results.series("dvol2@pnnode4_17")[index], 0.0)
|
||||||
|
self.assertAlmostEqual(
|
||||||
|
self.results.series("press1@pnnode4_17")[index],
|
||||||
|
self.results.series("p2@pneumatic_68")[index],
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_rejects_nonphysical_primary_pressure_or_temperature(self) -> None:
|
||||||
|
node = self.nodes["pnnode4_17"]
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
node.balance(
|
||||||
|
port_2_temperature_k=0.0,
|
||||||
|
port_2_pressure_pa=1.0e5,
|
||||||
|
port_1_enthalpy_flow_w=0.0,
|
||||||
|
port_1_mass_flow_g_s=0.0,
|
||||||
|
port_3_enthalpy_flow_w=0.0,
|
||||||
|
port_3_mass_flow_g_s=0.0,
|
||||||
|
port_4_enthalpy_flow_w=0.0,
|
||||||
|
port_4_mass_flow_g_s=0.0,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
Reference in new issue
Block a user