def sfc_at_max_power(self, atmosphere: Atmosphere) -> Union[float, Sequence]: """ Computation of Specific Fuel Consumption at maximum power. :param atmosphere: Atmosphere instance at intended altitude :return: SFC_P (in kg/s/W) """ altitude = atmosphere.get_altitude(altitude_in_feet=True) sigma = Atmosphere(altitude).density / Atmosphere(0.0).density max_power = (self.max_power / 1e3) * (sigma - (1 - sigma) / 7.55 ) # max power in kW if self.fuel_type == 1.: if self.strokes_nb == 2.: # Gasoline 2-strokes sfc_p = 1125.9 * max_power**(-0.2441) else: # Gasoline 4-strokes sfc_p = -0.0011 * max_power**2 + 0.5905 * max_power + 228.58 elif self.fuel_type == 2.: if self.strokes_nb == 2.: # Diesel 2-strokes sfc_p = -0.765 * max_power + 334.94 else: # Diesel 4-strokes sfc_p = -0.964 * max_power + 231.91 else: raise FastBasicICEngineInconsistentInputParametersError( "Bad engine configuration: fuel type {0:f} model does not exist." .format(float(self.fuel_type))) sfc_p = sfc_p / 1e6 / 3600.0 # change units to be in kg/s/W return sfc_p
def test_sfc_at_max_thrust(): """ Checks model against values from :... .. bibliography:: ../refs.bib """ # Check with arrays # BasicICEngine(max_power(W), design_altitude(m), design_speed(m/s), fuel_type, strokes_nb) _50kw_engine = BasicICEngine(50000.0, 2400.0, 81.0, 1.0, 4.0, 1.0) atm = Atmosphere([0, 10668, 13000], altitude_in_feet=False) sfc = _50kw_engine.sfc_at_max_power(atm) # Note: value for alt==10668 is different from PhD report # alt=13000 is here just for testing in stratosphere np.testing.assert_allclose( sfc, [7.09319444e-08, 6.52497276e-08, 6.44112478e-08], rtol=1e-4) # Check with scalars # BasicICEngine(max_power(W), design_altitude(m), design_speed(m/s), fuel_type, strokes_nb) _250kw_engine = BasicICEngine(250000.0, 2400.0, 81.0, 1.0, 4.0, 1.0) atm = Atmosphere(0, altitude_in_feet=False) sfc = _250kw_engine.sfc_at_max_power(atm) np.testing.assert_allclose(sfc, 8.540416666666667e-08, rtol=1e-4) # Check with scalars # BasicICEngine(max_power(W), design_altitude(m), design_speed(m/s), fuel_type, strokes_nb) _130kw_engine = BasicICEngine(130000.0, 2400.0, 81.0, 1.0, 4.0, 1.0) atm = Atmosphere(0, altitude_in_feet=False) sfc = _130kw_engine.sfc_at_max_power(atm) np.testing.assert_allclose(sfc, 7.965417e-08, rtol=1e-4)
def compute(self, inputs, outputs, discrete_inputs=None, discrete_outputs=None): propulsion_model = FuelEngineSet( self._engine_wrapper.get_model(inputs), inputs["data:geometry:propulsion:count"]) if self.options["taxi_out"]: thrust_rate = inputs["data:mission:sizing:taxi_out:thrust_rate"] duration = inputs["data:mission:sizing:taxi_out:duration"] mach = inputs["data:mission:sizing:taxi_out:speed"] / Atmosphere( 0.0).speed_of_sound else: thrust_rate = inputs["data:mission:sizing:taxi_in:thrust_rate"] duration = inputs["data:mission:sizing:taxi_in:duration"] mach = inputs["data:mission:sizing:taxi_in:speed"] / Atmosphere( 0.0).speed_of_sound # FIXME: no specific settings for taxi (to be changed in fastoad\constants.py) flight_point = FlightPoint(mach=mach, altitude=0.0, engine_setting=EngineSetting.TAKEOFF, thrust_rate=thrust_rate) propulsion_model.compute_flight_points(flight_point) fuel_mass = propulsion_model.get_consumed_mass(flight_point, duration) if self.options["taxi_out"]: outputs["data:mission:sizing:taxi_out:fuel"] = fuel_mass else: outputs["data:mission:sizing:taxi_in:fuel"] = fuel_mass
def sfc_at_max_power(self, atmosphere: Atmosphere) -> Union[float, Sequence]: """ Computation of Specific Fuel Consumption at maximum power. :param atmosphere: Atmosphere instance at intended altitude :return: SFC_P (in kg/s/W) """ altitude = atmosphere.get_altitude(altitude_in_feet=True) sigma = Atmosphere(altitude).density / Atmosphere(0.0).density max_power = (self.max_power / 1e3) * (sigma - (1 - sigma) / 7.55 ) # max power in kW if self.fuel_type == 1.: if self.strokes_nb == 2.: # Gasoline 2-strokes sfc_p = 1125.9 * max_power**(-0.2441) else: # Gasoline 4-strokes sfc_p = -0.0011 * max_power**2 + 0.5905 * max_power + 228.58 elif self.fuel_type == 2.: if self.strokes_nb == 2.: # Diesel 2-strokes sfc_p = -0.765 * max_power + 334.94 else: # Diesel 4-strokes sfc_p = -0.964 * max_power + 231.91 else: warnings.warn( 'Propulsion layout {} not implemented in model, replaced by layout 1!' .format(self.fuel_type)) if self.strokes_nb == 2.: # Gasoline 2-strokes sfc_p = 1125.9 * max_power**(-0.2441) else: # Gasoline 4-strokes sfc_p = -0.0011 * max_power**2 + 0.5905 * max_power + 228.58 sfc_p = sfc_p / 1e6 / 3600.0 # change units to be in kg/s/W return sfc_p
def max_thrust( self, atmosphere: Atmosphere, mach: Union[float, Sequence[float]], ) -> np.ndarray: """ Computation of maximum thrust. Uses model described in ... :param atmosphere: Atmosphere instance at intended altitude (should be <=20km) :param mach: Mach number(s) (should be between 0.05 and 1.0) :return: maximum thrust (in N) """ # Calculate maximum mechanical power @ given altitude altitude = atmosphere.get_altitude(altitude_in_feet=True) mach = np.asarray(mach) sigma = Atmosphere(altitude).density / Atmosphere(0.0).density max_power = self.max_power * (sigma - (1 - sigma) / 7.55) _, _, _, _, _, _ = self.compute_dimensions() thrust_1 = (self.propeller["thrust_SL"] * g) * sigma**( 1 / 3) # considered fixed point @altitude thrust_2 = max_power * PROPELLER_EFFICIENCY / np.maximum( mach * Atmosphere(altitude).speed_of_sound, 1e-20) return np.minimum(thrust_1, thrust_2)
def compute(self, inputs, outputs, discrete_inputs=None, discrete_outputs=None): if self.options["low_speed_aero"]: altitude = 0.0 mach = inputs["data:TLAR:v_approach"] / Atmosphere( altitude).speed_of_sound else: altitude = float( inputs["data:mission:sizing:main_route:cruise:altitude"]) mach = inputs["data:TLAR:v_cruise"] / Atmosphere( altitude, altitude_in_feet=False).speed_of_sound unit_reynolds = Atmosphere( altitude, altitude_in_feet=False).get_unitary_reynolds(mach) if self.options["low_speed_aero"]: outputs["data:aerodynamics:low_speed:mach"] = mach outputs[ "data:aerodynamics:low_speed:unit_reynolds"] = unit_reynolds else: outputs["data:aerodynamics:cruise:mach"] = mach outputs["data:aerodynamics:cruise:unit_reynolds"] = unit_reynolds
def compute(self, inputs, outputs, discrete_inputs=None, discrete_outputs=None): l0_wing = inputs["data:geometry:wing:MAC:length"] speed = inputs["data:TLAR:approach_speed"] atm = Atmosphere(0.0, 15.0) mach = speed / atm.speed_of_sound reynolds = atm.get_unitary_reynolds(mach) * l0_wing outputs["data:aerodynamics:aircraft:landing:mach"] = mach outputs["data:aerodynamics:aircraft:landing:reynolds"] = reynolds
def delta_climb_rate(self, x_cg, v_ref, mass, propulsion_model, inputs): coeff_k_wing = inputs[ "data:aerodynamics:wing:low_speed:induced_drag_coefficient"] coeff_k_htp = inputs[ "data:aerodynamics:horizontal_tail:low_speed:induced_drag_coefficient"] cl_alpha_wing = inputs["data:aerodynamics:wing:low_speed:CL_alpha"] cl_alpha_htp = inputs[ "data:aerodynamics:horizontal_tail:low_speed:CL_alpha"] cl_delta_htp = inputs["data:aerodynamics:elevator:low_speed:CL_delta"] cd_flaps = inputs["data:aerodynamics:flaps:landing:CD"] cl_flaps = inputs["data:aerodynamics:flaps:landing:CL"] cd_0 = inputs["data:aerodynamics:aircraft:low_speed:CD0"] rho = Atmosphere(0.0).density sos = Atmosphere(0.0).speed_of_sound dynamic_pressure = 1. / 2. * rho * v_ref**2.0 alpha_ac, delta_e, equilibrium_found = self.found_cl_repartition( inputs, 1.0, mass, dynamic_pressure, x_cg) cl_AOA_wing = cl_alpha_wing * alpha_ac cl_AOA_htp = cl_alpha_htp * alpha_ac cl_elevator = cl_delta_htp * delta_e cd_min = cd_0 + cd_flaps cl = cl_AOA_wing + cl_AOA_htp + cl_elevator + cl_flaps cd = cd_min + \ coeff_k_wing * (cl_AOA_wing + cl_flaps) ** 2.0 + \ coeff_k_htp * (cl_AOA_htp + cl_elevator) ** 2.0 flight_point = FlightPoint( mach=v_ref / sos, altitude=0.0, engine_setting=EngineSetting.TAKEOFF, thrust_rate=1.0) # with engine_setting as EngineSetting propulsion_model.compute_flight_points(flight_point) thrust = float(flight_point.thrust) propeller_advance_ratio = v_ref / (2700. / 60. * 1.97) propeller_efficiency_reduction = math.sin(propeller_advance_ratio * math.pi / 2.) climb_angle = math.asin(propeller_efficiency_reduction * thrust / (mass * 9.81) - cd / cl) climb_gradient = math.tan(climb_angle) return climb_gradient, equilibrium_found
def compute(self, inputs, outputs, discrete_inputs=None, discrete_outputs=None): # Get inputs wing_area = inputs['data:geometry:wing:area'] htp_area = inputs['data:geometry:horizontal_tail:area'] aspect_ratio = inputs['data:geometry:wing:aspect_ratio'] if self.options["low_speed_aero"]: altitude = 0.0 atm = Atmosphere(altitude) mach = inputs["data:aerodynamics:low_speed:mach"] else: altitude = inputs["data:mission:sizing:main_route:cruise:altitude"] atm = Atmosphere(altitude) mach = inputs["data:aerodynamics:cruise:mach"] v_inf = max(atm.speed_of_sound * mach, 0.01) # avoid V=0 m/s crashes super()._run(inputs) beta = math.sqrt(1 - mach**2) # Prandtl-Glauert cl_wing, _, _, _ = super().compute_wing(inputs, _INPUT_AOAList, v_inf, flaps_angle=0.0, use_airfoil=True) # Calculate downwash angle based on Gudmundsson model (p.467) downwash_angle = 2.0 * np.array(cl_wing) / beta * 180.0 / ( aspect_ratio * np.pi**2) HTP_AOAList = list(np.array(_INPUT_AOAList) - downwash_angle) result_cl, _ = super().compute_htp(inputs, HTP_AOAList, v_inf, use_airfoil=True) # Write value with wing Sref result_cl = np.array(result_cl) / beta * htp_area / wing_area # Calculate derivative cl_alpha = float( (result_cl[1] - result_cl[0]) / ((_INPUT_AOAList[1] - _INPUT_AOAList[0]) * math.pi / 180)) if self.options["low_speed_aero"]: outputs[ 'data:aerodynamics:horizontal_tail:low_speed:CL_alpha'] = cl_alpha else: outputs[ 'data:aerodynamics:horizontal_tail:cruise:CL_alpha'] = cl_alpha
def sfc_at_max_thrust(self, atmosphere: Atmosphere, mach: Union[float, Sequence[float]]) -> np.ndarray: """ Computation of Specific Fuel Consumption at maximum thrust. Uses model described in :cite:`roux:2005`, p.41. :param atmosphere: Atmosphere instance at intended altitude :param mach: Mach number(s) :return: SFC (in kg/s/N) """ altitude = atmosphere.get_altitude(False) mach = np.asarray(mach) # Following coefficients are constant for alt<=0 and alt >=11000m. # We use numpy to implement that so we are safe if altitude is a sequence. bound_altitude = np.minimum(11000, np.maximum(0, altitude)) # pylint: disable=invalid-name # coefficients are named after model a1 = -7.44e-13 * bound_altitude + 6.54e-7 a2 = -3.32e-10 * bound_altitude + 8.54e-6 b1 = -3.47e-11 * bound_altitude - 6.58e-7 b2 = 4.23e-10 * bound_altitude + 1.32e-5 c = -1.05e-7 theta = atmosphere.temperature / ATM_SEA_LEVEL.temperature sfc = (mach * (a1 * self.bypass_ratio + a2) + (b1 * self.bypass_ratio + b2) * np.sqrt(theta) + ((7.4e-13 * (self.overall_pressure_ratio - 30) * altitude) + c) * (self.overall_pressure_ratio - 30)) return sfc
def compute(self, inputs, outputs, discrete_inputs=None, discrete_outputs=None): # Get inputs wing_area = inputs['data:geometry:wing:area'] htp_area = inputs['data:geometry:horizontal_tail:area'] aspect_ratio = inputs['data:geometry:wing:aspect_ratio'] mach = inputs["data:aerodynamics:low_speed:mach"] v_inf = max(Atmosphere(0.0).speed_of_sound * mach, 0.01) # avoid V=0 m/s crashes super()._run(inputs) beta = math.sqrt(1 - mach ** 2) # Prandtl-Glauert cl_wing, _, _, result_cm2 = super().compute_wing(inputs, _INPUT_AOAList, v_inf, flaps_angle=0.0, use_airfoil=True) result_cm2 = np.array(result_cm2)/beta # Calculate downwash angle based on Gudmundsson model (p.467) downwash_angle = 2.0 * np.array(cl_wing)/beta * 180.0 / (aspect_ratio * np.pi**2) HTP_AOAList = list(np.array(_INPUT_AOAList) - downwash_angle) result_cl, result_cm1 = super().compute_htp(inputs, HTP_AOAList, v_inf, use_airfoil=True) # Write value with wing Sref result_cl = np.array(result_cl)/beta * htp_area / wing_area result_cm1 = np.array(result_cm1)/beta * htp_area / wing_area outputs['data:aerodynamics:horizontal_tail:low_speed:alpha'] = np.array(_INPUT_AOAList) outputs['data:aerodynamics:horizontal_tail:low_speed:CL'] = result_cl outputs['data:aerodynamics:horizontal_tail:low_speed:CM'] = result_cm1 outputs['data:aerodynamics:wing:low_speed:alpha'] = np.array(_INPUT_AOAList) outputs['data:aerodynamics:wing:low_speed:CM'] = result_cm2
def delta_axial_load(self, air_speed, inputs, altitude, mass): propulsion_model = FuelEngineSet( self._engine_wrapper.get_model(inputs), inputs["data:geometry:propulsion:count"]) wing_area = inputs["data:geometry:wing:area"] cd0 = inputs["data:aerodynamics:aircraft:cruise:CD0"] coef_k = inputs[ "data:aerodynamics:wing:cruise:induced_drag_coefficient"] # Get the available thrust from propulsion system atm = Atmosphere(altitude, altitude_in_feet=False) flight_point = FlightPoint(mach=air_speed / atm.speed_of_sound, altitude=altitude, engine_setting=EngineSetting.TAKEOFF, thrust_rate=1.0) propulsion_model.compute_flight_points(flight_point) thrust = float(flight_point.thrust) # Get the necessary thrust to overcome cl = (mass * g) / (0.5 * atm.density * wing_area * air_speed**2.0) cd = cd0 + coef_k * cl**2.0 drag = 0.5 * atm.density * wing_area * cd * air_speed**2.0 return thrust - drag
def compute(self, inputs, outputs, discrete_inputs=None, discrete_outputs=None): # Get inputs b_f = inputs['data:geometry:fuselage:maximum_width'] span = inputs['data:geometry:wing:span'] if self.options["low_speed_aero"]: mach = inputs["data:aerodynamics:low_speed:mach"] V_inf = max(Atmosphere(0.0).speed_of_sound * mach, 0.01) # avoid V=0 m/s crashes else: mach = inputs["data:aerodynamics:cruise:mach"] V_inf = inputs["data:TLAR:v_cruise"] super()._run(inputs) Cl, Cdi, Oswald, Cm = super().compute_wing(inputs, _INPUT_AOAList, V_inf, flaps_angle=0.0, use_airfoil=True) k_fus = 1 + 0.025 * b_f / span - 0.025 * ( b_f / span)**2 # Fuselage correction beta = math.sqrt(1 - mach**2) # Prandtl-Glauert cl_alpha = (Cl[1] - Cl[0]) / ((_INPUT_AOAList[1] - _INPUT_AOAList[0]) * math.pi / 180) * k_fus / beta cl_0 = Cl[0] / beta y_vector, cl_vector = super().get_cl_curve(_INPUT_AOAList[0], V_inf) cl_vector = list(np.array(cl_vector) / beta) real_length = min(SPAN_MESH_POINT_OPENVSP, len(y_vector)) if real_length < len(y_vector): warnings.warn( "Defined maximum span mesh in constants.py exceeded!") if self.options["low_speed_aero"]: outputs['data:aerodynamics:aircraft:low_speed:CL0_clean'] = cl_0 outputs['data:aerodynamics:aircraft:low_speed:CL_alpha'] = cl_alpha if real_length >= len(y_vector): outputs[ 'data:aerodynamics:wing:low_speed:Y_vector'] = np.zeros( SPAN_MESH_POINT_OPENVSP) outputs[ 'data:aerodynamics:wing:low_speed:CL_vector'] = np.zeros( SPAN_MESH_POINT_OPENVSP) outputs['data:aerodynamics:wing:low_speed:Y_vector'][ 0:real_length] = y_vector outputs['data:aerodynamics:wing:low_speed:CL_vector'][ 0:real_length] = cl_vector else: outputs[ 'data:aerodynamics:aircraft:wing:Y_vector'] = np.linspace( y_vector[0], y_vector[1], SPAN_MESH_POINT_OPENVSP) outputs['data:aerodynamics:aircraft:wing:CL_vector'] = \ np.interp(outputs['data:aerodynamics:aircraft:low_speed:Y_vector'], y_vector, cl_vector) else: outputs['data:aerodynamics:aircraft:cruise:CL0_clean'] = cl_0 outputs['data:aerodynamics:aircraft:cruise:CL_alpha'] = cl_alpha
def compute(self, inputs, outputs): altitude = inputs["data:mission:sizing:main_route:cruise:altitude"] mach = inputs["data:aerodynamics:cruise:mach"] atm = Atmosphere(altitude, altitude_in_feet=False) velocity = mach * atm.speed_of_sound # We need to compute the AOA for which the most constraining Delta_Cl due to slipstream will appear, this # is taken as the angle for which the clean wing is at its max angle of attack alpha_max = 15.0 wing_rotor = self.compute_wing_rotor_x57(inputs, outputs, altitude, mach, alpha_max) wing = self.compute_wing(inputs, outputs, altitude, mach, alpha_max) cl_vector_prop_on = wing_rotor["cl_vector"] y_vector_prop_on = wing_rotor["y_vector"] cl_vector_prop_off = wing["cl_vector"] y_vector_prop_off = wing["y_vector"] additional_zeros = list( np.zeros(SPAN_MESH_POINT - len(cl_vector_prop_on))) cl_vector_prop_on.extend(additional_zeros) y_vector_prop_on.extend(additional_zeros) cl_vector_prop_off.extend(additional_zeros) y_vector_prop_off.extend(additional_zeros) cl_diff = [] for i in range(len(cl_vector_prop_on)): cl_diff.append( round(cl_vector_prop_on[i] - cl_vector_prop_off[i], 4)) outputs[ "data:aerodynamics:slipstream:wing:prop_on:Y_vector"] = y_vector_prop_on outputs[ "data:aerodynamics:slipstream:wing:prop_on:CL_vector"] = cl_vector_prop_on outputs[ "data:aerodynamics:slipstream:wing:prop_on:CT_ref"] = wing_rotor[ "ct"] outputs["data:aerodynamics:slipstream:wing:prop_on:CL"] = wing_rotor[ "cl"] outputs[ "data:aerodynamics:slipstream:wing:prop_on:velocity"] = velocity outputs[ "data:aerodynamics:slipstream:wing:prop_off:Y_vector"] = y_vector_prop_off outputs[ "data:aerodynamics:slipstream:wing:prop_off:CL_vector"] = cl_vector_prop_off outputs["data:aerodynamics:slipstream:wing:prop_off:CL"] = wing["cl"] outputs[ "data:aerodynamics:slipstream:wing:only_prop:CL_vector"] = cl_diff
def compute_dimensions(self) -> (float, float, float, float, float, float): """ Computes propulsion dimensions (engine/nacelle/propeller) from maximum power. Model from :... """ # Compute engine dimensions self.engine.length = self.ref["length"] * ( self.max_power / self.ref["max_power"])**(1 / 3) self.engine.height = self.ref["height"] * ( self.max_power / self.ref["max_power"])**(1 / 3) self.engine.width = self.ref["width"] * ( self.max_power / self.ref["max_power"])**(1 / 3) if self.prop_layout == 3.0: nacelle_length = 1.15 * self.engine.length # Based on the length between nose and firewall for TB20 and SR22 else: nacelle_length = 1.50 * self.engine.length # Compute nacelle dimensions self.nacelle = Nacelle( height=self.engine.height * 1.1, width=self.engine.width * 1.1, length=nacelle_length, ) self.nacelle.wet_area = 2 * (self.nacelle.height + self.nacelle.width) * self.nacelle.length # Compute propeller dimensions (2-blades) w_propeller = 2500 # regulated propeller speed in RPM v_sound = Atmosphere(self.design_altitude, altitude_in_feet=False).speed_of_sound d_max = (((v_sound * 0.85)**2 - self.design_speed**2) / ((w_propeller * math.pi / 30) / 2)**2)**0.5 d_opt = 1.04**2 * ((self.max_power / 735.5) * 1e8 / (w_propeller**2 * self.design_speed * 3.6))**(1 / 4) d = min(d_max, d_opt) t_0 = 7.4 * ((self.max_power / 735.5) * d)**(2 / 3) area = 13307 * t_0 / (d**2 * w_propeller**2) chord = area / d self.propeller = Propeller( area=area, depth=chord * 1.1, diameter=d, thrust_SL=t_0, ) propeller_depth = max(chord * 1.1, 0.2 * d) # For clarity purposes, it has been assimilated as the spinner length return self.nacelle["height"], self.nacelle["width"], self.nacelle[ "length"], self.nacelle["wet_area"], self.propeller[ "diameter"], self.propeller["depth"]
def compute_flight_points(self, flight_points: Union[FlightPoint, pd.DataFrame]): altitude = float(Atmosphere(np.array(flight_points.altitude)).get_altitude(altitude_in_feet=True)) mach = np.array(flight_points.mach) thrust = np.array(flight_points.thrust) sigma = Atmosphere(altitude).density / Atmosphere(0.0).density max_power = self.max_power * (sigma - (1 - sigma) / 7.55) max_thrust = min( self.max_thrust * sigma ** (1. / 3.), max_power * 0.8 / np.maximum(mach * Atmosphere(altitude).speed_of_sound, 1e-20) ) if flight_points.thrust_rate is None: flight_points.thrust = min(max_thrust, float(thrust)) flight_points.thrust_rate = float(thrust) / max_thrust else: flight_points.thrust = max_thrust * np.array(flight_points.thrust_rate) sfc_pmax = 8.5080e-08 # fixed whatever the thrust ratio, sfc for ONE 130kW engine ! sfc = sfc_pmax * flight_points.thrust_rate * mach * Atmosphere(altitude).speed_of_sound flight_points['sfc'] = sfc
def compute(self, inputs, outputs, discrete_inputs=None, discrete_outputs=None): x0_wing = inputs["data:geometry:wing:MAC:leading_edge:x:local"] l0_wing = inputs["data:geometry:wing:MAC:length"] l1_wing = inputs["data:geometry:wing:root:virtual_chord"] width_max = inputs["data:geometry:fuselage:maximum_width"] fa_length = inputs["data:geometry:wing:MAC:at25percent:x"] fus_length = inputs["data:geometry:fuselage:length"] wing_area = inputs["data:geometry:wing:area"] aspect_ratio = inputs["data:geometry:wing:aspect_ratio"] lp_ht = inputs["data:geometry:horizontal_tail:MAC:at25percent:x:from_wingMAC25"] cl_alpha_wing = inputs["data:aerodynamics:wing:cruise:CL_alpha"] cl_alpha_ht = inputs["data:aerodynamics:horizontal_tail:cruise:CL_alpha"] cl_delta_ht = inputs["data:aerodynamics:elevator:low_speed:CL_delta"] ch_alpha_3d = inputs["data:aerodynamics:horizontal_tail:cruise:hinge_moment:CH_alpha"] ch_delta_3d = inputs["data:aerodynamics:horizontal_tail:cruise:hinge_moment:CH_delta"] tail_efficiency = inputs["data:aerodynamics:horizontal_tail:efficiency"] v_cruise = inputs["data:TLAR:v_cruise"] alt_cruise = inputs["data:mission:sizing:main_route:cruise:altitude"] # TODO: make variable name in computation sequence more english x0_25 = fa_length - 0.25 * l0_wing - x0_wing + 0.25 * l1_wing ratio_x025 = x0_25 / fus_length # fitting result of Raymer book, figure 16.14 k_h = 0.01222 - 7.40541e-4 * ratio_x025 * 100 + 2.1956e-5 * (ratio_x025 * 100) ** 2 # equation from Raymer book, eqn 16.22 # FIXME: introduce cm_alpha_wing to the equation (non-symmetrical profile) cm_alpha_fus = k_h * width_max ** 2 * fus_length / (l0_wing * wing_area) * 180.0 / np.pi x_ca_plane = (tail_efficiency * cl_alpha_ht * lp_ht - cm_alpha_fus * l0_wing) / \ (cl_alpha_wing + tail_efficiency * cl_alpha_ht) x_aero_center = x_ca_plane / l0_wing + 0.25 outputs["data:aerodynamics:cruise:neutral_point:stick_fixed:x"] = x_aero_center sos = Atmosphere(alt_cruise).speed_of_sound mach = v_cruise / sos beta = math.sqrt(1. - mach ** 2.0) cl_delta_ht_cruise = cl_delta_ht / beta # The cl_alpha_ht in the formula for the free_elevator_factor is defined with respect to the tail angle of # attack, the one we compute is wth respect to the plane so it includes downwash, as a consequence we must # correct it influence for this specific calculation. We will use the formula for elliptical wing as it is well # known downwash_effect = (1. - 2. * cl_alpha_wing / (math.pi * aspect_ratio)) cl_alpha_ht_ht = cl_alpha_ht / downwash_effect free_elevator_factor = 1. - (cl_delta_ht_cruise/cl_alpha_ht_ht)*(ch_alpha_3d/ch_delta_3d) outputs["data:aerodynamics:cruise:neutral_point:free_elevator_factor"] = free_elevator_factor x_ca_plane_free = (tail_efficiency * free_elevator_factor * cl_alpha_ht * lp_ht - cm_alpha_fus * l0_wing) / \ (cl_alpha_wing + tail_efficiency * free_elevator_factor * cl_alpha_ht) x_aero_center_free = x_ca_plane_free / l0_wing + 0.25 outputs["data:aerodynamics:cruise:neutral_point:stick_free:x"] = x_aero_center_free
def compute(self, inputs, outputs, discrete_inputs=None, discrete_outputs=None): fus_length = inputs["data:geometry:fuselage:length"] lav = inputs["data:geometry:fuselage:front_length"] lar = inputs["data:geometry:fuselage:rear_length"] maximum_width = inputs["data:geometry:fuselage:maximum_width"] maximum_height = inputs["data:geometry:fuselage:maximum_height"] wet_area_fus = inputs["data:geometry:fuselage:wet_area"] sizing_factor_ultimate = inputs["data:mission:sizing:cs23:sizing_factor_ultimate"] mtow = inputs["data:weight:aircraft:MTOW"] lp_ht = inputs["data:geometry:horizontal_tail:MAC:at25percent:x:from_wingMAC25"] cruise_alt = inputs["data:mission:sizing:main_route:cruise:altitude"] v_cruise = inputs["data:TLAR:v_cruise"] * 0.5144 atm_cruise = Atmosphere(cruise_alt) rho_cruise = atm_cruise.density pressure_cruise = atm_cruise.pressure atm_sl = Atmosphere(0.0) pressure_sl = atm_sl.pressure dynamic_pressure = 1. / 2. * rho_cruise * v_cruise ** 2. * 0.020885434273039 if cruise_alt > 10000.: fus_dia = (maximum_height + maximum_width) / 2. v_press = (fus_length - lar - lav) * math.pi * (fus_dia / 2.) ** 2.0 delta_p = (pressure_sl - pressure_cruise) * 0.000145038 else: v_press = 0.0 delta_p = 0.0 a2 = 0.052 * ( wet_area_fus ** 1.086 * (sizing_factor_ultimate * mtow) ** 0.177 * lp_ht ** (-0.051) * ((fus_length - lar - lav) / maximum_height) ** (-0.072) * dynamic_pressure ** 0.241 + 11.9 * (v_press * delta_p) ** 0.271 ) outputs["data:weight:airframe:fuselage:mass_raymer"] = a2
def compute(self, inputs, outputs, discrete_inputs=None, discrete_outputs=None): wing_area = inputs["data:geometry:wing:area"] ht_area = inputs["data:geometry:horizontal_tail:area"] mlw = inputs["data:weight:aircraft:MLW"] cl0_htp = inputs["data:aerodynamics:horizontal_tail:low_speed:CL0"] cl_alpha_htp_isolated = inputs[ "data:aerodynamics:horizontal_tail:low_speed:CL_alpha_isolated"] cl_alpha_htp = inputs[ "data:aerodynamics:horizontal_tail:low_speed:CL_alpha"] cl0_clean_wing = inputs["data:aerodynamics:wing:low_speed:CL0_clean"] cl_alpha_wing = inputs["data:aerodynamics:wing:low_speed:CL_alpha"] cl_flaps_landing = inputs["data:aerodynamics:flaps:landing:CL"] cl_max_landing = inputs["data:aerodynamics:aircraft:landing:CL_max"] cl_delta_elev = inputs["data:aerodynamics:elevator:low_speed:CL_delta"] # Conditions for calculation atm = Atmosphere(0.0) rho = atm.density # Calculate elevator max. additional lift if self.options["landing"]: elev_angle = inputs["data:mission:sizing:landing:elevator_angle"] else: elev_angle = inputs["data:mission:sizing:takeoff:elevator_angle"] cl_elev = cl_delta_elev * elev_angle # Define alpha angle depending on phase if self.options["landing"]: # Calculation of take-off minimum speed weight = mlw * g vs0 = math.sqrt(weight / (0.5 * rho * wing_area * cl_max_landing)) # Rotation speed correction v_r = vs0 * 1.3 # Evaluate aircraft overall angle (aoa) cl0_landing = cl0_clean_wing + cl_flaps_landing cl_landing = weight / (0.5 * rho * v_r**2 * wing_area) alpha = (cl_landing - cl0_landing) / cl_alpha_wing * 180 / math.pi else: # Define aircraft overall angle (aoa) alpha = 0.0 # Interpolate cl/cm and define with ht reference surface cl_htp = ((cl0_htp + (alpha * math.pi / 180) * cl_alpha_htp + cl_elev) * wing_area / ht_area) # Define Cl_alpha with htp reference surface cl_alpha_htp_isolated = cl_alpha_htp_isolated * wing_area / ht_area outputs["cl_htp"] = cl_htp outputs["cl_alpha_htp_isolated"] = cl_alpha_htp_isolated
def sfc_ratio( self, altitude: Union[float, Sequence[float]], thrust_rate: Union[float, Sequence[float]], mach: Union[float, Sequence[float]] = 0.8, ) -> Tuple[np.ndarray, np.ndarray]: """ Computation of ratio :math:`\\frac{SFC(P)}{SFC(Pmax)}`, given altitude and thrust_rate :math:`\\frac{F}{Fmax}`. Warning: this model is very limited :param altitude: :param thrust_rate: :param mach: Mach number(s) :return: SFC ratio and Power (in W) """ altitude = np.asarray(altitude) thrust_rate = np.asarray(thrust_rate) mach = np.asarray(mach) mach = mach + (mach == 0) * 1e-12 max_thrust = self.max_thrust( Atmosphere(altitude, altitude_in_feet=False), mach) sigma = Atmosphere( altitude, altitude_in_feet=False).density / Atmosphere(0.0).density max_power = np.minimum( self.max_power * (sigma - (1 - sigma) / 7.55), max_thrust * mach * Atmosphere(altitude).speed_of_sound / PROPELLER_EFFICIENCY) prop_power = (max_thrust * thrust_rate * mach * Atmosphere(altitude).speed_of_sound) mech_power = prop_power / PROPELLER_EFFICIENCY # FIXME: low speed efficiency should drop down leading to high mechanical power! power_rate = mech_power / max_power sfc_ratio = (-0.9976 * power_rate**2 + 1.9964 * power_rate) return sfc_ratio, (power_rate * max_power)
def test_sfc_at_max_thrust(): """ Checks model against values from :cite:`roux:2005` p.40 (only for ground/Mach=0 values, as cruise values of the report look flawed) .. bibliography:: ../refs.bib """ # Check with arrays cfm56_3c1 = RubberEngine(6, 25.7, 0, 0, 0, 0) atm = Atmosphere([0, 10668, 13000], altitude_in_feet=False) sfc = cfm56_3c1.sfc_at_max_thrust(atm, [0, 0.8, 0.8]) # Note: value for alt==10668 is different from PhD report # alt=13000 is here just for testing in stratosphere np.testing.assert_allclose(sfc, [0.97035e-5, 1.7756e-5, 1.7711e-5], rtol=1e-4) # Check with scalars trent900 = RubberEngine(7.14, 41, 0, 0, 0, 0) atm = Atmosphere(0, altitude_in_feet=False) sfc = trent900.sfc_at_max_thrust(atm, 0) np.testing.assert_allclose(sfc, 0.73469e-5, rtol=1e-4) atm = Atmosphere(9144, altitude_in_feet=False) sfc = trent900.sfc_at_max_thrust(atm, 0.8) np.testing.assert_allclose(sfc, 1.6766e-5, rtol=1e-4) # value is different from PhD report # Check with arrays pw2037 = RubberEngine(6, 31.8, 0, 0, 0, 0) atm = Atmosphere(0, altitude_in_feet=False) sfc = pw2037.sfc_at_max_thrust(atm, 0) np.testing.assert_allclose(sfc, 0.9063e-5, rtol=1e-4) atm = Atmosphere(10668, altitude_in_feet=False) sfc = pw2037.sfc_at_max_thrust(atm, 0.85) np.testing.assert_allclose(sfc, 1.7439e-5, rtol=1e-4) # value is different from PhD report
def compute(self, inputs, outputs, discrete_inputs=None, discrete_outputs=None): sizing_factor_ultimate = inputs[ "data:mission:sizing:cs23:sizing_factor_ultimate"] mtow = inputs["data:weight:aircraft:MTOW"] v_cruise_ktas = inputs["data:TLAR:v_cruise"] cruise_alt = inputs["data:mission:sizing:main_route:cruise:altitude"] area_ht = inputs["data:geometry:horizontal_tail:area"] t_c_ht = inputs["data:geometry:horizontal_tail:thickness_ratio"] sweep_25_ht = inputs["data:geometry:horizontal_tail:sweep_25"] ar_ht = inputs["data:geometry:horizontal_tail:aspect_ratio"] taper_ht = inputs["data:geometry:horizontal_tail:taper_ratio"] rho_cruise = Atmosphere(cruise_alt).density dynamic_pressure = 1. / 2. * rho_cruise * (v_cruise_ktas * 0.5144)**2. * 0.0208854 # In lb/ft2 a31 = 0.016 * ( (sizing_factor_ultimate * mtow)**0.414 * dynamic_pressure**0.168 * area_ht**0.896 * (100 * t_c_ht / math.cos(sweep_25_ht * math.pi / 180.))**-0.12 * (ar_ht / (math.cos(sweep_25_ht * math.pi / 180.))**2.0)**0.043 * taper_ht**-0.02) # Mass formula in lb outputs["data:weight:airframe:horizontal_tail:mass"] = a31 has_t_tail = inputs["data:geometry:has_T_tail"] area_vt = inputs["data:geometry:vertical_tail:area"] t_c_vt = inputs["data:geometry:vertical_tail:thickness_ratio"] sweep_25_vt = inputs["data:geometry:vertical_tail:sweep_25"] ar_vt = inputs["data:geometry:vertical_tail:aspect_ratio"] taper_vt = inputs["data:geometry:vertical_tail:taper_ratio"] a32 = 0.073 * (1. + 0.2 * has_t_tail) * ( (sizing_factor_ultimate * mtow)**0.376 * dynamic_pressure**0.122 * area_vt**0.873 * (100 * t_c_vt / math.cos(sweep_25_vt * math.pi / 180.))**-0.49 * (ar_vt / (math.cos(sweep_25_vt * math.pi / 180.))**2.0)**0.357 * taper_vt**0.039) # Mass formula in lb outputs["data:weight:airframe:vertical_tail:mass"] = a32
def compute(self, inputs, outputs, discrete_inputs=None, discrete_outputs=None): mtow = inputs["data:weight:aircraft:MTOW"] npax = inputs["data:TLAR:NPAX"] m_iae = inputs["data:weight:systems:navigation:mass"] limit_speed = inputs["data:TLAR:v_limit"] atm = Atmosphere(0.0) limit_speed = limit_speed / atm.speed_of_sound # converted to mach c22 = 0.261 * mtow**.52 * npax**0.68 * m_iae**0.17 * limit_speed**0.08 # mass formula in lb outputs["data:weight:systems:life_support:air_conditioning:mass"] = c22
def compute(self, inputs, outputs, discrete_inputs=None, discrete_outputs=None): # Get inputs b_f = inputs['data:geometry:fuselage:maximum_width'] span = inputs['data:geometry:wing:span'] aspect_ratio = inputs['data:geometry:wing:aspect_ratio'] wing_area = inputs['data:geometry:wing:area'] if self.options["low_speed_aero"]: mach = inputs["data:aerodynamics:low_speed:mach"] v_inf = max(Atmosphere(0.0).speed_of_sound * mach, 0.01) # avoid V=0 m/s crashes cl_clean = inputs["data:aerodynamics:wing:low_speed:CL"] cdp_clean = inputs["data:aerodynamics:wing:low_speed:CDp"] else: mach = inputs["data:aerodynamics:cruise:mach"] v_inf = inputs["data:TLAR:v_cruise"] cl_clean = inputs["data:aerodynamics:wing:cruise:CL"] cdp_clean = inputs["data:aerodynamics:wing:cruise:CDp"] super()._run(inputs) cl, _, oswald, _ = super().compute_wing(inputs, _INPUT_AOAList, v_inf, flaps_angle=0.0, use_airfoil=True) k_fus = 1 - 2 * (b_f / span)**2 oswald = oswald[0] * k_fus # Fuselage correction if mach > 0.4: oswald = oswald * (-0.001521 * ( (mach - 0.05) / 0.3 - 1)**10.82 + 1) # Mach correction cdp_foil = self._interpolate_cdp(cl_clean, cdp_clean, cl[0]) cdi = (1.05 * cl[0])**2 / ( math.pi * aspect_ratio * oswald) + cdp_foil # Aircraft cor.: Wing = 105% total lift. coef_e = cl[0]**2 / (math.pi * aspect_ratio * cdi) coef_k = 1. / (math.pi * span**2 / wing_area * coef_e) if self.options["low_speed_aero"]: outputs[ 'data:aerodynamics:aircraft:low_speed:induced_drag_coefficient'] = coef_k else: outputs[ 'data:aerodynamics:aircraft:cruise:induced_drag_coefficient'] = coef_k
def compute(self, inputs, outputs, discrete_inputs=None, discrete_outputs=None): propulsion_model = FuelEngineSet( self._engine_wrapper.get_model(inputs), inputs["data:geometry:propulsion:count"] ) cl_max_clean = inputs["data:aerodynamics:wing:low_speed:CL_max_clean"] cl0 = inputs["data:aerodynamics:wing:low_speed:CL0_clean"] + inputs["data:aerodynamics:flaps:takeoff:CL"] cl_alpha = inputs["data:aerodynamics:wing:low_speed:CL_alpha"] cd0 = inputs["data:aerodynamics:aircraft:low_speed:CD0"] + inputs["data:aerodynamics:flaps:takeoff:CD"] coef_k = inputs["data:aerodynamics:wing:low_speed:induced_drag_coefficient"] wing_area = inputs["data:geometry:wing:area"] wing_span = inputs["data:geometry:wing:span"] lg_height = inputs["data:geometry:landing_gear:height"] mtow = inputs["data:weight:aircraft:MTOW"] # Define atmospheric condition for safety height atm = Atmosphere(SAFETY_HEIGHT, altitude_in_feet=False) # Define Cl considering 30% margin and estimate alpha cl = cl_max_clean / 1.2 ** 2 # V2>=1.2*VS1 alpha_interp = np.linspace(0.0, 30.0, 31) * math.pi / 180.0 cl_interp = cl0 + alpha_interp * cl_alpha alpha = np.interp(cl, cl_interp, alpha_interp) # Calculate drag coefficient k_ground = ( 33. * ((lg_height + SAFETY_HEIGHT) / wing_span) ** 1.5 / (1. + 33. * ((lg_height + SAFETY_HEIGHT) / wing_span) ** 1.5) ) cd = cd0 + k_ground * coef_k * cl ** 2 # Find v2 safety speed for 0% climb rate v2 = math.sqrt((mtow * g) / (0.5 * atm.density * wing_area * cl)) # Estimate climb rate considering alpha~0° and max thrust rate for CS23.65 (loop on error) flight_point = FlightPoint( mach=v2 / atm.speed_of_sound, altitude=SAFETY_HEIGHT, engine_setting=EngineSetting.TAKEOFF, thrust_rate=1.0 ) propulsion_model.compute_flight_points(flight_point) thrust = float(flight_point.thrust) gamma = math.asin(thrust / (mtow * g) - cd / cl) rel_error = 0.1 while rel_error > 0.05: new_gamma = math.asin(thrust / (mtow * g) - cd / cl * math.cos(gamma)) rel_error = abs((new_gamma - gamma) / new_gamma) gamma = new_gamma outputs["v2:speed"] = v2 outputs["v2:angle"] = alpha outputs["v2:climb_rate"] = math.sin(gamma)
def compute_dimensions(self) -> (float, float, float, float): """ Computes propulsion dimensions (engine/nacelle/propeller) from maximum power. Model from :... """ # Compute engine dimensions self.engine.length = self.ref["length"] * ( self.max_power / self.ref["max_power"])**(1 / 3) self.engine.height = self.ref["height"] * ( self.max_power / self.ref["max_power"])**(1 / 3) self.engine.width = self.ref["width"] * ( self.max_power / self.ref["max_power"])**(1 / 3) # Compute nacelle dimensions self.nacelle = Nacelle( height=self.engine.height * 1.1, width=self.engine.width * 1.1, length=1.5 * self.engine.length, ) self.nacelle.wet_area = 2 * (self.nacelle.height + self.nacelle.width) * self.nacelle.length # Compute propeller dimensions (2-blades) w_propeller = 2500 # regulated propeller speed in RPM v_sound = Atmosphere(self.design_altitude, altitude_in_feet=False).speed_of_sound d_max = (((v_sound * 0.85)**2 - self.design_speed**2) / ((w_propeller * math.pi / 30) / 2)**2)**0.5 d_opt = 1.04**2 * ((self.max_power / 735.5) * 1e8 / (w_propeller**2 * self.design_speed * 3.6))**(1 / 4) d = min(d_max, d_opt) t_0 = 7.4 * ((self.max_power / 735.5) * d)**(2 / 3) area = 13307 * t_0 / (d**2 * w_propeller**2) chord = area / d self.propeller = Propeller( area=area, depth=chord * 1.1, diameter=d, thrust_SL=t_0, ) return self.nacelle["height"], self.nacelle["width"], self.nacelle[ "length"], self.nacelle["wet_area"]
def compute(self, inputs, outputs, discrete_inputs=None, discrete_outputs=None): propulsion_model = FuelEngineSet( self._engine_wrapper.get_model(inputs), inputs["data:geometry:propulsion:count"] ) cl0 = inputs["data:aerodynamics:wing:low_speed:CL0_clean"] + inputs["data:aerodynamics:flaps:takeoff:CL"] cl_alpha = inputs["data:aerodynamics:wing:low_speed:CL_alpha"] cd0 = inputs["data:aerodynamics:aircraft:low_speed:CD0"] + inputs["data:aerodynamics:flaps:takeoff:CD"] coef_k = inputs["data:aerodynamics:wing:low_speed:induced_drag_coefficient"] wing_area = inputs["data:geometry:wing:area"] wing_span = inputs["data:geometry:wing:span"] lg_height = inputs["data:geometry:landing_gear:height"] mtow = inputs["data:weight:aircraft:MTOW"] thrust_rate = inputs["data:mission:sizing:takeoff:thrust_rate"] friction_coeff = inputs["data:mission:sizing:takeoff:friction_coefficient_no_brake"] v_t = float(inputs["vloff:speed"]) alpha_t = float(inputs["vloff:angle"]) # Define ground factor effect on Drag k_ground = 33. * (lg_height / wing_span) ** 1.5 / (1. + 33. * (lg_height / wing_span) ** 1.5) # Start reverted calculation of flight from lift-off to 0° alpha angle atm = Atmosphere(0.0) while (alpha_t != 0.0) and (v_t != 0.0): # Estimation of thrust flight_point = FlightPoint( mach=v_t / atm.speed_of_sound, altitude=0.0, engine_setting=EngineSetting.TAKEOFF, thrust_rate=thrust_rate ) propulsion_model.compute_flight_points(flight_point) thrust = float(flight_point.thrust) # Calculate lift and drag cl = cl0 + cl_alpha * alpha_t lift = 0.5 * atm.density * wing_area * cl * v_t ** 2 cd = cd0 + k_ground * coef_k * cl ** 2 drag = 0.5 * atm.density * wing_area * cd * v_t ** 2 # Calculate rolling resistance load friction = (mtow * g - lift - thrust * math.sin(alpha_t)) * friction_coeff # Calculate acceleration acc_x = (thrust * math.cos(alpha_t) - drag - friction) / mtow # Speed and angle update (feedback) dt = min(TIME_STEP / 5, alpha_t / ALPHA_RATE, v_t / acc_x) v_t = v_t - acc_x * dt alpha_t = alpha_t - ALPHA_RATE * dt outputs["vr:speed"] = v_t
def get_cd0(alt, mach, cl, low_speed_aero): reynolds = Atmosphere(alt).get_unitary_reynolds(mach) ivc = get_indep_var_comp(input_list) if low_speed_aero: ivc.add_output("data:aerodynamics:aircraft:takeoff:mach", mach) ivc.add_output("data:aerodynamics:wing:low_speed:reynolds", reynolds) ivc.add_output("data:aerodynamics:aircraft:low_speed:CL", 150 * [cl]) # needed because size of input array is fixed else: ivc.add_output("data:TLAR:cruise_mach", mach) ivc.add_output("data:aerodynamics:wing:cruise:reynolds", reynolds) ivc.add_output("data:aerodynamics:aircraft:cruise:CL", 150 * [cl]) # needed because size of input array is fixed problem = run_system(CD0(low_speed_aero=low_speed_aero), ivc) if low_speed_aero: return problem["data:aerodynamics:aircraft:low_speed:CD0"][0] else: return problem["data:aerodynamics:aircraft:cruise:CD0"][0]
def compute(self, inputs, outputs, discrete_inputs=None, discrete_outputs=None): mlw = inputs["data:weight:aircraft:MLW"] cl_max_landing = inputs["data:aerodynamics:aircraft:landing:CL_max"] wing_area = inputs["data:geometry:wing:area"] fa_length = inputs["data:geometry:wing:MAC:at25percent:x"] l0_wing = inputs["data:geometry:wing:MAC:length"] rho = Atmosphere(0.0).density v_s0 = math.sqrt( (mlw * 9.81) / (0.5 * rho * wing_area * cl_max_landing)) v_ref = 1.3 * v_s0 propulsion_model = FuelEngineSet( self._engine_wrapper.get_model(inputs), inputs["data:geometry:propulsion:count"]) x_cg = float(fa_length) increment = l0_wing / 100. equilibrium_found = True climb_gradient_achieved = True while equilibrium_found and climb_gradient_achieved: climb_gradient, equilibrium_found = self.delta_climb_rate( x_cg, v_ref, mlw, propulsion_model, inputs) if climb_gradient < 0.033: climb_gradient_achieved = False x_cg -= increment outputs["data:handling_qualities:balked_landing_limit:x"] = x_cg x_cg_ratio = (x_cg - fa_length + 0.25 * l0_wing) / l0_wing outputs[ "data:handling_qualities:balked_landing_limit:MAC_position"] = x_cg_ratio
def compute(self, inputs, outputs): v_tas = inputs["data:TLAR:v_cruise"] cruise_altitude = inputs["data:mission:sizing:main_route:cruise:altitude"] design_mass = inputs["data:weight:aircraft:MTOW"] design_vc = Atmosphere(cruise_altitude, altitude_in_feet=False).get_equivalent_airspeed(v_tas) velocity_array, load_factor_array, _ = self.flight_domain(inputs, outputs, design_mass, cruise_altitude, design_vc, design_n_ps=0.0, design_n_ng=0.0) if DOMAIN_PTS_NB < len(velocity_array): velocity_array = velocity_array[0:DOMAIN_PTS_NB - 1] load_factor_array = load_factor_array[0:DOMAIN_PTS_NB - 1] warnings.warn("Defined maximum stored domain points in fast compute_vn.py exceeded!") else: additional_zeros = list(np.zeros(DOMAIN_PTS_NB - len(velocity_array))) velocity_array.extend(additional_zeros) load_factor_array.extend(additional_zeros) outputs["data:flight_domain:velocity"] = np.array(velocity_array) outputs["data:flight_domain:load_factor"] = np.array(load_factor_array)