STAR
Cal STAR — firmware, DAQ server, and engine design
Loading...
Searching...
No Matches
run_layer1_impinging_8000n_fresh.py
Go to the documentation of this file.
1#!/usr/bin/env python3
2"""Run Layer 1 on ``configs/impinging_lox_ch4_8000N.yaml`` and print injector / performance summary."""
3
4from __future__ import annotations
5
6import argparse
7import copy
8import sys
9from pathlib import Path
10
11import yaml
12
13ROOT = Path(__file__).resolve().parents[1]
14sys.path.insert(0, str(ROOT))
15
16from engine.core.runner import PintleEngineRunner
17from engine.optimizer.layers.layer1_static_optimization import run_layer1_optimization
18from engine.pipeline.io import load_config
19
20
21def main() -> None:
22 ap = argparse.ArgumentParser(description=__doc__)
23 ap.add_argument(
24 "--config",
25 default=str(ROOT / "configs" / "impinging_lox_ch4_8000N.yaml"),
26 help="YAML path",
27 )
28 ap.add_argument("--layer1-max-iterations", type=int, default=90)
29 ap.add_argument("--layer1-cma-restarts", type=int, default=3)
30 ap.add_argument(
31 "--layer1-smoke",
32 action="store_true",
33 help="Short single-restart smoke (overrides iterations if used with scripts)",
34 )
35 ap.add_argument(
36 "--write-config",
37 metavar="PATH",
38 default=None,
39 help="After optimization, write optimized PintleEngineConfig YAML (JSON-safe dump)",
40 )
41 args = ap.parse_args()
42
43 cfg = load_config(args.config)
44 req = cfg.design_requirements.model_dump()
45 runner = PintleEngineRunner(copy.deepcopy(cfg))
46
47 pcfg = {
48 "mode": "optimizer_controlled",
49 "max_lox_pressure_psi": float(req["max_lox_tank_pressure_psi"]),
50 "max_fuel_pressure_psi": float(req["max_fuel_tank_pressure_psi"]),
51 }
52
53 smoke = bool(args.layer1_smoke)
54 max_it = max(1, int(args.layer1_max_iterations))
55 restarts = 1 if smoke else max(1, int(args.layer1_cma_restarts))
56
57 # Match Layer‑1 gates: thrust fallback uses tolerances["thrust"] only when YAML omits layer1_thrust_validation_rel_tol.
58 _thr = req.get("layer1_thrust_validation_rel_tol")
59 thrust_tol = float(_thr) if _thr is not None else 0.10
60
61 opt_cfg, results = run_layer1_optimization(
62 copy.deepcopy(cfg),
63 runner,
64 req,
65 target_burn_time=float(req.get("target_burn_time", 6.0)),
66 tolerances={"thrust": thrust_tol, "apogee": 0.15},
67 pressure_config=pcfg,
68 layer1_smoke=smoke,
69 layer1_max_iterations=max_it if not smoke else min(max_it, 24),
70 layer1_cma_restarts=restarts if not smoke else 1,
71 )
72
73 perf = results.get("performance") or {}
74 diag = perf.get("diagnostics") if isinstance(perf.get("diagnostics"), dict) else {}
75
76 def gv(key: str):
77 v = perf.get(key)
78 return v if v is not None else diag.get(key)
79
80 ox = opt_cfg.injector.geometry.oxidizer
81 fu = opt_cfg.injector.geometry.fuel
82
83 a_go = float(gv("A_geom_O"))
84 a_gf = float(gv("A_geom_F"))
85 a_eo = float(gv("A_eff_O"))
86 a_ef = float(gv("A_eff_F"))
87
88 print("=== Layer 1 optimization ===")
89 print(f"config: {args.config}")
90 print(f"layer1_smoke={smoke} max_iterations={max_it if not smoke else min(max_it, 24)} cma_restarts={restarts}")
91 print(f"requirements: thrust={req.get('target_thrust')} N MR={req.get('optimal_of_ratio')}")
92 imp_deg = float(ox.impingement_angle) + float(fu.impingement_angle)
93 print(f"impingement: θ_total = θ_O+θ_F = {imp_deg:.2f} deg (solver uses this)")
94 print()
95 print(f"thrust F [N]: {perf.get('F')}")
96 print(f"MR (O/F): {perf.get('MR')}")
97 print(f"momentum_ratio_R: {perf.get('momentum_ratio_R')}")
98 print(f"ΔP_O/Pc: {perf.get('injector_dp_ratio_O')}")
99 print(f"ΔP_F/Pc: {perf.get('injector_dp_ratio_F')}")
100 print(f"P_O / P_F [psi]: {perf.get('P_O_start_psi')} / {perf.get('P_F_start_psi')}")
101 if getattr(opt_cfg.injector, "type", None) == "impinging" and isinstance(diag, dict):
102 print(
103 "injector inlet P_inj [Pa]: "
104 f"O={diag.get('P_injector_O')} F={diag.get('P_injector_F')}"
105 )
106 print(
107 "Δp_feed tank→inj [Pa]: "
108 f"O={diag.get('delta_p_feed_O')} F={diag.get('delta_p_feed_F')}"
109 )
110 print(
111 "Δp_inj jet→Pc [Pa]: "
112 f"O={diag.get('delta_p_injector_O')} F={diag.get('delta_p_injector_F')} "
113 "(ΔP/Pc ratios use these ÷ Pc; tank−Pc would wrongly include feed losses)"
114 )
115 print(f"Cd_O / Cd_F: {gv('Cd_O')} / {gv('Cd_F')}")
116 print(f"A_geom_O/A_geom_F: {a_go / a_gf:.6g}")
117 print(f"A_eff_O/A_eff_F: {a_eo / a_ef:.6g}")
118 n_d = int(min(int(ox.n_elements), int(fu.n_elements)))
119 print(f"n_doublets (min O/F): {n_d}")
120 print(
121 f"n_O / n_F: {int(ox.n_elements)} / {int(fu.n_elements)}\n"
122 f"d_jet_O / d_jet_F [mm]: {float(ox.d_jet) * 1000:.5f} / {float(fu.d_jet) * 1000:.5f}\n"
123 f"spacing_O / spacing_F [mm]: "
124 f"{float(ox.spacing) * 1000:.5f} / {float(fu.spacing) * 1000:.5f}\n"
125 f"θ_O / θ_F [deg]: {float(ox.impingement_angle):.2f} / {float(fu.impingement_angle):.2f}"
126 )
127 foci = diag.get("feed_orifice_coupling_iterations")
128 print(f"feed_orifice_coupling_iterations: {foci}")
129 mdo, mdf = perf.get("mdot_O"), perf.get("mdot_F")
130 mbo, mbf = diag.get("mdot_from_bernoulli_O"), diag.get("mdot_from_bernoulli_F")
131 print(f"mdot_O / mdot_from_Bern_O: {mdo} / {mbo}")
132 print(f"mdot_F / mdot_from_Bern_F: {mdf} / {mbf}")
133 print(f"pressure_candidate_valid: {perf.get('pressure_candidate_valid')}")
134 fr = perf.get("failure_reasons")
135 if fr:
136 print(f"failure_reasons: {fr}")
137 cg = opt_cfg.chamber_geometry
138 if cg is not None:
139 print(f"A_throat [m²] (optimized): {getattr(cg, 'A_throat', None)}")
140 if args.write_config:
141 out = Path(args.write_config)
142 out.parent.mkdir(parents=True, exist_ok=True)
143 data = opt_cfg.model_dump(mode="json")
144 out.write_text(yaml.safe_dump(data, sort_keys=False))
145 print(f"wrote optimized config: {out.resolve()}")
146
147
148if __name__ == "__main__":
149 main()
#define min(a, b)
Definition Arduino.h:42
#define max(a, b)
Definition Arduino.h:43