17 time_history: np.ndarray,
18 geometry_history: List[Dict],
19 config: PintleEngineConfig,
20 impingement_data: Optional[Dict] =
None,
24 Create animated plotly figure showing recession over time.
27 - Limits number of frames to prevent crashes
28 - Handles missing data gracefully
29 - Uses simpler animation structure
31 n_steps = len(time_history)
34 if n_steps > max_frames:
35 step = n_steps // max_frames
36 indices = np.arange(0, n_steps, step)
37 time_history = time_history[indices]
38 geometry_history = [geometry_history[i]
for i
in indices]
39 n_steps = len(time_history)
42 raise ValueError(
"No time steps available for animation")
45 initial_geometry = geometry_history[0]
46 positions = np.array(initial_geometry.get(
"positions", []))
48 if len(positions) == 0:
49 raise ValueError(
"No positions in geometry data")
54 subplot_titles=(
"Chamber Cross-Section",
"Recession Rate"),
55 vertical_spacing=0.15,
56 row_heights=[0.7, 0.3],
62 all_recession_rates = []
64 for geometry
in geometry_history:
66 positions = np.array(geometry.get(
"positions", []))
67 if len(positions) == 0:
71 D_gas_raw = geometry.get(
"D_gas_chamber", [])
72 if isinstance(D_gas_raw, (list, np.ndarray)):
73 D_gas = np.array(D_gas_raw)
74 if len(D_gas) != len(positions):
77 D_gas = np.full_like(positions, float(D_gas[0]))
79 D_gas = np.full_like(positions, float(D_gas[0]))
81 D_gas = np.full_like(positions, float(D_gas_raw)
if D_gas_raw
else 0.1)
84 D_ablative_raw = geometry.get(
"D_ablative_outer", D_gas)
85 if isinstance(D_ablative_raw, (list, np.ndarray)):
86 D_ablative = np.array(D_ablative_raw)
87 if len(D_ablative) != len(positions):
88 if len(D_ablative) == 1:
89 D_ablative = np.full_like(positions, float(D_ablative[0]))
91 D_ablative = np.full_like(positions, float(D_ablative[0]))
93 D_ablative = np.full_like(positions, float(D_ablative_raw)
if D_ablative_raw
else D_gas[0])
96 recession_raw = geometry.get(
"recession", [])
97 if isinstance(recession_raw, (list, np.ndarray)):
98 recession = np.array(recession_raw)
99 if len(recession) != len(positions):
100 if len(recession) == 1:
101 recession = np.full_like(positions, float(recession[0]))
103 recession = np.full_like(positions, float(recession[0]))
105 recession = np.full_like(positions, float(recession_raw)
if recession_raw
else 0.0)
108 R_ablative = D_ablative / 2.0
112 recession_rate = np.zeros_like(positions)
114 all_R_gas.append(R_gas)
115 all_R_ablative.append(R_ablative)
116 all_recession_rates.append(recession_rate)
117 except Exception
as e:
122 positions = np.array(geometry_history[0].get(
"positions", []))
125 fig.add_trace(go.Scatter(
127 y=all_R_gas[0]
if len(all_R_gas) > 0
else np.zeros_like(positions),
130 line=dict(color=
'orange', width=3),
133 fig.add_trace(go.Scatter(
135 y=-all_R_gas[0]
if len(all_R_gas) > 0
else np.zeros_like(positions),
137 name=
'Gas Boundary (Lower)',
138 line=dict(color=
'orange', width=3),
142 fig.add_trace(go.Scatter(
144 y=all_R_ablative[0]
if len(all_R_ablative) > 0
else np.zeros_like(positions),
146 name=
'Ablative Outer',
147 line=dict(color=
'brown', width=2, dash=
'dash'),
149 fillcolor=
'rgba(139, 69, 19, 0.3)',
152 fig.add_trace(go.Scatter(
154 y=-all_R_ablative[0]
if len(all_R_ablative) > 0
else np.zeros_like(positions),
156 name=
'Ablative Outer (Lower)',
157 line=dict(color=
'brown', width=2, dash=
'dash'),
159 fillcolor=
'rgba(139, 69, 19, 0.3)',
164 fig.add_trace(go.Scatter(
166 y=all_recession_rates[0]
if len(all_recession_rates) > 0
else np.zeros_like(positions),
168 name=
'Recession Rate',
169 line=dict(color=
'red', width=2),
174 for i
in range(n_steps):
175 if i >= len(all_R_gas)
or i >= len(all_R_ablative)
or i >= len(all_recession_rates):
179 go.Scatter(x=positions, y=all_R_gas[i], mode=
'lines', line=dict(color=
'orange', width=3)),
180 go.Scatter(x=positions, y=-all_R_gas[i], mode=
'lines', line=dict(color=
'orange', width=3), showlegend=
False),
181 go.Scatter(x=positions, y=all_R_ablative[i], mode=
'lines', line=dict(color=
'brown', width=2, dash=
'dash'),
182 fill=
'tonexty', fillcolor=
'rgba(139, 69, 19, 0.3)'),
183 go.Scatter(x=positions, y=-all_R_ablative[i], mode=
'lines', line=dict(color=
'brown', width=2, dash=
'dash'),
184 fill=
'tonexty', fillcolor=
'rgba(139, 69, 19, 0.3)', showlegend=
False),
185 go.Scatter(x=positions, y=all_recession_rates[i], mode=
'lines', line=dict(color=
'red', width=2)),
188 frames.append(go.Frame(
191 traces=[0, 1, 2, 3, 4]
198 title=
"Ablative Recession Over Time",
208 "frame": {
"duration": 200,
"redraw":
True},
210 "transition": {
"duration": 100}
217 "frame": {
"duration": 0,
"redraw":
False},
219 "transition": {
"duration": 0}
233 "frame": {
"duration": 100,
"redraw":
True},
235 "transition": {
"duration": 100}
240 for i, t
in enumerate(time_history[:len(frames)])
246 fig.update_xaxes(title_text=
"Axial Position [m]", row=1, col=1)
247 fig.update_yaxes(title_text=
"Radius [m]", row=1, col=1, scaleanchor=
"x", scaleratio=1)
248 fig.update_xaxes(title_text=
"Axial Position [m]", row=2, col=1)
249 fig.update_yaxes(title_text=
"Recession Rate [mm/s]", row=2, col=1)