STAR
Cal STAR — firmware, DAQ server, and engine design
Loading...
Searching...
No Matches
comprehensive_optimizer.py
Go to the documentation of this file.
1"""Comprehensive optimization system for pintle engine design.
2
3This module provides end-to-end optimization from vehicle requirements to detailed
4pintle injector and chamber geometry.
5
6Optimization Levels:
71. Vehicle-Level: Flight performance → Propellant masses, tank sizes, COPV
82. Engine-Level: Thrust requirements → Chamber geometry, nozzle, cooling
93. Injector-Level: Performance targets → Pintle dimensions, orifice sizing, angles
104. Material-Level: Heat flux → Ablative thickness, graphite sizing
11
12All levels are coupled and solved iteratively.
13"""
14
15from __future__ import annotations
16
17from typing import Dict, Any, Optional, List, Tuple, Callable
18import numpy as np
19from scipy.optimize import minimize, differential_evolution, Bounds
20from engine.pipeline.config_schemas import PintleEngineConfig, ensure_chamber_geometry
21from engine.core.runner import PintleEngineRunner
22
23
25 """
26 Comprehensive optimizer that solves for all pintle dimensions and chamber geometry.
27
28 Optimizes:
29 - Pintle injector: d_pintle_tip, h_gap, n_orifices, d_orifice, theta_orifice
30 - Chamber: A_throat, A_exit, Lstar, D_chamber
31 - Materials: ablative thickness, graphite thickness
32 - Feed system: tank pressures, COPV sizing
33
34 All coupled with flight simulation and stability analysis.
35 """
36
37 def __init__(self, base_config: PintleEngineConfig):
38 self.base_config = base_config
39 self.runner = PintleEngineRunner(base_config)
41
43 self,
44 target_thrust: float,
45 target_isp: Optional[float] = None,
46 target_mr: Optional[float] = None,
47 P_tank_O: float = 3.0e6, # 3 MPa
48 P_tank_F: float = 3.0e6, # 3 MPa
49 constraints: Optional[Dict[str, Any]] = None,
50 ) -> Dict[str, Any]:
51 """
52 Optimize complete pintle injector geometry.
53
54 Design variables:
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]
64
65 Parameters:
66 -----------
67 target_thrust : float
68 Target thrust [N]
69 target_isp : float, optional
70 Target specific impulse [s]
71 target_mr : float, optional
72 Target mixture ratio
73 P_tank_O : float
74 Oxidizer tank pressure [Pa]
75 P_tank_F : float
76 Fuel tank pressure [Pa]
77 constraints : dict, optional
78 Design constraints
79
80 Returns:
81 --------
82 results : dict
83 Optimized configuration and performance
84 """
85 if constraints is None:
86 constraints = self._default_constraints()
87
88 # Design variable bounds
89 # [d_pintle_tip, h_gap, n_orifices, d_orifice, theta_orifice, A_throat, A_exit, Lstar, D_chamber]
90 bounds = self._setup_pintle_bounds(constraints)
91
92 # Initial guess
93 x0 = self._generate_pintle_initial_guess(target_thrust, constraints)
94
95 history = []
96
97 def objective(x: np.ndarray) -> float:
98 """Minimize error in meeting targets."""
99 try:
100 # Update config
101 config = self._update_config_from_pintle_x(x, self.base_config)
102
103 # Evaluate engine
104 runner = PintleEngineRunner(config)
105 results = runner.evaluate(P_tank_O, P_tank_F)
106
107 # Calculate errors
108 F_actual = results.get("F", 0.0)
109 thrust_error = abs(F_actual - target_thrust) / target_thrust
110
111 Isp_actual = results.get("Isp", 0.0)
112 isp_error = 0.0
113 if target_isp is not None:
114 isp_error = abs(Isp_actual - target_isp) / target_isp
115
116 MR_actual = results.get("MR", 0.0)
117 mr_error = 0.0
118 if target_mr is not None:
119 mr_error = abs(MR_actual - target_mr) / target_mr
120
121 # Stability penalty (check all three types)
122 stability = results.get("stability_results", {})
123
124 # Chugging stability
125 chugging = stability.get("chugging", {})
126 chugging_margin = chugging.get("stability_margin", 0.0)
127
128 # Acoustic stability
129 acoustic = stability.get("acoustic", {})
130 acoustic_margin = acoustic.get("stability_margin", 0.0)
131
132 # Feed system stability
133 feed_system = stability.get("feed_system", {})
134 feed_margin = feed_system.get("stability_margin", 0.0)
135
136 # Default minimums (can be overridden by constraints if provided)
137 min_stability = 1.2
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)
141
142 # Combined stability penalty
143 stability_penalty = chugging_penalty + acoustic_penalty + feed_penalty
144
145 # Combined objective
146 objective_value = (
147 10.0 * thrust_error +
148 5.0 * isp_error +
149 3.0 * mr_error +
150 2.0 * stability_penalty
151 )
152
153 history.append({
154 "x": x.copy(),
155 "F": F_actual,
156 "Isp": Isp_actual,
157 "MR": MR_actual,
158 "stability_margin": stability_margin,
159 "objective": objective_value,
160 })
161
162 return objective_value
163
164 except Exception as e:
165 return 1e6
166
167 # Run optimization
168 result = minimize(
169 objective,
170 x0,
171 method="SLSQP",
172 bounds=bounds,
173 options={"maxiter": 200, "ftol": 1e-6},
174 )
175
176 # Extract optimized config
177 optimized_config = self._update_config_from_pintle_x(result.x, self.base_config)
178 optimized_runner = PintleEngineRunner(optimized_config)
179 final_results = optimized_runner.evaluate(P_tank_O, P_tank_F)
180
181 return {
182 "optimized_config": optimized_config,
183 "optimization_result": result,
184 "performance": final_results,
185 "convergence_history": history,
186 }
187
188 def _default_constraints(self) -> Dict[str, Any]:
189 """Default design constraints for small-scale engines."""
190 return {
191 "min_pintle_tip_diameter": 0.010, # 10mm
192 "max_pintle_tip_diameter": 0.030, # 30mm
193 "min_gap_height": 0.0002, # 0.2mm
194 "max_gap_height": 0.001, # 1mm
195 "min_orifices": 6,
196 "max_orifices": 24,
197 "min_orifice_diameter": 0.001, # 1mm
198 "max_orifice_diameter": 0.005, # 5mm
199 "min_injection_angle": 20.0, # deg
200 "max_injection_angle": 45.0, # deg
201 "min_expansion_ratio": 5.0,
202 "max_expansion_ratio": 20.0,
203 "min_Lstar": 0.8, # Allow down to 0.8m for very small engines
204 "max_Lstar": 2.0, # Up to 2.0m for larger small engines
205 "min_chamber_diameter": 0.05, # 50mm - appropriate for small engines
206 "max_chamber_diameter": 0.15, # 150mm - appropriate for small engines
207 }
208
209 def _setup_pintle_bounds(self, constraints: Dict[str, Any]) -> Bounds:
210 """Set up optimization bounds for pintle variables."""
211 return Bounds(
212 [
213 constraints["min_pintle_tip_diameter"],
214 constraints["min_gap_height"],
215 constraints["min_orifices"],
216 constraints["min_orifice_diameter"],
217 constraints["min_injection_angle"],
218 # Chamber geometry bounds
219 1e-6, # A_throat min
220 1e-5, # A_exit min
221 constraints["min_Lstar"],
222 constraints["min_chamber_diameter"],
223 ],
224 [
225 constraints["max_pintle_tip_diameter"],
226 constraints["max_gap_height"],
227 constraints["max_orifices"],
228 constraints["max_orifice_diameter"],
229 constraints["max_injection_angle"],
230 # Chamber geometry bounds
231 0.01, # A_throat max
232 0.1, # A_exit max
233 constraints["max_Lstar"],
234 constraints["max_chamber_diameter"],
235 ],
236 )
237
239 self, target_thrust: float, constraints: Dict[str, Any]
240 ) -> np.ndarray:
241 """Generate initial guess for pintle optimization."""
242 # Estimate throat area from thrust
243 Pc_estimate = 3.0e6 # 3 MPa
244 Cf_estimate = 1.5
245 A_throat_estimate = target_thrust / (0.7 * Pc_estimate * Cf_estimate)
246
247 # Initial pintle guess
248 d_pintle_guess = 0.015 # 15mm
249 h_gap_guess = 0.0005 # 0.5mm
250 n_orifices_guess = 12
251 d_orifice_guess = 0.003 # 3mm
252 theta_orifice_guess = 30.0 # 30 deg
253
254 # Chamber geometry
255 eps_estimate = 10.0
256 A_exit_estimate = A_throat_estimate * eps_estimate
257 # For small-scale engines: L* typically 1.0-1.5 m (40-60 inches)
258 Lstar_estimate = 1.27 # 50 inches - good default for small engines
259 # Chamber diameter for small engine: typically 3-4x throat diameter
260 # For 20mm throat, chamber ~60-80mm
261 D_chamber_estimate = 0.070 # 70mm - appropriate for small engines
262
263 return np.array([
264 d_pintle_guess,
265 h_gap_guess,
266 n_orifices_guess,
267 d_orifice_guess,
268 theta_orifice_guess,
269 A_throat_estimate,
270 A_exit_estimate,
271 Lstar_estimate,
272 D_chamber_estimate,
273 ])
274
276 self, x: np.ndarray, base_config: PintleEngineConfig
277 ) -> PintleEngineConfig:
278 """Update configuration from pintle optimization variables."""
279 import copy
280 config = copy.deepcopy(base_config)
281
282 d_pintle_tip, h_gap, n_orifices, d_orifice, theta_orifice, A_throat, A_exit, Lstar, D_chamber = x
283
284 # Update injector geometry
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)
293
294 # Ensure chamber_geometry exists
295 if config.chamber_geometry is None:
296 cg = ensure_chamber_geometry(config)
297 else:
298 cg = config.chamber_geometry
299
300 volume = Lstar * A_throat
301
302 # Update chamber_geometry
303 cg.A_throat = float(A_throat)
304 cg.volume = volume
305 cg.Lstar = float(Lstar)
306 cg.chamber_diameter = float(D_chamber)
307
308 # Also update legacy chamber if it exists
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)
314
315 # Calculate chamber length
316 A_chamber = np.pi * (D_chamber / 2.0) ** 2
317 if A_chamber > 0:
318 length = volume / A_chamber
319 else:
320 length = 0.25
321
322 cg.length = length
323 if config.chamber is not None:
324 config.chamber.length = length
325
326 # Update nozzle geometry
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
329
330 cg.A_exit = float(A_exit)
331 cg.exit_diameter = D_exit
332 cg.expansion_ratio = expansion_ratio
333
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
339
340 # Update CEA cache expansion ratio
341 if hasattr(config.combustion, 'cea'):
342 config.combustion.cea.expansion_ratio = expansion_ratio
343
344 return config
345
346
348 """
349 Vehicle-level optimizer that couples flight simulation with engine optimization.
350
351 Takes vehicle requirements (altitude, payload, constraints) and optimizes:
352 - Propellant masses
353 - Tank sizes
354 - Engine geometry (via ComprehensivePintleOptimizer)
355 - COPV sizing
356 """
357
358 def __init__(self, base_config: PintleEngineConfig):
359 self.base_config = base_config
361
363 self,
364 vehicle_requirements: Dict[str, Any],
365 constraints: Optional[Dict[str, Any]] = None,
366 ) -> Dict[str, Any]:
367 """
368 Optimize complete vehicle design.
369
370 Parameters:
371 -----------
372 vehicle_requirements : dict
373 - target_altitude: float [m]
374 - payload_mass: float [kg]
375 - max_length: float [m]
376 - max_diameter: float [m]
377 - recovery_system_mass: float [kg]
378 - avionics_mass: float [kg]
379 - constraints: dict with bounds
380
381 Returns:
382 --------
383 results : dict
384 Optimized vehicle configuration
385 """
386 # This is a placeholder - full implementation would:
387 # 1. Optimize propellant masses and tank sizes
388 # 2. For each candidate, optimize engine geometry
389 # 3. Run flight simulation
390 # 4. Check constraints (altitude, acceleration, etc.)
391 # 5. Minimize total vehicle mass
392
393 # For now, return structure
394 return {
395 "optimized_config": self.base_config,
396 "vehicle_mass": 0.0,
397 "flight_results": {},
398 }
399
#define max(a, b)
Definition Arduino.h:43
Dict[str, Any] optimize_pintle_geometry(self, float target_thrust, Optional[float] target_isp=None, Optional[float] target_mr=None, float P_tank_O=3.0e6, float P_tank_F=3.0e6, Optional[Dict[str, Any]] constraints=None)
PintleEngineConfig _update_config_from_pintle_x(self, np.ndarray x, PintleEngineConfig base_config)
np.ndarray _generate_pintle_initial_guess(self, float target_thrust, Dict[str, Any] constraints)
Dict[str, Any] optimize_vehicle(self, Dict[str, Any] vehicle_requirements, Optional[Dict[str, Any]] constraints=None)