45 target_isp: Optional[float] =
None,
46 target_mr: Optional[float] =
None,
47 P_tank_O: float = 3.0e6,
48 P_tank_F: float = 3.0e6,
49 constraints: Optional[Dict[str, Any]] =
None,
52 Optimize complete pintle injector geometry.
55 - d_pintle_tip: Pintle tip diameter [m]
56 - h_gap: Annular gap height [m]
57 - n_orifices: Number of LOX orifices
58 - d_orifice: LOX orifice diameter [m]
59 - theta_orifice: LOX injection angle [deg]
60 - A_throat: Throat area [m²]
61 - A_exit: Exit area [m²]
62 - Lstar: Characteristic length [m]
63 - D_chamber: Chamber diameter [m]
69 target_isp : float, optional
70 Target specific impulse [s]
71 target_mr : float, optional
74 Oxidizer tank pressure [Pa]
76 Fuel tank pressure [Pa]
77 constraints : dict, optional
83 Optimized configuration and performance
85 if constraints
is None:
97 def objective(x: np.ndarray) -> float:
98 """Minimize error in meeting targets."""
105 results = runner.evaluate(P_tank_O, P_tank_F)
108 F_actual = results.get(
"F", 0.0)
109 thrust_error = abs(F_actual - target_thrust) / target_thrust
111 Isp_actual = results.get(
"Isp", 0.0)
113 if target_isp
is not None:
114 isp_error = abs(Isp_actual - target_isp) / target_isp
116 MR_actual = results.get(
"MR", 0.0)
118 if target_mr
is not None:
119 mr_error = abs(MR_actual - target_mr) / target_mr
122 stability = results.get(
"stability_results", {})
125 chugging = stability.get(
"chugging", {})
126 chugging_margin = chugging.get(
"stability_margin", 0.0)
129 acoustic = stability.get(
"acoustic", {})
130 acoustic_margin = acoustic.get(
"stability_margin", 0.0)
133 feed_system = stability.get(
"feed_system", {})
134 feed_margin = feed_system.get(
"stability_margin", 0.0)
138 chugging_penalty =
max(0.0, min_stability - chugging_margin)
139 acoustic_penalty =
max(0.0, min_stability - acoustic_margin)
140 feed_penalty =
max(0.0, min_stability - feed_margin)
143 stability_penalty = chugging_penalty + acoustic_penalty + feed_penalty
147 10.0 * thrust_error +
150 2.0 * stability_penalty
158 "stability_margin": stability_margin,
159 "objective": objective_value,
162 return objective_value
164 except Exception
as e:
173 options={
"maxiter": 200,
"ftol": 1e-6},
179 final_results = optimized_runner.evaluate(P_tank_O, P_tank_F)
182 "optimized_config": optimized_config,
183 "optimization_result": result,
184 "performance": final_results,
185 "convergence_history": history,
239 self, target_thrust: float, constraints: Dict[str, Any]
241 """Generate initial guess for pintle optimization."""
245 A_throat_estimate = target_thrust / (0.7 * Pc_estimate * Cf_estimate)
248 d_pintle_guess = 0.015
250 n_orifices_guess = 12
251 d_orifice_guess = 0.003
252 theta_orifice_guess = 30.0
256 A_exit_estimate = A_throat_estimate * eps_estimate
258 Lstar_estimate = 1.27
261 D_chamber_estimate = 0.070
276 self, x: np.ndarray, base_config: PintleEngineConfig
277 ) -> PintleEngineConfig:
278 """Update configuration from pintle optimization variables."""
280 config = copy.deepcopy(base_config)
282 d_pintle_tip, h_gap, n_orifices, d_orifice, theta_orifice, A_throat, A_exit, Lstar, D_chamber = x
285 if hasattr(config.injector,
'geometry'):
286 if hasattr(config.injector.geometry,
'fuel'):
287 config.injector.geometry.fuel.d_pintle_tip = float(d_pintle_tip)
288 config.injector.geometry.fuel.h_gap = float(h_gap)
289 if hasattr(config.injector.geometry,
'lox'):
290 config.injector.geometry.lox.n_orifices = int(round(n_orifices))
291 config.injector.geometry.lox.d_orifice = float(d_orifice)
292 config.injector.geometry.lox.theta_orifice = float(theta_orifice)
295 if config.chamber_geometry
is None:
296 cg = ensure_chamber_geometry(config)
298 cg = config.chamber_geometry
300 volume = Lstar * A_throat
303 cg.A_throat = float(A_throat)
305 cg.Lstar = float(Lstar)
306 cg.chamber_diameter = float(D_chamber)
309 if config.chamber
is not None:
310 config.chamber.A_throat = float(A_throat)
311 config.chamber.volume = volume
312 config.chamber.Lstar = float(Lstar)
313 config.chamber.chamber_inner_diameter = float(D_chamber)
316 A_chamber = np.pi * (D_chamber / 2.0) ** 2
318 length = volume / A_chamber
323 if config.chamber
is not None:
324 config.chamber.length = length
327 D_exit = np.sqrt(4.0 * A_exit / np.pi)
if A_exit > 0
else 0.0
328 expansion_ratio = A_exit / A_throat
if A_throat > 0
else 1.0
330 cg.A_exit = float(A_exit)
331 cg.exit_diameter = D_exit
332 cg.expansion_ratio = expansion_ratio
334 if config.nozzle
is not None:
335 config.nozzle.A_throat = float(A_throat)
336 config.nozzle.A_exit = float(A_exit)
337 config.nozzle.expansion_ratio = expansion_ratio
338 config.nozzle.exit_diameter = D_exit
341 if hasattr(config.combustion,
'cea'):
342 config.combustion.cea.expansion_ratio = expansion_ratio