perf: retain exact flow caches and compiled targets
This commit is contained in:
1 parent
f09dfcf542
commit
22b35b2945
7 files changed
+138
-21
No files matched your search
@@ -716,6 +716,7 @@ class AmesimPnvo001FixedOpening(AlgebraicComponent):
|
|||||||
upstream_temperature=self._upstream_temperature("port_3"),
|
upstream_temperature=self._upstream_temperature("port_3"),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@lru_cache(maxsize=32768)
|
||||||
def _one_way_flow_characteristics(
|
def _one_way_flow_characteristics(
|
||||||
self,
|
self,
|
||||||
*,
|
*,
|
||||||
|
|||||||
@@ -267,6 +267,7 @@ class AmesimPnl00r(AlgebraicComponent):
|
|||||||
)
|
)
|
||||||
return magnitude if mass_flow > 0.0 else -magnitude
|
return magnitude if mass_flow > 0.0 else -magnitude
|
||||||
|
|
||||||
|
@lru_cache(maxsize=32768)
|
||||||
def _mass_flow_for_pressure_drop(
|
def _mass_flow_for_pressure_drop(
|
||||||
self,
|
self,
|
||||||
pressure_drop: float,
|
pressure_drop: float,
|
||||||
@@ -1378,6 +1379,7 @@ class AmesimPnl0003(DynamicComponent):
|
|||||||
magnitude = friction * (self.le / self.diam) * density * velocity * velocity / 2.0
|
magnitude = friction * (self.le / self.diam) * density * velocity * velocity / 2.0
|
||||||
return magnitude if mass_flow > 0.0 else -magnitude
|
return magnitude if mass_flow > 0.0 else -magnitude
|
||||||
|
|
||||||
|
@lru_cache(maxsize=32768)
|
||||||
def _mass_flow_for_pressure_drop(
|
def _mass_flow_for_pressure_drop(
|
||||||
self,
|
self,
|
||||||
pressure_drop: float,
|
pressure_drop: float,
|
||||||
|
|||||||
@@ -327,6 +327,14 @@ class PressureFlowSolver:
|
|||||||
selected: self._filter_explicit_flow_plan(selected)
|
selected: self._filter_explicit_flow_plan(selected)
|
||||||
for selected in (frozenset(("f", "m_flow")), frozenset(("f",)))
|
for selected in (frozenset(("f", "m_flow")), frozenset(("f",)))
|
||||||
}
|
}
|
||||||
|
self._explicit_flow_unknowns_by_variables = {
|
||||||
|
selected: tuple(
|
||||||
|
unknown
|
||||||
|
for unknown in self.unknowns
|
||||||
|
if unknown.variable in selected
|
||||||
|
)
|
||||||
|
for selected in self._explicit_flow_plans_by_variables
|
||||||
|
}
|
||||||
self._equation_scale_plans = self._build_equation_scale_plans(
|
self._equation_scale_plans = self._build_equation_scale_plans(
|
||||||
self._equation_templates
|
self._equation_templates
|
||||||
)
|
)
|
||||||
@@ -1519,6 +1527,25 @@ class PressureFlowSolver:
|
|||||||
|
|
||||||
return read_component_equation
|
return read_component_equation
|
||||||
|
|
||||||
|
def _connection_flow_target_reader(
|
||||||
|
self,
|
||||||
|
equation,
|
||||||
|
unknown: AlgebraicUnknown,
|
||||||
|
) -> Callable[[], float]:
|
||||||
|
if equation.relation != "sumToZero" or len(equation.variables) != 2:
|
||||||
|
raise ValueError(
|
||||||
|
f"Connection flow equation {equation.id} must sum two variables."
|
||||||
|
)
|
||||||
|
first = self._unknowns_by_id[equation.variables[0]]
|
||||||
|
second = self._unknowns_by_id[equation.variables[1]]
|
||||||
|
if unknown is first:
|
||||||
|
return lambda: 0.0 - (0.0 + second.read())
|
||||||
|
if unknown is second:
|
||||||
|
return lambda: 0.0 - (first.read() + 0.0)
|
||||||
|
raise ValueError(
|
||||||
|
f"Connection flow equation {equation.id} does not contain {unknown.id}."
|
||||||
|
)
|
||||||
|
|
||||||
def _pressure_flow_equation_values(self) -> tuple[float, ...]:
|
def _pressure_flow_equation_values(self) -> tuple[float, ...]:
|
||||||
"""Evaluate live equation values through the compiled topology."""
|
"""Evaluate live equation values through the compiled topology."""
|
||||||
|
|
||||||
@@ -1562,7 +1589,7 @@ class PressureFlowSolver:
|
|||||||
return ExplicitFlowAssignment(
|
return ExplicitFlowAssignment(
|
||||||
equation_id=equation.id,
|
equation_id=equation.id,
|
||||||
unknown=unknown,
|
unknown=unknown,
|
||||||
evaluate=self._equation_value_reader(equation),
|
evaluate=self._connection_flow_target_reader(equation, unknown),
|
||||||
)
|
)
|
||||||
|
|
||||||
evaluation = self._component_equation_plans_by_id[equation.owner_id]
|
evaluation = self._component_equation_plans_by_id[equation.owner_id]
|
||||||
@@ -1747,6 +1774,7 @@ class PressureFlowSolver:
|
|||||||
def _evaluate_explicit_flow_stage(
|
def _evaluate_explicit_flow_stage(
|
||||||
stage: ExplicitFlowStage,
|
stage: ExplicitFlowStage,
|
||||||
) -> tuple[float, ...]:
|
) -> tuple[float, ...]:
|
||||||
|
"""Evaluate simultaneous targets while this stage's unknowns are zero."""
|
||||||
assignments = stage.assignments
|
assignments = stage.assignments
|
||||||
values: list[float | None] = [None] * len(assignments)
|
values: list[float | None] = [None] * len(assignments)
|
||||||
for assignment_index, evaluate in stage.direct_evaluations:
|
for assignment_index, evaluate in stage.direct_evaluations:
|
||||||
@@ -1764,7 +1792,9 @@ class PressureFlowSolver:
|
|||||||
"Compiled algebraic equation disappeared at runtime: "
|
"Compiled algebraic equation disappeared at runtime: "
|
||||||
f"{equation_id}."
|
f"{equation_id}."
|
||||||
)
|
)
|
||||||
values[assignment_index] = float(equation_values[equation_index])
|
values[assignment_index] = 0.0 - float(
|
||||||
|
equation_values[equation_index]
|
||||||
|
)
|
||||||
if any(value is None for value in values):
|
if any(value is None for value in values):
|
||||||
raise RuntimeError("Explicit flow evaluation plan returned no value.")
|
raise RuntimeError("Explicit flow evaluation plan returned no value.")
|
||||||
return tuple(float(value) for value in values)
|
return tuple(float(value) for value in values)
|
||||||
@@ -1776,9 +1806,15 @@ class PressureFlowSolver:
|
|||||||
"""Execute staged flow/force assignments without repeated equations."""
|
"""Execute staged flow/force assignments without repeated equations."""
|
||||||
|
|
||||||
selected = frozenset(variables)
|
selected = frozenset(variables)
|
||||||
for unknown in self.unknowns:
|
reset_unknowns = self._explicit_flow_unknowns_by_variables.get(selected)
|
||||||
if unknown.variable in selected:
|
if reset_unknowns is None:
|
||||||
unknown.write(0.0)
|
reset_unknowns = tuple(
|
||||||
|
unknown
|
||||||
|
for unknown in self.unknowns
|
||||||
|
if unknown.variable in selected
|
||||||
|
)
|
||||||
|
for unknown in reset_unknowns:
|
||||||
|
unknown.write(0.0)
|
||||||
|
|
||||||
seeded_ids: set[str] = set()
|
seeded_ids: set[str] = set()
|
||||||
plan = self._explicit_flow_plans_by_variables.get(selected)
|
plan = self._explicit_flow_plans_by_variables.get(selected)
|
||||||
@@ -1787,14 +1823,7 @@ class PressureFlowSolver:
|
|||||||
for stage in plan:
|
for stage in plan:
|
||||||
assignments = stage.assignments
|
assignments = stage.assignments
|
||||||
values = self._evaluate_explicit_flow_stage(stage)
|
values = self._evaluate_explicit_flow_stage(stage)
|
||||||
targets = tuple(
|
for assignment, target_value in zip(assignments, values):
|
||||||
(
|
|
||||||
assignment,
|
|
||||||
assignment.unknown.read() - value,
|
|
||||||
)
|
|
||||||
for assignment, value in zip(assignments, values)
|
|
||||||
)
|
|
||||||
for assignment, target_value in targets:
|
|
||||||
if not isfinite(target_value):
|
if not isfinite(target_value):
|
||||||
continue
|
continue
|
||||||
assignment.unknown.write(target_value)
|
assignment.unknown.write(target_value)
|
||||||
|
|||||||
@@ -779,14 +779,7 @@ class StreamPressureBlockSolver:
|
|||||||
unknown.write(0.0)
|
unknown.write(0.0)
|
||||||
for stage in self._selected_explicit_flow_plan:
|
for stage in self._selected_explicit_flow_plan:
|
||||||
values = solver._evaluate_explicit_flow_stage(stage)
|
values = solver._evaluate_explicit_flow_stage(stage)
|
||||||
targets = tuple(
|
for assignment, target_value in zip(stage.assignments, values):
|
||||||
(
|
|
||||||
assignment,
|
|
||||||
assignment.unknown.read() - value,
|
|
||||||
)
|
|
||||||
for assignment, value in zip(stage.assignments, values)
|
|
||||||
)
|
|
||||||
for assignment, target_value in targets:
|
|
||||||
if isfinite(target_value):
|
if isfinite(target_value):
|
||||||
assignment.unknown.write(target_value)
|
assignment.unknown.write(target_value)
|
||||||
seeded_equation_ids.add(assignment.equation_id)
|
seeded_equation_ids.add(assignment.equation_id)
|
||||||
|
|||||||
@@ -320,6 +320,38 @@ class AmesimPnl0003ComponentTests(unittest.TestCase):
|
|||||||
self.assertLess(abs(above - below), abs(above) * 0.05)
|
self.assertLess(abs(above - below), abs(above) * 0.05)
|
||||||
self.assertEqual(equal, 0.0)
|
self.assertEqual(equal, 0.0)
|
||||||
|
|
||||||
|
def test_exact_repeated_center_flow_inversion_is_cached(self) -> None:
|
||||||
|
pipe = AmesimPnl0003("pnl_3", self.medium)
|
||||||
|
pipe._mass_flow_for_pressure_drop.cache_clear()
|
||||||
|
|
||||||
|
first = pipe._mass_flow_for_pressure_drop(
|
||||||
|
1000.0,
|
||||||
|
density=1.2,
|
||||||
|
temperature=300.0,
|
||||||
|
)
|
||||||
|
after_first = pipe._mass_flow_for_pressure_drop.cache_info()
|
||||||
|
second = pipe._mass_flow_for_pressure_drop(
|
||||||
|
1000.0,
|
||||||
|
density=1.2,
|
||||||
|
temperature=300.0,
|
||||||
|
)
|
||||||
|
after_second = pipe._mass_flow_for_pressure_drop.cache_info()
|
||||||
|
changed_temperature = pipe._mass_flow_for_pressure_drop(
|
||||||
|
1000.0,
|
||||||
|
density=1.2,
|
||||||
|
temperature=400.0,
|
||||||
|
)
|
||||||
|
after_temperature_change = pipe._mass_flow_for_pressure_drop.cache_info()
|
||||||
|
|
||||||
|
self.assertEqual(first, second)
|
||||||
|
self.assertEqual(after_first.misses, 1)
|
||||||
|
self.assertEqual(after_second.hits, after_first.hits + 1)
|
||||||
|
self.assertNotEqual(changed_temperature, second)
|
||||||
|
self.assertEqual(
|
||||||
|
after_temperature_change.misses,
|
||||||
|
after_second.misses + 1,
|
||||||
|
)
|
||||||
|
|
||||||
def test_pressure_flow_residuals_bind_both_port_pressures_to_states(self) -> None:
|
def test_pressure_flow_residuals_bind_both_port_pressures_to_states(self) -> None:
|
||||||
pipe = AmesimPnl0003("pnl_3", self.medium)
|
pipe = AmesimPnl0003("pnl_3", self.medium)
|
||||||
pipe.properties_1()
|
pipe.properties_1()
|
||||||
|
|||||||
@@ -77,6 +77,34 @@ class AmesimPnl00rComponentTests(unittest.TestCase):
|
|||||||
self.assertGreater(pipe.mass_flow(15.3e6 + 0.1, 15.3e6), 0.0)
|
self.assertGreater(pipe.mass_flow(15.3e6 + 0.1, 15.3e6), 0.0)
|
||||||
|
|
||||||
|
|
||||||
|
def test_pressure_drop_inversion_cache_keys_all_property_inputs(self) -> None:
|
||||||
|
pipe = AmesimPnl00r(
|
||||||
|
"pnl_1",
|
||||||
|
self.medium,
|
||||||
|
diam=0.014,
|
||||||
|
le=1.0,
|
||||||
|
rr=0.045 / 14.0,
|
||||||
|
)
|
||||||
|
pipe.port_1.h_outflow = self.medium.specific_enthalpy(300.0)
|
||||||
|
pipe._mass_flow_for_pressure_drop.cache_clear()
|
||||||
|
|
||||||
|
first = pipe.mass_flow(501000.0, 500000.0)
|
||||||
|
after_first = pipe._mass_flow_for_pressure_drop.cache_info()
|
||||||
|
second = pipe.mass_flow(501000.0, 500000.0)
|
||||||
|
after_second = pipe._mass_flow_for_pressure_drop.cache_info()
|
||||||
|
pipe.port_1.h_outflow = self.medium.specific_enthalpy(400.0)
|
||||||
|
third = pipe.mass_flow(501000.0, 500000.0)
|
||||||
|
after_temperature_change = pipe._mass_flow_for_pressure_drop.cache_info()
|
||||||
|
|
||||||
|
self.assertEqual(first, second)
|
||||||
|
self.assertEqual(after_first.misses, 1)
|
||||||
|
self.assertEqual(after_second.hits, after_first.hits + 1)
|
||||||
|
self.assertNotEqual(third, second)
|
||||||
|
self.assertEqual(
|
||||||
|
after_temperature_change.misses,
|
||||||
|
after_second.misses + 1,
|
||||||
|
)
|
||||||
|
|
||||||
def test_friction_factor_transitions_from_laminar_to_turbulent(self) -> None:
|
def test_friction_factor_transitions_from_laminar_to_turbulent(self) -> None:
|
||||||
pipe = AmesimPnl00r("pnl_1", self.medium, rr=1e-5)
|
pipe = AmesimPnl00r("pnl_1", self.medium, rr=1e-5)
|
||||||
|
|
||||||
|
|||||||
@@ -100,6 +100,38 @@ class AmesimSignalComponentTests(unittest.TestCase):
|
|||||||
valve.res.signal = -0.5
|
valve.res.signal = -0.5
|
||||||
self.assertAlmostEqual(valve.opening, 0.0)
|
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:
|
def test_signal_resolver_propagates_step_to_valve_input(self) -> None:
|
||||||
network = SimulationNetwork("signal-smoke")
|
network = SimulationNetwork("signal-smoke")
|
||||||
step = AmesimStep0("step_1", self.medium, initial=0.0, final=0.75, time=0.1)
|
step = AmesimStep0("step_1", self.medium, initial=0.0, final=0.75, time=0.1)
|
||||||
|
|||||||
Reference in new issue
Block a user