예제 #1
0
    def compute_from(self, start: FlightPoint) -> pd.DataFrame:
        self.complete_flight_point(
            start)  # needed to ensure all speed values are computed.

        if self.target.altitude is not None:
            if isinstance(self.target.altitude, str):
                # Target altitude will be modified along the process, so we keep track
                # of the original order in target CL, that is not used otherwise.
                self.target.CL = self.target.altitude  # pylint: disable=invalid-name
                # let's put a numerical, negative value in self.target.altitude to
                # ensure there will be no problem in self._get_distance_to_target()
                self.target.altitude = -1000.0
                self.interrupt_if_getting_further_from_target = False
            else:
                # Target altitude is fixed, back to original settings (in case
                # this instance is used more than once)
                self.target.CL = None
                self.interrupt_if_getting_further_from_target = True

        atm = AtmosphereSI(start.altitude)
        if self.target.equivalent_airspeed == self.CONSTANT_VALUE:
            atm.equivalent_airspeed = start.equivalent_airspeed
            start.true_airspeed = atm.true_airspeed
        elif self.target.mach == self.CONSTANT_VALUE:
            atm.mach = start.mach
            start.true_airspeed = atm.true_airspeed

        return super().compute_from(start)
예제 #2
0
def test_EngineSet():
    """Tests behaviour of FuelEngineSet"""
    engine = DummyEngine(1.2e5, 1.0e-5)

    # input = thrust rate
    thrust_rate = np.linspace(0.0, 1.0, 20)
    for engine_count in [1, 2, 3, 4]:
        engine_set = FuelEngineSet(engine, engine_count)
        flight_point = FlightPoint(mach=0.0,
                                   altitude=0.0,
                                   engine_setting=1,
                                   thrust_rate=thrust_rate)
        engine_set.compute_flight_points(flight_point)
        assert_allclose(flight_point.sfc, engine.max_sfc * thrust_rate)
        assert_allclose(flight_point.thrust,
                        engine.max_thrust * engine_count * thrust_rate)

    # input = thrust
    thrust = np.linspace(0.0, engine.max_thrust, 30)
    for engine_count in [1, 2, 3, 4]:
        engine_set = FuelEngineSet(engine, engine_count)
        flight_point = FlightPoint(mach=0.0,
                                   altitude=0.0,
                                   engine_setting=1,
                                   thrust=thrust)
        engine_set.compute_flight_points(flight_point)
        assert_allclose(flight_point.thrust_rate,
                        thrust / engine_count / engine.max_thrust)
        assert_allclose(flight_point.sfc,
                        engine.max_sfc * flight_point.thrust_rate)
예제 #3
0
파일: cruise.py 프로젝트: jnain/FAST-OAD
    def _climb_to_altitude_and_cruise(
        self,
        start: FlightPoint,
        cruise_altitude: float,
        climb_segment: AltitudeChangeSegment,
        cruise_segment: CruiseSegment,
    ):
        """
        Climbs up to cruise_altitude and cruise, while ensuring final ground_distance is
        equal to self.target.ground_distance.

        :param start:
        :param cruise_altitude:
        :param climb_segment:
        :param cruise_segment:
        :return:
        """
        climb_segment.target = FlightPoint(
            altitude=cruise_altitude,
            mach=cruise_segment.target.mach,
            true_airspeed=cruise_segment.target.true_airspeed,
            equivalent_airspeed=cruise_segment.target.equivalent_airspeed,
        )
        climb_points = climb_segment.compute_from(start)

        cruise_start = FlightPoint.create(climb_points.iloc[-1])
        cruise_segment.target.ground_distance = (self.target.ground_distance -
                                                 cruise_start.ground_distance)
        cruise_points = cruise_segment.compute_from(cruise_start)

        return pd.concat([climb_points, cruise_points]).reset_index(drop=True)
예제 #4
0
    def complete_flight_point(self, flight_point: FlightPoint):
        """
        Computes data for provided flight point.

        Assumes that it is already defined for time, altitude, mass,
        ground distance and speed (TAS, EAS, or Mach).

        :param flight_point: the flight point that will be completed in-place
        """
        flight_point.engine_setting = self.engine_setting

        self._complete_speed_values(flight_point)

        atm = AtmosphereSI(flight_point.altitude)
        reference_force = 0.5 * atm.density * flight_point.true_airspeed**2 * self.reference_area

        if self.polar:
            flight_point.CL = flight_point.mass * g / reference_force
            flight_point.CD = self.polar.cd(flight_point.CL)
        else:
            flight_point.CL = flight_point.CD = 0.0
        flight_point.drag = flight_point.CD * reference_force

        self._compute_propulsion(flight_point)
        flight_point.slope_angle, flight_point.acceleration = self._get_gamma_and_acceleration(
            flight_point.mass, flight_point.drag, flight_point.thrust)
예제 #5
0
def test_breguet_cruise(polar):
    propulsion = FuelEngineSet(DummyEngine(0.5e5, 1.0e-5), 2)

    segment = BreguetCruiseSegment(
        target=FlightPoint(ground_distance=5.0e5),
        propulsion=propulsion,
        reference_area=120.0,
        polar=polar,
        engine_setting=EngineSetting.
        CRUISE,  # The engine model does not use this setting
    )
    flight_points = segment.compute_from(
        FlightPoint(time=10000.0, mass=70000.0, altitude=10000.0, mach=0.78))

    print_dataframe(flight_points)

    first_point = flight_points.iloc[0]
    last_point = flight_points.iloc[-1]
    # Note: reference values are obtained by running the process with CruiseSegment and 0.05s as
    # time step
    assert_allclose(first_point.altitude, 10000.0)
    assert_allclose(first_point.true_airspeed, 233.6, atol=0.1)
    assert first_point.engine_setting == EngineSetting.CRUISE

    assert_allclose(last_point.ground_distance, 500000.0)
    assert_allclose(last_point.altitude, 10000.0)
    assert_allclose(last_point.time, 12141.0, rtol=1e-2)
    assert_allclose(last_point.true_airspeed, 233.6, atol=0.1)
    assert_allclose(last_point.mass, 69568.0, rtol=1e-4)
    assert last_point.engine_setting == EngineSetting.CRUISE
예제 #6
0
def test_climb_fixed_altitude_at_constant_EAS(polar):
    propulsion = FuelEngineSet(DummyEngine(1.0e5, 1.0e-5), 2)

    flight_points = AltitudeChangeSegment(
        target=FlightPoint(altitude=10000.0, equivalent_airspeed="constant"),
        propulsion=propulsion,
        reference_area=120.0,
        polar=polar,
        thrust_rate=1.0,
        time_step=2.0,
        engine_setting=EngineSetting.
        CRUISE,  # The engine model does not use this setting
    ).compute_from(
        FlightPoint(altitude=5000.0, mass=70000.0, equivalent_airspeed=100.0))

    first_point = flight_points.iloc[0]
    last_point = flight_points.iloc[-1]
    # Note: reference values are obtained by running the process with 0.01s as time step
    assert_allclose(last_point.altitude, 10000.0)
    assert_allclose(last_point.equivalent_airspeed, 100.0)
    assert_allclose(last_point.time, 145.2, rtol=1e-2)
    assert_allclose(first_point.true_airspeed, 129.0, atol=0.1)
    assert_allclose(last_point.true_airspeed, 172.3, atol=0.1)
    assert_allclose(last_point.mass, 69710.0, rtol=1e-4)
    assert_allclose(last_point.ground_distance, 20915.0, rtol=1e-3)
    assert last_point.engine_setting == EngineSetting.CRUISE
예제 #7
0
    def _compute_breguet(self, inputs, outputs):
        """
        Computes mission using simple Breguet formula at altitude==100m and Mach 0.1
        (but max L/D ratio is assumed anyway)

        Useful only for initiating the computation.

        :param inputs: OpenMDAO input vector
        :param outputs: OpenMDAO output vector
        """
        propulsion_model = FuelEngineSet(
            self._engine_wrapper.get_model(inputs), inputs["data:geometry:propulsion:engine:count"]
        )

        high_speed_polar = self._get_initial_polar(inputs)
        distance = np.asscalar(
            np.sum(self._mission_wrapper.get_route_ranges(inputs, self.options["mission_name"]))
        )

        altitude = 100.0
        cruise_mach = 0.1

        breguet = BreguetCruiseSegment(
            FlightPoint(ground_distance=distance),
            propulsion=propulsion_model,
            polar=high_speed_polar,
            use_max_lift_drag_ratio=True,
        )
        start_point = FlightPoint(
            mass=inputs[self._mission_vars.TOW.value], altitude=altitude, mach=cruise_mach
        )
        flight_points = breguet.compute_from(start_point)
        end_point = FlightPoint.create(flight_points.iloc[-1])
        outputs[self._mission_vars.NEEDED_BLOCK_FUEL.value] = start_point.mass - end_point.mass
예제 #8
0
def test_optimal_cruise(polar):
    propulsion = FuelEngineSet(DummyEngine(0.5e5, 1.0e-5), 2)

    segment = OptimalCruiseSegment(
        target=FlightPoint(ground_distance=5.0e5),
        propulsion=propulsion,
        reference_area=120.0,
        polar=polar,
        engine_setting=EngineSetting.CRUISE,
    )
    flight_points = segment.compute_from(
        FlightPoint(mass=70000.0, time=1000.0, ground_distance=1e5, mach=0.78))
    print_dataframe(flight_points)

    first_point = flight_points.iloc[0]
    last_point = flight_points.iloc[-1]
    # Note: reference values are obtained by running the process with 0.05s as time step
    assert_allclose(first_point.altitude, 9156.0, atol=1.0)
    assert_allclose(first_point.true_airspeed, 236.4, atol=0.1)
    assert_allclose(first_point.CL, polar.optimal_cl)
    assert_allclose(first_point.CL, polar.optimal_cl)
    assert first_point.engine_setting == EngineSetting.CRUISE

    assert_allclose(last_point.ground_distance, 600000.0)
    assert_allclose(last_point.CL, polar.optimal_cl)
    assert_allclose(last_point.altitude, 9196.0, atol=1.0)
    assert_allclose(last_point.time, 3115.0, rtol=1e-2)
    assert_allclose(last_point.true_airspeed, 236.3, atol=0.1)
    assert_allclose(last_point.mass, 69577.0, rtol=1e-4)
    assert last_point.engine_setting == EngineSetting.CRUISE
예제 #9
0
def test_climb_fixed_altitude_at_constant_TAS(polar):
    propulsion = FuelEngineSet(DummyEngine(1.0e5, 1.0e-5), 2)

    # initialisation then change instance attributes
    segment = AltitudeChangeSegment(
        target=FlightPoint(altitude=10000.0),
        propulsion=propulsion,
        reference_area=120.0,
        polar=polar,
        engine_setting=EngineSetting.CLIMB,
    )  # not constant TAS order, as it is the default
    segment.thrust_rate = 1.0
    segment.time_step = 2.0
    flight_points = segment.compute_from(
        FlightPoint(altitude=5000.0, mass=70000.0,
                    true_airspeed=150.0))  # Test with dict

    last_point = flight_points.iloc[-1]
    # Note: reference values are obtained by running the process with 0.01s as time step
    assert_allclose(last_point.altitude, 10000.0)
    assert_allclose(last_point.true_airspeed, 150.0)
    assert_allclose(last_point.time, 143.5, rtol=1e-2)
    assert_allclose(last_point.mass, 69713.0, rtol=1e-4)
    assert_allclose(last_point.ground_distance, 20943.0, rtol=1e-3)
    assert last_point.engine_setting == EngineSetting.CLIMB
예제 #10
0
    def compute_next_flight_point(self, flight_points: List[FlightPoint],
                                  time_step: float) -> FlightPoint:
        """
        Computes time, altitude, speed, mass and ground distance of next flight point.

        :param flight_points: previous flight points
        :param time_step: time step for computing next point
        :return: the computed next flight point
        """
        start = flight_points[0]
        previous = flight_points[-1]
        next_point = FlightPoint()

        next_point.mass = previous.mass - self.propulsion.get_consumed_mass(
            previous, time_step)
        next_point.time = previous.time + time_step
        next_point.ground_distance = (
            previous.ground_distance +
            previous.true_airspeed * time_step * np.cos(previous.slope_angle))
        self._compute_next_altitude(next_point, previous)

        if self.target.true_airspeed == self.CONSTANT_VALUE:
            next_point.true_airspeed = previous.true_airspeed
        elif self.target.equivalent_airspeed == self.CONSTANT_VALUE:
            next_point.equivalent_airspeed = start.equivalent_airspeed
        elif self.target.mach == self.CONSTANT_VALUE:
            next_point.mach = start.mach
        else:
            next_point.true_airspeed = previous.true_airspeed + time_step * previous.acceleration

        # The naming is not done in complete_flight_point for not naming the start point
        next_point.name = self.name
        return next_point
예제 #11
0
    def compute(self, inputs: Vector, outputs: Vector,
                start_flight_point: FlightPoint) -> pd.DataFrame:
        """
        To be used during compute() of an OpenMDAO component.

        Builds the mission from input file, and computes it. `outputs` vector is
        filled with duration, burned fuel and covered ground distance for each
        part of the flight.

        :param inputs: the input vector of the OpenMDAO component
        :param outputs: the output vector of the OpenMDAO component
        :param start_flight_point: the starting flight point just after takeoff
        :return: a pandas DataFrame where columns names match fields of
                 :class:`~fastoad.model_base.flight_point.FlightPoint`
        """
        mission = self.build(inputs, self.mission_name)

        def _compute_vars(name_root, start: FlightPoint, end: FlightPoint):
            """Computes duration, burned fuel and covered distance."""
            if name_root + ":duration" in outputs:
                outputs[name_root + ":duration"] = end.time - start.time
            if name_root + ":fuel" in outputs:
                outputs[name_root + ":fuel"] = start.mass - end.mass
            if name_root + ":distance" in outputs:
                outputs[
                    name_root +
                    ":distance"] = end.ground_distance - start.ground_distance

        if not start_flight_point.name:
            start_flight_point.name = mission.flight_sequence[0].name

        current_flight_point = start_flight_point
        flight_points = mission.compute_from(start_flight_point)
        for part in mission.flight_sequence:
            var_name_root = "data:mission:%s" % part.name
            part_end = FlightPoint.create(
                flight_points.loc[flight_points.name.str.startswith(
                    part.name)].iloc[-1])
            _compute_vars(var_name_root, current_flight_point, part_end)

            if isinstance(part, FlightSequence):
                # In case of a route, outputs are computed for each phase in the route
                phase_start = current_flight_point
                for phase in part.flight_sequence:
                    phase_points = flight_points.loc[flight_points.name ==
                                                     phase.name]
                    if len(phase_points) > 0:
                        phase_end = FlightPoint.create(phase_points.iloc[-1])
                        var_name_root = "data:mission:%s" % phase.name
                        _compute_vars(var_name_root, phase_start, phase_end)
                        phase_start = phase_end

            current_flight_point = part_end

        # Outputs for the whole mission
        var_name_root = "data:mission:%s" % mission.name
        _compute_vars(var_name_root, start_flight_point, current_flight_point)

        return flight_points
예제 #12
0
    def compute_flight_points(self, flight_point: FlightPoint):

        if flight_point.thrust_is_regulated or flight_point.thrust_rate is None:
            flight_point.thrust_rate = flight_point.thrust / self.max_thrust
        else:
            flight_point.thrust = self.max_thrust * flight_point.thrust_rate

        flight_point.sfc = self.max_sfc * (1.0 + flight_point.thrust_rate) / 2.0
예제 #13
0
def test_compute_flight_points():
    engine = RubberEngine(5, 30, 1500, 1, 0.95,
                          10000)  # f0=1 so that output is simply fc/f0

    # Test with scalars
    flight_point = FlightPoint(
        mach=0,
        altitude=0,
        engine_setting=EngineSetting.TAKEOFF,
        thrust_rate=0.8)  # with engine_setting as EngineSetting
    engine.compute_flight_points(flight_point)
    np.testing.assert_allclose(flight_point.thrust, 0.9553 * 0.8, rtol=1e-4)
    np.testing.assert_allclose(flight_point.sfc, 0.99210e-5, rtol=1e-4)

    flight_point = FlightPoint(mach=0.3,
                               altitude=0,
                               engine_setting=EngineSetting.CLIMB.value,
                               thrust=0.35677)  # with engine_setting as int
    engine.compute_flight_points(flight_point)
    np.testing.assert_allclose(flight_point.thrust_rate, 0.5, rtol=1e-4)
    np.testing.assert_allclose(flight_point.sfc, 1.3496e-5, rtol=1e-4)

    # Test full arrays
    # 2D arrays are used, where first line is for thrust rates, and second line
    # is for thrust values.
    # As thrust rates and thrust values match, thrust rate results are 2 equal
    # lines and so are thrust value results.
    machs = [0, 0.3, 0.3, 0.8, 0.8]
    altitudes = [0, 0, 0, 10000, 13000]
    thrust_rates = [0.8, 0.5, 0.5, 0.4, 0.7]
    thrusts = [0.9553 * 0.8, 0.38851, 0.35677, 0.09680, 0.11273]
    engine_settings = [
        EngineSetting.TAKEOFF,
        EngineSetting.TAKEOFF,
        EngineSetting.CLIMB,
        EngineSetting.IDLE,
        EngineSetting.CRUISE.value,
    ]  # mix EngineSetting with integers
    expected_sfc = [0.99210e-5, 1.3496e-5, 1.3496e-5, 1.8386e-5, 1.5957e-5]

    flight_points = pd.DataFrame()
    flight_points["mach"] = machs + machs
    flight_points["altitude"] = altitudes + altitudes
    flight_points["engine_setting"] = engine_settings + engine_settings
    flight_points["thrust_is_regulated"] = [False] * 5 + [True] * 5
    flight_points["thrust_rate"] = thrust_rates + [0.0] * 5
    flight_points["thrust"] = [0.0] * 5 + thrusts
    engine.compute_flight_points(flight_points)
    np.testing.assert_allclose(flight_points.sfc,
                               expected_sfc + expected_sfc,
                               rtol=1e-4)
    np.testing.assert_allclose(flight_points.thrust_rate,
                               thrust_rates + thrust_rates,
                               rtol=1e-4)
    np.testing.assert_allclose(flight_points.thrust,
                               thrusts + thrusts,
                               rtol=1e-4)
def test_dummy_reserve():
    dummy_reserve = DummyTransitionSegment(
        target=FlightPoint(altitude=0.0, mach=0.0), reserve_mass_ratio=0.1
    )
    flight_points = dummy_reserve.compute_from(FlightPoint(altitude=0.0, mach=0.0, mass=55.0e3))
    assert_allclose(flight_points.mass, [55.0e3, 55.0e3, 50.0e3])
    assert_allclose(flight_points.altitude, [0.0, 0.0, 0.0])
    assert_allclose(flight_points.ground_distance, [0.0, 0.0, 0.0])
    assert_allclose(flight_points.mach, [0.0, 0.0, 0.0])
    assert_allclose(flight_points.true_airspeed, [0.0, 0.0, 0.0], rtol=1.0e-4)
예제 #15
0
def test_ranged_route(low_speed_polar, high_speed_polar, cleanup):

    engine = DummyEngine(1e5, 2.0e-5)
    propulsion = FuelEngineSet(engine, 2)

    total_distance = 2.0e6

    kwargs = dict(propulsion=propulsion, reference_area=120.0)
    initial_climb = InitialClimbPhase(**kwargs,
                                      polar=low_speed_polar,
                                      thrust_rate=1.0,
                                      name="initial_climb",
                                      time_step=0.2)
    climb = ClimbPhase(
        **kwargs,
        polar=high_speed_polar,
        thrust_rate=0.8,
        target_altitude="mach",
        maximum_mach=0.78,
        name="climb",
        time_step=5.0,
    )
    cruise = CruiseSegment(
        **kwargs,
        target=FlightPoint(ground_distance=0.0),
        polar=high_speed_polar,
        engine_setting=EngineSetting.CRUISE,
        name=FlightPhase.CRUISE.value,
    )
    descent = DescentPhase(
        **kwargs,
        polar=high_speed_polar,
        thrust_rate=0.05,
        target_altitude=1500.0 * foot,
        name=FlightPhase.DESCENT.value,
        time_step=5.0,
    )

    flight_calculator = RangedRoute([initial_climb, climb], cruise, [descent],
                                    total_distance)
    assert flight_calculator.cruise_speed == ("mach", 0.78)

    start = FlightPoint(true_airspeed=150.0 * knot,
                        altitude=100.0 * foot,
                        mass=70000.0,
                        ground_distance=100000.0)
    flight_points = flight_calculator.compute_from(start)

    # plot_flight(flight_points, "test_ranged_flight.png")

    assert_allclose(
        flight_points.iloc[-1].ground_distance,
        total_distance + start.ground_distance,
        atol=flight_calculator.distance_accuracy,
    )
예제 #16
0
def test_acceleration_not_enough_thrust(polar):
    propulsion = FuelEngineSet(DummyEngine(0.5e5, 1.0e-5), 2)

    segment = SpeedChangeSegment(
        target=FlightPoint(true_airspeed=250.0),
        propulsion=propulsion,
        reference_area=120.0,
        polar=polar,
        thrust_rate=0.1,
    )
    assert len(
        segment.compute_from(
            FlightPoint(altitude=5000.0, true_airspeed=150.0, mass=70000.0)))
예제 #17
0
def test_dummy_descent_with_reserve():
    dummy_descent_reserve = DummyTransitionSegment(
        target=FlightPoint(altitude=0.0, mach=0.0, ground_distance=500.0e3),
        mass_ratio=0.9,
        reserve_mass_ratio=0.08,
    )
    flight_points = dummy_descent_reserve.compute_from(
        FlightPoint(altitude=9.0e3, mass=60.0e3, mach=0.8))
    assert_allclose(flight_points.mass, [60.0e3, 54.0e3, 50.0e3])
    assert_allclose(flight_points.altitude, [9.0e3, 0.0, 0.0])
    assert_allclose(flight_points.ground_distance, [0.0, 500.0e3, 500.0e3])
    assert_allclose(flight_points.mach, [0.8, 0.0, 0.0])
    assert_allclose(flight_points.true_airspeed, [243.04, 0.0, 0.0],
                    rtol=1.0e-4)
예제 #18
0
def test_climb_not_enough_thrust(polar):
    propulsion = FuelEngineSet(DummyEngine(1.0e5, 1.0e-5), 2)

    segment = AltitudeChangeSegment(
        target=FlightPoint(altitude=10000.0, true_airspeed="constant"),
        propulsion=propulsion,
        reference_area=120.0,
        polar=polar,
        thrust_rate=0.1,
    )
    assert (len(
        segment.compute_from(
            FlightPoint(altitude=5000.0, true_airspeed=150.0,
                        mass=70000.0))) == 1)
def test_dummy_climb():
    dummy_climb = DummyTransitionSegment(
        target=FlightPoint(altitude=9.0e3, mach=0.8, ground_distance=400.0e3), mass_ratio=0.8
    )

    flight_points = dummy_climb.compute_from(
        FlightPoint(altitude=0.0, mass=100.0e3, mach=0.0, ground_distance=100.0e3)
    )

    assert_allclose(flight_points.mass, [100.0e3, 80.0e3])
    assert_allclose(flight_points.altitude, [0.0, 9.0e3])
    assert_allclose(flight_points.ground_distance, [100.0e3, 500.0e3])
    assert_allclose(flight_points.mach, [0.0, 0.8])
    assert_allclose(flight_points.true_airspeed, [0.0, 243.04], rtol=1.0e-4)
예제 #20
0
    def compute_from(self, start: FlightPoint) -> pd.DataFrame:
        parts = []
        part_start = start
        for part in self.flight_sequence:
            flight_points = part.compute_from(part_start)
            if len(parts) > 0 and len(flight_points) > 1:
                # First point of the segment is omitted, as it is the last of previous segment.
                #
                # But sometimes (especially in the case of simplistic segments), the new first
                # point may contain more information than the previous last one. In such case,
                # it is interesting to complete the previous last one.
                last_flight_points = parts[-1]
                last_index = last_flight_points.index[-1]
                for name in flight_points.columns:
                    value = last_flight_points.loc[last_index, name]
                    if not value:
                        last_flight_points.loc[last_index,
                                               name] = flight_points.loc[0,
                                                                         name]

                parts.append(flight_points.iloc[1:])

            else:
                # But it is kept if the computed segment is the first one.
                parts.append(flight_points)

            part_start = FlightPoint.create(flight_points.iloc[-1])

        if parts:
            return pd.concat(parts).reset_index(drop=True)
예제 #21
0
def test_hold(polar):
    propulsion = FuelEngineSet(DummyEngine(0.5e5, 2.0e-5), 2)

    segment = HoldSegment(target=FlightPoint(time=3000.0),
                          propulsion=propulsion,
                          reference_area=120.0,
                          polar=polar)
    flight_points = segment.compute_from(
        FlightPoint(altitude=500.0, equivalent_airspeed=250.0, mass=60000.0), )

    last_point = flight_points.iloc[-1]
    assert_allclose(last_point.time, 3000.0)
    assert_allclose(last_point.altitude, 500.0)
    assert_allclose(last_point.equivalent_airspeed, 250.0, atol=0.1)
    assert_allclose(last_point.mass, 57776.0, rtol=1e-4)
    assert_allclose(last_point.ground_distance, 768323.0, rtol=1.0e-3)
예제 #22
0
 def flight_sequence(self) -> List[Union[IFlightPart, str]]:
     return [
         AltitudeChangeSegment(
             target=FlightPoint(equivalent_airspeed="constant", altitude=400.0 * foot),
             engine_setting=EngineSetting.TAKEOFF,
             **self.segment_kwargs,
         ),
         SpeedChangeSegment(
             target=FlightPoint(equivalent_airspeed=250.0 * knot),
             engine_setting=EngineSetting.TAKEOFF,
             **self.segment_kwargs,
         ),
         AltitudeChangeSegment(
             target=FlightPoint(equivalent_airspeed="constant", altitude=1500.0 * foot),
             engine_setting=EngineSetting.TAKEOFF,
             **self.segment_kwargs,
         ),
     ]
예제 #23
0
def test_taxi():
    propulsion = FuelEngineSet(DummyEngine(0.5e5, 1.0e-5), 2)

    segment = TaxiSegment(target=FlightPoint(time=500.0),
                          propulsion=propulsion,
                          thrust_rate=0.1)
    flight_points = segment.compute_from(
        FlightPoint(altitude=10.0,
                    true_airspeed=10.0,
                    mass=50000.0,
                    time=10000.0), )

    last_point = flight_points.iloc[-1]
    assert_allclose(last_point.altitude, 10.0, atol=1.0)
    assert_allclose(last_point.time, 10500.0, rtol=1e-2)
    assert_allclose(last_point.true_airspeed, 10.0, atol=0.1)
    assert_allclose(last_point.mass, 49973.0, rtol=1e-4)
    assert_allclose(last_point.ground_distance, 5000.0)
예제 #24
0
def test_climb_and_cruise_at_optimal_flight_level(polar):
    propulsion = FuelEngineSet(DummyEngine(0.5e5, 3.0e-5), 2)
    reference_area = 120.0

    segment = ClimbAndCruiseSegment(
        target=FlightPoint(
            ground_distance=10.0e6,
            altitude=AltitudeChangeSegment.OPTIMAL_FLIGHT_LEVEL),
        propulsion=propulsion,
        reference_area=reference_area,
        polar=polar,
        engine_setting=EngineSetting.CRUISE,
        climb_segment=AltitudeChangeSegment(
            target=FlightPoint(),
            propulsion=propulsion,
            reference_area=reference_area,
            polar=polar,
            thrust_rate=0.9,
            engine_setting=EngineSetting.CLIMB,
        ),
    )

    flight_points = segment.compute_from(
        FlightPoint(mass=70000.0,
                    altitude=8000.0,
                    mach=0.78,
                    ground_distance=1.0e6))

    first_point = flight_points.iloc[0]
    last_point = flight_points.iloc[-1]
    # Note: reference values are obtained by running the process with 1.0s as time step

    assert_allclose(first_point.altitude, 8000.0)
    assert_allclose(first_point.thrust_rate, 0.9)
    assert_allclose(first_point.true_airspeed, 240.3, atol=0.1)
    assert first_point.engine_setting == EngineSetting.CLIMB

    assert_allclose(flight_points.mach, 0.78)
    assert_allclose(last_point.altitude, 9753.6)
    assert_allclose(last_point.ground_distance, 11.0e6)
    assert_allclose(last_point.time, 42659.0, rtol=1e-3)
    assert_allclose(last_point.true_airspeed, 234.4, atol=0.1)
    assert_allclose(last_point.mass, 48874.0, rtol=1e-4)
    assert last_point.engine_setting == EngineSetting.CRUISE
def test_descent_to_fixed_altitude_at_constant_EAS(polar):
    propulsion = FuelEngineSet(DummyEngine(1.0e5, 1.0e-5), 2)

    flight_points = AltitudeChangeSegment(
        target=FlightPoint(altitude=5000.0, equivalent_airspeed="constant"),
        propulsion=propulsion,
        reference_area=100.0,
        polar=polar,
        thrust_rate=0.1,
        time_step=2.0,
    ).compute_from(FlightPoint(altitude=10000.0, equivalent_airspeed=200.0, mass=70000.0))

    last_point = flight_points.iloc[-1]
    # Note: reference values are obtained by running the process with 0.01s as time step
    assert_allclose(last_point.altitude, 5000.0)
    assert_allclose(last_point.equivalent_airspeed, 200.0)
    assert_allclose(last_point.time, 821.4, rtol=1e-2)
    assert_allclose(last_point.mass, 69910.0, rtol=1e-4)
    assert_allclose(last_point.ground_distance, 243155.0, rtol=1e-3)
예제 #26
0
 def flight_sequence(self) -> List[Union[IFlightPart, str]]:
     self.segment_kwargs["engine_setting"] = EngineSetting.IDLE
     return [
         AltitudeChangeSegment(
             target=FlightPoint(equivalent_airspeed=300.0 * knot, mach="constant"),
             **self.segment_kwargs,
         ),
         AltitudeChangeSegment(
             target=FlightPoint(altitude=10000.0 * foot, equivalent_airspeed="constant"),
             **self.segment_kwargs,
         ),
         SpeedChangeSegment(
             target=FlightPoint(equivalent_airspeed=250.0 * knot), **self.segment_kwargs,
         ),
         AltitudeChangeSegment(
             target=FlightPoint(altitude=self.target_altitude, equivalent_airspeed="constant"),
             **self.segment_kwargs,
         ),
     ]
예제 #27
0
def test_acceleration_to_mach(polar):
    propulsion = FuelEngineSet(DummyEngine(0.5e5, 1.0e-5), 2)

    flight_points = SpeedChangeSegment(
        target=FlightPoint(mach=0.8),
        propulsion=propulsion,
        reference_area=120.0,
        polar=polar,
        thrust_rate=1.0,
        time_step=0.2,
    ).compute_from(
        FlightPoint(altitude=1000.0, true_airspeed=150.0, mass=70000.0))

    last_point = flight_points.iloc[-1]
    # Note: reference values are obtained by running the process with 0.01s as time step
    assert_allclose(last_point.time, 138.0, rtol=1e-2)
    assert_allclose(last_point.altitude, 1000.0)
    assert_allclose(last_point.mach, 0.8, atol=1e-5)
    assert_allclose(last_point.mass, 69862.0, rtol=1e-4)
    assert_allclose(last_point.ground_distance, 29470.0, rtol=1e-3)
def test_climb_optimal_flight_level_at_fixed_mach(polar):
    propulsion = FuelEngineSet(DummyEngine(1.0e5, 1.0e-5), 2)

    flight_points = AltitudeChangeSegment(
        target=FlightPoint(altitude=AltitudeChangeSegment.OPTIMAL_FLIGHT_LEVEL, mach="constant"),
        propulsion=propulsion,
        reference_area=120.0,
        polar=polar,
        thrust_rate=1.0,
        time_step=2.0,
    ).compute_from(FlightPoint(altitude=5000.0, mach=0.82, mass=70000.0))

    last_point = flight_points.iloc[-1]
    # Note: reference values are obtained by running the process with 0.01s as time step
    assert_allclose(flight_points.mach, 0.82)
    assert_allclose(last_point.altitude / foot, 32000.0, atol=0.1)
    assert_allclose(last_point.time, 77.5, rtol=1e-2)
    assert_allclose(last_point.true_airspeed, 246.44, rtol=1e-4)
    assert_allclose(last_point.mass, 69843.0, rtol=1e-4)
    assert_allclose(last_point.ground_distance, 19179.0, rtol=1e-3)
예제 #29
0
 def _compute_taxi_out(self, inputs, outputs, propulsion_model):
     """
     Computes the taxi-out segment.
     """
     start_of_taxi_out = FlightPoint(
         altitude=inputs[self._mission_vars.TAKEOFF_ALTITUDE.value],
         true_airspeed=0.0,
         # start mass is irrelevant here as long it does not get negative during computation.
         mass=inputs[self._mission_vars.TOW.value],
     )
     taxi_segment = TaxiSegment(
         target=FlightPoint(time=inputs[self._mission_vars.TAXI_OUT_DURATION.value],),
         thrust_rate=inputs[self._mission_vars.TAXI_OUT_THRUST_RATE.value],
         propulsion=propulsion_model,
     )
     flight_points = taxi_segment.compute_from(start_of_taxi_out)
     end_of_taxi_out = flight_points.iloc[-1]
     outputs[self._mission_vars.TAXI_OUT_FUEL.value] = (
         start_of_taxi_out.mass - end_of_taxi_out.mass
     )
예제 #30
0
def test_descent_to_fixed_EAS_at_constant_mach(polar):
    propulsion = FuelEngineSet(DummyEngine(1.0e5, 1.0e-5), 2)

    flight_points = AltitudeChangeSegment(
        target=FlightPoint(equivalent_airspeed=150.0, mach="constant"),
        propulsion=propulsion,
        reference_area=100.0,
        polar=polar,
        thrust_rate=0.1,
        # time_step=5.0, # we use default time step
    ).compute_from(FlightPoint(altitude=10000.0, mass=70000.0, mach=0.78))

    last_point = flight_points.iloc[-1]
    # Note: reference values are obtained by running the process with 0.01s as time step
    assert_allclose(last_point.equivalent_airspeed, 150.0)
    assert_allclose(last_point.mach, 0.78)
    assert_allclose(last_point.time, 343.6, rtol=1e-2)
    assert_allclose(last_point.altitude, 8654.0, atol=1.0)
    assert_allclose(last_point.true_airspeed, 238.1, atol=0.1)
    assert_allclose(last_point.mass, 69962.0, rtol=1e-4)
    assert_allclose(last_point.ground_distance, 81042.0, rtol=1e-3)