24 L_nozzle: float = 0.0,
25 expansion_ratio: float = 10.0,
26 ablative_config: Optional[AblativeCoolingConfig] =
None,
27 graphite_config: Optional[GraphiteInsertConfig] =
None,
28 recession_chamber: float = 0.0,
29 recession_graphite: float = 0.0,
31) -> Dict[str, np.ndarray]:
33 Calculate chamber geometry - delegates to fixed version.
35 return calculate_chamber_geometry_fixed(
40 expansion_ratio=expansion_ratio,
41 ablative_config=ablative_config,
42 graphite_config=graphite_config,
43 recession_chamber=recession_chamber,
44 recession_graphite=recession_graphite,
53 L_nozzle: float = 0.0,
54 expansion_ratio: float = 10.0,
55 ablative_config: Optional[AblativeCoolingConfig] =
None,
56 graphite_config: Optional[GraphiteInsertConfig] =
None,
57 recession_chamber: float = 0.0,
58 recession_graphite: float = 0.0,
60) -> Dict[str, np.ndarray]:
62 Calculate clear chamber geometry showing actual structure.
65 - 0 to L_chamber: Chamber (ablative liner)
66 - L_chamber: Throat (graphite insert, small axial extent)
67 - L_chamber to L_chamber+L_nozzle: Nozzle (diverging, can be ablative or other)
72 Chamber length [m] (from injector to throat)
74 Chamber inner diameter [m]
78 Nozzle length [m] (diverging section after throat)
79 expansion_ratio : float
80 Nozzle expansion ratio (A_exit / A_throat)
81 ablative_config : AblativeCoolingConfig, optional
82 Ablative configuration
83 graphite_config : GraphiteInsertConfig, optional
84 Graphite configuration
85 recession_chamber : float
86 Cumulative ablative recession [m]
87 recession_graphite : float
88 Cumulative graphite recession [m]
90 Number of points for geometry
95 - positions: Axial positions [m]
96 - R_gas: Gas boundary radius [m]
97 - R_ablative_outer: Ablative outer radius [m] (chamber region only)
98 - R_graphite_outer: Graphite outer radius [m] (throat region only)
99 - R_stainless: Stainless steel outer radius [m]
100 - R_nozzle_gas: Nozzle gas boundary radius [m]
101 - throat_position: Throat axial position [m]
102 - graphite_start: Graphite insert start position [m]
103 - graphite_end: Graphite insert end position [m]
106 A_throat = np.pi * (D_throat / 2.0) ** 2
107 A_exit = A_throat * expansion_ratio
108 D_exit = np.sqrt(4.0 * A_exit / np.pi)
111 L_total = L_chamber + L_nozzle
114 positions = np.linspace(0.0, L_total, n_points)
117 throat_pos = L_chamber
120 R_gas = np.zeros_like(positions)
121 for i, x
in enumerate(positions):
124 R_gas[i] = (D_chamber / 2.0) + recession_chamber
127 x_nozzle = x - throat_pos
128 R_throat = D_throat / 2.0
129 R_exit = D_exit / 2.0
131 R_gas[i] = R_throat + (R_exit - R_throat) * (x_nozzle / L_nozzle)
137 if ablative_config
and ablative_config.enabled:
139 base_thickness =
max(
140 ablative_config.initial_thickness - recession_chamber,
146 ablative_thickness_profile = np.full(n_points, base_thickness)
148 R_ablative_outer = np.zeros_like(positions)
149 for i, x
in enumerate(positions):
152 R_ablative_outer[i] = R_gas[i] + ablative_thickness_profile[i]
155 R_ablative_outer[i] = R_gas[i]
157 R_ablative_outer = R_gas.copy()
160 if graphite_config
and graphite_config.enabled:
162 graphite_axial_half_length = getattr(graphite_config,
'axial_half_length',
None)
163 if graphite_axial_half_length
is None or graphite_axial_half_length <= 0:
166 _gr = locals().get(
'graphite_config')
or locals().get(
'config')
167 _ratio = float(getattr(getattr(_gr,
'graphite_insert', _gr),
'axial_half_length_ratio', 0.75)
or 0.75)
168 graphite_axial_half_length = _ratio * D_throat
170 graphite_start =
max(throat_pos - graphite_axial_half_length, 0.0)
171 graphite_end =
min(throat_pos + graphite_axial_half_length, L_total)
180 heat_flux_profile = np.zeros_like(positions)
181 for i, x
in enumerate(positions):
182 if graphite_start <= x <= graphite_end:
184 distance_from_throat = abs(x - throat_pos)
185 heat_flux_profile[i] = 1e6 * np.exp(-distance_from_throat / (0.1 * D_throat))
187 heat_flux_profile[i] = 0.0
190 graphite_geometry = calculate_graphite_geometry_conforming(
194 positions_chamber=positions,
196 heat_flux_chamber=heat_flux_profile,
197 graphite_config=graphite_config,
198 graphite_start=graphite_start,
199 graphite_end=graphite_end,
200 recession_graphite=recession_graphite,
204 R_graphite_outer = np.zeros_like(positions)
205 for i, x
in enumerate(positions):
206 if graphite_start <= x <= graphite_end:
208 if len(graphite_geometry[
"positions"]) > 0:
209 idx = np.argmin(np.abs(graphite_geometry[
"positions"] - x))
210 if idx < len(graphite_geometry[
"R_outer"]):
211 R_graphite_outer[i] = graphite_geometry[
"R_outer"][idx]
214 R_graphite_outer[i] = R_gas[i] +
max(graphite_config.initial_thickness - recession_graphite, 0.0)
216 R_graphite_outer[i] = R_gas[i] +
max(graphite_config.initial_thickness - recession_graphite, 0.0)
219 R_graphite_outer[i] = R_gas[i]
222 graphite_thickness =
max(graphite_config.initial_thickness - recession_graphite, 0.0)
223 R_graphite_outer = np.zeros_like(positions)
224 for i, x
in enumerate(positions):
225 if graphite_start <= x <= graphite_end:
227 R_graphite_outer[i] = R_gas[i] + graphite_thickness
229 R_graphite_outer[i] = R_gas[i]
231 R_graphite_outer = R_gas.copy()
232 graphite_start = throat_pos
233 graphite_end = throat_pos
238 R_inner_max = np.maximum(R_ablative_outer, R_graphite_outer)
239 R_inner_max_global = np.max(R_inner_max)
242 stainless_thickness = 0.002
246 R_stainless_constant = R_inner_max_global + stainless_thickness
249 R_stainless = np.full_like(positions, R_stainless_constant)
252 "positions": positions,
254 "R_ablative_outer": R_ablative_outer,
255 "R_graphite_outer": R_graphite_outer,
256 "R_stainless": R_stainless,
257 "throat_position": throat_pos,
258 "graphite_start": graphite_start,
259 "graphite_end": graphite_end,
260 "D_chamber": D_chamber,
261 "D_throat": D_throat,
268 Create clear plotly figure showing chamber geometry structure.
271 - Gas boundary (orange)
272 - Ablative liner (brown, chamber region only)
273 - Graphite insert (black, throat region only)
274 - Stainless steel (gray, outer)
275 - Throat marker (red)
277 positions = geometry[
"positions"]
278 R_gas = geometry[
"R_gas"]
279 R_ablative = geometry[
"R_ablative_outer"]
280 R_graphite = geometry[
"R_graphite_outer"]
281 R_stainless = geometry[
"R_stainless"]
282 throat_pos = geometry[
"throat_position"]
283 graphite_start = geometry[
"graphite_start"]
284 graphite_end = geometry[
"graphite_end"]
289 fig.add_trace(go.Scatter(
294 line=dict(color=
'orange', width=3),
296 fillcolor=
'rgba(255, 165, 0, 0.1)',
300 fig.add_trace(go.Scatter(
304 name=
'Gas Boundary (Lower)',
305 line=dict(color=
'orange', width=3),
307 fillcolor=
'rgba(255, 165, 0, 0.1)',
313 chamber_mask = positions < throat_pos
314 if np.any(chamber_mask):
315 chamber_positions = positions[chamber_mask]
316 chamber_ablative = R_ablative[chamber_mask]
318 fig.add_trace(go.Scatter(
322 name=
'Ablative Liner (Chamber)',
323 line=dict(color=
'brown', width=2, dash=
'dash'),
325 fillcolor=
'rgba(139, 69, 19, 0.3)',
328 fig.add_trace(go.Scatter(
332 name=
'Ablative (Lower)',
333 line=dict(color=
'brown', width=2, dash=
'dash'),
335 fillcolor=
'rgba(139, 69, 19, 0.3)',
340 graphite_mask = (positions >= graphite_start) & (positions <= graphite_end)
341 if np.any(graphite_mask):
342 graphite_positions = positions[graphite_mask]
343 graphite_outer = R_graphite[graphite_mask]
345 fig.add_trace(go.Scatter(
346 x=graphite_positions,
349 name=
'Graphite Insert (Throat)',
350 line=dict(color=
'black', width=3),
352 fillcolor=
'rgba(0, 0, 0, 0.4)',
355 fig.add_trace(go.Scatter(
356 x=graphite_positions,
359 name=
'Graphite (Lower)',
360 line=dict(color=
'black', width=3),
362 fillcolor=
'rgba(0, 0, 0, 0.4)',
368 R_under_stainless = np.maximum(R_ablative, R_graphite)
371 fig.add_trace(go.Scatter(
375 name=
'Stainless Steel Case',
376 line=dict(color=
'gray', width=2, dash=
'dot'),
378 fillcolor=
'rgba(128, 128, 128, 0.2)',
381 fig.add_trace(go.Scatter(
385 name=
'Stainless (Lower)',
386 line=dict(color=
'gray', width=2, dash=
'dot'),
388 fillcolor=
'rgba(128, 128, 128, 0.2)',
393 R_throat = geometry[
"D_throat"] / 2.0
394 fig.add_trace(go.Scatter(
399 marker=dict(size=15, color=
'red', symbol=
'circle', line=dict(width=3, color=
'darkred')),
402 fig.add_trace(go.Scatter(
406 marker=dict(size=15, color=
'red', symbol=
'circle', line=dict(width=3, color=
'darkred')),
411 fig.add_trace(go.Scatter(
412 x=[positions[0], positions[-1]],
416 line=dict(color=
'gray', width=1, dash=
'dash'),
421 x_min, x_max = positions.min(), positions.max()
422 y_max =
max(np.max(R_stainless), np.max(R_gas))
425 x_range = x_max - x_min
426 y_range = y_max - y_min
429 x_pad = 0.05 * x_range
430 y_pad = 0.05 * y_range
433 title=
"Chamber Geometry: Gas → Ablative (Chamber) → Graphite (Throat) → Stainless Steel",
434 xaxis_title=
"Axial Position [m]",
435 yaxis_title=
"Radius [m]",
440 range=[x_min - x_pad, x_max + x_pad],
444 range=[y_min - y_pad, y_max + y_pad],