82 """Get chamber geometry for visualization.
84 Returns the geometry arrays needed to render the chamber cross-section:
85 - Gas boundary (inner surface)
86 - Ablative liner (chamber region)
87 - Graphite insert (throat region)
88 - Stainless steel case (outer)
90 if not session.app_state.has_config():
93 detail=
"No config loaded. Upload a config file first."
96 config = session.app_state.config
102 chamber_geom = getattr(config,
'chamber_geometry',
None)
103 chamber = getattr(config,
'chamber',
None)
104 nozzle = getattr(config,
'nozzle',
None)
107 if chamber_geom
is None and (chamber
is None or nozzle
is None):
110 detail=
"Must provide either 'chamber_geometry' section or both 'chamber' and 'nozzle' sections"
114 if chamber_geom
is not None:
116 D_chamber = getattr(chamber_geom,
'chamber_diameter', 0.08)
117 D_exit = getattr(chamber_geom,
'exit_diameter', 0.1)
118 expansion_ratio = getattr(chamber_geom,
'expansion_ratio', 8.0)
119 A_throat = getattr(chamber_geom,
'A_throat',
None)
120 L_chamber = getattr(chamber_geom,
'length',
None)
123 D_chamber = getattr(chamber,
'chamber_inner_diameter', 0.08)
if chamber
else 0.08
124 D_exit = (getattr(chamber,
'exit_diameter',
None)
if chamber
else None)
or (getattr(nozzle,
'exit_diameter', 0.1)
if nozzle
else 0.1)
125 expansion_ratio = getattr(nozzle,
'expansion_ratio', 8.0)
if nozzle
else 8.0
126 A_throat = (getattr(chamber,
'A_throat',
None)
if chamber
else None)
or (getattr(nozzle,
'A_throat', 1e-4)
if nozzle
else 1e-4)
127 L_chamber = getattr(chamber,
'length', 0.15)
if chamber
else 0.15
132 A_exit = np.pi * (D_exit / 2.0) ** 2
133 A_throat = A_exit / expansion_ratio
135 D_throat = np.sqrt(4 * A_throat / np.pi)
136 A_exit = A_throat * expansion_ratio
138 if L_chamber
is None:
142 L_nozzle = getattr(nozzle,
'length',
None)
if nozzle
else None
145 L_nozzle = (D_exit - D_throat) / (2 * np.tan(np.radians(15)))
148 ablative_cfg: AblativeCoolingConfig |
None =
None
149 ablative_enabled =
False
150 if hasattr(config,
'ablative_cooling')
and config.ablative_cooling:
151 ablative_cfg = config.ablative_cooling
152 ablative_enabled = getattr(ablative_cfg,
'enabled',
False)
155 graphite_cfg: GraphiteInsertConfig |
None =
None
156 graphite_enabled =
False
157 if hasattr(config,
'graphite_insert')
and config.graphite_insert:
158 graphite_cfg = config.graphite_insert
159 graphite_enabled = getattr(graphite_cfg,
'enabled',
False)
162 geometry = calculate_chamber_geometry_fixed(
167 expansion_ratio=expansion_ratio,
168 ablative_config=ablative_cfg
if ablative_enabled
else None,
169 graphite_config=graphite_cfg
if graphite_enabled
else None,
170 recession_chamber=0.0,
171 recession_graphite=0.0,
178 chamber_contour_x = []
179 chamber_contour_y = []
181 Cf_ideal_solved =
None
182 A_throat_solved =
None
183 chamber_contour_method =
None
187 use_solved_plot =
False
188 if chamber_geom
is not None:
189 required_attrs = [
'A_throat',
'A_exit',
'volume',
'Lstar',
'chamber_diameter',
'length']
191 hasattr(chamber_geom, attr)
and getattr(chamber_geom, attr)
is not None
192 for attr
in required_attrs
195 use_solved_plot =
True
201 chamber_pts, table_data, lengths = solved_chamber_plot(
202 area_throat=chamber_geom.A_throat,
203 area_exit=chamber_geom.A_exit,
204 volume_chamber=chamber_geom.volume,
205 lstar=chamber_geom.Lstar,
206 chamber_diameter=chamber_geom.chamber_diameter,
207 length=chamber_geom.length,
214 chamber_contour_x = chamber_pts[:, 0].tolist()
215 chamber_contour_y = chamber_pts[:, 1].tolist()
218 Cf_solved = getattr(chamber_geom,
'Cf',
None)
219 Cf_ideal_solved = getattr(chamber_geom,
'Cf_ideal',
None)
220 A_throat_solved = chamber_geom.A_throat
221 chamber_contour_method =
"solved"
230 if chamber_geom
is not None:
232 Pc_design = getattr(chamber_geom,
'design_pressure', 2.0e6)
233 F_design = getattr(chamber_geom,
'design_thrust', 5000.0)
234 MR = getattr(chamber_geom,
'design_MR', 2.55)
235 Lstar = getattr(chamber_geom,
'Lstar', 1.0)
236 D_chamber_design = getattr(chamber_geom,
'chamber_diameter', D_chamber)
237 D_exit_design = getattr(chamber_geom,
'exit_diameter', D_exit)
238 nozzle_eff = getattr(chamber_geom,
'nozzle_efficiency', 0.95)
241 Pc_design = getattr(chamber,
'design_pressure', 2.0e6)
if chamber
else 2.0e6
242 F_design = getattr(chamber,
'design_thrust', 5000.0)
if chamber
else 5000.0
243 MR = getattr(chamber,
'design_MR', 2.55)
if chamber
else 2.55
244 Lstar = getattr(chamber,
'Lstar', 1.0)
if chamber
else 1.0
245 D_chamber_design = getattr(chamber,
'chamber_inner_diameter', D_chamber)
if chamber
else D_chamber
246 D_exit_design = getattr(chamber,
'exit_diameter', D_exit)
if chamber
else (getattr(nozzle,
'exit_diameter', D_exit)
if nozzle
else D_exit)
247 nozzle_eff = getattr(nozzle,
'efficiency', 0.95)
if nozzle
else 0.95
251 if hasattr(config,
'environment')
and config.environment:
252 elevation = getattr(config.environment,
'elevation', 0.0)
255 Pa = 101325.0 * (1.0 - 2.25577e-5 * elevation) ** 5.25588
258 cea_config = config.combustion.cea
if hasattr(config,
'combustion')
and hasattr(config.combustion,
'cea')
else None
260 if cea_config
is not None:
264 chamber_pts, table_data, total_length, solver_info = solve_chamber_geometry_with_cea(
266 thrust_design=F_design,
269 diameter_inner=D_chamber_design,
270 diameter_exit=D_exit_design,
273 nozzle_efficiency=nozzle_eff,
280 chamber_contour_x = chamber_pts[:, 0].tolist()
281 chamber_contour_y = chamber_pts[:, 1].tolist()
284 Cf_solved = solver_info.get(
'final_Cf')
285 Cf_ideal_solved = solver_info.get(
'final_Cf_ideal')
286 A_throat_solved = solver_info.get(
'final_A_throat')
287 chamber_contour_method =
"cea_iterative"
289 except Exception
as e:
293 warnings.warn(f
"Chamber contour solver failed: {e}")
294 chamber_contour_method =
"failed"
299 nozzle_pts, x_first, y_first = rao(
300 area_throat=A_throat,
309 nozzle_x = (nozzle_pts[:, 0] + L_chamber).tolist()
310 nozzle_y = nozzle_pts[:, 1].tolist()
311 nozzle_method =
"rao_garcia"
312 except Exception
as e:
316 nozzle_method = f
"fallback_conical (rao failed: {str(e)[:50]})"
319 t_abl_opt_mm: float |
None =
None
320 t_gra_opt_mm: float |
None =
None
325 layer3_status = session.optimizer.layer3_status
326 if layer3_status.get(
"results")
is not None:
327 l3_results = layer3_status[
"results"]
328 summary = l3_results.get(
"summary", {})
329 t_abl_opt_mm = summary.get(
"optimized_ablative_thickness")
330 if t_abl_opt_mm
is not None:
331 t_abl_opt_mm *= 1000.0
332 t_gra_opt_mm = summary.get(
"optimized_graphite_thickness")
333 if t_gra_opt_mm
is not None:
334 t_gra_opt_mm *= 1000.0
337 t_abl_config_mm: float |
None =
None
338 t_gra_config_mm: float |
None =
None
339 if ablative_cfg
is not None:
341 t_abl_config_mm = float(getattr(ablative_cfg,
"initial_thickness", 0.0)) * 1000.0
343 t_abl_config_mm =
None
344 if graphite_cfg
is not None:
346 t_gra_config_mm = float(getattr(graphite_cfg,
"initial_thickness", 0.0)) * 1000.0
348 t_gra_config_mm =
None
351 positions=geometry[
"positions"].tolist(),
352 R_gas=geometry[
"R_gas"].tolist(),
353 R_ablative_outer=geometry[
"R_ablative_outer"].tolist(),
354 R_graphite_outer=geometry[
"R_graphite_outer"].tolist(),
355 R_stainless=geometry[
"R_stainless"].tolist(),
356 throat_position=float(geometry[
"throat_position"]),
357 graphite_start=float(geometry[
"graphite_start"]),
358 graphite_end=float(geometry[
"graphite_end"]),
359 D_chamber=float(geometry[
"D_chamber"]),
360 D_throat=float(geometry[
"D_throat"]),
361 D_exit=float(geometry[
"D_exit"]),
364 expansion_ratio=expansion_ratio,
365 ablative_enabled=ablative_enabled,
366 graphite_enabled=graphite_enabled,
369 nozzle_method=nozzle_method,
370 chamber_contour_x=chamber_contour_x,
371 chamber_contour_y=chamber_contour_y,
373 Cf_ideal=Cf_ideal_solved,
374 A_throat_solved=A_throat_solved,
375 chamber_contour_method=chamber_contour_method,
376 t_abl_opt_mm=t_abl_opt_mm,
377 t_gra_opt_mm=t_gra_opt_mm,
378 t_abl_config_mm=t_abl_config_mm,
379 t_gra_config_mm=t_gra_config_mm,
382 except Exception
as e:
385 detail=f
"Failed to calculate geometry: {str(e)}"