Create app.py
Browse files
app.py
ADDED
|
@@ -0,0 +1,220 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Import required libraries
|
| 2 |
+
import gradio as gr
|
| 3 |
+
import numpy as np
|
| 4 |
+
import matplotlib.pyplot as plt
|
| 5 |
+
from scipy.interpolate import interp1d
|
| 6 |
+
|
| 7 |
+
# Constants
|
| 8 |
+
G_EARTH = 9.81 # m/s^2, gravitational acceleration
|
| 9 |
+
R_EARTH = 6371000 # m, Earth's radius
|
| 10 |
+
MU_EARTH = 3.986e14 # m^3/s^2, Earth's gravitational parameter
|
| 11 |
+
R_GAS = 287 # J/(kg·K), specific gas constant for air
|
| 12 |
+
ATM_PRESSURE_SEA = 101325 # Pa, sea-level pressure
|
| 13 |
+
TEMP_SEA = 288.15 # K, sea-level temperature
|
| 14 |
+
LAPSE_RATE = 0.0065 # K/m, temperature lapse rate
|
| 15 |
+
|
| 16 |
+
# Atmospheric model (simplified ISA)
|
| 17 |
+
def atmospheric_conditions(altitude):
|
| 18 |
+
temp = TEMP_SEA - LAPSE_RATE * altitude if altitude < 11000 else 216.65
|
| 19 |
+
pressure = ATM_PRESSURE_SEA * (temp / TEMP_SEA) ** (-G_EARTH / (LAPSE_RATE * R_GAS))
|
| 20 |
+
density = pressure / (R_GAS * temp)
|
| 21 |
+
return temp, pressure, density
|
| 22 |
+
|
| 23 |
+
# Orbital velocity calculation
|
| 24 |
+
def orbital_velocity(altitude):
|
| 25 |
+
return np.sqrt(MU_EARTH / (R_EARTH + altitude))
|
| 26 |
+
|
| 27 |
+
# Drag force calculation
|
| 28 |
+
def drag_force(velocity, altitude, diameter, cd=0.3):
|
| 29 |
+
_, _, rho = atmospheric_conditions(altitude)
|
| 30 |
+
area = np.pi * (diameter / 2) ** 2
|
| 31 |
+
return 0.5 * rho * velocity ** 2 * cd * area
|
| 32 |
+
|
| 33 |
+
# Advanced rocket simulation function
|
| 34 |
+
def simulate_rocket(
|
| 35 |
+
engine_type, num_engines, isp_vac, thrust_vac, chamber_pressure, nozzle_diameter,
|
| 36 |
+
structural_material, structural_thickness, diameter, height, staging_enabled,
|
| 37 |
+
stage1_fuel_mass, stage1_oxidizer_mass, stage2_fuel_mass, stage2_oxidizer_mass,
|
| 38 |
+
control_system, payload_mass, tank_material, insulation_thickness, target_altitude
|
| 39 |
+
):
|
| 40 |
+
"""
|
| 41 |
+
Simulate a detailed rocket design with staging, aerodynamics, and thermal effects.
|
| 42 |
+
|
| 43 |
+
Parameters:
|
| 44 |
+
- engine_type (str): "Liquid", "Solid", "Hybrid"
|
| 45 |
+
- num_engines (int): Number of engines per stage
|
| 46 |
+
- isp_vac (float): Specific impulse in vacuum (s)
|
| 47 |
+
- thrust_vac (float): Thrust per engine in vacuum (N)
|
| 48 |
+
- chamber_pressure (float): Engine chamber pressure (Pa)
|
| 49 |
+
- nozzle_diameter (float): Nozzle exit diameter (m)
|
| 50 |
+
- structural_material (str): "Aluminum", "Titanium", "Composite"
|
| 51 |
+
- structural_thickness (float): Structural skin thickness (mm)
|
| 52 |
+
- diameter (float): Rocket diameter (m)
|
| 53 |
+
- height (float): Total rocket height (m)
|
| 54 |
+
- staging_enabled (bool): Use two stages if True
|
| 55 |
+
- stage1_fuel_mass, stage1_oxidizer_mass (float): Propellant masses for stage 1 (kg)
|
| 56 |
+
- stage2_fuel_mass, stage2_oxidizer_mass (float): Propellant masses for stage 2 (kg)
|
| 57 |
+
- control_system (str): "Gimbaled", "TVS", "RCS"
|
| 58 |
+
- payload_mass (float): Payload mass (kg)
|
| 59 |
+
- tank_material (str): "Aluminum", "Stainless Steel", "Composite"
|
| 60 |
+
- insulation_thickness (float): Thermal insulation thickness (mm)
|
| 61 |
+
- target_altitude (float): Desired orbital altitude (m)
|
| 62 |
+
|
| 63 |
+
Returns:
|
| 64 |
+
- dict: Performance metrics and design feasibility
|
| 65 |
+
- matplotlib.figure: Radar chart of normalized performance
|
| 66 |
+
"""
|
| 67 |
+
# Material properties
|
| 68 |
+
material_density = {"Aluminum": 2700, "Titanium": 4500, "Composite": 1600} # kg/m^3
|
| 69 |
+
material_strength = {"Aluminum": 310e6, "Titanium": 950e6, "Composite": 600e6} # Pa
|
| 70 |
+
tank_density = {"Aluminum": 2700, "Stainless Steel": 8000, "Composite": 1600} # kg/m^3
|
| 71 |
+
|
| 72 |
+
# Structural mass calculation
|
| 73 |
+
surface_area = 2 * np.pi * (diameter / 2) * height + 2 * np.pi * (diameter / 2) ** 2
|
| 74 |
+
structural_mass = surface_area * (structural_thickness / 1000) * material_density[structural_material]
|
| 75 |
+
|
| 76 |
+
# Tank mass (cylindrical tanks with spherical ends)
|
| 77 |
+
tank_volume = (stage1_fuel_mass + stage1_oxidizer_mass + stage2_fuel_mass + stage2_oxidizer_mass) / 1000 # m^3
|
| 78 |
+
tank_surface_area = 4 * np.pi * (diameter / 2) ** 2 + np.pi * diameter * height / 2
|
| 79 |
+
tank_mass = tank_surface_area * (insulation_thickness / 1000) * tank_density[tank_material]
|
| 80 |
+
|
| 81 |
+
# Total masses
|
| 82 |
+
stage1_prop_mass = stage1_fuel_mass + stage1_oxidizer_mass
|
| 83 |
+
stage2_prop_mass = stage2_fuel_mass + stage2_oxidizer_mass
|
| 84 |
+
stage1_dry_mass = structural_mass * 0.7 + tank_mass * 0.7 + num_engines * 50 # Engine mass ~50 kg each
|
| 85 |
+
stage2_dry_mass = structural_mass * 0.3 + tank_mass * 0.3 + (num_engines * 50 if staging_enabled else 0)
|
| 86 |
+
total_mass_initial = stage1_dry_mass + stage1_prop_mass + stage2_dry_mass + stage2_prop_mass + payload_mass
|
| 87 |
+
|
| 88 |
+
# Thrust and ISP at sea level
|
| 89 |
+
isp_sea = isp_vac * (1 - (ATM_PRESSURE_SEA / chamber_pressure) * (nozzle_diameter / diameter) ** 2)
|
| 90 |
+
thrust_sea = thrust_vac * (isp_sea / isp_vac)
|
| 91 |
+
total_thrust_sea = num_engines * thrust_sea
|
| 92 |
+
|
| 93 |
+
# Check lift-off capability
|
| 94 |
+
twr_sea = total_thrust_sea / (total_mass_initial * G_EARTH)
|
| 95 |
+
if twr_sea <= 1.1: # Minimum TWR for lift-off with margin
|
| 96 |
+
return {"error": "Insufficient thrust for lift-off"}, plt.figure(figsize=(6, 6))
|
| 97 |
+
|
| 98 |
+
# Delta-v calculation with staging
|
| 99 |
+
if staging_enabled:
|
| 100 |
+
delta_v1 = isp_vac * G_EARTH * np.log((stage1_dry_mass + stage1_prop_mass + stage2_dry_mass + stage2_prop_mass + payload_mass) /
|
| 101 |
+
(stage1_dry_mass + stage2_dry_mass + stage2_prop_mass + payload_mass))
|
| 102 |
+
delta_v2 = isp_vac * G_EARTH * np.log((stage2_dry_mass + stage2_prop_mass + payload_mass) /
|
| 103 |
+
(stage2_dry_mass + payload_mass))
|
| 104 |
+
total_delta_v = delta_v1 + delta_v2
|
| 105 |
+
else:
|
| 106 |
+
total_delta_v = isp_vac * G_EARTH * np.log(total_mass_initial / (structural_mass + tank_mass + payload_mass))
|
| 107 |
+
total_delta_v /= 1000 # Convert to km/s
|
| 108 |
+
|
| 109 |
+
# Orbital requirement
|
| 110 |
+
v_orbit = orbital_velocity(target_altitude) / 1000 # km/s
|
| 111 |
+
orbit_capable = total_delta_v >= v_orbit + 1.5 # 1.5 km/s margin for losses
|
| 112 |
+
|
| 113 |
+
# Structural integrity (stress analysis)
|
| 114 |
+
max_pressure = chamber_pressure * 1.5 # Safety factor
|
| 115 |
+
hoop_stress = max_pressure * (diameter / 2) / (structural_thickness / 1000)
|
| 116 |
+
structural_integrity = min(100, 100 * (material_strength[structural_material] / hoop_stress))
|
| 117 |
+
|
| 118 |
+
# Aerodynamic drag losses (simplified)
|
| 119 |
+
avg_velocity = total_delta_v * 1000 / 10 # m/s, rough ascent average
|
| 120 |
+
drag_loss = drag_force(avg_velocity, target_altitude / 2, diameter) / (total_mass_initial * G_EARTH) * 100 # km/s
|
| 121 |
+
|
| 122 |
+
# Thermal protection adequacy
|
| 123 |
+
reentry_heat_flux = 50000 * (target_altitude / 200000) ** 0.5 # W/m^2, simplified
|
| 124 |
+
insulation_effectiveness = insulation_thickness / 10 # Arbitrary scaling
|
| 125 |
+
thermal_score = min(100, 100 * insulation_effectiveness / (reentry_heat_flux / 10000))
|
| 126 |
+
|
| 127 |
+
# Cost estimation
|
| 128 |
+
cost = (structural_mass * {"Aluminum": 50, "Titanium": 150, "Composite": 200}[structural_material] +
|
| 129 |
+
tank_mass * {"Aluminum": 40, "Stainless Steel": 60, "Composite": 180}[tank_material] +
|
| 130 |
+
num_engines * 200000 + payload_mass * 100)
|
| 131 |
+
|
| 132 |
+
# Stability based on control system
|
| 133 |
+
stability = {"Gimbaled": 90, "TVS": 80, "RCS": 70}[control_system]
|
| 134 |
+
|
| 135 |
+
# Output dictionary
|
| 136 |
+
results = {
|
| 137 |
+
"Delta-v (km/s)": f"{total_delta_v:.2f}",
|
| 138 |
+
"TWR (Sea Level)": f"{twr_sea:.2f}",
|
| 139 |
+
"Structural Integrity (%)": f"{structural_integrity:.1f}",
|
| 140 |
+
"Cost ($)": f"{cost:.2f}",
|
| 141 |
+
"Stability (%)": f"{stability}",
|
| 142 |
+
"Orbit Capable": "Yes" if orbit_capable else "No",
|
| 143 |
+
"Thermal Protection (%)": f"{thermal_score:.1f}",
|
| 144 |
+
"Drag Loss (km/s)": f"{drag_loss:.2f}"
|
| 145 |
+
}
|
| 146 |
+
|
| 147 |
+
# Radar chart
|
| 148 |
+
metrics = [total_delta_v / 15, twr_sea / 5, structural_integrity / 100, stability / 100, thermal_score / 100]
|
| 149 |
+
labels = ["Delta-v", "TWR", "Integrity", "Stability", "Thermal"]
|
| 150 |
+
angles = np.linspace(0, 2 * np.pi, len(labels), endpoint=False).tolist()
|
| 151 |
+
metrics += metrics[:1]
|
| 152 |
+
angles += angles[:1]
|
| 153 |
+
|
| 154 |
+
fig = plt.figure(figsize=(6, 6))
|
| 155 |
+
ax = fig.add_subplot(111, polar=True)
|
| 156 |
+
ax.fill(angles, metrics, alpha=0.25)
|
| 157 |
+
ax.set_xticks(angles[:-1])
|
| 158 |
+
ax.set_xticklabels(labels)
|
| 159 |
+
ax.set_title("Rocket Performance Profile")
|
| 160 |
+
|
| 161 |
+
return results, fig
|
| 162 |
+
|
| 163 |
+
# Gradio interface
|
| 164 |
+
with gr.Blocks(title="Advanced Rocket Design Simulator") as app:
|
| 165 |
+
gr.Markdown(
|
| 166 |
+
"""
|
| 167 |
+
# Advanced Rocket Design Simulator
|
| 168 |
+
Design a rocket with detailed engineering parameters. This tool simulates performance metrics
|
| 169 |
+
considering aerodynamics, staging, thermal effects, and structural integrity. Adjust inputs
|
| 170 |
+
to create a feasible design for your target altitude.
|
| 171 |
+
"""
|
| 172 |
+
)
|
| 173 |
+
|
| 174 |
+
with gr.Row():
|
| 175 |
+
with gr.Column():
|
| 176 |
+
gr.Markdown("### Rocket Configuration")
|
| 177 |
+
engine_type = gr.Dropdown(choices=["Liquid", "Solid", "Hybrid"], value="Liquid", label="Engine Type")
|
| 178 |
+
num_engines = gr.Slider(1, 10, value=2, step=1, label="Number of Engines")
|
| 179 |
+
isp_vac = gr.Slider(250, 450, value=320, step=5, label="ISP (Vacuum, s)")
|
| 180 |
+
thrust_vac = gr.Slider(50000, 2000000, value=500000, step=10000, label="Thrust (Vacuum, N)")
|
| 181 |
+
chamber_pressure = gr.Slider(1e6, 20e6, value=7e6, step=1e5, label="Chamber Pressure (Pa)")
|
| 182 |
+
nozzle_diameter = gr.Slider(0.1, 2.0, value=0.5, step=0.05, label="Nozzle Diameter (m)")
|
| 183 |
+
|
| 184 |
+
structural_material = gr.Dropdown(choices=["Aluminum", "Titanium", "Composite"], value="Aluminum", label="Structural Material")
|
| 185 |
+
structural_thickness = gr.Slider(1, 20, value=5, step=0.5, label="Structural Thickness (mm)")
|
| 186 |
+
diameter = gr.Slider(1, 10, value=3, step=0.1, label="Rocket Diameter (m)")
|
| 187 |
+
height = gr.Slider(5, 50, value=20, step=1, label="Rocket Height (m)")
|
| 188 |
+
|
| 189 |
+
staging_enabled = gr.Checkbox(label="Enable Two-Stage Design", value=False)
|
| 190 |
+
stage1_fuel_mass = gr.Slider(100, 5000, value=2000, step=50, label="Stage 1 Fuel Mass (kg)")
|
| 191 |
+
stage1_oxidizer_mass = gr.Slider(100, 5000, value=2000, step=50, label="Stage 1 Oxidizer Mass (kg)")
|
| 192 |
+
stage2_fuel_mass = gr.Slider(0, 2000, value=500, step=50, label="Stage 2 Fuel Mass (kg)")
|
| 193 |
+
stage2_oxidizer_mass = gr.Slider(0, 2000, value=500, step=50, label="Stage 2 Oxidizer Mass (kg)")
|
| 194 |
+
|
| 195 |
+
control_system = gr.Dropdown(choices=["Gimbaled", "TVS", "RCS"], value="Gimbaled", label="Control System")
|
| 196 |
+
payload_mass = gr.Slider(10, 1000, value=200, step=10, label="Payload Mass (kg)")
|
| 197 |
+
tank_material = gr.Dropdown(choices=["Aluminum", "Stainless Steel", "Composite"], value="Aluminum", label="Tank Material")
|
| 198 |
+
insulation_thickness = gr.Slider(1, 50, value=10, step=1, label="Insulation Thickness (mm)")
|
| 199 |
+
target_altitude = gr.Slider(100000, 2000000, value=400000, step=10000, label="Target Altitude (m)")
|
| 200 |
+
|
| 201 |
+
with gr.Column():
|
| 202 |
+
gr.Markdown("### Performance Metrics")
|
| 203 |
+
outputs = gr.JSON(label="Design Results")
|
| 204 |
+
radar_plot = gr.Plot(label="Performance Profile")
|
| 205 |
+
|
| 206 |
+
# Inputs list
|
| 207 |
+
inputs = [
|
| 208 |
+
engine_type, num_engines, isp_vac, thrust_vac, chamber_pressure, nozzle_diameter,
|
| 209 |
+
structural_material, structural_thickness, diameter, height, staging_enabled,
|
| 210 |
+
stage1_fuel_mass, stage1_oxidizer_mass, stage2_fuel_mass, stage2_oxidizer_mass,
|
| 211 |
+
control_system, payload_mass, tank_material, insulation_thickness, target_altitude
|
| 212 |
+
]
|
| 213 |
+
|
| 214 |
+
# Event handling
|
| 215 |
+
app.load(fn=simulate_rocket, inputs=inputs, outputs=[outputs, radar_plot])
|
| 216 |
+
for input_component in inputs:
|
| 217 |
+
input_component.change(fn=simulate_rocket, inputs=inputs, outputs=[outputs, radar_plot])
|
| 218 |
+
|
| 219 |
+
# Launch the app
|
| 220 |
+
app.launch()
|