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
Beispiel #2
0
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)
Beispiel #3
0
    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
Beispiel #4
0
    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
Beispiel #5
0
    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)
Beispiel #6
0
    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
Beispiel #10
0
    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
Beispiel #11
0
    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
Beispiel #12
0
    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
Beispiel #13
0
    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
Beispiel #17
0
    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
Beispiel #20
0
    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
Beispiel #22
0
    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
Beispiel #23
0
    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
Beispiel #25
0
    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"]
Beispiel #27
0
    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
Beispiel #28
0
    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
Beispiel #30
0
    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)