from __future__ import annotations import unittest from PythonModels.core.solver import SolveIVPConfig from PythonModels.systems.test_mql import TestMqlSystem class TestMqlPnl0001ChamberSegmentTests(unittest.TestCase): def setUp(self) -> None: self.system = TestMqlSystem() self.spec = self.system.discover_pneumatic_branch_topology().chamber_segment_specs[0] def test_system_assembles_all_twenty_physical_pnl0001_lines(self) -> None: self.assertEqual(self.system.typed_pnl0001_line_count, 20) self.assertIn("pneumatic_96", self.system.pnl0001_assembly.lines) def test_closure_inserts_topology_derived_inlet_line(self) -> None: closure = self.system.pnl0001_chamber_segment_closure_from_spec( self.spec, inlet_node_pressure_pa=16.0e6, outlet_pressure_pa=15.3e6, ) snapshot = closure.snapshot() rhs = closure.rhs(closure.initial_state_vector()) self.assertEqual(closure.components.inlet_line.name, "pneumatic_96") self.assertEqual(len(closure.initial_state_vector()), 4) self.assertEqual(len(rhs), 4) self.assertGreater(snapshot.node_to_line_flow, 0.0) self.assertAlmostEqual(snapshot.line_to_chamber_flow, 0.0, delta=1.0e-7) self.assertAlmostEqual(snapshot.outlet_flow, 0.0, delta=1.0e-7) self.assertAlmostEqual(rhs[0] + rhs[2], snapshot.node_to_line_flow) def test_internal_line_orifice_flow_cancels_from_total_mass_balance(self) -> None: closure = self.system.pnl0001_chamber_segment_closure_from_spec( self.spec, inlet_node_pressure_pa=15.3e6, outlet_pressure_pa=14.0e6, ) state = closure.initial_state_vector() state[0] *= 1.01 state[1] *= 1.01 snapshot = closure.snapshot(state) rhs = closure.rhs(state) self.assertGreater(snapshot.line_to_chamber_flow, 0.0) self.assertAlmostEqual( rhs[0] + rhs[2], snapshot.node_to_line_flow - snapshot.outlet_flow, delta=1.0e-12, ) self.assertAlmostEqual( closure.components.inlet_line.port_2.m_flow, -snapshot.line_to_chamber_flow, ) def test_simulates_four_state_line_chamber_segment(self) -> None: solution = self.system.simulate_pnl0001_chamber_segment_from_spec( self.spec, inlet_node_pressure_pa=15.31e6, outlet_pressure_pa=15.29e6, config=SolveIVPConfig( t_start=0.0, t_stop=1.0e-7, max_step=1.0e-8, ), t_eval=[0.0, 5.0e-8, 1.0e-7], ) self.assertTrue(solution.success) self.assertEqual(len(solution.t), 3) self.assertEqual(len(solution.y), 4) self.assertEqual([len(row) for row in solution.y], [3, 3, 3, 3]) class TestMqlPnl0001PairChamberSegmentTests(unittest.TestCase): def setUp(self) -> None: self.system = TestMqlSystem() self.spec = self.system.discover_pneumatic_branch_topology().chamber_segment_specs[0] def test_pair_closure_uses_both_topology_derived_lines(self) -> None: closure = self.system.pnl0001_pair_chamber_segment_closure_from_spec( self.spec, inlet_node_pressure_pa=16.0e6, outlet_node_pressure_pa=14.0e6, ) snapshot = closure.snapshot() rhs = closure.rhs(closure.initial_state_vector()) self.assertEqual(closure.components.inlet_line.name, "pneumatic_96") self.assertEqual(closure.components.outlet_line.name, "pneumatic_97") self.assertEqual(len(closure.initial_state_vector()), 6) self.assertEqual(len(rhs), 6) self.assertGreater(snapshot.inlet_node_to_line_flow, 0.0) self.assertLess(snapshot.outlet_node_to_line_flow, 0.0) self.assertAlmostEqual( rhs[0] + rhs[2] + rhs[4], snapshot.inlet_node_to_line_flow + snapshot.outlet_node_to_line_flow, delta=1.0e-12, ) def test_both_internal_orifice_flows_cancel_from_total_mass(self) -> None: closure = self.system.pnl0001_pair_chamber_segment_closure_from_spec( self.spec, inlet_node_pressure_pa=15.3e6, outlet_node_pressure_pa=15.3e6, ) state = closure.initial_state_vector() state[0] *= 1.01 state[1] *= 1.01 state[4] *= 0.99 state[5] *= 0.99 snapshot = closure.snapshot(state) rhs = closure.rhs(state) self.assertGreater(snapshot.inlet_line_to_chamber_flow, 0.0) self.assertLess(snapshot.outlet_line_to_chamber_flow, 0.0) self.assertAlmostEqual( rhs[0] + rhs[2] + rhs[4], snapshot.inlet_node_to_line_flow + snapshot.outlet_node_to_line_flow, delta=1.0e-12, ) self.assertAlmostEqual( closure.components.outlet_line.port_2.m_flow, -snapshot.outlet_line_to_chamber_flow, ) def test_simulates_six_state_pair_line_chamber_segment(self) -> None: solution = self.system.simulate_pnl0001_pair_chamber_segment_from_spec( self.spec, inlet_node_pressure_pa=15.31e6, outlet_node_pressure_pa=15.29e6, config=SolveIVPConfig( t_start=0.0, t_stop=1.0e-7, max_step=1.0e-8, ), t_eval=[0.0, 5.0e-8, 1.0e-7], ) self.assertTrue(solution.success) self.assertEqual(len(solution.t), 3) self.assertEqual(len(solution.y), 6) self.assertEqual([len(row) for row in solution.y], [3, 3, 3, 3, 3, 3]) class TestMqlPn3NodeChamberSegmentTests(unittest.TestCase): def setUp(self) -> None: self.system = TestMqlSystem() self.spec = self.system.discover_pneumatic_branch_topology().chamber_segment_specs[0] def test_closure_replaces_outlet_boundary_with_real_pn3_node(self) -> None: closure = self.system.pn3_node_chamber_segment_closure_from_spec( self.spec, inlet_node_pressure_pa=15.31e6, resistance_boundary_pressure_pa=15.29e6, ) snapshot = closure.snapshot() rhs = closure.rhs(closure.initial_state_vector()) self.assertEqual(closure.components.inlet_line.name, "pneumatic_96") self.assertEqual(closure.components.outlet_line.name, "pneumatic_97") self.assertEqual(closure.components.node_line.name, "pneumatic_88") self.assertEqual(closure.components.node_resistance.name, "pneumatic_100") self.assertEqual(closure.components.node_orifice.name, "pn_morifice_9") self.assertEqual(closure.components.node.alias, "pn_node3_9") self.assertEqual(len(closure.initial_state_vector()), 10) self.assertEqual(len(rhs), 10) self.assertAlmostEqual( snapshot.node_balance.pressure_pa, snapshot.node_line_port_1.p, ) self.assertAlmostEqual( snapshot.node_balance.temperature_k, snapshot.node_line_port_1.T, ) def test_pn3_node_flow_cancels_between_adjacent_lines(self) -> None: closure = self.system.pn3_node_chamber_segment_closure_from_spec( self.spec, inlet_node_pressure_pa=15.3e6, resistance_boundary_pressure_pa=15.0e6, p4_boundary_pressure_pa=15.0e6, ) state = closure.initial_state_vector() state[4] *= 0.99 state[5] *= 0.99 snapshot = closure.snapshot(state) rhs = closure.rhs(state) self.assertGreater(snapshot.outlet_node_to_line_flow, 0.0) self.assertGreater(snapshot.node_to_resistance_flow, 0.0) self.assertAlmostEqual( snapshot.node_balance.port_2_mass_flow_g_s, (snapshot.outlet_node_to_line_flow + snapshot.node_to_resistance_flow) * 1.0e3, ) self.assertAlmostEqual( snapshot.node_to_pnl0003_flow, -(snapshot.outlet_node_to_line_flow + snapshot.node_to_resistance_flow), ) self.assertGreater(snapshot.pnl0003_to_p4_flow, 0.0) self.assertAlmostEqual( closure.components.node_line.port_1.m_flow, snapshot.node_to_pnl0003_flow, ) self.assertAlmostEqual( closure.components.node_line.port_2.m_flow, -snapshot.pnl0003_to_p4_flow, ) mass_derivative_sum = rhs[0] + rhs[2] + rhs[4] + rhs[6] + rhs[8] self.assertAlmostEqual( mass_derivative_sum, ( snapshot.inlet_node_to_line_flow - snapshot.pnl0003_to_p4_flow - snapshot.node_to_resistance_flow ), delta=1.0e-12, ) def test_simulates_ten_state_pn3_node_chamber_segment(self) -> None: solution = self.system.simulate_pn3_node_chamber_segment_from_spec( self.spec, inlet_node_pressure_pa=15.31e6, resistance_boundary_pressure_pa=15.29e6, p4_boundary_pressure_pa=15.29e6, config=SolveIVPConfig( t_start=0.0, t_stop=1.0e-8, max_step=1.0e-9, ), t_eval=[0.0, 5.0e-9, 1.0e-8], ) self.assertTrue(solution.success) self.assertEqual(len(solution.t), 3) self.assertEqual(len(solution.y), 10) self.assertEqual([len(row) for row in solution.y], [3] * 10) if __name__ == "__main__": unittest.main()