52def shut(tank: Tank, parts, *, wall_above: float, pressure: float = 101325.0):
53 """Loaded to 95 %, with the vent just shut, wetted wall ``wall_above``
54 kelvin over the liquid. At more than twice saturation the ullage is
55 seeded with the propellant's own vapour at the liquid's saturation
56 pressure, as a tank that has sat a while holds."""
57 lox, _, geometry = parts
58 state = tank.initial_state(
60 liquid_mass=0.95 * geometry.total_volume * lox.get(
"rho", T=90.0, q=0.0),
61 liquid_temperature=90.0,
66 return replace(state, wetted_wall_temperature=90.0 + wall_above)
76 nucleate_conductance=0.0,
77 leidenfrost_superheat=float(
"inf"),
82 for above
in (2.0, 60.0):
83 a = old.rates(
shut(old, parts, wall_above=above))
84 b = new.rates(
shut(new, parts, wall_above=above))
92 """A wall 100 K above saturation is insulated by its own vapour film; one
93 5 K above is wetted and nucleate boiling pulls thirty times the heat."""
95 both =
build(parts, nucleate_conductance=3000.0, leidenfrost_superheat=40.0)
96 hot_f = film.rates(
shut(film, parts, wall_above=100.0)).heat_to_wetted_wall
97 hot_b = both.rates(
shut(both, parts, wall_above=100.0)).heat_to_wetted_wall
98 assert hot_b == pytest.approx(hot_f)
99 warm_f = film.rates(
shut(film, parts, wall_above=5.0)).heat_to_wetted_wall
100 warm_b = both.rates(
shut(both, parts, wall_above=5.0)).heat_to_wetted_wall
101 assert warm_b == pytest.approx(30.0 * warm_f, rel=1e-6)
105 """Two kelvin of superheat: with no onset the wall boils it all into the
106 ullage; with a 5 K onset the same heat reaches the liquid as sensible."""
108 waits =
build(parts, boiling_onset=5.0)
109 r_boil = boils.rates(
shut(boils, parts, wall_above=2.0))
110 r_wait = waits.rates(
shut(waits, parts, wall_above=2.0))
114 assert r_boil.vapour_mass - r_boil.evaporation > 0.0
115 assert r_wait.vapour_mass - r_wait.evaporation == pytest.approx(0.0, abs=1e-12)
116 assert r_wait.heat_to_wetted_wall == pytest.approx(r_boil.heat_to_wetted_wall)
118 assert r_wait.latent_power > r_boil.latent_power
120 hot = waits.rates(
shut(waits, parts, wall_above=60.0))
121 assert hot.vapour_mass == pytest.approx(
122 boils.rates(
shut(boils, parts, wall_above=60.0)).vapour_mass
130 lox, _, geometry = parts
131 tank =
build(parts, boiling_onset=5.0, surface_layer=0.01, surface_mixing=5.0)
134 state =
shut(tank, parts, wall_above=1.0, pressure=2.5e5)
135 assert state.vapour_mass > 0.0
136 assert state.surface_temperature == pytest.approx(90.0)
137 rates = tank.rates(state)
138 m_s = tank.surface_mass(state)
139 assert 0.1 < m_s < 0.4, m_s
141 assert rates.surface_temperature > 0.0
142 assert rates.surface_temperature > 20.0 * rates.liquid_temperature
143 stepped = tank.step(state, rates, 1.0)
144 assert stepped.surface_temperature == pytest.approx(
145 90.0 + rates.surface_temperature, rel=1e-9
148 mixed = replace(state, surface_temperature=
None)
149 plain = tank.rates(mixed)
150 assert plain.surface_temperature == 0.0
151 assert plain.liquid_temperature > rates.liquid_temperature
155 """A wall two kelvin warm holds thirty kilojoules; poured into a
156 two-hundred-gram layer that would be a surface hotter than the metal.
157 Liquid warmed at a wall arrives no hotter than the wall, so once the
158 layer reaches it the rest thickens the layer -- here, warms the bulk."""
164 nucleate_conductance=3000.0,
165 leidenfrost_superheat=40.0,
167 state =
shut(tank, parts, wall_above=2.0, pressure=2.5e5)
168 state = replace(state, surface_temperature=state.liquid_temperature + 2.0)
169 rates = tank.rates(state)
172 assert rates.heat_to_wetted_wall > 0.0
173 assert rates.liquid_temperature > 0.0
174 below = replace(state, surface_temperature=state.liquid_temperature + 0.5)
175 r_below = tank.rates(below)
176 assert r_below.surface_temperature > rates.surface_temperature
177 assert r_below.liquid_temperature < rates.liquid_temperature
183 """The stand's number is ~20 psi/min (operator). Where it comes from: the
184 dry wall above the liquid is still warm after a load (the liquid never
185 touched it), it warms the ullage, the ullage warms the surface, and the
186 surface sets the pressure. The three closures together land in that
187 decade and keep climbing while the hardware has heat to give; the old
188 closure jumps and stalls."""
190 def climb(tank: Tank, dt: float = 0.05) -> tuple[float, float]:
191 """psi/min over the first and the second half-minute."""
192 state =
shut(tank, parts, wall_above=0.5)
194 state = replace(state, ullage=replace(state.ullage, wall_temperature=200.0))
195 marks = [tank.pressure(state)]
196 for half
in range(2):
197 for _
in range(int(30.0 / dt)):
198 state = tank.step(state, tank.rates(state), dt)
199 marks.append(tank.pressure(state))
200 return (marks[1] - marks[0]) / PSI * 2.0, (marks[2] - marks[1]) / PSI * 2.0
204 nucleate_conductance=3000.0,
205 leidenfrost_superheat=40.0,
210 first, second = climb(stratified)
215 10.0 < (first + second) / 2.0 < 80.0
216 ), f
"{first:.1f} then {second:.1f} psi/min"
217 assert second > 2.0, f
"{first:.1f} then {second:.1f} psi/min"
221 boiling =
build(parts, nucleate_conductance=3000.0, leidenfrost_superheat=40.0)
222 b_first, b_second = climb(boiling)
223 assert b_second < 0.2 * b_first, f
"{b_first:.1f} then {b_second:.1f} psi/min"
229 """A dry wall at room temperature over a small ullage pours kilowatts into
230 the gas, and the gas into the surface. The layer stops at saturation for
231 the tank's pressure and the heat becomes vapour; it never reaches the
232 critical temperature, where every property call fails and the tank
233 used to freeze at zero."""
234 tank =
build(parts, boiling_onset=2.0, surface_layer=0.01, surface_mixing=5.0)
235 state =
shut(tank, parts, wall_above=0.5, pressure=2.5e5)
236 state = replace(state, ullage=replace(state.ullage, wall_temperature=293.0))
237 T_crit = float(parts[0].critical_temperature)
238 for _
in range(int(60.0 / 0.02)):
239 rates = tank.rates(state)
240 state = tank.step(state, rates, 0.02)
241 assert state.surface_temperature
is not None
242 assert state.surface_temperature < T_crit - 1.0
243 assert tank.pressure(state) > 1.0e5
244 assert state.vapour_mass > 0.0
248 p_crit = float(parts[0].critical_pressure)
249 for _
in range(int(120.0 / 0.02)):
250 state = tank.step(state, tank.rates(state), 0.02)
251 assert tank.pressure(state) < 1.01 * p_crit
252 assert state.liquid_temperature > 90.05
258 """A wall at 92 K over 90 K liquid with the layer at 91 K: half the wall's
259 convective heat reaches the surface, half thickens the layer into the bulk.
260 Only the half that arrives can evaporate the surface. The exchange used to
261 price all of it as latent and then debit the layer again for the half the
262 bulk took -- evaporating with heat that never got there."""
263 tank =
build(parts, boiling_onset=5.0, surface_layer=0.01, surface_mixing=0.0)
264 state =
shut(tank, parts, wall_above=2.0, pressure=2.5e5)
265 state = replace(state, vapour_mass=0.0, surface_temperature=91.0)
266 rates = tank.rates(state)
267 reach = (92.0 - 91.0) / (92.0 - 90.0)
268 assert rates.heat_to_wetted_wall > 0.0
and rates.heat_to_liquid > 0.0
269 arrives = rates.heat_to_liquid + reach * rates.heat_to_wetted_wall
270 assert rates.latent_power == pytest.approx(arrives, rel=1e-9)
272 to_bulk = (1.0 - reach) * rates.heat_to_wetted_wall
273 m_s = tank.surface_mass(state)
274 cp = parts[0].get(
"cp", T=90.0, q=0.0)
275 assert rates.liquid_temperature == pytest.approx(
276 to_bulk / ((state.liquid_mass - m_s) * cp), rel=1e-9
278 assert rates.surface_temperature == pytest.approx(0.0, abs=1e-12)