Files
SystemSimulationApp/tests/test_amesim_signal_components.py

197 lines
7.9 KiB
Python

from __future__ import annotations
import unittest
from app.simulation.components.amesim.flow.orifices import AmesimPnvo001SignalOpening
from app.simulation.components.amesim.signals.sources import AmesimStep0, AmesimUd00
from app.simulation.core.medium import IdealGasMedium
from app.simulation.registry import COMPONENT_MODEL_REGISTRY
from app.simulation.solvers.signal import SignalResolver
from app.simulation.systems.network import SimulationNetwork
class AmesimSignalComponentTests(unittest.TestCase):
def setUp(self) -> None:
self.medium = IdealGasMedium()
def test_step0_output_switches_at_step_time(self) -> None:
step = AmesimStep0("step_1", self.medium, initial=0.2, final=0.8, time=0.5)
self.assertEqual(step.output_at(0.49), 0.2)
self.assertEqual(step.output_at(0.5), 0.8)
self.assertEqual(step.signal_output_values(0.5), {"out": 0.8})
self.assertEqual(step.signal_event_times(0.0, 1.0), (0.5,))
self.assertEqual(step.signal_event_times(0.5, 1.0), ())
def test_ud00_output_interpolates_piecewise_signal(self) -> None:
signal = AmesimUd00(
"piecewise_1",
self.medium,
tstart=0.5,
starts=(0.0, 10.0, 20.0, 0.0, 0.0, 0.0, 0.0, 0.0),
ends=(10.0, 20.0, 30.0, 0.0, 0.0, 0.0, 0.0, 0.0),
durations=(1.0, 2.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0),
nstages=3,
)
self.assertAlmostEqual(signal.output_at(0.0), 0.0)
self.assertAlmostEqual(signal.output_at(1.0), 5.0)
self.assertAlmostEqual(signal.output_at(2.5), 15.0)
self.assertAlmostEqual(signal.output_at(5.0), 35.0)
self.assertEqual(signal.signal_output_values(2.5), {"out": 15.0})
self.assertEqual(signal.signal_event_times(0.0, 5.0), (0.5, 1.5, 3.5))
def test_ud00_can_cycle_active_stages(self) -> None:
signal = AmesimUd00(
"piecewise_1",
self.medium,
starts=(0.0, 10.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0),
ends=(10.0, 20.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0),
durations=(1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0),
nstages=2,
iscyclic=True,
)
self.assertAlmostEqual(signal.output_at(0.25), 2.5)
self.assertAlmostEqual(signal.output_at(1.25), 12.5)
self.assertAlmostEqual(signal.output_at(2.25), 2.5)
self.assertEqual(signal.signal_event_times(0.0, 5.0), (1.0, 2.0, 3.0, 4.0))
def test_ud00_registry_rejects_fractional_stage_controls(self) -> None:
with self.assertRaisesRegex(ValueError, "must be one of"):
COMPONENT_MODEL_REGISTRY["amesim_ud00"].create(
"piecewise_1",
self.medium,
{"nstages": 1.5},
)
for name, value in (("nstages", 9.0), ("iscyclic", 2.0)):
with self.subTest(name=name):
with self.assertRaisesRegex(
ValueError,
"must be (?:one of|at most)",
):
COMPONENT_MODEL_REGISTRY["amesim_ud00"].create(
"piecewise_1",
self.medium,
{name: value},
)
def test_pnvo001_registry_rejects_invalid_flowset_choice(self) -> None:
with self.assertRaisesRegex(ValueError, "must be one of"):
COMPONENT_MODEL_REGISTRY["amesim_pnvo001"].create(
"valve_1",
self.medium,
{"flowset": 1.5},
)
def test_pnvo001_signal_opening_reads_res_port(self) -> None:
valve = COMPONENT_MODEL_REGISTRY["amesim_pnvo001"].create(
"valve_1",
self.medium,
{"opening0": 0.25},
)
self.assertIsInstance(valve, AmesimPnvo001SignalOpening)
self.assertEqual(set(valve.ports), {"res", "port_2", "port_3"})
self.assertAlmostEqual(valve.opening, 0.25)
valve.res.signal = 1.5
self.assertAlmostEqual(valve.opening, 1.0)
valve.res.signal = -0.5
self.assertAlmostEqual(valve.opening, 0.0)
def test_pnvo001_exact_repeated_flow_characteristics_are_cached(self) -> None:
valve = AmesimPnvo001SignalOpening("valve_1", self.medium, opening0=0.25)
valve._one_way_flow_characteristics.cache_clear()
first = valve._one_way_flow_characteristics(
upstream_pressure=501000.0,
downstream_pressure=500000.0,
upstream_temperature=300.0,
)
after_first = valve._one_way_flow_characteristics.cache_info()
second = valve._one_way_flow_characteristics(
upstream_pressure=501000.0,
downstream_pressure=500000.0,
upstream_temperature=300.0,
)
after_second = valve._one_way_flow_characteristics.cache_info()
changed_pressure = valve._one_way_flow_characteristics(
upstream_pressure=502000.0,
downstream_pressure=500000.0,
upstream_temperature=300.0,
)
after_pressure_change = valve._one_way_flow_characteristics.cache_info()
self.assertEqual(first, second)
self.assertEqual(after_first.misses, 1)
self.assertEqual(after_second.hits, after_first.hits + 1)
self.assertNotEqual(changed_pressure, second)
self.assertEqual(
after_pressure_change.misses,
after_second.misses + 1,
)
def test_signal_resolver_propagates_step_to_valve_input(self) -> None:
network = SimulationNetwork("signal-smoke")
step = AmesimStep0("step_1", self.medium, initial=0.0, final=0.75, time=0.1)
valve = AmesimPnvo001SignalOpening("valve_1", self.medium, opening0=0.0)
network.add_component(step)
network.add_component(valve)
network.connect("step_1", "out", "valve_1", "res", connection_id="signal-1")
resolver = SignalResolver(network)
resolver.solve(0.2)
self.assertAlmostEqual(step.out.signal, 0.75)
self.assertAlmostEqual(valve.res.signal, 0.75)
self.assertAlmostEqual(valve.opening, 0.75)
self.assertEqual(resolver.event_times(0.0, 0.2), (0.1,))
def test_signal_output_can_fan_out_but_input_has_one_driver(self) -> None:
network = SimulationNetwork("signal-cardinality")
step_1 = AmesimStep0(
"step_1", self.medium, initial=0.0, final=0.75, time=0.1
)
step_2 = AmesimStep0(
"step_2", self.medium, initial=1.0, final=0.25, time=0.1
)
valve_1 = AmesimPnvo001SignalOpening(
"valve_1", self.medium, opening0=0.0
)
valve_2 = AmesimPnvo001SignalOpening(
"valve_2", self.medium, opening0=0.0
)
for component in (step_1, step_2, valve_1, valve_2):
network.add_component(component)
network.connect("step_1", "out", "valve_1", "res")
network.connect("step_1", "out", "valve_2", "res")
with self.assertRaisesRegex(ValueError, "already has a driver"):
network.connect("step_2", "out", "valve_1", "res")
SignalResolver(network).solve(0.2)
self.assertAlmostEqual(valve_1.res.signal, 0.75)
self.assertAlmostEqual(valve_2.res.signal, 0.75)
def test_signal_resolver_event_times_are_sorted_and_deduplicated(self) -> None:
network = SimulationNetwork("signal-events")
network.add_component(
AmesimStep0("step_2", self.medium, initial=0.0, final=1.0, time=0.2)
)
network.add_component(
AmesimStep0("step_1", self.medium, initial=1.0, final=0.0, time=0.1)
)
network.add_component(
AmesimStep0("step_3", self.medium, initial=0.0, final=1.0, time=0.2)
)
resolver = SignalResolver(network)
self.assertEqual(resolver.event_times(0.0, 0.3), (0.1, 0.2))
self.assertEqual(resolver.event_times(0.2, 0.3), ())
if __name__ == "__main__":
unittest.main()