diff --git a/PythonModels/components/amesim_pneumatic_line.py b/PythonModels/components/amesim_pneumatic_line.py index bcd4b16..7810d0d 100644 --- a/PythonModels/components/amesim_pneumatic_line.py +++ b/PythonModels/components/amesim_pneumatic_line.py @@ -343,10 +343,10 @@ class AmesimPnl0001Pipe(_DarcyPipeResistanceMixin, DynamicComponent): connected_h_2: float, ) -> VolumeState: internal = self.properties() - # PNL0001 is a fixed-volume distributed line store. Its transported - # energy variable therefore follows specific internal energy, not the - # chamber-style stagnation enthalpy contract. For this ideal gas, - # h = gamma * u. Outflow always carries the local u. + # Default first-pass PNL0001 behavior uses the historical internal-energy + # approximation. AMESim-specific transport-enthalpy corrections are kept + # behind derivatives_from_transport_enthalpy_connections so they can be + # applied only where validated against baseline data. inlet_u_1 = ( connected_h_1 / self.gas.gamma if port_1_m_flow > 0.0 @@ -367,6 +367,27 @@ class AmesimPnl0001Pipe(_DarcyPipeResistanceMixin, DynamicComponent): U=port_1_m_flow * inlet_u_1 + port_2_m_flow * inlet_u_2 + heat_flow, ) + def derivatives_from_transport_enthalpy_connections( + self, + *, + port_1_m_flow: float, + connected_h_1: float, + port_2_m_flow: float, + connected_h_2: float, + ) -> VolumeState: + internal = self.properties() + inlet_h_1 = connected_h_1 if port_1_m_flow > 0.0 else internal.h + inlet_h_2 = connected_h_2 if port_2_m_flow > 0.0 else internal.h + heat_flow = ( + self.heat_transfer_coefficient + * self.heat_transfer_area + * (self.external_temperature - internal.T) + ) + return VolumeState( + m=port_1_m_flow + port_2_m_flow, + U=port_1_m_flow * inlet_h_1 + port_2_m_flow * inlet_h_2 + heat_flow, + ) + class AmesimPnl0003Pipe(_DarcyPipeResistanceMixin, DynamicComponent): """First-pass AMESim ``PNL0003`` (C-R-C) pipe. diff --git a/PythonModels/scripts/run_test_mql_full_state_comparison.py b/PythonModels/scripts/run_test_mql_full_state_comparison.py index fdc7cda..93bef13 100644 --- a/PythonModels/scripts/run_test_mql_full_state_comparison.py +++ b/PythonModels/scripts/run_test_mql_full_state_comparison.py @@ -1056,14 +1056,14 @@ def _pnl0001_energy_flow_diagnostic( snapshot.p4_port3_remote_balance.port_2_enthalpy_flow_w / p4_node_port_2_mass_flow_kg_s ) - p4_node_port_2_inlet_u = ( - p4_node_port_2_connected_h / line.gas.gamma + p4_node_port_2_inlet_h = ( + p4_node_port_2_connected_h if rhs_diagnostic.node_to_line_flow_kg_s > 0.0 - else line_properties.u + else line_properties.h ) p4_node_port_2_counterfactual_energy_derivative = ( rhs_diagnostic.port_1_energy_flow_w - + rhs_diagnostic.node_to_line_flow_kg_s * p4_node_port_2_inlet_u + + rhs_diagnostic.node_to_line_flow_kg_s * p4_node_port_2_inlet_h + rhs_diagnostic.thermal_energy_flow_w ) p4_node_port_2_counterfactual_dtemp = ( diff --git a/PythonModels/systems/test_mql.py b/PythonModels/systems/test_mql.py index 6061e07..258d4e7 100644 --- a/PythonModels/systems/test_mql.py +++ b/PythonModels/systems/test_mql.py @@ -4190,21 +4190,24 @@ class TestMqlFullStateClosure: chamber_properties = getattr(snapshot.pneumatic, chamber_field) chamber_to_line_flow = getattr(snapshot.pneumatic, chamber_flow_field) node_to_line_flow = getattr(snapshot.pneumatic, node_flow_field) - line_derivative = line.derivatives_from_connections( + p4_balance = snapshot.pneumatic.p4_port3_remote_balance + p4_port_2_mass_flow_kg_s = p4_balance.port_2_mass_flow_g_s * 1.0e-3 + node_connected_h = ( + line_properties.h + if abs(p4_port_2_mass_flow_kg_s) <= 1.0e-12 + else p4_balance.port_2_enthalpy_flow_w / p4_port_2_mass_flow_kg_s + ) + line_derivative = line.derivatives_from_transport_enthalpy_connections( port_1_m_flow=chamber_to_line_flow, connected_h_1=chamber_properties.h, port_2_m_flow=node_to_line_flow, - connected_h_2=line_properties.h, + connected_h_2=node_connected_h, ) - port_1_inlet_u = ( - chamber_properties.h / line.gas.gamma - if chamber_to_line_flow > 0.0 - else line_properties.u + port_1_inlet_h = ( + chamber_properties.h if chamber_to_line_flow > 0.0 else line_properties.h ) - port_2_inlet_u = ( - line_properties.h / line.gas.gamma - if node_to_line_flow > 0.0 - else line_properties.u + port_2_inlet_h = ( + node_connected_h if node_to_line_flow > 0.0 else line_properties.h ) thermal_energy_flow = ( line.heat_transfer_coefficient @@ -4221,8 +4224,8 @@ class TestMqlFullStateClosure: chamber_to_line_flow_kg_s=chamber_to_line_flow, node_to_line_flow_kg_s=node_to_line_flow, mass_derivative_kg_s=line_derivative.m, - port_1_energy_flow_w=chamber_to_line_flow * port_1_inlet_u, - port_2_energy_flow_w=node_to_line_flow * port_2_inlet_u, + port_1_energy_flow_w=chamber_to_line_flow * port_1_inlet_h, + port_2_energy_flow_w=node_to_line_flow * port_2_inlet_h, thermal_energy_flow_w=thermal_energy_flow, energy_derivative_w=line_derivative.U, ) diff --git a/PythonModels/systems/test_mql_closure.py b/PythonModels/systems/test_mql_closure.py index 4aecbae..7347d43 100644 --- a/PythonModels/systems/test_mql_closure.py +++ b/PythonModels/systems/test_mql_closure.py @@ -1913,6 +1913,17 @@ class TestMqlPn3P4NodeChamberSegmentClosure: port_3_mass_flow_g_s=port_3.port_mass_flow_kg_s * 1.0e3, ) + @staticmethod + def _p4_node_port_2_connected_h( + *, + balance: TestMqlPneumaticNode4Balance, + fallback_h: float, + ) -> float: + mass_flow_kg_s = balance.port_2_mass_flow_g_s * 1.0e-3 + if abs(mass_flow_kg_s) <= 1.0e-12: + return fallback_h + return balance.port_2_enthalpy_flow_w / mass_flow_kg_s + def _p4_node_balance( self, *, @@ -2073,12 +2084,20 @@ class TestMqlPn3P4NodeChamberSegmentClosure: chamber_properties: ThermodynamicProperties, chamber_to_line_flow: float, node_to_line_flow: float, + node_connected_h: float | None = None, ) -> tuple[VolumeState, VolumeState]: - line_derivative = line.derivatives_from_connections( + derivative_method = ( + line.derivatives_from_connections + if node_connected_h is None + else line.derivatives_from_transport_enthalpy_connections + ) + line_derivative = derivative_method( port_1_m_flow=chamber_to_line_flow, connected_h_1=chamber_properties.h, port_2_m_flow=node_to_line_flow, - connected_h_2=line_properties.h, + connected_h_2=( + line_properties.h if node_connected_h is None else node_connected_h + ), ) chamber_derivative = chamber.derivatives_from_two_connections( port_a_m_flow=-chamber_to_line_flow, @@ -2791,6 +2810,10 @@ 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, + ), ) p4_port3_remote_port3_derivative = self._connection_line_derivative( line=self.components.p4_port3_remote_port3_line, diff --git a/tests/test_amesim_pnl0001_pipe.py b/tests/test_amesim_pnl0001_pipe.py index dc79fc4..0818eb6 100644 --- a/tests/test_amesim_pnl0001_pipe.py +++ b/tests/test_amesim_pnl0001_pipe.py @@ -115,6 +115,29 @@ class AmesimPnl0001PipeTests(unittest.TestCase): - 0.1 * internal.u, ) + def test_transport_enthalpy_derivative_preserves_pn2vol_energy_basis(self) -> None: + internal = self.pipe.properties() + derivative = self.pipe.derivatives_from_transport_enthalpy_connections( + port_1_m_flow=0.2, + connected_h_1=internal.h + 1000.0, + port_2_m_flow=-0.1, + connected_h_2=internal.h - 1000.0, + ) + + self.assertAlmostEqual(derivative.m, 0.1) + self.assertAlmostEqual( + derivative.U, + 0.2 * (internal.h + 1000.0) - 0.1 * internal.h, + ) + temperature_derivative = ( + derivative.U - internal.u * derivative.m + ) / (self.pipe.state.m * self.pipe.gas.cv) + expected_temperature_derivative = ( + 0.2 * (internal.h + 1000.0 - internal.u) + - 0.1 * (internal.h - internal.u) + ) / (self.pipe.state.m * self.pipe.gas.cv) + self.assertAlmostEqual(temperature_derivative, expected_temperature_derivative) + if __name__ == "__main__": unittest.main() diff --git a/tests/test_test_mql_pnl0001_segment.py b/tests/test_test_mql_pnl0001_segment.py index 232cc83..f8ba4b8 100644 --- a/tests/test_test_mql_pnl0001_segment.py +++ b/tests/test_test_mql_pnl0001_segment.py @@ -773,7 +773,8 @@ class TestMqlPn3P4NodeChamberSegmentTests(unittest.TestCase): ) rhs = closure.rhs_at(0.04, state) line = closure.pneumatic_closure.components.p4_port3_remote_primary_line - expected_line_derivative = line.derivatives_from_connections( + expected_line_derivative = ( + line.derivatives_from_transport_enthalpy_connections( port_1_m_flow=( open_snapshot.p4_port3_remote_chamber_to_line_flow ), @@ -781,7 +782,8 @@ class TestMqlPn3P4NodeChamberSegmentTests(unittest.TestCase): port_2_m_flow=( open_snapshot.p4_port3_remote_node_to_primary_line_flow ), - connected_h_2=node_inlet_h, + connected_h_2=node_inlet_h, + ) ) self.assertAlmostEqual(rhs[45], expected_line_derivative.U) self.assertAlmostEqual(