1"""Example: Pressure sweep - evaluate performance at different tank pressures"""
4import matplotlib.pyplot
as plt
6from pathlib
import Path
8sys.path.insert(0, str(Path(__file__).parent.parent))
15config_path = Path(__file__).parent.parent /
"configs" /
"default.yaml"
16config = load_config(str(config_path))
22print(
"PRESSURE SWEEP: Evaluate Engine Performance at Different Tank Pressures")
24print(
"\nINPUT: Arrays of P_tank_O and P_tank_F")
25print(
"OUTPUT: Solved Pc, Thrust, and all performance parameters")
29print(
"\n Example 1: 2D Pressure Grid (P_tank_O vs P_tank_F)")
34P_tank_O_grid = np.linspace(3.0e6, 8.0e6, 20)
35P_tank_F_grid = np.linspace(2.7e6, 7.6e6, 20)
38P_O_mesh, P_F_mesh = np.meshgrid(P_tank_O_grid, P_tank_F_grid)
41P_O_flat = P_O_mesh.flatten()
42P_F_flat = P_F_mesh.flatten()
44print(f
"Pressure Grid:")
45print(f
" P_tank_O: {len(P_tank_O_grid)} points from {P_tank_O_grid[0]/6894.76:.0f} to {P_tank_O_grid[-1]/6894.76:.0f} psi")
46print(f
" P_tank_F: {len(P_tank_F_grid)} points from {P_tank_F_grid[0]/6894.76:.0f} to {P_tank_F_grid[-1]/6894.76:.0f} psi")
47print(f
" Total combinations: {len(P_O_flat)}")
50print(f
"\nSolving for Pc and calculating thrust at each point...")
51results = runner.evaluate_arrays(P_O_flat, P_F_flat)
54F_2d = results[
'F'].reshape(P_O_mesh.shape) / 1000
55Pc_2d = results[
'Pc'].reshape(P_O_mesh.shape) / 6894.76
56MR_2d = results[
'MR'].reshape(P_O_mesh.shape)
57Isp_2d = results[
'Isp'].reshape(P_O_mesh.shape)
58mdot_total_2d = (results[
'mdot_O'] + results[
'mdot_F']).reshape(P_O_mesh.shape)
61valid_mask = ~np.isnan(results[
'Pc'])
62print(f
"\nResults Summary (valid points only):")
63print(f
" Valid points: {np.sum(valid_mask)}/{len(P_O_flat)}")
64if np.sum(valid_mask) > 0:
65 print(f
" Solved Pc range: {results['Pc'][valid_mask].min()/6894.76:.0f} - {results['Pc'][valid_mask].max()/6894.76:.0f} psi")
66 print(f
" Thrust range: {results['F'][valid_mask].min()/1000:.2f} - {results['F'][valid_mask].max()/1000:.2f} kN")
67 print(f
" MR range: {results['MR'][valid_mask].min():.2f} - {results['MR'][valid_mask].max():.2f}")
68 print(f
" Isp range: {results['Isp'][valid_mask].min():.1f} - {results['Isp'][valid_mask].max():.1f} s")
71fig, axes = plt.subplots(2, 2, figsize=(14, 12))
72fig.suptitle(
"2D Pressure Grid Results (P_tank_O vs P_tank_F)", fontsize=16, fontweight=
"bold")
76contour = ax.contourf(P_O_mesh / 6894.76, P_F_mesh / 6894.76, F_2d, levels=20, cmap=
'viridis')
77ax.set_xlabel(
'Oxidizer Tank Pressure [psi]')
78ax.set_ylabel(
'Fuel Tank Pressure [psi]')
79ax.set_title(
'Thrust [kN]')
80plt.colorbar(contour, ax=ax, label=
'Thrust [kN]')
84contour = ax.contourf(P_O_mesh / 6894.76, P_F_mesh / 6894.76, Pc_2d, levels=20, cmap=
'plasma')
85ax.set_xlabel(
'Oxidizer Tank Pressure [psi]')
86ax.set_ylabel(
'Fuel Tank Pressure [psi]')
87ax.set_title(
'Chamber Pressure [psi] (SOLVED)')
88plt.colorbar(contour, ax=ax, label=
'Pc [psi]')
92contour = ax.contourf(P_O_mesh / 6894.76, P_F_mesh / 6894.76, MR_2d, levels=20, cmap=
'coolwarm')
93ax.set_xlabel(
'Oxidizer Tank Pressure [psi]')
94ax.set_ylabel(
'Fuel Tank Pressure [psi]')
95ax.set_title(
'Mixture Ratio (O/F)')
96plt.colorbar(contour, ax=ax, label=
'O/F Ratio')
100contour = ax.contourf(P_O_mesh / 6894.76, P_F_mesh / 6894.76, Isp_2d, levels=20, cmap=
'inferno')
101ax.set_xlabel(
'Oxidizer Tank Pressure [psi]')
102ax.set_ylabel(
'Fuel Tank Pressure [psi]')
103ax.set_title(
'Specific Impulse [s]')
104plt.colorbar(contour, ax=ax, label=
'Isp [s]')
107output_path = Path(__file__).parent /
"pressure_sweep_results.png"
108plt.savefig(str(output_path), dpi=300, bbox_inches=
'tight')
109print(f
"\n[OK] Saved 2D pressure grid plots to {output_path}")
112print(
"\n Example 2: 1D Slices Through Pressure Grid")
117P_tank_F_fixed = 5.0e6
118P_tank_O_slice = np.linspace(3.0e6, 8.0e6, 30)
119P_tank_F_slice1 = np.full_like(P_tank_O_slice, P_tank_F_fixed)
122P_tank_O_fixed = 6.0e6
123P_tank_F_slice = np.linspace(2.7e6, 7.6e6, 30)
124P_tank_O_slice2 = np.full_like(P_tank_F_slice, P_tank_O_fixed)
126print(f
"Slice 1: P_tank_F fixed at {P_tank_F_fixed/1e6:.1f} MPa, varying P_tank_O")
127results_slice1 = runner.evaluate_arrays(P_tank_O_slice, P_tank_F_slice1)
129print(f
"Slice 2: P_tank_O fixed at {P_tank_O_fixed/1e6:.1f} MPa, varying P_tank_F")
130results_slice2 = runner.evaluate_arrays(P_tank_O_slice2, P_tank_F_slice)
133fig, axes = plt.subplots(2, 2, figsize=(12, 10))
134fig.suptitle(
"1D Slices Through Pressure Grid", fontsize=16, fontweight=
"bold")
138ax.plot(P_tank_O_slice / 6894.76, results_slice1[
'F'] / 1000,
'b-', linewidth=2, marker=
'o', markersize=4,
139 label=f
'P_tank_F = {P_tank_F_fixed/6894.76:.0f} psi')
140ax.plot(P_tank_F_slice / 6894.76, results_slice2[
'F'] / 1000,
'r--', linewidth=2, marker=
's', markersize=4,
141 label=f
'P_tank_O = {P_tank_O_fixed/6894.76:.0f} psi')
142ax.set_xlabel(
'Tank Pressure [psi]')
143ax.set_ylabel(
'Thrust [kN]')
145ax.grid(
True, alpha=0.3)
146ax.set_title(
'Thrust vs Tank Pressure (Slices)')
150ax.plot(P_tank_O_slice / 6894.76, results_slice1[
'Pc'] / 6894.76,
'b-', linewidth=2, marker=
'o', markersize=4,
151 label=f
'P_tank_F = {P_tank_F_fixed/6894.76:.0f} psi')
152ax.plot(P_tank_F_slice / 6894.76, results_slice2[
'Pc'] / 6894.76,
'r--', linewidth=2, marker=
's', markersize=4,
153 label=f
'P_tank_O = {P_tank_O_fixed/6894.76:.0f} psi')
154ax.set_xlabel(
'Tank Pressure [psi]')
155ax.set_ylabel(
'Chamber Pressure [psi] (SOLVED)')
157ax.grid(
True, alpha=0.3)
158ax.set_title(
'Pc vs Tank Pressure (Slices)')
162ax.plot(P_tank_O_slice / 6894.76, results_slice1[
'MR'],
'b-', linewidth=2, marker=
'o', markersize=4,
163 label=f
'P_tank_F = {P_tank_F_fixed/6894.76:.0f} psi')
164ax.plot(P_tank_F_slice / 6894.76, results_slice2[
'MR'],
'r--', linewidth=2, marker=
's', markersize=4,
165 label=f
'P_tank_O = {P_tank_O_fixed/6894.76:.0f} psi')
166ax.set_xlabel(
'Tank Pressure [psi]')
167ax.set_ylabel(
'Mixture Ratio (O/F)')
169ax.grid(
True, alpha=0.3)
170ax.set_title(
'MR vs Tank Pressure (Slices)')
174ax.plot(P_tank_O_slice / 6894.76, results_slice1[
'Isp'],
'b-', linewidth=2, marker=
'o', markersize=4,
175 label=f
'P_tank_F = {P_tank_F_fixed/6894.76:.0f} psi')
176ax.plot(P_tank_F_slice / 6894.76, results_slice2[
'Isp'],
'r--', linewidth=2, marker=
's', markersize=4,
177 label=f
'P_tank_O = {P_tank_O_fixed/6894.76:.0f} psi')
178ax.set_xlabel(
'Tank Pressure [psi]')
179ax.set_ylabel(
'Specific Impulse [s]')
181ax.grid(
True, alpha=0.3)
182ax.set_title(
'Isp vs Tank Pressure (Slices)')
185output_path_slices = Path(__file__).parent /
"pressure_slices.png"
186plt.savefig(str(output_path_slices), dpi=300, bbox_inches=
'tight')
187print(f
"[OK] Saved 1D slice plots to {output_path_slices}")
189print(
"\n" +
"=" * 70)
190print(
"[OK] Pressure sweep complete!")
192print(
"\nKey Points:")
193print(
" • INPUT: Arrays of P_tank_O and P_tank_F")
194print(
" • Pc is SOLVED at each point (not input!)")
195print(
" • All performance parameters calculated from solved Pc")
196print(
" • Can evaluate any number of pressure combinations")