STAR
Cal STAR — firmware, DAQ server, and engine design
Loading...
Searching...
No Matches
test_pressure_reference.py
Go to the documentation of this file.
1"""A drawing's pressures: absolute ones read as gauge unless they say psia, and
2differences never carry a reference (feedtwin.model.pressure).
3
4Checked against what the drawing's author wrote, not against the code: a tank
5drawn "at 500 psi" sits at 500 on its gauge; a dome loader drawn at 500 and the
6dome knob turned to 500 lock the tanks up at the same pressure; a 50 psi bias is
750 psi whatever reference anyone thinks of.
8"""
9
10from __future__ import annotations
11
12import json
13import tomllib
14from importlib.resources import files
15from pathlib import Path
16
17import pytest
18from test_pid_drawn_freely import P, node
19
20from feedtwin.model.pressure import (
21 ABSOLUTE,
22 DIFFERENCE,
23 PressureReferenceError,
24 drawn_unit,
25)
26from feedtwin.pid import DiagramError, read_diagram
27
28PSI = 6894.757293168361
29ATM = 101325.0
30STAR = Path(__file__).resolve().parents[3]
31
32
33def _read(*nodes: dict) -> dict: # type: ignore[type-arg]
34 drawing = read_diagram({"nodes": list(nodes), "edges": []}, name="t")
35 return {n.id: n.params for n in drawing.nodes}
36
37
39 params = _read(
40 node("T", "TANK", "TK", params={"pressure": P(500, "psi")}),
41 node("B", "KBOTTLE", "KB", params={"pressure": P(300, "bar")}),
42 node("R", "PR", "PR", params={"setpoint": P(560, "psi")}),
43 )
44 assert params["T"]["pressure"].si == pytest.approx(500 * PSI + ATM)
45 assert params["B"]["pressure"].si == pytest.approx(300e5 + ATM)
46 assert params["R"]["setpoint"].si == pytest.approx(560 * PSI + ATM)
47
48
50 params = _read(
51 node("T", "TANK", "TK", params={"pressure": P(514.7, "psia")}),
52 node("U", "TANK", "TK2", params={"pressure": P(1, "atm")}),
53 )
54 assert params["T"]["pressure"].si == pytest.approx(514.7 * PSI)
55 assert params["U"]["pressure"].si == pytest.approx(ATM)
56
57
59 params = _read(
60 node(
61 "R",
62 "PR",
63 "PR",
64 params={"dome_bias": P(50, "psi"), "flow_droop": P(20, "psi")},
65 )
66 )
67 assert params["R"]["dome_bias"].si == pytest.approx(50 * PSI)
68 assert params["R"]["flow_droop"].si == pytest.approx(20 * PSI)
69 with pytest.raises(DiagramError, match="dome_bias is a pressure difference"):
70 _read(node("R", "PR", "PR", params={"dome_bias": P(50, "psig")}))
71 with pytest.raises(PressureReferenceError):
72 drawn_unit("set_pressure", "psia")
73
74
76 params = _read(
77 node("E", "ENGINE", "ENG", params={"chamber_pressure": P(385, "psi")})
78 )
79 assert params["E"]["chamber_pressure"].si == pytest.approx(385 * PSI)
80
81
83 """The case that found this. The shipped stand's control regulator is drawn at
84 450 and the dome-loaded one's dome comes from it; the cockpit's dome knob at
85 450 psig drives the same dome. Read as psia, the drawing's 450 was an atmosphere
86 under the knob's, and the same stand locked up 14.7 psi apart."""
87 from feedtwin.pid import build_network
88 from feedtwin.session.gauge import from_psig
89
90 stand = STAR / "feed-twin" / "backend" / "diagrams" / "ethalox_stand.json"
91 built = build_network(read_diagram(json.loads(stand.read_text()), name="s"))
92 (loader,) = built.dome_loaders.values()
93 loaded = loader.signal.split(".")[0]
94 dome = next(
95 b.component.p["dome_pressure"]
96 for b in built.network.branches.values()
97 if getattr(b.component, "id", "") == loaded
98 )
99 assert dome == pytest.approx(from_psig(450.0))
100 # ...and a plain regulator's drawn setpoint is gauge too.
101 plain = _read(node("R", "PR", "PR", params={"setpoint": P(560, "psi")}))
102 assert plain["R"]["setpoint"].si == pytest.approx(from_psig(560.0))
103
104
106 """The lists in feedtwin.model.pressure are complete: every pressure parameter
107 the component catalogue declares, and every one on a shipped or saved drawing,
108 is either absolute or a difference. A new one fails here until it is placed."""
109 catalogue = tomllib.loads(
110 files("feedtwin.model").joinpath("components.toml").read_text()
111 )
112 names = {
113 name
114 for spec in catalogue.values()
115 if isinstance(spec, dict)
116 for name, param in (spec.get("params") or {}).items()
117 if isinstance(param, dict) and param.get("dimension") == "pressure"
118 }
119 pressure_units = {"psi", "bar", "Pa", "kPa", "MPa", "atm", "psia", "psig"}
120 drawings = list((STAR / "feed-twin" / "backend" / "diagrams").glob("*.json"))
121 for path in drawings:
122 payload = json.loads(path.read_text())
123 for item in payload.get("nodes", []) + payload.get("edges", []):
124 for name, param in ((item.get("data") or {}).get("params") or {}).items():
125 if isinstance(param, dict) and param.get("unit") in pressure_units:
126 names.add(name)
127 assert names, "found no pressure parameters to check"
128 unplaced = sorted(names - ABSOLUTE - DIFFERENCE)
129 assert not unplaced, f"place these in feedtwin.model.pressure: {unplaced}"
130 assert not ABSOLUTE & DIFFERENCE
None test_a_drawn_dome_and_the_dome_knob_at_the_same_number_agree()
None test_a_difference_is_the_same_size_and_refuses_a_reference()
None test_a_bare_absolute_pressure_is_what_its_gauge_reads()
None test_every_pressure_the_catalogue_and_the_drawings_use_is_classified()