Skip to content

Commit

Permalink
Merge pull request #430 from fast-aircraft-design/mission-fixes
Browse files Browse the repository at this point in the history
Enhancements in Mission module
  • Loading branch information
christophe-david committed Oct 11, 2022
2 parents 1175218 + 65186b4 commit 45e0a68
Show file tree
Hide file tree
Showing 73 changed files with 7,506 additions and 2,355 deletions.
78 changes: 2 additions & 76 deletions docs/documentation/custom_modules/add_propulsion_module.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,82 +11,6 @@ By following instructions in this page, you should ensure your propulsion model
will run smoothly with the existing performance models. You will also be able
to access your engine parameters through FAST-OAD process.

*********************
The FlightPoint class
*********************

The :class:`~fastoad.model_base.flight_point.FlightPoint` class is designed to store
flight parameters for one flight point.

It is meant to be the class that performance modules will work with, and that
will be exchanged with propulsion models.

FlightPoint class is meant for:

- storing all needed parameters that are needed for performance modelling,
including propulsion parameters.
- easily exchanging data with pandas DataFrame.
- being extensible for new parameters.

.. note::

All parameters in FlightPoint instances are expected to be in SI units.

Available flight parameters
===========================
The documentation of :class:`~fastoad.model_base.flight_point.FlightPoint` provides
the list of available flight parameters, available as attributes.
As FlightPoint is a dataclass, this list is available through Python using::

>>> import fastoad.api as oad
>>> from dataclasses import fields

>>> [f.name for f in fields(oad.FlightPoint)]

Exchanges with pandas DataFrame
===============================
A pandas DataFrame can be generated from a list of FlightPoint instances::

>>> import pandas as pd
>>> import fastoad.api as oad

>>> fp1 = oad.FlightPoint(mass=70000., altitude=0.)
>>> fp2 = oad.FlightPoint(mass=60000., altitude=10000.)
>>> df = pd.DataFrame([fp1, fp2])

And FlightPoint instances can be created from DataFrame rows::

# Get one FlightPoint instance from a DataFrame row
>>> fp1bis = oad.FlightPoint.create(df.iloc[0])

# Get a list of FlightPoint instances from the whole DataFrame
>>> flight_points = oad.FlightPoint.create_list(df)


.. _flight_point_extensibility:

Extensibility
=============
FlightPoint class is bundled with several fields that are commonly used in trajectory
assessment, but one might need additional fields.

Python allows to add attributes to any instance at runtime, but for FlightPoint to run
smoothly, especially when exchanging data with pandas, you have to work at class level.
This can be done using :meth:`~fastoad.model_base.flight_point.FlightPoint.add_field`, preferably
outside of any class or function::

# Adds a float field with None as default value
>>> FlightPoint.add_field("ion_drive_power")

# Adds a field and define its type and default value
>>> FlightPoint.add_field("warp", annotation_type=int, default_value=9)

# Now these fields can be used at instantiation
>>> fp = FlightPoint(ion_drive_power=110.0, warp=12)

# Removes a field, even an original one (useful only to avoid having it in outputs)
>>> FlightPoint.remove_field("sfc")

*************************
The IPropulsion interface
*************************
Expand All @@ -113,6 +37,8 @@ For your model to work with current performance models, your model is expected
to rely on known flight parameters, i.e. the original parameters of
:class:`~fastoad.model_base.flight_point.FlightPoint`.

See :ref:`flight-point` for more details.

.. note::

Special attention has to be paid to the **thrust parameters**. Depending on the
Expand Down
2 changes: 1 addition & 1 deletion docs/documentation/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ General documentation
Here you will find the first things to know about FAST-OAD.

.. toctree::
:maxdepth: 2
:maxdepth: 4

Installation <install>
Process overview <overview>
Expand Down
221 changes: 221 additions & 0 deletions docs/documentation/mission_module/extensibility/adding_segments.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
.. _adding-segments:

########################
Adding segment types
########################

In FAST-OAD mission module, :ref:`segments<flight-segments>` are the base building blocks used
in the :ref:`mission definition file<mission-definition>`. They are implemented in Python and
FAST-OAD offers a set of segment types that allows defining typical aircraft mission profiles.

Yet, the need for some other segment types may occur. This is why FAST-OAD mission module is
designed so that any user can develop new segment types and use them in a custom mission
file.


First of all, be aware that segment implementation relies on Python
`dataclasses <https://docs.python.org/3/library/dataclasses.html>`_. This chapter
will assume you already know how it works.


.. contents::
:local:
:depth: 2

.. _adding-segments-links-with-mission-file:

Links between Python implementation and mission definition file
###############################################################

Flight segment classes must all derive from
:class:`~fastoad.models.performances.mission.segments.base.AbstractFlightSegment`.


Segment keyword
***************

When subclassing, a keyword is associated to the class::

import fastoad.api as oad
from dataclasses import dataclass

@dataclass
class NewSegment(oad.AbstractFlightSegment, mission_file_keyword="new_segment"):
...

As soon as your code is interpreted, the :code:`mission_file_keyword` will be usable in mission
definition file when specifying segments:

.. code-block:: yaml
phases:
some_phase:
parts:
- segment: taxi
...
- segment: new_segment
...
.. note::

**Where to put the code for a new segment implementations?**

Having your class in any imported Python module will do.

If you use FAST-OAD through Python, you are free to put your new segment classes where it suits
you.

Also, know that FAST-OAD will make Python interpret any Python module in the :ref:`module
folders you declare in the configuration file <add-modules-set-configuration-files>`. This works
also for :ref:`declared plugins <add-plugin>`. In both cases, it is not mandatory to add custom
FAST-OAD modules.

Segment parameters
******************

The other strong link between segment implementation and the mission definition file is that
any dataclass field of the defined segment class will be available as parameter in the mission
definition file.

Given this implementation::

import fastoad.api as oad
from dataclasses import dataclass, field
from typing import List

@dataclass
class NewSegment(oad.AbstractFlightSegment, mission_file_keyword="new_segment"):
my_float: float = 0.0
my_bool: bool = True
my_array: List[float] = field(default_factory=list)
...

... the mission definition file will accept the following implementation:

.. code-block:: yaml
phases:
some_phase:
parts:
- segment: new_segment
my_float: 50.0
my_bool: false
my_array: [10.0, 20.0, 30.0]
target:
...
.. note::

**Defining mandatory parameters**

It is possible to declare a segment parameter as mandatory (i.e. without associated default
value) by using :code:`fastoad.api.MANDATORY_FIELD`::

import fastoad.api as oad
from dataclasses import dataclass

@dataclass
class NewSegment(oad.AbstractFlightSegment, mission_file_keyword="new_segment"):
my_mandatory_float: float = oad.MANDATORY_FIELD
...

This is a way to work around the fact that if a dataclass defines a field with a default value,
inheritor classes will not be allowed to define fields without default value, because then the
non-default fields will follow a default field, which is forbidden.

Implementation of a segment class
#################################


The AbstractFlightSegment class
*******************************

As :ref:`previously said <adding-segments-links-with-mission-file>`, a segment class has to
inherit from :class:`~fastoad.models.performances.mission.segments.base.AbstractFlightSegment`
(and specify the `mission_file_keyword` if its usage is intended in mission definition files)
and will be implemented like this::

import fastoad.api as oad
from dataclasses import dataclass, field
from typing import List

@dataclass
class NewSegment(oad.AbstractFlightSegment, mission_file_keyword="new_segment"):
my_float: float = 0.0
...

The main field of the class will be
:attr:`~fastoad.models.performances.mission.segments.base.AbstractFlightSegment.target`,
provided as a :ref:`FlightPoint instance <flight-point>`, which
will contain the flight point parameters set as target in the mission definition file.

The instantiation in FAST-OAD will be like this::

import fastoad.api as oad

segment = NewSegment( target=oad.FlightPoint(altitude=5000.0, true_airspeed=200.0),
my_float=4.2,
...
)

.. note::

Instantiation arguments will always be passed as keyword arguments (this behavior can be
enforced only for Python 3.10+).

The new class will have to implement the method
:meth:`~fastoad.models.performances.mission.segments.base.AbstractFlightSegment.compute_from_start_to_target`
that will be in charge of computing the flight points between a provided `start`
and a provided `target` (providing the result as a pandas DataFrame)

.. note::

The mission computation will actually call the method
:meth:`~fastoad.models.performances.mission.segments.base.AbstractFlightSegment.compute_from`,
that will do the computation between provided `start` and the target defined at instantiation
(i.e. in the mission definition file).

This method does some generic pre-processing of start and target before calling
:meth:`~fastoad.models.performances.mission.segments.base.AbstractFlightSegment.compute_from_start_to_target`.
Therefore, in the vast majority of cases, implementing the latter will be the correct thing to do.


The AbstractTimeStepFlightSegment class
***************************************

:class:`~fastoad.models.performances.mission.segments.base.AbstractTimeStepFlightSegment` is a
base class for segments that do time step computations.

This class has 4 main additional fields:

- :attr:`~fastoad.models.performances.mission.segments.base.AbstractTimeStepFlightSegment.propulsion`,
that is expected to be an :class:`~fastoad.model_base.propulsion.IPropulsion` instance.
- :attr:`~fastoad.models.performances.mission.segments.base.AbstractTimeStepFlightSegment.polar`,
that is expected to be a :class:`~fastoad.models.performances.mission.polar.Polar` instance.
- :attr:`~fastoad.models.performances.mission.segments.base.AbstractTimeStepFlightSegment.reference_area`,
that provides the reference surface area consistently with provided aerodynamic polar.
- :attr:`~fastoad.models.performances.mission.segments.base.AbstractTimeStepFlightSegment.time_step`,
that sets the time step for resolution. It is set with a low enough default value.

An inheritor class will have to provide the implementations for 3 methods that are used at each
computed time step:
:meth:`~fastoad.models.performances.mission.segments.base.AbstractTimeStepFlightSegment.get_distance_to_target`,
:meth:`~fastoad.models.performances.mission.segments.base.AbstractTimeStepFlightSegment.compute_propulsion` and
:meth:`~fastoad.models.performances.mission.segments.base.AbstractTimeStepFlightSegment.get_gamma_and_acceleration`.
(see each method documentation for more information)

There are some specialized base classes that provide a partial implementation of
:class:`~fastoad.models.performances.mission.segments.base.AbstractTimeStepFlightSegment`:

- :class:`~fastoad.models.performances.mission.segments.base.AbstractManualThrustSegment`
implements :meth:`~fastoad.models.performances.mission.segments.base.AbstractTimeStepFlightSegment.compute_propulsion`.
It has its own field,
:attr:`~fastoad.models.performances.mission.segments.base.AbstractManualThrustSegment.thrust_rate`,
that is used to compute thrust.
- :class:`~fastoad.models.performances.mission.segments.base.AbstractRegulatedThrustSegment` also
implements :meth:`~fastoad.models.performances.mission.segments.base.AbstractTimeStepFlightSegment.compute_propulsion`,
but it adjusts the thrust rate to have aircraft thrust equal to its drag.
- :class:`~fastoad.models.performances.mission.segments.base.AbstractFixedDurationSegment`
implements :meth:`~fastoad.models.performances.mission.segments.base.AbstractTimeStepFlightSegment.get_distance_to_target`.
It allows to compute a segment with a time duration set by the target.

0 comments on commit 45e0a68

Please sign in to comment.