Files
SystemSimulationApp/tests/test_component_interfaces.py
T
ljz bbc88a6bbb 规范仿真模型库并完善前端交互
归档仿真模型并补充组件目录、建模规范与校验。

完善控制台、默认节点、视图适配及前端自动化测试。
2026-07-29 15:44:16 +08:00

186 lines
7.6 KiB
Python

from __future__ import annotations
import unittest
from app.main import compile_reactflow_network
from app.simulation.components.experimental.junctions.tee import Tee
from app.simulation.components.experimental.storage.cylinder import Cylinder
from app.simulation.components.experimental.storage.tank import Tank
from app.simulation.core.base import Component
from app.simulation.core.medium import IdealGasMedium
from app.simulation.core.ports import PortDefinition, PortState, PortVariableDefinition
from app.simulation.examples.testmodel.system import TestModelSystem
from app.simulation.systems.network import SimulationNetwork
from tests.test_system_xml_protocol import physical_connection_project
class ComponentInterfaceTests(unittest.TestCase):
def test_pneumatic_port_exposes_equation_roles_and_signed_flow(self) -> None:
port = PortState.pneumatic("port_a", nominal_role="bidirectional")
assert port.definition is not None
self.assertEqual(
[
(variable.name, variable.role, variable.connection_rule)
for variable in port.definition.variables
],
[
("p", "effort", "equal"),
("m_flow", "flow", "sumToZero"),
("h_outflow", "stream", "streamMix"),
],
)
port.m_flow = 10.0
self.assertEqual(port.actual_direction(), "in")
self.assertEqual(port.inflow_rate, 10.0)
self.assertEqual(port.outflow_rate, 0.0)
port.m_flow = -4.0
self.assertEqual(port.actual_direction(), "out")
self.assertEqual(port.inflow_rate, 0.0)
self.assertEqual(port.outflow_rate, 4.0)
def test_physical_connection_is_neutral_and_canonical(self) -> None:
forward = compile_reactflow_network(physical_connection_project())
reverse = compile_reactflow_network(
physical_connection_project(reverse_edge=True)
)
self.assertEqual(
forward.connections[0].undirected_key,
reverse.connections[0].undirected_key,
)
self.assertEqual(
forward.connections[0].as_interface_dict(),
reverse.connections[0].as_interface_dict(),
)
self.assertNotIn("source", forward.connections[0].as_interface_dict())
def test_compiler_instantiates_parameters_and_actual_edges(self) -> None:
project = physical_connection_project()
network = compile_reactflow_network(project)
self.assertEqual(set(network.components), {"cylinder_1", "tank_1"})
self.assertEqual(network.components["cylinder_1"].V, 0.01)
self.assertEqual(network.components["tank_1"].V, 0.1)
self.assertEqual(len(network.connections), 1)
self.assertEqual(network.connections[0].id, "edge-1")
self.assertEqual(network.connections[0].kind, "physical")
self.assertEqual(network.connections[0].domain, "pneumatic")
equation_system = network.as_interface_dict()["pressureFlowSystem"]
self.assertEqual(equation_system["unknownCount"], 4)
self.assertEqual(equation_system["equationCount"], 4)
self.assertTrue(equation_system["isSquare"])
def test_physical_port_requires_a_tee_for_branching(self) -> None:
medium = IdealGasMedium()
network = SimulationNetwork("branch-check")
network.add_component(Cylinder("cylinder_1", medium))
network.add_component(Tank("tank_1", medium))
network.add_component(Tank("tank_2", medium))
network.connect("cylinder_1", "port_b", "tank_1", "port_a")
with self.assertRaisesRegex(ValueError, "Use a junction component"):
network.connect("cylinder_1", "port_b", "tank_2", "port_a")
def test_connection_requires_the_same_variable_contract(self) -> None:
medium = IdealGasMedium()
network = SimulationNetwork("contract-check")
network.add_component(Cylinder("cylinder_1", medium))
incompatible = Component("incompatible_1")
incompatible.register_port(
PortState(
definition=PortDefinition(
name="port_a",
kind="physical",
domain="pneumatic",
nominal_role="bidirectional",
positive_flow_direction="intoComponent",
variables=(PortVariableDefinition("p", "effort", "equal"),),
)
)
)
network.add_component(incompatible)
with self.assertRaisesRegex(ValueError, "variable contracts do not match"):
network.connect("cylinder_1", "port_b", "incompatible_1", "port_a")
def test_connection_equations_are_executable_and_endpoint_neutral(self) -> None:
medium = IdealGasMedium()
network = SimulationNetwork("residual-check")
cylinder = Cylinder("cylinder_1", medium)
tank = Tank("tank_1", medium)
network.add_component(cylinder)
network.add_component(tank)
network.connect("tank_1", "port_a", "cylinder_1", "port_b")
cylinder.port_b.p = 2.0e5
cylinder.port_b.m_flow = -3.0
tank.port_a.p = 2.0e5
tank.port_a.m_flow = 3.0
residuals = {
residual.variables[0].rsplit(".", 1)[-1]: residual
for residual in network.connection_equation_residuals()
}
self.assertEqual(set(residuals), {"p", "m_flow"})
self.assertEqual(residuals["p"].relation, "equal")
self.assertEqual(residuals["m_flow"].relation, "sumToZero")
self.assertEqual(residuals["p"].value, 0.0)
self.assertEqual(residuals["m_flow"].value, 0.0)
def test_tee_ports_are_physical_and_bidirectional(self) -> None:
tee = Tee("tee_1")
self.assertEqual(
{definition.name for definition in tee.port_definitions},
{"port_in", "port_out1", "port_out2"},
)
self.assertTrue(
all(
definition.kind == "physical"
and definition.nominal_role == "bidirectional"
for definition in tee.port_definitions
)
)
def test_existing_testmodel_topology_still_builds(self) -> None:
system = TestModelSystem()
self.assertEqual(len(system.network.components), 8)
self.assertEqual(len(system.network.connections), 8)
self.assertTrue(
all(connection.kind == "physical" for connection in system.network.connections)
)
self.assertEqual(len(system.initial_state_vector()), 8)
def test_testmodel_snapshot_satisfies_acausal_connection_equations(self) -> None:
system = TestModelSystem()
state = system.consistent_initial_state_vector()
system.snapshot(state)
residuals = system.network.pressure_flow_equation_residuals()
self.assertEqual(len(residuals), 4 * len(system.network.connections))
self.assertEqual(len(system.network.pressure_flow_unknowns()), len(residuals))
for residual in residuals:
tolerance = 1e-6
self.assertLessEqual(
abs(residual.value),
tolerance,
msg=f"{residual.id} residual is {residual.value}",
)
upstream_tee = system.mytee
self.assertAlmostEqual(
upstream_tee.port_in.m_flow
+ upstream_tee.port_out1.m_flow
+ upstream_tee.port_out2.m_flow,
0.0,
places=12,
)
self.assertAlmostEqual(upstream_tee.port_in.p, upstream_tee.port_out1.p)
self.assertAlmostEqual(upstream_tee.port_in.p, upstream_tee.port_out2.p)
if __name__ == "__main__":
unittest.main()