37from typing
import Any, Dict, List, Optional
47def fit_feed(prep: Prepared, result: Dict[str, Any], config: Any) -> Dict[str, Any]:
48 """Per side: the drawing's feed against the design's, and the K0 that makes them agree."""
51 series = result[
"series"]
52 fire = [i
for i, f
in enumerate(series[
"firing"])
if f]
54 return {
"available":
False,
"error":
"the burn is too short to fit"}
55 t0 = series[
"t"][fire[0]]
56 idx = [i
for i
in fire[:-1]
if series[
"t"][i] - t0 >= SETTLED_S]
58 return {
"available":
False,
"error":
"too few settled steps to fit"}
59 flown = bool((result.get(
"flight")
or {}).get(
"ok"))
60 sides: Dict[str, Any] = {}
61 for key, cside, label
in SIDES:
63 lockup = float(result[
"summary"][key][
"t0_psia"])
64 fs = config.feed_system[cside]
65 rho = float(config.fluids[cside].density)
66 mu = getattr(config.fluids[cside],
"viscosity",
None)
67 area = float(fs.A_hydraulic)
71 m = float(s[
"mdot"][i])
72 q = 0.5 * rho * (m / (rho * area)) ** 2 / PSI
73 rows.append((m, q, lockup, float(s[
"tank_psia"][i]), float(s[
"inlet_psia"][i]), float(s[
"manifold_psia"][i])))
74 m_mean = sum(r[0]
for r
in rows) / len(rows)
75 q_mean = sum(r[1]
for r
in rows) / len(rows)
76 sum_qq = sum(r[1] ** 2
for r
in rows)
78 k_line = sum((r[3] - r[4]) * r[1]
for r
in rows) / sum_qq
80 deficit = sum(r[2] - r[3]
for r
in rows) / len(rows)
81 k_supply = deficit / q_mean
82 k0 = k_line + k_supply
84 def design_manifold(m: float, cfg: Any) -> float:
85 return lockup - delta_p_feed(m, rho, cfg, lockup * PSI, mu=mu) / PSI
86 fitted_cfg = fs.model_copy(update={
"K0": k0,
"roughness_m":
None,
"fittings": [],
"K1": 0.0,
"phi_type":
"none"})
87 twin_man = [r[5]
for r
in rows]
88 today = [design_manifold(r[0], fs)
for r
in rows]
89 fitted = [design_manifold(r[0], fitted_cfg)
for r
in rows]
90 mean =
lambda v: sum(v) / len(v)
91 rms =
lambda a, b: (sum((x - y) ** 2
for x, y
in zip(a, b)) / len(a)) ** 0.5
92 line_loss = mean([r[3] - r[4]
for r
in rows])
93 line_loss_design = mean([delta_p_feed(r[0], rho, fs.model_copy(update={
"K_exit": 0.0}), lockup * PSI, mu=mu) / PSI
95 tank_firing = mean([r[3]
for r
in rows])
99 "velocity_head_psi": q_mean,
100 "lockup_psia": lockup,
101 "tank_firing_psia": tank_firing,
102 "supply_deficit_psi": deficit,
103 "supply_deficit_worst_psi":
max(r[2] - r[3]
for r
in rows),
104 "line_loss_psi": line_loss,
105 "line_loss_design_psi": line_loss_design,
106 "manifold_psia": mean(twin_man),
107 "manifold_design_psia": mean(today),
108 "manifold_gap_psi": mean(twin_man) - mean(today),
109 "manifold_fitted_psia": mean(fitted),
110 "fitted_rms_psi": rms(twin_man, fitted),
112 "K_supply": k_supply,
114 "K0_design": float(fs.K0),
115 "design_path": (
"friction (roughness_m) + fittings" if getattr(fs,
"roughness_m",
None)
is not None
116 else "K0" + (
" + fittings" if getattr(fs,
"fittings",
None)
else "")),
117 "write_refused":
_refusal(label, k0, k_line, k_supply, line_loss, line_loss_design, tank_firing, lockup),
119 refused = [s[
"write_refused"]
for s
in sides.values()
if s[
"write_refused"]]
124 "writable":
not refused,
125 "write_refused":
" ".join(refused)
or None,
127 "basis": ((
"In flight: every liquid column at the flight's acceleration. " if flown
else "On the pad, one g. ")
128 + f
"Settled firing steps ({len(idx)}, from {SETTLED_S:g} s after Fire, last step left out), "
129 "against the design's velocity head (its density, its A_hydraulic). Fit again after the "
130 "injector is resized: the supply term is exact only at the flow it was fitted at."),
132 "drawing": {
"id": prep.drawing.id,
"name": prep.drawing.name,
"sha256": prep.drawing.sha256},
136def _refusal(label: str, k0: float, k_line: float, k_supply: float, line_psi: float, line_design_psi: float,
137 tank_psia: float, lockup_psia: float) -> Optional[str]:
138 """Why a side's fitted K0 cannot go into the design, or ``None`` when it can. K0 is a loss in
139 velocity heads; at or below zero the fit is saying the feed *adds* pressure, which a loss
140 coefficient cannot hold. Said by its parts: the lines (a side drawn with no line loses nothing
141 between the tank and the injector) and the supply (the regulator's outlet climbs as the bottle
142 empties, the supply-pressure effect, so the tank sits above its T-0 pressure while firing)."""
147 why.append(f
"the drawing loses {line_psi:.1f} psi between the tank and the injector against the design's "
148 f
"{line_design_psi:.1f}, so its lines are likely undrawn")
150 why.append(f
"the tank sits above its T-0 pressure while firing ({tank_psia:.1f} against {lockup_psia:.1f} "
151 "psia: the regulator's outlet climbs as the bottle empties)")
152 return f
"{label}: K0 {k0:.3f} is not a loss; " + (
" and ".join(why)
or "the fit adds pressure") +
"."
Optional[str] _refusal(str label, float k0, float k_line, float k_supply, float line_psi, float line_design_psi, float tank_psia, float lockup_psia)