STAR
Cal STAR — firmware, DAQ server, and engine design
Loading...
Searching...
No Matches
test_line_walls.py
Go to the documentation of this file.
1"""Which lines get a wall: `Session._build_line_walls` against the drawing.
2
3The wall model itself is held in ``test_wall.py``. This holds the build: a line
4the drawing itemised into segments is solved as a ``segmented`` pipe whose bore
5and length live in its segments, not in ``p`` (``_SUPERSEDED_BY_SEGMENTS``).
6Reading only ``p["bore"]`` left every such line bare, which on the LE4 (3)
7stand -- every line itemised -- made the default-on fitting heat a silent
8no-op in Hotbench and Layer X alike.
9"""
10
11from __future__ import annotations
12
13import math
14from pathlib import Path
15from types import SimpleNamespace
16from typing import Any
17
18import pytest
19
20from feedtwin.comps.wall import fitting_metal
21from feedtwin.pid import build_network, read_diagram
22from feedtwin.session.core import Session
23
24HERE = Path(__file__).resolve().parent
25LE4 = HERE / "fixtures" / "le4_rocket_and_gse.json"
26
27#: The LE4 (3) run from the regulator to the ox tank, as pid-designer saves it:
28#: 727.08 mm of 0.43 in. bore tube, three sketched 45 degree bends of 29.92 mm
29#: each, and the line-level numbers the sketch writes on save (length 0.817 m
30#: end to end, fitting_count 3 counted off the segment list).
31BORE_MM = 10.922
32TUBE_MM = 727.08
33BEND_MM = 29.923670025442775
34
35
36def _measured(value: float, unit: str) -> dict[str, object]:
37 return {"value": value, "unit": unit, "source": "measured"}
38
39
40def _drawing(segmented: bool, **line: dict[str, object]) -> dict[str, object]:
41 params: dict[str, object] = {
42 "length": _measured((TUBE_MM + 3 * BEND_MM) / 1000.0, "m"),
43 "bore": _measured(BORE_MM, "mm"),
44 "fitting_count": _measured(3, "-"),
45 **line,
46 }
47 data: dict[str, object] = {"lineType": "pipe", "params": params}
48 if segmented:
49 data["segments"] = [
50 {
51 "id": "seg_1",
52 "method": "itemised",
53 "bore": _measured(BORE_MM, "mm"),
54 "length": _measured(TUBE_MM, "mm"),
55 "lengthBasis": "tube",
56 "fittings": [
57 {"id": f"fit_{i}", "kind": "bend", "count": 1, "lengthMm": BEND_MM}
58 for i in range(3)
59 ],
60 }
61 ]
62 return {
63 "name": "one line",
64 "nodes": [
65 {
66 "id": "n1",
67 "type": "KBOTTLE",
68 "data": {
69 "label": "K-01",
70 "fluidType": "nitrogen",
71 "params": {
72 "pressure": _measured(2000.0, "psi"),
73 "volume": _measured(50.0, "L"),
74 },
75 },
76 },
77 {"id": "n2", "type": "VENT", "data": {"label": "V-01"}},
78 ],
79 "edges": [{"id": "L1", "source": "n1", "target": "n2", "data": data}],
80 }
81
82
83def _walls(doc: dict[str, object]) -> dict[str, Any]:
84 """`_build_line_walls` on the drawing's network, nothing else of a session."""
85 built = build_network(read_diagram(doc))
86 stub: Any = SimpleNamespace(model=SimpleNamespace(built=built))
87 Session._build_line_walls(stub)
88 return dict(stub.walls)
89
90
92 """The same run, itemised or not, carries the same metal and the same area."""
93 plain = _walls(_drawing(segmented=False))
94 itemised = _walls(_drawing(segmented=True))
95 assert set(plain) == {"L1"}
96 assert set(itemised) == {"L1"}, "the itemised run was left bare"
97 a, b = plain["L1"], itemised["L1"]
98 assert b.bore == pytest.approx(a.bore, rel=1e-12)
99 assert b.mass == pytest.approx(a.mass, rel=1e-12)
100 # pid-designer rounds the line-level length to the millimetre; the segments
101 # are exact, so the areas agree to that rounding and no further.
102 assert b.area == pytest.approx(a.area, rel=1e-3)
103
104
106 """Worked by hand: three 1/2 in.-class unions scaled to a 10.922 mm bore."""
107 wall = _walls(_drawing(segmented=True))["L1"]
108 bore = BORE_MM / 1000.0
109 assert wall.mass == pytest.approx(fitting_metal(3, bore))
110 assert wall.mass == pytest.approx(3 * 0.113 * (10.922 / 10.92) ** 2, rel=1e-9)
111 run = (TUBE_MM + 3 * BEND_MM) / 1000.0
112 assert wall.area == pytest.approx(math.pi * bore * run, rel=1e-12)
113
114
116 thickness = _measured(0.89, "mm")
117 plain = _walls(_drawing(segmented=False, wall_thickness=thickness))["L1"]
118 itemised = _walls(_drawing(segmented=True, wall_thickness=thickness))["L1"]
119 assert itemised.mass > fitting_metal(3, BORE_MM / 1000.0)
120 assert itemised.mass == pytest.approx(plain.mass, rel=1e-3)
121
122
124 doc = _drawing(segmented=True)
125 edge: Any = doc["edges"]
126 edge[0]["data"]["segments"][0].pop("bore")
127 edge[0]["data"]["params"].pop("bore")
128 assert _walls(doc) == {}
129
130
132 """The shipped LE4 drawing: every pressurant run is itemised."""
133 import json
134
135 built = build_network(read_diagram(json.loads(LE4.read_text()), name="LE4"))
136 stub: Any = SimpleNamespace(model=SimpleNamespace(built=built))
137 Session._build_line_walls(stub)
138 for line in ("node_2-junc_49", "junc_49-junc_48", "node_38-node_36"):
139 assert line in stub.walls, (line, sorted(stub.walls))
140 # Drawn with no bore at all: nothing to size a wall on, so none.
141 assert "node_15-node_38" not in stub.walls
None test_a_drawn_wall_thickness_adds_the_tube_on_every_segment()
dict[str, object] _measured(float value, str unit)
None test_the_le4_press_lines_get_their_walls()
None test_a_one_segment_line_gets_the_darcy_pipes_wall()
None test_the_metal_is_the_counted_fittings_at_the_segment_bore()
dict[str, Any] _walls(dict[str, object] doc)
None test_an_itemised_run_with_no_bore_anywhere_stays_bare()
dict[str, object] _drawing(bool segmented, **dict[str, object] line)