def sound_speed_grad(tamb, tamb_d): """ Sound speed for ideal gaz """ R = earth.gaz_constant() gam = earth.heat_ratio() vsnd = numpy.sqrt(gam * R * tamb) vsnd_d = 0.5 * numpy.sqrt(gam * R / tamb) * tamb_d return vsnd, vsnd_d
def forward_cg_stall(aircraft, altp, disa, nei, hld_conf, speed_mode, mass): """ Computes max forward trimmable CG position at stall speed """ wing = aircraft.wing htp = aircraft.horizontal_tail gam = earth.heat_ratio() [pamb, tamb, tstd, dtodz] = earth.atmosphere(altp, disa) [cz_max_wing, cz0] = airplane_aero.high_lift( wing, hld_conf) # Wing maximum lift coefficient without margin [cza_htp, xlc_htp, aoa_max_htp, ki_htp] = frame_aero.htp_aero_data(aircraft) cz_max_htp = cza_htp * aoa_max_htp c_z = cz_max_wing - cz_max_htp # Max forward Cg assumed, HTP has down lift mach = flight.speed_from_lift(aircraft, pamb, c_z, mass) [cza_wo_htp, xlc_wo_htp, ki_wing] = frame_aero.wing_aero_data(aircraft, mach, hld_conf) if (nei > 0): dcx_oei = nei * propu.oei_drag(pamb, mach) else: dcx_oei = 0 dw_angle = frame_aero.wing_downwash( aircraft, cz_max_wing) # Downwash angle due to the wing cx_basic, lod_trash = airplane_aero.drag( aircraft, pamb, tamb, mach, cz_max_wing) # By definition of the drag_ function cxi_htp = (ki_htp * cz_max_htp**2) * (htp.area / wing.area ) # Induced drag generated by HTP cx_inter = cz_max_htp * dw_angle # Interaction drag (due to downwash) cx_trimmed = cx_basic + cxi_htp + cx_inter + dcx_oei fn = 0.5 * gam * pamb * mach**2 * wing.area * cx_trimmed cm_prop = propu.thrust_pitch_moment(aircraft, fn, pamb, mach, dcx_oei) cg_max_fwd_stall = (cm_prop + xlc_wo_htp * cz_max_wing - xlc_htp * cz_max_htp) / (cz_max_wing - cz_max_htp) aoa_wing = (cz_max_wing - cz0) / cza_wo_htp # Wing angle of attack aoa = aoa_wing - wing.setting # Reference angle of attack (fuselage axis versus air speed) ih = -aoa + dw_angle - aoa_max_htp # HTP trim setting speed = flight.get_speed(pamb, speed_mode, mach) return cg_max_fwd_stall, speed, fn, aoa, ih, c_z, cx_trimmed
def corrected_air_flow(Ptot, Ttot, Mach): """ Computes the corrected air flow per square meter """ R = earth.gaz_constant() gam = earth.heat_ratio() f_M = Mach * (1. + 0.5 * (gam - 1) * Mach**2)**(-(gam + 1.) / (2. * (gam - 1.))) CQoA = (numpy.sqrt(gam / R) * Ptot / numpy.sqrt(Ttot)) * f_M return CQoA
def thrust_pitch_moment(aircraft, fn, pamb, mach, dcx_oei): propulsion = aircraft.propulsion wing = aircraft.wing gam = earth.heat_ratio() if (propulsion.architecture == 1): nacelle = aircraft.turbofan_nacelle elif (propulsion.architecture == 2): nacelle = aircraft.turbofan_nacelle else: raise Exception("propulsion.architecture index is out of range") cm_prop = nacelle.z_ext * (dcx_oei - fn / (0.5 * gam * pamb * mach**2 * wing.area)) return cm_prop
def state_dot(xin, state, rating, nei, aircraft): g = earth.gravity() gam = earth.heat_ratio() area = aircraft.wing.area cz, fn = xin t, mass, xg, zg, vgnd, path = state xg_d = vgnd * numpy.cos(path) zg_d = vgnd * numpy.sin(path) pamb, pamb_d, tamb, tamb_d, disa, disa_d, wx, wx_d, wz, wz_d = air( xg, zg, xg_d, zg_d) rho, rho_d, sig, sig_d = g_earth.air_density_grad(pamb, pamb_d, tamb, tamb_d) vsnd, vsnd_d = g_earth.sound_speed_grad(tamb, tamb_d) altp, altp_d = g_earth.pressure_altitude_grad(pamb, pamb_d) # Compute state_dot #----------------------------------------------------------------------------------------------------------- vxair = xg_d - wx # Local wind is introduced here vzair = zg_d - wz vair = numpy.sqrt(vxair**2 + vzair**2) mach = vair / vsnd cx, lod = airplane_aero.drag(aircraft, pamb, tamb, mach, cz) # Aerodynamic drag model path_d = ((0.5 * rho * vair**2) * area * cz - mass * g * numpy.cos(path)) / (mass * vair) # Lift equation vgnd_d = (fn - mass * g * numpy.sin(path) - (0.5 * rho * vair**2) * area * cx) / mass # Drag equation sfc = propu.sfc(aircraft, pamb, tamb, mach, rating, nei) # Propulsion consumption model mass_d = -sfc * fn state_d = numpy.array([1., mass_d, xg_d, zg_d, vgnd_d, path_d]) # Compute other derivatives #----------------------------------------------------------------------------------------------------------- vxgnd_d = vgnd_d * numpy.cos(path) - vgnd * numpy.sin(path) * path_d vzgnd_d = vgnd_d * numpy.sin(path) + vgnd * numpy.cos(path) * path_d vxair_d = vxgnd_d - wx_d vzair_d = vzgnd_d - wz_d vair_d = (vxair * vxair_d + vzair * vzair_d) / vair mach_d = vair_d / vsnd - vair * vsnd_d / vsnd**2 vcas, vcas_d = g_earth.vcas_from_mach_grad(pamb, pamb_d, mach, mach_d) xout = numpy.array([ t, mass, xg, zg, vgnd, path, 1., mass_d, xg_d, zg_d, vgnd_d, path_d, vxgnd_d, vzgnd_d, vair, vair_d, vxair, vxair_d, vzair, vzair_d, mach, mach_d, vcas, vcas_d, altp, altp_d, pamb, pamb_d, tamb, tamb_d, disa, disa_d, rho, rho_d, wx, wx_d, wz, wz_d, cz, cx, fn ]) return xout, state_d
def eval_wing_design(aircraft): """ Wing predesign """ design_driver = aircraft.design_driver fuselage = aircraft.fuselage vtp = aircraft.vertical_tail nacelle = aircraft.turbofan_nacelle weights = aircraft.weights c_g = aircraft.center_of_gravity wing = aircraft.wing wing.t_o_c_t = 0.10 wing.t_o_c_k = wing.t_o_c_t + 0.01 wing.t_o_c_r = wing.t_o_c_k + 0.03 wing.sweep = 1.6 * max(0, (design_driver.cruise_mach - 0.5)) # Empirical law wing.dihedral = unit.rad_deg(5) if (wing.morphing == 1): # Aspect ratio is driving parameter wing.span = numpy.sqrt(wing.aspect_ratio * wing.area) elif (wing.morphing == 2): # Span is driving parameter wing.aspect_ratio = wing.span**2 / wing.area else: print("geometry_predesign_, wing_morphing index is unkown") # Correlation between span loading and tapper ratio wing.taper_ratio = 0.3 - 0.025 * (1e-3 * weights.mtow / wing.span) # Factor 1.64 accounts for the part of HTP ref area hidden in the fuselage wing.net_wetted_area = 1.64 * wing.area wing.y_kink = 0.7 * fuselage.width + 1.4 * nacelle.width # statistical regression wing.y_root = 0.5 * fuselage.width wing.y_tip = 0.5 * wing.span if (15 < unit.deg_rad(wing.sweep)): # With kink Phi100intTE = max(0, 2 * (wing.sweep - unit.rad_deg(32))) tan_phi100 = numpy.tan(Phi100intTE) A = ((1 - 0.25 * wing.taper_ratio) * wing.y_kink + 0.25 * wing.taper_ratio * wing.y_root - wing.y_tip) / ( 0.75 * wing.y_kink + 0.25 * wing.y_root - wing.y_tip) B = (numpy.tan(wing.sweep) - tan_phi100) * ( (wing.y_tip - wing.y_kink) * (wing.y_kink - wing.y_root)) / (0.25 * wing.y_root + 0.75 * wing.y_kink - wing.y_tip) wing.c_root = (wing.area - B * (wing.y_tip - wing.y_root)) / ( wing.y_root + wing.y_kink + A * (wing.y_tip - wing.y_root) + wing.taper_ratio * (wing.y_tip - wing.y_kink)) wing.c_kink = A * wing.c_root + B wing.c_tip = wing.taper_ratio * wing.c_root else: # Without kink wing.c_root = 2 * wing.area / ( 2 * wing.y_root * (1 - wing.taper_ratio) + (1 + wing.taper_ratio) * numpy.sqrt(wing.aspect_ratio * wing.area)) wing.c_tip = wing.taper_ratio * wing.c_root wing.c_kink = ((wing.y_tip - wing.y_kink) * wing.c_root + (wing.y_kink - wing.y_root) * wing.c_tip) / ( wing.y_tip - wing.y_root) tan_phi0 = 0.25 * (wing.c_kink - wing.c_tip) / ( wing.y_tip - wing.y_kink) + numpy.tan(wing.sweep) wing.mac = 2.*(3*wing.y_root*wing.c_root**2 \ +(wing.y_kink-wing.y_root)*(wing.c_root**2+wing.c_kink**2+wing.c_root*wing.c_kink) \ +(wing.y_tip-wing.y_kink)*(wing.c_kink**2+wing.c_tip**2+wing.c_kink*wing.c_tip) \ )/(3*wing.area) wing.y_mac = ( 3*wing.c_root*wing.y_root**2 \ +(wing.y_kink-wing.y_root)*(wing.c_kink*(wing.y_root+wing.y_kink*2.)+wing.c_root*(wing.y_kink+wing.y_root*2.)) \ +(wing.y_tip-wing.y_kink)*(wing.c_tip*(wing.y_kink+wing.y_tip*2.)+wing.c_kink*(wing.y_tip+wing.y_kink*2.)) \ )/(3*wing.area) x_mac_local = ( (wing.y_kink-wing.y_root)*tan_phi0*((wing.y_kink-wing.y_root)*(wing.c_kink*2.+wing.c_root) \ +(wing.y_tip-wing.y_kink)*(wing.c_kink*2.+wing.c_tip))+(wing.y_tip-wing.y_root)*tan_phi0*(wing.y_tip-wing.y_kink)*(wing.c_tip*2.+wing.c_kink) \ )/(3*wing.area) wing.x_root = vtp.x_mac + 0.25 * vtp.mac - vtp.lever_arm - 0.25 * wing.mac - x_mac_local wing.x_kink = wing.x_root + (wing.y_kink - wing.y_root) * tan_phi0 wing.x_tip = wing.x_root + (wing.y_tip - wing.y_root) * tan_phi0 wing.x_mac = wing.x_root+( (wing.x_kink-wing.x_root)*((wing.y_kink-wing.y_root)*(wing.c_kink*2.+wing.c_root) \ +(wing.y_tip-wing.y_kink)*(wing.c_kink*2.+wing.c_tip))+(wing.x_tip-wing.x_root)*(wing.y_tip-wing.y_kink)*(wing.c_tip*2.+wing.c_kink) \ )/(wing.area*3.) if (wing.attachment == 1): wing.z_root = 0 else: wing.z_root = fuselage.height - 0.5 * wing.t_o_c_r * wing.c_root wing.z_kink = wing.z_root + (wing.y_kink - wing.y_root) * numpy.tan( wing.dihedral) wing.z_tip = wing.z_root + (wing.y_tip - wing.y_root) * numpy.tan( wing.dihedral) # Wing setting #----------------------------------------------------------------------------------------------------------- g = earth.gravity() gam = earth.heat_ratio() disa = 0 rca = design_driver.ref_cruise_altp mach = design_driver.cruise_mach mass = 0.95 * weights.mtow pamb, tamb, tstd, dtodz = earth.atmosphere(rca, disa) cza_wo_htp = frame_aero.cza_wo_htp(mach, fuselage.width, wing.aspect_ratio, wing.span, wing.sweep) # AoA = 2.5° at cruise start wing.setting = (0.97 * mass * g) / (0.5 * gam * pamb * mach**2 * wing.area * cza_wo_htp) - unit.rad_deg(2.5) return
def eval_bli_nacelle_design(this_nacelle,Pamb,Tamb,Mach,shaft_power,hub_width,body_length,body_width): """ BLI nacelle design """ gam = earth.heat_ratio() r = earth.gaz_constant() Cp = earth.heat_constant(gam,r) (rho,sig) = earth.air_density(Pamb,Tamb) Vsnd = earth.sound_speed(Tamb) Re = earth.reynolds_number(Pamb,Tamb,Mach) Vair = Vsnd*Mach # Precalculation of the relation between d0 and d1 #----------------------------------------------------------------------------------------------------------- body_bnd_layer = resize_boundary_layer(body_width,hub_width) # Electrical nacelle geometry : e-nacelle diameter is size by cruise conditions #----------------------------------------------------------------------------------------------------------- r0 = 0.5*body_width # Radius of the fuselage, supposed constant d0 = jet.boundary_layer(Re,body_length) # theoritical thickness of the boundary layer without taking account of fuselage tapering r1 = 0.5*hub_width # Radius of the hub of the efan nacelle d1 = lin_interp_1d(d0,body_bnd_layer[:,0],body_bnd_layer[:,1]) # Thickness of the BL around the hub deltaV = 2.*Vair*(this_nacelle.efficiency_fan/this_nacelle.efficiency_prop - 1.) # speed variation produced by the fan PwInput = this_nacelle.efficiency_fan*shaft_power # kinetic energy produced by the fan #=========================================================================================================== def fct_power_1(y,PwInput,deltaV,rho,Vair,r1,d1): (q0,q1,q2,v1,dVbli) = jet.air_flows(rho,Vair,r1,d1,y) Vinlet = Vair - dVbli Vjet = Vinlet + deltaV Pw = 0.5*q1*(Vjet**2 - Vinlet**2) y = PwInput - Pw return y #----------------------------------------------------------------------------------------------------------- fct_arg = (PwInput,deltaV,rho,Vair,r1,d1) # Computation of y1 : thickness of the vein swallowed by the inlet output_dict = fsolve(fct_power_1, x0=d1, args=fct_arg, full_output=True) y1 = output_dict[0][0] (q0,q1,q2,v1,dVbli) = jet.air_flows(rho,Vair,r1,d1,y1) MachInlet = v1/Vsnd # Mean Mach number at inlet position Ptot = earth.total_pressure(Pamb,MachInlet) # Stagnation pressure at inlet position Ttot = earth.total_temperature(Tamb,MachInlet) # Stagnation temperature at inlet position MachFan = 0.5 # required Mach number at fan position CQoA1 = jet.corrected_air_flow(Ptot,Ttot,MachFan) # Corrected air flow per area at fan position eFanArea = q1/CQoA1 # Fan area around the hub fan_width = numpy.sqrt(hub_width**2 + 4*eFanArea/numpy.pi) # Fan diameter Vjet = v1 + deltaV # Jet velocity TtotJet = Ttot + shaft_power/(q1*Cp) # Stagnation pressure increases due to introduced work Tstat = TtotJet - 0.5*Vjet**2/Cp # static temperature VsndJet = numpy.sqrt(gam*r*Tstat) # Sound velocity at nozzle exhaust MachJet = Vjet/VsndJet # Mach number at nozzle output PtotJet = earth.total_pressure(Pamb,MachJet) # total pressure at nozzle exhaust (P = Pamb) CQoA2 = jet.corrected_air_flow(PtotJet,TtotJet,MachJet) # Corrected air flow per area at nozzle output nozzle_area = q1/CQoA2 # Fan area around the hub nozzle_width = numpy.sqrt(4*nozzle_area/numpy.pi) # Nozzle diameter this_nacelle.hub_width = hub_width this_nacelle.fan_width = fan_width this_nacelle.nozzle_width = nozzle_width this_nacelle.nozzle_area = nozzle_area this_nacelle.width = 1.20*fan_width # Surrounding structure this_nacelle.length = 1.50*this_nacelle.width this_nacelle.net_wetted_area = numpy.pi*this_nacelle.width*this_nacelle.length # Nacelle wetted area this_nacelle.bnd_layer = body_bnd_layer this_nacelle.body_length = body_length return
def fan_thrust(nacelle, Pamb, Tamb, Mach, PwShaft): """ Compute the thrust of a fan of given geometry swallowing free air stream """ gam = earth.heat_ratio() r = earth.gaz_constant() Cp = earth.heat_constant(gam, r) #=========================================================================================================== def fct_power(q, PwShaft, Pamb, Ttot, Vair, NozzleArea): Vinlet = Vair PwInput = nacelle.efficiency_fan * PwShaft Vjet = numpy.sqrt(2. * PwInput / q + Vinlet**2) # Supposing isentropic compression TtotJet = Ttot + PwShaft / ( q * Cp) # Stagnation temperature increases due to introduced work TstatJet = TtotJet - 0.5 * Vjet**2 / Cp # Static temperature VsndJet = earth.sound_speed(TstatJet) # Sound speed at nozzle exhaust MachJet = Vjet / VsndJet # Mach number at nozzle output PtotJet = earth.total_pressure( Pamb, MachJet) # total pressure at nozzle exhaust (P = Pamb) CQoA1 = corrected_air_flow( PtotJet, TtotJet, MachJet) # Corrected air flow per area at fan position q0 = CQoA1 * NozzleArea y = q0 - q return y #----------------------------------------------------------------------------------------------------------- NozzleArea = nacelle.nozzle_area FanWidth = nacelle.fan_width Ptot = earth.total_pressure(Pamb, Mach) # Total pressure at inlet position Ttot = earth.total_temperature(Tamb, Mach) # Total temperature at inlet position Vsnd = earth.sound_speed(Tamb) Vair = Vsnd * Mach fct_arg = (PwShaft, Pamb, Ttot, Vair, NozzleArea) CQoA0 = corrected_air_flow( Ptot, Ttot, Mach) # Corrected air flow per area at fan position q0init = CQoA0 * (0.25 * numpy.pi * FanWidth**2) # Computation of the air flow swallowed by the inlet output_dict = fsolve(fct_power, x0=q0init, args=fct_arg, full_output=True) q0 = output_dict[0][0] if (output_dict[2] != 1): raise Exception("Convergence problem") Vinlet = Vair PwInput = nacelle.efficiency_fan * PwShaft Vjet = numpy.sqrt(2. * PwInput / q0 + Vinlet**2) eFn = q0 * (Vjet - Vinlet) return (eFn, q0)
def fan_thrust_with_bli(nacelle, Pamb, Tamb, Mach, PwShaft): """ Compute the thrust of a fan of a given geometry swallowing the boundary layer (BL) of a body of a given geometry The amount of swallowed BL depends on the given shaft power and flying conditions. """ gam = earth.heat_ratio() r = earth.gaz_constant() Cp = earth.heat_constant(gam, r) #=========================================================================================================== def fct_power_bli(y, PwShaft, Pamb, rho, Ttot, Vair, r1, d1, nozzle_area): (q0, q1, q2, Vinlet, dVbli) = air_flows(rho, Vair, r1, d1, y) PwInput = nacelle.efficiency_fan * PwShaft Vjet = numpy.sqrt(2. * PwInput / q1 + Vinlet**2) TtotJet = Ttot + PwShaft / ( q1 * Cp) # Stagnation temperature increases due to introduced work Tstat = TtotJet - 0.5 * Vjet**2 / Cp # Static temperature VsndJet = earth.sound_speed(Tstat) # Sound speed at nozzle exhaust MachJet = Vjet / VsndJet # Mach number at nozzle output PtotJet = earth.total_pressure( Pamb, MachJet ) # total pressure at nozzle exhaust (P = Pamb) supposing adapted nozzle CQoA1 = corrected_air_flow( PtotJet, TtotJet, MachJet) # Corrected air flow per area at nozzle position q = CQoA1 * nozzle_area y = q1 - q return y #----------------------------------------------------------------------------------------------------------- nozzle_area = nacelle.nozzle_area bnd_layer = nacelle.bnd_layer Re = earth.reynolds_number(Pamb, Tamb, Mach) d0 = boundary_layer( Re, nacelle.body_length ) # theorical thickness of the boundary layer without taking account of fuselage tapering r1 = 0.5 * nacelle.hub_width # Radius of the hub of the eFan nacelle d1 = lin_interp_1d(d0, bnd_layer[:, 0], bnd_layer[:, 1]) # Using the precomputed relation Ttot = earth.total_temperature( Tamb, Mach) # Stagnation temperature at inlet position rho, sig = earth.air_density(Pamb, Tamb) Vsnd = earth.sound_speed(Tamb) Vair = Vsnd * Mach fct_arg = (PwShaft, Pamb, rho, Ttot, Vair, r1, d1, nozzle_area) # Computation of y1 : thikness of the vein swallowed by the inlet output_dict = fsolve(fct_power_bli, x0=0.50, args=fct_arg, full_output=True) y = output_dict[0][0] if (output_dict[2] != 1): raise Exception("Convergence problem") (q0, q1, q2, Vinlet, dVbli) = air_flows(rho, Vair, r1, d1, y) PwInput = nacelle.efficiency_fan * PwShaft Vjet = numpy.sqrt(2. * PwInput / q1 + Vinlet**2) eFn = q1 * (Vjet - Vinlet) return (eFn, q1, dVbli)