""" AlgoRythm Prandtl Aero — Deterministic Physics Core All calculations in SI units internally, output in mm for PicoGK. Validated against: RL-10, Merlin 1D, Raptor 3, Rutherford """ import math G0 = 9.80665 # m/s² # === PROPELLANT DATABASE === PROPELLANTS = { "LOX/LH2": {"gamma": 1.20, "Tc": 3500, "cstar": 2360, "Isp_vac": 450, "Isp_sl": 363, "Rspec": 692.0, "MW": 12.0, "rho_f": 70.8, "rho_o": 1141, "OF": 5.5}, "LOX/RP1": {"gamma": 1.24, "Tc": 3670, "cstar": 1800, "Isp_vac": 338, "Isp_sl": 311, "Rspec": 362.0, "MW": 23.0, "rho_f": 810, "rho_o": 1141, "OF": 2.34}, "LOX/CH4": {"gamma": 1.20, "Tc": 3540, "cstar": 1860, "Isp_vac": 363, "Isp_sl": 330, "Rspec": 519.0, "MW": 16.0, "rho_f": 422.6, "rho_o": 1141, "OF": 3.5}, "N2O4/UDMH":{"gamma": 1.25, "Tc": 3150, "cstar": 1720, "Isp_vac": 320, "Isp_sl": 285, "Rspec": 340.0, "MW": 24.5, "rho_f": 793, "rho_o": 1440, "OF": 2.0}, } # === MATERIAL DATABASE === MATERIALS = { "Inconel_718": {"sigma_y_MPa": 1035, "sigma_u_MPa": 1240, "E_GPa": 205, "rho": 8190, "k_W_mK": 11.4, "Tmax_K": 990, "CTE": 13e-6}, "Inconel_625": {"sigma_y_MPa": 490, "sigma_u_MPa": 827, "E_GPa": 205, "rho": 8440, "k_W_mK": 9.8, "Tmax_K": 980, "CTE": 12.8e-6}, "CuCrZr": {"sigma_y_MPa": 320, "sigma_u_MPa": 440, "E_GPa": 130, "rho": 8900, "k_W_mK": 320, "Tmax_K": 573, "CTE": 17e-6}, "GRCop84": {"sigma_y_MPa": 207, "sigma_u_MPa": 345, "E_GPa": 115, "rho": 8810, "k_W_mK": 285, "Tmax_K": 700, "CTE": 17.7e-6}, "Haynes_230": {"sigma_y_MPa": 390, "sigma_u_MPa": 860, "E_GPa": 211, "rho": 8970, "k_W_mK": 8.9, "Tmax_K": 1420, "CTE": 12.7e-6}, "Ti6Al4V": {"sigma_y_MPa": 880, "sigma_u_MPa": 950, "E_GPa": 114, "rho": 4430, "k_W_mK": 6.7, "Tmax_K": 600, "CTE": 8.6e-6}, "C103_Niobium": {"sigma_y_MPa": 270, "sigma_u_MPa": 400, "E_GPa": 95, "rho": 8850, "k_W_mK": 43, "Tmax_K": 1650, "CTE": 6.5e-6}, } # === REFERENCE ENGINES (Validation Anchors) === REFERENCE_ENGINES = { "RL-10A": {"thrust_N": 110000, "Pc_bar": 44, "prop": "LOX/LH2", "Isp": 465, "Dt_mm": 102, "eps": 84, "cycle": "Expander"}, "Merlin_1D": {"thrust_N": 845000, "Pc_bar": 97, "prop": "LOX/RP1", "Isp": 311, "Dt_mm": 262, "eps": 16, "cycle": "Gas Generator"}, "Raptor_3": {"thrust_N": 2690000, "Pc_bar": 350, "prop": "LOX/CH4", "Isp": 350, "Dt_mm": 200, "eps": 40, "cycle": "Full-Flow Staged"}, "Vulcain_2": {"thrust_N": 1359000, "Pc_bar": 115, "prop": "LOX/LH2", "Isp": 434, "Dt_mm": 270, "eps": 58, "cycle": "Gas Generator"}, "Rutherford": {"thrust_N": 25000, "Pc_bar": 12, "prop": "LOX/RP1", "Isp": 343, "Dt_mm": 60, "eps": 12.6, "cycle": "Electric Pump"}, "RD-180": {"thrust_N": 4152000, "Pc_bar": 267, "prop": "LOX/RP1", "Isp": 338, "Dt_mm": 330, "eps": 36.9, "cycle": "Staged Combustion"}, "Vinci": {"thrust_N": 180000, "Pc_bar": 61, "prop": "LOX/LH2", "Isp": 465, "Dt_mm": 135, "eps": 240, "cycle": "Expander"}, "BE-4": {"thrust_N": 2400000, "Pc_bar": 134, "prop": "LOX/CH4", "Isp": 340, "Dt_mm": 310, "eps": 35, "cycle": "Oxidizer-Rich Staged"}, } # === CORE PHYSICS FUNCTIONS === def calc_thrust_coefficient(gamma, eps, Pc_Pa, Pe_Pa, Pa_Pa=0): """Thrust coefficient Cf from isentropic relations (NASA SP-125)""" g = gamma term1 = (2*g*g/(g-1)) * (2/(g+1))**((g+1)/(g-1)) pr = Pe_Pa / Pc_Pa term2 = 1 - pr**((g-1)/g) Cf = math.sqrt(term1 * term2) + eps * (Pe_Pa - Pa_Pa) / Pc_Pa return max(Cf, 0.8) # Physical lower bound def calc_exit_pressure(gamma, eps): """Iterative solve for Pe/Pc from area ratio using Newton's method""" g = gamma pr = 0.01 # Initial guess Pe/Pc for _ in range(50): M2_term = (2/(g-1)) * (pr**(-(g-1)/g) - 1) if M2_term <= 0: pr *= 0.5 continue Me = math.sqrt(M2_term) eps_calc = (1/Me) * ((2/(g+1)) * (1 + (g-1)/2 * Me*Me))**((g+1)/(2*(g-1))) deps = eps_calc - eps if abs(deps) < 0.001: break pr *= (1 - 0.1 * deps / max(eps, 1)) pr = max(pr, 1e-6) return pr def calc_throat_area(thrust_N, Pc_bar, Cf): """A* = F / (Pc × Cf) — returns m²""" Pc_Pa = Pc_bar * 1e5 return thrust_N / (Pc_Pa * Cf) def throat_area_to_diameter_mm(At_m2): """Convert throat area (m²) to diameter (mm)""" Dt_m = math.sqrt(4 * At_m2 / math.pi) return Dt_m * 1000 # m -> mm def calc_exit_diameter_mm(Dt_mm, eps): """De = Dt × sqrt(ε)""" return Dt_mm * math.sqrt(eps) def calc_mass_flow(thrust_N, Isp_s): """mdot = F / (Isp × g0) — returns kg/s""" return thrust_N / (Isp_s * G0) def calc_cstar(Tc, gamma, Rspec): """c* = sqrt(gamma*Rspec*Tc) / (gamma * sqrt((2/(gamma+1))^((gamma+1)/(gamma-1))))""" g = gamma num = math.sqrt(g * Rspec * Tc) denom = g * math.sqrt((2/(g+1))**((g+1)/(g-1))) return num / denom def calc_nozzle_length_mm(Dt_mm, De_mm, half_angle_deg=15.0, percent_bell=80.0): """Rao 80% bell nozzle length""" Rt = Dt_mm / 2 Re = De_mm / 2 Ln_conical = (Re - Rt) / math.tan(math.radians(half_angle_deg)) return Ln_conical * (percent_bell / 100.0) def calc_chamber_length_mm(cstar, Pc_bar, At_m2, mdot, Lstar_m=1.0): """Chamber length from L* (characteristic length)""" Vc_m3 = Lstar_m * At_m2 Ac_m2 = At_m2 * 3.0 # Contraction ratio ~3:1 Lc_m = Vc_m3 / Ac_m2 return Lc_m * 1000 # mm def calc_chamber_diameter_mm(Dt_mm, contraction_ratio=3.0): """Dc = Dt × sqrt(CR)""" return Dt_mm * math.sqrt(contraction_ratio) def calc_hoop_stress_MPa(Pc_bar, inner_D_mm, wall_t_mm): """Thin-wall hoop stress: σ = P×r / t (all consistent units)""" P_MPa = Pc_bar * 0.1 # bar -> MPa r_mm = inner_D_mm / 2.0 return (P_MPa * r_mm) / wall_t_mm def calc_margin_of_safety(sigma_yield_MPa, sigma_applied_MPa, safety_factor=1.25): """MoS = (σ_yield / (SF × σ_applied)) - 1""" return (sigma_yield_MPa / (safety_factor * sigma_applied_MPa)) - 1.0 def calc_bartz_heat_flux(Pc_bar, Dt_mm, cstar, mdot, Tc, gamma, Pr=0.5, mu=5e-5): """Bartz correlation for convective heat transfer at throat (W/m²)""" Dt_m = Dt_mm / 1000 At_m2 = math.pi * (Dt_m/2)**2 g = gamma sigma = 1.0 # Simplified correction factor Cp = gamma * 287 / (gamma - 1) # Approximate Cp h_g = (0.026 / (Dt_m**0.2)) * ((mu**0.2 * Cp) / (Pr**0.6)) * \ ((Pc_bar * 1e5 / cstar)**0.8) * (Dt_m / 1.0)**0.1 * sigma q = h_g * (Tc * 0.9) # Adiabatic wall temp ~ 0.9*Tc return q / 1e6 # MW/m² def calc_coolant_velocity(q_MW_m2, Tc_wall_K, Tc_cool_K, rho_cool, Cp_cool): """Required coolant velocity for given heat flux""" q_W = q_MW_m2 * 1e6 dT = Tc_wall_K - Tc_cool_K return q_W / (rho_cool * Cp_cool * dT) def select_wall_thickness_mm(Pc_bar, Dt_mm, material_key, target_MoS=0.5): """Auto-select wall thickness for target MoS""" mat = MATERIALS[material_key] sigma_y = mat["sigma_y_MPa"] P_MPa = Pc_bar * 0.1 r_mm = Dt_mm / 2 # σ = Pr/t -> t = Pr / (σ_y / (SF * (1+MoS))) t_mm = (P_MPa * r_mm * 1.25 * (1 + target_MoS)) / sigma_y return max(round(t_mm, 2), 0.5) # Minimum 0.5mm def select_cycle(thrust_N, propellant): """Deterministic power cycle selection based on physics""" p = PROPELLANTS[propellant] if thrust_N > 1500000: if "LH2" in propellant: return "Gas Generator", "High thrust LH2: GG avoids LH2 pre-burner complexity (Vulcain heritage)." return "Staged Combustion", "High thrust >1.5MN requires high Pc for compact design. Staged combustion maximizes Isp." elif thrust_N > 500000: if "CH4" in propellant: return "Full-Flow Staged Combustion", "Medium-high thrust CH4: FFSC maximizes Isp and eliminates turbine coking (Raptor heritage)." return "Gas Generator", "Medium thrust: GG provides simplicity with adequate performance." elif "LH2" in propellant and thrust_N < 200000: return "Expander Cycle", "Low thrust LH2: High heat capacity enables turbine drive from jacket heat alone (RL-10 heritage)." elif thrust_N < 5000: return "Pressure-Fed", "Very low thrust: Pressure-fed eliminates turbopump mass and complexity." else: return "Gas Generator", "Default: GG provides best reliability-to-performance ratio." def validate_against_reference(thrust_N, Pc_bar, prop, Dt_calc_mm): """Check calculated throat against nearest reference engine""" best_match = None best_dist = float('inf') for name, ref in REFERENCE_ENGINES.items(): if ref["prop"] == prop: dist = abs(ref["thrust_N"] - thrust_N) / ref["thrust_N"] if dist < best_dist: best_dist = dist best_match = (name, ref) if best_match and best_dist < 0.3: name, ref = best_match ratio = Dt_calc_mm / ref["Dt_mm"] scaled_ratio = ratio * math.sqrt(ref["Pc_bar"] / Pc_bar) * math.sqrt(thrust_N / ref["thrust_N"]) return name, ref["Dt_mm"], abs(1 - scaled_ratio) < 0.5 return None, None, True # === L-PBF MANUFACTURING PARAMETERS === LPBF_PARAMS = { "Inconel_718": {"power_W": 285, "speed_mm_s": 960, "hatch_um": 110, "layer_um": 40}, "Inconel_625": {"power_W": 275, "speed_mm_s": 800, "hatch_um": 100, "layer_um": 40}, "CuCrZr": {"power_W": 370, "speed_mm_s": 600, "hatch_um": 90, "layer_um": 30}, "GRCop84": {"power_W": 350, "speed_mm_s": 500, "hatch_um": 100, "layer_um": 30}, "Ti6Al4V": {"power_W": 280, "speed_mm_s": 1200, "hatch_um": 120, "layer_um": 30}, "C103_Niobium": {"power_W": 300, "speed_mm_s": 700, "hatch_um": 100, "layer_um": 40}, } # === ADVANCED PHYSICS — DEEPER ENCODING === def calc_exit_mach(gamma, eps): """Solve for exit Mach number from area ratio (Newton iteration)""" g = gamma Me = 3.0 # Supersonic initial guess for _ in range(100): f = (1/Me) * ((2/(g+1)) * (1 + (g-1)/2 * Me**2))**((g+1)/(2*(g-1))) - eps df = -f/Me + (1/Me) * ((g+1)/(2*(g-1))) * ((2/(g+1)) * (1 + (g-1)/2 * Me**2))**((g+1)/(2*(g-1)) - 1) * (2/(g+1)) * (g-1) * Me if abs(df) < 1e-12: break Me_new = Me - f / df if abs(Me_new - Me) < 1e-8: break Me = max(Me_new, 1.001) return Me def calc_exhaust_velocity(gamma, Rspec, Tc, Pe_Pc): """Ve = sqrt(2γRTc/(γ-1) × (1-(Pe/Pc)^((γ-1)/γ)))""" g = gamma term = (2 * g * Rspec * Tc / (g - 1)) * (1 - Pe_Pc**((g-1)/g)) return math.sqrt(max(term, 0)) def calc_isp_from_first_principles(gamma, Rspec, Tc, Pc_bar, eps, Pa_bar=0): """Isp = Ve/g0 + (Pe-Pa)×Ae/(mdot×g0) — full first-principles calculation""" pe_ratio = calc_exit_pressure(gamma, eps) Pe_bar = pe_ratio * Pc_bar Ve = calc_exhaust_velocity(gamma, Rspec, Tc, pe_ratio) # Momentum thrust term Isp_mom = Ve / G0 # Pressure thrust term (small correction) # Ae/At = eps, so pressure term scales with eps Cf = calc_thrust_coefficient(gamma, eps, 1.0, pe_ratio, Pa_bar/Pc_bar) return Ve / G0 # Simplified; full version uses Cf def calc_reynolds(rho, v, D_m, mu): """Re = ρvD/μ""" return rho * v * D_m / mu def calc_hydraulic_diameter(width_mm, depth_mm): """Dh = 4A/P for rectangular channel""" A = width_mm * depth_mm P = 2 * (width_mm + depth_mm) return 4 * A / P def calc_nusselt_dittus_boelter(Re, Pr, heating=True): """Dittus-Boelter: Nu = 0.023 × Re^0.8 × Pr^n (n=0.4 heating, 0.3 cooling)""" n = 0.4 if heating else 0.3 return 0.023 * Re**0.8 * Pr**n def calc_nusselt_sieder_tate(Re, Pr, L_D, mu_bulk, mu_wall): """Sieder-Tate: Nu = 0.027 × Re^0.8 × Pr^(1/3) × (μb/μw)^0.14""" return 0.027 * Re**0.8 * Pr**(1/3) * (mu_bulk / mu_wall)**0.14 def calc_thermal_resistance_wall(t_mm, k_W_mK, A_mm2): """R_wall = t / (k × A) — conductive thermal resistance""" t_m = t_mm / 1000 A_m2 = A_mm2 / 1e6 return t_m / (k_W_mK * A_m2) def calc_wall_temperature(Tc_K, q_MW_m2, wall_t_mm, k_wall): """T_wall_hot = Tc - q × t / k (hot-side wall temperature)""" q_W = q_MW_m2 * 1e6 t_m = wall_t_mm / 1000 return Tc_K - q_W * t_m / k_wall # This is actually Tc - q*t/k for cold side def calc_turbopump_power(mdot, dp_bar, rho, eta=0.65): """P_pump = mdot × ΔP / (ρ × η)""" dp_Pa = dp_bar * 1e5 return mdot * dp_Pa / (rho * eta) def calc_specific_speed(N_rpm, Q_m3s, H_m): """Ns = N×sqrt(Q) / H^0.75 — turbopump specific speed""" return N_rpm * math.sqrt(Q_m3s) / H_m**0.75 def calc_npsh_required(v_ms, rho, P_vapor_Pa, P_inlet_Pa): """NPSHr = (P_inlet - P_vapor)/(ρg) — cavitation check""" return (P_inlet_Pa - P_vapor_Pa) / (rho * G0) def calc_volumetric_energy_density(power_W, speed_mm_s, hatch_mm, layer_mm): """VED = P / (v × h × t) in J/mm³ — key L-PBF quality metric""" return power_W / (speed_mm_s * hatch_mm * layer_mm) def calc_contraction_angle(Dc_mm, Dt_mm, Lcon_mm): """Half-angle of converging section""" return math.degrees(math.atan((Dc_mm - Dt_mm) / (2 * Lcon_mm))) def calc_divergence_loss(half_angle_deg): """λ = (1 + cos(α))/2 — divergence efficiency factor""" return (1 + math.cos(math.radians(half_angle_deg))) / 2 def calc_combustion_efficiency(Lstar_m, cstar_theoretical): """η_c* approximation based on L* (higher L* = more complete combustion)""" eta = min(0.99, 0.85 + 0.12 * math.log(max(Lstar_m, 0.1))) return eta def calc_mixture_density(rho_f, rho_o, OF): """Bulk propellant density: 1/ρ_mix = (1/(1+OF))/ρ_f + (OF/(1+OF))/ρ_o""" w_f = 1 / (1 + OF) w_o = OF / (1 + OF) return 1 / (w_f / rho_f + w_o / rho_o) # === CONSTANTS DATABASE === UNIVERSAL_GAS_CONST = 8314.46 # J/(kmol·K) STEFAN_BOLTZMANN = 5.67e-8 # W/(m²·K⁴) BOLTZMANN_K = 1.381e-23 # J/K def calc_radiation_heat_loss(emissivity, T_surface_K, T_ambient_K=300): """q_rad = ε × σ × (T⁴_s - T⁴_a) — Stefan-Boltzmann radiation""" return emissivity * STEFAN_BOLTZMANN * (T_surface_K**4 - T_ambient_K**4) def calc_speed_of_sound(gamma, Rspec, T_K): """a = sqrt(γ × R × T)""" return math.sqrt(gamma * Rspec * T_K) # === GENERAL ENGINEERING PHYSICS === def calc_beam_deflection(load_N, length_mm, width_mm, height_mm, E_GPa): """Max deflection of cantilever beam: δ = (F*L^3) / (3*E*I)""" L = length_mm / 1000 w = width_mm / 1000 h = height_mm / 1000 E = E_GPa * 1e9 # Area Moment of Inertia for rectangle I = (w * h**3) / 12 deflection_m = (load_N * L**3) / (3 * E * I) return deflection_m * 1000 # return mm def calc_plate_buckling_stress(E_GPa, t_mm, width_mm, poisson=0.3): """Critical buckling stress for simply supported plate: σ_cr = (k*π^2*E)/(12*(1-v^2)*(b/t)^2)""" k = 4.0 # Simply supported E = E_GPa * 1e9 ratio = width_mm / t_mm sigma_cr = (k * math.pi**2 * E) / (12 * (1 - poisson**2) * ratio**2) return sigma_cr / 1e6 # MPa def calc_conduction_resistance(length_mm, area_mm2, k_W_mK): """Thermal resistance R = L / (k * A)""" L = length_mm / 1000 A = area_mm2 / 1e6 return L / (k_W_mK * A) def calc_heatsink_performance(power_W, T_max_C, T_amb_C, R_ideal_CW): """Check if heatsink meets thermal resistance target""" delta_T = T_max_C - T_amb_C R_actual = delta_T / power_W margin = (R_actual - R_ideal_CW) / R_ideal_CW return R_actual, margin def calc_bolt_shear_strength(diameter_mm, material_key): """Shear strength of a bolt ≈ 0.6 * Ultimate Tensile Strength""" mat = MATERIALS[material_key] area = math.pi * (diameter_mm/2)**2 shear_yield_MPa = 0.577 * mat["sigma_y_MPa"] # Von Mises criterion max_load_N = shear_yield_MPa * area return max_load_N def calc_natural_frequency_cantilever(length_mm, width_mm, height_mm, E_GPa, rho_kg_m3): """Fundamental frequency f = (0.56/L^2) * sqrt(E*I / (rho*A))""" L = length_mm / 1000 w = width_mm / 1000 h = height_mm / 1000 E = E_GPa * 1e9 I = (w * h**3) / 12 A = w * h freq_Hz = (3.52 / (2 * math.pi * L**2)) * math.sqrt((E * I) / (rho_kg_m3 * A)) return freq_Hz