Beispiel #1
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
Beispiel #2
0
    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)
Beispiel #3
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)
Beispiel #4
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
Beispiel #5
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:
                # First point of the segment is omitted, as it is the
                # last of previous segment.
                if len(flight_points) > 1:
                    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)
Beispiel #6
0
    def _compute_mission(self, inputs, outputs):
        """
        Computes mission using time-step integration.

        :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"])

        reference_area = inputs["data:geometry:wing:area"]

        self._mission_wrapper.propulsion = propulsion_model
        self._mission_wrapper.reference_area = reference_area

        self._compute_taxi_out(inputs, outputs, propulsion_model)
        end_of_takeoff = FlightPoint(
            time=0.0,
            mass=inputs[self._mission_vars.TOW],
            true_airspeed=inputs[self._mission_vars.TAKEOFF_V2],
            altitude=inputs[self._mission_vars.TAKEOFF_ALTITUDE] + 35 * foot,
            ground_distance=0.0,
        )

        self.flight_points = self._mission_wrapper.compute(
            inputs, outputs, end_of_takeoff)

        # Final ================================================================
        end_of_mission = FlightPoint.create(self.flight_points.iloc[-1])
        reserve = self._mission_wrapper.get_reserve(
            self.flight_points, self.options["mission_name"])
        zfw = end_of_mission.mass - reserve
        reserve_name = self._mission_wrapper.get_reserve_variable_name()
        if reserve_name in outputs:
            outputs[reserve_name] = reserve
        outputs[self._mission_vars.NEEDED_BLOCK_FUEL] = (
            inputs[self._mission_vars.TOW] +
            inputs[self._mission_vars.TAKEOFF_FUEL] +
            outputs[self._mission_vars.TAXI_OUT_FUEL] - zfw)
        outputs[self._mission_vars.NEEDED_FUEL_AT_TAKEOFF] = (
            outputs[self._mission_vars.NEEDED_BLOCK_FUEL] -
            inputs[self._mission_vars.TAKEOFF_FUEL] -
            outputs[self._mission_vars.TAXI_OUT_FUEL])
        if self.options["is_sizing"]:
            outputs["data:weight:aircraft:sizing_block_fuel"] = outputs[
                self._mission_vars.NEEDED_BLOCK_FUEL]
            outputs[
                "data:weight:aircraft:sizing_onboard_fuel_at_takeoff"] = outputs[
                    self._mission_vars.NEEDED_FUEL_AT_TAKEOFF]

        def as_scalar(value):
            if isinstance(value, np.ndarray):
                return np.asscalar(value)
            return value

        self.flight_points = self.flight_points.applymap(as_scalar)
        rename_dict = {
            field_name: "%s [%s]" % (field_name, unit)
            for field_name, unit in FlightPoint.get_units().items()
        }
        self.flight_points.rename(columns=rename_dict, inplace=True)

        if self.options["out_file"]:
            self.flight_points.to_csv(self.options["out_file"])