from __future__ import annotations import unittest from app.simulation.components.amesim.flow.pipes import AmesimPnl0001 from app.simulation.core.medium import IdealGasMedium from app.simulation.registry import COMPONENT_MODEL_REGISTRY class AmesimPnl0001ComponentTests(unittest.TestCase): def setUp(self) -> None: self.medium = IdealGasMedium() def test_default_create_preserves_amesim_parameter_contract(self) -> None: pipe = COMPONENT_MODEL_REGISTRY["amesim_pnl0001"].create( "pnl_1", self.medium, {}, ) self.assertIsInstance(pipe, AmesimPnl0001) self.assertEqual(set(pipe.ports), {"port_1", "port_2"}) self.assertEqual( set(pipe.parameter_values), { "diam", "le", "rr", "k", "kth", "extemp", "gi", "mode", "p0", "T0", }, ) self.assertEqual(len(pipe.get_state_vector()), 2) self.assertAlmostEqual(pipe.volume, 7.853981633974483e-5) def test_rejects_fractional_integer_parameters(self) -> None: with self.assertRaisesRegex(ValueError, "gi must be an integer"): COMPONENT_MODEL_REGISTRY["amesim_pnl0001"].create( "pnl_1", self.medium, {"gi": 1.5}, ) with self.assertRaisesRegex(ValueError, "mode must be an integer"): COMPONENT_MODEL_REGISTRY["amesim_pnl0001"].create( "pnl_1", self.medium, {"mode": 1.5}, ) def test_initial_state_uses_pipe_volume_pressure_and_temperature(self) -> None: pipe = AmesimPnl0001( "pnl_1", self.medium, diam=0.014, le=1.0, rr=0.045 / 14.0, p0=15.3e6, T0=293.15, ) props = pipe.properties() self.assertAlmostEqual(pipe.volume, 1.539380400258999e-4) self.assertAlmostEqual(props.p, 15.3e6, delta=1.0e-5) self.assertAlmostEqual(props.T, 293.15) self.assertAlmostEqual(pipe.port_2.p, props.p) def test_resistance_flow_follows_pressure_gradient(self) -> None: pipe = AmesimPnl0001( "pnl_1", self.medium, diam=0.014, le=1.0, rr=0.045 / 14.0, p0=15.3e6, T0=293.15, ) props = pipe.properties() forward = pipe.mass_flow(15.31e6, props.p, props.T) reverse = pipe.mass_flow(15.29e6, props.p, props.T) self.assertGreater(forward, 0.0) self.assertLess(reverse, 0.0) self.assertAlmostEqual(abs(forward), abs(reverse), delta=abs(forward) * 0.02) def test_pressure_flow_residuals_do_not_force_storage_mass_balance(self) -> None: pipe = AmesimPnl0001("pnl_1", self.medium) pipe.port_1.p = 101000.0 pipe.port_2.m_flow = -1.0e-4 props = pipe.properties() pipe.port_1.m_flow = pipe.mass_flow(pipe.port_1.p, pipe.port_2.p, props.T) residuals = { residual.id.rsplit(":", 1)[1]: residual for residual in pipe.pressure_flow_equation_residuals() } self.assertEqual( set(residuals), {"port_2_pressure_state", "port_1_pressure_flow_relation"}, ) self.assertAlmostEqual(residuals["port_2_pressure_state"].value, 0.0) self.assertAlmostEqual(residuals["port_1_pressure_flow_relation"].value, 0.0) def test_connection_derivative_preserves_two_port_accumulation(self) -> None: pipe = AmesimPnl0001("pnl_1", self.medium, kth=2.0, extemp=310.0) props = pipe.properties() pipe.port_1.m_flow = 0.2 pipe.port_2.m_flow = -0.1 derivative = pipe.state_derivative_from_ports( { "port_1": props.h + 1000.0, "port_2": props.h - 1000.0, } ) self.assertAlmostEqual(derivative[0], 0.1) self.assertGreater(derivative[1], 0.0) def test_results_include_thermodynamic_and_pipe_diagnostics(self) -> None: pipe = AmesimPnl0001("pnl_1", self.medium) pipe.port_1.p = 101000.0 values = pipe.component_result_values() self.assertEqual( set(values), {"m", "U", "p", "T", "rho", "u", "h", "re", "cm", "v", "ff"}, ) if __name__ == "__main__": unittest.main()