STAR
Cal STAR — firmware, DAQ server, and engine design
Loading...
Searching...
No Matches
test_vessel_step_retry.py
Go to the documentation of this file.
1"""A vessel step that halves itself still integrates the whole step.
2
3``TankSim._one_step`` halves a piece the equation of state refuses and tries
4again. It used to return on the first piece that landed, so a step that
5retried once integrated half its ``dt`` and dropped the rest of the liquid
6outflow and gas exchange on the floor -- during vents and near-empty ullages,
7exactly where mass matters. Checked against the arithmetic of the inputs: the
8ullage's mass rate is the net gas flow, and the liquid's is the outflow.
9"""
10
11from __future__ import annotations
12
13from typing import Any
14
15import pytest
16
17from feedtwin.props import Fluid
18from feedtwin.session.core import TankSim
19from feedtwin.session.gauge import from_psig
20from feedtwin.vessels import CylindricalTank, Tank, TankState
21
22
23def _sim() -> TankSim:
24 tank = Tank(
25 Fluid("ethanol"),
26 Fluid("nitrogen"),
27 CylindricalTank(diameter=0.152, barrel_length=0.40),
28 )
29 state = tank.initial_state(
30 pressure=from_psig(400.0),
31 liquid_mass=4.0,
32 liquid_temperature=293.15,
33 gas_temperature=293.15,
34 )
35 return TankSim(
36 id="T",
37 label="T",
38 tank=tank,
39 state=state,
40 ullage_node="u",
41 outlet_node="o",
42 )
43
44
45def _refuse(sim: TankSim, times: int, monkeypatch: pytest.MonkeyPatch) -> list[int]:
46 """Make the tank refuse the first ``times`` candidates a step proposes.
47
48 Only candidates: the rates evaluation prices the state the sim is at,
49 which the equation of state has already accepted.
50 """
51 real = sim.tank.pressure
52 refused = [0]
53
54 def pressure(state: TankState) -> Any:
55 if state is not sim.state and refused[0] < times:
56 refused[0] += 1
57 raise ValueError("refused for the test")
58 return real(state)
59
60 monkeypatch.setattr(sim.tank, "pressure", pressure)
61 return refused
62
63
64@pytest.mark.parametrize("times", [0, 1, 3])
66 times: int, monkeypatch: pytest.MonkeyPatch
67) -> None:
68 sim = _sim()
69 refused = _refuse(sim, times, monkeypatch)
70 before = sim.state
71 dt, liquid_out, net_gas = 0.05, 2.0, 0.01
72
73 sim._one_step(dt, liquid_out, net_gas, sim.tank.gas_enthalpy(before))
74
75 assert refused[0] == times
76 assert before.liquid_mass - sim.state.liquid_mass == pytest.approx(
77 liquid_out * dt, rel=1e-12
78 )
79 assert sim.state.ullage.mass - before.ullage.mass == pytest.approx(
80 net_gas * dt, rel=1e-9
81 )
82
83
85 """The common case is unchanged bit for bit: one Euler step of ``dt``."""
86 sim = _sim()
87 before = sim.state
88 dt, liquid_out, net_gas = 0.05, 2.0, 0.01
89 h = sim.tank.gas_enthalpy(before)
90 rates = sim.tank.rates(
91 before, mdot_liquid_out=liquid_out, mdot_gas_in=net_gas, enthalpy_gas_in=h
92 )
93 expected = sim.tank.step(before, rates, dt)
94
95 sim._one_step(dt, liquid_out, net_gas, h)
96
97 assert sim.state.liquid_mass == expected.liquid_mass
98 assert sim.state.ullage == expected.ullage
list[int] _refuse(TankSim sim, int times, pytest.MonkeyPatch monkeypatch)
None test_a_retried_step_moves_all_of_its_mass(int times, pytest.MonkeyPatch monkeypatch)