24 parser = argparse.ArgumentParser(
25 description=
"Extract thrust curve from engine config"
30 default=Path(
"engine_sim/configs/default.yaml"),
31 help=
"Engine config path (relative to project root)",
33 parser.add_argument(
"--project-root", type=Path, default=Path.cwd())
35 "--json", action=
"store_true", help=
"Output JSON for downstream use"
42 help=
"Write thrust curve (time_s, thrust_N) to CSV for FSW",
44 args = parser.parse_args()
46 project_root = args.project_root.resolve()
47 config_path = project_root / args.config
48 if not config_path.exists():
49 print(f
"Config not found: {config_path}", file=sys.stderr)
57 generate_pressure_curve_from_segments,
60 cfg = load_engine_config(str(config_path))
61 pc = cfg.pressure_curves
63 print(
"No pressure_curves in config", file=sys.stderr)
66 n_points = pc.n_points
69 "length_ratio": s.length_ratio,
71 "start_pressure": s.start_pressure_pa,
72 "end_pressure": s.end_pressure_pa,
75 for s
in pc.lox_segments
79 "length_ratio": s.length_ratio,
81 "start_pressure": s.start_pressure_pa,
82 "end_pressure": s.end_pressure_pa,
85 for s
in pc.fuel_segments
88 P_lox = generate_pressure_curve_from_segments(lox_segments, n_points)
89 P_fuel = generate_pressure_curve_from_segments(fuel_segments, n_points)
91 runner = PintleEngineRunner(cfg)
92 thrust = np.zeros(n_points)
93 for i
in range(n_points):
95 res = runner.evaluate(
96 P_tank_O=float(P_lox[i]), P_tank_F=float(P_fuel[i]), silent=
True
98 thrust[i] = float(res.get(
"F", np.nan))
102 valid = np.isfinite(thrust)
103 F_min = float(np.min(thrust[valid]))
if np.any(valid)
else 0.0
104 F_max = float(np.max(thrust[valid]))
if np.any(valid)
else 0.0
105 P_fuel_min = float(np.min(P_fuel))
106 P_fuel_max = float(np.max(P_fuel))
107 P_lox_min = float(np.min(P_lox))
108 P_lox_max = float(np.max(P_lox))
110 if args.output
is not None:
111 burn_time_s = pc.target_burn_time_s
112 time_s = np.linspace(0.0, burn_time_s, n_points)
113 out_path = args.output
114 out_path.parent.mkdir(parents=
True, exist_ok=
True)
115 with open(out_path,
"w")
as f:
116 f.write(
"time_s,thrust_N\n")
117 for t, F
in zip(time_s, thrust):
119 f.write(f
"{t:.6f},{F:.2f}\n")
121 f
"Wrote thrust curve to {out_path} ({n_points} points, {burn_time_s:.2f} s)"
128 "thrust_min_N": F_min,
129 "thrust_max_N": F_max,
130 "P_u_fuel_min_Pa": P_fuel_min,
131 "P_u_fuel_max_Pa": P_fuel_max,
132 "P_u_ox_min_Pa": P_lox_min,
133 "P_u_ox_max_Pa": P_lox_max,
134 "n_points": n_points,
135 "burn_time_s": pc.target_burn_time_s,
137 print(json.dumps(out, indent=2))
138 elif args.output
is None:
139 print(f
"Thrust: {F_min:.0f} – {F_max:.0f} N")
140 print(f
"P_u_fuel: {P_fuel_min/1e6:.2f} – {P_fuel_max/1e6:.2f} MPa")
141 print(f
"P_u_ox: {P_lox_min/1e6:.2f} – {P_lox_max/1e6:.2f} MPa")
142 print(f
"Points: {n_points}, burn_time: {pc.target_burn_time_s} s")