52 state = tank.initial_state(
53 pressure=from_psig(0.0),
55 liquid_temperature=293.15,
56 gas_temperature=293.15,
59 id=
"T", label=
"T", tank=tank, state=state, ullage_node=
"u", outlet_node=
"o"
66 air = sim.state.ullage.mass
73 enthalpy_gas_in=sim.tank.gas_enthalpy(sim.state),
75 assert sim.state.ullage.mass == pytest.approx(air, rel=1e-12),
"no air in"
76 assert sim.pressure < AMBIENT - 0.05 * 6894.76,
"a vacuum, not atmosphere"
77 assert sim.fixed_kg == pytest.approx(0.0, abs=1e-12)
78 assert sim.fixed_J == pytest.approx(0.0, abs=1e-6)
82 """The floor's own job: a vent asked for more gas than there is above
83 atmosphere leaves the tank *at* atmosphere -- at the gas's own (expanded)
84 temperature, not the wall's -- and what that takes is air the vent let in,
85 booked as inflow, not as a clamp."""
88 sim.state, ullage=replace(sim.state.ullage, mass=sim.state.ullage.mass * 3.0)
94 mdot_gas_out=sim.state.ullage.mass / 0.02,
95 enthalpy_gas_in=sim.tank.gas_enthalpy(sim.state),
98 assert sim.pressure == pytest.approx(AMBIENT, rel=1e-4)
99 assert sim.vent_drawn_kg > 0.0
100 assert sim.fixed_kg == pytest.approx(0.0, abs=1e-9),
"not a clamp"
104 """A drain valve between a tank outlet 0.45 psi above atmosphere and the
105 air -- and, beside it, a 6,000 psi K-bottle line that has nothing to do
107 valve = build_component(
108 ComponentInstance.build(
111 {
"Cv":
Param(0.8,
"Cv", M,
"datasheet"),
"bore":
Param(0.25,
"in", M,
"")},
115 head = 0.45 * 6894.76
116 net.add_node(
"outlet",
"ethanol", 293.15, pressure=AMBIENT + head)
117 net.add_node(
"air",
"ethanol", 293.15, pressure=AMBIENT)
118 net.add_branch(
"drain", valve,
"outlet",
"air")
120 line = build_component(
121 ComponentInstance.build(
125 "Cv":
Param(0.1,
"Cv", M,
"datasheet"),
126 "bore":
Param(0.25,
"in", M,
""),
130 net.add_node(
"bottle",
"nitrogen", 293.15, pressure=from_psig(6000.0))
131 net.add_node(
"reg",
"nitrogen", 293.15, pressure=from_psig(5990.0))
132 net.add_branch(
"kline", line,
"bottle",
"reg")
150 """The operator's case on LE4 (6): fill the fuel tank, stop the fill (Armed
151 shuts the vent), and a drain teed into the tank's bottom port is open. The
152 tank pulls its head's worth of vacuum and keeps its fuel; the old floor
153 let it drain at 129 g/s and booked the air it made as clamp energy."""
154 payload = json.loads(DRAWING.read_text())
155 ids = {n[
"data"][
"label"]: n[
"id"]
for n
in payload[
"nodes"]}
156 tank, qd = ids[
"Eth-Tank"], ids[
"QD-FFB"]
159 for e
in payload[
"edges"]
160 if e[
"source"] == tank
and e.get(
"sourceHandle") ==
"b2"
162 assert edge[
"target"] == qd
163 edge.update(target=
"drain_tee", targetHandle=
"t")
164 payload[
"nodes"] += [
168 "position": {
"x": 0,
"y": 0},
169 "data": {
"componentType":
"JUNCTION",
"label":
"drain_tee"},
174 "position": {
"x": 40,
"y": 0},
175 "data": {
"componentType":
"MAN",
"label":
"FD-MAN",
"options": {}},
178 payload[
"edges"] += [
179 {
"id":
"t-qd",
"source":
"drain_tee",
"sourceHandle":
"b",
"target": qd},
180 {
"id":
"t-dr",
"source":
"drain_tee",
"sourceHandle":
"r",
"target":
"drain"},
182 for edge
in payload[
"edges"][-2:]:
183 edge.update(targetHandle=
"l", type=
"smoothstep", data={})
184 model = assemble_model(read_diagram(payload, name=
"drain"), diagram_id=
"drain")
185 session = open_session(model, load_machine(tables=TABLES))
186 fuel = session.tanks[tank]
187 for state, seconds
in ((
"Armed", 1.0), (
"Fuel Fill", 12.0), (
"Armed", 1.0)):
188 session.command_state(state)
189 for _
in range(int(seconds / 0.5)):
191 full = fuel.state.liquid_mass
192 assert full > 5.0,
"the fuel tank was filled"
193 assert fuel.pressure == pytest.approx(AMBIENT, abs=0.5 * 6894.76)
194 air, fixed = fuel.state.ullage.mass, fuel.fixed_J
196 session.set_valve(
"drain",
True)
200 assert full - fuel.state.liquid_mass < 0.05,
"it holds, bar a few grams"
201 assert fuel.state.ullage.mass == pytest.approx(air, rel=1e-6),
"no air made"
202 assert fuel.fixed_J - fixed == pytest.approx(0.0, abs=1.0)
203 head = AMBIENT - fuel.pressure
204 assert 0.2 * 6894.76 < head < 0.8 * 6894.76,
"a column's worth of vacuum"
208 assert fuel.refused_kg < 1e-4
209 assert [f.to_dict()
for f
in session.watcher.flags] == []
213 not (DRAWING.exists()
and TABLES.is_dir()), reason=
"LE4 fixture or tables absent"
216 """LE4 (6)'s unsized tee joins the fill line and the dump into the cart
217 tank's outlet node. With both hand valves held open the transfer tank
218 pushes into the fuel tank, then dumps itself dry -- and the fuel tank's
219 pressure must then go out through the same tee. Cutting every branch on the
220 dry tank's outlet froze it at 45 psig with the dump open."""
221 payload = json.loads(DRAWING.read_text())
222 ids = {n[
"data"][
"label"]: n[
"id"]
for n
in payload[
"nodes"]}
223 model = assemble_model(read_diagram(payload, name=
"dump"), diagram_id=
"dump")
224 session = open_session(model, load_machine(tables=TABLES))
226 session.tanks[ids[
"Eth-Tank"]],
227 session.tanks[ids[
"Fuel Transfer Tank"]],
229 for state, seconds
in ((
"Armed", 1.0), (
"Fuel Fill", 10.0), (
"Armed", 2.0)):
230 session.command_state(state)
231 for _
in range(int(seconds / 0.5)):
233 session.set_valve(ids[
"FF-MAN-Output"],
True)
234 session.set_valve(ids[
"FF-MAN-Dump"],
True)
237 assert cart.state.liquid_mass < 1e-3,
"the dump emptied the cart tank"
238 assert fuel.pressure < AMBIENT + 0.5 * 6894.76,
"and then the fuel tank"
239 assert fuel.state.liquid_mass > 5.0,
"down to its head, not drained"
240 assert not [f
for f
in session.watcher.flags
if f.check ==
"held_open"]