def fuselage_base_drag_coefficient(mach: float) -> float: """ A fit for the fuselage base drag coefficient of a cylindrical fuselage, as described in: MIL-HDBK-762: DESIGN OF AERODYNAMICALLY STABILIZED FREE ROCKETS: * Section 5-5.3.1 Body-of-Revolution Base Drag, Rocket Jet Plume-Off * Figure 5-140: Effects of Mach Number and Reynolds Number on Base Pressure Fits in /studies/FuselageBaseDragCoefficient Args: mach: Mach number [-] Returns: Fuselage base drag coefficient """ m = mach p = { 'a': 0.18024110740341143, 'center_sup': -0.21737019935624047, 'm_trans': 0.9985447737532848, 'pc_sub': 0.15922582283573747, 'pc_sup': 0.04698820458826384, 'scale_sup': 0.34978926411193456, 'trans_str': 9.999987483414937 } return np.blend( p["trans_str"] * (m - p["m_trans"]), p["pc_sup"] + p["a"] * np.exp(-(p["scale_sup"] * (m - p["center_sup"]))**2), p["pc_sub"])
def CM_function(alpha, Re, mach=0, deflection=0): alpha = np.mod(alpha + 180, 360) - 180 # Keep alpha in the valid range. CM_attached = CM_attached_interpolator({ "alpha": alpha, "ln_Re": np.log(Re), }) CM_separated = CM_separated_interpolator(alpha) return np.blend(separation_parameter(alpha, Re), CM_separated, CM_attached)
def CD_function(alpha, Re, mach=0, deflection=0): alpha = np.mod(alpha + 180, 360) - 180 # Keep alpha in the valid range. log10_CD_attached = log10_CD_attached_interpolator({ "alpha": alpha, "ln_Re": np.log(Re), }) log10_CD_separated = log10_CD_separated_interpolator(alpha) return 10**np.blend( separation_parameter(alpha, Re), log10_CD_separated, log10_CD_attached, )
def approximate_CD_wave( mach, mach_crit, CD_wave_at_fully_supersonic, ): """ An approximate relation for computing transonic wave drag, based on an object's Mach number. Considered reasonably valid from Mach 0 up to around Mach 2 or 3-ish. Methodology is a combination of: * The methodology described in Raymer, "Aircraft Design: A Conceptual Approach", Section 12.5.10 Transonic Parasite Drag (pg. 449 in Ed. 2) and * The methodology described in W.H. Mason's Configuration Aerodynamics, Chapter 7. Transonic Aerodynamics of Airfoils and Wings. Args: mach: Mach number at the operating point to be evaluated mach_crit: Critical mach number, a function of the body geometry CD_wave_at_fully_supersonic: The wave drag coefficient of the body at the speed that it first goes ( effectively) fully supersonic. Here, that is taken to mean at the Mach 1.2 case. This value should probably be derived using something similar to a Sears-Haack relation for the body in question, with a markup depending on geometry smoothness. The CD_wave predicted by this function will match this value exactly at M=1.2 and M=1.05. The peak CD_wave that is predicted is ~1.23 * this value, which occurs at M=1.10. In the high-Mach limit, this function asymptotes at 0.80 * this value, as empirically stated by Raymer. However, this model is only approximate and is likely not valid for high-supersonic flows. Returns: The approximate wave drag coefficient at the specified Mach number. The reference area is whatever the reference area used in the `CD_wave_at_fully_supersonic` parameter is. """ mach_crit_max = 1 - (0.1 / 80)**(1 / 3) mach_crit = -np.softmax(-mach_crit, -mach_crit_max, hardness=50) ### The following approximate relation is derived in W.H. Mason, "Configuration Aerodynamics", Chapter 7. Transonic Aerodynamics of Airfoils and Wings. ### Equation 7-8 on Page 7-19. ### This is in turn based on Lock's proposed empirically-derived shape of the drag rise, from Hilton, W.F., High Speed Aerodynamics, Longmans, Green & Co., London, 1952, pp. 47-49 mach_dd = mach_crit + (0.1 / 80)**(1 / 3) ### Model drag sections and cutoffs: return CD_wave_at_fully_supersonic * np.where( mach < mach_crit, 0, np.where( mach < mach_dd, 20 * (mach - mach_crit)**4, np.where( mach < 1.05, cubic_hermite_patch(mach, x_a=mach_dd, x_b=1.05, f_a=20 * (0.1 / 80)**(4 / 3), f_b=1, dfdx_a=0.1, dfdx_b=10), np.where(mach < 1.2, cubic_hermite_patch(mach, x_a=1.05, x_b=1.2, f_a=1, f_b=1, dfdx_a=10, dfdx_b=-4), np.blend( switch=4 * 2 * (mach - 1.2) / (1.2 - 0.8), value_switch_high=0.8, value_switch_low=1.2, ) # 0.8 + 0.2 * np.exp(20 * (1.2 - mach)) ))))
def model(m, p): return np.blend( p["trans_str"] * (m - p["m_trans"]), p["pc_sup"] + p["a"] * np.exp(-(p["scale_sup"] * (m - p["center_sup"]))**2), p["pc_sub"])
def model(m, p): return np.blend( p["trans_str"] * (m - p["trans"]), p["cd_sup"] + np.exp(p["a_sup"] + p["s_sup"] * (m - p["trans"])), p["cd_sub"] + np.exp(p["a_sub"] + p["s_sub"] * (m - p["trans"])))
def Cd_cylinder(Re_D: float, mach: float = 0., include_mach_effects=True, subcritical_only=False) -> float: """ Returns the drag coefficient of a cylinder in crossflow as a function of its Reynolds number and Mach. Args: Re_D: Reynolds number, referenced to diameter mach: Mach number include_mach_effects: If this is set False, it assumes Mach = 0, which simplifies the computation. subcritical_only: Determines whether the model models purely subcritical (Re < 300k) cylinder flows. Useful, since this model is now convex and can be more well-behaved. Returns: # TODO rework this function to use tanh blending, which will mitigate overflows """ ##### Do the viscous part of the computation csigc = 5.5766722118597247 csigh = 23.7460859935990563 csub0 = -0.6989492360435040 csub1 = 1.0465189382830078 csub2 = 0.7044228755898569 csub3 = 0.0846501115443938 csup0 = -0.0823564417206403 csupc = 6.8020230357616764 csuph = 9.9999999999999787 csupscl = -0.4570690347113859 x = np.log10(np.abs(Re_D) + 1e-16) if subcritical_only: Cd_mach_0 = 10**(csub0 * x + csub1) + csub2 + csub3 * x else: log10_Cd = ( (np.log10(10**(csub0 * x + csub1) + csub2 + csub3 * x)) * (1 - 1 / (1 + np.exp(-csigh * (x - csigc)))) + (csup0 + csupscl / csuph * np.log(np.exp(csuph * (csupc - x)) + 1)) * (1 / (1 + np.exp(-csigh * (x - csigc))))) Cd_mach_0 = 10**log10_Cd ##### Do the compressible part of the computation if include_mach_effects: m = mach p = { 'a_sub': 0.03458900259594298, 'a_sup': -0.7129528087049688, 'cd_sub': 1.163206940186374, 'cd_sup': 1.2899213533122527, 's_sub': 3.436601777569716, 's_sup': -1.37123096976983, 'trans': 1.022819211244295, 'trans_str': 19.017600596069848 } Cd_over_Cd_mach_0 = np.blend( p["trans_str"] * (m - p["trans"]), p["cd_sup"] + np.exp(p["a_sup"] + p["s_sup"] * (m - p["trans"])), p["cd_sub"] + np.exp(p["a_sub"] + p["s_sub"] * (m - p["trans"]))) / 1.1940010047391572 Cd = Cd_mach_0 * Cd_over_Cd_mach_0 else: Cd = Cd_mach_0 return Cd