STAR
Cal STAR — firmware, DAQ server, and engine design
Loading...
Searching...
No Matches
chamber_geometry_fixed.py
Go to the documentation of this file.
1"""Fixed chamber geometry calculation with proper constant outer diameter.
2
3This ensures:
41. Constant outer diameter for stainless steel
52. Proper layering: Gas → Ablative → Graphite → Stainless
63. Smooth transitions
74. Correct thickness modeling
8"""
9
10from __future__ import annotations
11
12from typing import Dict, Optional
13import numpy as np
14from engine.pipeline.config_schemas import AblativeCoolingConfig, GraphiteInsertConfig
15
16
18 L_chamber: float,
19 D_chamber: float,
20 D_throat: float,
21 L_nozzle: float = 0.0,
22 expansion_ratio: float = 10.0,
23 ablative_config: Optional[AblativeCoolingConfig] = None,
24 graphite_config: Optional[GraphiteInsertConfig] = None,
25 recession_chamber: float = 0.0,
26 recession_graphite: float = 0.0,
27 n_points: int = 200,
28) -> Dict[str, np.ndarray]:
29 """
30 Calculate chamber geometry with FIXED constant outer diameter.
31
32 Structure:
33 - Gas boundary (inner surface)
34 - Ablative liner (chamber region only)
35 - Graphite insert (throat region only)
36 - Stainless steel (CONSTANT outer diameter)
37
38 Parameters:
39 -----------
40 L_chamber : float
41 Chamber length [m]
42 D_chamber : float
43 Chamber inner diameter [m] (after recession)
44 D_throat : float
45 Throat diameter [m]
46 L_nozzle : float
47 Nozzle length [m]
48 expansion_ratio : float
49 Nozzle expansion ratio
50 ablative_config : AblativeCoolingConfig, optional
51 Ablative configuration
52 graphite_config : GraphiteInsertConfig, optional
53 Graphite configuration
54 recession_chamber : float
55 Chamber recession [m]
56 recession_graphite : float
57 Graphite recession [m]
58 n_points : int
59 Number of points for geometry
60
61 Returns:
62 --------
63 geometry : dict
64 - positions: Axial positions [m]
65 - R_gas: Gas boundary radius [m]
66 - R_ablative_outer: Ablative outer radius [m]
67 - R_graphite_outer: Graphite outer radius [m]
68 - R_stainless: Stainless outer radius [m] (CONSTANT)
69 - throat_position: Throat axial position [m]
70 - graphite_start: Graphite start position [m]
71 - graphite_end: Graphite end position [m]
72 """
73 # Total length
74 L_total = L_chamber + L_nozzle
75
76 # Create positions
77 positions = np.linspace(0.0, L_total, n_points)
78
79 # Throat position
80 throat_pos = L_chamber
81
82 # Calculate exit diameter from expansion ratio
83 A_throat = np.pi * (D_throat / 2.0) ** 2
84 A_exit = A_throat * expansion_ratio
85 D_exit = np.sqrt(4.0 * A_exit / np.pi)
86
87 # Gas boundary radius profile
88 R_gas = np.zeros(n_points)
89 for i, x in enumerate(positions):
90 if x < throat_pos:
91 # Chamber: constant diameter
92 R_gas[i] = D_chamber / 2.0
93 elif x < throat_pos + L_nozzle:
94 # Nozzle: diverging
95 x_nozzle = (x - throat_pos) / L_nozzle if L_nozzle > 0 else 0.0
96 R_throat = D_throat / 2.0
97 R_exit = D_exit / 2.0
98 # Smooth transition
99 R_gas[i] = R_throat + (R_exit - R_throat) * x_nozzle
100 else:
101 # Beyond nozzle
102 R_gas[i] = D_exit / 2.0
103
104 # Ablative liner (chamber region only)
105 if ablative_config and ablative_config.enabled:
106 ablative_thickness = max(
107 ablative_config.initial_thickness - recession_chamber,
108 0.0
109 )
110 R_ablative_outer = np.zeros_like(positions)
111 for i, x in enumerate(positions):
112 if x < throat_pos:
113 R_ablative_outer[i] = R_gas[i] + ablative_thickness
114 else:
115 R_ablative_outer[i] = R_gas[i] # No ablative in nozzle
116 else:
117 R_ablative_outer = R_gas.copy()
118
119 # Graphite insert (throat region only)
120 if graphite_config and graphite_config.enabled:
121 graphite_axial_half_length = getattr(graphite_config, 'axial_half_length', None)
122 if graphite_axial_half_length is None or graphite_axial_half_length <= 0:
123 # Ratio is configurable (graphite_insert.axial_half_length_ratio, default 0.75);
124 # 0.75 was hardcoded here. An explicit axial_half_length overrides it upstream.
125 _gr = locals().get('graphite_config') or locals().get('config')
126 _ratio = float(getattr(getattr(_gr, 'graphite_insert', _gr), 'axial_half_length_ratio', 0.75) or 0.75)
127 graphite_axial_half_length = _ratio * D_throat
128
129 graphite_start = max(throat_pos - graphite_axial_half_length, 0.0)
130 graphite_end = min(throat_pos + graphite_axial_half_length, L_total)
131
132 graphite_thickness = max(
133 graphite_config.initial_thickness - recession_graphite,
134 0.0
135 )
136
137 R_graphite_outer = np.zeros_like(positions)
138 for i, x in enumerate(positions):
139 if graphite_start <= x <= graphite_end:
140 # Graphite conforms to gas boundary + thickness
141 R_graphite_outer[i] = R_gas[i] + graphite_thickness
142 else:
143 R_graphite_outer[i] = R_gas[i]
144 else:
145 R_graphite_outer = R_gas.copy()
146 graphite_start = throat_pos
147 graphite_end = throat_pos
148
149 # Stainless steel: CONSTANT OUTER DIAMETER
150 # Find maximum inner radius (ablative or graphite) across ALL positions
151 R_inner_max = np.maximum(R_ablative_outer, R_graphite_outer)
152 R_inner_max_global = np.max(R_inner_max)
153
154 # Constant thickness (realistic: 2-3mm for small engines)
155 stainless_thickness = 0.0025 # 2.5mm (more realistic)
156
157 # Constant outer radius everywhere
158 R_stainless_constant = R_inner_max_global + stainless_thickness
159 R_stainless = np.full_like(positions, R_stainless_constant)
160
161 return {
162 "positions": positions,
163 "R_gas": R_gas,
164 "R_ablative_outer": R_ablative_outer,
165 "R_graphite_outer": R_graphite_outer,
166 "R_stainless": R_stainless,
167 "throat_position": throat_pos,
168 "graphite_start": graphite_start,
169 "graphite_end": graphite_end,
170 "D_chamber": D_chamber,
171 "D_throat": D_throat,
172 "D_exit": D_exit,
173 }
174
#define min(a, b)
Definition Arduino.h:42
#define max(a, b)
Definition Arduino.h:43
Dict[str, np.ndarray] calculate_chamber_geometry_fixed(float L_chamber, float D_chamber, float D_throat, float L_nozzle=0.0, float expansion_ratio=10.0, Optional[AblativeCoolingConfig] ablative_config=None, Optional[GraphiteInsertConfig] graphite_config=None, float recession_chamber=0.0, float recession_graphite=0.0, int n_points=200)