STAR
Cal STAR — firmware, DAQ server, and engine design
Loading...
Searching...
No Matches
test_regulator.py
Go to the documentation of this file.
1"""The regulator: spec-sheet droop, and the two effects it is made of.
2
3Everything here is checked against numbers a datasheet actually prints. The
4model has no geometry in it on purpose -- see
5:mod:`feedtwin.comps.regulator` for why a diaphragm force balance was
6abandoned rather than deferred out of laziness.
7
8The team's dome regulator is the worked example throughout: the Aqua
9Environment 1092-50, 500 psi setpoint, 17 psi of outlet rise per 1000 psi of
10inlet decay -- declared in the datasheet's own units, not as a bare ratio.
11"""
12
13from __future__ import annotations
14
15import math
16
17import pytest
18from fluids.fittings import Cv_to_K
19
20from feedtwin.comps import FlowConditions, build_component
21from feedtwin.comps.regulator import SUPPLY_ZERO, IdealRegulator, Regulator
22from feedtwin.model import ComponentInstance, Param, Provenance
23from feedtwin.model.spec import SpecError
24from feedtwin.model.units import get_unit
25
26#: Exact, from the library's own table -- a truncated 6894.757 leaves a
27#: 0.02 Pa round-trip error that these tolerances would otherwise chase.
28PSI = get_unit("psi").factor
29
30#: GN2 at roughly 500 psi and 293 K. Enough to exercise the seat term.
31GAS = FlowConditions(rho=39.0, mu=1.78e-5, p_upstream=4500 * PSI, temperature=293.15)
32
33M = Provenance.MANUFACTURER
34
35
36def make(model: str = "droop", **overrides: object) -> Regulator:
37 params = {
38 "setpoint": Param(500.0, "psi", Provenance.MANUFACTURER, "PR-01 datasheet"),
39 "supply_coefficient": Param(
40 17.0,
41 "psi/1000psi",
42 Provenance.MANUFACTURER,
43 "Aqua 1092-50: 17 psi per 1000 psi inlet",
44 ),
45 "Cv": Param(0.8, "Cv", Provenance.MANUFACTURER, "datasheet"),
46 "bore": Param(7.75, "mm", Provenance.MANUFACTURER, "3/8 seat"),
47 }
48 for key, value in overrides.items():
49 params[key] = value # type: ignore[assignment]
50 built = build_component(
51 ComponentInstance.build("PR-01", "regulator", params, model=model)
52 )
53 assert isinstance(built, Regulator)
54 return built
55
56
57def outlet(reg: Regulator, mdot: float, p_in: float) -> float:
58 flow = FlowConditions(rho=39.0, mu=1.78e-5, p_upstream=p_in, temperature=293.15)
59 return p_in - reg.pressure_drop(mdot, flow)
60
61
62def gauge(psig: float) -> float:
63 """An inlet the way the stand reads it [psig] -> absolute [Pa]."""
64 return psig * PSI + SUPPLY_ZERO
65
66
67# ------------------------------------------------------- supply-pressure effect
68
69
71 """dome + bias - S x inlet, the inlet in gauge: a 1092-50 loaded to 500 with
72 4000 psi of COPV behind it holds 500 + 50 - 4 x 14.7 = 491.2 psi. Not the
73 setpoint at some charge pressure and the effect only beyond it (the team,
74 2026-10-07)."""
75 reg = make(
76 supply_coefficient=Param(14.7, "psi/1000psi", M, "LE4 drawing"),
77 dome_pressure=Param(500.0, "psi", M, "dome"),
78 dome_bias=Param(50.0, "psi", M, "1092-50"),
79 )
80 assert outlet(reg, 0.01, gauge(4000.0)) == pytest.approx(491.2 * PSI, rel=1e-9)
81 # The law's own zero: what it would hold with nothing behind it.
82 empty = FlowConditions(
83 rho=1.2, mu=1.78e-5, p_upstream=gauge(0.0), temperature=293.15
84 )
85 assert reg.outlet_setpoint(0.0, empty) == pytest.approx(550.0 * PSI, rel=1e-9)
86
87
89 """The sign people guess wrong: outlet goes UP as inlet goes DOWN."""
90 reg = make()
91 assert outlet(reg, 0.01, 1500 * PSI) > outlet(reg, 0.01, 4500 * PSI)
92
93
95 """17 psi per 1000 psi, checked as the quoted rate and over a full decay."""
96 reg = make()
97 per_1000 = outlet(reg, 0.01, 3500 * PSI) - outlet(reg, 0.01, 4500 * PSI)
98 assert per_1000 / PSI == pytest.approx(17.0, rel=1e-6)
99
100 # Over a real COPV decay, 4500 -> 1500 psig: 51 psi up, from 423.5 to 474.5.
101 full, empty = outlet(reg, 0.01, gauge(4500.0)), outlet(reg, 0.01, gauge(1500.0))
102 assert full / PSI == pytest.approx(500.0 - 4.5 * 17.0, rel=1e-9)
103 assert (empty - full) / PSI == pytest.approx(51.0, rel=1e-6)
104
105
107 """psi/1000psi, psi/100psi and psi/psi are the same number, and `-` is not.
108
109 The whole point of giving this parameter its own dimension: a bare ratio
110 reads the same whether the author meant 17 per thousand or 17 per hundred,
111 and the solver cannot tell. Spelling the unit out makes the three agree and
112 makes the fourth an error at load.
113 """
114 at = lambda p: outlet(p, 0.01, 1500 * PSI)
115 a = at(make(supply_coefficient=Param(17.0, "psi/1000psi", M, "")))
116 b = at(make(supply_coefficient=Param(1.7, "psi/100psi", M, "")))
117 c = at(make(supply_coefficient=Param(0.017, "psi/psi", M, "")))
118 assert a == pytest.approx(b) == pytest.approx(c)
119
120 with pytest.raises(SpecError, match="dimensionless"):
121 make(supply_coefficient=Param(0.017, "-", M, "bare ratio"))
122
123
125 """An old drawing's ``inlet_reference`` still loads, moves nothing, and
126 check() says to drop it."""
127 drawn = make(inlet_reference=Param(4500.0, "psi", M, "COPV service pressure"))
128 plain = make()
129 for p_in in (gauge(0.0), gauge(1500.0), gauge(4500.0)):
130 assert outlet(drawn, 0.01, p_in) == outlet(plain, 0.01, p_in)
131 assert "inlet_reference" in [v.limit for v in drawn.check()]
132 assert "inlet_reference" not in [v.limit for v in plain.check()]
133
134
135# ------------------------------------------------------------------ flow droop
136
137
139 """Cv does not produce it. This is the correction Rev D of the plan needed.
140
141 At a tenth of rated flow the wide-open seat loses a couple of psi while a
142 real unit has already drooped by ten. If Cv alone produced droop, these two
143 regulators -- identical but for ``flow_droop`` -- would agree.
144 """
145 without = make()
146 with_droop = make(
147 flow_droop=Param(10.0, "psi", Provenance.MANUFACTURER, "droop at rated"),
148 rated_flow=Param(0.10, "kg/s", Provenance.MANUFACTURER, "rated"),
149 )
150 p_without = outlet(without, 0.01, 4500 * PSI)
151 p_with = outlet(with_droop, 0.01, 4500 * PSI)
152 assert p_without - p_with == pytest.approx(1.0 * PSI, rel=1e-6)
153
154
156 reg = make(
157 flow_droop=Param(10.0, "psi", Provenance.MANUFACTURER, "droop"),
158 rated_flow=Param(0.10, "kg/s", Provenance.MANUFACTURER, "rated"),
159 )
160 # Referenced to zero flow, where the droop term is exactly zero. A small
161 # non-zero reference carries its own droop and biases every comparison.
162 base = outlet(reg, 0.0, 4500 * PSI)
163 for fraction in (0.25, 0.5, 1.0):
164 sag = base - outlet(reg, fraction * 0.10, 4500 * PSI)
165 assert sag / PSI == pytest.approx(10.0 * fraction, rel=1e-3)
166
167
169 reg = make(flow_droop=Param(10.0, "psi", Provenance.MANUFACTURER, "droop"))
170 assert "rated_flow" in [v.limit for v in reg.check()]
171
172
174 """Inlet decay lifts the outlet; flow pulls it down. Both, at once."""
175 reg = make(
176 flow_droop=Param(51.0, "psi", Provenance.MANUFACTURER, "droop at rated"),
177 rated_flow=Param(0.10, "kg/s", Provenance.MANUFACTURER, "rated"),
178 )
179 # 1500 psi in the bottle holds it 25.5 under; full rated flow pulls 51 more.
180 assert outlet(reg, 0.10, gauge(1500.0)) / PSI == pytest.approx(423.5, rel=1e-3)
181 # Emptying the bottle by 3000 psi gives back exactly what rated flow costs.
182 assert outlet(reg, 0.10, gauge(1500.0)) == pytest.approx(
183 outlet(reg, 0.0001, gauge(4500.0)), rel=2e-3
184 )
185
186
187# --------------------------------------------------------------- limits
188
189
191 """Inlet below setpoint: the outlet follows the inlet down.
192
193 It does not track the inlet *exactly*, and that is right rather than a
194 rounding error -- a wide-open regulator is still a restriction, so the
195 outlet sits one seat-loss below the inlet. What must never happen is an
196 outlet above the inlet, which is what a naive ``p_in - (p_in - setpoint)``
197 would produce the moment the bottle falls below setpoint.
198 """
199 reg = make()
200 flow = FlowConditions(39.0, 1.78e-5, 300 * PSI)
201 dp = reg.pressure_drop(0.01, flow)
202 assert dp > 0.0
203 assert outlet(reg, 0.01, 300 * PSI) < 300 * PSI
204
205 # The drop is exactly the wide-open seat's, nothing more.
206 seat = (
207 Cv_to_K(reg.p["Cv"], reg.p["bore"])
208 * 0.5
209 * 39.0
210 * (0.01 / (39.0 * math.pi * reg.p["bore"] ** 2 / 4.0)) ** 2
211 )
212 assert dp == pytest.approx(seat, rel=1e-9)
213
214
216 """What a downstream relief valve actually sees between firings."""
217 reg = make(lockup_rise=Param(25.0, "psi", Provenance.MANUFACTURER, "lockup"))
218 # 500 - 4.5 x 17 + 25 of seat creep.
219 assert outlet(reg, 0.0, gauge(4500.0)) / PSI == pytest.approx(448.5, rel=1e-9)
220 # And it is above where the regulator holds while flowing, which is the point.
221 flow = FlowConditions(39.0, 1.78e-5, gauge(4500.0))
222 assert outlet(reg, 0.0, gauge(4500.0)) > reg.outlet_setpoint(0.0, flow)
223
224
226 """Past its capacity a regulator is a hole, and should say so."""
227 reg = make()
228 small = FlowConditions(39.0, 1.78e-5, 4500 * PSI)
229 assert not reg.is_saturated(0.01, small)
230 assert reg.is_saturated(2.0, small)
231
232 violations = reg.envelope_violations(2.0, small)
233 assert any(v.limit == "capacity" for v in violations)
234
235
237 reg = make()
238 flow = FlowConditions(39.0, 1.78e-5, 600 * PSI)
239 # At a flow the seat cannot pass, the drop is the seat's, not the setpoint's.
240 dp = reg.pressure_drop(0.5, flow)
241 assert dp > 600 * PSI - 500 * PSI
242 assert reg.is_saturated(0.5, flow)
243
244
246 """A datasheet droop is quoted at its rated flow. LE4's regulator ran at
247 2.7-3.2x its (default) 0.05 kg/s for the whole burn, extrapolating 54-64 psi
248 of droop, and nothing said so. The physics stays linear; the envelope says
249 how far past the characterisation it is."""
250 reg = make(
251 flow_droop=Param(20.0, "psi", M, "droop at rated"),
252 rated_flow=Param(0.05, "kg/s", M, "rated"),
253 )
254 flow = FlowConditions(39.0, 1.78e-5, 4500 * PSI)
255 assert not any(v.limit == "rated_flow" for v in reg.envelope_violations(0.05, flow))
256 past = [v for v in reg.envelope_violations(0.1355, flow) if v.limit == "rated_flow"]
257 assert past and past[0].severity == "warning"
258 assert "2.71x" in past[0].detail and "54.2 psi" in past[0].detail
259 assert reg.diagnostics(0.1355, flow)["rated_multiple"] == pytest.approx(2.71)
260 # Not clamped: the droop at 2.71x is still 2.71 x 20 psi.
261 at_rated = reg.outlet_setpoint(0.05, flow)
262 assert at_rated - reg.outlet_setpoint(0.1355, flow) == pytest.approx(
263 1.71 * 20.0 * PSI
264 )
265
266
268 """The droop scales with rated_flow; a fallback one is as unmeasured as a
269 fallback droop."""
270 reg = make(
271 flow_droop=Param(20.0, "psi", M, "droop at rated"),
272 rated_flow=Param(0.05, "kg/s", Provenance.DEFAULT, "fallback"),
273 )
274 warnings = [v for v in reg.check() if v.limit == "rated_flow"]
275 assert warnings and warnings[0].severity == "warning"
276 measured = make(
277 flow_droop=Param(20.0, "psi", M, "droop at rated"),
278 rated_flow=Param(0.05, "kg/s", M, "rated"),
279 )
280 assert not [v for v in measured.check() if v.limit == "rated_flow"]
281
282
284 reg = make(
285 min_inlet_differential=Param(50.0, "psi", Provenance.MANUFACTURER, "dropout")
286 )
287 ok = reg.envelope_violations(0.01, FlowConditions(39.0, 1.78e-5, 4500 * PSI))
288 assert not any(v.limit == "min_inlet_differential" for v in ok)
289
290 starved = reg.envelope_violations(0.01, FlowConditions(39.0, 1.78e-5, 520 * PSI))
291 assert any(v.limit == "min_inlet_differential" for v in starved)
292
293
295 """An unstated coefficient must not look like a perfect regulator."""
296 reg = make(
297 supply_coefficient=Param(0.0, "psi/psi", Provenance.DEFAULT, "not filled in"),
298 )
299 warnings = [v for v in reg.check() if v.limit == "supply_coefficient"]
300 assert warnings and warnings[0].severity == "warning"
301
302
303# ------------------------------------------------------------ fidelity models
304
305
307 """Distinguishable from a droop model with zero coefficients, on purpose."""
308 reg = make(model="ideal")
309 assert isinstance(reg, IdealRegulator)
310 assert outlet(reg, 0.01, 4500 * PSI) == pytest.approx(500 * PSI)
311 assert outlet(reg, 5.00, 1000 * PSI) == pytest.approx(500 * PSI)
312 assert not reg.is_saturated(5.0, GAS)
313
314
316 flat = make(supply_coefficient=Param(0.0, "psi/psi", M, "none"))
317 assert outlet(flat, 0.01, 4500 * PSI) == pytest.approx(
318 outlet(make(model="ideal"), 0.01, 4500 * PSI), rel=1e-9
319 )
320
321
323 reg = make()
324 out = reg.diagnostics(0.01, FlowConditions(39.0, 1.78e-5, gauge(1500.0)))
325 assert out["droop_from_setpoint"] / PSI == pytest.approx(-25.5, abs=0.1)
326 assert out["outlet_actual"] == pytest.approx(out["outlet_target"], rel=1e-9)
327 assert out["saturated"] == 0.0
328
329
330# ---------------------------------------------------- dome loading and bias
331
332
333def dome_loaded(**overrides: object) -> Regulator:
334 return make(
335 dome_pressure=Param(450.0, "psi", M, "control regulator setting"),
336 dome_bias=Param(50.0, "psi", M, "Aqua Environment 1092-50, +50 psi"),
337 **overrides, # type: ignore[arg-type]
338 )
339
340
342 """The 1092-50 delivers 50 psi above whatever its dome is loaded to."""
343 reg = dome_loaded()
344 assert reg.commanded_setpoint(GAS) / PSI == pytest.approx(500.0, rel=1e-9)
345 # And that, not the configured `setpoint`, is what it holds -- less 4.5 x 17
346 # for the bottle behind it.
347 assert outlet(reg, 0.0001, gauge(4500.0)) / PSI == pytest.approx(423.5, abs=0.1)
348
349
351 """Two regulators, same dome, different bias. They must differ by the bias.
352
353 Folding the bias into the dome setting would make every scenario responsible
354 for remembering to add 50 psi, and one day one of them would not.
355 """
356 biased = dome_loaded()
357 unbiased = make(dome_pressure=Param(450.0, "psi", M, "same dome"))
358 difference = biased.commanded_setpoint(GAS) - unbiased.commanded_setpoint(GAS)
359 assert difference / PSI == pytest.approx(50.0, rel=1e-9)
360
361
363 """The dome is a signal, because on the stand it is a hand on a knob."""
364 reg = dome_loaded()
365
366 def at_dome(psi_value: float) -> float:
367 flow = FlowConditions(
368 rho=39.0,
369 mu=1.78e-5,
370 p_upstream=4500 * PSI,
371 signals={"PR-01.dome": psi_value * PSI},
372 )
373 return reg.commanded_setpoint(flow) / PSI
374
375 assert at_dome(400.0) == pytest.approx(450.0, rel=1e-9)
376 assert at_dome(520.0) == pytest.approx(570.0, rel=1e-9)
377 # Turning the control regulator down really does lower the outlet.
378 assert at_dome(400.0) < at_dome(450.0) < at_dome(520.0)
379
380
382 reg = dome_loaded()
383 flow = FlowConditions(
384 rho=39.0,
385 mu=1.78e-5,
386 p_upstream=4500 * PSI,
387 signals={"PR-01.dome": 300.0 * PSI},
388 )
389 assert reg.commanded_setpoint(flow) / PSI == pytest.approx(350.0, rel=1e-9)
390
391
393 """Both droop terms are relative to what the regulator was *told* to hold."""
394 reg = dome_loaded(
395 flow_droop=Param(20.0, "psi", M, "at rated"),
396 rated_flow=Param(0.05, "kg/s", M, "rated"),
397 )
398 flow = FlowConditions(
399 rho=39.0,
400 mu=1.78e-5,
401 p_upstream=gauge(4500.0),
402 signals={"PR-01.dome": 400.0 * PSI},
403 )
404 commanded = reg.commanded_setpoint(flow) / PSI
405 assert commanded == pytest.approx(450.0)
406 # Half of rated flow costs half the droop, measured from the commanded value
407 # (less the bottle's 4.5 x 17).
408 dp = reg.pressure_drop(0.025, flow)
409 held = (gauge(4500.0) - dp) / PSI
410 assert held == pytest.approx(commanded - 76.5 - 10.0, abs=0.1)
411
412
414 """What a downstream relief sees between firings moves with the knob."""
415 reg = dome_loaded(lockup_rise=Param(25.0, "psi", M, "seat creep"))
416 flow = FlowConditions(
417 rho=39.0,
418 mu=1.78e-5,
419 p_upstream=gauge(4500.0),
420 signals={"PR-01.dome": 400.0 * PSI},
421 )
422 assert reg.lockup_pressure(flow) / PSI == pytest.approx(475.0 - 76.5, rel=1e-9)
423
424
425# ------------------------------------------------- the outlet across zero flow
426
427
428def outlet_either_side(reg: Regulator, p_in: float) -> tuple[float, float, float]:
429 """Outlet just backwards, at, and just forwards of zero flow [psi]."""
430 flow = FlowConditions(rho=39.0, mu=1.78e-5, p_upstream=p_in, temperature=293.15)
431 return tuple( # type: ignore[return-value]
432 (p_in - reg.total_dp(m, flow)) / PSI for m in (-1.0e-12, 0.0, 1.0e-12)
433 )
434
435
437 """The supply effect is the inlet pushing on the poppet; it does not stop when the
438 flow does. Leaving it out of lockup put a step of the whole supply term in the
439 branch at zero flow -- 76 psi with a full bottle, now that it is measured from
440 zero -- and a tank inside that step has no root.
441 """
442 reg = make()
443 for p_in, expected in ((2000.0, 466.0), (4500.0, 423.5)):
444 back, zero, ahead = outlet_either_side(reg, gauge(p_in))
445 assert zero == pytest.approx(expected, abs=1e-6)
446 # 1e-12 kg/s backwards costs REVERSE_STIFFNESS * 1e-12 = 0.01 Pa.
447 assert back == pytest.approx(zero, abs=1e-3)
448 assert ahead == pytest.approx(zero, abs=1e-3)
449
450
452 reg = make(lockup_rise=Param(25.0, "psi", M, "seat creep"))
453 _, zero, _ = outlet_either_side(reg, gauge(2000.0))
454 assert zero == pytest.approx(466.0 + 25.0, abs=1e-6)
455
456
458 """A zero coefficient or the ideal model: nothing comes off for the inlet."""
459 zero = Param(0.0, "psi/psi", M, "none")
460 for reg in (make(supply_coefficient=zero), make("ideal")):
461 for p_in in (900.0, 2000.0, 4500.0):
462 assert outlet_either_side(reg, gauge(p_in))[1] == pytest.approx(500.0)
463
464
466 """A 50 psi offset that silently is not applied is worse than none."""
467 reg = make(dome_bias=Param(50.0, "psi", M, "1092-50"))
468 assert "dome_bias" in [v.limit for v in reg.check()]
469
470
472 reg = make(
473 model="ideal",
474 dome_pressure=Param(450.0, "psi", M, "dome"),
475 dome_bias=Param(50.0, "psi", M, "bias"),
476 )
477 assert outlet(reg, 0.01, 4500 * PSI) / PSI == pytest.approx(500.0, rel=1e-9)
478
479
481 """With zero flow droop the branch equation ``(p_up - p_dn) - dp(mdot) = 0``
482 is satisfied for *every* mass flow, so its Jacobian row is identically zero
483 and the flow is genuinely undetermined. ``check()`` must say so before
484 anyone spends an afternoon on a solve that will not converge. (Found by the
485 old transient integrator, and kept when it went.)
486 """
487 params = {
488 "setpoint": Param(500.0, "psi", M, "x"),
489 "supply_coefficient": Param(17.0, "psi/1000psi", M, "x"),
490 "Cv": Param(0.8, "Cv", M, "x"),
491 "bore": Param(7.75, "mm", M, "x"),
492 }
493 flat = build_component(
494 ComponentInstance.build("PR-01", "regulator", params, model="droop")
495 )
496 flow = FlowConditions(rho=39.0, mu=1.78e-5, p_upstream=4500 * PSI)
497
498 h = 1e-8
499 slope = (
500 flat.pressure_drop(0.01 + h, flow) - flat.pressure_drop(0.01 - h, flow)
501 ) / (2 * h)
502 assert slope == 0.0
503 assert "flow_droop" in [v.limit for v in flat.check()]
504
505 drooping = build_component(
506 ComponentInstance.build(
507 "PR-02",
508 "regulator",
509 {
510 **params,
511 "flow_droop": Param(20.0, "psi", M, "at rated"),
512 "rated_flow": Param(0.05, "kg/s", M, "rated"),
513 },
514 model="droop",
515 )
516 )
517 slope = (
518 drooping.pressure_drop(0.01 + h, flow) - drooping.pressure_drop(0.01 - h, flow)
519 ) / (2 * h)
520 assert slope == pytest.approx(20.0 * PSI / 0.05, rel=1e-3)
521 assert "flow_droop" not in [v.limit for v in drooping.check()]
T & at(const key_type &key)
Definition json.hpp:19110
None test_a_signal_beats_the_configured_dome()
None test_an_assumed_rated_flow_is_flagged()
None test_the_two_effects_oppose_each_other()
None test_lockup_sets_the_no_flow_outlet()
None test_the_control_regulator_can_be_turned_during_a_run()
None test_a_regulator_never_raises_pressure()
None test_an_ideal_regulator_still_honours_the_dome()
None test_lockup_follows_the_dome()
None test_droop_without_a_rated_flow_is_flagged()
None test_saturated_outlet_is_set_by_the_seat()
None test_diagnostics_report_the_droop_from_setpoint()
float outlet(Regulator reg, float mdot, float p_in)
None test_assumed_droop_is_flagged_as_a_missing_measurement()
None test_a_perfect_regulator_makes_its_branch_indeterminate()
None test_the_supply_effect_is_measured_from_zero_inlet()
None test_a_dome_regulator_outlet_is_dome_plus_bias()
None test_flow_droop_is_linear_in_rated_fraction()
None test_the_datasheet_rate_can_be_written_three_ways()
None test_saturation_is_reported_not_hidden()
None test_with_no_supply_effect_lockup_is_the_setpoint()
None test_supply_effect_matches_the_datasheet_number()
float gauge(float psig)
None test_lockup_carries_the_supply_effect_so_the_outlet_is_continuous()
None test_a_bias_with_no_dome_is_flagged()
None test_dropout_below_minimum_differential()
Regulator make(str model="droop", **object overrides)
None test_flow_droop_is_a_separate_coefficient()
None test_droop_and_ideal_agree_with_no_supply_effect()
None test_droop_rides_on_the_dome_setting_not_on_the_configured_setpoint()
None test_running_past_the_rated_flow_is_flagged_not_clamped()
None test_a_drawn_inlet_reference_is_not_used_and_is_flagged()
tuple[float, float, float] outlet_either_side(Regulator reg, float p_in)
Regulator dome_loaded(**object overrides)
None test_ideal_model_is_flat_and_named()
None test_lockup_still_adds_the_seat_creep()
None test_outlet_rises_as_the_bottle_empties()
None test_the_bias_is_not_folded_into_the_setpoint()