完善 XML 通用仿真与结果查看
This commit is contained in:
1 parent
104d41d294
commit
f1256a121d
36 files changed
+6666
-196
No files matched your search
@@ -0,0 +1,185 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import unittest
|
||||
|
||||
from app.main import compile_reactflow_network
|
||||
from PythonModels.components.cylinder import Cylinder
|
||||
from PythonModels.components.tank import Tank
|
||||
from PythonModels.components.tee import Tee
|
||||
from PythonModels.core.base import Component
|
||||
from PythonModels.core.medium import IdealGasMedium
|
||||
from PythonModels.core.network import SimulationNetwork
|
||||
from PythonModels.core.ports import PortDefinition, PortState, PortVariableDefinition
|
||||
from PythonModels.systems.testmodel import TestModelSystem
|
||||
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()
|
||||
Reference in new issue
Block a user