29 design_requirements: Dict[str, Any],
30 constraints: Dict[str, Any],
31 initial_guess: Optional[Dict[str, float]] =
None,
34 Optimize chamber geometry to meet design requirements.
38 design_requirements : dict
39 - target_thrust: float [N] - Desired thrust
40 - target_burn_time: float [s] - Desired burn time
41 - target_stability_margin: float - Minimum stability margin (e.g., 1.2 = 20% margin)
42 - P_tank_O: float [Pa] - Oxidizer tank pressure
43 - P_tank_F: float [Pa] - Fuel tank pressure
44 - target_Isp: Optional[float] [s] - Desired specific impulse (optional)
47 - max_chamber_length: float [m] - Maximum chamber length
48 - max_chamber_diameter: float [m] - Maximum chamber diameter
49 - min_Lstar: float [m] - Minimum L* for combustion
50 - max_Lstar: float [m] - Maximum L* (weight/structure)
51 - min_expansion_ratio: float - Minimum expansion ratio
52 - max_expansion_ratio: float - Maximum expansion ratio
53 - manufacturing_tolerance: float [m] - Manufacturing tolerance for areas
54 - max_wall_thickness: float [m] - Maximum wall thickness (structural)
55 - min_wall_thickness: float [m] - Minimum wall thickness (structural)
57 initial_guess : dict, optional
58 - A_throat: float [m²]
61 - chamber_diameter: float [m]
66 - optimized_config: PintleEngineConfig - Optimized configuration
67 - performance: dict - Performance metrics
68 - convergence_history: list - Optimization history
69 - diagnostics: dict - System diagnostics
72 target_thrust = design_requirements[
"target_thrust"]
73 target_burn_time = design_requirements.get(
"target_burn_time", 10.0)
74 target_stability_margin = design_requirements.get(
"target_stability_margin", 1.2)
75 P_tank_O = design_requirements[
"P_tank_O"]
76 P_tank_F = design_requirements[
"P_tank_F"]
77 target_Isp = design_requirements.get(
"target_Isp",
None)
80 if initial_guess
is None:
85 initial_guess[
"A_throat"],
86 initial_guess[
"A_exit"],
87 initial_guess[
"Lstar"],
88 initial_guess.get(
"chamber_diameter", 0.1),
98 def objective(x: np.ndarray) -> float:
99 """Minimize error in meeting design requirements."""
106 results = runner.evaluate(P_tank_O, P_tank_F)
109 F_actual = results.get(
"F", 0.0)
110 thrust_error = abs(F_actual - target_thrust) / target_thrust
if target_thrust > 0
else 1.0
112 Isp_actual = results.get(
"Isp", 0.0)
114 if target_Isp
is not None and target_Isp > 0:
115 isp_error = abs(Isp_actual - target_Isp) / target_Isp
118 stability = results.get(
"stability_results", {})
119 chugging = stability.get(
"chugging", {})
120 stability_margin = chugging.get(
"stability_margin", 0.0)
121 if target_stability_margin > 0:
122 stability_error =
max(0.0, target_stability_margin - stability_margin) / target_stability_margin
124 stability_error = 0.0
if stability_margin >= 0.5
else 1.0
128 10.0 * thrust_error +
130 3.0 * stability_error
138 "thrust_error": thrust_error,
139 "isp_error": isp_error,
140 "stability_error": stability_error,
141 "objective": objective_value,
144 return objective_value
146 except Exception
as e:
151 constraints_list = self.
_setup_constraints(constraints, design_requirements, P_tank_O, P_tank_F)
159 constraints=constraints_list,
160 options={
"maxiter": 100,
"ftol": 1e-6},
166 final_results = optimized_runner.evaluate(P_tank_O, P_tank_F)
169 diagnostics = self.
diagnostics.diagnose_all(P_tank_O, P_tank_F)
173 optimized_config, optimized_runner, target_burn_time, P_tank_O, P_tank_F
177 cg = ensure_chamber_geometry(optimized_config)
179 "A_throat": cg.A_throat,
182 "chamber_diameter": cg.chamber_diameter,
183 "chamber_length": cg.length,
184 "expansion_ratio": cg.expansion_ratio,
188 "optimized_config": optimized_config,
189 "optimization_result": result,
190 "performance": final_results,
191 "convergence_history": history,
192 "diagnostics": diagnostics,
193 "burn_analysis": burn_analysis,
194 "design_requirements": design_requirements,
195 "constraints": constraints,
196 "optimized_parameters": optimized_params,
237 """Set up optimization bounds."""
243 min_eps = constraints.get(
"min_expansion_ratio", 3.0)
244 max_eps = constraints.get(
"max_expansion_ratio", 30.0)
246 A_exit_min = A_throat_min * min_eps
247 A_exit_max = A_throat_max * max_eps
249 Lstar_min = constraints.get(
"min_Lstar", 0.5)
250 Lstar_max = constraints.get(
"max_Lstar", 2.5)
252 chamber_diameter_min = 0.05
253 chamber_diameter_max = constraints.get(
"max_chamber_diameter", 0.3)
256 [A_throat_min, A_exit_min, Lstar_min, chamber_diameter_min],
257 [A_throat_max, A_exit_max, Lstar_max, chamber_diameter_max],
261 self, constraints: Dict, design_requirements: Dict, P_tank_O: float, P_tank_F: float
263 """Set up optimization constraints including airframe and system constraints."""
267 def expansion_ratio_constraint(x):
268 A_throat, A_exit = x[0], x[1]
269 eps = A_exit / A_throat
if A_throat > 0
else 1.0
270 min_eps = constraints.get(
"min_expansion_ratio", 3.0)
271 max_eps = constraints.get(
"max_expansion_ratio", 30.0)
272 return [eps - min_eps, max_eps - eps]
274 constraint_list.append({
276 "fun":
lambda x: expansion_ratio_constraint(x)[0],
278 constraint_list.append({
280 "fun":
lambda x: expansion_ratio_constraint(x)[1],
284 def chamber_length_constraint(x):
285 Lstar, chamber_diameter = x[2], x[3]
288 V_chamber = Lstar * A_throat
289 A_chamber = np.pi * (chamber_diameter / 2) ** 2
290 L_chamber = V_chamber / A_chamber
if A_chamber > 0
else 0.0
291 max_length = constraints.get(
"max_chamber_length", 1.0)
293 airframe_max_length = constraints.get(
"max_vehicle_length",
None)
294 if airframe_max_length
is not None:
295 max_length =
min(max_length, airframe_max_length)
296 return max_length - L_chamber
298 constraint_list.append({
300 "fun": chamber_length_constraint,
304 def chamber_diameter_constraint(x):
305 chamber_diameter = x[3]
306 max_diameter = constraints.get(
"max_chamber_diameter", 0.3)
308 airframe_max_diameter = constraints.get(
"max_vehicle_diameter",
None)
309 if airframe_max_diameter
is not None:
310 max_diameter =
min(max_diameter, airframe_max_diameter)
311 return max_diameter - chamber_diameter
313 constraint_list.append({
315 "fun": chamber_diameter_constraint,
319 def weight_constraint(x):
323 results = runner.evaluate(P_tank_O, P_tank_F)
327 A_throat, A_exit, Lstar, chamber_diameter = x[0], x[1], x[2], x[3]
330 L_chamber = (Lstar * A_throat) / (np.pi * (chamber_diameter / 2) ** 2)
if chamber_diameter > 0
else 0.25
331 chamber_wall_area = np.pi * chamber_diameter * L_chamber
332 chamber_wall_volume = chamber_wall_area * 0.003
333 chamber_wall_mass = chamber_wall_volume * 8000.0
337 if config.ablative_cooling
and config.ablative_cooling.enabled:
338 ablative_thickness = config.ablative_cooling.initial_thickness
339 ablative_area = chamber_wall_area * config.ablative_cooling.coverage_fraction
340 ablative_volume = ablative_area * ablative_thickness
341 ablative_mass = ablative_volume * config.ablative_cooling.material_density
345 if hasattr(config,
'graphite_insert')
and config.graphite_insert
and config.graphite_insert.enabled:
346 graphite_thickness = config.graphite_insert.initial_thickness
347 graphite_area = np.pi * (np.sqrt(4.0 * A_throat / np.pi) / 2.0) ** 2
348 graphite_volume = graphite_area * graphite_thickness
349 graphite_mass = graphite_volume * config.graphite_insert.material_density
352 nozzle_length = constraints.get(
"nozzle_length", 0.1)
353 nozzle_wall_thickness = 0.002
354 D_exit = np.sqrt(4.0 * A_exit / np.pi)
355 D_throat = np.sqrt(4.0 * A_throat / np.pi)
356 nozzle_avg_diameter = (D_exit + D_throat) / 2.0
357 nozzle_wall_area = np.pi * nozzle_avg_diameter * nozzle_length
358 nozzle_wall_volume = nozzle_wall_area * nozzle_wall_thickness
359 nozzle_wall_mass = nozzle_wall_volume * 8000.0
362 total_mass = chamber_wall_mass + ablative_mass + graphite_mass + nozzle_wall_mass
365 max_weight = constraints.get(
"max_engine_weight",
None)
366 if max_weight
is not None:
367 return max_weight - total_mass
374 max_weight = constraints.get(
"max_engine_weight",
None)
375 if max_weight
is not None:
376 constraint_list.append({
378 "fun": weight_constraint,
382 def stability_constraint(x):
386 results = runner.evaluate(P_tank_O, P_tank_F)
388 stability = results.get(
"stability_results", {})
391 chugging = stability.get(
"chugging", {})
392 chugging_margin = chugging.get(
"stability_margin", 0.0)
395 acoustic = stability.get(
"acoustic", {})
396 acoustic_margin = acoustic.get(
"stability_margin", 0.0)
399 feed_system = stability.get(
"feed_system", {})
400 feed_margin = feed_system.get(
"stability_margin", 0.0)
403 min_stability = design_requirements.get(
"target_stability_margin", 1.2)
404 chugging_min = design_requirements.get(
"chugging_margin_min", min_stability)
405 acoustic_min = design_requirements.get(
"acoustic_margin_min", min_stability)
406 feed_min = design_requirements.get(
"feed_stability_min", min_stability)
410 chugging_margin - chugging_min,
411 acoustic_margin - acoustic_min,
412 feed_margin - feed_min
420 constraint_list.append({
422 "fun": stability_constraint,
426 def manufacturing_constraint(x):
427 A_throat, A_exit = x[0], x[1]
428 tolerance = constraints.get(
"manufacturing_tolerance", 0.0001)
431 min_area = np.pi * (tolerance / 2.0) ** 2
432 return min(A_throat - min_area, A_exit - min_area)
434 constraint_list.append({
436 "fun": manufacturing_constraint,
439 return constraint_list
442 """Update configuration from optimization variables."""
444 config = copy.deepcopy(base_config)
446 A_throat, A_exit, Lstar, chamber_diameter = x[0], x[1], x[2], x[3]
449 if config.chamber_geometry
is None:
450 cg = ensure_chamber_geometry(config)
452 cg = config.chamber_geometry
455 volume = Lstar * A_throat
456 A_chamber = np.pi * (chamber_diameter / 2.0) ** 2
458 length = volume / A_chamber
463 D_exit = np.sqrt(4.0 * A_exit / np.pi)
if A_exit > 0
else 0.0
464 expansion_ratio = A_exit / A_throat
if A_throat > 0
else 1.0
467 cg.A_throat = A_throat
472 cg.chamber_diameter = chamber_diameter
473 cg.exit_diameter = D_exit
474 cg.expansion_ratio = expansion_ratio
477 if config.chamber
is not None:
478 config.chamber.A_throat = A_throat
479 config.chamber.volume = volume
480 config.chamber.Lstar = Lstar
481 config.chamber.chamber_inner_diameter = chamber_diameter
482 config.chamber.length = length
483 if config.nozzle
is not None:
484 config.nozzle.A_throat = A_throat
485 config.nozzle.A_exit = A_exit
486 config.nozzle.expansion_ratio = expansion_ratio
487 config.nozzle.exit_diameter = D_exit
490 if hasattr(config.combustion,
'cea'):
491 config.combustion.cea.expansion_ratio = expansion_ratio
494 if config.regen_cooling
is not None and config.regen_cooling.enabled:
495 config.regen_cooling.chamber_inner_diameter = chamber_diameter