from __future__ import annotations import unittest from app.main import ( ReactFlowProjectPayload, build_reactflow_system_xml, compile_reactflow_network, run_system_xml_simulation, ) from app.system_xml import validate_system_xml_document from tests.test_generic_system_xml_simulation import component_node, physical_edge from tests.test_system_xml_protocol import physical_port def signal_port(name: str, role: str, side: str) -> dict[str, str | None]: return { "name": name, "kind": "signal", "domain": "signal", "nominalRole": role, "positiveFlowDirection": None, "side": side, } def signal_edge(edge_id: str, source: str, source_port: str, target: str, target_port: str) -> dict[str, str]: return { "id": edge_id, "source": source, "sourceHandle": source_port, "target": target, "targetHandle": target_port, } def amesim_pnvo001_signal_project() -> ReactFlowProjectPayload: return ReactFlowProjectPayload( projectSchemaVersion=1, name="amesim-pnvo001-signal-smoke", nodes=[ component_node( "step_1", "amesim_step0", [signal_port("out", "output", "right")], {"initial": 1.0, "final": 1.0, "time": 0.0}, ), component_node( "cylinder_1", "cylinder", [physical_port("port_b", "outlet", "right")], {"volume": 0.01, "p0": 300000.0, "T0": 300.0}, ), component_node( "valve_1", "amesim_pnvo001", [ signal_port("res", "input", "left"), physical_port("port_2", "bidirectional", "left"), physical_port("port_3", "bidirectional", "right"), ], { "cq": 0.72, "area0": 5.0e-6, "Cv": 0.5, "Kv": 0.4, "gi": 0.0, "flowset": 1.0, "opening0": 0.0, }, ), component_node( "tank_1", "tank", [physical_port("port_a", "inlet", "left")], {"volume": 0.1, "p0": 100000.0, "T0": 300.0}, ), ], edges=[ signal_edge("signal-1", "step_1", "out", "valve_1", "res"), physical_edge("edge-1", "cylinder_1", "port_b", "valve_1", "port_2"), physical_edge("edge-2", "valve_1", "port_3", "tank_1", "port_a"), ], simulation={"t_start": 0.0, "t_stop": 0.002, "step": 0.001, "max_step": 0.001, "method": "BDF"}, ) def high_pressure_helium_step_project() -> ReactFlowProjectPayload: chamber_ports = [ physical_port("port_1", "bidirectional", "left"), physical_port("port_2", "bidirectional", "right"), ] return ReactFlowProjectPayload( projectSchemaVersion=1, name="amesim-pnvo001-helium-step-regression", nodes=[ component_node( "helium_1", "amesim_helium_medium", [], {"gi": 1.0, "property_model": 0.0}, ), component_node( "step_1", "amesim_step0", [signal_port("out", "output", "right")], {"initial": 0.0, "final": 1.0, "time": 0.04}, ), component_node( "high_chamber", "amesim_pnch023", chamber_ports, { "gi": 1.0, "cvol": 0.057, "kth": 0.0, "sth": 0.1, "extemp": 293.15, "p0": 15_300_000.0, "T0": 293.15, }, ), component_node( "low_chamber", "amesim_pnch023", chamber_ports, { "gi": 1.0, "cvol": 0.015, "kth": 1500.0, "sth": 0.7, "extemp": 293.15, "p0": 100_000.0, "T0": 293.15, }, ), component_node( "valve_1", "amesim_pnvo001", [ signal_port("res", "input", "left"), physical_port("port_2", "bidirectional", "left"), physical_port("port_3", "bidirectional", "right"), ], { "gi": 1.0, "cq": 0.45, "area0": 7.85e-5, "Cv": 0.5, "Kv": 0.4, "flowset": 1.0, "opening0": 1.0, }, ), component_node( "high_plug", "amesim_pnpl01", [physical_port("port_1", "bidirectional", "left")], {}, ), component_node( "low_plug", "amesim_pnpl01", [physical_port("port_1", "bidirectional", "left")], {}, ), ], edges=[ signal_edge("signal-1", "step_1", "out", "valve_1", "res"), physical_edge( "edge-high-plug", "high_plug", "port_1", "high_chamber", "port_1", ), physical_edge( "edge-high-valve", "high_chamber", "port_2", "valve_1", "port_2", ), physical_edge( "edge-valve-low", "valve_1", "port_3", "low_chamber", "port_1", ), physical_edge( "edge-low-plug", "low_chamber", "port_2", "low_plug", "port_1", ), ], simulation={ "t_start": 0.0, "t_stop": 0.042, "step": 0.002, "max_step": 0.002, "method": "BDF", }, ) class AmesimPnvo001SignalXmlTests(unittest.TestCase): def test_signal_project_compiles_with_signal_connection(self) -> None: network = compile_reactflow_network(amesim_pnvo001_signal_project()) self.assertIn("step_1", network.components) self.assertIn("valve_1", network.components) self.assertEqual(network.components["valve_1"].model_type, "amesim_pnvo001") self.assertTrue(network.pressure_flow_structure_dict()["isSquare"]) def test_signal_system_xml_validates_and_simulates(self) -> None: xml = build_reactflow_system_xml(amesim_pnvo001_signal_project()) report = validate_system_xml_document(xml) self.assertTrue(report.valid, report.as_dict()) result = run_system_xml_simulation(xml) self.assertTrue(result["success"], result["message"]) self.assertEqual(result["series"]["time"], [0.0, 0.001, 0.002]) self.assertEqual(result["series"]["step_1.out.signal"], [1.0, 1.0, 1.0]) self.assertEqual(result["series"]["valve_1.res.signal"], [1.0, 1.0, 1.0]) self.assertIn("valve_1.xv", result["series"]) def test_high_pressure_helium_step_restarts_solver_at_event(self) -> None: xml = build_reactflow_system_xml(high_pressure_helium_step_project()) result = run_system_xml_simulation(xml) self.assertTrue(result["success"], result["message"]) self.assertEqual(result["simulatedUntil"], 0.042) times = result["series"]["time"] before_event = times.index(0.038) at_event = times.index(0.04) self.assertEqual(result["series"]["step_1.out.signal"][before_event], 0.0) self.assertEqual(result["series"]["step_1.out.signal"][at_event], 1.0) self.assertEqual(result["series"]["valve_1.xv"][before_event], 0.0) self.assertEqual(result["series"]["valve_1.xv"][at_event], 1.0) self.assertEqual(result["series"]["valve_1.gasvel"][before_event], 0.0) self.assertNotEqual(result["series"]["valve_1.gasvel"][at_event], 0.0) self.assertGreater( result["series"]["valve_1.port_2.m_flow"][at_event], 0.45, ) if __name__ == "__main__": unittest.main()