from __future__ import annotations import unittest from pathlib import Path from PythonModels.core.solver import SolveIVPConfig from PythonModels.reporting.amesim_results import load_test_mql_amesim_results from PythonModels.reporting.test_mql_output_schema import build_test_mql_output_schema from PythonModels.reporting.test_mql_output_validation import compare_validated_test_mql_output from PythonModels.systems.test_mql import TestMqlSystem REPO_ROOT = Path(__file__).resolve().parents[1] TEST_MQL_AME = REPO_ROOT / "AmesimModels" / "test_mql.ame" 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 self.assertAlmostEqual(closure.components.node_orifice.opening, 0.0) closure.components.node_orifice.opening = 1.0 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) class TestMqlPn3P4NodeChamberSegmentTests(unittest.TestCase): @classmethod def setUpClass(cls) -> None: cls.amesim_results = load_test_mql_amesim_results(TEST_MQL_AME) cls.output_schema = build_test_mql_output_schema(cls.amesim_results) def setUp(self) -> None: self.system = TestMqlSystem() self.spec = self.system.discover_pneumatic_branch_topology().chamber_segment_specs[0] def test_closure_replaces_p4_pressure_boundary_with_real_node_and_lines(self) -> None: closure = self.system.pn3_p4_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.volume.name, "pn_general_chamber") self.assertEqual(closure.components.outlet_line.name, "pneumatic_97") self.assertEqual(closure.components.inlet_orifice.name, "pn_orifice_18") self.assertEqual(closure.components.outlet_orifice.name, "pn_orifice_19") self.assertEqual(closure.components.node_orifice.name, "pn_morifice_9") self.assertEqual(closure.components.node_resistance.name, "pneumatic_100") self.assertEqual(closure.components.node.alias, "pn_node3_9") self.assertEqual(closure.components.p4_node.alias, "pnnode4_17") self.assertEqual(closure.components.p4_primary_line.name, "pneumatic_68") self.assertEqual(closure.components.p4_primary_chamber.name, "pn_c1_9") self.assertEqual(closure.components.p4_port1_line.name, "pneumatic_86") self.assertEqual(closure.components.p4_port1_remote_node.alias, "pnnode4_18") self.assertEqual( closure.components.p4_port1_remote_primary_line.name, "pneumatic_66", ) self.assertEqual( closure.components.p4_port1_remote_primary_chamber.name, "pn_c1_10", ) self.assertEqual( closure.components.p4_port1_remote_port1_line.name, "pneumatic_80", ) self.assertEqual(closure.components.p4_port1_far_node.alias, "pnnode4_19") self.assertEqual( closure.components.p4_port1_far_primary_line.name, "pneumatic_65", ) self.assertEqual( closure.components.p4_port1_far_primary_chamber.name, "pn_c1_11", ) self.assertEqual(closure.components.p4_port1_far_port1_line.name, "pneumatic_85") self.assertEqual(closure.components.p4_port1_next_node.alias, "pnnode4_23") self.assertEqual( closure.components.p4_port1_next_primary_line.name, "pneumatic_71", ) self.assertEqual( closure.components.p4_port1_next_primary_chamber.name, "pn_c1_12", ) self.assertEqual(closure.components.p4_port1_next_port1_line.name, "pneumatic_81") self.assertEqual(closure.components.p4_bridge_node.alias, "pnnode4_20") self.assertEqual(closure.components.p4_bridge_primary_line.name, "pneumatic_72") self.assertEqual(closure.components.p4_bridge_primary_chamber.name, "pn_c1_13") self.assertEqual( closure.components.p4_port1_remote_orifice.name, "pn_morifice_10", ) self.assertEqual( closure.components.p4_port1_remote_orifice_line.name, "pneumatic_75", ) self.assertEqual( closure.components.p4_port1_remote_orifice_node.alias, "pn_node3_10", ) self.assertEqual(closure.components.p4_port3_line.name, "pneumatic_83") self.assertEqual(closure.components.p4_port3_remote_node.alias, "pnnode4_16") self.assertEqual( closure.components.p4_port3_remote_primary_line.name, "pneumatic_69", ) self.assertEqual( closure.components.p4_port3_remote_primary_chamber.name, "pn_c1_8", ) self.assertEqual( closure.components.p4_port3_remote_port3_line.name, "pneumatic_95", ) self.assertEqual(closure.components.p4_port3_far_node.alias, "pnnode4_22") self.assertEqual( closure.components.p4_port3_far_primary_line.name, "pneumatic_74", ) self.assertEqual( closure.components.p4_port3_far_primary_chamber.name, "pn_c1_15", ) self.assertEqual(closure.components.p4_port3_far_port3_line.name, "pneumatic_82") self.assertEqual(closure.components.p4_port3_next_node.alias, "pnnode4_21") self.assertEqual( closure.components.p4_port3_next_primary_line.name, "pneumatic_73", ) self.assertEqual( closure.components.p4_port3_next_primary_chamber.name, "pn_c1_14", ) self.assertEqual(closure.components.p4_port3_next_port3_line.name, "pneumatic_84") self.assertEqual( closure.components.p4_port3_next_orifice.name, "pn_morifice_13", ) self.assertEqual( closure.components.p4_port3_next_orifice_line.name, "pneumatic_91", ) self.assertEqual( closure.components.p4_port3_next_orifice_node.alias, "pn_node3_14", ) self.assertEqual( closure.components.p4_port3_next_orifice_output_line.name, "pneumatic_93", ) self.assertEqual( closure.components.p4_port3_next_chamber_inlet_line.name, "pneumatic_103", ) self.assertEqual( closure.components.p4_port3_next_chamber.name, "pn_general_chamber_5", ) self.assertEqual( closure.components.p4_port3_next_chamber_outlet_line.name, "pneumatic_104", ) self.assertEqual( closure.components.p4_port3_next_chamber_inlet_orifice.name, "pn_orifice_24", ) self.assertEqual( closure.components.p4_port3_next_chamber_outlet_orifice.name, "pn_orifice_25", ) self.assertEqual( closure.components.p4_port3_remote_orifice.name, "pn_morifice_1", ) self.assertEqual( closure.components.p4_port3_remote_orifice_line.name, "pneumatic_87", ) self.assertEqual( closure.components.p4_port3_remote_orifice_node.alias, "pn_node3_8", ) self.assertEqual( closure.components.p4_port3_remote_orifice_resistance.name, "pneumatic_105", ) self.assertEqual( closure.components.p4_port3_remote_resistance_node.alias, "pn_node3_15", ) self.assertEqual( closure.components.p4_port3_remote_resistance_node_line.name, "pneumatic_92", ) self.assertEqual( closure.components.p4_port3_remote_resistance_orifice.name, "pn_morifice_14", ) self.assertEqual( closure.components.p4_port3_remote_resistance_output_line.name, "pneumatic_94", ) self.assertEqual( closure.components.p4_port3_next_orifice_resistance.name, "pneumatic_106", ) self.assertEqual( closure.components.p4_port3_next_resistance_node.alias, "pn_node3_13", ) self.assertEqual( closure.components.p4_port3_next_resistance_node_line.name, "pneumatic_78", ) self.assertEqual( closure.components.p4_port3_next_resistance_orifice.name, "pn_morifice_12", ) self.assertEqual( closure.components.p4_port3_next_resistance_output_line.name, "pneumatic_70", ) self.assertEqual( closure.components.p4_port3_next_resistance_chamber_inlet_line.name, "pneumatic_101", ) self.assertEqual( closure.components.p4_port3_next_resistance_chamber.name, "pn_general_chamber_4", ) self.assertEqual( closure.components.p4_port3_next_resistance_chamber_outlet_line.name, "pneumatic_102", ) self.assertEqual( closure.components.p4_port3_next_resistance_chamber_inlet_orifice.name, "pn_orifice_22", ) self.assertEqual( closure.components.p4_port3_next_resistance_chamber_outlet_orifice.name, "pn_orifice_23", ) self.assertEqual( closure.components.p4_port1_next_orifice.name, "pn_morifice_11", ) self.assertEqual( closure.components.p4_port1_next_orifice_line.name, "pneumatic_77", ) self.assertEqual( closure.components.p4_port1_next_orifice_node.alias, "pn_node3_12", ) self.assertEqual( closure.components.p4_port1_next_orifice_output_line.name, "pneumatic_79", ) self.assertEqual( closure.components.p4_port1_next_orifice_resistance.name, "pneumatic_107", ) self.assertEqual( closure.components.p4_port1_next_orifice_resistance_node.alias, "pn_node3_11", ) self.assertEqual( closure.components.p4_port1_next_orifice_resistance_line.name, "pneumatic_76", ) self.assertEqual( closure.components.p4_port1_next_orifice_resistance_orifice.name, "pn_morifice_15", ) self.assertEqual( closure.components.p4_port1_next_orifice_resistance_chamber_inlet_line.name, "pneumatic_98", ) self.assertEqual( closure.components.p4_port1_next_orifice_resistance_chamber.name, "pn_general_chamber_2", ) self.assertEqual( closure.components.p4_port1_next_orifice_resistance_chamber_outlet_line.name, "pneumatic_99", ) self.assertEqual( closure.components.p4_port1_next_orifice_resistance_chamber_inlet_orifice.name, "pn_orifice_20", ) self.assertEqual( closure.components.p4_port1_next_orifice_resistance_chamber_outlet_orifice.name, "pn_orifice_21", ) self.assertEqual(len(closure.initial_state_vector()), 112) self.assertEqual(len(rhs), 112) self.assertAlmostEqual( snapshot.p4_balance.pressure_pa, snapshot.p4_primary_line.p, ) self.assertAlmostEqual( snapshot.p4_balance.temperature_k, snapshot.p4_primary_line.T, ) def test_full_state_closure_combines_pneumatic_and_mechanical_states(self) -> None: closure = self.system.full_state_closure_from_spec( self.spec, inlet_node_pressure_pa=15.31e6, resistance_boundary_pressure_pa=15.29e6, ) state = closure.initial_state_vector() snapshot = closure.snapshot(state) rhs = closure.rhs(state) self.assertEqual(closure.pneumatic_state_count, 112) self.assertEqual(closure.mechanical_state_count, 20) self.assertEqual(closure.state_count, 132) self.assertEqual(len(state), 132) self.assertEqual(snapshot.state_count, 132) self.assertEqual(snapshot.mechanical.state_count, 20) self.assertEqual(len(rhs), 132) self.assertAlmostEqual( snapshot.pneumatic.p4_balance.pressure_pa, snapshot.pneumatic.p4_primary_line.p, ) self.assertEqual( snapshot.mechanical.states[0].alias, "mass_friction_endstops_10", ) self.assertAlmostEqual( snapshot.mechanical_node_total_force_by_alias[ "dynamic_mechanical_node_alternative_2" ], 0.0, ) self.assertAlmostEqual( snapshot.mechanical_node_total_force_by_alias[ "dynamic_mechanical_node_alternative_3" ], -326.7174678324367, ) self.assertAlmostEqual(rhs[112], -0.8167936695810917) def test_full_state_closure_feeds_piston_kinematics_to_variable_chambers(self) -> None: closure = self.system.full_state_closure_from_spec( self.spec, inlet_node_pressure_pa=15.31e6, resistance_boundary_pressure_pa=15.29e6, ) state = closure.initial_state_vector() state[113] = 0.05 snapshot = closure.snapshot(state) chamber = closure.pneumatic_closure.components.p4_port3_remote_primary_chamber piston = closure.mechanical_closure.assembly.pistons["pn_brp2_8"] expected_external_volume = piston.geometry().chamber_volume_m3(0.0, 0.05) self.assertAlmostEqual( chamber.volume, chamber.dead_volume + expected_external_volume, ) self.assertLess( snapshot.pneumatic.p4_port3_remote_primary_chamber.p, self.system.pneumatic_assembly.variable_initial_absolute_pressure_pa, ) base_rhs = closure.rhs(closure.initial_state_vector()) moving_state = closure.initial_state_vector() moving_state[112] = 0.1 moving_snapshot = closure.snapshot(moving_state) rhs = closure.rhs(moving_state) expected_volume_rate = piston.geometry().chamber_volume_rate_m3_s(0.0, 0.1) self.assertAlmostEqual(chamber.volume_rate_m3_s(), expected_volume_rate) self.assertAlmostEqual( rhs[47] - base_rhs[47], -moving_snapshot.pneumatic.p4_port3_remote_primary_chamber.p * expected_volume_rate, ) def test_full_state_closure_applies_elastic_endstop_force_to_small_masses(self) -> None: closure = self.system.full_state_closure_from_spec( self.spec, inlet_node_pressure_pa=15.31e6, resistance_boundary_pressure_pa=15.29e6, ) state = closure.initial_state_vector() state[113] = 1.0e-3 snapshot = closure.snapshot(state) rhs = closure.rhs(state) endstop = closure.mechanical_closure.assembly.elastic_endstops[ "elasticendstop_8" ].endstop() expected_contact_force = endstop.contact_force(-1.0, 0.0) mass = closure.mechanical_closure.assembly.masses[ "mass_friction_endstops_10" ].mass_kg expected_piston_force = closure._piston_force_by_mass_alias( snapshot.pneumatic )["mass_friction_endstops_10"] self.assertAlmostEqual( rhs[112], (expected_piston_force - expected_contact_force) / mass, ) self.assertAlmostEqual( snapshot.mechanical_node_total_force_by_alias[ "dynamic_mechanical_node_alternative_2" ], expected_contact_force, ) def test_full_state_closure_constrains_large_masses_at_active_limits(self) -> None: closure = self.system.full_state_closure_from_spec( self.spec, inlet_node_pressure_pa=15.31e6, resistance_boundary_pressure_pa=15.29e6, ) rhs = closure.rhs(closure.initial_state_vector()) self.assertAlmostEqual(rhs[128], 0.0) self.assertAlmostEqual(rhs[129], 0.0) self.assertAlmostEqual(rhs[130], 0.0) self.assertAlmostEqual(rhs[131], 0.0) def test_full_state_closure_releases_large_mass_when_node_force_points_inward(self) -> None: closure = self.system.full_state_closure_from_spec( self.spec, inlet_node_pressure_pa=15.31e6, resistance_boundary_pressure_pa=15.29e6, ) state = closure.initial_state_vector() state[113] = 1.0e-3 rhs = closure.rhs_at(0.8000000000000009, state) endstop = closure.mechanical_closure.assembly.elastic_endstops[ "elasticendstop_8" ].endstop() expected_contact_force = endstop.contact_force(-1.0, 0.0) support_mass = closure.mechanical_closure.assembly.masses[ "mass_friction_endstops_18" ].mass_kg self.assertAlmostEqual(rhs[128], expected_contact_force / support_mass) self.assertAlmostEqual(rhs[129], 0.0) def test_full_state_closure_diagnoses_variable_chamber_rhs_terms(self) -> None: closure = self.system.full_state_closure_from_spec( self.spec, inlet_node_pressure_pa=15.31e6, resistance_boundary_pressure_pa=15.29e6, ) state = closure.initial_state_vector() diagnostic = closure.variable_chamber_rhs_diagnostic( chamber_alias="pn_c1_8", state_vector=state, ) rhs = closure.rhs(state) self.assertEqual(diagnostic.piston_alias, "pn_brp2_8") self.assertAlmostEqual(diagnostic.chamber_volume_m3, 0.015) self.assertAlmostEqual(diagnostic.chamber_volume_rate_m3_s, 0.0) self.assertAlmostEqual(diagnostic.mass_derivative_kg_s, rhs[46]) self.assertAlmostEqual(diagnostic.energy_derivative_w, rhs[47]) self.assertAlmostEqual( diagnostic.energy_derivative_w, diagnostic.port_a_energy_flow_w + diagnostic.port_b_energy_flow_w + diagnostic.boundary_work_w + diagnostic.thermal_energy_flow_w, ) def test_full_state_closure_diagnoses_pnl0001_line_rhs_terms(self) -> None: closure = self.system.full_state_closure_from_spec( self.spec, inlet_node_pressure_pa=15.31e6, resistance_boundary_pressure_pa=15.29e6, ) state = closure.initial_state_vector() diagnostic = closure.pnl0001_line_rhs_diagnostic( line_alias="pneumatic_69", state_vector=state, time_s=0.04, ) rhs = closure.rhs_at(0.04, state) self.assertEqual(diagnostic.chamber_alias, "pn_c1_8") self.assertGreater(diagnostic.node_to_line_flow_kg_s, 0.0) self.assertAlmostEqual( diagnostic.mass_derivative_kg_s, diagnostic.chamber_to_line_flow_kg_s + diagnostic.node_to_line_flow_kg_s, ) self.assertAlmostEqual(diagnostic.mass_derivative_kg_s, rhs[44]) self.assertAlmostEqual(diagnostic.energy_derivative_w, rhs[45]) self.assertAlmostEqual( diagnostic.energy_derivative_w, diagnostic.port_1_energy_flow_w + diagnostic.port_2_energy_flow_w + diagnostic.thermal_energy_flow_w, ) def test_full_state_closure_applies_step_controls_to_variable_orifices(self) -> None: closure = self.system.full_state_closure_from_spec( self.spec, inlet_node_pressure_pa=15.31e6, resistance_boundary_pressure_pa=15.29e6, ) state = closure.initial_state_vector() closed_snapshot = closure.snapshot_at(0.0, state).pneumatic open_snapshot = closure.snapshot_at(0.04, state).pneumatic self.assertAlmostEqual( closed_snapshot.p4_port3_remote_node_to_primary_line_flow, 0.0, ) self.assertGreater( open_snapshot.p4_port3_remote_node_to_primary_line_flow, 0.0, ) node_inlet_h = ( open_snapshot.p4_port3_remote_balance.port_2_enthalpy_flow_w / open_snapshot.p4_port3_remote_node_to_primary_line_flow ) self.assertAlmostEqual( node_inlet_h, open_snapshot.p4_port3_remote_orifice_line_port_2.h, ) rhs = closure.rhs_at(0.04, state) line = closure.pneumatic_closure.components.p4_port3_remote_primary_line expected_line_derivative = ( line.derivatives_from_transport_enthalpy_connections( port_1_m_flow=( open_snapshot.p4_port3_remote_chamber_to_line_flow ), connected_h_1=open_snapshot.p4_port3_remote_primary_chamber.h, port_2_m_flow=( open_snapshot.p4_port3_remote_node_to_primary_line_flow ), connected_h_2=node_inlet_h, ) ) self.assertAlmostEqual(rhs[45], expected_line_derivative.U) self.assertAlmostEqual( self.system.pneumatic_assembly.variable_orifices[ "pn_morifice_1" ].opening, 1.0, ) closure.snapshot_at(0.0, state) self.assertAlmostEqual( self.system.pneumatic_assembly.variable_orifices[ "pn_morifice_1" ].opening, 0.0, ) def test_simulates_full_state_key_data_paths_for_amesim_comparison(self) -> None: data_paths = ( "press@pn_c1_8", "vol@pn_c1_8", "mgas1@pn_c1_8", "vol1@pn_brp2_8", "vvol1@pn_brp2_8", "x1@mass_friction_endstops_10", "v1@mass_friction_endstops_10", "acc1@mass_friction_endstops_10", "x1@mass_friction_endstops_18", "v1@mass_friction_endstops_18", "acc1@mass_friction_endstops_18", "p2@pneumatic_69", "t2@pneumatic_69", "mgas@pneumatic_69", "re@pneumatic_69", "v@pneumatic_69", "ff@pneumatic_69", "dm1@pneumatic_69", "xv@pn_morifice_1", "dm2@pn_morifice_1", ) result = self.system.simulate_full_state_series_from_spec( self.spec, inlet_node_pressure_pa=15.31e6, resistance_boundary_pressure_pa=15.29e6, config=SolveIVPConfig(t_start=0.0, t_stop=0.0), data_paths=data_paths, ) comparison = compare_validated_test_mql_output( times=result.t, series_by_data_path={data_path: result.series[data_path] for data_path in data_paths}, schema=self.output_schema, amesim_results=self.amesim_results, data_paths=data_paths, ) self.assertEqual(result.series["time"], [0.0]) self.assertEqual(len(result.y), 132) self.assertEqual(len(comparison.metrics), len(data_paths)) self.assertAlmostEqual(result.series["press@pn_c1_8"][0], -1300.0) self.assertAlmostEqual(result.series["vol@pn_c1_8"][0], 15000.0) self.assertAlmostEqual( result.series["mgas1@pn_c1_8"][0], self.amesim_results.series("mgas1@pn_c1_8")[0], delta=1.0e-4, ) self.assertAlmostEqual(result.series["vol1@pn_brp2_8"][0], 0.0) self.assertAlmostEqual(result.series["vvol1@pn_brp2_8"][0], 0.0) self.assertAlmostEqual(result.series["x1@mass_friction_endstops_10"][0], 0.0) self.assertAlmostEqual(result.series["v1@mass_friction_endstops_10"][0], 0.0) self.assertAlmostEqual( result.series["acc1@mass_friction_endstops_10"][0], -0.8167936695810917, ) self.assertAlmostEqual(result.series["acc1@mass_friction_endstops_18"][0], 0.0) self.assertAlmostEqual(result.series["p2@pneumatic_69"][0], -1300.0) self.assertAlmostEqual(result.series["t2@pneumatic_69"][0], 293.15) self.assertAlmostEqual( result.series["mgas@pneumatic_69"][0], self.amesim_results.series("mgas@pneumatic_69")[0], delta=1.0e-5, ) self.assertAlmostEqual(result.series["re@pneumatic_69"][0], 0.0) self.assertAlmostEqual(result.series["v@pneumatic_69"][0], 0.0) self.assertGreater(result.series["ff@pneumatic_69"][0], 0.0) self.assertAlmostEqual(result.series["dm1@pneumatic_69"][0], 0.0) self.assertAlmostEqual(result.series["xv@pn_morifice_1"][0], 0.0) self.assertAlmostEqual(result.series["dm2@pn_morifice_1"][0], 0.0) def test_simulates_full_132_state_closure(self) -> None: solution = self.system.simulate_full_state_from_spec( self.spec, inlet_node_pressure_pa=15.31e6, resistance_boundary_pressure_pa=15.29e6, config=SolveIVPConfig(t_start=0.0, t_stop=1.0e-5, max_step=1.0e-6), t_eval=[0.0, 1.0e-5], ) self.assertTrue(solution.success) self.assertEqual(len(solution.t), 2) self.assertEqual(len(solution.y), 132) self.assertEqual([len(row) for row in solution.y], [2] * 132) def test_p4_closure_uses_structured_neighborhood(self) -> None: closure = self.system.pn3_p4_node_chamber_segment_closure_from_spec( self.spec, inlet_node_pressure_pa=15.31e6, resistance_boundary_pressure_pa=15.29e6, ) neighborhood = self.system.p4_node_neighborhood(closure.components.p4_node.alias) self.assertEqual( closure.components.p4_primary_line.name, neighborhood.primary.line_alias, ) self.assertEqual( closure.components.p4_primary_chamber.name, neighborhood.primary.chamber_alias, ) self.assertEqual( closure.components.p4_port1_line.name, neighborhood.port_1.line_alias, ) self.assertEqual( closure.components.p4_port1_remote_node.alias, neighborhood.port_1.remote_node_alias, ) port1_remote_neighborhood = self.system.p4_node_neighborhood( neighborhood.port_1.remote_node_alias ) self.assertEqual( closure.components.p4_port1_remote_port1_line.name, port1_remote_neighborhood.port_1.line_alias, ) self.assertEqual( closure.components.p4_port1_far_node.alias, port1_remote_neighborhood.port_1.remote_node_alias, ) self.assertEqual( closure.components.p4_port1_far_primary_line.name, self.system._p4_primary_connection_for_node( port1_remote_neighborhood.port_1.remote_node_alias ).line_alias, ) port1_far_port1 = self.system._pnl0002_connection_for_node_port( closure.components.p4_port1_far_node.alias, "port_1", ) self.assertEqual( closure.components.p4_port1_far_port1_line.name, port1_far_port1.line_alias, ) self.assertEqual( closure.components.p4_port1_next_node.alias, port1_far_port1.remote_node_alias, ) self.assertEqual( closure.components.p4_port1_next_primary_line.name, self.system._p4_primary_connection_for_node( port1_far_port1.remote_node_alias ).line_alias, ) port1_next_port1 = self.system._pnl0002_connection_for_node_port( closure.components.p4_port1_next_node.alias, "port_1", ) self.assertEqual( closure.components.p4_port1_next_port1_line.name, port1_next_port1.line_alias, ) self.assertEqual( closure.components.p4_port1_next_orifice_output_line.name, self.system._pnl0001_line_for_node_port( closure.components.p4_port1_next_node.alias, "port_4", ).name, ) self.assertEqual( closure.components.p4_port1_next_orifice_node.alias, "pn_node3_12", ) self.assertEqual( closure.components.p4_port1_next_orifice_line.name, self.system._pnl0003_line_for_node_port("pn_node3_12", "port_2").name, ) self.assertEqual( closure.components.p4_port1_next_orifice.name, "pn_morifice_11", ) self.assertEqual( closure.components.p4_port1_next_orifice_resistance.name, self.system._pnl00r_line_for_node_port("pn_node3_12", "port_3").name, ) self.assertEqual( closure.components.p4_port1_next_orifice_resistance_node.alias, "pn_node3_11", ) self.assertEqual( closure.components.p4_port1_next_orifice_resistance_line.name, self.system._pnl0003_line_for_node_port("pn_node3_11", "port_2").name, ) self.assertEqual( closure.components.p4_port1_next_orifice_resistance_orifice.name, "pn_morifice_15", ) resistance_chamber_segment = ( self.system.discover_pneumatic_branch_topology().chamber_segment_specs[1] ) self.assertEqual( closure.components.p4_port1_next_orifice_resistance_chamber_spec, resistance_chamber_segment, ) self.assertEqual( closure.components.p4_port1_next_orifice_resistance_chamber_inlet_line.name, resistance_chamber_segment.inlet_line_alias, ) self.assertEqual( closure.components.p4_port1_next_orifice_resistance_chamber_outlet_line.name, resistance_chamber_segment.outlet_line_alias, ) self.assertEqual( closure.components.p4_port1_next_orifice_resistance_chamber_inlet_orifice.name, resistance_chamber_segment.inlet_orifice_alias, ) self.assertEqual( closure.components.p4_port1_next_orifice_resistance_chamber_outlet_orifice.name, resistance_chamber_segment.outlet_orifice_alias, ) self.assertEqual( closure.components.p4_bridge_node.alias, port1_next_port1.remote_node_alias, ) self.assertEqual( closure.components.p4_bridge_primary_line.name, self.system._p4_primary_connection_for_node( closure.components.p4_bridge_node.alias ).line_alias, ) self.assertEqual( closure.components.p4_port1_remote_orifice.name, port1_remote_neighborhood.port_4.orifice_alias, ) self.assertEqual( closure.components.p4_port1_remote_orifice_line.name, self.system._pnl0003_line_for_node_port("pn_node3_10", "port_2").name, ) self.assertEqual( closure.components.p4_port1_remote_orifice_node.alias, "pn_node3_10", ) self.assertEqual( closure.components.node_resistance.name, self.system._pnl00r_line_for_node_port("pn_node3_10", "port_3").name, ) self.assertEqual( closure.components.node.alias, "pn_node3_9", ) self.assertEqual( closure.components.p4_port3_line.name, neighborhood.port_3.line_alias, ) self.assertEqual( closure.components.p4_port3_remote_node.alias, neighborhood.port_3.remote_node_alias, ) port3_remote_neighborhood = self.system.p4_node_neighborhood( neighborhood.port_3.remote_node_alias ) self.assertEqual( closure.components.p4_port3_remote_port3_line.name, port3_remote_neighborhood.port_3.line_alias, ) self.assertEqual( closure.components.p4_port3_far_node.alias, port3_remote_neighborhood.port_3.remote_node_alias, ) self.assertEqual( closure.components.p4_port3_far_primary_line.name, self.system._p4_primary_connection_for_node( port3_remote_neighborhood.port_3.remote_node_alias ).line_alias, ) port3_far_port3 = self.system._pnl0002_connection_for_node_port( closure.components.p4_port3_far_node.alias, "port_3", ) self.assertEqual( closure.components.p4_port3_far_port3_line.name, port3_far_port3.line_alias, ) self.assertEqual( closure.components.p4_port3_next_node.alias, port3_far_port3.remote_node_alias, ) self.assertEqual( closure.components.p4_port3_next_primary_line.name, self.system._p4_primary_connection_for_node( port3_far_port3.remote_node_alias ).line_alias, ) port3_next_port3 = self.system._pnl0002_connection_for_node_port( closure.components.p4_port3_next_node.alias, "port_3", ) self.assertEqual( closure.components.p4_port3_next_port3_line.name, port3_next_port3.line_alias, ) self.assertEqual( closure.components.p4_bridge_node.alias, port3_next_port3.remote_node_alias, ) self.assertEqual( closure.components.p4_port3_next_orifice_output_line.name, self.system._pnl0001_line_for_node_port( closure.components.p4_port3_next_node.alias, "port_4", ).name, ) self.assertEqual( closure.components.p4_port3_next_orifice.name, "pn_morifice_13", ) self.assertEqual( closure.components.p4_port3_next_orifice_line.name, self.system._pnl0003_line_for_node_port("pn_node3_14", "port_2").name, ) self.assertEqual( closure.components.p4_port3_next_orifice_node.alias, "pn_node3_14", ) chamber_segment = ( self.system.discover_pneumatic_branch_topology().chamber_segment_specs[3] ) self.assertEqual( closure.components.p4_port3_next_chamber_spec, chamber_segment, ) self.assertEqual( closure.components.p4_port3_next_chamber_inlet_line.name, chamber_segment.inlet_line_alias, ) self.assertEqual( closure.components.p4_port3_next_chamber_outlet_line.name, chamber_segment.outlet_line_alias, ) self.assertEqual( closure.components.p4_port3_next_orifice_resistance.name, self.system._pnl00r_line_for_node_port("pn_node3_14", "port_3").name, ) self.assertEqual( closure.components.p4_port3_next_resistance_node.alias, "pn_node3_13", ) self.assertEqual( closure.components.p4_port3_next_resistance_node_line.name, self.system._pnl0003_line_for_node_port("pn_node3_13", "port_2").name, ) self.assertEqual( closure.components.p4_port3_next_resistance_orifice.name, "pn_morifice_12", ) self.assertEqual( closure.components.p4_port3_next_resistance_output_line.name, self.system._pnl0001_line_for_node_port( closure.components.p4_bridge_node.alias, "port_4", ).name, ) resistance_chamber_segment = ( self.system.discover_pneumatic_branch_topology().chamber_segment_specs[2] ) self.assertEqual( closure.components.p4_port3_next_resistance_chamber_spec, resistance_chamber_segment, ) self.assertEqual( closure.components.p4_port3_next_resistance_chamber_inlet_line.name, resistance_chamber_segment.inlet_line_alias, ) self.assertEqual( closure.components.p4_port3_next_resistance_chamber_outlet_line.name, resistance_chamber_segment.outlet_line_alias, ) self.assertEqual( closure.components.p4_port3_next_resistance_chamber_inlet_orifice.name, resistance_chamber_segment.inlet_orifice_alias, ) self.assertEqual( closure.components.p4_port3_next_resistance_chamber_outlet_orifice.name, resistance_chamber_segment.outlet_orifice_alias, ) self.assertEqual( closure.components.p4_port3_remote_orifice.name, port3_remote_neighborhood.port_4.orifice_alias, ) self.assertEqual( closure.components.p4_port3_remote_orifice_line.name, self.system._pnl0003_line_for_node_port("pn_node3_8", "port_2").name, ) self.assertEqual( closure.components.p4_port3_remote_orifice_node.alias, "pn_node3_8", ) self.assertEqual( closure.components.p4_port3_remote_orifice_resistance.name, self.system._pnl00r_line_for_node_port("pn_node3_8", "port_3").name, ) self.assertEqual( closure.components.p4_port3_remote_resistance_node.alias, "pn_node3_15", ) self.assertEqual( closure.components.p4_port3_remote_resistance_node_line.name, self.system._pnl0003_line_for_node_port("pn_node3_15", "port_2").name, ) self.assertEqual( closure.components.p4_port3_remote_resistance_orifice.name, "pn_morifice_14", ) self.assertEqual( closure.components.p4_port3_remote_resistance_output_line.name, self.system._pnl0001_line_for_node_port( closure.components.p4_port3_far_node.alias, "port_4", ).name, ) self.assertEqual( closure.components.node_orifice.name, neighborhood.port_4.orifice_alias, ) def test_p4_node_flow_cancels_between_pnvo_pnl0002_and_primary_line(self) -> None: closure = self.system.pn3_p4_node_chamber_segment_closure_from_spec( self.spec, inlet_node_pressure_pa=15.31e6, resistance_boundary_pressure_pa=15.29e6, ) self.assertAlmostEqual(closure.components.node_orifice.opening, 0.0) closure.components.node_orifice.opening = 1.0 snapshot = closure.snapshot() rhs = closure.rhs(closure.initial_state_vector()) self.assertGreater(snapshot.pnl0003_to_p4_flow, 0.0) self.assertAlmostEqual( snapshot.p4_balance.port_2_mass_flow_g_s, ( -snapshot.p4_to_port1_line_flow - snapshot.p4_to_port3_line_flow + snapshot.pnl0003_to_p4_flow ) * 1.0e3, ) self.assertAlmostEqual( snapshot.p4_node_to_primary_line_flow, snapshot.p4_balance.port_2_mass_flow_g_s * 1.0e-3, ) self.assertAlmostEqual( snapshot.p4_node_to_primary_line_flow + snapshot.p4_to_port1_line_flow + snapshot.p4_to_port3_line_flow - snapshot.pnl0003_to_p4_flow, 0.0, delta=1.0e-12, ) self.assertAlmostEqual( snapshot.p4_port1_remote_node_to_primary_line_flow + snapshot.p4_port1_remote_node_to_line_flow + snapshot.p4_port1_remote_to_port1_line_flow - snapshot.p4_port1_remote_orifice_to_node_flow, 0.0, delta=1.0e-12, ) self.assertAlmostEqual( snapshot.p4_port1_far_node_to_primary_line_flow + snapshot.p4_port1_far_node_to_line_flow + snapshot.p4_port1_far_to_next_line_flow - snapshot.p4_port1_next_orifice_resistance_to_p4_flow, 0.0, delta=1.0e-12, ) self.assertAlmostEqual( snapshot.p4_port1_next_node_to_primary_line_flow + snapshot.p4_port1_next_node_to_line_flow + snapshot.p4_port1_next_to_bridge_line_flow + snapshot.p4_port1_next_orifice_p4_to_output_line_flow, 0.0, delta=1.0e-12, ) self.assertAlmostEqual( snapshot.p4_bridge_node_to_primary_line_flow + snapshot.p4_bridge_to_port1_next_line_flow + snapshot.p4_bridge_to_port3_next_line_flow - snapshot.p4_port3_next_resistance_output_to_p4_flow, 0.0, delta=1.0e-12, ) self.assertAlmostEqual( snapshot.p4_port1_remote_node_to_orifice_line_flow + snapshot.p4_port1_next_orifice_resistance_chamber_inlet_node_to_line_flow - snapshot.node_to_resistance_flow, 0.0, delta=1.0e-12, ) self.assertAlmostEqual( snapshot.node_to_pnl0003_flow + snapshot.node_to_resistance_flow + snapshot.outlet_node_to_line_flow, 0.0, delta=1.0e-12, ) self.assertAlmostEqual( snapshot.p4_port3_remote_node_to_orifice_line_flow + snapshot.inlet_node_to_line_flow + snapshot.p4_port3_remote_node_to_resistance_flow, 0.0, delta=1.0e-12, ) self.assertAlmostEqual( snapshot.p4_port3_remote_node_to_primary_line_flow + snapshot.p4_port3_remote_node_to_line_flow + snapshot.p4_port3_remote_to_port3_line_flow - snapshot.p4_port3_remote_orifice_to_node_flow, 0.0, delta=1.0e-12, ) self.assertAlmostEqual( snapshot.p4_port3_remote_resistance_node_to_line_flow - snapshot.p4_port3_remote_node_to_resistance_flow + snapshot.p4_port3_next_chamber_outlet_node_to_line_flow, 0.0, delta=1.0e-12, ) self.assertAlmostEqual( snapshot.p4_port3_remote_resistance_output_to_p4_flow, snapshot.p4_port3_remote_resistance_to_output_line_flow, delta=1.0e-12, ) self.assertAlmostEqual( snapshot.p4_port3_far_node_to_primary_line_flow + snapshot.p4_port3_far_node_to_line_flow + snapshot.p4_port3_far_to_next_line_flow - snapshot.p4_port3_remote_resistance_output_to_p4_flow, 0.0, delta=1.0e-12, ) self.assertAlmostEqual( snapshot.p4_port3_next_node_to_primary_line_flow + snapshot.p4_port3_next_node_to_line_flow + snapshot.p4_port3_next_to_bridge_line_flow - snapshot.p4_port3_next_orifice_output_to_p4_flow, 0.0, delta=1.0e-12, ) self.assertAlmostEqual( snapshot.p4_port3_next_orifice_node_to_line_flow + snapshot.p4_port3_next_chamber_inlet_node_to_line_flow - snapshot.p4_port3_next_resistance_node_to_next_flow, 0.0, delta=1.0e-12, ) self.assertAlmostEqual( snapshot.p4_port3_next_orifice_output_to_p4_flow, snapshot.p4_port3_next_orifice_to_output_line_flow, delta=1.0e-12, ) self.assertAlmostEqual( snapshot.p4_port3_next_resistance_node_to_line_flow + snapshot.p4_port3_next_resistance_node_to_next_flow + snapshot.p4_port3_next_resistance_chamber_outlet_node_to_line_flow, 0.0, delta=1.0e-12, ) self.assertAlmostEqual( snapshot.p4_port3_next_resistance_output_to_p4_flow, snapshot.p4_port3_next_resistance_to_output_line_flow, delta=1.0e-12, ) self.assertAlmostEqual( snapshot.p4_port1_next_orifice_node_to_line_flow + snapshot.p4_port3_next_resistance_chamber_inlet_node_to_line_flow - snapshot.p4_port1_next_orifice_resistance_node_to_next_flow, 0.0, delta=1.0e-12, ) self.assertAlmostEqual( snapshot.p4_port1_next_orifice_resistance_node_to_line_flow + snapshot.p4_port1_next_orifice_resistance_node_to_next_flow + snapshot.p4_port1_next_orifice_resistance_chamber_outlet_node_to_line_flow, 0.0, delta=1.0e-12, ) next_chamber_mass_sum = rhs[78] + rhs[80] + rhs[82] self.assertAlmostEqual( next_chamber_mass_sum, ( snapshot.p4_port3_next_chamber_inlet_node_to_line_flow + snapshot.p4_port3_next_chamber_outlet_node_to_line_flow ), delta=1.0e-12, ) resistance_chamber_mass_sum = rhs[90] + rhs[92] + rhs[94] self.assertAlmostEqual( resistance_chamber_mass_sum, ( snapshot.p4_port3_next_resistance_chamber_inlet_node_to_line_flow + snapshot.p4_port3_next_resistance_chamber_outlet_node_to_line_flow ), delta=1.0e-12, ) p4_port1_next_orifice_line_mass_sum = rhs[96] + rhs[98] self.assertAlmostEqual( p4_port1_next_orifice_line_mass_sum, ( snapshot.p4_port1_next_orifice_node_to_line_flow - snapshot.p4_port1_next_orifice_to_output_line_flow ), delta=1.0e-12, ) self.assertAlmostEqual( rhs[100], ( snapshot.p4_port1_next_orifice_p4_to_output_line_flow + snapshot.p4_port1_next_orifice_to_output_line_flow ), delta=1.0e-12, ) p4_port1_next_orifice_resistance_line_mass_sum = rhs[102] + rhs[104] self.assertAlmostEqual( p4_port1_next_orifice_resistance_line_mass_sum, ( snapshot.p4_port1_next_orifice_resistance_node_to_line_flow - snapshot.p4_port1_next_orifice_resistance_to_p4_flow ), delta=1.0e-12, ) p4_port1_next_orifice_resistance_chamber_mass_sum = ( rhs[106] + rhs[108] + rhs[110] ) self.assertAlmostEqual( p4_port1_next_orifice_resistance_chamber_mass_sum, ( snapshot.p4_port1_next_orifice_resistance_chamber_inlet_node_to_line_flow + snapshot.p4_port1_next_orifice_resistance_chamber_outlet_node_to_line_flow ), delta=1.0e-12, ) mass_derivative_sum = sum(rhs[index] for index in range(0, 112, 2)) self.assertAlmostEqual( mass_derivative_sum, 0.0, delta=1.0e-12, ) def test_simulates_one_hundred_twelve_state_pn3_p4_node_chamber_segment(self) -> None: solution = self.system.simulate_pn3_p4_node_chamber_segment_from_spec( self.spec, inlet_node_pressure_pa=15.31e6, resistance_boundary_pressure_pa=15.29e6, config=SolveIVPConfig( t_start=0.0, t_stop=1.0e-10, max_step=1.0e-11, ), t_eval=[0.0, 5.0e-11, 1.0e-10], ) self.assertTrue(solution.success) self.assertEqual(len(solution.t), 3) self.assertEqual(len(solution.y), 112) self.assertEqual([len(row) for row in solution.y], [3] * 112) if __name__ == "__main__": unittest.main()