STAR
Cal STAR — firmware, DAQ server, and engine design
Loading...
Searching...
No Matches
layer1_run.py
Go to the documentation of this file.
1"""Run Layer 1 on a config and write both the summary and the optimised config.
2
3 python3 scripts/layer1_run.py --config configs/ethalox_8kN_SHIP.yaml --out /tmp/run.json
4
5Writes <out> (summary JSON) and <out with _config.yaml> (the design itself -- that file is
6the artifact, the summary is not). Pin layer1_random_seed in the config for a reproducible
7result; Layer 1 has two recurring basins and an unseeded run can land in either.
8"""
9import sys, copy, json, argparse
10from pathlib import Path
11sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
12from engine.pipeline.io import load_config
13from engine.core.runner import PintleEngineRunner
14from engine.optimizer.layers.layer1_static_optimization import run_layer1_optimization
15
16def main():
17 ap = argparse.ArgumentParser()
18 ap.add_argument('--config', required=True)
19 ap.add_argument('--out', default='', help='summary JSON path; default alongside the config')
20 ap.add_argument('--label', default='')
21 a = ap.parse_args()
22 if not a.out:
23 a.out = str(Path(a.config).with_suffix('.layer1.json'))
24 cfg = load_config(a.config)
25 req = cfg.design_requirements.model_dump()
26 runner = PintleEngineRunner(copy.deepcopy(cfg))
27 pcfg = {"mode": "optimizer_controlled",
28 "max_lox_pressure_psi": float(req["max_lox_tank_pressure_psi"]),
29 "max_fuel_pressure_psi": float(req["max_fuel_tank_pressure_psi"])}
30 opt_cfg, results = run_layer1_optimization(
31 config_obj=copy.deepcopy(cfg), runner=runner, requirements=req,
32 target_burn_time=float(req.get("target_burn_time", 6.0)),
33 tolerances={"thrust": 0.10, "apogee": 0.15}, pressure_config=pcfg)
34 perf = results.get("performance") or {}
35 ci = results.get("convergence_info") or {}
36 out = {
37 "label": a.label, "config": a.config,
38 "failure_reasons": list(perf.get("failure_reasons") or []),
39 "gates": {k: v for k, v in perf.items() if k.endswith("_check_passed") or k.endswith("_gate_passed")},
40 "MR": perf.get("MR"), "F": perf.get("F"), "Pc_psi": (perf.get("Pc") or 0)/6894.757,
41 "Isp": perf.get("Isp"), "Lstar": perf.get("layer1_geometry_Lstar_config_m"),
42 "CR": perf.get("layer1_geometry_Ac_over_At"), "eta_cstar": perf.get("eta_cstar"),
43 "R": perf.get("momentum_ratio_R"), "n_F": perf.get("momentum_ratio_n_elements_F"),
44 "n_O": perf.get("momentum_ratio_n_elements_O"),
45 "imp_angle": perf.get("impingement_angle_deg_effective"),
46 "smd_um": perf.get("effective_smd_microns"),
47 "D_over_Dt": (ci.get("best_objective_breakdown") or {}).get("chamber_D_over_Dt"),
48 "Isp_merit": (ci.get("best_objective_breakdown") or {}).get("Isp_merit_s"),
49 "isp_penalty": (ci.get("best_objective_breakdown") or {}).get("isp_penalty"),
50 "chamber_mass_kg": (ci.get("best_objective_breakdown") or {}).get("chamber_mass_kg"),
51 "L_engine_m": (ci.get("best_objective_breakdown") or {}).get("L_engine_m"),
52 "angle_O": perf.get("impingement_angle_O_deg"), "angle_F": perf.get("impingement_angle_F_deg"),
53 "best_objective": ci.get("best_objective"),
54 "valid": perf.get("pressure_candidate_valid"),
55 # the replay must close at the emitted tank pressures (scale 1.0), on this design
56 "validation_tank_pressure_scale": perf.get("layer1_validation_tank_pressure_scale"),
57 "validation_replay_ok": perf.get("validation_replay_ok"),
58 "warnings": list(perf.get("layer1_warnings") or []),
59 "final_rebuild_diag": results.get("final_rebuild_diag"),
60 "breakdown": ci.get("best_objective_breakdown"),
61 }
62 try:
63 g = opt_cfg.injector.geometry
64 out["angle_O"] = float(g.oxidizer.impingement_angle)
65 out["angle_F"] = float(g.fuel.impingement_angle)
66 except AttributeError:
67 pass
68 # emit the optimised config too -- this is the artifact, not the summary.
69 # No try/except: if this cannot be written I want to know why, loudly.
70 import yaml as _yaml
71 from backend.routers.config import config_to_dict
72 _yaml.safe_dump(config_to_dict(opt_cfg),
73 open(a.out.replace('.json', '_config.yaml'), 'w'),
74 sort_keys=False, default_flow_style=False)
75 json.dump(out, open(a.out, 'w'), indent=1, default=str)
76 print("RESULT " + json.dumps({k: v for k, v in out.items()
77 if k not in ("breakdown", "final_rebuild_diag")}, default=str))
78
79if __name__ == '__main__':
80 main()