STAR
Cal STAR — firmware, DAQ server, and engine design
Loading...
Searching...
No Matches
sweep_impinging_geom_ao_af_eval.py
Go to the documentation of this file.
1#!/usr/bin/env python3
2"""Evaluate-only sweep: vary impinging A_geom_O/A_geom_F at fixed tank pressures.
3
4Varies oxidizer jet diameter so that (n_O d_O²)/(n_F d_F²) = target_ratio,
5holding fuel geometry and element counts fixed. Uses pressures from an optional
6prior Layer-1 run or explicit PSI inputs.
7
8Does not modify injector physics models — geometry-only perturbation + runner.evaluate.
9"""
10
11from __future__ import annotations
12
13import argparse
14import copy
15import math
16import sys
17from pathlib import Path
18
19import numpy as np
20
21ROOT = Path(__file__).resolve().parents[1]
22sys.path.insert(0, str(ROOT))
23
24PSI_TO_PA = 6894.76
25
26from engine.core.runner import PintleEngineRunner # noqa: E402
27from engine.optimizer.layers.layer1_static_optimization import run_layer1_optimization # noqa: E402
28from engine.pipeline.io import load_config # noqa: E402
29
30
31def _diag_dp_ratios(pc: float, ev: dict) -> tuple[float | None, float | None]:
32 if not (np.isfinite(pc) and pc > 0):
33 return None, None
34 dpo = ev.get("diagnostics", {}).get("delta_p_injector_O")
35 dpf = ev.get("diagnostics", {}).get("delta_p_injector_F")
36 ro = float(dpo) / pc if dpo is not None and np.isfinite(dpo) else None
37 rf = float(dpf) / pc if dpf is not None and np.isfinite(dpf) else None
38 return ro, rf
39
40
41def main() -> None:
42 ap = argparse.ArgumentParser(description=__doc__)
43 ap.add_argument(
44 "--config",
45 type=str,
46 default=str(ROOT / "configs/impinging_lox_ch4_8000N.yaml"),
47 help="YAML config path",
48 )
49 ap.add_argument("--skip-layer1", action="store_true", help="Use --po-psi/--pf-psi instead of optimizing")
50 ap.add_argument(
51 "--geometry-config",
52 type=str,
53 default=None,
54 help=(
55 "When used with --skip-layer1: YAML to copy injector (and fluids) from so the sweep "
56 "matches a prior optimized geometry. If omitted, injector geometry comes from --config."
57 ),
58 )
59 ap.add_argument("--po-psi", type=float, default=None, help="LOX tank pressure [psi] when skipping Layer 1")
60 ap.add_argument("--pf-psi", type=float, default=None, help="Fuel tank pressure [psi] when skipping Layer 1")
61 ap.add_argument("--ratio-min", type=float, default=1.5)
62 ap.add_argument("--ratio-max", type=float, default=3.0)
63 ap.add_argument("--ratio-steps", type=int, default=16)
64 ap.add_argument("--layer1-max-iterations", type=int, default=22)
65 ap.add_argument("--silent-eval", action="store_true", help="Pass silent=True to runner.evaluate")
66 args = ap.parse_args()
67
68 cfg = load_config(args.config)
69 req = cfg.design_requirements.model_dump()
70 pcfg = {
71 "mode": "optimizer_controlled",
72 "max_lox_pressure_psi": float(req["max_lox_tank_pressure_psi"]),
73 "max_fuel_pressure_psi": float(req["max_fuel_tank_pressure_psi"]),
74 }
75
76 if args.skip_layer1:
77 if args.po_psi is None or args.pf_psi is None:
78 raise SystemExit("--skip-layer1 requires --po-psi and --pf-psi")
79 geom_src = load_config(args.geometry_config) if args.geometry_config else cfg
80 opt_cfg = copy.deepcopy(geom_src)
81 po_pa = float(args.po_psi) * PSI_TO_PA
82 pf_pa = float(args.pf_psi) * PSI_TO_PA
83 perf_hint = {}
84 else:
85 opt_cfg, results = run_layer1_optimization(
86 copy.deepcopy(cfg),
87 PintleEngineRunner(copy.deepcopy(cfg)),
88 req,
89 target_burn_time=float(req.get("target_burn_time", 6.0)),
90 tolerances={"thrust": 0.10, "apogee": 0.15},
91 pressure_config=pcfg,
92 layer1_smoke=True,
93 layer1_max_iterations=int(args.layer1_max_iterations),
94 layer1_cma_restarts=1,
95 )
96 perf_hint = results.get("performance") or {}
97 po_pa = float(perf_hint["P_O_start_psi"]) * PSI_TO_PA
98 pf_pa = float(perf_hint["P_F_start_psi"]) * PSI_TO_PA
99
100 geom = getattr(getattr(opt_cfg, "injector", None), "geometry", None)
101 if geom is None:
102 raise SystemExit("No injector.geometry on config")
103 ox = geom.oxidizer
104 fu = geom.fuel
105 n_o = max(1, int(ox.n_elements))
106 n_f = max(1, int(fu.n_elements))
107 d_f = float(fu.d_jet)
108 d_o_base = float(ox.d_jet)
109
110 P_ambient = PintleEngineRunner(copy.deepcopy(opt_cfg))._get_ambient_pressure(None)
111
112 ratios = np.linspace(float(args.ratio_min), float(args.ratio_max), int(args.ratio_steps))
113
114 print("\n=== A_geom_O/A_geom_F sweep (evaluate-only, fixed tank pressures) ===")
115 print(f"P_tank_O = {po_pa / PSI_TO_PA:.3f} psi, P_tank_F = {pf_pa / PSI_TO_PA:.3f} psi")
116 print(f"Baseline: n_O={n_o} n_F={n_f} d_O={d_o_base * 1000:.4f} mm d_F={d_f * 1000:.4f} mm")
117 print(
118 f"{'AoAf_tgt':>10} {'AoAf_geom':>10} {'R':>8} {'MR':>8} {'F[N]':>10} "
119 f"{'dP_O/Pc':>10} {'dP_F/Pc':>10} {'err':>8}"
120 )
121
122 for r_tgt in ratios:
123 cfg_i = copy.deepcopy(opt_cfg)
124 g = cfg_i.injector.geometry
125 scale = math.sqrt(max(1e-30, float(r_tgt) * float(n_f) / float(n_o)))
126 d_o_new = d_f * scale
127 g.oxidizer.d_jet = d_o_new
128
129 runner = PintleEngineRunner(cfg_i)
130 try:
131 ev = runner.evaluate(po_pa, pf_pa, P_ambient=P_ambient, silent=args.silent_eval)
132 except Exception as e:
133 print(f"{r_tgt:10.4f} {'FAIL':>10} — {e}")
134 continue
135
136 a_go = float(ev.get("A_geom_O") or 0.0)
137 a_gf = float(ev.get("A_geom_F") or 0.0)
138 ao_af = a_go / a_gf if a_gf > 0 else float("nan")
139 R = ev.get("diagnostics", {}).get("momentum_ratio_R")
140 R = float(R) if R is not None and np.isfinite(R) else float("nan")
141 MR = float(ev.get("MR", float("nan")))
142 F = float(ev.get("F", float("nan")))
143 pc = float(ev.get("Pc", float("nan")))
144 ro, rf = _diag_dp_ratios(pc, ev)
145 err = abs(ao_af - r_tgt) / r_tgt if np.isfinite(ao_af) and r_tgt > 0 else float("nan")
146
147 print(
148 f"{r_tgt:10.4f} {ao_af:10.4f} {R:8.4f} {MR:8.4f} {F:10.2f} "
149 f"{(ro if ro is not None else float('nan')):10.4f} "
150 f"{(rf if rf is not None else float('nan')):10.4f} {err:8.2e}"
151 )
152
153
154if __name__ == "__main__":
155 main()
#define max(a, b)
Definition Arduino.h:43
tuple[float|None, float|None] _diag_dp_ratios(float pc, dict ev)