How to add a custom propulsion model to FAST-OAD

Propulsion models have a specific status because they are directly called by the performance models, so the connection is not done through OpenMDAO.

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 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 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)

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 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

When developing your propulsion model, to ensure that it will work smoothly with current performances models, you have to do it in a class that implements the IPropulsion interface, meaning your class must have at least the 2 methods compute_flight_points() and get_consumed_mass().

Computation of propulsion data

compute_flight_points() will modify the provided flight point(s) by adding propulsion-related parameters. A conventional fuel engine will rely on parameters like mach, altitude and will provide parameters like sfc (Specific Fuel Consumption).

Propulsion model inputs

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 FlightPoint.

Note

Special attention has to be paid to the thrust parameters. Depending on the flight phase, the aircraft can fly in manual mode, with an imposed thrust rate, or in regulated mode, where propulsion has to give an imposed thrust. Your model has to provide these two modes, and to use them as intended.

The thrust_is_regulated parameter tells what mode is on. If it is True, the model has to rely on the thrust parameter. If it False, the model has to rely on the thrust_rate parameter.

Propulsion model outputs

If you work with the Breguet module, your model has to compute the sfc parameter.

But if you use the mission module, you have total freedom about the output of your model. If you want to use a parameter that is not available, you can add it to the FlightPoint class as described above.

The only requirement is that you have to implement get_consumed_mass() accordingly for the mission module to have a correct assessment of mass evolution.

Computation of consumed mass

The get_consumed_mass() simply provides the mass consumption over the provided time. It is meant to use the parameters computed in compute_flight_points().

The OpenMDAO wrapper

Once your propulsion model is ready, you have to make a wrapper around it for:

  • having the possibility to choose it in the FAST-OAD configuration file

  • having its parameters available in FAST-OAD data files

Defining the wrapper

Your wrapper class has to implement the IOMPropulsionWrapper interface, meaning it should implement the 2 methods get_model() and setup().

get_model() has to provide an instance of your model. If the constructor of your propulsion model class needs parameters, you may get them from inputs, that will be the inputs parameter that OpenMDAO will provide to the performance module when calling compute() method.

Therefore, the performance module will have to define the inputs that your propulsion model needs in its setup method, as required by OpenMDAO. To do this, the setup method ot the performance module calls the setup() of your wrapper, that is expected to define the needed input variables.

For an example, please see the source code of OMRubberEngineWrapper.

Registering the wrapper

Registering is needed for being able to choose your propulsion wrapper in FAST-OAD configuration file. Due to the specific status of propulsion models, the registering process is a bit different that the one for classic OpenMDAO modules.

The registering is done using the RegisterPropulsion decorator:

import fastoad.api as oad


@oad.RegisterPropulsion("star.trek.propulsion")
class WarpDriveWrapper(oad.IOMPropulsionWrapper):

    [ ... ]

Using the wrapper in the configuration file

As for other custom modules, the folder that contains your Python module(s) must be listed in the module_folders of the configuration file.

The association of the propulsion model to the performance module is done with the propulsion_id keyword, as in following example:

title: OAD Process with custom propulsion model

# List of folder paths where user added custom registered OpenMDAO components
module_folders:
  - /path/to/my/propulsion/wrapper/

[ ... ]

# Definition of OpenMDAO model
model:
  [ ... ]
  performance:
    id: fastoad.performances.mission
    propulsion_id: star.trek.propulsion