Beispiel #1
0
    def compute(self,
                inputs,
                outputs,
                discrete_inputs=None,
                discrete_outputs=None):
        sea_level_density = Atmosphere(0).density
        wing_area = inputs["data:geometry:wing:area"]
        span = inputs["data:geometry:wing:span"]
        mzfw = inputs["data:weight:aircraft:MZFW"]
        mfw = inputs["data:weight:aircraft:MFW"]
        mtow = inputs["data:weight:aircraft:MTOW"]
        cl_alpha = inputs["data:aerodynamics:aircraft:cruise:CL_alpha"]
        u_gust1 = inputs["data:load_case:lc1:U_gust"]
        alt_1 = inputs["data:load_case:lc1:altitude"]
        vc_eas1 = inputs["data:load_case:lc1:Vc_EAS"]
        u_gust2 = inputs["data:load_case:lc2:U_gust"]
        alt_2 = inputs["data:load_case:lc2:altitude"]
        vc_eas2 = inputs["data:load_case:lc2:Vc_EAS"]

        # calculation of mean geometric chord
        chord_geom = wing_area / span

        # load case #1
        m1 = 1.05 * mzfw
        n_gust_1 = self.__n_gust(
            m1,
            wing_area,
            Atmosphere(alt_1).density,
            sea_level_density,
            chord_geom,
            vc_eas1,
            cl_alpha,
            u_gust1,
        )
        n1 = 1.5 * max(2.5, n_gust_1)
        n1m1 = n1 * m1

        # load case #2
        n_gust_2 = self.__n_gust(
            mtow,
            wing_area,
            Atmosphere(alt_2).density,
            sea_level_density,
            chord_geom,
            vc_eas2,
            cl_alpha,
            u_gust2,
        )
        n2 = 1.5 * max(2.5, n_gust_2)
        mcv = min(0.8 * mfw, mtow - mzfw)
        n2m2 = n2 * (mtow - 0.55 * mcv)

        outputs["data:mission:sizing:cs25:sizing_load_1"] = n1m1
        outputs["data:mission:sizing:cs25:sizing_load_2"] = n2m2
Beispiel #2
0
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 #3
0
    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)
        atm.true_airspeed = speed
        reynolds = atm.unitary_reynolds * l0_wing

        outputs["data:aerodynamics:aircraft:landing:mach"] = atm.mach
        outputs["data:aerodynamics:wing:landing:reynolds"] = reynolds
Beispiel #4
0
    def get_cd0(alt, mach, cl, low_speed_aero):
        atm = Atmosphere(alt)
        atm.mach = mach
        reynolds = atm.unitary_reynolds

        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]
Beispiel #5
0
    def compute(self,
                inputs,
                outputs,
                discrete_inputs=None,
                discrete_outputs=None):
        # Area of HTP is computed so its "lift" can counter the moment of weight
        # on front landing gear w.r.t. main landing gear when the CG is in its
        # most front position.

        tail_type = np.round(inputs["data:geometry:has_T_tail"])
        fuselage_length = inputs["data:geometry:fuselage:length"]
        x_wing_aero_center = inputs["data:geometry:wing:MAC:at25percent:x"]
        x_main_lg = inputs["data:weight:airframe:landing_gear:main:CG:x"]
        x_front_lg = inputs["data:weight:airframe:landing_gear:front:CG:x"]
        mtow = inputs["data:weight:aircraft:MTOW"]
        wing_area = inputs["data:geometry:wing:area"]
        wing_mac = inputs["data:geometry:wing:MAC:length"]
        cg_range = inputs["settings:weight:aircraft:CG:range"]
        front_lg_weight_ratio = inputs[
            "settings:weight:airframe:landing_gear:front:weight_ratio"]
        htp_aero_center_ratio = inputs[
            "settings:geometry:horizontal_tail:position_ratio_on_fuselage"]

        delta_lg = x_main_lg - x_front_lg
        atm = Atmosphere(0.0)
        rho = atm.density
        vspeed = atm.speed_of_sound * 0.2  # assume the corresponding Mach of VR is 0.2

        # Proportion of weight on front landing gear is equal to distance between
        # main landing gear and center of gravity, divided by distance between landing gears.

        # If CG is in the most front position, the distance between main landing gear
        # and center of gravity is:
        distance_cg_to_mlg = front_lg_weight_ratio * delta_lg + wing_mac * cg_range

        # So with this front CG, moment of (weight on front landing gear) w.r.t.
        # main landing gear is:
        m_front_lg = mtow * g * distance_cg_to_mlg

        # Moment coefficient
        pdyn = 0.5 * rho * vspeed**2
        cm_front_lg = m_front_lg / (pdyn * wing_area * wing_mac)

        # # CM of MTOW on main landing gear w.r.t 25% wing MAC
        # lever_arm = front_lg_weight_ratio * delta_lg  # lever arm wrt CoG
        # lever_arm += wing_mac * cg_range  # and now wrt 25% wing MAC
        # cm_wheel = mtow * g * lever_arm / (pdyn * wing_area * wing_mac)

        ht_volume_coeff = cm_front_lg

        if tail_type == 1:
            aero_centers_distance = fuselage_length - x_wing_aero_center
            wet_area_coeff = 1.6
        elif tail_type == 0:
            aero_centers_distance = htp_aero_center_ratio * fuselage_length - x_wing_aero_center
            wet_area_coeff = 2.0
        else:
            raise ValueError(
                "Value of data:geometry:has_T_tail can only be 0 or 1")

        htp_area = ht_volume_coeff / aero_centers_distance * wing_area * wing_mac
        wet_area_htp = wet_area_coeff * htp_area

        outputs[
            "data:geometry:horizontal_tail:MAC:at25percent:x:from_wingMAC25"] = aero_centers_distance
        outputs["data:geometry:horizontal_tail:wetted_area"] = wet_area_htp
        outputs["data:geometry:horizontal_tail:area"] = htp_area
Beispiel #6
0
def test_max_thrust():
    """
    Checks model against simplified (but analytically equivalent) formulas
    as in p. 59 of :cite:`roux:2005`, but with correct coefficients (yes, those in report
    are not consistent with the complete formula nor the figure 2.19 just below)

    .. bibliography:: ../refs.bib
    """
    engine = RubberEngine(5, 30, 1500, 1, 0,
                          0)  # f0=1 so that output is simply fmax/f0
    machs = np.arange(0, 1.01, 0.1)

    # Check with cruise altitude
    atm = Atmosphere(11000, altitude_in_feet=False)
    max_thrust_ratio = engine.max_thrust(atm, machs, -100)
    ref_max_thrust_ratio = (0.94916 * atm.density / 1.225 *
                            (1 - 0.68060 * machs + 0.51149 * machs**2))
    np.testing.assert_allclose(max_thrust_ratio,
                               ref_max_thrust_ratio,
                               rtol=1e-4)

    # Check with Takeoff altitude
    atm = Atmosphere(0, altitude_in_feet=False)
    max_thrust_ratio = engine.max_thrust(atm, machs, 0)
    ref_max_thrust_ratio = (0.9553 * atm.density / 1.225 *
                            (1 - 0.72971 * machs + 0.35886 * machs**2))
    np.testing.assert_allclose(max_thrust_ratio,
                               ref_max_thrust_ratio,
                               rtol=1e-4)

    # Check Cruise above 11000 with compression rate != 30 and bypass ratio != 5
    engine = RubberEngine(4, 35, 1500, 1, 0,
                          0)  # f0=1 so that output is simply fmax/f0
    atm = Atmosphere(13000, altitude_in_feet=False)
    max_thrust_ratio = engine.max_thrust(atm, machs, -50)
    ref_max_thrust_ratio = (0.96880 * atm.density / 1.225 *
                            (1 - 0.63557 * machs + 0.52108 * machs**2))
    np.testing.assert_allclose(max_thrust_ratio,
                               ref_max_thrust_ratio,
                               rtol=1e-4)

    # Check with compression rate != 30 and bypass ratio != 5 and an array for altitudes (as
    # many values as mach numbers)
    engine = RubberEngine(6, 22, 1500, 1, 0,
                          0)  # f0=1 so that output is simply fmax/f0
    atm = Atmosphere(np.arange(3000, 13100, 1000), altitude_in_feet=False)
    max_thrust_ratio = engine.max_thrust(atm, machs, -50)
    ref_max_thrust_ratio = [
        0.69811,
        0.59162,
        0.50117,
        0.42573,
        0.36417,
        0.31512,
        0.27704,
        0.24820,
        0.22678,
        0.19965,
        0.17795,
    ]
    np.testing.assert_allclose(max_thrust_ratio,
                               ref_max_thrust_ratio,
                               rtol=1e-4)
Beispiel #7
0
    def compute_flight_points_from_dt4(
        self,
        mach: Union[float, Sequence],
        altitude: Union[float, Sequence],
        delta_t4: Union[float, Sequence],
        thrust_is_regulated: Optional[Union[bool, Sequence]] = None,
        thrust_rate: Optional[Union[float, Sequence]] = None,
        thrust: Optional[Union[float, Sequence]] = None,
    ) -> Tuple[Union[float, Sequence], Union[float, Sequence], Union[
            float, Sequence]]:
        # pylint: disable=too-many-arguments  # they define the trajectory
        """
        Same as :meth:`compute_flight_points` except that delta_t4 is used directly
        instead of specifying flight engine_setting.

        :param mach: Mach number
        :param altitude: (unit=m) altitude w.r.t. to sea level
        :param delta_t4: (unit=K) difference between operational and design values of
                         turbine inlet temperature in K
        :param thrust_is_regulated: tells if thrust_rate or thrust should be used (works element-wise)
        :param thrust_rate: thrust rate (unit=none)
        :param thrust: required thrust (unit=N)
        :return: SFC (in kg/s/N), thrust rate, thrust (in N)
        """
        mach = np.asarray(mach)
        altitude = np.asarray(altitude)
        delta_t4 = np.asarray(delta_t4)

        if thrust_is_regulated is not None:
            thrust_is_regulated = np.asarray(np.round(thrust_is_regulated, 0),
                                             dtype=bool)

        thrust_is_regulated, thrust_rate, thrust = self._check_thrust_inputs(
            thrust_is_regulated, thrust_rate, thrust)

        thrust_is_regulated = np.asarray(np.round(thrust_is_regulated, 0),
                                         dtype=bool)
        thrust_rate = np.asarray(thrust_rate)
        thrust = np.asarray(thrust)

        atmosphere = Atmosphere(altitude, altitude_in_feet=False)

        max_thrust = self.max_thrust(atmosphere, mach, delta_t4)

        # We compute thrust values from thrust rates when needed
        idx = np.logical_not(thrust_is_regulated)
        if np.size(max_thrust) == 1:
            maximum_thrust = max_thrust
            out_thrust_rate = thrust_rate
            out_thrust = thrust
        else:
            out_thrust_rate = (np.full(np.shape(max_thrust),
                                       thrust_rate.item())
                               if np.size(thrust_rate) == 1 else thrust_rate)
            out_thrust = (np.full(np.shape(max_thrust), thrust.item())
                          if np.size(thrust) == 1 else thrust)

            maximum_thrust = max_thrust[idx]

        if np.any(idx):
            out_thrust[idx] = out_thrust_rate[idx] * maximum_thrust

        # thrust_rate is obtained from entire thrust vector (could be optimized if needed,
        # as some thrust rates that are computed may have been provided as input)
        out_thrust_rate = out_thrust / max_thrust

        # Now SFC can be computed
        sfc_0 = self.sfc_at_max_thrust(atmosphere, mach)
        sfc = sfc_0 * self.sfc_ratio(altitude, out_thrust_rate)

        return sfc, out_thrust_rate, out_thrust