From 420bafeb4eb172671a7bb4cfcb22d6e9332c4989 Mon Sep 17 00:00:00 2001 From: huojiarong Date: Wed, 29 Jul 2026 09:23:05 +0000 Subject: [PATCH] =?UTF-8?q?=E8=A1=A5=E5=85=85test=5Fmql=20pneumatic=5F69?= =?UTF-8?q?=E5=B1=80=E9=83=A8=E5=8F=82=E8=80=83RHS=E5=80=99=E9=80=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../run_test_mql_full_state_comparison.py | 25 +++- PythonModels/systems/test_mql.py | 12 ++ PythonModels/systems/test_mql_closure.py | 115 +++++++++++++++++- tests/test_test_mql_pnl0001_segment.py | 41 +++++++ 4 files changed, 186 insertions(+), 7 deletions(-) diff --git a/PythonModels/scripts/run_test_mql_full_state_comparison.py b/PythonModels/scripts/run_test_mql_full_state_comparison.py index 1ec2b49..1b3c8d4 100644 --- a/PythonModels/scripts/run_test_mql_full_state_comparison.py +++ b/PythonModels/scripts/run_test_mql_full_state_comparison.py @@ -784,6 +784,7 @@ class TestMqlFullStateComparisonExecutionConfig: inlet_node_temperature_k: float = 293.15 resistance_boundary_temperature_k: float = 293.15 use_pneumatic_96_reference_rhs: bool = False + use_pneumatic_69_reference_rhs: bool = False @dataclass(frozen=True) @@ -827,6 +828,9 @@ def run_test_mql_full_state_comparison( use_pneumatic_96_reference_rhs=( config.execution.use_pneumatic_96_reference_rhs ), + use_pneumatic_69_reference_rhs=( + config.execution.use_pneumatic_69_reference_rhs + ), config=config.execution.solver, t_eval=list(config.execution.t_eval) if config.execution.t_eval is not None else None, data_paths=selected_paths, @@ -844,6 +848,9 @@ def run_test_mql_full_state_comparison( use_pneumatic_96_reference_rhs=( config.execution.use_pneumatic_96_reference_rhs ), + use_pneumatic_69_reference_rhs=( + config.execution.use_pneumatic_69_reference_rhs + ), ) series_by_data_path = { data_path: result.series[data_path] @@ -923,6 +930,9 @@ def run_test_mql_pnvo_event_boundary_diagnostic( "use_pneumatic_96_reference_rhs": ( config.execution.use_pneumatic_96_reference_rhs ), + "use_pneumatic_69_reference_rhs": ( + config.execution.use_pneumatic_69_reference_rhs + ), } solution = system.simulate_full_state_from_spec( spec, @@ -3290,6 +3300,9 @@ def run_test_mql_pnvo_event_window_diagnostic( "use_pneumatic_96_reference_rhs": ( config.execution.use_pneumatic_96_reference_rhs ), + "use_pneumatic_69_reference_rhs": ( + config.execution.use_pneumatic_69_reference_rhs + ), } closure = system.full_state_closure_from_spec(spec, **closure_kwargs) state_vector = closure.initial_state_vector() @@ -4550,15 +4563,25 @@ def main() -> None: action="store_true", help="enable the local reference/mass-offset RHS candidate for pneumatic_96", ) + parser.add_argument( + "--pneumatic-69-reference-rhs", + action="store_true", + help="enable the local reference/mass-offset RHS candidate for pneumatic_69", + ) args = parser.parse_args() config = None - if args.amesim_results_archive is not None or args.pneumatic_96_reference_rhs: + if ( + args.amesim_results_archive is not None + or args.pneumatic_96_reference_rhs + or args.pneumatic_69_reference_rhs + ): config = TestMqlFullStateComparisonScriptConfig( paths=TestMqlFullStateComparisonPathConfig( amesim_results_archive_path=args.amesim_results_archive, ), execution=TestMqlFullStateComparisonExecutionConfig( use_pneumatic_96_reference_rhs=args.pneumatic_96_reference_rhs, + use_pneumatic_69_reference_rhs=args.pneumatic_69_reference_rhs, ), ) if args.pnvo_event_boundary: diff --git a/PythonModels/systems/test_mql.py b/PythonModels/systems/test_mql.py index 506fc4c..1bd736b 100644 --- a/PythonModels/systems/test_mql.py +++ b/PythonModels/systems/test_mql.py @@ -4803,6 +4803,7 @@ class TestMqlSystem: inlet_node_temperature_k: float = 293.15, resistance_boundary_temperature_k: float = 293.15, use_pneumatic_96_reference_rhs: bool = False, + use_pneumatic_69_reference_rhs: bool = False, ) -> TestMqlFullStateClosure: return TestMqlFullStateClosure( pneumatic_closure=self.pn3_p4_node_chamber_segment_closure_from_spec( @@ -4812,6 +4813,9 @@ class TestMqlSystem: inlet_node_temperature_k=inlet_node_temperature_k, resistance_boundary_temperature_k=resistance_boundary_temperature_k, use_inlet_line_reference_rhs=use_pneumatic_96_reference_rhs, + use_p4_port3_remote_primary_reference_rhs=( + use_pneumatic_69_reference_rhs + ), ), mechanical_closure=self.mechanical_mass_closure(), pneumatic_assembly=self.pneumatic_assembly, @@ -4826,6 +4830,7 @@ class TestMqlSystem: inlet_node_temperature_k: float = 293.15, resistance_boundary_temperature_k: float = 293.15, use_pneumatic_96_reference_rhs: bool = False, + use_pneumatic_69_reference_rhs: bool = False, config: SolveIVPConfig | None = None, t_eval: list[float] | None = None, ): @@ -4836,6 +4841,7 @@ class TestMqlSystem: inlet_node_temperature_k=inlet_node_temperature_k, resistance_boundary_temperature_k=resistance_boundary_temperature_k, use_pneumatic_96_reference_rhs=use_pneumatic_96_reference_rhs, + use_pneumatic_69_reference_rhs=use_pneumatic_69_reference_rhs, ) return integrate_ode( rhs=lambda t, state: closure.rhs_at(t, state), @@ -4856,6 +4862,7 @@ class TestMqlSystem: t_eval: list[float] | None = None, data_paths: tuple[str, ...] | list[str] | None = None, use_pneumatic_96_reference_rhs: bool = False, + use_pneumatic_69_reference_rhs: bool = False, ) -> TestMqlSimulationResult: closure = self.full_state_closure_from_spec( spec, @@ -4864,6 +4871,7 @@ class TestMqlSystem: inlet_node_temperature_k=inlet_node_temperature_k, resistance_boundary_temperature_k=resistance_boundary_temperature_k, use_pneumatic_96_reference_rhs=use_pneumatic_96_reference_rhs, + use_pneumatic_69_reference_rhs=use_pneumatic_69_reference_rhs, ) solution = integrate_ode( rhs=lambda t, state: closure.rhs_at(t, state), @@ -5336,6 +5344,7 @@ class TestMqlSystem: inlet_node_temperature_k: float = 293.15, resistance_boundary_temperature_k: float = 293.15, use_inlet_line_reference_rhs: bool = False, + use_p4_port3_remote_primary_reference_rhs: bool = False, ): """Close a PN3 chamber segment through the adjacent real P4 node. @@ -6082,6 +6091,9 @@ class TestMqlSystem: temperature_k=resistance_boundary_temperature_k, ), use_inlet_line_reference_rhs=use_inlet_line_reference_rhs, + use_p4_port3_remote_primary_reference_rhs=( + use_p4_port3_remote_primary_reference_rhs + ), ) def simulate_pn3_p4_node_chamber_segment_from_spec( diff --git a/PythonModels/systems/test_mql_closure.py b/PythonModels/systems/test_mql_closure.py index 9a5cdca..984d9f7 100644 --- a/PythonModels/systems/test_mql_closure.py +++ b/PythonModels/systems/test_mql_closure.py @@ -491,11 +491,15 @@ class TestMqlPn3P4NodeChamberSegmentClosure: inlet_node: TestMqlPneumaticBoundaryCondition, resistance_boundary: TestMqlPneumaticBoundaryCondition, use_inlet_line_reference_rhs: bool = False, + use_p4_port3_remote_primary_reference_rhs: bool = False, ) -> None: self.components = components self.inlet_node = inlet_node self.resistance_boundary = resistance_boundary self.use_inlet_line_reference_rhs = use_inlet_line_reference_rhs + self.use_p4_port3_remote_primary_reference_rhs = ( + use_p4_port3_remote_primary_reference_rhs + ) def initial_state_vector(self) -> list[float]: return [ @@ -1937,6 +1941,69 @@ class TestMqlPn3P4NodeChamberSegmentClosure: return fallback_h return balance.port_2_enthalpy_flow_w / mass_flow_kg_s + @staticmethod + def _reference_enthalpy_flow_for_node_port( + *, + gas, + node_properties: ThermodynamicProperties, + connected_properties: ThermodynamicProperties, + flow_kg_s: float, + port_mass_flow_kg_s: float, + reference_temperature_k: float = 298.15, + ) -> float: + stream_properties = ( + node_properties if flow_kg_s >= 0.0 else connected_properties + ) + return port_mass_flow_kg_s * gas.pressure_reference_enthalpy( + stream_properties.p, + stream_properties.T, + reference_temperature=reference_temperature_k, + ) + + def _p4_port3_remote_port_2_reference_h( + self, + *, + snapshot: TestMqlPn3P4NodeChamberSegmentSnapshot, + reference_temperature_k: float = 298.15, + ) -> float: + gas = self.components.p4_port3_remote_primary_line.gas + port_2_mass_flow = snapshot.p4_port3_remote_balance.port_2_mass_flow_g_s * 1.0e-3 + if abs(port_2_mass_flow) <= 1.0e-12: + return gas.pressure_reference_enthalpy( + snapshot.p4_port3_remote_primary_line.p, + snapshot.p4_port3_remote_primary_line.T, + reference_temperature=reference_temperature_k, + ) + port_2_enthalpy_flow = sum( + ( + self._reference_enthalpy_flow_for_node_port( + gas=gas, + node_properties=snapshot.p4_port3_remote_primary_line, + connected_properties=snapshot.p4_port3_line, + flow_kg_s=snapshot.p4_port3_remote_node_to_line_flow, + port_mass_flow_kg_s=-snapshot.p4_port3_remote_node_to_line_flow, + reference_temperature_k=reference_temperature_k, + ), + self._reference_enthalpy_flow_for_node_port( + gas=gas, + node_properties=snapshot.p4_port3_remote_primary_line, + connected_properties=snapshot.p4_port3_remote_port3_line, + flow_kg_s=snapshot.p4_port3_remote_to_port3_line_flow, + port_mass_flow_kg_s=-snapshot.p4_port3_remote_to_port3_line_flow, + reference_temperature_k=reference_temperature_k, + ), + self._reference_enthalpy_flow_for_node_port( + gas=gas, + node_properties=snapshot.p4_port3_remote_orifice_line_port_2, + connected_properties=snapshot.p4_port3_remote_primary_line, + flow_kg_s=snapshot.p4_port3_remote_orifice_to_node_flow, + port_mass_flow_kg_s=snapshot.p4_port3_remote_orifice_to_node_flow, + reference_temperature_k=reference_temperature_k, + ), + ) + ) + return port_2_enthalpy_flow / port_2_mass_flow + def _p4_node_balance( self, *, @@ -2097,25 +2164,42 @@ class TestMqlPn3P4NodeChamberSegmentClosure: port_1_flow: float, port_2_properties: ThermodynamicProperties, port_2_flow: float, + port_1_connected_reference_h: float | None = None, + port_2_connected_reference_h: float | None = None, reference_temperature_k: float = 298.15, ) -> VolumeState: - def reference_enthalpy_flow( + def specific_reference_h( *, port_flow: float, connected: ThermodynamicProperties, + connected_reference_h: float | None, ) -> float: + if port_flow > 0.0 and connected_reference_h is not None: + return connected_reference_h if port_flow > 0.0: pressure = connected.p temperature = connected.T else: pressure = line_properties.p temperature = line_properties.T - return port_flow * line.gas.pressure_reference_enthalpy( + return line.gas.pressure_reference_enthalpy( pressure, temperature, reference_temperature=reference_temperature_k, ) + def reference_enthalpy_flow( + *, + port_flow: float, + connected: ThermodynamicProperties, + connected_reference_h: float | None, + ) -> float: + return port_flow * specific_reference_h( + port_flow=port_flow, + connected=connected, + connected_reference_h=connected_reference_h, + ) + mass_derivative = port_1_flow + port_2_flow heat_flow = ( line.heat_transfer_coefficient @@ -2125,9 +2209,11 @@ class TestMqlPn3P4NodeChamberSegmentClosure: enthalpy_flow = reference_enthalpy_flow( port_flow=port_1_flow, connected=port_1_properties, + connected_reference_h=port_1_connected_reference_h, ) + reference_enthalpy_flow( port_flow=port_2_flow, connected=port_2_properties, + connected_reference_h=port_2_connected_reference_h, ) reference_offset_flow = ( line.gas.cp * reference_temperature_k - line.gas.cv * line_properties.T @@ -2880,6 +2966,10 @@ class TestMqlPn3P4NodeChamberSegmentClosure: port_2_properties=snapshot.p4_primary_line, port_2_flow=snapshot.p4_to_port3_line_flow, ) + p4_port3_remote_node_connected_h = self._p4_node_port_2_connected_h( + balance=snapshot.p4_port3_remote_balance, + fallback_h=snapshot.p4_port3_remote_primary_line.h, + ) ( p4_port3_remote_primary_derivative, p4_port3_remote_chamber_derivative, @@ -2890,11 +2980,24 @@ class TestMqlPn3P4NodeChamberSegmentClosure: chamber_properties=snapshot.p4_port3_remote_primary_chamber, chamber_to_line_flow=snapshot.p4_port3_remote_chamber_to_line_flow, node_to_line_flow=snapshot.p4_port3_remote_node_to_primary_line_flow, - node_connected_h=self._p4_node_port_2_connected_h( - balance=snapshot.p4_port3_remote_balance, - fallback_h=snapshot.p4_port3_remote_primary_line.h, - ), + node_connected_h=p4_port3_remote_node_connected_h, ) + if self.use_p4_port3_remote_primary_reference_rhs: + p4_port3_remote_primary_derivative = ( + self._pnl0001_reference_enthalpy_derivative( + line=self.components.p4_port3_remote_primary_line, + line_properties=snapshot.p4_port3_remote_primary_line, + port_1_properties=snapshot.p4_port3_remote_primary_chamber, + port_1_flow=snapshot.p4_port3_remote_chamber_to_line_flow, + port_2_properties=snapshot.p4_port3_remote_primary_line, + port_2_flow=snapshot.p4_port3_remote_node_to_primary_line_flow, + port_2_connected_reference_h=( + self._p4_port3_remote_port_2_reference_h( + snapshot=snapshot + ) + ), + ) + ) p4_port3_remote_port3_derivative = self._connection_line_derivative( line=self.components.p4_port3_remote_port3_line, port_1_properties=snapshot.p4_port3_far_primary_line, diff --git a/tests/test_test_mql_pnl0001_segment.py b/tests/test_test_mql_pnl0001_segment.py index 75ff8cd..48376f3 100644 --- a/tests/test_test_mql_pnl0001_segment.py +++ b/tests/test_test_mql_pnl0001_segment.py @@ -778,6 +778,47 @@ class TestMqlPn3P4NodeChamberSegmentTests(unittest.TestCase): self.assertAlmostEqual(candidate_rhs[1], expected.U) self.assertNotAlmostEqual(candidate_rhs[1], default_rhs[1]) + def test_full_state_closure_can_enable_pneumatic_69_reference_rhs_candidate( + self, + ) -> None: + default_closure = self.system.full_state_closure_from_spec( + self.spec, + inlet_node_pressure_pa=15.31e6, + resistance_boundary_pressure_pa=15.29e6, + ) + candidate_closure = self.system.full_state_closure_from_spec( + self.spec, + inlet_node_pressure_pa=15.31e6, + resistance_boundary_pressure_pa=15.29e6, + use_pneumatic_69_reference_rhs=True, + ) + state = default_closure.initial_state_vector() + + default_rhs = default_closure.rhs_at(0.04, state) + candidate_rhs = candidate_closure.rhs_at(0.04, state) + snapshot = candidate_closure.snapshot_at(0.04, state).pneumatic + line = candidate_closure.pneumatic_closure.components.p4_port3_remote_primary_line + expected = candidate_closure.pneumatic_closure._pnl0001_reference_enthalpy_derivative( + line=line, + line_properties=snapshot.p4_port3_remote_primary_line, + port_1_properties=snapshot.p4_port3_remote_primary_chamber, + port_1_flow=snapshot.p4_port3_remote_chamber_to_line_flow, + port_2_properties=snapshot.p4_port3_remote_primary_line, + port_2_flow=snapshot.p4_port3_remote_node_to_primary_line_flow, + port_2_connected_reference_h=( + candidate_closure.pneumatic_closure._p4_port3_remote_port_2_reference_h( + snapshot=snapshot + ) + ), + ) + + self.assertAlmostEqual(candidate_rhs[0], default_rhs[0]) + self.assertAlmostEqual(candidate_rhs[1], default_rhs[1]) + self.assertAlmostEqual(candidate_rhs[44], default_rhs[44]) + self.assertAlmostEqual(candidate_rhs[44], expected.m) + self.assertAlmostEqual(candidate_rhs[45], expected.U) + self.assertNotAlmostEqual(candidate_rhs[45], default_rhs[45]) + def test_full_state_closure_applies_step_controls_to_variable_orifices(self) -> None: closure = self.system.full_state_closure_from_spec( self.spec,