Repository: fast-aircraft-design/FAST-OAD (language: python)

Issue:
Enhancement in mission segments
Some changes would be needed in mission segments to prepare future evolutions, e.g. a takeoff segment:
- FlightSegment._get_gamma_and_acceleration() should take a FlightPoint as input, to allow the calculation to depend on more parameters than mass, drag and thrust
- dISA should be settable
- The definition of a starting point (especially altitude) should be possible
- It could be useful to have the possibility of defining in Python a segment that is a combination of other segments.


Discussion / hints:

Fix patch:
diff --git a/docs/documentation/custom_modules/add_propulsion_module.rst b/docs/documentation/custom_modules/add_propulsion_module.rst
index a3a9258c0..2ce5a0212 100644
--- a/docs/documentation/custom_modules/add_propulsion_module.rst
+++ b/docs/documentation/custom_modules/add_propulsion_module.rst
@@ -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
 *************************
@@ -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
diff --git a/docs/documentation/index.rst b/docs/documentation/index.rst
index 002313a76..4dbd43906 100644
--- a/docs/documentation/index.rst
+++ b/docs/documentation/index.rst
@@ -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>
diff --git a/docs/documentation/mission_module/extensibility/adding_segments.rst b/docs/documentation/mission_module/extensibility/adding_segments.rst
new file mode 100644
index 000000000..e1b2b8b8f
--- /dev/null
+++ b/docs/documentation/mission_module/extensibility/adding_segments.rst
@@ -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.
+
diff --git a/docs/documentation/mission_module/extensibility/flight_point.rst b/docs/documentation/mission_module/extensibility/flight_point.rst
new file mode 100644
index 000000000..aaf4d5619
--- /dev/null
+++ b/docs/documentation/mission_module/extensibility/flight_point.rst
@@ -0,0 +1,78 @@
+.. _flight-point:
+
+#####################
+The FlightPoint class
+#####################
+
+The :class:`~fastoad.model_base.flight_point.FlightPoint` class is designed to store
+flight parameters for one flight point of any computed mission.
+
+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")
+
diff --git a/docs/documentation/mission_module/extensibility/index.rst b/docs/documentation/mission_module/extensibility/index.rst
new file mode 100644
index 000000000..a6155252b
--- /dev/null
+++ b/docs/documentation/mission_module/extensibility/index.rst
@@ -0,0 +1,17 @@
+##############
+Extensibility
+##############
+
+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.
+
+.. toctree::
+   :maxdepth: 2
+
+    Adding segment types <adding_segments>
+    The FlightPoint class <flight_point>
diff --git a/docs/documentation/mission_module/index.rst b/docs/documentation/mission_module/index.rst
index 0215e50cb..213e7a434 100644
--- a/docs/documentation/mission_module/index.rst
+++ b/docs/documentation/mission_module/index.rst
@@ -9,3 +9,4 @@ Here you will find information about the performance module in FAST-OAD.
 
     Defining missions <mission_file/index>
     Configuring mission computation in FAST-OAD process <mission_module>
+    Extensibility <extensibility/index>
diff --git a/docs/documentation/mission_module/mission_file/index.rst b/docs/documentation/mission_module/mission_file/index.rst
index e87fee4c2..3eb86c48f 100644
--- a/docs/documentation/mission_module/mission_file/index.rst
+++ b/docs/documentation/mission_module/mission_file/index.rst
@@ -7,6 +7,6 @@ Here you will find information about the mission definition files for the FAST-O
 .. toctree::
    :maxdepth: 2
 
-    The mission file <mission_file>
-    Flight Segments <segments>
+    Mission file <mission_file>
+    Flight segments <segments>
     Setting values <setting_values>
diff --git a/docs/documentation/mission_module/mission_file/mission_file.rst b/docs/documentation/mission_module/mission_file/mission_file.rst
index 48dda7a2c..69f6f1931 100644
--- a/docs/documentation/mission_module/mission_file/mission_file.rst
+++ b/docs/documentation/mission_module/mission_file/mission_file.rst
@@ -4,22 +4,22 @@
 Mission file
 ############
 
-A mission file describes precisely one or several missions that could be computed by
-the performance model :code:`fastoad.performances.mission` of FAST-OAD.
-
-The file format of mission files is the `YAML <https://yaml.org>`_  format.
-A quick tutorial for YAML (among many ones) is available `here <https://www.cloudbees.com/blog/yaml-tutorial-everything-you-need-get-started/>`_
-
-
 .. contents::
    :local:
-   :depth: 1
+   :depth: 2
 
+*******************
+General description
+*******************
+A mission file describes precisely one or several missions that could be computed by
+the performance model :code:`fastoad.performances.mission` of FAST-OAD.
 
-***************************
-mission description
-***************************
+The file format of mission files is the `YAML <https://yaml.org>`_  format.
+A quick tutorial for YAML (among many ones) is available
+`here <https://www.cloudbees.com/blog/yaml-tutorial-everything-you-need-get-started/>`_.
 
+The mission definition relies on 4 concepts that are, from lowest level to the highest one:
+segments, phases, routes and missions. They are summarized in this table:
 
 .. list-table:: Mission elements
     :widths: 3 10 30
@@ -31,29 +31,42 @@ mission description
       - Description
     * - :ref:`segment <flight-segments>`
       - N/A
-      - The basic bricks that are provided by FAST-OAD.
+      - | The basic bricks that are provided by FAST-OAD. They are
+        | described in this :ref:`specific page <flight-segments>`.
     * - :ref:`phase <phase-section>`
-      - segment(s)
-      - A free assembly of one or more segments.
+      - | segment(s) and/or
+        | phase(s)
+      - A free assembly of one or more segments and/or other phases.
     * - :ref:`route <route-section>`
       - | zero or more phase(s)
         | **one cruise segment**
         | zero or more phase(s)
-      - | A route is a climb/cruise/descent sequence with a fixed range. The
-        | range is achieved by adjusting the distance covered during the
-        | cruise part.
+      - | A route is a climb/cruise/descent sequence with a fixed range.
+        | The range is achieved by adjusting the distance covered during
+        | the cruise part.
     * - :ref:`mission <mission-section>`
-      - routes and/or phases
+      - | routes and/or phases
+        | and/or segments
       - | A mission is what is computed by :code:`fastoad.performances.mission`.
         | Generally, it begins when engine starts and ends when engine
         | stops.
 
+*************
+File sections
+*************
+
+The organization of a mission definition file is organized in sections according to
+above-defined concepts.
+
+.. contents::
+   :local:
+   :depth: 1
+
 
 .. _phase-section:
 
-***************
-Phase section
-***************
+Phase definition section
+************************
 
 This section, identified by the :code:`phases` keyword, defines flight phases. A flight phase is
 defined as an assembly of one or more :ref:`flight segment(s) <flight-segments>`.
@@ -67,44 +80,50 @@ The phase section only defines flight phases, but not their usage, that is defin
 in :ref:`route <route-section>` and :ref:`mission <mission-section>` sections. Therefore, the
 definition order of flight phases has no importance.
 
+.. note::
+
+    Some parameters may be more conveniently set at an upper level than segment-level. See
+    section :ref:`factorizing-parameters` to see how.
+
+
 Example:
 
 .. code-block:: yaml
 
     phases:
-      initial_climb:                                # Phase name
-        engine_setting: takeoff                         # ---------------
-        polar: data:aerodynamics:aircraft:takeoff       #   Common segment
-        thrust_rate: 1.0                                #   parameters
-        time_step: 0.2                                  # ---------------
-        parts:                                          # Definition of segment list
-          - segment: altitude_change                    # 1st segment (climb)
+      initial_climb:                               # Phase name
+        parts:                                         # Definition of segment list
+          - segment: altitude_change                   # 1st segment (climb)
+            polar: data:aerodynamics:aircraft:takeoff
+            thrust_rate: 1.0
             target:
               altitude:
                 value: 400.
                 unit: ft
               equivalent_airspeed: constant
-          - segment: speed_change                       # 2nd segment (acceleration)
+          - segment: speed_change                      # 2nd segment (acceleration)
+            polar: data:aerodynamics:aircraft:takeoff
+            thrust_rate: 1.0
             target:
               equivalent_airspeed:
                 value: 250
                 unit: kn
-          - segment: altitude_change                    # 3rd segment (climb)
-            thrust_rate: 0.95                           # phase thrust rate value is overwritten
+          - segment: altitude_change                   # 3rd segment (climb)
+            polar: data:aerodynamics:aircraft:takeoff
+            thrust_rate: 0.95
             target:
               altitude:
                 value: 1500.
                 unit: ft
               equivalent_airspeed: constant
         climb:                                    # Phase name
-          ...                                       # Definition of the phase...
+          ...                                          # Definition of the phase...
 
 
 .. _route-section:
 
-*************
-Route section
-*************
+Route definition section
+************************
 
 This section, identified by the :code:`routes` keyword, defines flight routes. A flight route is
 defined as climb/cruise/descent sequence with a fixed range. The range is achieved by
@@ -121,7 +140,7 @@ A route is identified by its name and has 4 attributes:
 
 Example:
 
-.. code-block::         yaml
+.. code-block:: yaml
 
   routes:
     main_route:
@@ -155,9 +174,8 @@ Example:
 
 .. _mission-section:
 
-***************
-Mission section
-***************
+Mission definition section
+**************************
 
 This is the main section. It allows to define one or several missions, that will be computed
 by the mission module.
@@ -171,17 +189,9 @@ The mission name is used when configuring the mission module in the FAST-OAD con
 **If there is only one mission defined in the file, naming it in the configuration file is
 optional.**
 
-About mission start:
-
-    - Each mission begins by default by taxi-out and takeoff phases, but these phases are not
-      defined in the mission file. One reason for that is that the mass input for the mission is
-      the TakeOff Weight, which is the aircraft weight at the end of takeoff phase.
-    - A taxi-out phase is automatically computed at begin of the mission. To ignore this phase,
-      simply put its duration to 0. in the input data file.
-    - The takeoff data are simple inputs of the mission model. They have to be computed in a
-      dedicated takeoff model (available soon), or provided in the input data file.
+.. note::
 
-About reserve:
+    **About reserve**
 
     The :code:`reserve` keyword is typically designed to define fuel reserve as stated in
     EU-OPS 1.255.
@@ -213,3 +223,59 @@ Example:
           - phase: taxi_in
 
 
+
+.. _factorizing-parameters:
+
+**********************
+Factorizing parameters
+**********************
+
+Some parameters may be common to several segments and have same value across all of them.
+In such case, it is possible to define them at higher level (i.e. phase, route or mission)
+to avoid repeating them.
+
+For example, to specify a temperature increment at mission level, the mission section could be:
+
+.. code-block:: yaml
+
+    missions:
+      operational:
+        isa_offset: 15.0            # It will apply to the whole mission
+        parts:
+          - route: main_route
+          - phase: landing
+          - phase: taxi_in
+
+
+A high-level parameter definition will be overloaded by a lower-level definition, as illustrated
+in this example of phase definition:
+
+.. code-block:: yaml
+
+    phases:
+      initial_climb:                               # Phase name
+        engine_setting: takeoff                        # ---------------
+        polar: data:aerodynamics:aircraft:takeoff      #   Common segment
+        thrust_rate: 1.0                               #   parameters
+        time_step: 0.2                                 # ---------------
+
+        parts:                                         # Definition of segment list
+          - segment: altitude_change                     # 1st segment (climb)
+            target:
+              altitude:
+                value: 400.
+                unit: ft
+              equivalent_airspeed: constant
+          - segment: speed_change                        # 2nd segment (acceleration)
+            target:
+              equivalent_airspeed:
+                value: 250
+                unit: kn
+          - segment: altitude_change                     # 3rd segment (climb)
+            thrust_rate: 0.95        # --> PHASE THRUST RATE VALUE IS OVERWRITTEN
+            target:
+              altitude:
+                value: 1500.
+                unit: ft
+              equivalent_airspeed: constant
+
diff --git a/docs/documentation/mission_module/mission_file/segments.rst b/docs/documentation/mission_module/mission_file/segments.rst
index ac91c3634..f3dd8f416 100644
--- a/docs/documentation/mission_module/mission_file/segments.rst
+++ b/docs/documentation/mission_module/mission_file/segments.rst
@@ -29,8 +29,8 @@ Python implementation.
 All parameters of the Python constructor can be set in the mission file (except for
 :code:`propulsion` and :code:`reference_area` that are set within the mission module).
 Most of these parameters are scalars and can be set as described :ref:`here<setting-values>`.
-The segment target is a special parameter, detailed in :ref:`further section<segment-target>`
-Special parameters are detailed in :ref:`last section<segment-special-parameters>`.
+The segment target is a special parameter, detailed in :ref:`further section<segment-target>`.
+Other special parameters are detailed in :ref:`last section<segment-special-parameters>`.
 
 
 
@@ -43,8 +43,51 @@ Available segments are:
 .. _segment-speed_change:
 
 
-:code:`speed_change`
-====================
+:code:`start` segment
+=====================
+
+:code:`start` is a special segment to be used at the beginning of the mission definition to
+specify the starting point of the mission, preferably by defining variables so it can be
+controlled from FAST-OAD input file.
+
+Without no :code:`start` specified, the mission is assumed to start at altitude 0, speed 0.
+
+**Example:**
+
+.. code-block:: yaml
+
+    phases:
+      start_phase:
+        - segment: start
+          target:
+            true_airspeed: 0.0                # hard-coded value
+            altitude:
+              value: my:altitude:variable     # variable definition WITH associated default value
+              unit: ft
+              default: 100.0
+            mass:
+              value: my:mass:variable         # variable definition WITHOUT associated default value
+              unit: kg                        # (will have to be set by another module or by FAST-OAD
+                                            #  input file)
+
+    ...
+
+    missions:
+      main_mission:
+        parts:
+          - phase: start_phase
+          - ...
+
+.. note::
+
+    The :code:`start` segment allows to define the aircraft mass at the beginning of the mission.
+    Yet it is possible to define aircraft mass at some intermediate phase (e.g. takeoff) using
+    the `mass_input segment`_. In any case, mass has to be defined once and only once in the whole
+    mission.
+
+
+:code:`speed_change` segment
+============================
 
 A :code:`speed_change` segment simulates an acceleration or deceleration flight part, at constant
 altitude and thrust rate. It ends when the target speed (mach, true_airspeed or
@@ -52,7 +95,7 @@ equivalent_airspeed) is reached.
 
 Python documentation: :mod:`~fastoad.models.performances.mission.segments.speed_change.SpeedChangeSegment`
 
-Example:
+**Example:**
 
 .. code-block:: yaml
 
@@ -69,8 +112,8 @@ Example:
 
 .. _segment-altitude_change:
 
-:code:`altitude_change`
-=======================
+:code:`altitude_change` segment
+===============================
 
 An :code:`altitude_change` segment simulates a climb or descent flight part at constant thrust rate.
 Typically, it ends when the target altitude is reached.
@@ -80,7 +123,7 @@ Mach 0.8 while keeping equivalent_airspeed constant).
 
 Python documentation: :class:`~fastoad.models.performances.mission.segments.altitude_change.AltitudeChangeSegment`
 
-Examples:
+**Examples:**
 
 .. code-block:: yaml
 
@@ -119,8 +162,8 @@ Examples:
 
 .. _segment-cruise:
 
-:code:`cruise`
-==============
+:code:`cruise` segment
+======================
 
 A :code:`cruise` segment simulates a flight part at constant speed and altitude, and regulated
 thrust rate (drag is compensated).
@@ -137,7 +180,7 @@ prepending climb, if any).
 
 Python documentation: :class:`~fastoad.models.performances.mission.segments.cruise.ClimbAndCruiseSegment`
 
-Examples:
+**Examples:**
 
 .. code-block:: yaml
 
@@ -163,8 +206,8 @@ Examples:
 
 .. _segment-optimal_cruise:
 
-:code:`optimal_cruise`
-======================
+:code:`optimal_cruise` segment
+==============================
 
 An :code:`optimal_cruise` segment simulates a cruise climb, i.e. a cruise where the aircraft
 climbs gradually to keep being at altitude of maximum lift/drag ratio.
@@ -178,6 +221,8 @@ Such segment will be implemented in the future.*
 
 Python documentation: :class:`~fastoad.models.performances.mission.segments.cruise.OptimalCruiseSegment`
 
+**Example:**
+
 .. code-block:: yaml
 
     segment: optimal_cruise
@@ -189,8 +234,8 @@ Python documentation: :class:`~fastoad.models.performances.mission.segments.crui
         unit: NM
 
 
-:code:`holding`
-===============
+:code:`holding` segment
+=======================
 
 A :code:`holding` segment simulates a flight part at constant speed and altitude, and regulated
 thrust rate (drag is compensated). It ends when
@@ -198,7 +243,7 @@ the target time is covered.
 
 Python documentation: :class:`~fastoad.models.performances.mission.segments.hold.HoldSegment`
 
-Example:
+**Example:**
 
 .. code-block:: yaml
 
@@ -211,15 +256,15 @@ Example:
         unit: min
 
 
-:code:`taxi`
-============
+:code:`taxi` segment
+====================
 
 A :code:`taxi` segment simulates the mission parts between gate and takeoff or landing, at constant
 thrust rate. It ends when the target time is covered.
 
 Python documentation: :class:`~fastoad.models.performances.mission.segments.taxi.TaxiSegment`
 
-Example:
+**Example:**
 
 .. code-block:: yaml
 
@@ -229,6 +274,109 @@ Example:
       time:
         value: 300              # taxi for 300 seconds (5 minutes)
 
+:code:`transition` segment
+==========================
+
+A :code:`transition` segment is intended to "fill the gaps" when some flight part is not available
+for computation or is needed to be assessed without spending CPU time.
+
+It can be used in various ways:
+
+.. contents::
+   :local:
+   :depth: 1
+
+Target definition
+-----------------
+The most simple way is specifying a target with absolute and/or relative parameters. The second and
+last point of the flight segment will simply uses these values.
+
+**Example:**
+
+.. code-block:: yaml
+
+    segment: transition # Rough simulation of a takeoff
+    target:
+      delta_time: 60            # 60 seconds after start point
+      delta_altitude:           # 35 ft above start point
+        value: 35
+        unit: ft
+      delta_mass: -80.0         # 80kg lost from start point
+      true_airspeed: 85         # 85m/s at end of segment.
+
+Usage of a mass ratio
+---------------------
+
+As seen above, it is possible to force a mass evolution of a certain amount by specifying
+:code:`delta_mass`.
+
+It is also possible to specify a mass ratio. This can be done outside the target, as a segment
+parameter.
+
+**Example:**
+
+.. code-block:: yaml
+
+    segment: transition # Rough climb simulation
+    mass_ratio: 0.97            # Aircraft end mass will be 97% of total start mass
+    target:
+      altitude: 10000.
+      mach: 0.78
+      delta_ground_distance:    # 250 km after start point.
+        value: 250
+        unit: km
+
+Reserve mass ratio
+------------------
+
+Another segment parameter is :code:`reserve_mass_ratio`. When using this parameter, another flight
+point is added to computed segment, where the aircraft mass is decreased by a fraction of the mass
+that remains at the end of the segment (including this reserve consumption).
+
+Typically, it will be used as last segment to compute a reserve based on the Zero-Fuel-Weight mass.
+
+**Example:**
+
+    segment: transition # Rough reserve simulation
+    reserve_mass_ratio: 0.06
+    target:
+      altitude: 0.
+      mach: 0.
+
+
+:code:`mass_input` segment
+==================
+
+The `start segment` allows to define aircraft mass at the beginning of the mission, but it
+is sometimes needed to define the aircraft mass at some point in the mission. The typical
+example would be the need to specify a takeoff weight that is expected to be achieved after the
+taxi-out phase.
+
+The :code:`mass_input` segment is designed to address this need. It will ensure this mass is
+achieved at the specify instant in the mission by setting the start mass input accordingly.
+
+**Example:**
+
+.. code:: yaml
+
+    # For setting mass at the end of taxi-out:
+    phases:
+      taxi-out:
+        parts:
+          - segment: taxi
+            ...
+          - segment: mass_input
+            target:
+              mass:
+                value: my:MTOW:variable
+                unit: kg
+
+.. warning::
+
+    Currently, FAST-OAD assumes the fuel consumption before the :code:`mass_input` segment is
+    independent of aircraft mass, which is considered true in a phase such as taxi. Assuming
+    otherwise would require to solve an additional inner loop. Since it does not correspond to
+    any use case we currently know of, it has been decided to stick to the simple case.
 
 .. _segment-target:
 
@@ -247,6 +395,38 @@ a variable name, but it can also be a string. The most common string value is :c
 that tells the parameter value should be kept constant and equal to the start value.
 In any case, please refer to the documentation of the flight segment.
 
+Absolute and relative values
+============================
+
+Amost all target parameters are considered as absolute values, i.e. the target is considered
+reached if the named parameter gets equal to the provided value.
+
+They can also be specified as relative values, meaning that the target is considered reached if the
+named parameter gets equal to the provided value **added** to start value. To do so, the parameter
+name will be preceded by :code:`delta_`.
+
+**Examples:**
+
+.. code-block:: yaml
+
+    target:
+      altitude: # Target will be reached at 35000 ft.
+        value: 35000
+        unit: ft
+
+.. code-block:: yaml
+
+    target:
+      delta_altitude: # Target will be 5000 ft above the start altitude of the segment.
+        value: 5000
+        unit: ft
+
+.. important::
+    There are 2 exceptions : :code:`ground_distance` and :code:`time` are always considered as
+    relative values. Therefore, :code:`delta_ground_distance` and :code:`delta_time` will have the
+    same effect.
+
+
 
 .. _segment-special-parameters:
 
@@ -267,8 +447,8 @@ There are some special parameters that are detailed below.
 
 .. _segment-parameter-engine_setting:
 
-:code:`engine_setting`
-======================
+:code:`engine_setting` parameter
+================================
 
 Expected value for :code:`engine_setting` are :code:`takeoff`, :code:`climb`
 , :code:`cruise` or :code:`idle`
@@ -282,8 +462,8 @@ If another propulsion model is used, this parameter may become irrelevant, and t
 
 .. _segment-parameter-polar:
 
-:code:`polar`
-=============
+:code:`polar` parameter(s)
+==========================
 
 The aerodynamic polar defines the relation between lift and drag coefficients
 (respectively CL and CD).
diff --git a/docs/documentation/mission_module/mission_file/setting_values.rst b/docs/documentation/mission_module/mission_file/setting_values.rst
index a221cff59..8f6c33d62 100644
--- a/docs/documentation/mission_module/mission_file/setting_values.rst
+++ b/docs/documentation/mission_module/mission_file/setting_values.rst
@@ -1,3 +1,5 @@
+.. include:: .special.rst
+
 .. _setting-values:
 
 ##############################
@@ -46,7 +48,7 @@ Examples:
 hard-coded value with no unit
 *****************************
 
-When no unit is provided, the value can be set directly. As for :ref:`setting-values-value-unit`,
+When no unit is provided, the value can be "inlined". As for :ref:`setting-values-value-unit`,
 if the concerned parameter is not dimensionless, SI units will be assumed.
 
 Example:
@@ -64,27 +66,65 @@ Example:
 OpenMDAO variable
 *****************
 
-It is possible to provide a variable name instead of a hard-coded value. Then the
-value and unit will be set by some FAST-OAD module, or by the input file.
+It is possible to provide a variable name instead of a hard-coded value. This variable will be
+declared as input of the OpenMDAO component.
+
+Unit can be specified. In that case, it will be the unit for OpenMDAO declaration and usage. In any
+case, the unit for computation will be the internal unit of the segments (SI units). Conversion
+will be done when needed.
+
+Also, a default value can be specified, which will be the declared default value for OpenMDAO. It
+has to be consistent with declared unit. If no default value is specified, numpy.nan will be
+used in OpenMDAO declaration.
+
 
 Example:
 
+.. code-block:: yaml
+
+    altitude:
+        value: data:dummy_category:some_altitude
+        unit: ft
+        default: 35000.0
+
+As for numeric values, the definition can be inlined if no unit or default value has to be declared:
+
 .. code-block:: yaml
 
     altitude: data:dummy_category:some_altitude
 
 
+Using opposite value
+*********************
+
+Sometimes, it can be convenient to use the opposite value of a variable. It can be done by simply
+putting the minus sign "-" just before the variable:
+
+.. code-block:: yaml
+
+    delta_mass:
+        value: -data:dummy_category:consumed_fuel
+        unit: kg
+        default: 125.0
+
+.. important::
+
+    The specified default value is for the declared variable, even when the minus sign is used.
+    Therefore, if default value is set as negative and the variable is preceded by a minus sign,
+    the actually used value (if the default value is kept) will be positive.
+
+
 .. _setting-values-contextual-variable-name:
 
 ****************************
 Contextual OpenMDAO variable
 ****************************
 
-It is also possible to provide only a suffix for the variable name. Then the
-complete variable name will be decided by the hierarchy the defined parameter
-belongs to.
-The associated variable name will be
-:code:`data:mission:<mission_name>:<route_name>:<phase_name>:<suffix>`.
+By using the tilda (:code:`~`) in the variable name, it is also possible to make the variable name contextual according to the hierarchy the defined
+parameter belongs to.
+
+When a parameter value is defined as :code:`prefix~suffix`, the actual variable name will be
+:code:`prefix:<mission_name>:<route_name>:<phase_name>:suffix`.
 
 It is useful when defining a route or a phase that will be
 used in several missions (see :ref:`mission-definition`).
@@ -97,60 +137,28 @@ used in several missions (see :ref:`mission-definition`).
     - A contextual variable can be defined in a segment, but the variable will still be
       "associated" only to the phase.
 
+If no prefix is provided (:code:`~suffix`), the default prefix will be :code:`data:mission:`.
 
-A basic contextual variable is identified by a single tilde (:code:`~`). In such case,
-:code:`<suffix>` is the parameter name.
+If no suffix is provided (:code:`prefix~`), the default suffix will be the parameter name.
 
-A generic contextual variable is **preceded** by a tilde. In such case,
-:code:`<suffix>` is the name provided as value (without the tilde).
+It is also possible to have no prefix nor suffix (:code:`~`). Then the 2 rules above apply.
 
-Example 1 : generic contextual variable in a route
-==================================================
+Example
+*******
 
 .. code-block:: yaml
 
     routes:
       route_A:
-        range: ~distance   # "distance" will be the used variable name
-        parts:
-          - ...
-
-    missions:
-      mission_1:
-        parts:
-          - ...
-          - route: route_A
-          - ...
-      mission_2:
+        range: ~distance                # Example #1: here the suffix is customized.
         parts:
+          - phase_a
           - ...
-          - route: route_A
-          - ...
-
-:code:`route_A` contains the parameter :code:`range` where a contextual variable name is affected.
-:code:`route_A` is used as a step by both :code:`mission_1` and :code:`mission_2`.
-
-Then the mission computation has among its inputs:
-
-  - :code:`data:mission:mission_1:route_A:distance`
-  - :code:`data:mission:mission_2:route_A:distance`
-
-
-Example 2 : basic contextual variable in a flight phase
-=======================================================
-
-.. code-block:: yaml
 
     phases:
       phase_a:
-        thrust_rate: ~    # "thrust_rate" will be the used variable name
-
-    routes:
-      route_A:
-        range: ...
-        parts:
-          - phase_a
-          - ...
+        thrust_rate: ~                  # Example #2: default prefix and suffix will be used
+        time_step: settings:mission~    # Example #3: Here the prefix is customized
 
     missions:
       mission_1:
@@ -164,11 +172,78 @@ Example 2 : basic contextual variable in a flight phase
           - phase: phase_a
           - ...
 
-:code:`phase_a` contains the parameter :code:`thrust_rate` where a contextual variable name is affected.
-:code:`phase_a` is a used as a step by :code:`route_A`, that is used as a step by :code:`mission_1`.
+**Example 1**
+
+:code:`route_A` contains the parameter :code:`range` where a contextual variable name is affected,
+that will use the default prefix (:code:`data:mission:`) and the customized suffix
+(:code:`distance`).
+
+:code:`route_A` is used as a step by both :code:`mission_1` and :code:`mission_2`.
+
+Then the mission computation has among its inputs:
+
+.. list-table:: Variable names
+    :width: 100%
+    :header-rows: 1
+
+    * - #
+      - Prefix
+      - Hierarchy
+      - Suffix
+      - Full name
+    * - 1
+      - \data:mission
+      - mission_1:route_A
+      - distance
+      - \data:mission:mission_1:route_A:distance
+    * - 1
+      - \data:mission
+      - mission_2:route_A
+      - distance
+      - \data:mission:mission_2:route_A:distance
+
+
+**Examples 2 & 3**
+
+:code:`phase_a` contains the parameters :code:`thrust_rate` and :code:`time_step` where contextual
+variable names are affected.
+For :code:`thrust_rate`, default prefix (:code:`data:mission:`) and suffix (:code:`thrust_rate`)
+will be used.
+For :code:`time_step`, prefix is customized (:code:`settings:mission`) and default suffix
+(:code:`time_step`) will be used.
+
+
+:code:`phase_a` is used as a step by :code:`route_A`, that is used as a step by :code:`mission_1`.
 :code:`phase_a` is also used as a step directly by :code:`mission_2`.
 
 Then the mission computation has among its inputs:
 
-  - :code:`data:mission:mission_1:route_A:phase_a:thrust_rate`
-  - :code:`data:mission:mission_2:phase_a:thrust_rate`
+.. list-table:: Variable names
+    :width: 100%
+    :header-rows: 1
+
+    * - #
+      - Prefix
+      - Hierarchy
+      - Suffix
+      - Full name
+    * - 2
+      - \data:mission
+      - mission_1:route_A:phase_a
+      - thrust_rate
+      - \data:mission:mission_1:route_A:phase_a:thrust_rate
+    * - 2
+      - \data:mission
+      - mission_2:phase_a
+      - thrust_rate
+      - \data:mission:mission_2:phase_a:thrust_rate
+    * - 3
+      - \data:settings
+      - mission_1:route_A:phase_a
+      - time_step
+      - \data:settings:mission_1:route_A:phase_a:time_step
+    * - 3
+      - \data:settings
+      - mission_2:phase_a
+      - time_step
+      - \data:settings:mission_2:phase_a:time_step
diff --git a/docs/documentation/mission_module/mission_module.rst b/docs/documentation/mission_module/mission_module.rst
index 9c0120f5e..a8f16e377 100644
--- a/docs/documentation/mission_module/mission_module.rst
+++ b/docs/documentation/mission_module/mission_module.rst
@@ -22,7 +22,8 @@ The performance module, as any FAST-OAD module, is linked to the MDA process by
 of its input and output variables. But unlike other modules, the list of inputs and outputs is not
 fixed, and widely depends on the mission definition.
 
-The input variables are defined in the mission file, as described :ref:`here <setting-values-variable-name>`.
+The input variables are defined in the mission file, as described
+:ref:`here <setting-values-variable-name>`.
 
 Most outputs variables are automatically decided by the structure of the mission. Distance, duration
 and fuel burn are provided as outputs for each part of the mission.
diff --git a/src/fastoad/api.py b/src/fastoad/api.py
index ba57df994..1874f3544 100644
--- a/src/fastoad/api.py
+++ b/src/fastoad/api.py
@@ -48,6 +48,15 @@
 from fastoad.model_base.propulsion import IOMPropulsionWrapper
 from fastoad.openmdao.validity_checker import ValidityDomainChecker
 
+from fastoad.model_base.datacls import MANDATORY_FIELD
+from fastoad.models.performances.mission.segments.base import (
+    AbstractFlightSegment,
+    AbstractTimeStepFlightSegment,
+    AbstractManualThrustSegment,
+    AbstractFixedDurationSegment,
+    AbstractRegulatedThrustSegment,
+)
+
 from fastoad.gui.mission_viewer import MissionViewer
 from fastoad.gui.optimization_viewer import OptimizationViewer
 from fastoad.gui.variable_viewer import VariableViewer
diff --git a/src/fastoad/gui/analysis_and_plots.py b/src/fastoad/gui/analysis_and_plots.py
index 519b80d8c..7d59da29a 100644
--- a/src/fastoad/gui/analysis_and_plots.py
+++ b/src/fastoad/gui/analysis_and_plots.py
@@ -30,7 +30,7 @@
 
 # pylint: disable-msg=too-many-locals
 def wing_geometry_plot(
-    aircraft_file_path: str, name=None, fig=None, file_formatter=None
+    aircraft_file_path: str, name=None, fig=None, *, file_formatter=None
 ) -> go.FigureWidget:
     """
     Returns a figure plot of the top view of the wing.
@@ -102,7 +102,7 @@ def wing_geometry_plot(
 
 # pylint: disable-msg=too-many-locals
 def aircraft_geometry_plot(
-    aircraft_file_path: str, name=None, fig=None, file_formatter=None
+    aircraft_file_path: str, name=None, fig=None, *, file_formatter=None
 ) -> go.FigureWidget:
     """
     Returns a figure plot of the top view of the wing.
@@ -227,7 +227,7 @@ def aircraft_geometry_plot(
 
 
 def drag_polar_plot(
-    aircraft_file_path: str, name=None, fig=None, file_formatter=None
+    aircraft_file_path: str, name=None, fig=None, *, file_formatter=None
 ) -> go.FigureWidget:
     """
     Returns a figure plot of the aircraft drag polar.
@@ -267,7 +267,12 @@ def drag_polar_plot(
 
 
 def mass_breakdown_bar_plot(
-    aircraft_file_path: str, name=None, fig=None, file_formatter=None
+    aircraft_file_path: str,
+    name=None,
+    fig=None,
+    *,
+    file_formatter=None,
+    input_mass_name="data:weight:aircraft:MTOW",
 ) -> go.FigureWidget:
     """
     Returns a figure plot of the aircraft mass breakdown using bar plots.
@@ -279,15 +284,17 @@ def mass_breakdown_bar_plot(
     :param fig: existing figure to which add the plot
     :param file_formatter: the formatter that defines the format of data file. If not provided,
                            default format will be assumed.
+    :param input_mass_name: the variable name for the mass input as defined in the mission
+                            definition file.
     :return: bar plot figure
     """
     variables = VariableIO(aircraft_file_path, file_formatter).read()
 
     var_names_and_new_units = {
-        "data:weight:aircraft:MTOW": "kg",
+        input_mass_name: "kg",
         "data:weight:aircraft:OWE": "kg",
         "data:weight:aircraft:payload": "kg",
-        "data:weight:aircraft:sizing_onboard_fuel_at_takeoff": "kg",
+        "data:weight:aircraft:sizing_onboard_fuel_at_input_weight": "kg",
     }
 
     # pylint: disable=unbalanced-tuple-unpacking # It is balanced for the parameters provided
@@ -326,7 +333,12 @@ def mass_breakdown_bar_plot(
     return fig
 
 
-def mass_breakdown_sun_plot(aircraft_file_path: str, file_formatter=None):
+def mass_breakdown_sun_plot(
+    aircraft_file_path: str,
+    *,
+    file_formatter=None,
+    input_mass_name="data:weight:aircraft:MTOW",
+):
     """
     Returns a figure sunburst plot of the mass breakdown.
     On the left a MTOW sunburst and on the right a OWE sunburst.
@@ -336,15 +348,17 @@ def mass_breakdown_sun_plot(aircraft_file_path: str, file_formatter=None):
     :param aircraft_file_path: path of data file
     :param file_formatter: the formatter that defines the format of data file. If not provided,
                            default format will be assumed.
+    :param input_mass_name: the variable name for the mass input as defined in the mission
+                            definition file.
     :return: sunburst plot figure
     """
     variables = VariableIO(aircraft_file_path, file_formatter).read()
 
     var_names_and_new_units = {
-        "data:weight:aircraft:MTOW": "kg",
+        input_mass_name: "kg",
         "data:weight:aircraft:OWE": "kg",
         "data:weight:aircraft:payload": "kg",
-        "data:weight:aircraft:sizing_onboard_fuel_at_takeoff": "kg",
+        "data:weight:aircraft:sizing_onboard_fuel_at_input_weight": "kg",
     }
 
     # pylint: disable=unbalanced-tuple-unpacking # It is balanced for the parameters provided
diff --git a/src/fastoad/model_base/datacls.py b/src/fastoad/model_base/datacls.py
new file mode 100644
index 000000000..25c0dc73e
--- /dev/null
+++ b/src/fastoad/model_base/datacls.py
@@ -0,0 +1,41 @@
+"""Dataclass utilities."""
+#  This file is part of FAST-OAD : A framework for rapid Overall Aircraft Design
+#  Copyright (C) 2022 ONERA & ISAE-SUPAERO
+#  FAST is free software: you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation, either version 3 of the License, or
+#  (at your option) any later version.
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#  You should have received a copy of the GNU General Public License
+#  along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+from dataclasses import asdict, dataclass
+
+#: To be put as default value for dataclass fields that should not have a default value.
+#: See :class:`BaseDataClass` for further information.
+MANDATORY_FIELD = object()
+
+
+@dataclass
+class BaseDataClass:
+    """
+    This class is a workaround for the following dataclass problem:
+
+        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.
+
+    The chosen solution (from https://stackoverflow.com/a/53085935/16488238) is to always define
+    default values, but mandatory fields will have the :const:`MANDATORY_FIELD` object as default.
+
+    After initialization, :meth:`__post_init__` will process fields and raise an error if
+    a field has :const:`MANDATORY_FIELD` as value.
+    """
+
+    def __post_init__(self):
+        for name, value in asdict(self).items():
+            if value is MANDATORY_FIELD:
+                raise TypeError(f"__init__ missing 1 required argument: '{name}'")
diff --git a/src/fastoad/model_base/flight_point.py b/src/fastoad/model_base/flight_point.py
index a7a0005fe..34b9cd244 100644
--- a/src/fastoad/model_base/flight_point.py
+++ b/src/fastoad/model_base/flight_point.py
@@ -1,6 +1,6 @@
 """Structure for managing flight point data."""
 #  This file is part of FAST-OAD : A framework for rapid Overall Aircraft Design
-#  Copyright (C) 2021 ONERA & ISAE-SUPAERO
+#  Copyright (C) 2022 ONERA & ISAE-SUPAERO
 #  FAST is free software: you can redistribute it and/or modify
 #  it under the terms of the GNU General Public License as published by
 #  the Free Software Foundation, either version 3 of the License, or
@@ -11,9 +11,10 @@
 #  GNU General Public License for more details.
 #  You should have received a copy of the GNU General Public License
 #  along with this program.  If not, see <https://www.gnu.org/licenses/>.
-
-from dataclasses import asdict, dataclass
-from typing import Any, List, Mapping
+from copy import deepcopy
+from dataclasses import asdict, dataclass, fields
+from numbers import Number
+from typing import Any, List, Mapping, Sequence, Union
 
 import numpy as np
 import pandas as pd
@@ -120,6 +121,66 @@ class FlightPoint:
         acceleration="m/s**2",
     )
 
+    def __post_init__(self):
+        self._relative_parameters = {"ground_distance", "time"}
+
+    def set_as_relative(self, field_names: Union[Sequence[str], str]):
+        """
+        Makes that values for given field_names will be considered as relative when
+        calling :meth:`make_absolute`.
+
+        :param field_names:
+        """
+        if isinstance(field_names, str):
+            self._relative_parameters.add(field_names)
+        else:
+            self._relative_parameters |= set(field_names)
+
+    def set_as_absolute(self, field_names: Union[Sequence[str], str]):
+        """
+        Makes that values for given field_names will be considered as absolute when
+        calling :meth:`make_absolute`.
+
+        :param field_names:
+        """
+        if isinstance(field_names, str):
+            self._relative_parameters.remove(field_names)
+        else:
+            self._relative_parameters -= set(field_names)
+
+    def is_relative(self, field_name) -> bool:
+        """
+        Tells if given field is considered as relative or absolut
+
+        :param field_name:
+        :return: True if it is relative
+        """
+        return field_name in self._relative_parameters
+
+    def make_absolute(self, reference_point: "FlightPoint") -> "FlightPoint":
+        """
+        Computes a copy flight point where no field is relative.
+
+        :param reference_point: relative fields will be made absolute using this point.
+        :return: the copied flight point with no relative field.
+        """
+        new_point = deepcopy(self)
+        for field in fields(new_point):
+            reference_value = getattr(reference_point, field.name)
+            target_value = getattr(new_point, field.name)
+            if isinstance(target_value, Number) and new_point.is_relative(field.name):
+                setattr(new_point, field.name, reference_value + target_value)
+                new_point.set_as_absolute(field.name)
+        new_point.scalarize()
+        return new_point
+
+    @classmethod
+    def get_field_names(cls):
+        """
+        :return: names of all fields of the flight point.
+        """
+        return [field.name for field in fields(cls) if not field.name.startswith("_")]
+
     @classmethod
     def get_units(cls) -> dict:
         """
diff --git a/src/fastoad/models/performances/mission/base.py b/src/fastoad/models/performances/mission/base.py
index 2cd2038cc..84cbb33d3 100644
--- a/src/fastoad/models/performances/mission/base.py
+++ b/src/fastoad/models/performances/mission/base.py
@@ -13,17 +13,24 @@
 #  along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 from abc import ABC, abstractmethod
-from dataclasses import dataclass
-from typing import List
+from copy import deepcopy
+from dataclasses import dataclass, field
+from typing import List, Optional
 
 import pandas as pd
 
 from fastoad.model_base import FlightPoint
+from fastoad.model_base.datacls import BaseDataClass
 
 
-class IFlightPart(ABC):
-    def __init__(self):
-        self.name = ""
+@dataclass
+class IFlightPart(ABC, BaseDataClass):
+    """
+    Base class for all flight parts.
+    """
+
+    name: str = ""
+    target: FlightPoint = field(init=False, default=None)
 
     @abstractmethod
     def compute_from(self, start: FlightPoint) -> pd.DataFrame:
@@ -33,7 +40,7 @@ def compute_from(self, start: FlightPoint) -> pd.DataFrame:
         :param start: the initial flight point, defined for `altitude`, `mass` and speed
                       (`true_airspeed`, `equivalent_airspeed` or `mach`). Can also be
                       defined for `time` and/or `ground_distance`.
-        :return: a pandas DataFrame where columns names match fields of
+        :return: a pandas DataFrame where column names match fields of
                  :class:`~fastoad.model_base.flight_point.FlightPoint`
         """
 
@@ -44,14 +51,40 @@ class FlightSequence(IFlightPart):
     Defines and computes a flight sequence.
     """
 
-    def __post_init__(self):
-        self._flight_sequence = []
+    #: List of IFlightPart instances that should be run sequentially.
+    flight_sequence: List[IFlightPart] = field(default_factory=list)
+
+    #: Consumed mass between sequence start and target mass, if any defined
+    consumed_mass_before_input_weight: float = field(default=0.0, init=False)
 
     def compute_from(self, start: FlightPoint) -> pd.DataFrame:
         parts = []
-        part_start = start
+        part_start = deepcopy(start)
+        part_start.scalarize()
+
+        self.consumed_mass_before_input_weight = 0.0
+        consumed_mass = 0.0
         for part in self.flight_sequence:
+            # This check has to be done first because relative target parameters
+            # will be made absolute during compute_from()
+            part_has_target_mass = not (part.target.mass is None or part.target.is_relative("mass"))
+
             flight_points = part.compute_from(part_start)
+
+            consumed_mass += flight_points.iloc[0].mass - flight_points.iloc[-1].mass
+
+            if isinstance(part, FlightSequence):
+                self.consumed_mass_before_input_weight += part.consumed_mass_before_input_weight
+
+            # If a part has a target mass, computed mass of all previous part must be
+            # offset so this target will be reached.
+            # (mass consumption of previous parts is assumed independent of aircraft mass)
+            if part_has_target_mass:
+                mass_offset = flight_points.iloc[0].mass - part_start.mass
+                for previous_flight_points in parts:
+                    previous_flight_points.mass += mass_offset
+                self.consumed_mass_before_input_weight = float(consumed_mass)
+
             if len(parts) > 0 and len(flight_points) > 1:
                 # First point of the segment is omitted, as it is the last of previous segment.
                 #
@@ -72,11 +105,15 @@ def compute_from(self, start: FlightPoint) -> pd.DataFrame:
                 parts.append(flight_points)
 
             part_start = FlightPoint.create(flight_points.iloc[-1])
+            part_start.scalarize()
 
         if parts:
             return pd.concat(parts).reset_index(drop=True)
 
     @property
-    def flight_sequence(self) -> List[IFlightPart]:
-        """List of IFlightPart instances that should be run sequentially."""
-        return self._flight_sequence
+    def target(self) -> Optional[FlightPoint]:
+        """Target of the last element of current sequence."""
+        if len(self.flight_sequence) > 0:
+            return self.flight_sequence[-1].target
+
+        return None
diff --git a/src/fastoad/models/performances/mission/mission_definition/mission_builder.py b/src/fastoad/models/performances/mission/mission_definition/mission_builder.py
deleted file mode 100644
index 39cac6b47..000000000
--- a/src/fastoad/models/performances/mission/mission_definition/mission_builder.py
+++ /dev/null
@@ -1,498 +0,0 @@
-"""
-Mission generator.
-"""
-#  This file is part of FAST-OAD : A framework for rapid Overall Aircraft Design
-#  Copyright (C) 2021 ONERA & ISAE-SUPAERO
-#  FAST is free software: you can redistribute it and/or modify
-#  it under the terms of the GNU General Public License as published by
-#  the Free Software Foundation, either version 3 of the License, or
-#  (at your option) any later version.
-#  This program is distributed in the hope that it will be useful,
-#  but WITHOUT ANY WARRANTY; without even the implied warranty of
-#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-#  GNU General Public License for more details.
-#  You should have received a copy of the GNU General Public License
-#  along with this program.  If not, see <https://www.gnu.org/licenses/>.
-
-from collections import OrderedDict
-from copy import deepcopy
-from typing import Dict, List, Mapping, Optional, Tuple, Union
-
-import openmdao.api as om
-import pandas as pd
-
-from fastoad.constants import EngineSetting
-from fastoad.model_base import FlightPoint
-from fastoad.model_base.propulsion import IPropulsion
-from .exceptions import FastMissionFileMissingMissionNameError
-from .schema import (
-    CLIMB_PARTS_TAG,
-    CRUISE_PART_TAG,
-    DESCENT_PARTS_TAG,
-    MISSION_DEFINITION_TAG,
-    MissionDefinition,
-    PARTS_TAG,
-    PHASE_DEFINITIONS_TAG,
-    PHASE_TAG,
-    RESERVE_TAG,
-    ROUTE_DEFINITIONS_TAG,
-    SEGMENT_TAG,
-)
-from ..base import FlightSequence, IFlightPart
-from ..polar import Polar
-from ..routes import RangedRoute
-from ..segments.base import FlightSegment, SegmentDefinitions
-
-BASE_UNITS = {
-    "altitude": "m",
-    "true_airspeed": "m/s",
-    "equivalent_airspeed": "m/s",
-    "range": "m",
-    "time": "s",
-    "ground_distance": "m",
-}
-
-
-class MissionBuilder:
-    def __init__(
-        self,
-        mission_definition: Union[str, MissionDefinition],
-        *,
-        propulsion: IPropulsion = None,
-        reference_area: float = None,
-    ):
-        """
-        This class builds and computes a mission from a provided definition.
-
-        :param mission_definition: as file path or MissionDefinition instance
-        :param propulsion: if not provided, the property :attr:`propulsion` must be
-                           set before calling :meth:`build`
-        :param reference_area: if not provided, the property :attr:`reference_area` must be
-                               set before calling :meth:`build`
-        """
-        super().__init__()
-        self.definition = mission_definition
-        self._base_kwargs = {"reference_area": reference_area, "propulsion": propulsion}
-
-    @property
-    def definition(self) -> MissionDefinition:
-        """
-        The mission definition instance.
-
-        If it is set as a file path, then the matching file will be read and interpreted.
-        """
-        return self._definition
-
-    @definition.setter
-    def definition(self, mission_definition: Union[str, MissionDefinition]):
-        if isinstance(mission_definition, str):
-            self._definition = MissionDefinition(mission_definition)
-        else:
-            self._definition = mission_definition
-
-        self._structure = self._build_structure()
-
-    @property
-    def propulsion(self) -> IPropulsion:
-        """Propulsion model for performance computation."""
-        return self._base_kwargs["propulsion"]
-
-    @propulsion.setter
-    def propulsion(self, propulsion: IPropulsion):
-        self._base_kwargs["propulsion"] = propulsion
-
-    @property
-    def reference_area(self) -> float:
-        """Reference area for aerodynamic polar."""
-        return self._base_kwargs["reference_area"]
-
-    @reference_area.setter
-    def reference_area(self, reference_area: float):
-        self._base_kwargs["reference_area"] = reference_area
-
-    def build(self, inputs: Optional[Mapping] = None, mission_name: str = None) -> FlightSequence:
-        """
-        Builds the flight sequence from definition file.
-
-        :param inputs: if provided, any input parameter that is a string which matches
-                       a key of `inputs` will be replaced by the corresponding value
-        :param mission_name: mission name (can be omitted if only one mission is defined)
-        :return:
-        """
-        self._parse_values_and_units(self._structure)
-        self.get_input_variables(mission_name)  # Needed to process "contextual" variable names
-        if mission_name is None:
-            mission_name = self.get_unique_mission_name()
-        mission = self._build_mission(self._structure[mission_name], inputs)
-        self._propagate_name(mission, mission.name)
-        return mission
-
-    def get_route_ranges(
-        self, inputs: Optional[Mapping] = None, mission_name: str = None
-    ) -> List[float]:
-        """
-
-        :param inputs: if provided, any input parameter that is a string which matches
-                       a key of `inputs` will be replaced by the corresponding value
-        :param mission_name: mission name (can be omitted if only one mission is defined)
-        :return: list of flight ranges for each element of the flight sequence that is a route
-        """
-        routes = self.build(inputs, mission_name).flight_sequence
-        return [route.flight_distance for route in routes if isinstance(route, RangedRoute)]
-
-    def get_reserve(self, flight_points: pd.DataFrame, mission_name: str = None) -> float:
-        """
-        Computes the reserve fuel according to definition in mission input file.
-
-        :param flight_points: the dataframe returned by compute_from() method of the
-                              instance returned by :meth:`build`
-        :param mission_name: mission name (can be omitted if only one mission is defined)
-        :return: the reserve fuel mass in kg, or 0.0 if no reserve is defined.
-        """
-
-        if mission_name is None:
-            mission_name = self.get_unique_mission_name()
-
-        last_part_spec = self.definition[MISSION_DEFINITION_TAG][mission_name][PARTS_TAG][-1]
-        if RESERVE_TAG in last_part_spec:
-            ref_name = last_part_spec[RESERVE_TAG]["ref"]
-            multiplier = last_part_spec[RESERVE_TAG]["multiplier"]
-
-            route_points = flight_points.loc[
-                flight_points.name.str.contains("%s:%s" % (mission_name, ref_name))
-            ]
-            consumed_mass = route_points.mass.iloc[0] - route_points.mass.iloc[-1]
-            return consumed_mass * multiplier
-
-        return 0.0
-
-    def get_input_variables(self, mission_name=None) -> Dict[str, str]:
-        """
-        Identify variables for a defined mission.
-
-        :param mission_name: mission name (can be omitted if only one mission is defined)
-        :return: a dict where key, values are names, units.
-        """
-        if mission_name is None:
-            mission_name = self.get_unique_mission_name()
-
-        input_definition = {}
-        self._identify_inputs(input_definition, self._structure[mission_name])
-
-        return input_definition
-
-    def get_unique_mission_name(self) -> str:
-        """
-        Provides mission name if only one mission is defined in mission file.
-
-        :return: the mission name, if only one mission is defined
-        :raise FastMissionFileMissingMissionNameError: if several missions are defined in mission
-                                                       file
-        """
-        if len(self._structure) == 1:
-            return list(self._structure.keys())[0]
-
-        raise FastMissionFileMissingMissionNameError(
-            "Mission name must be specified if several missions are defined in mission file."
-        )
-
-    def _build_structure(self) -> OrderedDict:
-        """
-        Builds mission structures.
-
-        Unlike definition that is a mirror of the definition file, mission structures are
-        a mirror of the matching missions, so that building the actual mission instance will
-        just be a translation of the structure.
-        E.g., the definition can contain a phase that is defined once, but used in several routes.
-        The structure will define each route with the complete definition of the phase in each one.
-
-        The returned dict has mission names as keys and mission structures as values.
-        """
-        structure = OrderedDict()
-        structure.update(deepcopy(self.definition[MISSION_DEFINITION_TAG]))
-        for mission_name, mission_definition in self.definition[MISSION_DEFINITION_TAG].items():
-            mission_structure = OrderedDict({"mission": mission_name})
-            mission_structure.update(self._build_mission_structure(mission_definition))
-            structure[mission_name] = mission_structure
-        return structure
-
-    def _build_mission_structure(self, mission_definition) -> OrderedDict:
-        """Builds structure of a mission from its definition."""
-        mission_structure = OrderedDict()
-        mission_structure.update(deepcopy(mission_definition))
-        mission_parts = []
-        for part_definition in mission_definition[PARTS_TAG]:
-            if "route" in part_definition:
-                route_name = part_definition["route"]
-                route_structure = OrderedDict({"route": route_name})
-                route_structure.update(
-                    self._build_route_structure(self.definition[ROUTE_DEFINITIONS_TAG][route_name])
-                )
-                mission_parts.append(route_structure)
-            elif "phase" in part_definition:
-                phase_name = part_definition["phase"]
-                phase_definition = OrderedDict({"phase": phase_name})
-                phase_definition.update(
-                    deepcopy(self.definition[PHASE_DEFINITIONS_TAG][phase_name])
-                )
-                mission_parts.append(phase_definition)
-            else:
-                mission_parts.append(part_definition)
-
-        mission_structure[PARTS_TAG] = mission_parts
-        return mission_structure
-
-    def _build_route_structure(self, route_definition) -> OrderedDict:
-        """Builds structure of a route from its definition."""
-        route_structure = OrderedDict()
-        route_structure.update(deepcopy(route_definition))
-
-        route_structure[CLIMB_PARTS_TAG] = self._get_route_climb_or_descent_structure(
-            route_definition[CLIMB_PARTS_TAG]
-        )
-        route_structure[DESCENT_PARTS_TAG] = self._get_route_climb_or_descent_structure(
-            route_definition[DESCENT_PARTS_TAG]
-        )
-
-        return route_structure
-
-    def _get_route_climb_or_descent_structure(self, definition):
-        parts = []
-        for part_definition in definition:
-            phase_name = part_definition["phase"]
-            phase_structure = OrderedDict({"phase": phase_name})
-            phase_structure.update(deepcopy(self.definition[PHASE_DEFINITIONS_TAG][phase_name]))
-            parts.append(phase_structure)
-        return parts
-
-    def _identify_inputs(
-        self,
-        input_definition: Dict[str, Tuple[str, str]],
-        struct: Union[OrderedDict, list],
-        prefix: str = None,
-    ):
-        """
-        Identifies the OpenMDAO variables that are provided as parameter values.
-
-        A value is considered an OpenMDAO variable as soon as it is a string that
-        contains a colon ":".
-
-        If a value starts with a tilde "~<some_name>", it will be considered contextual and the
-        actual name will be built as
-        "data:mission:<mission_name>:<route_name>:<phase_name>:<some_name>" (route and phase names
-        will be used only if applicable). The variable name in provided structure will be modified
-        accordingly.
-        If value is simply "~", the parameter name will be used.
-
-        :param input_definition: dictionary to be completed with variable names as keys and
-                                 (units, description) as values
-        :param struct: any part of the structure dictionary.
-        :param prefix:
-
-        """
-        if prefix is None:
-            prefix = "data:mission"
-
-        if isinstance(struct, dict):
-            for key, value in struct.items():
-                name = struct.get("mission", "") + struct.get("route", "") + struct.get("phase", "")
-                prefix_addition = ":" + name if name else ""
-
-                if isinstance(value, str) and value.startswith("~") or value is None:
-                    # "~" alone is interpreted as "null" by the yaml parser
-                    # In that case, the parameter name in the mission file is used as suffix.
-                    # Otherwise, the string after the "~" is used as suffix.
-                    suffix = key if value is None else value[1:]
-                    value = prefix + prefix_addition + ":" + suffix
-                if isinstance(value, str) and ":" in value:
-                    input_definition[value] = (BASE_UNITS.get(key), "Input defined by the mission.")
-                    struct[key] = value
-                elif isinstance(value, (dict, list)):
-                    self._identify_inputs(input_definition, value, prefix + prefix_addition)
-
-        elif isinstance(struct, list):
-            for value in struct:
-                self._identify_inputs(input_definition, value, prefix)
-
-    def _build_mission(
-        self, mission_structure: OrderedDict, inputs: Optional[Mapping] = None
-    ) -> FlightSequence:
-        """
-        Builds mission instance from provided structure.
-
-        :param mission_structure: structure of the mission to build
-        :param inputs: if provided, variable inputs will be replaced by their value.
-        :return: the mission instance
-        """
-        mission = FlightSequence()
-
-        mission.name = mission_structure["mission"]
-        for part_spec in mission_structure[PARTS_TAG]:
-            if "route" in part_spec:
-                part = self._build_route(part_spec, inputs)
-            elif "phase" in part_spec:
-                part = self._build_phase(part_spec, inputs)
-            elif "segment" in part_spec:
-                part = self._build_segment(part_spec, {}, inputs)
-            else:  # reserve definition is used differently
-                continue
-            part.name = list(part_spec.values())[0]
-            mission.flight_sequence.append(part)
-
-        return mission
-
-    def _build_route(self, route_structure: OrderedDict, inputs: Optional[Mapping] = None):
-        """
-        Builds route instance.
-
-        :param route_structure: structure of the route to build
-        :param inputs: if provided, variable inputs will be replaced by their value.
-        :return: the route instance
-        """
-        climb_phases = []
-        descent_phases = []
-
-        for part_structure in route_structure[CLIMB_PARTS_TAG]:
-            phase = self._build_phase(part_structure, inputs)
-            climb_phases.append(phase)
-            phase.name = list(part_structure.values())[0]
-
-        cruise_phase = self._build_segment(
-            route_structure[CRUISE_PART_TAG],
-            {"name": "cruise", "target": FlightPoint(ground_distance=0.0)},
-            inputs,
-        )
-        cruise_phase.name = "cruise"
-
-        for part_structure in route_structure[DESCENT_PARTS_TAG]:
-            phase = self._build_phase(part_structure, inputs)
-            descent_phases.append(phase)
-            phase.name = list(part_structure.values())[0]
-
-        if "range" in route_structure:
-            flight_range = route_structure["range"]
-            if isinstance(flight_range, str):
-                flight_range = inputs[route_structure["range"]]
-            route = RangedRoute(
-                climb_phases, cruise_phase, descent_phases, flight_distance=flight_range
-            )
-        else:
-            route = FlightSequence()
-            route.flight_sequence.extend(climb_phases)
-
-        return route
-
-    def _build_phase(self, phase_structure, inputs: Optional[Mapping] = None):
-        """
-        Builds phase instance
-
-        :param phase_structure: structure of the phase to build
-        :param inputs: if provided, variable inputs will be replaced by their value.
-        :return: the phase instance
-        """
-        phase = FlightSequence()
-        kwargs = {name: value for name, value in phase_structure.items() if name != PARTS_TAG}
-        del kwargs[PHASE_TAG]
-
-        for part_structure in phase_structure[PARTS_TAG]:
-            segment = self._build_segment(part_structure, kwargs, inputs)
-            phase.flight_sequence.append(segment)
-
-        return phase
-
-    def _build_segment(
-        self, segment_definition: dict, kwargs: dict, inputs: Optional[Mapping], tag=SEGMENT_TAG
-    ) -> FlightSegment:
-        """
-        Builds a flight segment according to provided definition.
-
-        :param segment_definition: the segment definition from mission file
-        :param kwargs: a preset of keyword arguments for FlightSegment instantiation
-        :param inputs: if provided, any input parameter that is a string which matches
-                       a key of `inputs` will be replaced by the corresponding value
-        :param tag: the expected tag for specifying the segment type
-        :return: the FlightSegment instance
-        """
-        segment_class = SegmentDefinitions.get_segment_class(segment_definition[tag])
-        part_kwargs = kwargs.copy()
-        part_kwargs.update(
-            {name: value for name, value in segment_definition.items() if name != tag}
-        )
-        part_kwargs.update(self._base_kwargs)
-        for key, value in part_kwargs.items():
-            if key == "polar":
-                polar = {}
-                for coeff in ["CL", "CD"]:
-                    polar[coeff] = value[coeff]
-                self._replace_by_inputs(polar, inputs)
-                value = Polar(polar["CL"], polar["CD"])
-            elif key == "target":
-                if not isinstance(value, FlightPoint):
-                    self._replace_by_inputs(value, inputs)
-                    value = FlightPoint(**value)
-
-            part_kwargs[key] = value
-
-        if "engine_setting" in part_kwargs:
-            part_kwargs["engine_setting"] = EngineSetting.convert(part_kwargs["engine_setting"])
-
-        self._replace_by_inputs(part_kwargs, inputs)
-
-        segment = segment_class(**part_kwargs)
-        return segment
-
-    def _propagate_name(self, part: IFlightPart, new_name: str):
-        """
-        Changes the `name` property of all flight sub-parts of provided IFlightPart instance.
-
-        If a subpart has a non-empty name, its new name is its old name, prepended with `new_name`.
-        Otherwise, its names becomes the one of its parent after modification.
-
-        :param part:
-        :param new_name:
-        """
-        part.name = new_name
-        if isinstance(part, FlightSequence):
-            for subpart in part.flight_sequence:
-                if subpart.name:
-                    self._propagate_name(subpart, ":".join([part.name, subpart.name]))
-                else:
-                    subpart.name = part.name
-
-    @classmethod
-    def _parse_values_and_units(cls, definition):
-        """
-        Browse recursively provided dictionary and if a dictionary that has
-        "value" and "unit" as only keys is found, it is transformed into a
-        value in base units, as defined in BASE_UNITS.
-
-        Does nothing if definition is not a dict.
-
-        :param definition:
-        """
-
-        if isinstance(definition, dict):
-            for key, value in definition.items():
-                if isinstance(value, dict) and "value" in value.keys():
-                    definition[key] = om.convert_units(
-                        value["value"], value.get("unit"), BASE_UNITS.get(key)
-                    )
-                else:
-                    cls._parse_values_and_units(value)
-        elif isinstance(definition, list):
-            for value in definition:
-                cls._parse_values_and_units(value)
-
-    @staticmethod
-    def _replace_by_inputs(parameter_definition: dict, inputs: Optional[Mapping]):
-        """
-        In provided dict, if a value is a string that matches a key of `inputs`, replaces
-        it by the value provided in `inputs`.
-
-        :param parameter_definition:
-        :param inputs:
-        """
-        if inputs:
-            for key, value in parameter_definition.items():
-                if isinstance(value, str) and value in inputs:
-                    parameter_definition[key] = inputs[value]
diff --git a/src/fastoad/models/performances/mission/mission_definition/mission_builder/__init__.py b/src/fastoad/models/performances/mission/mission_definition/mission_builder/__init__.py
new file mode 100644
index 000000000..92d03116c
--- /dev/null
+++ b/src/fastoad/models/performances/mission/mission_definition/mission_builder/__init__.py
@@ -0,0 +1,15 @@
+"""Package for providing Python implementation from mission definition file."""
+#  This file is part of FAST-OAD : A framework for rapid Overall Aircraft Design
+#  Copyright (C) 2022 ONERA & ISAE-SUPAERO
+#  FAST is free software: you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation, either version 3 of the License, or
+#  (at your option) any later version.
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#  You should have received a copy of the GNU General Public License
+#  along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+from .mission_builder import MissionBuilder  # noqa: F401
diff --git a/src/fastoad/models/performances/mission/mission_definition/mission_builder/constants.py b/src/fastoad/models/performances/mission/mission_definition/mission_builder/constants.py
new file mode 100644
index 000000000..00640a28d
--- /dev/null
+++ b/src/fastoad/models/performances/mission/mission_definition/mission_builder/constants.py
@@ -0,0 +1,25 @@
+"""
+Constants for mission builder package.
+"""
+#  This file is part of FAST-OAD : A framework for rapid Overall Aircraft Design
+#  Copyright (C) 2022 ONERA & ISAE-SUPAERO
+#  FAST is free software: you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation, either version 3 of the License, or
+#  (at your option) any later version.
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#  You should have received a copy of the GNU General Public License
+#  along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+NAME_TAG = "name"
+TYPE_TAG = "type"
+SEGMENT_TYPE_TAG = "segment_type"
+
+# FIXME: should be set in Route class
+BASE_UNITS = {
+    "range": "m",
+    "distance_accuracy": "m",
+}
diff --git a/src/fastoad/models/performances/mission/mission_definition/mission_builder/input_definition.py b/src/fastoad/models/performances/mission/mission_definition/mission_builder/input_definition.py
new file mode 100644
index 000000000..2b5f3ada3
--- /dev/null
+++ b/src/fastoad/models/performances/mission/mission_definition/mission_builder/input_definition.py
@@ -0,0 +1,210 @@
+"""Management of mission input definitions."""
+#  This file is part of FAST-OAD : A framework for rapid Overall Aircraft Design
+#  Copyright (C) 2022 ONERA & ISAE-SUPAERO
+#  FAST is free software: you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation, either version 3 of the License, or
+#  (at your option) any later version.
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#  You should have received a copy of the GNU General Public License
+#  along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+from dataclasses import InitVar, dataclass, field
+from numbers import Number
+from typing import Iterable, Mapping, Optional, Tuple, Union
+
+import numpy as np
+from openmdao import api as om
+
+from fastoad.model_base import FlightPoint
+from fastoad.openmdao.variables import Variable
+from .constants import BASE_UNITS
+
+
+@dataclass
+class InputDefinition:
+    """
+    Class for managing definition of mission inputs.
+
+    It stores and processes input definition from mission files:
+        - provides values to be used for mission computation (management of units and variables)
+        - provides information for OpenMDAO declaration
+    """
+
+    #: The parameter this input is defined for.
+    parameter_name: str
+
+    #: Value, matching `input_unit`. At instantiation, it can also be the variable name.
+    input_value: Optional[Union[Number, Iterable, str]]
+
+    #: Unit used for self.input_value.
+    input_unit: Optional[str] = None
+
+    #: Default value. Used if value is a variable name.
+    default_value: Number = np.nan
+
+    #: True if variable is defined as relative.
+    is_relative: bool = False
+
+    #: Prefix used when generating variable name because "~" was used in variable name input.
+    part_identifier: str = ""
+
+    #: Unit used for self.value. Automatically determined from self.parameter_name,
+    #: mainly from unit definition for FlightPoint class.
+    output_unit: Optional[str] = field(default=None, init=False, repr=False)
+
+    #: Value of the "shape" openMDAO flag for input declaration.
+    shape: Optional[Tuple[int]] = None
+
+    #: Value of the "shape_by_conn" openMDAO flag for input declaration.
+    shape_by_conn: bool = False
+
+    #: Used only for tests
+    variable_name: InitVar[Optional[str]] = None
+
+    #: Used only for tests
+    use_opposite: InitVar[Optional[bool]] = None
+
+    #: True if the opposite value should be used, if input is defined by a variable.
+    _use_opposite: bool = field(default=False, init=False, repr=True)
+
+    _variable_name: Optional[str] = field(default=None, init=False, repr=True)
+
+    def __post_init__(self, variable_name, use_opposite):
+
+        if self.parameter_name.startswith("delta_"):
+            self.is_relative = True
+            self.parameter_name = self.parameter_name[6:]
+
+        self.output_unit = FlightPoint.get_units().get(self.parameter_name)
+        if self.output_unit is None:
+            self.output_unit = BASE_UNITS.get(self.parameter_name)
+        if self.output_unit == "-":
+            self.output_unit = None
+
+        if self.input_unit is None:
+            self.input_unit = self.output_unit
+
+        if variable_name and not isinstance(variable_name, property):
+            # dataclass "feature": default value of 'variable_name' is 'property' because it
+            # is defined as a property.
+            self.variable_name = variable_name
+            self.input_value = None
+        elif isinstance(self.input_value, str) and (
+            ":" in self.input_value or self.input_value.startswith(("~", "-~"))
+        ):
+            # This is done at end of initialization, because self.variable_name property may need
+            # data as self.parameter_name, self.prefix...
+            self.variable_name = self.input_value
+            self.input_value = None
+
+        if use_opposite is not None:
+            self._use_opposite = use_opposite
+
+    @property
+    def value(self):
+        """
+
+        :return: Value of variable in DEFAULT unit (unit used by mission calculation),
+                 or None if input is a variable and set_variable_input() has NOT been called,
+                 or the unchanged value if it is not a number.
+        """
+        try:
+            return om.convert_units(self.input_value, self.input_unit, self.output_unit)
+        except TypeError:
+            return self.input_value
+
+    @classmethod
+    def from_dict(cls, parameter_name, definition_dict: dict, part_identifier=None):
+        """
+        Instantiates InputDefinition from definition_dict.
+
+        definition_dict["value"] is used as `input_value` in instantiation. It can be an actual
+        value or a variable name.
+
+        :param parameter_name: used if definition_dict["value"] == "~" (or "-~")
+        :param definition_dict: dict with keys ("value", "unit", "default"). "unit" and "default"
+                                are optional.
+        :param part_identifier: used if "~" is in definition_dict["value"]
+        """
+        if "value" not in definition_dict:
+            return None
+
+        input_def = cls(
+            parameter_name,
+            definition_dict["value"],
+            input_unit=definition_dict.get("unit"),
+            default_value=definition_dict.get("default", np.nan),
+            shape_by_conn=definition_dict.get("shape_by_conn", False),
+            part_identifier=part_identifier,
+        )
+        return input_def
+
+    def set_variable_value(self, inputs: Mapping):
+        """
+        Sets numerical value from OpenMDAO inputs.
+
+        OpenMDAO value is assumed to be provided with unit self.input_unit.
+
+        :param inputs:
+        """
+        if self.variable_name:
+            # Note: OpenMDAO `inputs` object has no `get()` method, so we need to do this:
+            value = (
+                inputs[self.variable_name] if self.variable_name in inputs else self.default_value
+            )
+            if self._use_opposite:
+                self.input_value = -value
+            else:
+                self.input_value = value
+
+    def get_input_definition(self) -> Optional[Variable]:
+        """
+        Provides information for input definition in OpenMDAO.
+
+        :return: Variable instance with input definition, or None if no variable name was defined.
+        """
+        if self.variable_name:
+            return Variable(
+                name=self.variable_name,
+                val=self.default_value,
+                shape_by_conn=self.shape_by_conn,
+                units=self.input_unit,
+                desc="Input defined by the mission.",
+            )
+        return None
+
+    # pylint: disable=function-redefined  # the variable_name field is an InitVar.
+    @property
+    def variable_name(self):
+        """Associated variable name."""
+        return self._variable_name
+
+    @variable_name.setter
+    def variable_name(self, var_name: Optional[str]):
+        if isinstance(var_name, str):
+            self._use_opposite = var_name.startswith("-")
+            var_name = var_name.strip("- ")
+
+            if "~" in var_name:
+                # We authorize colons next to "~", but we do as they were not present.
+                var_name = var_name.replace(":~", "~").replace("~:", "~")
+                prefix, suffix = var_name.replace(":~", "~").split("~")
+                if not prefix:
+                    # If nothing before "~", a default value is used
+                    prefix = "data:mission"
+                if not suffix:
+                    # If nothing after "~", the parameter name is used
+                    suffix = self.parameter_name
+
+                var_name = ":".join([prefix, self.part_identifier, suffix])
+
+            self._variable_name = var_name
+        else:
+            self._variable_name = None
+
+    def __str__(self):
+        return str(self.value)
diff --git a/src/fastoad/models/performances/mission/mission_definition/mission_builder/mission_builder.py b/src/fastoad/models/performances/mission/mission_definition/mission_builder/mission_builder.py
new file mode 100644
index 000000000..9d10550cb
--- /dev/null
+++ b/src/fastoad/models/performances/mission/mission_definition/mission_builder/mission_builder.py
@@ -0,0 +1,420 @@
+"""Mission generator."""
+#  This file is part of FAST-OAD : A framework for rapid Overall Aircraft Design
+#  Copyright (C) 2022 ONERA & ISAE-SUPAERO
+#  FAST is free software: you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation, either version 3 of the License, or
+#  (at your option) any later version.
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#  You should have received a copy of the GNU General Public License
+#  along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+from collections import ChainMap, OrderedDict
+from dataclasses import fields
+from typing import Dict, List, Mapping, Optional, Union
+
+import pandas as pd
+
+from fastoad.constants import EngineSetting
+from fastoad.model_base import FlightPoint
+from fastoad.model_base.propulsion import IPropulsion
+from fastoad.openmdao.variables import VariableList
+from .constants import NAME_TAG, SEGMENT_TYPE_TAG, TYPE_TAG
+from .input_definition import InputDefinition
+from .structure_builders import AbstractStructureBuilder, MissionStructureBuilder
+from ..exceptions import FastMissionFileMissingMissionNameError
+from ..schema import (
+    CLIMB_PARTS_TAG,
+    CRUISE_PART_TAG,
+    DESCENT_PARTS_TAG,
+    MISSION_DEFINITION_TAG,
+    MissionDefinition,
+    PARTS_TAG,
+    PHASE_TAG,
+    RESERVE_TAG,
+    ROUTE_TAG,
+    SEGMENT_TAG,
+)
+from ...base import FlightSequence
+from ...polar import Polar
+from ...routes import RangedRoute
+from ...segments.base import AbstractFlightSegment, SegmentDefinitions
+
+
+class MissionBuilder:
+    """
+    This class builds and computes a mission from a provided definition.
+    """
+
+    def __init__(
+        self,
+        mission_definition: Union[str, MissionDefinition],
+        *,
+        propulsion: IPropulsion = None,
+        reference_area: float = None,
+    ):
+        """
+        :param mission_definition: a file path or MissionDefinition instance
+        :param propulsion: if not provided, the property :attr:`propulsion` must be
+                           set before calling :meth:`build`
+        :param reference_area: if not provided, the property :attr:`reference_area` must be
+                               set before calling :meth:`build`
+        """
+        self._structure_builders: Dict[str, AbstractStructureBuilder] = {}
+        self.definition = mission_definition
+        self._base_kwargs = {"reference_area": reference_area, "propulsion": propulsion}
+
+    @property
+    def definition(self) -> MissionDefinition:
+        """
+        The mission definition instance.
+
+        If it is set as a file path, then the matching file will be read and interpreted.
+        """
+        return self._definition
+
+    @definition.setter
+    def definition(self, mission_definition: Union[str, MissionDefinition]):
+        if isinstance(mission_definition, str):
+            self._definition = MissionDefinition(mission_definition)
+        else:
+            self._definition = mission_definition
+
+        for mission_name in self._definition[MISSION_DEFINITION_TAG]:
+            self._structure_builders[mission_name] = MissionStructureBuilder(
+                self._definition, mission_name
+            )
+
+    @property
+    def propulsion(self) -> IPropulsion:
+        """Propulsion model for performance computation."""
+        return self._base_kwargs["propulsion"]
+
+    @propulsion.setter
+    def propulsion(self, propulsion: IPropulsion):
+        self._base_kwargs["propulsion"] = propulsion
+
+    @property
+    def reference_area(self) -> float:
+        """Reference area for aerodynamic polar."""
+        return self._base_kwargs["reference_area"]
+
+    @reference_area.setter
+    def reference_area(self, reference_area: float):
+        self._base_kwargs["reference_area"] = reference_area
+
+    def build(self, inputs: Optional[Mapping] = None, mission_name: str = None) -> FlightSequence:
+        """
+        Builds the flight sequence from definition file.
+
+        :param inputs: if provided, any input parameter that is a string which matches
+                       a key of `inputs` will be replaced by the corresponding value
+        :param mission_name: mission name (can be omitted if only one mission is defined)
+        :return:
+        """
+        for input_def in self._structure_builders[mission_name].get_input_definitions():
+            input_def.set_variable_value(inputs)
+        if mission_name is None:
+            mission_name = self.get_unique_mission_name()
+        mission = self._build_mission(self._structure_builders[mission_name].structure)
+        return mission
+
+    def get_route_ranges(
+        self, inputs: Optional[Mapping] = None, mission_name: str = None
+    ) -> List[float]:
+        """
+
+        :param inputs: if provided, any input parameter that is a string which matches
+                       a key of `inputs` will be replaced by the corresponding value
+        :param mission_name: mission name (can be omitted if only one mission is defined)
+        :return: list of flight ranges for each element of the flight sequence that is a route
+        """
+        routes = self.build(inputs, mission_name).flight_sequence
+        return [route.flight_distance for route in routes if isinstance(route, RangedRoute)]
+
+    def get_reserve(self, flight_points: pd.DataFrame, mission_name: str = None) -> float:
+        """
+        Computes the reserve fuel according to definition in mission input file.
+
+        :param flight_points: the dataframe returned by compute_from() method of the
+                              instance returned by :meth:`build`
+        :param mission_name: mission name (can be omitted if only one mission is defined)
+        :return: the reserve fuel mass in kg, or 0.0 if no reserve is defined.
+        """
+
+        if mission_name is None:
+            mission_name = self.get_unique_mission_name()
+
+        last_part_spec = self._get_mission_part_structures(mission_name)[-1]
+        if RESERVE_TAG in last_part_spec:
+            ref_name = last_part_spec[RESERVE_TAG]["ref"].value
+            multiplier = last_part_spec[RESERVE_TAG]["multiplier"].value
+
+            route_points = flight_points.loc[
+                flight_points.name.str.contains(f"{mission_name}:{ref_name}")
+            ]
+            consumed_mass = route_points.mass.iloc[0] - route_points.mass.iloc[-1]
+            return consumed_mass * multiplier
+
+        return 0.0
+
+    def get_input_variables(self, mission_name=None) -> VariableList:
+        """
+        Identify variables for a defined mission.
+
+        :param mission_name: mission name (can be omitted if only one mission is defined)
+        :return: a VariableList instance.
+        """
+        if mission_name is None:
+            mission_name = self.get_unique_mission_name()
+
+        input_definition = VariableList()
+        for input_def in self._structure_builders[mission_name].get_input_definitions():
+            if input_def.variable_name and input_def.variable_name not in input_definition.names():
+                input_definition.append(input_def.get_input_definition())
+
+        return input_definition
+
+    def get_unique_mission_name(self) -> str:
+        """
+        Provides mission name if only one mission is defined in mission file.
+
+        :return: the mission name, if only one mission is defined
+        :raise FastMissionFileMissingMissionNameError: if several missions are defined in mission
+                                                       file
+        """
+        if len(self._structure_builders) == 1:
+            return list(self._structure_builders.keys())[0]
+
+        raise FastMissionFileMissingMissionNameError(
+            "Mission name must be specified if several missions are defined in mission file."
+        )
+
+    def get_input_weight_variable_name(self, mission_name: str) -> Optional[str]:
+        """
+        Search the mission structure for a segment that has a target absolute mass defined and
+        returns the associated variable name.
+
+        :param mission_name:
+        :return: The variable name, or None if no target mass found.
+        """
+        return self._get_input_weight_variable_name_in_structure(
+            self._get_mission_part_structures(mission_name)
+        )
+
+    def get_mission_start_mass_input(self, mission_name: str) -> Optional[str]:
+        """
+
+        :param mission_name:
+        :return: Target mass variable of first segment, if any.
+        """
+        part = self._get_first_segment_structure(mission_name)
+        if "mass" in part["target"]:
+            return part["target"]["mass"].variable_name
+
+        return None
+
+    def _get_first_segment_structure(self, mission_name: str):
+        part = self._get_mission_part_structures(mission_name)[0]
+        while PARTS_TAG in part:
+            part = part[PARTS_TAG][0]
+        return part
+
+    def get_mission_part_names(self, mission_name: str) -> List[str]:
+        """
+
+        :param mission_name:
+        :return: list of names of parts (phase or route) for specified mission.
+        """
+        return [
+            part[NAME_TAG]
+            for part in self._get_mission_part_structures(mission_name)
+            if part.get(TYPE_TAG) in [ROUTE_TAG, PHASE_TAG]
+        ]
+
+    def _build_mission(self, mission_structure: OrderedDict) -> FlightSequence:
+        """
+        Builds mission instance from provided structure.
+
+        :param mission_structure: structure of the mission to build
+        :return: the mission instance
+        """
+        mission = FlightSequence()
+
+        part_kwargs = self._get_part_kwargs({}, mission_structure)
+
+        mission.name = mission_structure[NAME_TAG]
+        for part_spec in mission_structure[PARTS_TAG]:
+            if TYPE_TAG not in part_spec:
+                continue
+            if part_spec[TYPE_TAG] == SEGMENT_TAG:
+                part = self._build_segment(part_spec, part_kwargs)
+            if part_spec[TYPE_TAG] == ROUTE_TAG:
+                part = self._build_route(part_spec, part_kwargs)
+            elif part_spec[TYPE_TAG] == PHASE_TAG:
+                part = self._build_phase(part_spec, part_kwargs)
+            mission.flight_sequence.append(part)
+
+        return mission
+
+    def _build_route(self, route_structure: OrderedDict, kwargs: Mapping = None):
+        """
+        Builds route instance.
+
+        :param route_structure: structure of the route to build
+        :return: the route instance
+        """
+        climb_phases = []
+        descent_phases = []
+
+        part_kwargs = self._get_part_kwargs(
+            kwargs,
+            route_structure,
+            [
+                CLIMB_PARTS_TAG,
+                CRUISE_PART_TAG,
+                DESCENT_PARTS_TAG,
+                "range",
+                "distance_accuracy",
+            ],
+        )
+
+        for part_structure in route_structure[CLIMB_PARTS_TAG]:
+            phase = self._build_phase(part_structure, part_kwargs)
+            climb_phases.append(phase)
+
+        cruise_phase = self._build_segment(
+            route_structure[CRUISE_PART_TAG],
+            ChainMap({"target": FlightPoint(ground_distance=0.0)}, part_kwargs),
+        )
+
+        for part_structure in route_structure[DESCENT_PARTS_TAG]:
+            phase = self._build_phase(part_structure, part_kwargs)
+            descent_phases.append(phase)
+
+        if "range" in route_structure:
+            flight_range = route_structure["range"].value
+            route = RangedRoute(
+                climb_phases=climb_phases,
+                cruise_segment=cruise_phase,
+                descent_phases=descent_phases,
+                flight_distance=flight_range,
+            )
+        else:
+            route = FlightSequence()
+            route.flight_sequence.extend(climb_phases)
+
+        if "distance_accuracy" in route_structure:
+            route.distance_accuracy = route_structure["distance_accuracy"].value
+
+        route.name = route_structure[NAME_TAG]
+        return route
+
+    def _build_phase(self, phase_structure: Mapping, kwargs: Mapping = None):
+        """
+        Builds phase instance
+
+        :param phase_structure: structure of the phase to build
+        :return: the phase instance
+        """
+        phase = FlightSequence(name=phase_structure[NAME_TAG])
+        part_kwargs = self._get_part_kwargs(kwargs, phase_structure)
+
+        for part_structure in phase_structure[PARTS_TAG]:
+            if part_structure[TYPE_TAG] == PHASE_TAG:
+                flight_part = self._build_phase(part_structure, part_kwargs)
+            else:
+                flight_part = self._build_segment(part_structure, part_kwargs)
+            phase.flight_sequence.append(flight_part)
+
+        return phase
+
+    def _build_segment(self, segment_definition: Mapping, kwargs: Mapping) -> AbstractFlightSegment:
+        """
+        Builds a flight segment according to provided definition.
+
+        :param segment_definition: the segment definition from mission file
+        :param kwargs: a preset of keyword arguments for AbstractFlightSegment instantiation
+        :param tag: the expected tag for specifying the segment type
+        :return: the FlightSegment instance
+        """
+        segment_class = SegmentDefinitions.get_segment_class(segment_definition[SEGMENT_TYPE_TAG])
+        part_kwargs = kwargs.copy()
+        part_kwargs.update(segment_definition)
+        part_kwargs.update(self._base_kwargs)
+        for key, value in part_kwargs.items():
+            if key == "polar":
+                value = Polar(value["CL"].value, value["CD"].value)
+            elif key == "target":
+                if not isinstance(value, FlightPoint):
+                    target_parameters = {
+                        param.parameter_name: param.value for param in value.values()
+                    }
+                    relative_fields = [
+                        param.parameter_name for param in value.values() if param.is_relative
+                    ]
+                    value = FlightPoint(**target_parameters)
+                    value.set_as_relative(relative_fields)
+
+            part_kwargs[key] = value
+
+        self._replace_input_definitions_by_values(part_kwargs)
+
+        if "engine_setting" in part_kwargs:
+            part_kwargs["engine_setting"] = EngineSetting.convert(part_kwargs["engine_setting"])
+
+        input_field_names = [
+            class_field.name for class_field in fields(segment_class) if class_field.init
+        ]
+        part_kwargs = {key: value for key, value in part_kwargs.items() if key in input_field_names}
+        segment = segment_class(**part_kwargs)
+        return segment
+
+    @staticmethod
+    def _replace_input_definitions_by_values(part_kwargs):
+        for key, input_def in part_kwargs.items():
+            if isinstance(input_def, InputDefinition):
+                part_kwargs[key] = input_def.value
+
+    def _get_mission_part_structures(self, mission_name: str):
+        return self._structure_builders[mission_name].structure[PARTS_TAG]
+
+    def _get_part_kwargs(
+        self, kwargs: Mapping, phase_structure: Mapping, specific_exclude: List[str] = None
+    ):
+        part_kwargs = {}
+        if kwargs is not None:
+            part_kwargs.update(kwargs)
+        if specific_exclude is None:
+            specific_exclude = []
+        exclude = [NAME_TAG, PARTS_TAG, TYPE_TAG] + specific_exclude
+        part_kwargs.update(
+            {name: value for name, value in phase_structure.items() if name not in exclude}
+        )
+        self._replace_input_definitions_by_values(part_kwargs)
+        return part_kwargs
+
+    def _get_input_weight_variable_name_in_structure(
+        self, structure: Union[list, dict]
+    ) -> Optional[str]:
+        for tag in [PARTS_TAG, CLIMB_PARTS_TAG, DESCENT_PARTS_TAG]:
+            if tag in structure:
+                return self._get_input_weight_variable_name_in_structure(structure[tag])
+
+        if isinstance(structure, list):
+            for item in structure:
+                result = self._get_input_weight_variable_name_in_structure(item)
+                if result:
+                    return result
+
+        if "target" in structure and "mass" in structure["target"]:
+            target_mass = structure["target"]["mass"]
+            if not target_mass.is_relative:
+                name = target_mass.variable_name
+                if name:
+                    return name
+
+        return None
diff --git a/src/fastoad/models/performances/mission/mission_definition/mission_builder/structure_builders.py b/src/fastoad/models/performances/mission/mission_definition/mission_builder/structure_builders.py
new file mode 100644
index 000000000..fc0e506be
--- /dev/null
+++ b/src/fastoad/models/performances/mission/mission_definition/mission_builder/structure_builders.py
@@ -0,0 +1,371 @@
+"""
+Classes for managing internal structures of missions.
+
+The mission file provides a "human" definition of the mission.
+Structures are the translation of this human definition, that is ready to
+be transformed in a Python implementation.
+"""
+#  This file is part of FAST-OAD : A framework for rapid Overall Aircraft Design
+#  Copyright (C) 2022 ONERA & ISAE-SUPAERO
+#  FAST is free software: you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation, either version 3 of the License, or
+#  (at your option) any later version.
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#  You should have received a copy of the GNU General Public License
+#  along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+from abc import ABC, abstractmethod
+from collections import OrderedDict
+from copy import deepcopy
+from dataclasses import InitVar, dataclass, field, fields
+from itertools import chain
+from typing import List, Tuple
+
+import numpy as np
+
+from .constants import NAME_TAG, SEGMENT_TYPE_TAG, TYPE_TAG
+from .input_definition import InputDefinition
+from ..schema import (
+    CLIMB_PARTS_TAG,
+    CRUISE_PART_TAG,
+    DESCENT_PARTS_TAG,
+    MISSION_DEFINITION_TAG,
+    PARTS_TAG,
+    PHASE_DEFINITIONS_TAG,
+    PHASE_TAG,
+    ROUTE_DEFINITIONS_TAG,
+    ROUTE_TAG,
+    SEGMENT_TAG,
+)
+from ...segments.base import SegmentDefinitions
+
+
+@dataclass
+class AbstractStructureBuilder(ABC):
+    """
+    Base class for building structures of mission parts.
+
+    "Structures" are dicts that are derived from the mission definition file so that they can be
+    readily translated into the matching implementation.
+
+    Usage:
+
+    Subclasses must implement the build method that will create the specific part of the structure
+    dict (name and type fields are automated).
+
+    If the structure has to contain the result of another result, :meth:`insert_builder` should
+    be used to ensure a correct processing of the global structure, especially to get a correct
+    resolution of :attr:`input_definitions`.
+    """
+
+    definition: InitVar[dict]
+    name: str
+    parent_name: str = None
+
+    _structure: OrderedDict = field(default=None, init=False)
+
+    _input_definitions: List[InputDefinition] = field(default_factory=list, init=False)
+    _builders: List[Tuple["AbstractStructureBuilder", dict]] = field(
+        default_factory=list, init=False
+    )
+
+    #: Defined by subclass
+    type = None
+
+    def __init_subclass__(cls, *, structure_type=None):
+        cls.type = structure_type
+
+    def __post_init__(self, definition):
+        self._structure = self._build(definition)
+        self._structure[NAME_TAG] = self.qualified_name
+        if self.__class__.type:
+            self._structure[TYPE_TAG] = self.__class__.type
+        self._parse_inputs(self._structure, self._input_definitions)
+
+    @property
+    def structure(self) -> OrderedDict:
+        """A dictionary that is ready to be translated to the matching implementation."""
+        for builder, place_holder in self._builders:
+            place_holder.update(builder.structure)
+            self._input_definitions += builder.get_input_definitions()
+        self._builders = []  # Builders have been used and can be forgotten.
+        return self._structure
+
+    def get_input_definitions(self) -> List[InputDefinition]:
+        """List of InputDefinition instances in the structure."""
+        return self._input_definitions + list(
+            chain(*[builder.get_input_definitions() for builder, _ in self._builders])
+        )
+
+    def _insert_builder(self, builder: "AbstractStructureBuilder", place_holder: dict):
+        """
+        Method to be used when another StructureBuilder object is needed in :meth:`build`.
+
+        Not using this method will prevent a correct processing of :attr:`input_definitions`.
+
+        At location where the builder result should be used, an empty dict should be put, and this
+        empty dict should be provided as `place_holder` argument.
+
+        :param builder:
+        :param place_holder:
+        """
+        self._builders.append((builder, place_holder))
+
+    @abstractmethod
+    def _build(self, definition: dict) -> OrderedDict:
+        """
+        This method creates the needed structure dict.
+
+        Keys "name" and "type" are not needed, as they will be written later on in the process.
+
+        .. Important::
+
+            Please use :meth:`_insert_builder` when another StructureBuilder object is needed in
+            the currently built structure.
+
+        :param definition: the dict that will be converted.
+        :return: the structure dict
+        """
+
+    @property
+    def qualified_name(self):
+        """Name of the current structure, preceded by the parent names, separated by colons (:)."""
+        if not self.parent_name:
+            return self.name
+
+        name = self.parent_name
+        if self.name:
+            name += f":{self.name}"
+        return name
+
+    def _parse_inputs(
+        self, structure, input_definitions, parent=None, part_identifier="", segment_class=None
+    ):
+        """
+        Returns the `definition` structure where all inputs (string/numeric values, numeric lists,
+        dicts with a "value key"), have been converted to an InputDefinition instance.
+
+        Created InputDefinition instances are stored in self._input_definitions.
+
+        :param structure:
+        :param parent:
+        :param part_identifier:
+        :return: amended definition
+        """
+
+        if isinstance(structure, dict):
+            if "polar" in structure:
+                structure["polar"] = self._process_polar(structure["polar"])
+
+            if "value" in structure:
+                input_definition = InputDefinition.from_dict(
+                    parent, structure, part_identifier=part_identifier
+                )
+                input_definitions.append(input_definition)
+                return input_definition
+
+            part_identifier = structure.get(NAME_TAG, part_identifier)
+            segment_class = SegmentDefinitions.get_segment_class(structure.get(SEGMENT_TYPE_TAG))
+            for key, value in structure.items():
+                if key in [
+                    NAME_TAG,
+                    TYPE_TAG,
+                    SEGMENT_TYPE_TAG,
+                    PARTS_TAG,
+                    CLIMB_PARTS_TAG,
+                    DESCENT_PARTS_TAG,
+                    CRUISE_PART_TAG,
+                ]:
+                    continue
+                if (
+                    segment_class
+                    and isinstance(value, dict)
+                    and isinstance(value.get("value"), str)
+                    and self._is_shape_by_conn(key, segment_class)
+                ):
+                    value["shape_by_conn"] = True
+
+                structure[key] = self._parse_inputs(
+                    value,
+                    input_definitions,
+                    parent=key,
+                    part_identifier=part_identifier,
+                    segment_class=segment_class,
+                )
+            return structure
+
+        # Here structure is not a dict, hence a directly given value.
+        key, value = parent, structure
+        input_definition = InputDefinition(key, value, part_identifier=part_identifier)
+        if segment_class and isinstance(value, str):
+            input_definition.shape_by_conn = self._is_shape_by_conn(key, segment_class)
+        input_definitions.append(input_definition)
+        return input_definition
+
+    @staticmethod
+    def _is_shape_by_conn(key, segment_class) -> bool:
+        """
+        Here variables that are expected to be arrays or lists in the provided segment class are
+        attributed the "shape_by_conn=True" property.
+        """
+        segment_fields = [fld for fld in fields(segment_class) if fld.name == key]
+        return len(segment_fields) == 1 and issubclass(segment_fields[0].type, (list, np.ndarray))
+
+    @staticmethod
+    def _process_polar(polar_structure):
+        """
+        Polar data are handled specifically, as it a particular parameter of segments (
+        a Polar class).
+
+        If "foo:bar:baz" is provided as `polar_structure`, it is replaced by the dict
+        { "CL": {value:"foo:bar:baz:CL", "shape_by_conn": True},
+          "CD": {value:"foo:bar:baz:CD", "shape_by_conn": True}}
+
+        Also, even if `polar_structure` is a dict, it is ensured that it has the structure above.
+        """
+
+        if isinstance(polar_structure, str):
+            polar_structure = OrderedDict(
+                {
+                    "CL": {"value": polar_structure + ":CL", "shape_by_conn": True},
+                    "CD": {"value": polar_structure + ":CD", "shape_by_conn": True},
+                }
+            )
+        elif isinstance(polar_structure, dict):
+            for key in ["CL", "CD"]:
+                if isinstance(polar_structure[key], str):
+                    polar_structure[key] = {"value": polar_structure[key], "shape_by_conn": True}
+                elif isinstance(polar_structure[key], dict):
+                    polar_structure[key]["shape_by_conn"] = True
+
+        return polar_structure
+
+
+class DefaultStructureBuilder(AbstractStructureBuilder):
+    """
+    Builder for structures that do not need to process the given definition.
+
+    :param definition: the definition for the part only
+    """
+
+    def _build(self, definition: dict) -> OrderedDict:
+        return OrderedDict(deepcopy(definition))
+
+
+class SegmentStructureBuilder(AbstractStructureBuilder, structure_type=SEGMENT_TAG):
+    """
+    Structure builder for segment definition.
+
+    :param definition: the definition for the segment only
+    """
+
+    def _build(self, definition: dict) -> OrderedDict:
+        segment_structure = OrderedDict(deepcopy(definition))
+        del segment_structure[SEGMENT_TAG]
+        segment_structure[SEGMENT_TYPE_TAG] = definition[SEGMENT_TAG]
+
+        return segment_structure
+
+
+class PhaseStructureBuilder(AbstractStructureBuilder, structure_type=PHASE_TAG):
+    """
+    Structure builder for phase definition.
+
+    :param definition: the whole content of definition file
+    """
+
+    def _build(self, definition: dict) -> OrderedDict:
+        phase_definition = definition[PHASE_DEFINITIONS_TAG][self.name]
+        phase_structure = OrderedDict(deepcopy(phase_definition))
+
+        for i, part in enumerate(phase_definition[PARTS_TAG]):
+            if PHASE_TAG in part:
+                builder = PhaseStructureBuilder(definition, part[PHASE_TAG], self.qualified_name)
+            elif SEGMENT_TAG in part:
+                builder = SegmentStructureBuilder(part, "", self.qualified_name)
+            else:
+                raise RuntimeError(f"Unexpected structure in definition of phase {self.name}")
+
+            phase_structure[PARTS_TAG][i] = {}
+            self._insert_builder(builder, phase_structure[PARTS_TAG][i])
+
+        return phase_structure
+
+
+class RouteStructureBuilder(AbstractStructureBuilder, structure_type=ROUTE_TAG):
+    """
+    Structure builder for route definition.
+
+    :param definition: the whole content of definition file
+    """
+
+    def _build(self, definition: dict) -> OrderedDict:
+        route_definition = definition[ROUTE_DEFINITIONS_TAG][self.name]
+        route_structure = OrderedDict(deepcopy(route_definition))
+
+        route_structure[CLIMB_PARTS_TAG] = self._get_route_climb_or_descent_structure(
+            definition, route_definition[CLIMB_PARTS_TAG]
+        )
+
+        builder = SegmentStructureBuilder(
+            route_definition[CRUISE_PART_TAG], "cruise", self.qualified_name
+        )
+        route_structure[CRUISE_PART_TAG] = {}
+        self._insert_builder(builder, route_structure[CRUISE_PART_TAG])
+
+        route_structure[DESCENT_PARTS_TAG] = self._get_route_climb_or_descent_structure(
+            definition, route_definition[DESCENT_PARTS_TAG]
+        )
+
+        return route_structure
+
+    def _get_route_climb_or_descent_structure(self, global_definition, parts_definition):
+        parts = []
+        for part_definition in parts_definition:
+            phase_name = part_definition["phase"]
+            builder = PhaseStructureBuilder(global_definition, phase_name, self.qualified_name)
+            phase_structure = {}
+            self._insert_builder(builder, phase_structure)
+            parts.append(phase_structure)
+        return parts
+
+
+class MissionStructureBuilder(AbstractStructureBuilder, structure_type="mission"):
+    """
+    Structure builder for mission definition.
+
+    :param definition: the whole content of definition file
+    """
+
+    def _build(self, definition: dict) -> OrderedDict:
+        mission_definition = definition[MISSION_DEFINITION_TAG][self.name]
+        mission_structure = OrderedDict(deepcopy(mission_definition))
+
+        mission_parts = []
+        for part_definition in mission_definition[PARTS_TAG]:
+            if ROUTE_TAG in part_definition:
+                route_name = part_definition[ROUTE_TAG]
+                builder = RouteStructureBuilder(definition, route_name, self.qualified_name)
+            elif PHASE_TAG in part_definition:
+                phase_name = part_definition[PHASE_TAG]
+                builder = PhaseStructureBuilder(definition, phase_name, self.qualified_name)
+            elif SEGMENT_TAG in part_definition:
+                builder = SegmentStructureBuilder(part_definition, "", self.qualified_name)
+            else:
+                builder = DefaultStructureBuilder(part_definition, "", self.qualified_name)
+
+            part_structure = {}
+            self._insert_builder(builder, part_structure)
+            mission_parts.append(part_structure)
+
+        mission_structure[PARTS_TAG] = mission_parts
+
+        return mission_structure
+
+    @property
+    def qualified_name(self):
+        return self.name
diff --git a/src/fastoad/models/performances/mission/mission_definition/resources/mission_schema.json b/src/fastoad/models/performances/mission/mission_definition/resources/mission_schema.json
index 86446354d..1bb3eca78 100644
--- a/src/fastoad/models/performances/mission/mission_definition/resources/mission_schema.json
+++ b/src/fastoad/models/performances/mission/mission_definition/resources/mission_schema.json
@@ -94,6 +94,9 @@
               "properties": {
                 "value": {
                   "$ref": "#/definitions/inline_number_value"
+                },
+                "default": {
+                  "type": "number"
                 }
               },
               "required": [
@@ -122,6 +125,9 @@
                 },
                 "unit": {
                   "type": "string"
+                },
+                "default": {
+                  "type": "number"
                 }
               },
               "required": [
@@ -234,7 +240,14 @@
             "parts": {
               "type": "array",
               "items": {
-                "$ref": "#/definitions/segment"
+                "oneOf": [
+                  {
+                    "$ref": "#/definitions/mission_phase"
+                  },
+                  {
+                    "$ref": "#/definitions/segment"
+                  }
+                ]
               }
             }
           },
@@ -265,6 +278,9 @@
         "range": {
           "$ref": "#/definitions/parameter_value_with_unit"
         },
+        "distance_accuracy": {
+          "$ref": "#/definitions/parameter_value_with_unit"
+        },
         "climb_parts": {
           "description": "Flight phases before cruise",
           "$ref": "#/definitions/route_parts"
@@ -312,10 +328,7 @@
               "type": "string"
             },
             "multiplier": {
-              "type": [
-                "number",
-                "string"
-              ]
+              "$ref": "#/definitions/parameter_value_without_unit"
             }
           }
         }
@@ -369,8 +382,7 @@
     }
   },
   "required": [
-    "missions",
-    "routes"
+    "missions"
   ],
   "additionalProperties": false
 }
\ No newline at end of file
diff --git a/src/fastoad/models/performances/mission/mission_definition/schema.py b/src/fastoad/models/performances/mission/mission_definition/schema.py
index 987f629fb..09397803a 100644
--- a/src/fastoad/models/performances/mission/mission_definition/schema.py
+++ b/src/fastoad/models/performances/mission/mission_definition/schema.py
@@ -2,7 +2,7 @@
 Schema for mission definition files.
 """
 #  This file is part of FAST-OAD : A framework for rapid Overall Aircraft Design
-#  Copyright (C) 2021 ONERA & ISAE-SUPAERO
+#  Copyright (C) 2022 ONERA & ISAE-SUPAERO
 #  FAST is free software: you can redistribute it and/or modify
 #  it under the terms of the GNU General Public License as published by
 #  the Free Software Foundation, either version 3 of the License, or
@@ -43,7 +43,7 @@
 POLAR_TAG = "polar"
 
 
-class MissionDefinition(dict):
+class MissionDefinition(OrderedDict):
     def __init__(self, file_path: Union[str, PathLike] = None):
         """
         Class for reading a mission definition from a YAML file.
@@ -66,7 +66,7 @@ def load(self, file_path: Union[str, PathLike]):
         :param file_path: path of YAML file to read.
         """
         self.clear()
-        yaml = YAML()
+        yaml = YAML(typ="safe", pure=True)
 
         with open(file_path) as yaml_file:
             data = yaml.load(yaml_file)
@@ -83,12 +83,13 @@ def _validate(cls, content: dict):
         """
         Does a second pass validation of file content.
 
-        Also applies this feature:
-                - polar: foo:bar
-            is translated to:
-                - polar:
-                    CL: foo:bar:CL
-                    CD: foo:bar:CD
+        Also applies thess features:
+            * None values are set back to "~".
+            *       - polar: foo:bar
+                is translated to:
+                    - polar:
+                        CL: foo:bar:CL
+                        CD: foo:bar:CD
 
         Errors are raised if file content is incorrect.
 
@@ -96,18 +97,12 @@ def _validate(cls, content: dict):
         """
         phase_names = set(content[PHASE_DEFINITIONS_TAG].keys())
 
-        for phase_definition in content[PHASE_DEFINITIONS_TAG].values():
-            cls._process_polar_definition(phase_definition)
-            for segment_definition in phase_definition[PARTS_TAG]:
-                cls._process_polar_definition(segment_definition)
-
-        for route_definition in content[ROUTE_DEFINITIONS_TAG].values():
-            cls._process_polar_definition(route_definition[CRUISE_PART_TAG])
-            for part in list(route_definition[CLIMB_PARTS_TAG]) + list(
-                route_definition[DESCENT_PARTS_TAG]
-            ):
-                cls._process_polar_definition(part)
-                Ensure(part[PHASE_TAG]).is_in(phase_names)
+        if ROUTE_DEFINITIONS_TAG in content:
+            for route_definition in content[ROUTE_DEFINITIONS_TAG].values():
+                for part in list(route_definition[CLIMB_PARTS_TAG]) + list(
+                    route_definition[DESCENT_PARTS_TAG]
+                ):
+                    Ensure(part[PHASE_TAG]).is_in(phase_names)
 
         for mission_definition in content[MISSION_DEFINITION_TAG].values():
             reserve_count = 0
@@ -125,13 +120,19 @@ def _validate(cls, content: dict):
                 # reserve definition should be the last part
                 Ensure(part_type).equals(RESERVE_TAG)
 
-    @staticmethod
-    def _process_polar_definition(struct: dict):
+        cls._convert_none_values(content)
+
+    @classmethod
+    def _convert_none_values(cls, struct: Union[dict, list]):
         """
-        If "foo:bar:baz" is provided as value for the "polar" key in provided dictionary, it is
-        replaced by the dict {"CL":"foo:bar:baz:CL", "CD":"foo:bar:baz:CD"}
+        Recursively transforms any None value in struct to "~"
         """
-        if POLAR_TAG in struct:
-            polar_def = struct[POLAR_TAG]
-            if isinstance(polar_def, str) and ":" in polar_def:
-                struct[POLAR_TAG] = OrderedDict({"CL": polar_def + ":CL", "CD": polar_def + ":CD"})
+        if isinstance(struct, dict):
+            for key, value in struct.items():
+                if value is None:
+                    struct[key] = "~"
+                else:
+                    cls._convert_none_values(value)
+        elif isinstance(struct, list):
+            for item in struct:
+                cls._convert_none_values(item)
diff --git a/src/fastoad/models/performances/mission/openmdao/link_mtow.py b/src/fastoad/models/performances/mission/openmdao/link_mtow.py
index be6c0720c..32db0aa60 100644
--- a/src/fastoad/models/performances/mission/openmdao/link_mtow.py
+++ b/src/fastoad/models/performances/mission/openmdao/link_mtow.py
@@ -2,7 +2,7 @@
 OpenMDAO component for computation of sizing mission.
 """
 #  This file is part of FAST-OAD : A framework for rapid Overall Aircraft Design
-#  Copyright (C) 2021 ONERA & ISAE-SUPAERO
+#  Copyright (C) 2022 ONERA & ISAE-SUPAERO
 #  FAST is free software: you can redistribute it and/or modify
 #  it under the terms of the GNU General Public License as published by
 #  the Free Software Foundation, either version 3 of the License, or
@@ -36,7 +36,7 @@ def setup(self):
             [
                 "data:weight:aircraft:OWE",
                 "data:weight:aircraft:payload",
-                "data:weight:aircraft:sizing_onboard_fuel_at_takeoff",
+                "data:weight:aircraft:sizing_onboard_fuel_at_input_weight",
             ],
             units="kg",
         )
diff --git a/src/fastoad/models/performances/mission/openmdao/mission.py b/src/fastoad/models/performances/mission/openmdao/mission.py
index 30b2e2962..bd2901fef 100644
--- a/src/fastoad/models/performances/mission/openmdao/mission.py
+++ b/src/fastoad/models/performances/mission/openmdao/mission.py
@@ -1,5 +1,5 @@
 """
-OpenMDAO component for time-step computation of missions.
+FAST-OAD model for mission computation.
 """
 #  This file is part of FAST-OAD : A framework for rapid Overall Aircraft Design
 #  Copyright (C) 2022 ONERA & ISAE-SUPAERO
@@ -15,26 +15,17 @@
 #  along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 import logging
-import os.path as pth
-from collections import namedtuple
 from importlib.resources import path
-from os import makedirs
 
-import numpy as np
 import openmdao.api as om
 import pandas as pd
-from scipy.constants import foot
 
-from fastoad.model_base import FlightPoint
-from fastoad.model_base.propulsion import IOMPropulsionWrapper
 from fastoad.module_management.constants import ModelDomain
-from fastoad.module_management.service_registry import RegisterOpenMDAOSystem, RegisterPropulsion
+from fastoad.module_management.service_registry import RegisterOpenMDAOSystem
 from . import resources
+from .mission_component import MissionComponent, _get_variable_name_provider
 from .mission_wrapper import MissionWrapper
 from ..mission_definition.schema import MissionDefinition
-from ..polar import Polar
-from ..segments.cruise import BreguetCruiseSegment
-from ..segments.taxi import TaxiSegment
 
 _LOGGER = logging.getLogger(__name__)  # Logger for this module
 
@@ -91,24 +82,37 @@ def initialize(self):
             "adjust_fuel",
             default=True,
             types=bool,
-            desc="If True, block fuel will fit fuel consumption during mission.\n"
+            desc="If True, block fuel will fit fuel consumption during mission. In that case, a "
+            "solver (local or global) will be needed. (see `add_solver` option for more"
+            "information)\n"
             "If False, block fuel will be taken from input data.",
         )
+        self.options.declare(
+            "compute_input_weight",
+            default=False,
+            types=bool,
+            desc="If True, input weight will be deduced from block fuel.\n"
+            "If False, block fuel will be deduced from input weight.\n"
+            "Not used (actually forced to True) if adjust_fuel is True.",
+        )
         self.options.declare(
             "compute_TOW",
             default=False,
             types=bool,
-            desc="If True, TakeOff Weight will be computed from mission block fuel and ZFW.\n"
-            "If False, block fuel will be computed from TOW and ZFW.\n"
+            desc="(Deprecated. Replaced by compute_input_weight)\n"
+            "If True, TakeOff Weight will be computed from onboard fuel at takeoff and ZFW.\n"
+            "If False, block fuel will be computed from ramp weight and ZFW.\n"
             "Not used (actually forced to True) if adjust_fuel is True.",
         )
         self.options.declare(
             "add_solver",
             default=False,
             types=bool,
-            desc="Not used if compute_TOW is False.\n"
-            "Otherwise, setting this option to False will deactivate the local solver.\n"
-            "Useful if a global solver is used.",
+            desc="If True, a local solver is set for the mission computation.\n"
+            "It is useful if `adjust_fuel` is set to True, or to ensure consistency between "
+            "ramp weight and takeoff weight + taxi-out fuel.\n"
+            "If a global solver is used, using a local solver or not should be only a question"
+            "of CPU time consumption and is not expected to modify the results.",
         )
         self.options.declare(
             "is_sizing",
@@ -125,6 +129,8 @@ def initialize(self):
         )
 
     def setup(self):
+        self.options["compute_input_weight"] = self.options["compute_TOW"]
+
         if "::" in self.options["mission_file_path"]:
             # The configuration file parser will have added the working directory before
             # the file name. But as the user-provided string begins with "::", we just
@@ -133,43 +139,50 @@ def setup(self):
             file_name = self.options["mission_file_path"][i + 2 :] + ".yml"
             with path(resources, file_name) as mission_input_file:
                 self.options["mission_file_path"] = MissionDefinition(mission_input_file)
-        mission_wrapper = MissionWrapper(self.options["mission_file_path"])
+        self._mission_wrapper = MissionWrapper(self.options["mission_file_path"])
         if self.options["mission_name"] is None:
-            self.options["mission_name"] = mission_wrapper.get_unique_mission_name()
+            self.options["mission_name"] = self._mission_wrapper.get_unique_mission_name()
 
         mission_name = self.options["mission_name"]
+        self._name_provider = _get_variable_name_provider(mission_name)
 
         self.add_subsystem("ZFW_computation", self._get_zfw_component(mission_name), promotes=["*"])
 
         if self.options["adjust_fuel"]:
-            self.options["compute_TOW"] = True
+            self.options["compute_input_weight"] = True
             self.connect(
-                f"data:mission:{mission_name}:needed_onboard_fuel_at_takeoff",
-                f"data:mission:{mission_name}:onboard_fuel_at_takeoff",
+                self._name_provider.NEEDED_BLOCK_FUEL.value,
+                self._name_provider.BLOCK_FUEL.value,
             )
-            if self.options["add_solver"]:
-                self.nonlinear_solver = om.NonlinearBlockGS(maxiter=30, rtol=1.0e-4, iprint=0)
-                self.linear_solver = om.DirectSolver()
+        if self.options["add_solver"]:
+            self.nonlinear_solver = om.NonlinearBlockGS(maxiter=30, rtol=1.0e-4, iprint=0)
+            self.linear_solver = om.DirectSolver()
 
-        if self.options["compute_TOW"]:
+        if self.options["compute_input_weight"]:
             self.add_subsystem(
-                "TOW_computation", self._get_tow_component(mission_name), promotes=["*"]
+                "input_mass_computation",
+                self._get_input_weight_component(mission_name),
+                promotes=["*"],
             )
 
         mission_options = dict(self.options.items())
         del mission_options["adjust_fuel"]
+        del mission_options["compute_input_weight"]
         del mission_options["compute_TOW"]
         del mission_options["add_solver"]
         del mission_options["mission_file_path"]
-        mission_options["mission_wrapper"] = mission_wrapper
+        mission_options["mission_wrapper"] = self._mission_wrapper
         mission_options["mission_name"] = mission_name
-        self.add_subsystem(
-            "mission_computation", MissionComponent(**mission_options), promotes=["*"]
-        )
 
         self.add_subsystem(
-            "block_fuel_computation", self._get_block_fuel_component(mission_name), promotes=["*"]
+            "mission_computation", MissionComponent(**mission_options), promotes=["*"]
         )
+        if not self.options["compute_input_weight"]:
+            self.add_subsystem(
+                "block_fuel_computation",
+                self._get_block_fuel_component(mission_name),
+                promotes=["*"],
+            )
 
     @property
     def flight_points(self) -> pd.DataFrame:
@@ -185,367 +198,56 @@ def _get_zfw_component(self, mission_name: str) -> om.AddSubtractComp:
         if self.options["is_sizing"]:
             payload_var = "data:weight:aircraft:payload"
         else:
-            payload_var = f"data:mission:{mission_name}:payload"
+            payload_var = self._name_provider.PAYLOAD.value
 
         zfw_computation = om.AddSubtractComp()
         zfw_computation.add_equation(
-            f"data:mission:{mission_name}:ZFW",
+            self._name_provider.ZFW.value,
             ["data:weight:aircraft:OWE", payload_var],
             units="kg",
             desc=f'Zero Fuel Weight for mission "{mission_name}"',
         )
         return zfw_computation
 
-    @staticmethod
-    def _get_tow_component(mission_name: str) -> om.AddSubtractComp:
-        """
-
-        :param mission_name:
-        :return: component that computes TakeOff Weight from ZFW and loaded fuel at takeoff
-        """
-        tow_computation = om.AddSubtractComp()
-        tow_computation.add_equation(
-            f"data:mission:{mission_name}:TOW",
-            [
-                f"data:mission:{mission_name}:ZFW",
-                f"data:mission:{mission_name}:onboard_fuel_at_takeoff",
+    def _get_input_weight_component(self, mission_name: str):
+        input_weight_variable = self._mission_wrapper.get_input_weight_variable_name(mission_name)
+        if not input_weight_variable:
+            return None
+
+        computation = om.AddSubtractComp()
+        computation.add_equation(
+            output_name=input_weight_variable,
+            input_names=[
+                self._name_provider.ZFW.value,
+                self._name_provider.BLOCK_FUEL.value,
+                self._name_provider.CONSUMED_FUEL_BEFORE_INPUT_WEIGHT.value,
             ],
             units="kg",
-            desc=f'TakeOff Weight for mission "{mission_name}"',
+            scaling_factors=[1, 1, -1],
+            desc=f'Loaded fuel at beginning for mission "{mission_name}"',
         )
-        return tow_computation
 
-    @staticmethod
-    def _get_block_fuel_component(mission_name: str) -> om.AddSubtractComp:
+        return computation
+
+    def _get_block_fuel_component(self, mission_name: str) -> om.AddSubtractComp:
         """
 
         :param mission_name:
-        :return: component that computes initial block fuel from TOW and ZFW
+        :return: component that computes block fuel from ramp weight and ZFW
         """
+        input_weight_variable = self._mission_wrapper.get_input_weight_variable_name(mission_name)
+
         block_fuel_computation = om.AddSubtractComp()
         block_fuel_computation.add_equation(
-            f"data:mission:{mission_name}:block_fuel",
-            [
-                f"data:mission:{mission_name}:TOW",
-                f"data:mission:{mission_name}:taxi_out:fuel",
-                f"data:mission:{mission_name}:takeoff:fuel",
-                f"data:mission:{mission_name}:ZFW",
+            output_name=self._name_provider.BLOCK_FUEL.value,
+            input_names=[
+                input_weight_variable,
+                self._name_provider.CONSUMED_FUEL_BEFORE_INPUT_WEIGHT.value,
+                self._name_provider.ZFW.value,
             ],
             units="kg",
-            scaling_factors=[1, 1, 1, -1],
-            desc=f'Loaded fuel before taxi-out for mission "{mission_name}"',
-        )
-        return block_fuel_computation
-
-
-_MissionVariables = namedtuple(
-    "_MissionVariables",
-    [
-        "TOW",
-        "NEEDED_BLOCK_FUEL",
-        "NEEDED_FUEL_AT_TAKEOFF",
-        "TAXI_OUT_DURATION",
-        "TAXI_OUT_THRUST_RATE",
-        "TAXI_OUT_DISTANCE",
-        "TAXI_OUT_FUEL",
-        "TAKEOFF_FUEL",
-        "TAKEOFF_ALTITUDE",
-        "TAKEOFF_V2",
-    ],
-)
-
-
-class MissionComponent(om.ExplicitComponent):
-    def __init__(self, **kwargs):
-        """
-        Computes a mission as specified in mission input file
-
-        Options:
-          - propulsion_id: (mandatory) the identifier of the propulsion wrapper.
-          - out_file: if provided, a csv file will be written at provided path with all computed
-                      flight points.
-          - mission_wrapper: the MissionWrapper instance that defines the mission.
-          - use_initializer_iteration: During first solver loop, a complete mission computation can
-                                       fail or consume useless CPU-time. When activated, this option
-                                       ensures the first iteration is done using a simple, dummy,
-                                       formula instead of the specified mission.
-                                       Set this option to False if you do expect this model to be
-                                       computed only once.
-          - is_sizing: if True, TOW will be considered equal to MTOW and mission payload will be
-                       considered equal to design payload.
-          - reference_area_variable: Defines the name of the variable for providing aircraft
-                                     reference surface area.
-        """
-        super().__init__(**kwargs)
-        self.flight_points = None
-        self._engine_wrapper = None
-        self._mission_wrapper: MissionWrapper = None
-        self._mission_vars: _MissionVariables = None
-
-    def initialize(self):
-        self.options.declare("propulsion_id", default="", types=str)
-        self.options.declare("out_file", default="", types=str)
-        self.options.declare("use_initializer_iteration", default=True, types=bool)
-        self.options.declare("mission_wrapper", types=MissionWrapper)
-        self.options.declare("mission_name", types=str)
-        self.options.declare("is_sizing", default=False, types=bool)
-        self.options.declare(
-            "reference_area_variable", default="data:geometry:wing:area", types=str
-        )
-
-    def setup(self):
-        self._engine_wrapper = self._get_engine_wrapper()
-        self._engine_wrapper.setup(self)
-        self._mission_wrapper = self.options["mission_wrapper"]
-        self._mission_wrapper.setup(self, self.options["mission_name"])
-
-        mission_name = self.options["mission_name"]
-
-        self._mission_vars = _MissionVariables(
-            TOW=f"data:mission:{mission_name}:TOW",
-            NEEDED_BLOCK_FUEL=f"data:mission:{mission_name}:needed_block_fuel",
-            NEEDED_FUEL_AT_TAKEOFF=f"data:mission:{mission_name}:needed_onboard_fuel_at_takeoff",
-            TAXI_OUT_DURATION=f"data:mission:{mission_name}:taxi_out:duration",
-            TAXI_OUT_THRUST_RATE=f"data:mission:{mission_name}:taxi_out:thrust_rate",
-            TAXI_OUT_DISTANCE=f"data:mission:{mission_name}:taxi_out:distance",
-            TAXI_OUT_FUEL=f"data:mission:{mission_name}:taxi_out:fuel",
-            TAKEOFF_FUEL=f"data:mission:{mission_name}:takeoff:fuel",
-            TAKEOFF_ALTITUDE=f"data:mission:{mission_name}:takeoff:altitude",
-            TAKEOFF_V2=f"data:mission:{mission_name}:takeoff:V2",
-        )
-
-        self.add_input(self.options["reference_area_variable"], np.nan, units="m**2")
-        self.add_input(
-            self._mission_vars.TOW,
-            np.nan,
-            units="kg",
-            desc=f'TakeOff Weight for mission "{mission_name}"',
-        )
-        self.add_input(
-            self._mission_vars.TAXI_OUT_DURATION,
-            np.nan,
-            units="s",
-            desc=f'duration of taxi-out in mission "{mission_name}"',
-        )
-        self.add_input(
-            self._mission_vars.TAXI_OUT_THRUST_RATE,
-            np.nan,
-            units=None,
-            desc=f'thrust rate during taxi-out in mission "{mission_name}"',
-        )
-        self.add_input(
-            self._mission_vars.TAKEOFF_ALTITUDE,
-            np.nan,
-            units="m",
-            desc=f'altitude of airport for mission "{mission_name}"',
-        )
-        self.add_input(
-            self._mission_vars.TAKEOFF_FUEL,
-            np.nan,
-            units="kg",
-            desc=f'burned fuel during takeoff phase of mission "{mission_name}"',
-        )
-        self.add_input(
-            self._mission_vars.TAKEOFF_V2,
-            np.nan,
-            units="m/s",
-            desc=f'takeoff safety speed for mission "{mission_name}"',
-        )
-        self.add_output(
-            self._mission_vars.TAXI_OUT_DISTANCE,
-            units="m",
-            desc=f'distance during taxi-out of mission "{mission_name}"',
-        )
-        self.add_output(
-            self._mission_vars.TAXI_OUT_FUEL,
-            units="kg",
-            desc=f'burned fuel during taxi-out of mission "{mission_name}"',
-        )
-        self.add_output(
-            self._mission_vars.NEEDED_BLOCK_FUEL,
-            units="kg",
-            desc=f'Needed fuel to complete mission "{mission_name}", including reserve fuel',
+            scaling_factors=[1, 1, -1],
+            desc=f'Loaded fuel at beginning for mission "{mission_name}"',
         )
-        self.add_output(
-            self._mission_vars.NEEDED_FUEL_AT_TAKEOFF,
-            units="kg",
-            desc=f'fuel quantity at instant of takeoff of mission "{mission_name}"',
-        )
-
-        if self.options["is_sizing"]:
-            self.add_output("data:weight:aircraft:sizing_block_fuel", units="kg")
-            self.add_output("data:weight:aircraft:sizing_onboard_fuel_at_takeoff", units="kg")
-
-    def setup_partials(self):
-        self.declare_partials(["*"], ["*"], method="fd")
-
-    def compute(self, inputs, outputs, discrete_inputs=None, discrete_outputs=None):
-        iter_count = self.iter_count_without_approx
-        message_prefix = f"Mission computation - iteration {iter_count:d} : "
-        if iter_count == 0 and self.options["use_initializer_iteration"]:
-            _LOGGER.info(message_prefix + "Using initializer computation. OTHER ITERATIONS NEEDED.")
-            self._compute_breguet(inputs, outputs)
-        else:
-            _LOGGER.info(message_prefix + "Using mission definition.")
-            self._compute_mission(inputs, outputs)
-
-    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 = self._engine_wrapper.get_model(inputs)
-
-        high_speed_polar = self._get_initial_polar(inputs)
-        distance = np.sum(
-            self._mission_wrapper.get_route_ranges(inputs, self.options["mission_name"])
-        ).item()
-
-        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], 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] = start_point.mass - end_point.mass
-
-    @staticmethod
-    def _get_initial_polar(inputs) -> Polar:
-        """
-        At computation start, polar may be irrelevant and give a very low lift/drag ratio.
-
-        In that case, this method returns a fake polar that has 10.0 as max lift drag ratio.
-        Otherwise, the actual cruise polar is returned.
-        """
-        high_speed_polar = Polar(
-            inputs["data:aerodynamics:aircraft:cruise:CL"],
-            inputs["data:aerodynamics:aircraft:cruise:CD"],
-        )
-        use_minimum_l_d_ratio = False
-        try:
-            if (
-                high_speed_polar.optimal_cl / high_speed_polar.cd(high_speed_polar.optimal_cl)
-                < 10.0
-            ):
-                use_minimum_l_d_ratio = True
-        except ZeroDivisionError:
-            use_minimum_l_d_ratio = True
-        if use_minimum_l_d_ratio:
-            # We replace by a polar that has at least 10.0 as max L/D ratio
-            high_speed_polar = Polar(np.array([0.0, 0.5, 1.0]), np.array([0.1, 0.05, 1.0]))
-
-        return high_speed_polar
-
-    def _compute_mission(self, inputs, outputs):
-        """
-        Computes mission using time-step integration.
 
-        :param inputs: OpenMDAO input vector
-        :param outputs: OpenMDAO output vector
-        """
-        propulsion_model = self._engine_wrapper.get_model(inputs)
-        reference_area = inputs[self.options["reference_area_variable"]]
-
-        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 value.item()
-            return value
-
-        self.flight_points = self.flight_points.applymap(as_scalar)
-        rename_dict = {
-            field_name: f"{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"]:
-            makedirs(pth.dirname(self.options["out_file"]), exist_ok=True)
-            self.flight_points.to_csv(self.options["out_file"])
-
-    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],
-            true_airspeed=0.0,
-            # start mass is irrelevant here as long it does not get negative during computation.
-            mass=inputs[self._mission_vars.TOW],
-        )
-        taxi_segment = TaxiSegment(
-            target=FlightPoint(time=inputs[self._mission_vars.TAXI_OUT_DURATION]),
-            thrust_rate=inputs[self._mission_vars.TAXI_OUT_THRUST_RATE],
-            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_DISTANCE] = (
-            end_of_taxi_out.ground_distance - start_of_taxi_out.ground_distance
-        )
-        outputs[self._mission_vars.TAXI_OUT_FUEL] = start_of_taxi_out.mass - end_of_taxi_out.mass
-
-    def _get_engine_wrapper(self) -> IOMPropulsionWrapper:
-        """
-        Overloading this method allows to define the engine without relying on the propulsion
-        option.
-
-        (useful for tests)
-
-        :return: the engine wrapper instance
-        """
-        return RegisterPropulsion.get_provider(self.options["propulsion_id"])
+        return block_fuel_computation
diff --git a/src/fastoad/models/performances/mission/openmdao/mission_component.py b/src/fastoad/models/performances/mission/openmdao/mission_component.py
new file mode 100644
index 000000000..85e81d5bc
--- /dev/null
+++ b/src/fastoad/models/performances/mission/openmdao/mission_component.py
@@ -0,0 +1,275 @@
+"""
+OpenMDAO component for time-step computation of missions.
+"""
+#  This file is part of FAST-OAD : A framework for rapid Overall Aircraft Design
+#  Copyright (C) 2022 ONERA & ISAE-SUPAERO
+#  FAST is free software: you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation, either version 3 of the License, or
+#  (at your option) any later version.
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#  You should have received a copy of the GNU General Public License
+#  along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+import logging
+from enum import Enum
+from os import makedirs, path as pth
+
+import numpy as np
+from openmdao import api as om
+
+from fastoad.model_base import FlightPoint
+from fastoad.model_base.propulsion import IOMPropulsionWrapper
+from fastoad.models.performances.mission.openmdao.mission_wrapper import MissionWrapper
+from fastoad.models.performances.mission.polar import Polar
+from fastoad.models.performances.mission.segments.cruise import BreguetCruiseSegment
+from fastoad.module_management.service_registry import RegisterPropulsion
+
+_LOGGER = logging.getLogger(__name__)  # Logger for this module
+
+
+class MissionComponent(om.ExplicitComponent):
+    """
+    Computes a mission as specified in mission input file
+
+    Options:
+      - propulsion_id: (mandatory) the identifier of the propulsion wrapper.
+      - out_file: if provided, a csv file will be written at provided path with all computed
+                  flight points.
+      - mission_wrapper: the MissionWrapper instance that defines the mission.
+      - use_initializer_iteration: During first solver loop, a complete mission computation can
+                                   fail or consume useless CPU-time. When activated, this option
+                                   ensures the first iteration is done using a simple, dummy,
+                                   formula instead of the specified mission.
+                                   Set this option to False if you do expect this model to be
+                                   computed only once.
+      - is_sizing: if True, TOW will be considered equal to MTOW and mission payload will be
+                   considered equal to design payload.
+      - reference_area_variable: Defines the name of the variable for providing aircraft
+                                 reference surface area.
+    """
+
+    def __init__(self, **kwargs):
+        super().__init__(**kwargs)
+        self.flight_points = None
+        self._engine_wrapper = None
+        self._mission_wrapper: MissionWrapper = None
+        self._name_provider = None
+        self._input_weight_variable_name = ""
+
+    def initialize(self):
+        self.options.declare("propulsion_id", default="", types=str)
+        self.options.declare("out_file", default="", types=str)
+        self.options.declare("use_initializer_iteration", default=True, types=bool)
+        self.options.declare("mission_wrapper", types=MissionWrapper)
+        self.options.declare("mission_name", types=str)
+        self.options.declare("is_sizing", default=False, types=bool)
+        self.options.declare(
+            "reference_area_variable", default="data:geometry:wing:area", types=str
+        )
+
+    def setup(self):
+        self._engine_wrapper = self._get_engine_wrapper()
+        self._engine_wrapper.setup(self)
+        self._mission_wrapper = self.options["mission_wrapper"]
+        self._mission_wrapper.setup(self, self.options["mission_name"])
+
+        mission_name = self.options["mission_name"]
+
+        self._name_provider = _get_variable_name_provider(mission_name)
+        self._input_weight_variable_name = self._mission_wrapper.get_input_weight_variable_name(
+            self.options["mission_name"]
+        )
+
+        try:
+            self.add_input(self.options["reference_area_variable"], np.nan, units="m**2")
+        except ValueError:
+            pass
+
+        # Global mission outputs
+        self.add_output(
+            self._name_provider.NEEDED_BLOCK_FUEL.value,
+            units="kg",
+            desc=f'Needed fuel to complete mission "{mission_name}", including reserve fuel',
+        )
+        self.add_output(
+            self._name_provider.CONSUMED_FUEL_BEFORE_INPUT_WEIGHT.value,
+            units="kg",
+            desc=f'consumed fuel quantity before target mass defined for "{mission_name}",'
+            f" if any (e.g. TakeOff Weight)",
+        )
+        if self.options["is_sizing"]:
+            self.add_output("data:weight:aircraft:sizing_block_fuel", units="kg")
+            self.add_output("data:weight:aircraft:sizing_onboard_fuel_at_input_weight", units="kg")
+
+    def setup_partials(self):
+        self.declare_partials(["*"], ["*"], method="fd")
+
+    def compute(self, inputs, outputs, discrete_inputs=None, discrete_outputs=None):
+        iter_count = self.iter_count_without_approx
+        message_prefix = f"Mission computation - iteration {iter_count:d} : "
+        if iter_count == 0 and self.options["use_initializer_iteration"]:
+            _LOGGER.info(
+                "%sUsing initializer computation. OTHER ITERATIONS NEEDED.", message_prefix
+            )
+            self._compute_breguet(inputs, outputs)
+        else:
+            _LOGGER.info("%sUsing mission definition.", message_prefix)
+            self._compute_mission(inputs, outputs)
+
+    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 = self._engine_wrapper.get_model(inputs)
+
+        high_speed_polar = self._get_initial_polar(inputs)
+        distance = np.sum(
+            self._mission_wrapper.get_route_ranges(inputs, self.options["mission_name"])
+        ).item()
+
+        altitude = 100.0
+        cruise_mach = 0.1
+
+        breguet = BreguetCruiseSegment(
+            target=FlightPoint(ground_distance=distance),
+            propulsion=propulsion_model,
+            polar=high_speed_polar,
+            use_max_lift_drag_ratio=True,
+        )
+        start_point = FlightPoint(
+            mass=inputs[self._input_weight_variable_name],
+            altitude=altitude,
+            mach=cruise_mach,
+        )
+        flight_points = breguet.compute_from(start_point)
+        end_point = FlightPoint.create(flight_points.iloc[-1])
+        outputs[self._name_provider.NEEDED_BLOCK_FUEL.value] = start_point.mass - end_point.mass
+
+    @staticmethod
+    def _get_initial_polar(inputs) -> Polar:
+        """
+        At computation start, polar may be irrelevant and give a very low lift/drag ratio.
+
+        In that case, this method returns a fake polar that has 10.0 as max lift drag ratio.
+        Otherwise, the actual cruise polar is returned.
+        """
+        high_speed_polar = Polar(
+            inputs["data:aerodynamics:aircraft:cruise:CL"],
+            inputs["data:aerodynamics:aircraft:cruise:CD"],
+        )
+        use_minimum_l_d_ratio = False
+        try:
+            if (
+                high_speed_polar.optimal_cl / high_speed_polar.cd(high_speed_polar.optimal_cl)
+                < 10.0
+            ):
+                use_minimum_l_d_ratio = True
+        except ZeroDivisionError:
+            use_minimum_l_d_ratio = True
+        if use_minimum_l_d_ratio:
+            # We replace by a polar that has at least 10.0 as max L/D ratio
+            high_speed_polar = Polar(np.array([0.0, 0.5, 1.0]), np.array([0.1, 0.05, 1.0]))
+
+        return high_speed_polar
+
+    def _compute_mission(self, inputs, outputs):
+        """
+        Computes mission using time-step integration.
+
+        :param inputs: OpenMDAO input vector
+        :param outputs: OpenMDAO output vector
+        """
+        propulsion_model = self._engine_wrapper.get_model(inputs)
+        reference_area = inputs[self.options["reference_area_variable"]]
+
+        self._mission_wrapper.propulsion = propulsion_model
+        self._mission_wrapper.reference_area = reference_area
+
+        # This is the default start point, that can be overridden by using the "start"
+        # segment in the mission definition.
+        start_flight_point = FlightPoint(
+            altitude=0.0, mass=inputs[self._input_weight_variable_name], true_airspeed=0.0
+        )
+
+        self.flight_points = self._mission_wrapper.compute(start_flight_point, inputs, outputs)
+
+        # Final ================================================================
+        start_of_mission = FlightPoint.create(self.flight_points.iloc[0])
+        end_of_mission = FlightPoint.create(self.flight_points.iloc[-1])
+        reserve = self._mission_wrapper.get_reserve(
+            self.flight_points, self.options["mission_name"]
+        )
+        reserve_name = self._mission_wrapper.get_reserve_variable_name()
+        if reserve_name in outputs:
+            outputs[reserve_name] = reserve
+        outputs[self._name_provider.NEEDED_BLOCK_FUEL.value] = (
+            start_of_mission.mass - end_of_mission.mass + reserve
+        )
+
+        outputs[
+            self._name_provider.CONSUMED_FUEL_BEFORE_INPUT_WEIGHT.value
+        ] = self._mission_wrapper.consumed_fuel_before_input_weight
+
+        if self.options["is_sizing"]:
+            outputs["data:weight:aircraft:sizing_block_fuel"] = outputs[
+                self._name_provider.NEEDED_BLOCK_FUEL.value
+            ]
+            outputs["data:weight:aircraft:sizing_onboard_fuel_at_input_weight"] = (
+                outputs[self._name_provider.NEEDED_BLOCK_FUEL.value]
+                - self._mission_wrapper.consumed_fuel_before_input_weight
+            )
+
+        def as_scalar(value):
+            if isinstance(value, np.ndarray):
+                return value.item()
+            return value
+
+        self.flight_points = self.flight_points.applymap(as_scalar)
+        rename_dict = {
+            field_name: f"{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"]:
+            makedirs(pth.dirname(self.options["out_file"]), exist_ok=True)
+            self.flight_points.to_csv(self.options["out_file"])
+
+    def _get_engine_wrapper(self) -> IOMPropulsionWrapper:
+        """
+        Overloading this method allows to define the engine without relying on the propulsion
+        option.
+
+        (useful for tests)
+
+        :return: the engine wrapper instance
+        """
+        return RegisterPropulsion.get_provider(self.options["propulsion_id"])
+
+
+def _get_variable_name_provider(mission_name):
+    """Factory for enum class that provide mission variable names."""
+
+    def get_variable_name(suffix):
+        return f"data:mission:{mission_name}:{suffix}"
+
+    class VariableNames(Enum):
+        """Enum with mission-related variable names."""
+
+        ZFW = get_variable_name("ZFW")
+        PAYLOAD = get_variable_name("payload")
+        BLOCK_FUEL = get_variable_name("block_fuel")
+        NEEDED_BLOCK_FUEL = get_variable_name("needed_block_fuel")
+        CONSUMED_FUEL_BEFORE_INPUT_WEIGHT = get_variable_name("consumed_fuel_before_input_weight")
+
+    return VariableNames
diff --git a/src/fastoad/models/performances/mission/openmdao/mission_wrapper.py b/src/fastoad/models/performances/mission/openmdao/mission_wrapper.py
index a6d231214..fad1c259b 100644
--- a/src/fastoad/models/performances/mission/openmdao/mission_wrapper.py
+++ b/src/fastoad/models/performances/mission/openmdao/mission_wrapper.py
@@ -34,15 +34,6 @@
     ROUTE_TAG,
 )
 
-BASE_UNITS = {
-    "altitude": "m",
-    "true_airspeed": "m/s",
-    "equivalent_airspeed": "m/s",
-    "range": "m",
-    "time": "s",
-    "ground_distance": "m",
-}
-
 
 class MissionWrapper(MissionBuilder):
     """
@@ -54,6 +45,7 @@ class MissionWrapper(MissionBuilder):
     def __init__(self, *args, **kwargs):
         super().__init__(*args, **kwargs)
         self.mission_name = None
+        self.consumed_fuel_before_input_weight = 0.0
 
     def setup(self, component: om.ExplicitComponent, mission_name: str = None):
         """
@@ -71,19 +63,18 @@ def setup(self, component: om.ExplicitComponent, mission_name: str = None):
         input_definition = self.get_input_variables(mission_name)
         output_definition = self._identify_outputs()
         output_definition = {
-            name: value for name, value in output_definition.items() if name not in input_definition
+            name: value
+            for name, value in output_definition.items()
+            if name not in input_definition.names()
         }
-        for name, (units, desc) in input_definition.items():
-            if name.endswith(":CD") or name.endswith(":CL"):
-                component.add_input(name, np.nan, shape_by_conn=True, desc=desc)
-            else:
-                component.add_input(name, np.nan, units=units, desc=desc)
+        for variable in input_definition:
+            component.add_input(**variable.get_openmdao_kwargs())
 
         for name, (units, desc) in output_definition.items():
-            component.add_output(name, units=units, desc=desc)
+            component.add_output(name, 0.0, units=units, desc=desc)
 
     def compute(
-        self, inputs: Vector, outputs: Vector, start_flight_point: FlightPoint
+        self, start_flight_point: FlightPoint, inputs: Vector, outputs: Vector
     ) -> pd.DataFrame:
         """
         To be used during compute() of an OpenMDAO component.
@@ -92,10 +83,10 @@ def compute(
         filled with duration, burned fuel and covered ground distance for each
         part of the flight.
 
+        :param start_flight_point: starting point of mission
         :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
+        :return: a pandas DataFrame where column names match fields of
                  :class:`~fastoad.model_base.flight_point.FlightPoint`
         """
         mission = self.build(inputs, self.mission_name)
@@ -130,7 +121,7 @@ def _compute_vars(name_root, start: FlightPoint, end: FlightPoint):
             )
 
         del flight_points["name2"]
-
+        self.consumed_fuel_before_input_weight = mission.consumed_mass_before_input_weight
         return flight_points
 
     def get_reserve_variable_name(self) -> str:
@@ -138,7 +129,7 @@ def get_reserve_variable_name(self) -> str:
         :return: the name of OpenMDAO variable for fuel reserve. This name is among the declared
                  outputs in :meth:`setup`.
         """
-        return "data:mission:%s:reserve:fuel" % self.mission_name
+        return f"{self._variable_prefix}:reserve:fuel"
 
     def _identify_outputs(self) -> Dict[str, Tuple[str, str]]:
         """
@@ -169,7 +160,7 @@ def _identify_outputs(self) -> Dict[str, Tuple[str, str]]:
             elif RESERVE_TAG in part:
                 output_definition[self.get_reserve_variable_name()] = (
                     "kg",
-                    'reserve fuel for mission "%s"' % self.mission_name,
+                    f'reserve fuel for mission "{self.mission_name}"',
                 )
 
         return output_definition
@@ -190,23 +181,26 @@ def _add_vars(mission_name, route_name=None, phase_name=None) -> dict:
             name for name in ["data:mission", mission_name, route_name, phase_name] if name
         )
         if route_name and phase_name:
-            flight_part_desc = 'phase "%s" of route "%s" in mission "%s"' % (
-                phase_name,
-                route_name,
-                mission_name,
+            flight_part_desc = (
+                f'phase "{phase_name}" of route "{route_name}" in mission "{mission_name}"'
             )
+
         elif route_name:
-            flight_part_desc = 'route "%s" in mission "%s"' % (route_name, mission_name)
+            flight_part_desc = f'route "{route_name}" in mission "{mission_name}"'
         elif phase_name:
-            flight_part_desc = 'phase "%s" in mission "%s"' % (phase_name, mission_name)
+            flight_part_desc = f'phase "{phase_name}" in mission "{mission_name}"'
         else:
-            flight_part_desc = 'mission "%s"' % (mission_name,)
+            flight_part_desc = f'mission "{mission_name}"'
 
-        output_definition[name_root + ":duration"] = ("s", "duration of %s" % flight_part_desc)
-        output_definition[name_root + ":fuel"] = ("kg", "burned fuel during %s" % flight_part_desc)
+        output_definition[name_root + ":duration"] = ("s", f"duration of {flight_part_desc}")
+        output_definition[name_root + ":fuel"] = ("kg", f"burned fuel during {flight_part_desc}")
         output_definition[name_root + ":distance"] = (
             "m",
-            "covered ground distance during %s" % flight_part_desc,
+            f"covered ground distance during {flight_part_desc}",
         )
 
         return output_definition
+
+    @property
+    def _variable_prefix(self):
+        return f"data:mission:{self.mission_name}"
diff --git a/src/fastoad/models/performances/mission/openmdao/resources/sizing_breguet.yml b/src/fastoad/models/performances/mission/openmdao/resources/sizing_breguet.yml
index 02a5ead0d..6ef928b29 100644
--- a/src/fastoad/models/performances/mission/openmdao/resources/sizing_breguet.yml
+++ b/src/fastoad/models/performances/mission/openmdao/resources/sizing_breguet.yml
@@ -1,31 +1,72 @@
 phases:
+  start:
+    parts:
+      - segment: start
+        target:
+          altitude:
+            value: ~
+            unit: ft
+            default: 0.0
+          true_airspeed:
+            value: ~
+            unit: m/s
+            default: 0.0
+  taxi_out:
+    parts:
+      - segment: taxi
+        thrust_rate: ~
+        true_airspeed:
+          value: ~
+          default: 0.
+        target:
+          time: ~duration
+  takeoff:
+    parts:
+      - segment: transition
+        target:
+          delta_altitude:
+            value: ~safety_altitude
+            unit: ft
+            default: 35
+          delta_mass: -~fuel
+          time: ~duration
+          true_airspeed: ~V2
+      - segment: mass_input
+        target:
+          mass: data:mission:sizing:TOW
   climb:
     parts:
       - segment: transition
         target:
           altitude: data:mission:sizing:main_route:cruise:altitude
           mach: data:TLAR:cruise_mach
-          ground_distance:
+          delta_ground_distance:
             value: 250
             unit: km
-        mass_ratio: settings:mission:sizing:breguet:climb:mass_ratio
+        mass_ratio:
+          value: settings:mission:sizing:breguet:climb:mass_ratio
+          default: 0.97
   descent:
     parts:
       - segment: transition
         target:
           altitude: 0.
           mach: 0.
-          ground_distance:
+          delta_ground_distance:
             value: 250
             unit: km
-        mass_ratio: settings:mission:sizing:breguet:descent:mass_ratio
+        mass_ratio:
+          value: settings:mission:sizing:breguet:descent:mass_ratio
+          default: 0.98
   global_reserve:
     parts:
       - segment: transition
         target:
           altitude: 0.
           mach: 0.
-        reserve_mass_ratio: settings:mission:sizing:breguet:reserve:mass_ratio
+        reserve_mass_ratio:
+          value: settings:mission:sizing:breguet:reserve:mass_ratio
+          default: 0.06
 
 routes:
   main_route:
@@ -42,6 +83,13 @@ routes:
 
 missions:
   sizing:
+    isa_offset:
+      value: ~ISA_offset
+      unit: degK
+      default: 0.0
     parts:
+      - phase: start
+      - phase: taxi_out
+      - phase: takeoff
       - route: main_route
       - phase: global_reserve
diff --git a/src/fastoad/models/performances/mission/openmdao/resources/sizing_mission.yml b/src/fastoad/models/performances/mission/openmdao/resources/sizing_mission.yml
index 36ebcc221..7162049a4 100644
--- a/src/fastoad/models/performances/mission/openmdao/resources/sizing_mission.yml
+++ b/src/fastoad/models/performances/mission/openmdao/resources/sizing_mission.yml
@@ -1,9 +1,54 @@
 phases:
+  start:
+    parts:
+      - segment: start
+        target:
+          altitude:
+            value: ~
+            unit: ft
+            default: 0.0
+          true_airspeed:
+            value: ~
+            unit: m/s
+            default: 0.0
+  taxi_out:
+    time_step:
+      value: settings:mission~
+      default: 60
+      unit: s
+    parts:
+      - segment: taxi
+        thrust_rate: ~
+        true_airspeed:
+          value: ~
+          default: 0.
+        target:
+          time: ~duration
+  takeoff:
+    parts:
+      - segment: transition
+        target:
+          delta_altitude:
+            value: ~safety_altitude
+            unit: ft
+            default: 35
+          delta_mass: -~fuel
+          time: ~duration
+          true_airspeed: ~V2
+      - segment: mass_input
+        target:
+          mass: data:mission:sizing:TOW
+
   initial_climb:
     engine_setting: takeoff
     polar: data:aerodynamics:aircraft:takeoff
-    thrust_rate: 1.0
-    time_step: 0.2
+    thrust_rate:
+      value: data:mission:sizing:initial_climb:thrust_rate
+      default: 1.0
+    time_step:
+      value: settings:mission~
+      default: 0.2
+      unit: s
     parts:
       - segment: altitude_change
         target:
@@ -14,7 +59,8 @@ phases:
       - segment: speed_change
         target:
           equivalent_airspeed:
-            value: 250
+            value: data:mission:sizing:climb:first_segment:CAS
+            default: 250
             unit: kn
       - segment: altitude_change
         #        polar: data:aerodynamics:aircraft:low_speed
@@ -26,8 +72,13 @@ phases:
   climb:
     engine_setting: climb
     polar: data:aerodynamics:aircraft:cruise
-    thrust_rate: data:mission:sizing:climb:thrust_rate
-    time_step: 5.0
+    thrust_rate:
+      value: data:mission:sizing:climb:thrust_rate
+      default: 0.93
+    time_step:
+      value: settings:mission~
+      default: 5.0
+      unit: s
     parts:
       - segment: altitude_change
         target:
@@ -38,7 +89,8 @@ phases:
       - segment: speed_change
         target:
           equivalent_airspeed:
-            value: 300.
+            value: data:mission:sizing:climb:second_segment:CAS
+            default: 300.
             unit: kn
       - segment: altitude_change
         target:
@@ -53,7 +105,10 @@ phases:
     engine_setting: climb
     polar: data:aerodynamics:aircraft:cruise
     thrust_rate: data:mission:sizing:climb:thrust_rate
-    time_step: 5.0
+    time_step:
+      value: settings:mission~
+      default: 5.0
+      unit: s
     parts:
       - segment: altitude_change
         target:
@@ -64,7 +119,8 @@ phases:
       - segment: speed_change
         target:
           equivalent_airspeed:
-            value: 300.
+            value: data:mission:sizing:climb:second_segment:CAS
+            default: 300.
             unit: kn
       - segment: altitude_change
         target:
@@ -76,7 +132,10 @@ phases:
     engine_setting: idle
     polar: data:aerodynamics:aircraft:cruise
     thrust_rate: data:mission:sizing:descent:thrust_rate
-    time_step: 5.0
+    time_step:
+      value: settings:mission~
+      default: 5.0
+      unit: s
     parts:
       - segment: altitude_change
         target:
@@ -102,22 +161,36 @@ phases:
             value: 1500.
             unit: ft
   holding:
+    time_step:
+      value: settings:mission~
+      default: 60.0
+      unit: s
     parts:
       - segment: holding
         polar: data:aerodynamics:aircraft:cruise
         target:
-          time: data:mission:sizing:holding:duration
+          delta_time: ~duration
   taxi_in:
+    time_step:
+      value: settings:mission~
+      default: 60.0
+      unit: s
     parts:
       - segment: taxi
-        thrust_rate: data:mission:sizing:taxi_in:thrust_rate
-        true_airspeed: 0.
+        thrust_rate: ~
+        true_airspeed:
+          value: ~
+          default: 0.
         target:
-          time: data:mission:sizing:taxi_in:duration
+          delta_time: ~duration
 
 routes:
   main_route:
     range: data:TLAR:range
+    distance_accuracy:
+      value: settings:mission:sizing:main_route:distance_accuracy
+      default: 0.5
+      unit: km
     climb_parts:
       - phase: initial_climb
       - phase: climb
@@ -131,7 +204,11 @@ routes:
     descent_parts:
       - phase: descent
   diversion:
-    range: data:mission:sizing:diversion:distance
+    distance_accuracy:
+      value: settings:mission:sizing:diversion:distance_accuracy
+      default: 0.5
+      unit: km
+    range: ~distance
     climb_parts:
       - phase: diversion_climb
     cruise_part:
@@ -143,11 +220,20 @@ routes:
 
 missions:
   sizing:
+    isa_offset:
+      value: ~ISA_offset
+      unit: degK
+      default: 0.0
     parts:
+      - phase: start
+      - phase: taxi_out
+      - phase: takeoff
       - route: main_route
       - route: diversion
       - phase: holding
       - phase: taxi_in
       - reserve:
           ref: main_route
-          multiplier: 0.03
+          multiplier:
+            value: data:mission:sizing:reserve_coefficient
+            default: 0.03
diff --git a/src/fastoad/models/performances/mission/routes.py b/src/fastoad/models/performances/mission/routes.py
index e6b0f402f..476957125 100644
--- a/src/fastoad/models/performances/mission/routes.py
+++ b/src/fastoad/models/performances/mission/routes.py
@@ -2,7 +2,7 @@
 Classes for computation of routes (i.e. assemblies of climb, cruise and descent phases).
 """
 #  This file is part of FAST-OAD : A framework for rapid Overall Aircraft Design
-#  Copyright (C) 2021 ONERA & ISAE-SUPAERO
+#  Copyright (C) 2022 ONERA & ISAE-SUPAERO
 #  FAST is free software: you can redistribute it and/or modify
 #  it under the terms of the GNU General Public License as published by
 #  the Free Software Foundation, either version 3 of the License, or
@@ -22,8 +22,9 @@
 from scipy.optimize import root_scalar
 
 from fastoad.model_base import FlightPoint
+from fastoad.model_base.datacls import MANDATORY_FIELD
 from fastoad.models.performances.mission.base import FlightSequence, IFlightPart
-from fastoad.models.performances.mission.segments.base import FlightSegment
+from fastoad.models.performances.mission.segments.base import AbstractFlightSegment
 from fastoad.models.performances.mission.segments.cruise import CruiseSegment
 
 
@@ -32,20 +33,20 @@ class SimpleRoute(FlightSequence):
     """
     Computes a simple route.
 
-    The computed route will be be made of:
+    The computed route will be made of:
         - any number of climb phases
         - one cruise segment
         - any number of descent phases.
     """
 
     #: Any number of flight phases that will occur before cruise.
-    climb_phases: List[FlightSequence]
+    climb_phases: List[FlightSequence] = MANDATORY_FIELD
 
     #: The cruise phase.
-    cruise_segment: CruiseSegment
+    cruise_segment: CruiseSegment = MANDATORY_FIELD
 
     #: Any number of flight phases that will occur after cruise.
-    descent_phases: List[FlightSequence]
+    descent_phases: List[FlightSequence] = MANDATORY_FIELD
 
     def __post_init__(self):
         super().__post_init__()
@@ -60,7 +61,8 @@ def cruise_distance(self):
 
     @cruise_distance.setter
     def cruise_distance(self, cruise_distance):
-        self.cruise_segment.target.ground_distance = cruise_distance
+        self.cruise_segment.target.ground_distance = float(cruise_distance)
+        self.cruise_segment.target.set_as_relative("ground_distance")
 
     @property
     def cruise_speed(self) -> Optional[Tuple[str, float]]:
@@ -75,7 +77,7 @@ def cruise_speed(self) -> Optional[Tuple[str, float]]:
         for segment in climb_segments:
             for speed_param in ["true_airspeed", "equivalent_airspeed", "mach"]:
                 speed_value = getattr(segment.target, speed_param)
-                if speed_value and speed_value != FlightSegment.CONSTANT_VALUE:
+                if speed_value and speed_value != AbstractFlightSegment.CONSTANT_VALUE:
                     return speed_param, speed_value
 
         return None
@@ -98,7 +100,7 @@ class RangedRoute(SimpleRoute):
     """
 
     #: Target ground distance for whole route
-    flight_distance: float
+    flight_distance: float = MANDATORY_FIELD
 
     #: Accuracy on actual total ground distance for the solver. In meters
     distance_accuracy: float = 0.5e3
@@ -128,7 +130,7 @@ def compute_from(self, start: FlightPoint) -> pd.DataFrame:
     def _get_ground_distances(cls, phase: FlightSequence) -> list:
         ground_distances = []
         for flight_part in phase.flight_sequence:
-            if isinstance(flight_part, FlightSegment):
+            if isinstance(flight_part, AbstractFlightSegment):
                 ground_distances.append(flight_part.target.ground_distance)
             else:
                 ground_distances.extend(cls._get_ground_distances(flight_part))
@@ -146,7 +148,7 @@ def _solve_cruise_distance(self, start: FlightPoint) -> pd.DataFrame:
             args=(start,),
             x0=self.flight_distance * 0.5,
             x1=self.flight_distance * 0.25,
-            xtol=0.5e3,
+            xtol=self.distance_accuracy,
             method="secant",
         )
 
diff --git a/src/fastoad/models/performances/mission/segments/altitude_change.py b/src/fastoad/models/performances/mission/segments/altitude_change.py
index d3f6bfa0b..48a29b9a8 100644
--- a/src/fastoad/models/performances/mission/segments/altitude_change.py
+++ b/src/fastoad/models/performances/mission/segments/altitude_change.py
@@ -1,6 +1,6 @@
 """Classes for climb/descent segments."""
 #  This file is part of FAST-OAD : A framework for rapid Overall Aircraft Design
-#  Copyright (C) 2021 ONERA & ISAE-SUPAERO
+#  Copyright (C) 2022 ONERA & ISAE-SUPAERO
 #  FAST is free software: you can redistribute it and/or modify
 #  it under the terms of the GNU General Public License as published by
 #  the Free Software Foundation, either version 3 of the License, or
@@ -19,10 +19,9 @@
 
 import pandas as pd
 from scipy.constants import foot, g
-from stdatm import AtmosphereSI
 
 from fastoad.model_base import FlightPoint
-from .base import ManualThrustSegment
+from .base import AbstractManualThrustSegment
 from ..exceptions import FastFlightSegmentIncompleteFlightPoint
 from ..util import get_closest_flight_level
 
@@ -30,7 +29,10 @@
 
 
 @dataclass
-class AltitudeChangeSegment(ManualThrustSegment, mission_file_keyword="altitude_change"):
+class AltitudeChangeSegment(
+    AbstractManualThrustSegment,
+    mission_file_keyword="altitude_change",
+):
     """
     Computes a flight path segment where altitude is modified with constant speed.
 
@@ -80,35 +82,35 @@ class AltitudeChangeSegment(ManualThrustSegment, mission_file_keyword="altitude_
     #: with max lift/drag ratio.
     OPTIMAL_FLIGHT_LEVEL = "optimal_flight_level"  # pylint: disable=invalid-name # used as constant
 
-    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):
+    def compute_from_start_to_target(self, start: FlightPoint, target: FlightPoint) -> pd.DataFrame:
+        if target.altitude is not None:
+            if isinstance(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
+                target.CL = target.altitude  # pylint: disable=invalid-name
+                # let's put a numerical, negative value in target.altitude to
                 # ensure there will be no problem in self.get_distance_to_target()
-                self.target.altitude = -1000.0
+                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
+                target.CL = None
                 self.interrupt_if_getting_further_from_target = True
 
-        atm = AtmosphereSI(start.altitude)
-        if self.target.equivalent_airspeed == self.CONSTANT_VALUE:
+        atm = self._get_atmosphere_point(start.altitude)
+        if 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:
+        elif target.mach == self.CONSTANT_VALUE:
             atm.mach = start.mach
             start.true_airspeed = atm.true_airspeed
 
-        return super().compute_from(start)
+        return super().compute_from_start_to_target(start, target)
 
-    def get_distance_to_target(self, flight_points: List[FlightPoint]) -> float:
+    def get_distance_to_target(
+        self, flight_points: List[FlightPoint], target: FlightPoint
+    ) -> float:
         current = flight_points[-1]
 
         # Max flight level is first priority
@@ -116,20 +118,20 @@ def get_distance_to_target(self, flight_points: List[FlightPoint]) -> float:
         if current.altitude >= max_authorized_altitude:
             return max_authorized_altitude - current.altitude
 
-        if self.target.CL:
+        if target.CL:
             # Optimal altitude is based on a target Mach number, though target speed
             # may be specified as TAS or EAS. If so, Mach number has to be computed
             # for target altitude and speed.
 
             # First, as target speed is expected to be set to self.CONSTANT_VALUE for one
             # parameter. Let's get the real value from start point.
-            target_speed = copy(self.target)
+            target_speed = copy(target)
             for speed_param in ["true_airspeed", "equivalent_airspeed", "mach"]:
                 if isinstance(getattr(target_speed, speed_param), str):
                     setattr(target_speed, speed_param, getattr(flight_points[0], speed_param))
 
             # Now, let's compute target Mach number
-            atm = AtmosphereSI(max(self.target.altitude, current.altitude))
+            atm = self._get_atmosphere_point(max(target.altitude, current.altitude))
             if target_speed.equivalent_airspeed:
                 atm.equivalent_airspeed = target_speed.equivalent_airspeed
                 target_speed.true_airspeed = atm.true_airspeed
@@ -141,24 +143,19 @@ def get_distance_to_target(self, flight_points: List[FlightPoint]) -> float:
             optimal_altitude = self._get_optimal_altitude(
                 current.mass, target_speed.mach, current.altitude
             )
-            if self.target.CL == self.OPTIMAL_ALTITUDE:
-                self.target.altitude = optimal_altitude
-            else:  # self.target.CL == self.OPTIMAL_FLIGHT_LEVEL:
-                self.target.altitude = get_closest_flight_level(
-                    optimal_altitude, up_direction=False
-                )
-
-        if self.target.altitude is not None:
-            return self.target.altitude - current.altitude
-        if self.target.true_airspeed and self.target.true_airspeed != self.CONSTANT_VALUE:
-            return self.target.true_airspeed - current.true_airspeed
-        if (
-            self.target.equivalent_airspeed
-            and self.target.equivalent_airspeed != self.CONSTANT_VALUE
-        ):
-            return self.target.equivalent_airspeed - current.equivalent_airspeed
-        if self.target.mach is not None and self.target.mach != self.CONSTANT_VALUE:
-            return self.target.mach - current.mach
+            if target.CL == self.OPTIMAL_ALTITUDE:
+                target.altitude = optimal_altitude
+            else:  # target.CL == self.OPTIMAL_FLIGHT_LEVEL:
+                target.altitude = get_closest_flight_level(optimal_altitude, up_direction=False)
+
+        if target.altitude is not None:
+            return target.altitude - current.altitude
+        if target.true_airspeed and target.true_airspeed != self.CONSTANT_VALUE:
+            return target.true_airspeed - current.true_airspeed
+        if target.equivalent_airspeed and target.equivalent_airspeed != self.CONSTANT_VALUE:
+            return target.equivalent_airspeed - current.equivalent_airspeed
+        if target.mach is not None and target.mach != self.CONSTANT_VALUE:
+            return target.mach - current.mach
 
         raise FastFlightSegmentIncompleteFlightPoint(
             "No valid target definition for altitude change."
diff --git a/src/fastoad/models/performances/mission/segments/base.py b/src/fastoad/models/performances/mission/segments/base.py
index 777b2307b..9a973aa5c 100644
--- a/src/fastoad/models/performances/mission/segments/base.py
+++ b/src/fastoad/models/performances/mission/segments/base.py
@@ -1,6 +1,6 @@
 """Base classes for simulating flight segments."""
 #  This file is part of FAST-OAD : A framework for rapid Overall Aircraft Design
-#  Copyright (C) 2021 ONERA & ISAE-SUPAERO
+#  Copyright (C) 2022 ONERA & ISAE-SUPAERO
 #  FAST is free software: you can redistribute it and/or modify
 #  it under the terms of the GNU General Public License as published by
 #  the Free Software Foundation, either version 3 of the License, or
@@ -14,18 +14,21 @@
 
 import logging
 from abc import ABC, abstractmethod
-from dataclasses import dataclass
-from typing import List, Tuple
+from copy import deepcopy
+from dataclasses import dataclass, field
+from typing import Dict, List, Optional, Tuple, Type
 
 import numpy as np
 import pandas as pd
 from aenum import Enum, extend_enum
+from deprecated import deprecated
 from scipy.constants import g
 from scipy.optimize import root_scalar
 from stdatm import AtmosphereSI
 
 from fastoad.constants import EngineSetting
 from fastoad.model_base import FlightPoint
+from fastoad.model_base.datacls import MANDATORY_FIELD
 from fastoad.model_base.propulsion import IPropulsion
 from fastoad.models.performances.mission.polar import Polar
 from ..base import IFlightPart
@@ -42,34 +45,40 @@ class SegmentDefinitions(Enum):
     """
 
     @classmethod
-    def add_segment(cls, segment_name: str, segment_class: type):
+    def add_segment(cls, segment_name: str, segment_class: Type["AbstractFlightSegment"]):
         """
         Adds a segment definition.
 
         :param segment_name: segment names (mission file keyword)
         :param segment_class: segment implementation (derived of :class:`~FlightSegment`)
         """
-        if issubclass(segment_class, FlightSegment):
+        if issubclass(segment_class, AbstractFlightSegment):
             extend_enum(cls, segment_name, segment_class)
         else:
             raise RuntimeWarning(
-                '"%s" is ignored as segment name because its associated class '
-                "does not derive from FlightSegment." % segment_name
+                f'"{segment_name}" is ignored as segment name because its associated class '
+                "does not derive from FlightSegment."
             )
 
     @classmethod
-    def get_segment_class(cls, segment_name) -> type:
+    def get_segment_class(cls, segment_name) -> Optional[Type["AbstractFlightSegment"]]:
         """
         Provides the segment implementation for provided name.
 
         :param segment_name:
-        :return: the segment implementation (derived of :class:`~FlightSegment`)
+        :return: the segment implementation (derived of :class:`~FlightSegment`),
+                 or None if segment_name is not known
         """
-        return cls[segment_name].value
+        try:
+            enum = cls[segment_name]
+        except KeyError:
+            enum = None
+
+        return enum.value if enum else None
 
 
 @dataclass
-class FlightSegment(IFlightPart):
+class AbstractFlightSegment(IFlightPart, ABC):
     """
     Base class for flight path segment.
 
@@ -78,7 +87,7 @@ class FlightSegment(IFlightPart):
     When subclassing this class, the attribute "mission_file_keyword" can be set,
     so that the segment can be used in mission file definition with this keyword:
 
-        >>> class NewSegment(FlightSegment, mission_file_keyword="new_segment")
+        >>> class NewSegment(AbstractFlightSegment, mission_file_keyword="new_segment")
         >>>     ...
 
     Then in mission definition:
@@ -89,6 +98,15 @@ class FlightSegment(IFlightPart):
             my_phase:
                 parts:
                     - segment: new_segment
+
+    .. Important::
+
+        :meth:`compute_from` is the method to call to achieve the segment computation.
+
+        However, when subclassing, the method to overload is :meth:`compute_from_start_to_target`.
+        Generic reprocessing of start and target flight points is done in :meth:`compute_from`
+        before calling :meth:`compute_from_start_to_target`
+
     """
 
     #: A FlightPoint instance that provides parameter values that should all be reached at the
@@ -96,25 +114,211 @@ class FlightSegment(IFlightPart):
     #: Possible parameters depend on the current segment. A parameter can also be set to
     #: :attr:`~fastoad.models.performances.mission.segments.base.FlightSegment.CONSTANT_VALUE`
     #: to tell that initial value should be kept during all segment.
-    target: FlightPoint
+    target: FlightPoint = MANDATORY_FIELD
+
+    # the `target` field above will be overloaded by a property, using the hidden value below:
+    _target: FlightPoint = field(default=MANDATORY_FIELD, init=False)
+
+    #: The temperature offset for ISA atmosphere model.
+    isa_offset: float = 0.0
+
+    #: Using this value will tell to keep the associated parameter constant.
+    CONSTANT_VALUE = "constant"  # pylint: disable=invalid-name # used as constant
+
+    # To be noted: this one is not a dataclass field, but an actual class attribute
+    _attribute_units = dict(reference_area="m**2", time_step="s")
+
+    @abstractmethod
+    def compute_from_start_to_target(self, start, target) -> pd.DataFrame:
+        """
+        Here should come the implementation for computing flight points
+        between start and target flight points.
+
+        :param start:
+        :param target: Definition of segment target
+        :return: a pandas DataFrame where column names match fields of
+                 :class:`~fastoad.model_base.flight_point.FlightPoint`
+        """
+
+    @classmethod
+    def __init_subclass__(cls, *, mission_file_keyword="", attribute_units: Dict[str, str] = None):
+        if mission_file_keyword:
+            SegmentDefinitions.add_segment(mission_file_keyword, cls)
+        cls._attribute_units = cls._attribute_units.copy()
+        if attribute_units:
+            cls._attribute_units.update(attribute_units)
+
+        # We want to have self.target as a property to ensure it gets always "scalarized".
+        # But properties and dataclasses do not mix very well. It would be possible to
+        # declare "target" as property, though it is a dataclass field, but it would then
+        # be considered as a field with defined default value (the default being the property
+        # object XD ). And since it is followed by fields without default, Python complains.
+        #
+        # The solution is to define the property afterwards, and since we are in an abstract class,
+        # it can be done when subclassing.
+        def _get_target(self) -> FlightPoint:
+            return self._target
+
+        def _set_target(self, value: FlightPoint):
+            value.scalarize()
+            # Initializing self._target elsewhere give bad results. Since target is mandatory
+            # in constructor, this initialization will happen.
+            self._target = value
+
+        cls.target = property(_get_target, _set_target)
+
+    @classmethod
+    def get_attribute_unit(cls, attribute_name: str) -> str:
+        """Returns unit for specified attribute."""
+        return cls._attribute_units.get(attribute_name)
+
+    def compute_from(self, start: FlightPoint) -> pd.DataFrame:
+        """
+        Computes the flight path segment from provided start point.
+
+        Computation ends when target is attained, or if the computation stops getting
+        closer to target.
+        For instance, a climb computation with too low thrust will only return one
+        flight point, that is the provided start point.
+
+        .. Important::
+
+            When subclasssing, if you need to overload :meth:`compute_from`, you should consider
+            overriding :meth:`_compute_from` instead. Therefore, you will take benefit of the
+            preprocessing of start and target flight points that is done in :meth:`compute_from`
+
+
+        :param start: the initial flight point, defined for `altitude`, `mass` and speed
+                      (`true_airspeed`, `equivalent_airspeed` or `mach`). Can also be
+                      defined for `time` and/or `ground_distance`.
+        :return: a pandas DataFrame where column names match fields of
+                 :class:`~fastoad.model_base.flight_point.FlightPoint`
+        """
+        # Let's ensure we do not modify the original definitions of start and target
+        # during the process
+        start_copy = deepcopy(start)
+        if start_copy.altitude is not None:
+            try:
+                self.complete_flight_point(start_copy)
+            except FastFlightSegmentIncompleteFlightPoint:
+                pass
+        start_copy.scalarize()
+
+        target_copy = self._target.make_absolute(start_copy)
+        target_copy.scalarize()
+
+        if start_copy.time is None:
+            start_copy.time = 0.0
+        if start_copy.ground_distance is None:
+            start_copy.ground_distance = 0.0
+
+        flight_points = self.compute_from_start_to_target(start_copy, target_copy)
+
+        return flight_points
+
+    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
+        """
+
+        self._complete_speed_values(flight_point)
+
+    @staticmethod
+    def complete_flight_point_from(flight_point: FlightPoint, source: FlightPoint):
+        """
+        Sets undefined values in `flight_point` using the ones from `source`.
+
+        The particular case of speeds is taken into account: if at least one speed parameter is
+        defined, all other speed parameters are considered defined, because they will be
+        deduced when needed.
+
+        :param flight_point:
+        :param source:
+        """
+        speed_fields = {
+            "true_airspeed",
+            "equivalent_airspeed",
+            "calibrated_airspeed",
+            "mach",
+            "unitary_reynolds",
+        }.intersection(flight_point.get_field_names())
+        other_fields = list(set(flight_point.get_field_names()) - speed_fields)
+
+        speeds_are_missing = np.all([getattr(flight_point, name) is None for name in speed_fields])
+
+        for field_name in other_fields + speeds_are_missing * list(speed_fields):
+            if getattr(flight_point, field_name) is None and not source.is_relative(field_name):
+                setattr(flight_point, field_name, getattr(source, field_name))
+
+    def _complete_speed_values(
+        self, flight_point: FlightPoint, raise_error_on_missing_speeds=True
+    ) -> bool:
+        """
+        Computes consistent values between TAS, EAS and Mach, assuming one of them is defined.
+        """
+        atm = self._get_atmosphere_point(flight_point.altitude)
+
+        if flight_point.true_airspeed is None:
+            if flight_point.mach is not None:
+                atm.mach = flight_point.mach
+            elif flight_point.equivalent_airspeed is not None:
+                atm.equivalent_airspeed = flight_point.equivalent_airspeed
+            elif raise_error_on_missing_speeds:
+                raise FastFlightSegmentIncompleteFlightPoint(
+                    "Flight point should be defined for true_airspeed, "
+                    "equivalent_airspeed, or mach."
+                )
+            else:
+                return False
+            flight_point.true_airspeed = atm.true_airspeed
+        else:
+            atm.true_airspeed = flight_point.true_airspeed
+
+        flight_point.mach = atm.mach
+        flight_point.equivalent_airspeed = atm.equivalent_airspeed
+        return True
+
+    def _get_atmosphere_point(self, altitude: float) -> AtmosphereSI:
+        """
+        Convenience method to ensure used atmosphere model is initiated with :attr:`delta_isa`.
+
+        :param altitude: in meters
+        :return: AtmosphereSI instantiated from provided altitude and :attr:`delta_isa`
+        """
+        return AtmosphereSI(altitude, self.isa_offset)
+
+
+@dataclass
+class AbstractTimeStepFlightSegment(
+    AbstractFlightSegment,
+    ABC,
+):
+    """
+    Base class for time step computation flight segments.
+
+    This class implements the time computation. For this computation to work, subclasses must
+    implement abstract methods :meth:`get_distance_to_target`,
+    :meth:`get_gamma_and_acceleration` and :meth:`compute_propulsion`.
+    """
 
     #: A IPropulsion instance that will be called at each time step.
-    propulsion: IPropulsion
+    propulsion: IPropulsion = MANDATORY_FIELD
 
     #: The Polar instance that will provide drag data.
-    polar: Polar
+    polar: Polar = MANDATORY_FIELD
 
     #: The reference area, in m**2.
-    reference_area: float
+    reference_area: float = MANDATORY_FIELD
 
     #: Used time step for computation (actual time step can be lower at some particular times of
     #: the flight path).
     time_step: float = DEFAULT_TIME_STEP
 
-    #: The EngineSetting value associated to the segment. Can be used in the propulsion
-    #: model.
-    engine_setting: EngineSetting = EngineSetting.CLIMB
-
     #: Minimum and maximum authorized altitude values. If computed altitude gets beyond these
     #: limits, computation will be interrupted and a warning message will be issued in logger.
     altitude_bounds: tuple = (-500.0, 40000.0)
@@ -123,54 +327,82 @@ class FlightSegment(IFlightPart):
     #: computation will be interrupted and a warning message will be issued in logger.
     mach_bounds: tuple = (0.0, 5.0)
 
-    #: The name of the current flight sequence.
-    name: str = ""
-
     #: If True, computation will be interrupted if a parameter stops getting closer to target
     #: between two iterations (which can mean the provided thrust rate is not adapted).
     interrupt_if_getting_further_from_target: bool = True
 
-    #: Using this value will tell to keep the associated parameter constant.
-    CONSTANT_VALUE = "constant"  # pylint: disable=invalid-name # used as constant
+    #: The EngineSetting value associated to the segment. Can be used in the
+    #: propulsion model.
+    engine_setting: EngineSetting = EngineSetting.CLIMB
 
-    @classmethod
-    def __init_subclass__(cls, *, mission_file_keyword=""):
-        if mission_file_keyword:
-            SegmentDefinitions.add_segment(mission_file_keyword, cls)
+    @abstractmethod
+    def get_distance_to_target(
+        self, flight_points: List[FlightPoint], target: FlightPoint
+    ) -> float:
+        """
+        Computes a "distance" from last flight point to target.
 
-    def __post_init__(self):
-        # Ensure target fields are not numpy arrays
-        self.target.scalarize()
+        Computed does not need to have a real meaning.
+        The important point is that it must be signed so that algorithm knows on
+        which "side" of the target we are.
+        And of course, it should be 0. if flight point is on target.
 
-    def compute_from(self, start: FlightPoint) -> pd.DataFrame:
+        :param flight_points: list of all currently computed flight_points
+        :param target: segment target (will not contain relative values)
+        :return: O. if target is attained, a non-null value otherwise
         """
-        Computes the flight path segment from provided start point.
 
-        Computation ends when target is attained, or if the computation stops getting
-        closer to target.
-        For instance, a climb computation with too low thrust will only return one
-        flight point, that is the provided start point.
+    @abstractmethod
+    def compute_propulsion(self, flight_point: FlightPoint):
+        """
+        Computes propulsion data.
 
-        :param start: the initial flight point, defined for `altitude`, `mass` and speed
-                      (`true_airspeed`, `equivalent_airspeed` or `mach`). Can also be
-                      defined for `time` and/or `ground_distance`.
-        :return: a pandas DataFrame where columns names match fields of
-                 :meth:`~fastoad.model_base.flight_point.FlightPoint`
+        Provided flight point is modified in place.
+
+        Generally, this method should end with::
+
+            self.propulsion.compute_flight_points(flight_point)
+
+        :param flight_point:
         """
-        if start.time is None:
-            start.time = 0.0
-        if start.ground_distance is None:
-            start.ground_distance = 0.0
 
-        self.complete_flight_point(start)
+    @abstractmethod
+    def get_gamma_and_acceleration(self, flight_point: FlightPoint) -> Tuple[float, float]:
+        """
+        Computes slope angle (gamma) and acceleration.
 
-        flight_points = [start]
+        :param flight_point: parameters after propulsion model has been called
+                             (i.e. mass, thrust and drag are available)
+        :return: slope angle in radians and acceleration in m**2/s
+        """
 
-        previous_point_to_target = self.get_distance_to_target(flight_points)
+    def complete_flight_point(self, flight_point: FlightPoint):
+        super().complete_flight_point(flight_point)
+        if flight_point.altitude is not None:
+            atm = self._get_atmosphere_point(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
+        flight_point.engine_setting = self.engine_setting
+        self.compute_propulsion(flight_point)
+        flight_point.slope_angle, flight_point.acceleration = self.get_gamma_and_acceleration(
+            flight_point
+        )
+
+    def compute_from_start_to_target(self, start: FlightPoint, target: FlightPoint) -> pd.DataFrame:
+        flight_points = [start]
+        previous_point_to_target = self.get_distance_to_target(flight_points, target)
         tol = 1.0e-5  # Such accuracy is not needed, but ensures reproducibility of results.
         while np.abs(previous_point_to_target) > tol:
             self._add_new_flight_point(flight_points, self.time_step)
-            last_point_to_target = self.get_distance_to_target(flight_points)
+            last_point_to_target = self.get_distance_to_target(flight_points, target)
 
             if last_point_to_target * previous_point_to_target < 0.0:
 
@@ -190,12 +422,12 @@ def replace_last_point(time_step):
                         time_step = time_step.item()
                     del flight_points[-1]
                     self._add_new_flight_point(flight_points, time_step)
-                    return self.get_distance_to_target(flight_points)
+                    return self.get_distance_to_target(flight_points, target)
 
                 root_scalar(
                     replace_last_point, x0=self.time_step, x1=self.time_step / 2.0, rtol=tol
                 )
-                last_point_to_target = self.get_distance_to_target(flight_points)
+                last_point_to_target = self.get_distance_to_target(flight_points, target)
             elif (
                 np.abs(last_point_to_target) > np.abs(previous_point_to_target)
                 # If self.target.CL is defined, it means that we look for an optimal altitude and
@@ -219,37 +451,8 @@ def replace_last_point(time_step):
             previous_point_to_target = last_point_to_target
 
         flight_points_df = pd.DataFrame(flight_points)
-
         return flight_points_df
 
-    def _check_values(self, flight_point: FlightPoint) -> str:
-        """
-        Checks that computed values are consistent.
-
-        May be overloaded for doing specific additional checks at each time step.
-
-        :param flight_point:
-        :return: None if Ok, or an error message otherwise
-        """
-
-        if not self.mach_bounds[0] <= flight_point.mach <= self.mach_bounds[1]:
-            return "true_airspeed value %f.1m/s is out of bound." % flight_point.true_airspeed
-        if not self.altitude_bounds[0] <= flight_point.altitude <= self.altitude_bounds[1]:
-            return "Altitude value %.0fm is out of bound." % flight_point.altitude
-        if flight_point.mass <= 0.0:
-            return "Negative mass value."
-
-    def _add_new_flight_point(self, flight_points: List[FlightPoint], time_step):
-        """
-        Appends a new flight point to provided flight point list.
-
-        :param flight_points: list of previous flight points, modified in place.
-        :param time_step: time step for new computed flight point.
-        """
-        new_point = self.compute_next_flight_point(flight_points, time_step)
-        self.complete_flight_point(new_point)
-        flight_points.append(new_point)
-
     def compute_next_flight_point(
         self, flight_points: List[FlightPoint], time_step: float
     ) -> FlightPoint:
@@ -285,57 +488,33 @@ def compute_next_flight_point(
         next_point.name = self.name
         return next_point
 
-    def complete_flight_point(self, flight_point: FlightPoint):
+    def _check_values(self, flight_point: FlightPoint) -> str:
         """
-        Computes data for provided flight point.
+        Checks that computed values are consistent.
 
-        Assumes that it is already defined for time, altitude, mass,
-        ground distance and speed (TAS, EAS, or Mach).
+        May be overloaded for doing specific additional checks at each time step.
 
-        :param flight_point: the flight point that will be completed in-place
+        :param flight_point:
+        :return: None if Ok, or an error message otherwise
         """
-        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
-        )
+        if not self.mach_bounds[0] <= flight_point.mach <= self.mach_bounds[1]:
+            return f"true_airspeed value {flight_point.true_airspeed:.1f}m/s is out of bound."
+        if not self.altitude_bounds[0] <= flight_point.altitude <= self.altitude_bounds[1]:
+            return f"Altitude value {flight_point.altitude:.0f}m is out of bound."
+        if flight_point.mass <= 0.0:
+            return "Negative mass value."
 
-    @staticmethod
-    def _complete_speed_values(flight_point: FlightPoint):
-        """
-        Computes consistent values between TAS, EAS and Mach, assuming one of them is defined.
+    def _add_new_flight_point(self, flight_points: List[FlightPoint], time_step):
         """
-        atm = AtmosphereSI(flight_point.altitude)
-
-        if flight_point.true_airspeed is None:
-            if flight_point.mach is not None:
-                atm.mach = flight_point.mach
-            elif flight_point.equivalent_airspeed is not None:
-                atm.equivalent_airspeed = flight_point.equivalent_airspeed
-            else:
-                raise FastFlightSegmentIncompleteFlightPoint(
-                    "Flight point should be defined for true_airspeed, "
-                    "equivalent_airspeed, or mach."
-                )
-            flight_point.true_airspeed = atm.true_airspeed
-        else:
-            atm.true_airspeed = flight_point.true_airspeed
+        Appends a new flight point to provided flight point list.
 
-        flight_point.mach = atm.mach
-        flight_point.equivalent_airspeed = atm.equivalent_airspeed
+        :param flight_points: list of previous flight points, modified in place.
+        :param time_step: time step for new computed flight point.
+        """
+        new_point = self.compute_next_flight_point(flight_points, time_step)
+        self.complete_flight_point(new_point)
+        flight_points.append(new_point)
 
     @staticmethod
     def _compute_next_altitude(next_point: FlightPoint, previous_point: FlightPoint):
@@ -360,7 +539,7 @@ def _get_optimal_altitude(
             altitude_guess = 10000.0
 
         def distance_to_optimum(altitude):
-            atm = AtmosphereSI(altitude)
+            atm = self._get_atmosphere_point(altitude)
             true_airspeed = mach * atm.speed_of_sound
             optimal_air_density = (
                 2.0 * mass * g / (self.reference_area * true_airspeed ** 2 * self.polar.optimal_cl)
@@ -373,47 +552,24 @@ def distance_to_optimum(altitude):
 
         return optimal_altitude
 
-    @abstractmethod
-    def get_distance_to_target(self, flight_points: List[FlightPoint]) -> float:
-        """
-        Computes a "distance" from last flight point to target.
-
-        Computed does not need to have a real meaning.
-        The important point is that it must be signed so that algorithm knows on
-        which "side" of the target we are.
-        And of course, it should be 0. if flight point is on target.
 
-        :param flight_points: list of all currently computed flight_points
-        :return: O. if target is attained, a non-null value otherwise
-        """
-
-    @abstractmethod
-    def compute_propulsion(self, flight_point: FlightPoint):
-        """
-        Computes propulsion data.
-
-        Provided flight point is modified in place.
-
-        Generally, this method should end with::
-
-            self.propulsion.compute_flight_points(flight_point)
-
-        :param flight_point:
-        """
-
-    @abstractmethod
-    def get_gamma_and_acceleration(self, flight_point: FlightPoint) -> Tuple[float, float]:
-        """
-        Computes slope angle (gamma) and acceleration.
+@deprecated(
+    "Class FlightSegment will be removed in version 2.0. "
+    "It is replaced by class AbstractTimeStepFlightSegment."
+)
+@dataclass
+class FlightSegment(AbstractTimeStepFlightSegment, ABC):
+    """
+    Base class for time step computation flight segments.
 
-        :param flight_point: parameters after propulsion model has been called
-                             (i.e. mass, thrust and drag are available)
-        :return: slope angle in radians and acceleration in m**2/s
-        """
+    This class implements the time computation. For this computation to work, subclasses must
+    implement abstract methods :meth:`get_get_distance_to_target`,
+    :meth:`get_gamma_and_acceleration` and :meth:`compute_propulsion`.
+    """
 
 
 @dataclass
-class ManualThrustSegment(FlightSegment, ABC):
+class AbstractManualThrustSegment(AbstractTimeStepFlightSegment, ABC):
     """
     Base class for computing flight segment where thrust rate is imposed.
 
@@ -429,7 +585,7 @@ def compute_propulsion(self, flight_point: FlightPoint):
 
 
 @dataclass
-class RegulatedThrustSegment(FlightSegment, ABC):
+class AbstractRegulatedThrustSegment(AbstractTimeStepFlightSegment, ABC):
     """
     Base class for computing flight segment where thrust rate is adjusted on drag.
     """
@@ -437,6 +593,7 @@ class RegulatedThrustSegment(FlightSegment, ABC):
     time_step: float = 60.0
 
     def __post_init__(self):
+        super().__post_init__()
         self.target.mach = self.CONSTANT_VALUE
 
     def compute_propulsion(self, flight_point: FlightPoint):
@@ -449,22 +606,15 @@ def get_gamma_and_acceleration(self, flight_point: FlightPoint) -> Tuple[float,
 
 
 @dataclass
-class FixedDurationSegment(FlightSegment, ABC):
+class AbstractFixedDurationSegment(AbstractTimeStepFlightSegment, ABC):
     """
-    Class for computing phases where duration is fixed.
-
-    Target duration is provide as target.time.
-    When using :meth:`compute_from`, if start.time is not 0, end time will be
-    start.time + target.time.
+    Base class for computing a fixed-duration segment.
     """
 
     time_step: float = 60.0
 
-    def compute_from(self, start: FlightPoint) -> pd.DataFrame:
-        if start.time:
-            self.target.time = self.target.time + start.time
-        return super().compute_from(start)
-
-    def get_distance_to_target(self, flight_points: List[FlightPoint]) -> float:
+    def get_distance_to_target(
+        self, flight_points: List[FlightPoint], target: FlightPoint
+    ) -> float:
         current = flight_points[-1]
-        return self.target.time - current.time
+        return target.time - current.time
diff --git a/src/fastoad/models/performances/mission/segments/cruise.py b/src/fastoad/models/performances/mission/segments/cruise.py
index 13e779cbe..17c86c758 100644
--- a/src/fastoad/models/performances/mission/segments/cruise.py
+++ b/src/fastoad/models/performances/mission/segments/cruise.py
@@ -1,6 +1,6 @@
 """Classes for simulating cruise segments."""
 #  This file is part of FAST-OAD : A framework for rapid Overall Aircraft Design
-#  Copyright (C) 2021 ONERA & ISAE-SUPAERO
+#  Copyright (C) 2022 ONERA & ISAE-SUPAERO
 #  FAST is free software: you can redistribute it and/or modify
 #  it under the terms of the GNU General Public License as published by
 #  the Free Software Foundation, either version 3 of the License, or
@@ -22,12 +22,12 @@
 
 from fastoad.model_base import FlightPoint
 from .altitude_change import AltitudeChangeSegment
-from .base import FlightSegment, RegulatedThrustSegment
+from .base import AbstractRegulatedThrustSegment, AbstractTimeStepFlightSegment
 from ..util import get_closest_flight_level
 
 
 @dataclass
-class CruiseSegment(RegulatedThrustSegment):
+class CruiseSegment(AbstractRegulatedThrustSegment):
     """
     Class for computing cruise flight segment at constant altitude and speed.
 
@@ -39,23 +39,21 @@ class CruiseSegment(RegulatedThrustSegment):
     """
 
     def __post_init__(self):
+        super().__post_init__()
         # Constant speed at constant altitude is necessarily constant Mach, but
         # subclasses can be at variable altitude, so Mach is considered constant
         # if no other constant speed parameter is set to "constant".
-        if (
-            self.target.true_airspeed != FlightSegment.CONSTANT_VALUE
-            and self.target.equivalent_airspeed != FlightSegment.CONSTANT_VALUE
-        ):
-            self.target.mach = FlightSegment.CONSTANT_VALUE
-
-    def compute_from(self, start: FlightPoint) -> pd.DataFrame:
-        if start.ground_distance:
-            self.target.ground_distance = self.target.ground_distance + start.ground_distance
-        return super().compute_from(start)
-
-    def get_distance_to_target(self, flight_points: List[FlightPoint]) -> float:
+        if AbstractTimeStepFlightSegment.CONSTANT_VALUE not in [
+            self.target.true_airspeed,
+            self.target.equivalent_airspeed,
+        ]:
+            self.target.mach = AbstractTimeStepFlightSegment.CONSTANT_VALUE
+
+    def get_distance_to_target(
+        self, flight_points: List[FlightPoint], target: FlightPoint
+    ) -> float:
         current = flight_points[-1]
-        return self.target.ground_distance - current.ground_distance
+        return target.ground_distance - current.ground_distance
 
 
 @dataclass
@@ -71,9 +69,10 @@ class OptimalCruiseSegment(CruiseSegment, mission_file_keyword="optimal_cruise")
     `true_airspeed` and `equivalent_airspeed`. If not, Mach will be assumed constant.
     """
 
-    def compute_from(self, start: FlightPoint) -> pd.DataFrame:
+    def compute_from_start_to_target(self, start: FlightPoint, target: FlightPoint) -> pd.DataFrame:
         start.altitude = self._get_optimal_altitude(start.mass, start.mach)
-        return super().compute_from(start)
+        self.complete_flight_point(start)
+        return super().compute_from_start_to_target(start, target)
 
     def _compute_next_altitude(self, next_point: FlightPoint, previous_point: FlightPoint):
         next_point.altitude = self._get_optimal_altitude(
@@ -110,12 +109,12 @@ class ClimbAndCruiseSegment(CruiseSegment, mission_file_keyword="cruise"):
     #: The maximum allowed flight level (i.e. multiple of 100 feet).
     maximum_flight_level: float = 500.0
 
-    def compute_from(self, start: FlightPoint) -> pd.DataFrame:
+    def compute_from_start_to_target(self, start: FlightPoint, target: FlightPoint) -> pd.DataFrame:
         climb_segment = deepcopy(self.climb_segment)
-        climb_segment.target = deepcopy(self.target)
+        climb_segment.target = target
 
         cruise_segment = CruiseSegment(
-            target=deepcopy(self.target),
+            target=deepcopy(target),  # deepcopy needed because altitude will be modified.
             propulsion=self.propulsion,
             reference_area=self.reference_area,
             polar=self.polar,
@@ -123,9 +122,6 @@ def compute_from(self, start: FlightPoint) -> pd.DataFrame:
             engine_setting=self.engine_setting,
         )
 
-        if start.ground_distance:
-            self.target.ground_distance = self.target.ground_distance + start.ground_distance
-
         if (
             self.target.altitude == AltitudeChangeSegment.OPTIMAL_FLIGHT_LEVEL
             and climb_segment is not None
@@ -156,17 +152,17 @@ def compute_from(self, start: FlightPoint) -> pd.DataFrame:
                 if go_to_next_level:
                     results = new_results
 
-        elif self.target.altitude is not None:
+        elif target.altitude is not None:
             results = self._climb_to_altitude_and_cruise(
-                start, self.target.altitude, climb_segment, cruise_segment
+                start, target.altitude, climb_segment, cruise_segment
             )
         else:
-            results = super().compute_from(start)
+            results = super().compute_from_start_to_target(start, target)
 
         return results
 
+    @staticmethod
     def _climb_to_altitude_and_cruise(
-        self,
         start: FlightPoint,
         cruise_altitude: float,
         climb_segment: AltitudeChangeSegment,
@@ -191,16 +187,17 @@ def _climb_to_altitude_and_cruise(
         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)
 
 
 @dataclass
-class BreguetCruiseSegment(CruiseSegment, mission_file_keyword="breguet"):
+class BreguetCruiseSegment(
+    CruiseSegment,
+    mission_file_keyword="breguet",
+    attribute_units=dict(climb_and_descent_distance="m"),
+):
     """
     Class for computing cruise flight segment at constant altitude using Breguet-Leduc formula.
 
@@ -216,21 +213,14 @@ class BreguetCruiseSegment(CruiseSegment, mission_file_keyword="breguet"):
     #: The reference area, in m**2. Used only if use_max_lift_drag_ratio is False.
     reference_area: float = 1.0
 
-    #:
-    climb_and_descent_distance: float = 0.0
-
-    def __post_init__(self):
-        super().__post_init__()
-        self.target.ground_distance = self.target.ground_distance - self.climb_and_descent_distance
-
-    def compute_from(self, start: FlightPoint) -> pd.DataFrame:
-        self.complete_flight_point(start)
-
-        cruise_mass_ratio = self._compute_cruise_mass_ratio(start, self.target.ground_distance)
+    def compute_from_start_to_target(self, start: FlightPoint, target: FlightPoint) -> pd.DataFrame:
+        cruise_mass_ratio = self._compute_cruise_mass_ratio(
+            start, target.ground_distance - start.ground_distance
+        )
 
         end = deepcopy(start)
         end.mass = start.mass * cruise_mass_ratio
-        end.ground_distance = start.ground_distance + self.target.ground_distance
+        end.ground_distance = target.ground_distance
         end.time = start.time + (end.ground_distance - start.ground_distance) / end.true_airspeed
         end.name = self.name
         self.complete_flight_point(end)
diff --git a/src/fastoad/models/performances/mission/segments/hold.py b/src/fastoad/models/performances/mission/segments/hold.py
index 514a6c890..8f137c624 100644
--- a/src/fastoad/models/performances/mission/segments/hold.py
+++ b/src/fastoad/models/performances/mission/segments/hold.py
@@ -1,6 +1,6 @@
 """Class for simulating hold segment."""
 #  This file is part of FAST-OAD : A framework for rapid Overall Aircraft Design
-#  Copyright (C) 2021 ONERA & ISAE-SUPAERO
+#  Copyright (C) 2022 ONERA & ISAE-SUPAERO
 #  FAST is free software: you can redistribute it and/or modify
 #  it under the terms of the GNU General Public License as published by
 #  the Free Software Foundation, either version 3 of the License, or
@@ -12,10 +12,20 @@
 #  You should have received a copy of the GNU General Public License
 #  along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
-from .base import RegulatedThrustSegment, FixedDurationSegment
+from dataclasses import dataclass
 
+from .base import (
+    AbstractFixedDurationSegment,
+    AbstractRegulatedThrustSegment,
+)
 
-class HoldSegment(RegulatedThrustSegment, FixedDurationSegment, mission_file_keyword="holding"):
+
+@dataclass
+class HoldSegment(
+    AbstractRegulatedThrustSegment,
+    AbstractFixedDurationSegment,
+    mission_file_keyword="holding",
+):
     """
     Class for computing hold flight segment.
 
diff --git a/src/fastoad/models/performances/mission/segments/mass_input.py b/src/fastoad/models/performances/mission/segments/mass_input.py
new file mode 100644
index 000000000..9a978bd77
--- /dev/null
+++ b/src/fastoad/models/performances/mission/segments/mass_input.py
@@ -0,0 +1,38 @@
+"""Class for specifying input mass at "any" point in the mission."""
+#  This file is part of FAST-OAD : A framework for rapid Overall Aircraft Design
+#  Copyright (C) 2022 ONERA & ISAE-SUPAERO
+#  FAST is free software: you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation, either version 3 of the License, or
+#  (at your option) any later version.
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#  You should have received a copy of the GNU General Public License
+#  along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+from dataclasses import dataclass
+
+import pandas as pd
+
+from fastoad.model_base import FlightPoint
+from fastoad.models.performances.mission.segments.base import AbstractFlightSegment
+
+
+@dataclass
+class MassTargetSegment(AbstractFlightSegment, mission_file_keyword="mass_input"):
+    """
+    Class that simply sets a target mass.
+
+    :meth:`compute_from` returns a 1-row dataframe that is the start point with mass
+    set to provided target mass.
+
+    class:`~fastoad.models.performances.mission.base.FlightSequence` ensures that
+    mass is consistent for segments prior to this one.
+    """
+
+    def compute_from_start_to_target(self, start: FlightPoint, target: FlightPoint) -> pd.DataFrame:
+        start.mass = target.mass
+        self.complete_flight_point(start)
+        return pd.DataFrame([start])
diff --git a/src/fastoad/models/performances/mission/segments/speed_change.py b/src/fastoad/models/performances/mission/segments/speed_change.py
index 09d954855..11f8bf945 100644
--- a/src/fastoad/models/performances/mission/segments/speed_change.py
+++ b/src/fastoad/models/performances/mission/segments/speed_change.py
@@ -1,6 +1,6 @@
 """Classes for acceleration/deceleration segments."""
 #  This file is part of FAST-OAD : A framework for rapid Overall Aircraft Design
-#  Copyright (C) 2021 ONERA & ISAE-SUPAERO
+#  Copyright (C) 2022 ONERA & ISAE-SUPAERO
 #  FAST is free software: you can redistribute it and/or modify
 #  it under the terms of the GNU General Public License as published by
 #  the Free Software Foundation, either version 3 of the License, or
@@ -13,16 +13,18 @@
 #  along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 import logging
+from dataclasses import dataclass
 from typing import List, Tuple
 
 from fastoad.model_base import FlightPoint
-from .base import ManualThrustSegment
+from .base import AbstractManualThrustSegment
 from ..exceptions import FastFlightSegmentIncompleteFlightPoint
 
 _LOGGER = logging.getLogger(__name__)  # Logger for this module
 
 
-class SpeedChangeSegment(ManualThrustSegment, mission_file_keyword="speed_change"):
+@dataclass
+class SpeedChangeSegment(AbstractManualThrustSegment, mission_file_keyword="speed_change"):
     """
     Computes a flight path segment where speed is modified with no change in altitude.
 
@@ -30,13 +32,15 @@ class SpeedChangeSegment(ManualThrustSegment, mission_file_keyword="speed_change
     and mach.
     """
 
-    def get_distance_to_target(self, flight_points: List[FlightPoint]) -> float:
-        if self.target.true_airspeed is not None:
-            return self.target.true_airspeed - flight_points[-1].true_airspeed
-        if self.target.equivalent_airspeed is not None:
-            return self.target.equivalent_airspeed - flight_points[-1].equivalent_airspeed
-        if self.target.mach is not None:
-            return self.target.mach - flight_points[-1].mach
+    def get_distance_to_target(
+        self, flight_points: List[FlightPoint], target: FlightPoint
+    ) -> float:
+        if target.true_airspeed is not None:
+            return target.true_airspeed - flight_points[-1].true_airspeed
+        if target.equivalent_airspeed is not None:
+            return target.equivalent_airspeed - flight_points[-1].equivalent_airspeed
+        if target.mach is not None:
+            return target.mach - flight_points[-1].mach
 
         raise FastFlightSegmentIncompleteFlightPoint(
             "No valid target definition for altitude change."
diff --git a/src/fastoad/models/performances/mission/segments/start.py b/src/fastoad/models/performances/mission/segments/start.py
new file mode 100644
index 000000000..7846236b5
--- /dev/null
+++ b/src/fastoad/models/performances/mission/segments/start.py
@@ -0,0 +1,46 @@
+"""Class for mission start point."""
+#  This file is part of FAST-OAD : A framework for rapid Overall Aircraft Design
+#  Copyright (C) 2022 ONERA & ISAE-SUPAERO
+#  FAST is free software: you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation, either version 3 of the License, or
+#  (at your option) any later version.
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#  You should have received a copy of the GNU General Public License
+#  along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+from dataclasses import dataclass
+
+import pandas as pd
+
+from fastoad.model_base import FlightPoint
+from .base import AbstractFlightSegment
+from ..exceptions import FastFlightSegmentIncompleteFlightPoint
+
+
+@dataclass
+class Start(AbstractFlightSegment, mission_file_keyword="start"):
+    """
+    Provides a starting point for a mission.
+
+    :meth:`compute_from` will return only 1 flight points that matches the target.
+    """
+
+    def compute_from_start_to_target(self, start: FlightPoint, target: FlightPoint) -> pd.DataFrame:
+        target.name = self.name
+
+        if target.mass is None:
+            # If not setting the mass in the start point, the default value set in
+            # mission component will be used.
+            target.mass = start.mass
+
+        try:
+            self.complete_flight_point(target)
+        except FastFlightSegmentIncompleteFlightPoint:
+            target.true_airspeed = 0.0
+            self.complete_flight_point(target)
+
+        return pd.DataFrame([target])
diff --git a/src/fastoad/models/performances/mission/segments/taxi.py b/src/fastoad/models/performances/mission/segments/taxi.py
index 5bedf1e94..479a7d63f 100644
--- a/src/fastoad/models/performances/mission/segments/taxi.py
+++ b/src/fastoad/models/performances/mission/segments/taxi.py
@@ -1,6 +1,6 @@
 """Classes for Taxi sequences."""
 #  This file is part of FAST-OAD : A framework for rapid Overall Aircraft Design
-#  Copyright (C) 2021 ONERA & ISAE-SUPAERO
+#  Copyright (C) 2022 ONERA & ISAE-SUPAERO
 #  FAST is free software: you can redistribute it and/or modify
 #  it under the terms of the GNU General Public License as published by
 #  the Free Software Foundation, either version 3 of the License, or
@@ -12,20 +12,26 @@
 #  You should have received a copy of the GNU General Public License
 #  along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
-from copy import deepcopy
 from dataclasses import dataclass
 from typing import Tuple
 
 import pandas as pd
 
 from fastoad.model_base import FlightPoint
-from fastoad.models.performances.mission.segments.base import FixedDurationSegment
-from .base import ManualThrustSegment
+from fastoad.models.performances.mission.segments.base import (
+    AbstractFixedDurationSegment,
+)
+from .base import AbstractManualThrustSegment
 from ..polar import Polar
 
 
 @dataclass
-class TaxiSegment(ManualThrustSegment, FixedDurationSegment, mission_file_keyword="taxi"):
+class TaxiSegment(
+    AbstractManualThrustSegment,
+    AbstractFixedDurationSegment,
+    mission_file_keyword="taxi",
+    attribute_units=dict(true_airspeed="m/s"),
+):
     """
     Class for computing Taxi phases.
 
@@ -41,10 +47,10 @@ class TaxiSegment(ManualThrustSegment, FixedDurationSegment, mission_file_keywor
     def get_gamma_and_acceleration(self, flight_point: FlightPoint) -> Tuple[float, float]:
         return 0.0, 0.0
 
-    def compute_from(self, start: FlightPoint) -> pd.DataFrame:
-        new_start = deepcopy(start)
-        new_start.mach = None
-        new_start.equivalent_airspeed = None
-        new_start.true_airspeed = self.true_airspeed
+    def compute_from_start_to_target(self, start: FlightPoint, target: FlightPoint) -> pd.DataFrame:
+        start.mach = None
+        start.equivalent_airspeed = None
+        start.true_airspeed = self.true_airspeed
+        self.complete_flight_point(start)
 
-        return super().compute_from(new_start)
+        return super().compute_from_start_to_target(start, target)
diff --git a/src/fastoad/models/performances/mission/segments/transition.py b/src/fastoad/models/performances/mission/segments/transition.py
index 530aa7b06..21e52c935 100644
--- a/src/fastoad/models/performances/mission/segments/transition.py
+++ b/src/fastoad/models/performances/mission/segments/transition.py
@@ -1,6 +1,6 @@
 """Class for very simple transition in some flight phases."""
 #  This file is part of FAST-OAD : A framework for rapid Overall Aircraft Design
-#  Copyright (C) 2021 ONERA & ISAE-SUPAERO
+#  Copyright (C) 2022 ONERA & ISAE-SUPAERO
 #  FAST is free software: you can redistribute it and/or modify
 #  it under the terms of the GNU General Public License as published by
 #  the Free Software Foundation, either version 3 of the License, or
@@ -14,28 +14,29 @@
 
 from copy import deepcopy
 from dataclasses import dataclass
-from typing import List, Tuple
 
 import pandas as pd
 
 from fastoad.model_base import FlightPoint
-from fastoad.model_base.propulsion import IPropulsion
-from fastoad.models.performances.mission.polar import Polar
-from fastoad.models.performances.mission.segments.base import FlightSegment
+from fastoad.models.performances.mission.segments.base import (
+    AbstractFlightSegment,
+)
 
 
 @dataclass
-class DummyTransitionSegment(FlightSegment, mission_file_keyword="transition"):
+class DummyTransitionSegment(AbstractFlightSegment, mission_file_keyword="transition"):
     """
     Computes a transient flight part in a very quick and dummy way.
 
     :meth:`compute_from` will return only 2 or 3 flight points.
 
-    The second flight point is the end of transition and its mass is the start mass
-    multiplied by :attr:`mass_ratio`. Other parameters are equal to those provided in
-    :attr:`~fastoad.models.performances.mission.segments.base.FlightSegment.target`.
+    The second flight point is the end of transition. Its parameters are equal to those provided
+    in :attr:`~fastoad.models.performances.mission.segments.base.FlightSegment.target`.
 
-    If :attr:`reserve_mass_ratio` is non-zero, a third flight point, with parameters equal
+    There is an exception if target does not specify any mass (i.e. self.target.mass == 0). Then
+    the mass of the second flight point is the start mass multiplied by :attr:`mass_ratio`.
+
+    If :attr:`reserve_mass_ratio` is non-zero, a third flight point is added, with parameters equal
     to flight_point(2), except for mass where:
         mass(2) - reserve_mass_ratio * mass(3) = mass(3).
     In different words, mass(3) would be the Zero Fuel Weight (ZFW) and reserve can be
@@ -49,27 +50,14 @@ class DummyTransitionSegment(FlightSegment, mission_file_keyword="transition"):
     #: of segment.
     reserve_mass_ratio: float = 0.0
 
-    #: Unused
-    propulsion: IPropulsion = None
-
-    #: Unused
-    reference_area: float = 1.0
-
-    #: Unused
-    polar: Polar = None
-
-    def compute_from(self, start: FlightPoint) -> pd.DataFrame:
+    def compute_from_start_to_target(self, start: FlightPoint, target: FlightPoint) -> pd.DataFrame:
+        end = deepcopy(target)
+        end.name = self.name
 
-        self.complete_flight_point(start)
-        end = deepcopy(start)
+        if end.mass is None:
+            end.mass = start.mass * self.mass_ratio
 
-        end.mass = start.mass * self.mass_ratio
-        end.altitude = self.target.altitude
-        end.ground_distance = start.ground_distance + self.target.ground_distance
-        end.mach = self.target.mach
-        end.true_airspeed = self.target.true_airspeed
-        end.equivalent_airspeed = self.target.equivalent_airspeed
-        end.name = self.name
+        self.complete_flight_point_from(end, start)
         self.complete_flight_point(end)
 
         flight_points = [start, end]
@@ -80,14 +68,3 @@ def compute_from(self, start: FlightPoint) -> pd.DataFrame:
             flight_points.append(reserve)
 
         return pd.DataFrame(flight_points)
-
-    def get_gamma_and_acceleration(self, flight_point: FlightPoint) -> Tuple[float, float]:
-        return 0.0, 0.0
-
-    # As we overloaded self.compute_from(), next abstract method are not used.
-    # We just need to implement them for Python to be happy.
-    def get_distance_to_target(self, flight_points: List[FlightPoint]) -> float:
-        pass
-
-    def compute_propulsion(self, flight_point: FlightPoint):
-        pass
diff --git a/src/fastoad/openmdao/variables/variable.py b/src/fastoad/openmdao/variables/variable.py
index 6bec36325..8c69ba6a1 100644
--- a/src/fastoad/openmdao/variables/variable.py
+++ b/src/fastoad/openmdao/variables/variable.py
@@ -277,6 +277,32 @@ def is_input(self):
     def is_input(self, value):
         self.metadata["is_input"] = value
 
+    def get_openmdao_kwargs(self, keys: Iterable = None) -> dict:
+        """
+        Provides a dict usable as keyword args by OpenMDAO add_input()/add_output().
+
+        The dict keys will be the ones provided, or a default set if no keys are provided.
+
+        :param keys:
+        :return: the kwargs dict
+        """
+        if not keys:
+            keys = {
+                "name",
+                "val",
+                "units",
+                "desc",
+            }
+            if self.metadata["shape_by_conn"] or self.metadata["copy_shape"]:
+                keys.add("shape_by_conn")
+                keys.add("copy_shape")
+            else:
+                keys.add("shape")
+
+        kwargs = {key: self.metadata[key] for key in self.get_openmdao_keys() if key in keys}
+        kwargs["name"] = self.name
+        return kwargs
+
     def _set_default_shape(self):
         """Automatically sets shape if not set"""
         if self.metadata["shape"] is None:


Test patch:
diff --git a/src/fastoad/gui/tests/data/problem_outputs.xml b/src/fastoad/gui/tests/data/problem_outputs.xml
index 5463b8fa0..9884bb28b 100644
--- a/src/fastoad/gui/tests/data/problem_outputs.xml
+++ b/src/fastoad/gui/tests/data/problem_outputs.xml
@@ -1,6 +1,6 @@
 <!--
   ~ This file is part of FAST-OAD : A framework for rapid Overall Aircraft Design
-  ~ Copyright (C) 2021 ONERA & ISAE-SUPAERO
+  ~ Copyright (C) 2022 ONERA & ISAE-SUPAERO
   ~ FAST is free software: you can redistribute it and/or modify
   ~ it under the terms of the GNU General Public License as published by
   ~ the Free Software Foundation, either version 3 of the License, or
@@ -292,7 +292,7 @@
         <additional_fuel_capacity units="kg">1.9001079635927454e-05<!--fuel mass capacity of wing that exceeds sizing mission requirement--></additional_fuel_capacity>
         <max_payload units="kg">19608.0<!--max payload weight--></max_payload>
         <payload units="kg">13608.0<!--design payload weight--></payload>
-        <sizing_onboard_fuel_at_takeoff units="kg">20484.57180359991<!--consumed fuel mass during whole mission--></sizing_onboard_fuel_at_takeoff>
+        <sizing_onboard_fuel_at_input_weight units="kg">20484.57180359991<!--consumed fuel mass during whole mission--></sizing_onboard_fuel_at_input_weight>
 
         <CG>
           <aft>
diff --git a/src/fastoad/models/performances/mission/mission_definition/mission_builder/tests/__init__.py b/src/fastoad/models/performances/mission/mission_definition/mission_builder/tests/__init__.py
new file mode 100644
index 000000000..f519d3755
--- /dev/null
+++ b/src/fastoad/models/performances/mission/mission_definition/mission_builder/tests/__init__.py
@@ -0,0 +1,12 @@
+#  This file is part of FAST-OAD : A framework for rapid Overall Aircraft Design
+#  Copyright (C) 2022 ONERA & ISAE-SUPAERO
+#  FAST is free software: you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation, either version 3 of the License, or
+#  (at your option) any later version.
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#  You should have received a copy of the GNU General Public License
+#  along with this program.  If not, see <https://www.gnu.org/licenses/>.
diff --git a/src/fastoad/models/performances/mission/mission_definition/mission_builder/tests/data/mission.yml b/src/fastoad/models/performances/mission/mission_definition/mission_builder/tests/data/mission.yml
new file mode 100644
index 000000000..92cdbc6fc
--- /dev/null
+++ b/src/fastoad/models/performances/mission/mission_definition/mission_builder/tests/data/mission.yml
@@ -0,0 +1,205 @@
+phases:
+  test_phase:
+    parts:
+      - segment: test_segment_B
+        scalar_parameter: 42.0
+        vector_parameter_1:
+          value: some:static:array
+        vector_parameter_2: some:dynamic:array
+        vector_parameter_3:
+          value: [10.0, 20.0, 30.0]
+        vector_parameter_4: [100.0, 200.0, 300.0]
+        target:
+          altitude: 0.0
+  initial_climb:
+    engine_setting: takeoff
+    polar:
+      CL: [ 0.0, 0.5, 1.0 ]
+      CD: [ 0., 0.03, 0.12 ]
+    thrust_rate:
+      value: data:propulsion:initial_climb:thrust_rate
+      default: 1.0
+    time_step:
+      value: settings:mission:~
+      unit: s
+    parts:
+      - segment: altitude_change
+        target:
+          altitude:
+            value: 400.
+            unit: ft
+          equivalent_airspeed:
+            value: constant
+      - segment: speed_change
+        polar: data:aerodynamics:aircraft:takeoff
+        target:
+          equivalent_airspeed:
+            value: 250
+            unit: kn
+      - segment: altitude_change
+        polar:
+          CL: data:aerodynamics:aircraft:takeoff:CL
+          CD: data:aerodynamics:aircraft:takeoff:CD
+        target:
+          altitude:
+            value: 1500.
+            unit: ft
+          equivalent_airspeed:
+            value: constant
+  climb:
+    engine_setting: climb
+    polar: data:aerodynamics:aircraft:cruise
+    thrust_rate: data:propulsion:climb:thrust_rate
+    time_step:
+      value:
+      unit: s
+    parts:
+      - segment: altitude_change
+        target:
+          altitude:
+            value: 10000.
+            unit: ft
+          equivalent_airspeed:
+            value: constant
+      - segment: speed_change
+        target:
+          equivalent_airspeed:
+            value: 300.
+            unit: kn
+      - segment: altitude_change
+        target:
+          equivalent_airspeed: constant
+          mach: data:TLAR:cruise_mach
+      - segment: altitude_change
+        target:
+          mach: constant
+          altitude:
+            value: -20000.
+  diversion_climb:
+    engine_setting: climb
+    polar: data:aerodynamics:aircraft:cruise
+    thrust_rate: 0.93
+    time_step:
+      value: settings:mission~t_step
+      unit: s
+    parts:
+      - segment: altitude_change
+        target:
+          altitude:
+            value: 10000.
+            unit: ft
+          equivalent_airspeed: constant
+      - segment: speed_change
+        target:
+          equivalent_airspeed:
+            value: 300.
+            unit: kn
+      - segment: altitude_change
+        target:
+          altitude:
+            value: 22000.
+            unit: ft
+          equivalent_airspeed: constant
+  descent:
+    engine_setting:
+      value: idle
+    polar: data:aerodynamics:aircraft:cruise
+    thrust_rate: data:propulsion:descent:thrust_rate
+    parts:
+      - segment: altitude_change
+        target:
+          equivalent_airspeed:
+            value: 300
+            unit: kn
+          mach:
+            value: constant
+      - segment: altitude_change
+        target:
+          altitude:
+            value: 10000.
+            unit: ft
+          equivalent_airspeed:
+            value: constant
+      - segment: speed_change
+        target:
+          equivalent_airspeed:
+            value: 250.
+            unit: kn
+      - segment: altitude_change
+        target:
+          equivalent_airspeed:
+            value: constant
+          altitude: ~:final_altitude
+  holding:
+    parts:
+      - segment: holding
+        polar: data:aerodynamics:aircraft:cruise
+        target:
+          delta_time: ~duration
+  taxi_out:
+    parts:
+      - segment: taxi
+        thrust_rate: ~
+        true_airspeed: 0.
+        target:
+          delta_time: ~:duration
+          mass:
+            value: ~TOW
+            unit: kg
+  taxi_in:
+    thrust_rate: ~
+    parts:
+      - segment: taxi
+        true_airspeed: 0.
+        target:
+          delta_time: ~duration
+
+routes:
+  main:
+    range: ~
+    distance_accuracy: 500
+    climb_parts:
+      - phase: initial_climb
+      - phase: climb
+    cruise_part:
+      segment: optimal_cruise
+      engine_setting: cruise
+      polar: data:aerodynamics:aircraft:cruise
+    descent_parts:
+      - phase: descent
+
+  diversion:
+    range: ~
+    distance_accuracy:
+      value: 0.1
+      unit: km
+    climb_parts:
+      - phase: diversion_climb
+    cruise_part:
+      segment: cruise
+      engine_setting: cruise
+      polar: data:aerodynamics:aircraft:cruise
+    descent_parts:
+      - phase: descent
+
+missions:
+  sizing:
+    parts:
+      - route: main
+      - route: diversion
+      - phase: holding
+      - phase: taxi_in
+      - reserve:
+          ref: main
+          multiplier: 0.03
+  operational:
+    parts:
+      - phase: taxi_out
+      - route: main
+      - phase: taxi_in
+      - reserve:
+          ref: main
+          multiplier: 0.02
+  test:
+    parts:
+      - phase: test_phase
diff --git a/src/fastoad/models/performances/mission/mission_definition/mission_builder/tests/test_mission_builder.py b/src/fastoad/models/performances/mission/mission_definition/mission_builder/tests/test_mission_builder.py
new file mode 100644
index 000000000..0bcfa3fb5
--- /dev/null
+++ b/src/fastoad/models/performances/mission/mission_definition/mission_builder/tests/test_mission_builder.py
@@ -0,0 +1,2214 @@
+#  This file is part of FAST-OAD : A framework for rapid Overall Aircraft Design
+#  Copyright (C) 2022 ONERA & ISAE-SUPAERO
+#  FAST is free software: you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation, either version 3 of the License, or
+#  (at your option) any later version.
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#  You should have received a copy of the GNU General Public License
+#  along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+import os.path as pth
+from collections import OrderedDict
+from dataclasses import dataclass
+from unittest.mock import Mock
+
+import numpy as np
+import pandas as pd
+import pytest
+from numpy.testing import assert_allclose
+from scipy.constants import foot, knot
+
+from fastoad.model_base.datacls import MANDATORY_FIELD
+from fastoad.model_base.propulsion import IPropulsion
+from fastoad.models.performances.mission.base import FlightSequence
+from fastoad.models.performances.mission.segments.altitude_change import AltitudeChangeSegment
+from fastoad.models.performances.mission.segments.base import AbstractFlightSegment
+from fastoad.models.performances.mission.segments.hold import HoldSegment
+from fastoad.models.performances.mission.segments.speed_change import SpeedChangeSegment
+from fastoad.models.performances.mission.segments.taxi import TaxiSegment
+from fastoad.openmdao.variables import Variable
+from ..input_definition import InputDefinition
+from ..mission_builder import MissionBuilder
+from ...exceptions import FastMissionFileMissingMissionNameError
+from ...schema import MissionDefinition
+
+DATA_FOLDER_PATH = pth.join(pth.dirname(__file__), "data")
+
+
+@dataclass
+class TestSegment(AbstractFlightSegment, mission_file_keyword="test_segment_B"):
+    scalar_parameter: float = MANDATORY_FIELD
+    vector_parameter_1: np.ndarray = MANDATORY_FIELD
+    vector_parameter_2: np.ndarray = MANDATORY_FIELD
+    vector_parameter_3: np.ndarray = MANDATORY_FIELD
+    vector_parameter_4: np.ndarray = MANDATORY_FIELD
+
+    def compute_from_start_to_target(self, start, target) -> pd.DataFrame:
+        return pd.DataFrame([start])
+
+
+def test_input_definition_units():
+    inp1 = InputDefinition("anything", 1.0, input_unit="km")
+    assert inp1.value == 1.0
+
+    inp1.output_unit = "m"
+    assert inp1.value == 1000.0
+
+    # When parameter name is known, its default unit is automatically set.
+    inp2 = InputDefinition("ground_distance", 1.0, input_unit="km")
+    assert inp2.value == 1000.0
+    assert not inp2.is_relative
+
+    inp3 = InputDefinition("delta_time", 1.0, input_unit="h")
+    assert inp3.parameter_name == "time"
+    assert inp3.is_relative
+
+    assert inp3.value == 3600.0
+
+
+def test_initialization():
+    mission_builder = MissionBuilder(
+        pth.join(DATA_FOLDER_PATH, "mission.yml"),
+        propulsion=Mock(IPropulsion),
+        reference_area=100.0,
+    )
+
+    assert (
+        mission_builder._structure_builders["sizing"].structure
+        == _get_expected_structure()["sizing"]
+    )
+    assert (
+        mission_builder._structure_builders["operational"].structure
+        == _get_expected_structure()["operational"]
+    )
+    assert (
+        mission_builder._structure_builders["test"].structure == _get_expected_structure()["test"]
+    )
+
+
+def test_get_input_weight_variable_name():
+    mission_builder = MissionBuilder(
+        pth.join(DATA_FOLDER_PATH, "mission.yml"),
+        propulsion=Mock(IPropulsion),
+        reference_area=100.0,
+    )
+    assert mission_builder.get_input_weight_variable_name("sizing") is None
+    assert (
+        mission_builder.get_input_weight_variable_name("operational")
+        == "data:mission:operational:taxi_out:TOW"
+    )
+
+
+def test_inputs():
+    mission_builder = MissionBuilder(
+        pth.join(DATA_FOLDER_PATH, "mission.yml"),
+        propulsion=Mock(IPropulsion),
+        reference_area=100.0,
+    )
+    with pytest.raises(FastMissionFileMissingMissionNameError):
+        mission_builder.get_input_variables()
+
+    assert set(mission_builder.get_input_variables("sizing")) == {
+        Variable("data:TLAR:cruise_mach", val=np.nan),
+        Variable("data:aerodynamics:aircraft:cruise:CD", val=np.nan, shape_by_conn=True),
+        Variable("data:aerodynamics:aircraft:cruise:CL", val=np.nan, shape_by_conn=True),
+        Variable("data:aerodynamics:aircraft:takeoff:CD", val=np.nan, shape_by_conn=True),
+        Variable("data:aerodynamics:aircraft:takeoff:CL", val=np.nan, shape_by_conn=True),
+        Variable("data:mission:sizing:diversion:descent:final_altitude", val=np.nan, units="m"),
+        Variable("data:mission:sizing:diversion:range", val=np.nan, units="m"),
+        Variable("data:mission:sizing:holding:duration", val=np.nan, units="s"),
+        Variable("data:mission:sizing:main:descent:final_altitude", val=np.nan, units="m"),
+        Variable("data:mission:sizing:main:range", val=np.nan, units="m"),
+        Variable("data:mission:sizing:taxi_in:duration", val=np.nan, units="s"),
+        Variable("data:mission:sizing:taxi_in:thrust_rate", val=np.nan),
+        Variable("data:propulsion:initial_climb:thrust_rate", val=1.0),
+        Variable("data:propulsion:climb:thrust_rate", val=np.nan),
+        Variable("data:propulsion:descent:thrust_rate", val=np.nan),
+        Variable("data:mission:sizing:main:climb:time_step", val=np.nan, units="s"),
+        Variable("settings:mission:sizing:main:initial_climb:time_step", val=np.nan, units="s"),
+        Variable("settings:mission:sizing:diversion:diversion_climb:t_step", val=np.nan, units="s"),
+    }
+
+    assert set(mission_builder.get_input_variables("operational")) == {
+        Variable("data:TLAR:cruise_mach", val=np.nan),
+        Variable("data:aerodynamics:aircraft:cruise:CD", val=np.nan, shape_by_conn=True),
+        Variable("data:aerodynamics:aircraft:cruise:CL", val=np.nan, shape_by_conn=True),
+        Variable("data:aerodynamics:aircraft:takeoff:CD", val=np.nan, shape_by_conn=True),
+        Variable("data:aerodynamics:aircraft:takeoff:CL", val=np.nan, shape_by_conn=True),
+        Variable("data:mission:operational:main:descent:final_altitude", val=np.nan, units="m"),
+        Variable("data:mission:operational:main:range", val=np.nan, units="m"),
+        Variable("data:mission:operational:taxi_in:duration", val=np.nan, units="s"),
+        Variable("data:mission:operational:taxi_in:thrust_rate", val=np.nan),
+        Variable("data:mission:operational:taxi_out:duration", val=np.nan, units="s"),
+        Variable("data:mission:operational:taxi_out:thrust_rate", val=np.nan),
+        Variable("data:mission:operational:taxi_out:TOW", val=np.nan, units="kg"),
+        Variable("data:propulsion:initial_climb:thrust_rate", val=1.0),
+        Variable("data:propulsion:climb:thrust_rate", val=np.nan),
+        Variable("data:propulsion:descent:thrust_rate", val=np.nan),
+        Variable("data:mission:operational:main:climb:time_step", val=np.nan, units="s"),
+        Variable(
+            "settings:mission:operational:main:initial_climb:time_step", val=np.nan, units="s"
+        ),
+    }
+
+
+def test_build():
+    mission_definition = MissionDefinition(pth.join(DATA_FOLDER_PATH, "mission.yml"))
+    mission_builder = MissionBuilder(
+        mission_definition, propulsion=Mock(IPropulsion), reference_area=100.0
+    )
+
+    cl = np.linspace(0.0, 1.0, 11)
+    cd = 0.5 * cl ** 2
+
+    inputs = {
+        "data:TLAR:cruise_mach": 0.78,
+        "data:mission:sizing:main:range": 8000.0e3,
+        "data:mission:sizing:diversion:range": 926.0e3,
+        "initial_climb:final_altitude": 500,
+        "initial_climb:final_equivalent_airspeed": 130.0,
+        "data:aerodynamics:aircraft:cruise:CD": cd,
+        "data:aerodynamics:aircraft:cruise:CL": cl,
+        "data:aerodynamics:aircraft:low_speed:CD": cd,
+        "data:aerodynamics:aircraft:low_speed:CL": cl,
+        "data:aerodynamics:aircraft:takeoff:CD": cd,
+        "data:aerodynamics:aircraft:takeoff:CL": cl,
+        "data:mission:sizing:climb:thrust_rate": 0.9,
+        "data:mission:sizing:descent:thrust_rate": 0.5,
+        "data:mission:sizing:holding:duration": 2000.0,
+        "data:mission:sizing:taxi_in:duration": 300.0,
+        "data:mission:sizing:taxi_in:thrust_rate": 0.5,
+    }
+    mission = mission_builder.build(inputs, mission_name="sizing")
+
+    assert len(mission.flight_sequence) == 4
+    assert mission.flight_sequence[0].name == "sizing:main"
+    assert mission.flight_sequence[1].name == "sizing:diversion"
+    assert mission.flight_sequence[2].name == "sizing:holding"
+    assert mission.flight_sequence[3].name == "sizing:taxi_in"
+
+    main_route = mission.flight_sequence[0]
+    assert isinstance(main_route, FlightSequence)
+    assert len(main_route.flight_sequence) == 4
+    assert main_route.distance_accuracy == 500.0
+    assert main_route.flight_sequence[0].name == "sizing:main:initial_climb"
+    assert main_route.flight_sequence[1].name == "sizing:main:climb"
+    assert main_route.flight_sequence[2].name == "sizing:main:cruise"
+    assert main_route.flight_sequence[3].name == "sizing:main:descent"
+
+    initial_climb = main_route.flight_sequence[0]
+    assert isinstance(initial_climb, FlightSequence)
+    assert len(initial_climb.flight_sequence) == 3
+    # Here we test that phase parameter is distributed among segments AND that default
+    # value of variable is used.
+    assert_allclose([segment.thrust_rate for segment in initial_climb.flight_sequence], 1.0)
+
+    climb1 = initial_climb.flight_sequence[0]
+    assert isinstance(climb1, AltitudeChangeSegment)
+    assert climb1.target.altitude == pytest.approx(400 * foot)
+    assert climb1.target.equivalent_airspeed == "constant"
+    assert_allclose(climb1.polar.definition_cl, [0.0, 0.5, 1.0])
+    assert_allclose(climb1.polar.cd(), [0.0, 0.03, 0.12])
+
+    acceleration = initial_climb.flight_sequence[1]
+    assert isinstance(acceleration, SpeedChangeSegment)
+    assert acceleration.target.equivalent_airspeed == pytest.approx(250 * knot)
+    assert_allclose(acceleration.polar.definition_cl, cl)
+    assert_allclose(acceleration.polar.cd(), cd)
+
+    climb2 = initial_climb.flight_sequence[2]
+    assert isinstance(climb2, AltitudeChangeSegment)
+    assert_allclose(climb2.polar.definition_cl, cl)
+    assert_allclose(climb2.polar.cd(), cd)
+
+    holding_phase = mission.flight_sequence[2]
+    assert isinstance(holding_phase, FlightSequence)
+    assert len(holding_phase.flight_sequence) == 1
+    holding = holding_phase.flight_sequence[0]
+    assert isinstance(holding, HoldSegment)
+
+    taxi_in_phase = mission.flight_sequence[3]
+    assert isinstance(taxi_in_phase, FlightSequence)
+    assert len(taxi_in_phase.flight_sequence) == 1
+    taxi_in = taxi_in_phase.flight_sequence[0]
+    assert isinstance(taxi_in, TaxiSegment)
+
+    test_inputs = {
+        "some:static:array": [1.0, 2.0, 3.0],
+        "some:dynamic:array": np.linspace(0.0, 5.0, 6),
+    }
+    test_mission = mission_builder.build(test_inputs, mission_name="test")
+    assert len(test_mission.flight_sequence) == 1
+    assert isinstance(test_mission.flight_sequence[0], FlightSequence)
+    assert len(test_mission.flight_sequence[0].flight_sequence) == 1
+    segment = test_mission.flight_sequence[0].flight_sequence[0]
+    assert isinstance(segment, TestSegment)
+    assert segment.scalar_parameter == 42.0
+    assert_allclose(segment.vector_parameter_1, [1.0, 2.0, 3.0])
+    assert_allclose(segment.vector_parameter_2, [0.0, 1.0, 2.0, 3.0, 4.0, 5.0])
+    assert_allclose(segment.vector_parameter_3, [10.0, 20.0, 30.0])
+
+
+def test_get_route_ranges():
+    mission_definition = MissionDefinition(pth.join(DATA_FOLDER_PATH, "mission.yml"))
+    mission_builder = MissionBuilder(
+        mission_definition, propulsion=Mock(IPropulsion), reference_area=100.0
+    )
+
+    cl = np.linspace(0.0, 1.0, 11)
+    cd = 0.5 * cl ** 2
+
+    inputs = {
+        "data:TLAR:cruise_mach": 0.78,
+        "data:mission:sizing:main:range": 8000.0e3,
+        "data:mission:operational:main:range": 500.0e3,
+        "data:mission:sizing:diversion:range": 926.0e3,
+        "initial_climb:final_altitude": 500,
+        "initial_climb:final_equivalent_airspeed": 130.0,
+        "data:aerodynamics:aircraft:cruise:CD": cd,
+        "data:aerodynamics:aircraft:cruise:CL": cl,
+        "data:aerodynamics:aircraft:low_speed:CD": cd,
+        "data:aerodynamics:aircraft:low_speed:CL": cl,
+        "data:aerodynamics:aircraft:takeoff:CD": cd,
+        "data:aerodynamics:aircraft:takeoff:CL": cl,
+        "data:mission:sizing:climb:thrust_rate": 0.9,
+        "data:mission:sizing:descent:thrust_rate": 0.5,
+        "data:mission:sizing:holding:duration": 2000.0,
+        "data:mission:operational:taxi_out:duration": 300.0,
+        "data:mission:operational:taxi_out:thrust_rate": 0.5,
+        "data:mission:operational:taxi_in:duration": 300.0,
+        "data:mission:operational:taxi_in:thrust_rate": 0.5,
+        "data:mission:sizing:taxi_in:duration": 300.0,
+        "data:mission:sizing:taxi_in:thrust_rate": 0.5,
+    }
+
+    assert_allclose(mission_builder.get_route_ranges(inputs, "sizing"), [8000.0e3, 926.0e3])
+    assert_allclose(mission_builder.get_route_ranges(inputs, "operational"), [500.0e3])
+
+
+def test_get_reserve():
+    mission_builder = MissionBuilder(
+        pth.join(DATA_FOLDER_PATH, "mission.yml"),
+        propulsion=Mock(IPropulsion),
+        reference_area=100.0,
+    )
+
+    flight_points = pd.DataFrame(
+        dict(
+            mass=[70000, 65000, 55000, 45000],
+            name=[
+                "data:mission:sizing:main:start",
+                "data:mission:sizing:main:climb",
+                "data:mission:sizing:other:start",
+                "data:mission:sizing:other:climb",
+            ],
+        )
+    )
+    assert_allclose(mission_builder.get_reserve(flight_points, "sizing"), 5000 * 0.03)
+
+    flight_points.name = [
+        "data:mission:sizing:main:start",
+        "data:mission:sizing:main:climb",
+        "data:mission:sizing:main:cruise",
+        "data:mission:sizing:main:cruise",
+    ]
+    assert_allclose(mission_builder.get_reserve(flight_points, "sizing"), 25000 * 0.03)
+
+
+def _get_expected_structure():
+    # We have to use repr() to provide comparison at InputDefinition fields that are
+    # not settable in constructor.
+    # This is very inconvenient to read, but it will hopefully help to locate
+    # discrepancies that would occur.
+    return {
+        "operational": OrderedDict(
+            [
+                (
+                    "parts",
+                    [
+                        {
+                            "name": "operational:taxi_out",
+                            "parts": [
+                                {
+                                    "name": "operational:taxi_out",
+                                    "segment_type": "taxi",
+                                    "target": {
+                                        "delta_time": InputDefinition(
+                                            parameter_name="time",
+                                            input_value=None,
+                                            input_unit="s",
+                                            default_value=np.nan,
+                                            is_relative=True,
+                                            part_identifier="operational:taxi_out",
+                                            use_opposite=False,
+                                            variable_name="data:mission:operational:taxi_out:duration",
+                                        ),
+                                        "mass": InputDefinition(
+                                            parameter_name="mass",
+                                            input_value=None,
+                                            input_unit="kg",
+                                            default_value=np.nan,
+                                            is_relative=False,
+                                            part_identifier="operational:taxi_out",
+                                            use_opposite=False,
+                                            variable_name="data:mission:operational:taxi_out:TOW",
+                                        ),
+                                    },
+                                    "thrust_rate": InputDefinition(
+                                        parameter_name="thrust_rate",
+                                        input_value=None,
+                                        input_unit=None,
+                                        default_value=np.nan,
+                                        is_relative=False,
+                                        part_identifier="operational:taxi_out",
+                                        use_opposite=False,
+                                        variable_name="data:mission:operational:taxi_out:thrust_rate",
+                                    ),
+                                    "true_airspeed": InputDefinition(
+                                        parameter_name="true_airspeed",
+                                        input_value=0.0,
+                                        input_unit="m/s",
+                                        default_value=np.nan,
+                                        is_relative=False,
+                                        part_identifier="operational:taxi_out",
+                                        use_opposite=False,
+                                        variable_name=None,
+                                    ),
+                                    "type": "segment",
+                                }
+                            ],
+                            "type": "phase",
+                        },
+                        {
+                            "climb_parts": [
+                                {
+                                    "engine_setting": InputDefinition(
+                                        parameter_name="engine_setting",
+                                        input_value="takeoff",
+                                        input_unit=None,
+                                        default_value=np.nan,
+                                        is_relative=False,
+                                        part_identifier="operational:main:initial_climb",
+                                        use_opposite=False,
+                                        variable_name=None,
+                                    ),
+                                    "name": "operational:main:initial_climb",
+                                    "parts": [
+                                        {
+                                            "name": "operational:main:initial_climb",
+                                            "segment_type": "altitude_change",
+                                            "target": {
+                                                "altitude": InputDefinition(
+                                                    parameter_name="altitude",
+                                                    input_value=400.0,
+                                                    input_unit="ft",
+                                                    default_value=np.nan,
+                                                    is_relative=False,
+                                                    part_identifier="operational:main:initial_climb",
+                                                    use_opposite=False,
+                                                    variable_name=None,
+                                                ),
+                                                "equivalent_airspeed": InputDefinition(
+                                                    parameter_name="equivalent_airspeed",
+                                                    input_value="constant",
+                                                    input_unit="m/s",
+                                                    default_value=np.nan,
+                                                    is_relative=False,
+                                                    part_identifier="operational:main:initial_climb",
+                                                    use_opposite=False,
+                                                    variable_name=None,
+                                                ),
+                                            },
+                                            "type": "segment",
+                                        },
+                                        {
+                                            "name": "operational:main:initial_climb",
+                                            "polar": OrderedDict(
+                                                [
+                                                    (
+                                                        "CL",
+                                                        InputDefinition(
+                                                            parameter_name="CL",
+                                                            input_value=None,
+                                                            input_unit=None,
+                                                            default_value=np.nan,
+                                                            is_relative=False,
+                                                            part_identifier="operational:main:initial_climb",
+                                                            use_opposite=False,
+                                                            variable_name="data:aerodynamics:aircraft:takeoff:CL",
+                                                            shape_by_conn=True,
+                                                        ),
+                                                    ),
+                                                    (
+                                                        "CD",
+                                                        InputDefinition(
+                                                            parameter_name="CD",
+                                                            input_value=None,
+                                                            input_unit=None,
+                                                            default_value=np.nan,
+                                                            is_relative=False,
+                                                            part_identifier="operational:main:initial_climb",
+                                                            use_opposite=False,
+                                                            variable_name="data:aerodynamics:aircraft:takeoff:CD",
+                                                            shape_by_conn=True,
+                                                        ),
+                                                    ),
+                                                ]
+                                            ),
+                                            "segment_type": "speed_change",
+                                            "target": {
+                                                "equivalent_airspeed": InputDefinition(
+                                                    parameter_name="equivalent_airspeed",
+                                                    input_value=250,
+                                                    input_unit="kn",
+                                                    default_value=np.nan,
+                                                    is_relative=False,
+                                                    part_identifier="operational:main:initial_climb",
+                                                    use_opposite=False,
+                                                    variable_name=None,
+                                                )
+                                            },
+                                            "type": "segment",
+                                        },
+                                        {
+                                            "name": "operational:main:initial_climb",
+                                            "polar": {
+                                                "CD": InputDefinition(
+                                                    parameter_name="CD",
+                                                    input_value=None,
+                                                    input_unit=None,
+                                                    default_value=np.nan,
+                                                    is_relative=False,
+                                                    part_identifier="operational:main:initial_climb",
+                                                    use_opposite=False,
+                                                    variable_name="data:aerodynamics:aircraft:takeoff:CD",
+                                                    shape_by_conn=True,
+                                                ),
+                                                "CL": InputDefinition(
+                                                    parameter_name="CL",
+                                                    input_value=None,
+                                                    input_unit=None,
+                                                    default_value=np.nan,
+                                                    is_relative=False,
+                                                    part_identifier="operational:main:initial_climb",
+                                                    use_opposite=False,
+                                                    variable_name="data:aerodynamics:aircraft:takeoff:CL",
+                                                    shape_by_conn=True,
+                                                ),
+                                            },
+                                            "segment_type": "altitude_change",
+                                            "target": {
+                                                "altitude": InputDefinition(
+                                                    parameter_name="altitude",
+                                                    input_value=1500.0,
+                                                    input_unit="ft",
+                                                    default_value=np.nan,
+                                                    is_relative=False,
+                                                    part_identifier="operational:main:initial_climb",
+                                                    use_opposite=False,
+                                                    variable_name=None,
+                                                ),
+                                                "equivalent_airspeed": InputDefinition(
+                                                    parameter_name="equivalent_airspeed",
+                                                    input_value="constant",
+                                                    input_unit="m/s",
+                                                    default_value=np.nan,
+                                                    is_relative=False,
+                                                    part_identifier="operational:main:initial_climb",
+                                                    use_opposite=False,
+                                                    variable_name=None,
+                                                ),
+                                            },
+                                            "type": "segment",
+                                        },
+                                    ],
+                                    "polar": {
+                                        "CD": InputDefinition(
+                                            parameter_name="CD",
+                                            input_value=[0.0, 0.03, 0.12],
+                                            input_unit=None,
+                                            default_value=np.nan,
+                                            is_relative=False,
+                                            part_identifier="operational:main:initial_climb",
+                                            use_opposite=False,
+                                            variable_name=None,
+                                        ),
+                                        "CL": InputDefinition(
+                                            parameter_name="CL",
+                                            input_value=[0.0, 0.5, 1.0],
+                                            input_unit=None,
+                                            default_value=np.nan,
+                                            is_relative=False,
+                                            part_identifier="operational:main:initial_climb",
+                                            use_opposite=False,
+                                            variable_name=None,
+                                        ),
+                                    },
+                                    "thrust_rate": InputDefinition(
+                                        parameter_name="thrust_rate",
+                                        input_value=None,
+                                        input_unit=None,
+                                        default_value=1.0,
+                                        is_relative=False,
+                                        part_identifier="operational:main:initial_climb",
+                                        use_opposite=False,
+                                        variable_name="data:propulsion:initial_climb:thrust_rate",
+                                    ),
+                                    "time_step": InputDefinition(
+                                        parameter_name="time_step",
+                                        input_value=None,
+                                        input_unit="s",
+                                        default_value=np.nan,
+                                        is_relative=False,
+                                        part_identifier="operational:main:initial_climb",
+                                        use_opposite=False,
+                                        variable_name="settings:mission:operational:main:initial_climb:time_step",
+                                    ),
+                                    "type": "phase",
+                                },
+                                {
+                                    "engine_setting": InputDefinition(
+                                        parameter_name="engine_setting",
+                                        input_value="climb",
+                                        input_unit=None,
+                                        default_value=np.nan,
+                                        is_relative=False,
+                                        part_identifier="operational:main:climb",
+                                        use_opposite=False,
+                                        variable_name=None,
+                                    ),
+                                    "name": "operational:main:climb",
+                                    "parts": [
+                                        {
+                                            "name": "operational:main:climb",
+                                            "segment_type": "altitude_change",
+                                            "target": {
+                                                "altitude": InputDefinition(
+                                                    parameter_name="altitude",
+                                                    input_value=10000.0,
+                                                    input_unit="ft",
+                                                    default_value=np.nan,
+                                                    is_relative=False,
+                                                    part_identifier="operational:main:climb",
+                                                    use_opposite=False,
+                                                    variable_name=None,
+                                                ),
+                                                "equivalent_airspeed": InputDefinition(
+                                                    parameter_name="equivalent_airspeed",
+                                                    input_value="constant",
+                                                    input_unit="m/s",
+                                                    default_value=np.nan,
+                                                    is_relative=False,
+                                                    part_identifier="operational:main:climb",
+                                                    use_opposite=False,
+                                                    variable_name=None,
+                                                ),
+                                            },
+                                            "type": "segment",
+                                        },
+                                        {
+                                            "name": "operational:main:climb",
+                                            "segment_type": "speed_change",
+                                            "target": {
+                                                "equivalent_airspeed": InputDefinition(
+                                                    parameter_name="equivalent_airspeed",
+                                                    input_value=300.0,
+                                                    input_unit="kn",
+                                                    default_value=np.nan,
+                                                    is_relative=False,
+                                                    part_identifier="operational:main:climb",
+                                                    use_opposite=False,
+                                                    variable_name=None,
+                                                )
+                                            },
+                                            "type": "segment",
+                                        },
+                                        {
+                                            "name": "operational:main:climb",
+                                            "segment_type": "altitude_change",
+                                            "target": {
+                                                "equivalent_airspeed": InputDefinition(
+                                                    parameter_name="equivalent_airspeed",
+                                                    input_value="constant",
+                                                    input_unit="m/s",
+                                                    default_value=np.nan,
+                                                    is_relative=False,
+                                                    part_identifier="operational:main:climb",
+                                                    use_opposite=False,
+                                                    variable_name=None,
+                                                ),
+                                                "mach": InputDefinition(
+                                                    parameter_name="mach",
+                                                    input_value=None,
+                                                    input_unit=None,
+                                                    default_value=np.nan,
+                                                    is_relative=False,
+                                                    part_identifier="operational:main:climb",
+                                                    use_opposite=False,
+                                                    variable_name="data:TLAR:cruise_mach",
+                                                ),
+                                            },
+                                            "type": "segment",
+                                        },
+                                        {
+                                            "name": "operational:main:climb",
+                                            "segment_type": "altitude_change",
+                                            "target": {
+                                                "altitude": InputDefinition(
+                                                    parameter_name="altitude",
+                                                    input_value=-20000.0,
+                                                    input_unit="m",
+                                                    default_value=np.nan,
+                                                    is_relative=False,
+                                                    part_identifier="operational:main:climb",
+                                                    use_opposite=False,
+                                                    variable_name=None,
+                                                ),
+                                                "mach": InputDefinition(
+                                                    parameter_name="mach",
+                                                    input_value="constant",
+                                                    input_unit=None,
+                                                    default_value=np.nan,
+                                                    is_relative=False,
+                                                    part_identifier="operational:main:climb",
+                                                    use_opposite=False,
+                                                    variable_name=None,
+                                                ),
+                                            },
+                                            "type": "segment",
+                                        },
+                                    ],
+                                    "polar": OrderedDict(
+                                        [
+                                            (
+                                                "CL",
+                                                InputDefinition(
+                                                    parameter_name="CL",
+                                                    input_value=None,
+                                                    input_unit=None,
+                                                    default_value=np.nan,
+                                                    is_relative=False,
+                                                    part_identifier="operational:main:climb",
+                                                    use_opposite=False,
+                                                    variable_name="data:aerodynamics:aircraft:cruise:CL",
+                                                    shape_by_conn=True,
+                                                ),
+                                            ),
+                                            (
+                                                "CD",
+                                                InputDefinition(
+                                                    parameter_name="CD",
+                                                    input_value=None,
+                                                    input_unit=None,
+                                                    default_value=np.nan,
+                                                    is_relative=False,
+                                                    part_identifier="operational:main:climb",
+                                                    use_opposite=False,
+                                                    variable_name="data:aerodynamics:aircraft:cruise:CD",
+                                                    shape_by_conn=True,
+                                                ),
+                                            ),
+                                        ]
+                                    ),
+                                    "thrust_rate": InputDefinition(
+                                        parameter_name="thrust_rate",
+                                        input_value=None,
+                                        input_unit=None,
+                                        default_value=np.nan,
+                                        is_relative=False,
+                                        part_identifier="operational:main:climb",
+                                        use_opposite=False,
+                                        variable_name="data:propulsion:climb:thrust_rate",
+                                    ),
+                                    "time_step": InputDefinition(
+                                        parameter_name="time_step",
+                                        input_value=None,
+                                        input_unit="s",
+                                        default_value=np.nan,
+                                        is_relative=False,
+                                        part_identifier="operational:main:climb",
+                                        use_opposite=False,
+                                        variable_name="data:mission:operational:main:climb:time_step",
+                                    ),
+                                    "type": "phase",
+                                },
+                            ],
+                            "cruise_part": {
+                                "engine_setting": InputDefinition(
+                                    parameter_name="engine_setting",
+                                    input_value="cruise",
+                                    input_unit=None,
+                                    default_value=np.nan,
+                                    is_relative=False,
+                                    part_identifier="operational:main:cruise",
+                                    use_opposite=False,
+                                    variable_name=None,
+                                ),
+                                "name": "operational:main:cruise",
+                                "polar": OrderedDict(
+                                    [
+                                        (
+                                            "CL",
+                                            InputDefinition(
+                                                parameter_name="CL",
+                                                input_value=None,
+                                                input_unit=None,
+                                                default_value=np.nan,
+                                                is_relative=False,
+                                                part_identifier="operational:main:cruise",
+                                                use_opposite=False,
+                                                variable_name="data:aerodynamics:aircraft:cruise:CL",
+                                                shape_by_conn=True,
+                                            ),
+                                        ),
+                                        (
+                                            "CD",
+                                            InputDefinition(
+                                                parameter_name="CD",
+                                                input_value=None,
+                                                input_unit=None,
+                                                default_value=np.nan,
+                                                is_relative=False,
+                                                part_identifier="operational:main:cruise",
+                                                use_opposite=False,
+                                                variable_name="data:aerodynamics:aircraft:cruise:CD",
+                                                shape_by_conn=True,
+                                            ),
+                                        ),
+                                    ]
+                                ),
+                                "segment_type": "optimal_cruise",
+                                "type": "segment",
+                            },
+                            "descent_parts": [
+                                {
+                                    "engine_setting": InputDefinition(
+                                        parameter_name="engine_setting",
+                                        input_value="idle",
+                                        input_unit=None,
+                                        default_value=np.nan,
+                                        is_relative=False,
+                                        part_identifier="operational:main:descent",
+                                        use_opposite=False,
+                                        variable_name=None,
+                                    ),
+                                    "name": "operational:main:descent",
+                                    "parts": [
+                                        {
+                                            "name": "operational:main:descent",
+                                            "segment_type": "altitude_change",
+                                            "target": {
+                                                "equivalent_airspeed": InputDefinition(
+                                                    parameter_name="equivalent_airspeed",
+                                                    input_value=300,
+                                                    input_unit="kn",
+                                                    default_value=np.nan,
+                                                    is_relative=False,
+                                                    part_identifier="operational:main:descent",
+                                                    use_opposite=False,
+                                                    variable_name=None,
+                                                ),
+                                                "mach": InputDefinition(
+                                                    parameter_name="mach",
+                                                    input_value="constant",
+                                                    input_unit=None,
+                                                    default_value=np.nan,
+                                                    is_relative=False,
+                                                    part_identifier="operational:main:descent",
+                                                    use_opposite=False,
+                                                    variable_name=None,
+                                                ),
+                                            },
+                                            "type": "segment",
+                                        },
+                                        {
+                                            "name": "operational:main:descent",
+                                            "segment_type": "altitude_change",
+                                            "target": {
+                                                "altitude": InputDefinition(
+                                                    parameter_name="altitude",
+                                                    input_value=10000.0,
+                                                    input_unit="ft",
+                                                    default_value=np.nan,
+                                                    is_relative=False,
+                                                    part_identifier="operational:main:descent",
+                                                    use_opposite=False,
+                                                    variable_name=None,
+                                                ),
+                                                "equivalent_airspeed": InputDefinition(
+                                                    parameter_name="equivalent_airspeed",
+                                                    input_value="constant",
+                                                    input_unit="m/s",
+                                                    default_value=np.nan,
+                                                    is_relative=False,
+                                                    part_identifier="operational:main:descent",
+                                                    use_opposite=False,
+                                                    variable_name=None,
+                                                ),
+                                            },
+                                            "type": "segment",
+                                        },
+                                        {
+                                            "name": "operational:main:descent",
+                                            "segment_type": "speed_change",
+                                            "target": {
+                                                "equivalent_airspeed": InputDefinition(
+                                                    parameter_name="equivalent_airspeed",
+                                                    input_value=250.0,
+                                                    input_unit="kn",
+                                                    default_value=np.nan,
+                                                    is_relative=False,
+                                                    part_identifier="operational:main:descent",
+                                                    use_opposite=False,
+                                                    variable_name=None,
+                                                )
+                                            },
+                                            "type": "segment",
+                                        },
+                                        {
+                                            "name": "operational:main:descent",
+                                            "segment_type": "altitude_change",
+                                            "target": {
+                                                "altitude": InputDefinition(
+                                                    parameter_name="altitude",
+                                                    input_value=None,
+                                                    input_unit="m",
+                                                    default_value=np.nan,
+                                                    is_relative=False,
+                                                    part_identifier="operational:main:descent",
+                                                    use_opposite=False,
+                                                    variable_name="data:mission:operational:main:descent:final_altitude",
+                                                ),
+                                                "equivalent_airspeed": InputDefinition(
+                                                    parameter_name="equivalent_airspeed",
+                                                    input_value="constant",
+                                                    input_unit="m/s",
+                                                    default_value=np.nan,
+                                                    is_relative=False,
+                                                    part_identifier="operational:main:descent",
+                                                    use_opposite=False,
+                                                    variable_name=None,
+                                                ),
+                                            },
+                                            "type": "segment",
+                                        },
+                                    ],
+                                    "polar": OrderedDict(
+                                        [
+                                            (
+                                                "CL",
+                                                InputDefinition(
+                                                    parameter_name="CL",
+                                                    input_value=None,
+                                                    input_unit=None,
+                                                    default_value=np.nan,
+                                                    is_relative=False,
+                                                    part_identifier="operational:main:descent",
+                                                    use_opposite=False,
+                                                    variable_name="data:aerodynamics:aircraft:cruise:CL",
+                                                    shape_by_conn=True,
+                                                ),
+                                            ),
+                                            (
+                                                "CD",
+                                                InputDefinition(
+                                                    parameter_name="CD",
+                                                    input_value=None,
+                                                    input_unit=None,
+                                                    default_value=np.nan,
+                                                    is_relative=False,
+                                                    part_identifier="operational:main:descent",
+                                                    use_opposite=False,
+                                                    variable_name="data:aerodynamics:aircraft:cruise:CD",
+                                                    shape_by_conn=True,
+                                                ),
+                                            ),
+                                        ]
+                                    ),
+                                    "thrust_rate": InputDefinition(
+                                        parameter_name="thrust_rate",
+                                        input_value=None,
+                                        input_unit=None,
+                                        default_value=np.nan,
+                                        is_relative=False,
+                                        part_identifier="operational:main:descent",
+                                        use_opposite=False,
+                                        variable_name="data:propulsion:descent:thrust_rate",
+                                    ),
+                                    "type": "phase",
+                                }
+                            ],
+                            "distance_accuracy": InputDefinition(
+                                parameter_name="distance_accuracy",
+                                input_value=500,
+                                input_unit="m",
+                                default_value=np.nan,
+                                is_relative=False,
+                                part_identifier="operational:main",
+                                use_opposite=False,
+                                variable_name=None,
+                            ),
+                            "name": "operational:main",
+                            "range": InputDefinition(
+                                parameter_name="range",
+                                input_value=None,
+                                input_unit="m",
+                                default_value=np.nan,
+                                is_relative=False,
+                                part_identifier="operational:main",
+                                use_opposite=False,
+                                variable_name="data:mission:operational:main:range",
+                            ),
+                            "type": "route",
+                        },
+                        {
+                            "name": "operational:taxi_in",
+                            "parts": [
+                                {
+                                    "name": "operational:taxi_in",
+                                    "segment_type": "taxi",
+                                    "target": {
+                                        "delta_time": InputDefinition(
+                                            parameter_name="time",
+                                            input_value=None,
+                                            input_unit="s",
+                                            default_value=np.nan,
+                                            is_relative=True,
+                                            part_identifier="operational:taxi_in",
+                                            use_opposite=False,
+                                            variable_name="data:mission:operational:taxi_in:duration",
+                                        )
+                                    },
+                                    "true_airspeed": InputDefinition(
+                                        parameter_name="true_airspeed",
+                                        input_value=0.0,
+                                        input_unit="m/s",
+                                        default_value=np.nan,
+                                        is_relative=False,
+                                        part_identifier="operational:taxi_in",
+                                        use_opposite=False,
+                                        variable_name=None,
+                                    ),
+                                    "type": "segment",
+                                }
+                            ],
+                            "thrust_rate": InputDefinition(
+                                parameter_name="thrust_rate",
+                                input_value=None,
+                                input_unit=None,
+                                default_value=np.nan,
+                                is_relative=False,
+                                part_identifier="operational:taxi_in",
+                                use_opposite=False,
+                                variable_name="data:mission:operational:taxi_in:thrust_rate",
+                            ),
+                            "type": "phase",
+                        },
+                        {
+                            "name": "operational",
+                            "reserve": {
+                                "multiplier": InputDefinition(
+                                    parameter_name="multiplier",
+                                    input_value=0.02,
+                                    input_unit=None,
+                                    default_value=np.nan,
+                                    is_relative=False,
+                                    part_identifier="operational",
+                                    use_opposite=False,
+                                    variable_name=None,
+                                ),
+                                "ref": InputDefinition(
+                                    parameter_name="ref",
+                                    input_value="main",
+                                    input_unit=None,
+                                    default_value=np.nan,
+                                    is_relative=False,
+                                    part_identifier="operational",
+                                    use_opposite=False,
+                                    variable_name=None,
+                                ),
+                            },
+                        },
+                    ],
+                ),
+                ("name", "operational"),
+                ("type", "mission"),
+            ]
+        ),
+        "sizing": OrderedDict(
+            [
+                (
+                    "parts",
+                    [
+                        {
+                            "climb_parts": [
+                                {
+                                    "engine_setting": InputDefinition(
+                                        parameter_name="engine_setting",
+                                        input_value="takeoff",
+                                        input_unit=None,
+                                        default_value=np.nan,
+                                        is_relative=False,
+                                        part_identifier="sizing:main:initial_climb",
+                                        use_opposite=False,
+                                        variable_name=None,
+                                    ),
+                                    "name": "sizing:main:initial_climb",
+                                    "parts": [
+                                        {
+                                            "name": "sizing:main:initial_climb",
+                                            "segment_type": "altitude_change",
+                                            "target": {
+                                                "altitude": InputDefinition(
+                                                    parameter_name="altitude",
+                                                    input_value=400.0,
+                                                    input_unit="ft",
+                                                    default_value=np.nan,
+                                                    is_relative=False,
+                                                    part_identifier="sizing:main:initial_climb",
+                                                    use_opposite=False,
+                                                    variable_name=None,
+                                                ),
+                                                "equivalent_airspeed": InputDefinition(
+                                                    parameter_name="equivalent_airspeed",
+                                                    input_value="constant",
+                                                    input_unit="m/s",
+                                                    default_value=np.nan,
+                                                    is_relative=False,
+                                                    part_identifier="sizing:main:initial_climb",
+                                                    use_opposite=False,
+                                                    variable_name=None,
+                                                ),
+                                            },
+                                            "type": "segment",
+                                        },
+                                        {
+                                            "name": "sizing:main:initial_climb",
+                                            "polar": OrderedDict(
+                                                [
+                                                    (
+                                                        "CL",
+                                                        InputDefinition(
+                                                            parameter_name="CL",
+                                                            input_value=None,
+                                                            input_unit=None,
+                                                            default_value=np.nan,
+                                                            is_relative=False,
+                                                            part_identifier="sizing:main:initial_climb",
+                                                            use_opposite=False,
+                                                            variable_name="data:aerodynamics:aircraft:takeoff:CL",
+                                                            shape_by_conn=True,
+                                                        ),
+                                                    ),
+                                                    (
+                                                        "CD",
+                                                        InputDefinition(
+                                                            parameter_name="CD",
+                                                            input_value=None,
+                                                            input_unit=None,
+                                                            default_value=np.nan,
+                                                            is_relative=False,
+                                                            part_identifier="sizing:main:initial_climb",
+                                                            use_opposite=False,
+                                                            variable_name="data:aerodynamics:aircraft:takeoff:CD",
+                                                            shape_by_conn=True,
+                                                        ),
+                                                    ),
+                                                ]
+                                            ),
+                                            "segment_type": "speed_change",
+                                            "target": {
+                                                "equivalent_airspeed": InputDefinition(
+                                                    parameter_name="equivalent_airspeed",
+                                                    input_value=250,
+                                                    input_unit="kn",
+                                                    default_value=np.nan,
+                                                    is_relative=False,
+                                                    part_identifier="sizing:main:initial_climb",
+                                                    use_opposite=False,
+                                                    variable_name=None,
+                                                )
+                                            },
+                                            "type": "segment",
+                                        },
+                                        {
+                                            "name": "sizing:main:initial_climb",
+                                            "polar": {
+                                                "CD": InputDefinition(
+                                                    parameter_name="CD",
+                                                    input_value=None,
+                                                    input_unit=None,
+                                                    default_value=np.nan,
+                                                    is_relative=False,
+                                                    part_identifier="sizing:main:initial_climb",
+                                                    use_opposite=False,
+                                                    variable_name="data:aerodynamics:aircraft:takeoff:CD",
+                                                    shape_by_conn=True,
+                                                ),
+                                                "CL": InputDefinition(
+                                                    parameter_name="CL",
+                                                    input_value=None,
+                                                    input_unit=None,
+                                                    default_value=np.nan,
+                                                    is_relative=False,
+                                                    part_identifier="sizing:main:initial_climb",
+                                                    use_opposite=False,
+                                                    variable_name="data:aerodynamics:aircraft:takeoff:CL",
+                                                    shape_by_conn=True,
+                                                ),
+                                            },
+                                            "segment_type": "altitude_change",
+                                            "target": {
+                                                "altitude": InputDefinition(
+                                                    parameter_name="altitude",
+                                                    input_value=1500.0,
+                                                    input_unit="ft",
+                                                    default_value=np.nan,
+                                                    is_relative=False,
+                                                    part_identifier="sizing:main:initial_climb",
+                                                    use_opposite=False,
+                                                    variable_name=None,
+                                                ),
+                                                "equivalent_airspeed": InputDefinition(
+                                                    parameter_name="equivalent_airspeed",
+                                                    input_value="constant",
+                                                    input_unit="m/s",
+                                                    default_value=np.nan,
+                                                    is_relative=False,
+                                                    part_identifier="sizing:main:initial_climb",
+                                                    use_opposite=False,
+                                                    variable_name=None,
+                                                ),
+                                            },
+                                            "type": "segment",
+                                        },
+                                    ],
+                                    "polar": {
+                                        "CD": InputDefinition(
+                                            parameter_name="CD",
+                                            input_value=[0.0, 0.03, 0.12],
+                                            input_unit=None,
+                                            default_value=np.nan,
+                                            is_relative=False,
+                                            part_identifier="sizing:main:initial_climb",
+                                            use_opposite=False,
+                                            variable_name=None,
+                                        ),
+                                        "CL": InputDefinition(
+                                            parameter_name="CL",
+                                            input_value=[0.0, 0.5, 1.0],
+                                            input_unit=None,
+                                            default_value=np.nan,
+                                            is_relative=False,
+                                            part_identifier="sizing:main:initial_climb",
+                                            use_opposite=False,
+                                            variable_name=None,
+                                        ),
+                                    },
+                                    "thrust_rate": InputDefinition(
+                                        parameter_name="thrust_rate",
+                                        input_value=None,
+                                        input_unit=None,
+                                        default_value=1.0,
+                                        is_relative=False,
+                                        part_identifier="sizing:main:initial_climb",
+                                        use_opposite=False,
+                                        variable_name="data:propulsion:initial_climb:thrust_rate",
+                                    ),
+                                    "time_step": InputDefinition(
+                                        parameter_name="time_step",
+                                        input_value=None,
+                                        input_unit="s",
+                                        default_value=np.nan,
+                                        is_relative=False,
+                                        part_identifier="sizing:main:initial_climb",
+                                        use_opposite=False,
+                                        variable_name="settings:mission:sizing:main:initial_climb:time_step",
+                                    ),
+                                    "type": "phase",
+                                },
+                                {
+                                    "engine_setting": InputDefinition(
+                                        parameter_name="engine_setting",
+                                        input_value="climb",
+                                        input_unit=None,
+                                        default_value=np.nan,
+                                        is_relative=False,
+                                        part_identifier="sizing:main:climb",
+                                        use_opposite=False,
+                                        variable_name=None,
+                                    ),
+                                    "name": "sizing:main:climb",
+                                    "parts": [
+                                        {
+                                            "name": "sizing:main:climb",
+                                            "segment_type": "altitude_change",
+                                            "target": {
+                                                "altitude": InputDefinition(
+                                                    parameter_name="altitude",
+                                                    input_value=10000.0,
+                                                    input_unit="ft",
+                                                    default_value=np.nan,
+                                                    is_relative=False,
+                                                    part_identifier="sizing:main:climb",
+                                                    use_opposite=False,
+                                                    variable_name=None,
+                                                ),
+                                                "equivalent_airspeed": InputDefinition(
+                                                    parameter_name="equivalent_airspeed",
+                                                    input_value="constant",
+                                                    input_unit="m/s",
+                                                    default_value=np.nan,
+                                                    is_relative=False,
+                                                    part_identifier="sizing:main:climb",
+                                                    use_opposite=False,
+                                                    variable_name=None,
+                                                ),
+                                            },
+                                            "type": "segment",
+                                        },
+                                        {
+                                            "name": "sizing:main:climb",
+                                            "segment_type": "speed_change",
+                                            "target": {
+                                                "equivalent_airspeed": InputDefinition(
+                                                    parameter_name="equivalent_airspeed",
+                                                    input_value=300.0,
+                                                    input_unit="kn",
+                                                    default_value=np.nan,
+                                                    is_relative=False,
+                                                    part_identifier="sizing:main:climb",
+                                                    use_opposite=False,
+                                                    variable_name=None,
+                                                )
+                                            },
+                                            "type": "segment",
+                                        },
+                                        {
+                                            "name": "sizing:main:climb",
+                                            "segment_type": "altitude_change",
+                                            "target": {
+                                                "equivalent_airspeed": InputDefinition(
+                                                    parameter_name="equivalent_airspeed",
+                                                    input_value="constant",
+                                                    input_unit="m/s",
+                                                    default_value=np.nan,
+                                                    is_relative=False,
+                                                    part_identifier="sizing:main:climb",
+                                                    use_opposite=False,
+                                                    variable_name=None,
+                                                ),
+                                                "mach": InputDefinition(
+                                                    parameter_name="mach",
+                                                    input_value=None,
+                                                    input_unit=None,
+                                                    default_value=np.nan,
+                                                    is_relative=False,
+                                                    part_identifier="sizing:main:climb",
+                                                    use_opposite=False,
+                                                    variable_name="data:TLAR:cruise_mach",
+                                                ),
+                                            },
+                                            "type": "segment",
+                                        },
+                                        {
+                                            "name": "sizing:main:climb",
+                                            "segment_type": "altitude_change",
+                                            "target": {
+                                                "altitude": InputDefinition(
+                                                    parameter_name="altitude",
+                                                    input_value=-20000.0,
+                                                    input_unit="m",
+                                                    default_value=np.nan,
+                                                    is_relative=False,
+                                                    part_identifier="sizing:main:climb",
+                                                    use_opposite=False,
+                                                    variable_name=None,
+                                                ),
+                                                "mach": InputDefinition(
+                                                    parameter_name="mach",
+                                                    input_value="constant",
+                                                    input_unit=None,
+                                                    default_value=np.nan,
+                                                    is_relative=False,
+                                                    part_identifier="sizing:main:climb",
+                                                    use_opposite=False,
+                                                    variable_name=None,
+                                                ),
+                                            },
+                                            "type": "segment",
+                                        },
+                                    ],
+                                    "polar": OrderedDict(
+                                        [
+                                            (
+                                                "CL",
+                                                InputDefinition(
+                                                    parameter_name="CL",
+                                                    input_value=None,
+                                                    input_unit=None,
+                                                    default_value=np.nan,
+                                                    is_relative=False,
+                                                    part_identifier="sizing:main:climb",
+                                                    use_opposite=False,
+                                                    variable_name="data:aerodynamics:aircraft:cruise:CL",
+                                                    shape_by_conn=True,
+                                                ),
+                                            ),
+                                            (
+                                                "CD",
+                                                InputDefinition(
+                                                    parameter_name="CD",
+                                                    input_value=None,
+                                                    input_unit=None,
+                                                    default_value=np.nan,
+                                                    is_relative=False,
+                                                    part_identifier="sizing:main:climb",
+                                                    use_opposite=False,
+                                                    variable_name="data:aerodynamics:aircraft:cruise:CD",
+                                                    shape_by_conn=True,
+                                                ),
+                                            ),
+                                        ]
+                                    ),
+                                    "thrust_rate": InputDefinition(
+                                        parameter_name="thrust_rate",
+                                        input_value=None,
+                                        input_unit=None,
+                                        default_value=np.nan,
+                                        is_relative=False,
+                                        part_identifier="sizing:main:climb",
+                                        use_opposite=False,
+                                        variable_name="data:propulsion:climb:thrust_rate",
+                                    ),
+                                    "time_step": InputDefinition(
+                                        parameter_name="time_step",
+                                        input_value=None,
+                                        input_unit="s",
+                                        default_value=np.nan,
+                                        is_relative=False,
+                                        part_identifier="sizing:main:climb",
+                                        use_opposite=False,
+                                        variable_name="data:mission:sizing:main:climb:time_step",
+                                    ),
+                                    "type": "phase",
+                                },
+                            ],
+                            "cruise_part": {
+                                "engine_setting": InputDefinition(
+                                    parameter_name="engine_setting",
+                                    input_value="cruise",
+                                    input_unit=None,
+                                    default_value=np.nan,
+                                    is_relative=False,
+                                    part_identifier="sizing:main:cruise",
+                                    use_opposite=False,
+                                    variable_name=None,
+                                ),
+                                "name": "sizing:main:cruise",
+                                "polar": OrderedDict(
+                                    [
+                                        (
+                                            "CL",
+                                            InputDefinition(
+                                                parameter_name="CL",
+                                                input_value=None,
+                                                input_unit=None,
+                                                default_value=np.nan,
+                                                is_relative=False,
+                                                part_identifier="sizing:main:cruise",
+                                                use_opposite=False,
+                                                variable_name="data:aerodynamics:aircraft:cruise:CL",
+                                                shape_by_conn=True,
+                                            ),
+                                        ),
+                                        (
+                                            "CD",
+                                            InputDefinition(
+                                                parameter_name="CD",
+                                                input_value=None,
+                                                input_unit=None,
+                                                default_value=np.nan,
+                                                is_relative=False,
+                                                part_identifier="sizing:main:cruise",
+                                                use_opposite=False,
+                                                variable_name="data:aerodynamics:aircraft:cruise:CD",
+                                                shape_by_conn=True,
+                                            ),
+                                        ),
+                                    ]
+                                ),
+                                "segment_type": "optimal_cruise",
+                                "type": "segment",
+                            },
+                            "descent_parts": [
+                                {
+                                    "engine_setting": InputDefinition(
+                                        parameter_name="engine_setting",
+                                        input_value="idle",
+                                        input_unit=None,
+                                        default_value=np.nan,
+                                        is_relative=False,
+                                        part_identifier="sizing:main:descent",
+                                        use_opposite=False,
+                                        variable_name=None,
+                                    ),
+                                    "name": "sizing:main:descent",
+                                    "parts": [
+                                        {
+                                            "name": "sizing:main:descent",
+                                            "segment_type": "altitude_change",
+                                            "target": {
+                                                "equivalent_airspeed": InputDefinition(
+                                                    parameter_name="equivalent_airspeed",
+                                                    input_value=300,
+                                                    input_unit="kn",
+                                                    default_value=np.nan,
+                                                    is_relative=False,
+                                                    part_identifier="sizing:main:descent",
+                                                    use_opposite=False,
+                                                    variable_name=None,
+                                                ),
+                                                "mach": InputDefinition(
+                                                    parameter_name="mach",
+                                                    input_value="constant",
+                                                    input_unit=None,
+                                                    default_value=np.nan,
+                                                    is_relative=False,
+                                                    part_identifier="sizing:main:descent",
+                                                    use_opposite=False,
+                                                    variable_name=None,
+                                                ),
+                                            },
+                                            "type": "segment",
+                                        },
+                                        {
+                                            "name": "sizing:main:descent",
+                                            "segment_type": "altitude_change",
+                                            "target": {
+                                                "altitude": InputDefinition(
+                                                    parameter_name="altitude",
+                                                    input_value=10000.0,
+                                                    input_unit="ft",
+                                                    default_value=np.nan,
+                                                    is_relative=False,
+                                                    part_identifier="sizing:main:descent",
+                                                    use_opposite=False,
+                                                    variable_name=None,
+                                                ),
+                                                "equivalent_airspeed": InputDefinition(
+                                                    parameter_name="equivalent_airspeed",
+                                                    input_value="constant",
+                                                    input_unit="m/s",
+                                                    default_value=np.nan,
+                                                    is_relative=False,
+                                                    part_identifier="sizing:main:descent",
+                                                    use_opposite=False,
+                                                    variable_name=None,
+                                                ),
+                                            },
+                                            "type": "segment",
+                                        },
+                                        {
+                                            "name": "sizing:main:descent",
+                                            "segment_type": "speed_change",
+                                            "target": {
+                                                "equivalent_airspeed": InputDefinition(
+                                                    parameter_name="equivalent_airspeed",
+                                                    input_value=250.0,
+                                                    input_unit="kn",
+                                                    default_value=np.nan,
+                                                    is_relative=False,
+                                                    part_identifier="sizing:main:descent",
+                                                    use_opposite=False,
+                                                    variable_name=None,
+                                                )
+                                            },
+                                            "type": "segment",
+                                        },
+                                        {
+                                            "name": "sizing:main:descent",
+                                            "segment_type": "altitude_change",
+                                            "target": {
+                                                "altitude": InputDefinition(
+                                                    parameter_name="altitude",
+                                                    input_value=None,
+                                                    input_unit="m",
+                                                    default_value=np.nan,
+                                                    is_relative=False,
+                                                    part_identifier="sizing:main:descent",
+                                                    use_opposite=False,
+                                                    variable_name="data:mission:sizing:main:descent:final_altitude",
+                                                ),
+                                                "equivalent_airspeed": InputDefinition(
+                                                    parameter_name="equivalent_airspeed",
+                                                    input_value="constant",
+                                                    input_unit="m/s",
+                                                    default_value=np.nan,
+                                                    is_relative=False,
+                                                    part_identifier="sizing:main:descent",
+                                                    use_opposite=False,
+                                                    variable_name=None,
+                                                ),
+                                            },
+                                            "type": "segment",
+                                        },
+                                    ],
+                                    "polar": OrderedDict(
+                                        [
+                                            (
+                                                "CL",
+                                                InputDefinition(
+                                                    parameter_name="CL",
+                                                    input_value=None,
+                                                    input_unit=None,
+                                                    default_value=np.nan,
+                                                    is_relative=False,
+                                                    part_identifier="sizing:main:descent",
+                                                    use_opposite=False,
+                                                    variable_name="data:aerodynamics:aircraft:cruise:CL",
+                                                    shape_by_conn=True,
+                                                ),
+                                            ),
+                                            (
+                                                "CD",
+                                                InputDefinition(
+                                                    parameter_name="CD",
+                                                    input_value=None,
+                                                    input_unit=None,
+                                                    default_value=np.nan,
+                                                    is_relative=False,
+                                                    part_identifier="sizing:main:descent",
+                                                    use_opposite=False,
+                                                    variable_name="data:aerodynamics:aircraft:cruise:CD",
+                                                    shape_by_conn=True,
+                                                ),
+                                            ),
+                                        ]
+                                    ),
+                                    "thrust_rate": InputDefinition(
+                                        parameter_name="thrust_rate",
+                                        input_value=None,
+                                        input_unit=None,
+                                        default_value=np.nan,
+                                        is_relative=False,
+                                        part_identifier="sizing:main:descent",
+                                        use_opposite=False,
+                                        variable_name="data:propulsion:descent:thrust_rate",
+                                    ),
+                                    "type": "phase",
+                                }
+                            ],
+                            "distance_accuracy": InputDefinition(
+                                parameter_name="distance_accuracy",
+                                input_value=500,
+                                input_unit="m",
+                                default_value=np.nan,
+                                is_relative=False,
+                                part_identifier="sizing:main",
+                                use_opposite=False,
+                                variable_name=None,
+                            ),
+                            "name": "sizing:main",
+                            "range": InputDefinition(
+                                parameter_name="range",
+                                input_value=None,
+                                input_unit="m",
+                                default_value=np.nan,
+                                is_relative=False,
+                                part_identifier="sizing:main",
+                                use_opposite=False,
+                                variable_name="data:mission:sizing:main:range",
+                            ),
+                            "type": "route",
+                        },
+                        {
+                            "climb_parts": [
+                                {
+                                    "engine_setting": InputDefinition(
+                                        parameter_name="engine_setting",
+                                        input_value="climb",
+                                        input_unit=None,
+                                        default_value=np.nan,
+                                        is_relative=False,
+                                        part_identifier="sizing:diversion:diversion_climb",
+                                        use_opposite=False,
+                                        variable_name=None,
+                                    ),
+                                    "name": "sizing:diversion:diversion_climb",
+                                    "parts": [
+                                        {
+                                            "name": "sizing:diversion:diversion_climb",
+                                            "segment_type": "altitude_change",
+                                            "target": {
+                                                "altitude": InputDefinition(
+                                                    parameter_name="altitude",
+                                                    input_value=10000.0,
+                                                    input_unit="ft",
+                                                    default_value=np.nan,
+                                                    is_relative=False,
+                                                    part_identifier="sizing:diversion:diversion_climb",
+                                                    use_opposite=False,
+                                                    variable_name=None,
+                                                ),
+                                                "equivalent_airspeed": InputDefinition(
+                                                    parameter_name="equivalent_airspeed",
+                                                    input_value="constant",
+                                                    input_unit="m/s",
+                                                    default_value=np.nan,
+                                                    is_relative=False,
+                                                    part_identifier="sizing:diversion:diversion_climb",
+                                                    use_opposite=False,
+                                                    variable_name=None,
+                                                ),
+                                            },
+                                            "type": "segment",
+                                        },
+                                        {
+                                            "name": "sizing:diversion:diversion_climb",
+                                            "segment_type": "speed_change",
+                                            "target": {
+                                                "equivalent_airspeed": InputDefinition(
+                                                    parameter_name="equivalent_airspeed",
+                                                    input_value=300.0,
+                                                    input_unit="kn",
+                                                    default_value=np.nan,
+                                                    is_relative=False,
+                                                    part_identifier="sizing:diversion:diversion_climb",
+                                                    use_opposite=False,
+                                                    variable_name=None,
+                                                )
+                                            },
+                                            "type": "segment",
+                                        },
+                                        {
+                                            "name": "sizing:diversion:diversion_climb",
+                                            "segment_type": "altitude_change",
+                                            "target": {
+                                                "altitude": InputDefinition(
+                                                    parameter_name="altitude",
+                                                    input_value=22000.0,
+                                                    input_unit="ft",
+                                                    default_value=np.nan,
+                                                    is_relative=False,
+                                                    part_identifier="sizing:diversion:diversion_climb",
+                                                    use_opposite=False,
+                                                    variable_name=None,
+                                                ),
+                                                "equivalent_airspeed": InputDefinition(
+                                                    parameter_name="equivalent_airspeed",
+                                                    input_value="constant",
+                                                    input_unit="m/s",
+                                                    default_value=np.nan,
+                                                    is_relative=False,
+                                                    part_identifier="sizing:diversion:diversion_climb",
+                                                    use_opposite=False,
+                                                    variable_name=None,
+                                                ),
+                                            },
+                                            "type": "segment",
+                                        },
+                                    ],
+                                    "polar": OrderedDict(
+                                        [
+                                            (
+                                                "CL",
+                                                InputDefinition(
+                                                    parameter_name="CL",
+                                                    input_value=None,
+                                                    input_unit=None,
+                                                    default_value=np.nan,
+                                                    is_relative=False,
+                                                    part_identifier="sizing:diversion:diversion_climb",
+                                                    use_opposite=False,
+                                                    variable_name="data:aerodynamics:aircraft:cruise:CL",
+                                                    shape_by_conn=True,
+                                                ),
+                                            ),
+                                            (
+                                                "CD",
+                                                InputDefinition(
+                                                    parameter_name="CD",
+                                                    input_value=None,
+                                                    input_unit=None,
+                                                    default_value=np.nan,
+                                                    is_relative=False,
+                                                    part_identifier="sizing:diversion:diversion_climb",
+                                                    use_opposite=False,
+                                                    variable_name="data:aerodynamics:aircraft:cruise:CD",
+                                                    shape_by_conn=True,
+                                                ),
+                                            ),
+                                        ]
+                                    ),
+                                    "thrust_rate": InputDefinition(
+                                        parameter_name="thrust_rate",
+                                        input_value=0.93,
+                                        input_unit=None,
+                                        default_value=np.nan,
+                                        is_relative=False,
+                                        part_identifier="sizing:diversion:diversion_climb",
+                                        use_opposite=False,
+                                        variable_name=None,
+                                    ),
+                                    "time_step": InputDefinition(
+                                        parameter_name="time_step",
+                                        input_value=None,
+                                        input_unit="s",
+                                        default_value=np.nan,
+                                        is_relative=False,
+                                        part_identifier="sizing:diversion:diversion_climb",
+                                        use_opposite=False,
+                                        variable_name="settings:mission:sizing:diversion:diversion_climb:t_step",
+                                    ),
+                                    "type": "phase",
+                                }
+                            ],
+                            "cruise_part": {
+                                "engine_setting": InputDefinition(
+                                    parameter_name="engine_setting",
+                                    input_value="cruise",
+                                    input_unit=None,
+                                    default_value=np.nan,
+                                    is_relative=False,
+                                    part_identifier="sizing:diversion:cruise",
+                                    use_opposite=False,
+                                    variable_name=None,
+                                ),
+                                "name": "sizing:diversion:cruise",
+                                "polar": OrderedDict(
+                                    [
+                                        (
+                                            "CL",
+                                            InputDefinition(
+                                                parameter_name="CL",
+                                                input_value=None,
+                                                input_unit=None,
+                                                default_value=np.nan,
+                                                is_relative=False,
+                                                part_identifier="sizing:diversion:cruise",
+                                                use_opposite=False,
+                                                variable_name="data:aerodynamics:aircraft:cruise:CL",
+                                                shape_by_conn=True,
+                                            ),
+                                        ),
+                                        (
+                                            "CD",
+                                            InputDefinition(
+                                                parameter_name="CD",
+                                                input_value=None,
+                                                input_unit=None,
+                                                default_value=np.nan,
+                                                is_relative=False,
+                                                part_identifier="sizing:diversion:cruise",
+                                                use_opposite=False,
+                                                variable_name="data:aerodynamics:aircraft:cruise:CD",
+                                                shape_by_conn=True,
+                                            ),
+                                        ),
+                                    ]
+                                ),
+                                "segment_type": "cruise",
+                                "type": "segment",
+                            },
+                            "descent_parts": [
+                                {
+                                    "engine_setting": InputDefinition(
+                                        parameter_name="engine_setting",
+                                        input_value="idle",
+                                        input_unit=None,
+                                        default_value=np.nan,
+                                        is_relative=False,
+                                        part_identifier="sizing:diversion:descent",
+                                        use_opposite=False,
+                                        variable_name=None,
+                                    ),
+                                    "name": "sizing:diversion:descent",
+                                    "parts": [
+                                        {
+                                            "name": "sizing:diversion:descent",
+                                            "segment_type": "altitude_change",
+                                            "target": {
+                                                "equivalent_airspeed": InputDefinition(
+                                                    parameter_name="equivalent_airspeed",
+                                                    input_value=300,
+                                                    input_unit="kn",
+                                                    default_value=np.nan,
+                                                    is_relative=False,
+                                                    part_identifier="sizing:diversion:descent",
+                                                    use_opposite=False,
+                                                    variable_name=None,
+                                                ),
+                                                "mach": InputDefinition(
+                                                    parameter_name="mach",
+                                                    input_value="constant",
+                                                    input_unit=None,
+                                                    default_value=np.nan,
+                                                    is_relative=False,
+                                                    part_identifier="sizing:diversion:descent",
+                                                    use_opposite=False,
+                                                    variable_name=None,
+                                                ),
+                                            },
+                                            "type": "segment",
+                                        },
+                                        {
+                                            "name": "sizing:diversion:descent",
+                                            "segment_type": "altitude_change",
+                                            "target": {
+                                                "altitude": InputDefinition(
+                                                    parameter_name="altitude",
+                                                    input_value=10000.0,
+                                                    input_unit="ft",
+                                                    default_value=np.nan,
+                                                    is_relative=False,
+                                                    part_identifier="sizing:diversion:descent",
+                                                    use_opposite=False,
+                                                    variable_name=None,
+                                                ),
+                                                "equivalent_airspeed": InputDefinition(
+                                                    parameter_name="equivalent_airspeed",
+                                                    input_value="constant",
+                                                    input_unit="m/s",
+                                                    default_value=np.nan,
+                                                    is_relative=False,
+                                                    part_identifier="sizing:diversion:descent",
+                                                    use_opposite=False,
+                                                    variable_name=None,
+                                                ),
+                                            },
+                                            "type": "segment",
+                                        },
+                                        {
+                                            "name": "sizing:diversion:descent",
+                                            "segment_type": "speed_change",
+                                            "target": {
+                                                "equivalent_airspeed": InputDefinition(
+                                                    parameter_name="equivalent_airspeed",
+                                                    input_value=250.0,
+                                                    input_unit="kn",
+                                                    default_value=np.nan,
+                                                    is_relative=False,
+                                                    part_identifier="sizing:diversion:descent",
+                                                    use_opposite=False,
+                                                    variable_name=None,
+                                                )
+                                            },
+                                            "type": "segment",
+                                        },
+                                        {
+                                            "name": "sizing:diversion:descent",
+                                            "segment_type": "altitude_change",
+                                            "target": {
+                                                "altitude": InputDefinition(
+                                                    parameter_name="altitude",
+                                                    input_value=None,
+                                                    input_unit="m",
+                                                    default_value=np.nan,
+                                                    is_relative=False,
+                                                    part_identifier="sizing:diversion:descent",
+                                                    use_opposite=False,
+                                                    variable_name="data:mission:sizing:diversion:descent:final_altitude",
+                                                ),
+                                                "equivalent_airspeed": InputDefinition(
+                                                    parameter_name="equivalent_airspeed",
+                                                    input_value="constant",
+                                                    input_unit="m/s",
+                                                    default_value=np.nan,
+                                                    is_relative=False,
+                                                    part_identifier="sizing:diversion:descent",
+                                                    use_opposite=False,
+                                                    variable_name=None,
+                                                ),
+                                            },
+                                            "type": "segment",
+                                        },
+                                    ],
+                                    "polar": OrderedDict(
+                                        [
+                                            (
+                                                "CL",
+                                                InputDefinition(
+                                                    parameter_name="CL",
+                                                    input_value=None,
+                                                    input_unit=None,
+                                                    default_value=np.nan,
+                                                    is_relative=False,
+                                                    part_identifier="sizing:diversion:descent",
+                                                    use_opposite=False,
+                                                    variable_name="data:aerodynamics:aircraft:cruise:CL",
+                                                    shape_by_conn=True,
+                                                ),
+                                            ),
+                                            (
+                                                "CD",
+                                                InputDefinition(
+                                                    parameter_name="CD",
+                                                    input_value=None,
+                                                    input_unit=None,
+                                                    default_value=np.nan,
+                                                    is_relative=False,
+                                                    part_identifier="sizing:diversion:descent",
+                                                    use_opposite=False,
+                                                    variable_name="data:aerodynamics:aircraft:cruise:CD",
+                                                    shape_by_conn=True,
+                                                ),
+                                            ),
+                                        ]
+                                    ),
+                                    "thrust_rate": InputDefinition(
+                                        parameter_name="thrust_rate",
+                                        input_value=None,
+                                        input_unit=None,
+                                        default_value=np.nan,
+                                        is_relative=False,
+                                        part_identifier="sizing:diversion:descent",
+                                        use_opposite=False,
+                                        variable_name="data:propulsion:descent:thrust_rate",
+                                    ),
+                                    "type": "phase",
+                                }
+                            ],
+                            "distance_accuracy": InputDefinition(
+                                parameter_name="distance_accuracy",
+                                input_value=0.1,
+                                input_unit="km",
+                                default_value=np.nan,
+                                is_relative=False,
+                                part_identifier="sizing:diversion",
+                                use_opposite=False,
+                                variable_name=None,
+                            ),
+                            "name": "sizing:diversion",
+                            "range": InputDefinition(
+                                parameter_name="range",
+                                input_value=None,
+                                input_unit="m",
+                                default_value=np.nan,
+                                is_relative=False,
+                                part_identifier="sizing:diversion",
+                                use_opposite=False,
+                                variable_name="data:mission:sizing:diversion:range",
+                            ),
+                            "type": "route",
+                        },
+                        {
+                            "name": "sizing:holding",
+                            "parts": [
+                                {
+                                    "name": "sizing:holding",
+                                    "polar": OrderedDict(
+                                        [
+                                            (
+                                                "CL",
+                                                InputDefinition(
+                                                    parameter_name="CL",
+                                                    input_value=None,
+                                                    input_unit=None,
+                                                    default_value=np.nan,
+                                                    is_relative=False,
+                                                    part_identifier="sizing:holding",
+                                                    use_opposite=False,
+                                                    variable_name="data:aerodynamics:aircraft:cruise:CL",
+                                                    shape_by_conn=True,
+                                                ),
+                                            ),
+                                            (
+                                                "CD",
+                                                InputDefinition(
+                                                    parameter_name="CD",
+                                                    input_value=None,
+                                                    input_unit=None,
+                                                    default_value=np.nan,
+                                                    is_relative=False,
+                                                    part_identifier="sizing:holding",
+                                                    use_opposite=False,
+                                                    variable_name="data:aerodynamics:aircraft:cruise:CD",
+                                                    shape_by_conn=True,
+                                                ),
+                                            ),
+                                        ]
+                                    ),
+                                    "segment_type": "holding",
+                                    "target": {
+                                        "delta_time": InputDefinition(
+                                            parameter_name="time",
+                                            input_value=None,
+                                            input_unit="s",
+                                            default_value=np.nan,
+                                            is_relative=True,
+                                            part_identifier="sizing:holding",
+                                            use_opposite=False,
+                                            variable_name="data:mission:sizing:holding:duration",
+                                        )
+                                    },
+                                    "type": "segment",
+                                }
+                            ],
+                            "type": "phase",
+                        },
+                        {
+                            "name": "sizing:taxi_in",
+                            "parts": [
+                                {
+                                    "name": "sizing:taxi_in",
+                                    "segment_type": "taxi",
+                                    "target": {
+                                        "delta_time": InputDefinition(
+                                            parameter_name="time",
+                                            input_value=None,
+                                            input_unit="s",
+                                            default_value=np.nan,
+                                            is_relative=True,
+                                            part_identifier="sizing:taxi_in",
+                                            use_opposite=False,
+                                            variable_name="data:mission:sizing:taxi_in:duration",
+                                        )
+                                    },
+                                    "true_airspeed": InputDefinition(
+                                        parameter_name="true_airspeed",
+                                        input_value=0.0,
+                                        input_unit="m/s",
+                                        default_value=np.nan,
+                                        is_relative=False,
+                                        part_identifier="sizing:taxi_in",
+                                        use_opposite=False,
+                                        variable_name=None,
+                                    ),
+                                    "type": "segment",
+                                }
+                            ],
+                            "thrust_rate": InputDefinition(
+                                parameter_name="thrust_rate",
+                                input_value=None,
+                                input_unit=None,
+                                default_value=np.nan,
+                                is_relative=False,
+                                part_identifier="sizing:taxi_in",
+                                use_opposite=False,
+                                variable_name="data:mission:sizing:taxi_in:thrust_rate",
+                            ),
+                            "type": "phase",
+                        },
+                        {
+                            "name": "sizing",
+                            "reserve": {
+                                "multiplier": InputDefinition(
+                                    parameter_name="multiplier",
+                                    input_value=0.03,
+                                    input_unit=None,
+                                    default_value=np.nan,
+                                    is_relative=False,
+                                    part_identifier="sizing",
+                                    use_opposite=False,
+                                    variable_name=None,
+                                ),
+                                "ref": InputDefinition(
+                                    parameter_name="ref",
+                                    input_value="main",
+                                    input_unit=None,
+                                    default_value=np.nan,
+                                    is_relative=False,
+                                    part_identifier="sizing",
+                                    use_opposite=False,
+                                    variable_name=None,
+                                ),
+                            },
+                        },
+                    ],
+                ),
+                ("name", "sizing"),
+                ("type", "mission"),
+            ]
+        ),
+        "test": OrderedDict(
+            [
+                (
+                    "parts",
+                    [
+                        {
+                            "name": "test:test_phase",
+                            "parts": [
+                                {
+                                    "name": "test:test_phase",
+                                    "scalar_parameter": InputDefinition(
+                                        parameter_name="scalar_parameter",
+                                        input_value=42.0,
+                                        input_unit=None,
+                                        default_value=np.nan,
+                                        is_relative=False,
+                                        part_identifier="test:test_phase",
+                                        use_opposite=False,
+                                        variable_name=None,
+                                    ),
+                                    "segment_type": "test_segment_B",
+                                    "type": "segment",
+                                    "vector_parameter_1": InputDefinition(
+                                        parameter_name="vector_parameter_1",
+                                        input_value=None,
+                                        input_unit=None,
+                                        default_value=np.nan,
+                                        is_relative=False,
+                                        part_identifier="test:test_phase",
+                                        use_opposite=False,
+                                        variable_name="some:static:array",
+                                        shape_by_conn=True,
+                                    ),
+                                    "vector_parameter_2": InputDefinition(
+                                        parameter_name="vector_parameter_2",
+                                        input_value=None,
+                                        input_unit=None,
+                                        default_value=np.nan,
+                                        is_relative=False,
+                                        part_identifier="test:test_phase",
+                                        use_opposite=False,
+                                        variable_name="some:dynamic:array",
+                                        shape_by_conn=True,
+                                    ),
+                                    "vector_parameter_3": InputDefinition(
+                                        parameter_name="vector_parameter_3",
+                                        input_value=[10.0, 20.0, 30.0],
+                                        input_unit=None,
+                                        default_value=np.nan,
+                                        is_relative=False,
+                                        part_identifier="test:test_phase",
+                                        use_opposite=False,
+                                        variable_name=None,
+                                    ),
+                                    "vector_parameter_4": InputDefinition(
+                                        parameter_name="vector_parameter_4",
+                                        input_value=[100.0, 200.0, 300.0],
+                                        input_unit=None,
+                                        default_value=np.nan,
+                                        is_relative=False,
+                                        part_identifier="test:test_phase",
+                                        use_opposite=False,
+                                        variable_name=None,
+                                    ),
+                                    "target": {
+                                        "altitude": InputDefinition(
+                                            parameter_name="altitude",
+                                            input_value=0.0,
+                                            input_unit="m",
+                                            default_value=np.nan,
+                                            is_relative=False,
+                                            part_identifier="test:test_phase",
+                                            use_opposite=False,
+                                            variable_name=None,
+                                        )
+                                    },
+                                }
+                            ],
+                            "type": "phase",
+                        }
+                    ],
+                ),
+                ("name", "test"),
+                ("type", "mission"),
+            ]
+        ),
+    }
diff --git a/src/fastoad/models/performances/mission/mission_definition/tests/data/mission.yml b/src/fastoad/models/performances/mission/mission_definition/tests/data/mission.yml
index 7413c7a81..f031513a3 100644
--- a/src/fastoad/models/performances/mission/mission_definition/tests/data/mission.yml
+++ b/src/fastoad/models/performances/mission/mission_definition/tests/data/mission.yml
@@ -5,7 +5,11 @@ phases:
       CL: [ 0.0, 0.5, 1.0 ]
       CD: [ 0., 0.03, 0.12 ]
     thrust_rate:
-      value: 1.0
+      value: data:propulsion:initial_climb:thrust_rate
+      default: 1.0
+    time_step:
+      value: settings:mission:~
+      unit: s
     parts:
       - segment: altitude_change
         target:
@@ -34,6 +38,9 @@ phases:
     engine_setting: climb
     polar: data:aerodynamics:aircraft:cruise
     thrust_rate: data:propulsion:climb:thrust_rate
+    time_step:
+      value:
+      unit: s
     parts:
       - segment: altitude_change
         target:
@@ -61,7 +68,7 @@ phases:
     polar: data:aerodynamics:aircraft:cruise
     thrust_rate: 0.93
     time_step:
-      value: 5.0
+      value: settings:mission~t_step
       unit: s
     parts:
       - segment: altitude_change
@@ -110,31 +117,35 @@ phases:
         target:
           equivalent_airspeed:
             value: constant
-          altitude: ~final_altitude
+          altitude: ~:final_altitude
   holding:
     parts:
       - segment: holding
         polar: data:aerodynamics:aircraft:cruise
         target:
-          time: ~duration
+          delta_time: ~duration
   taxi_out:
     parts:
       - segment: taxi
         thrust_rate: ~
         true_airspeed: 0.
         target:
-          time: ~duration
+          delta_time: ~:duration
+          mass:
+            value: ~TOW
+            unit: kg
   taxi_in:
     thrust_rate: ~
     parts:
       - segment: taxi
         true_airspeed: 0.
         target:
-          time: ~duration
+          delta_time: ~duration
 
 routes:
   main:
     range: ~
+    distance_accuracy: 500
     climb_parts:
       - phase: initial_climb
       - phase: climb
@@ -147,6 +158,9 @@ routes:
 
   diversion:
     range: ~
+    distance_accuracy:
+      value: 0.1
+      unit: km
     climb_parts:
       - phase: diversion_climb
     cruise_part:
@@ -163,9 +177,15 @@ missions:
       - route: diversion
       - phase: holding
       - phase: taxi_in
+      - reserve:
+          ref: main
+          multiplier: 0.03
   operational:
     parts:
       - phase: taxi_out
       - route: main
       - phase: taxi_in
+      - reserve:
+          ref: main
+          multiplier: 0.02
 
diff --git a/src/fastoad/models/performances/mission/mission_definition/tests/test_mission_builder.py b/src/fastoad/models/performances/mission/mission_definition/tests/test_mission_builder.py
deleted file mode 100644
index bd3227f6c..000000000
--- a/src/fastoad/models/performances/mission/mission_definition/tests/test_mission_builder.py
+++ /dev/null
@@ -1,194 +0,0 @@
-#  This file is part of FAST-OAD : A framework for rapid Overall Aircraft Design
-#  Copyright (C) 2021 ONERA & ISAE-SUPAERO
-#  FAST is free software: you can redistribute it and/or modify
-#  it under the terms of the GNU General Public License as published by
-#  the Free Software Foundation, either version 3 of the License, or
-#  (at your option) any later version.
-#  This program is distributed in the hope that it will be useful,
-#  but WITHOUT ANY WARRANTY; without even the implied warranty of
-#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-#  GNU General Public License for more details.
-#  You should have received a copy of the GNU General Public License
-#  along with this program.  If not, see <https://www.gnu.org/licenses/>.
-
-import os.path as pth
-from unittest.mock import Mock
-
-import numpy as np
-import pytest
-from numpy.testing import assert_allclose
-from scipy.constants import foot, knot
-
-from fastoad.model_base.propulsion import IPropulsion
-from fastoad.models.performances.mission.base import FlightSequence
-from fastoad.models.performances.mission.segments.altitude_change import AltitudeChangeSegment
-from fastoad.models.performances.mission.segments.hold import HoldSegment
-from fastoad.models.performances.mission.segments.speed_change import SpeedChangeSegment
-from fastoad.models.performances.mission.segments.taxi import TaxiSegment
-from ..exceptions import FastMissionFileMissingMissionNameError
-from ..mission_builder import MissionBuilder
-from ..schema import MissionDefinition
-
-DATA_FOLDER_PATH = pth.join(pth.dirname(__file__), "data")
-
-
-def test_inputs():
-    mission_builder = MissionBuilder(
-        pth.join(DATA_FOLDER_PATH, "mission.yml"),
-        propulsion=Mock(IPropulsion),
-        reference_area=100.0,
-    )
-    with pytest.raises(FastMissionFileMissingMissionNameError):
-        mission_builder.get_input_variables()
-
-    assert mission_builder.get_input_variables("sizing") == {
-        "data:TLAR:cruise_mach": (None, "Input defined by the mission."),
-        "data:aerodynamics:aircraft:cruise:CD": (None, "Input defined by the mission."),
-        "data:aerodynamics:aircraft:cruise:CL": (None, "Input defined by the mission."),
-        "data:aerodynamics:aircraft:takeoff:CD": (None, "Input defined by the mission."),
-        "data:aerodynamics:aircraft:takeoff:CL": (None, "Input defined by the mission."),
-        "data:mission:sizing:diversion:descent:final_altitude": (
-            "m",
-            "Input defined by " "the mission.",
-        ),
-        "data:mission:sizing:diversion:range": ("m", "Input defined by the mission."),
-        "data:mission:sizing:holding:duration": ("s", "Input defined by the mission."),
-        "data:mission:sizing:main:descent:final_altitude": (
-            "m",
-            "Input defined by the " "mission.",
-        ),
-        "data:mission:sizing:main:range": ("m", "Input defined by the mission."),
-        "data:mission:sizing:taxi_in:duration": ("s", "Input defined by the mission."),
-        "data:mission:sizing:taxi_in:thrust_rate": (None, "Input defined by the mission."),
-        "data:propulsion:climb:thrust_rate": (None, "Input defined by the mission."),
-        "data:propulsion:descent:thrust_rate": (None, "Input defined by the mission."),
-    }
-    assert mission_builder.get_input_variables("operational") == {
-        "data:TLAR:cruise_mach": (None, "Input defined by the mission."),
-        "data:aerodynamics:aircraft:cruise:CD": (None, "Input defined by the mission."),
-        "data:aerodynamics:aircraft:cruise:CL": (None, "Input defined by the mission."),
-        "data:aerodynamics:aircraft:takeoff:CD": (None, "Input defined by the mission."),
-        "data:aerodynamics:aircraft:takeoff:CL": (None, "Input defined by the mission."),
-        "data:mission:operational:main:descent:final_altitude": (
-            "m",
-            "Input defined by " "the mission.",
-        ),
-        "data:mission:operational:main:range": ("m", "Input defined by the mission."),
-        "data:mission:operational:taxi_in:duration": ("s", "Input defined by the mission."),
-        "data:mission:operational:taxi_in:thrust_rate": (None, "Input defined by the " "mission."),
-        "data:mission:operational:taxi_out:duration": ("s", "Input defined by the " "mission."),
-        "data:mission:operational:taxi_out:thrust_rate": (None, "Input defined by the " "mission."),
-        "data:propulsion:climb:thrust_rate": (None, "Input defined by the mission."),
-        "data:propulsion:descent:thrust_rate": (None, "Input defined by the mission."),
-    }
-
-
-def test_build():
-    mission_definition = MissionDefinition(pth.join(DATA_FOLDER_PATH, "mission.yml"))
-    mission_builder = MissionBuilder(
-        mission_definition, propulsion=Mock(IPropulsion), reference_area=100.0
-    )
-
-    cl = np.linspace(0.0, 1.0, 11)
-    cd = 0.5 * cl ** 2
-
-    inputs = {
-        "data:TLAR:cruise_mach": 0.78,
-        "data:mission:sizing:main:range": 8000.0e3,
-        "data:mission:sizing:diversion:range": 926.0e3,
-        "initial_climb:final_altitude": 500,
-        "initial_climb:final_equivalent_airspeed": 130.0,
-        "data:aerodynamics:aircraft:cruise:CD": cd,
-        "data:aerodynamics:aircraft:cruise:CL": cl,
-        "data:aerodynamics:aircraft:low_speed:CD": cd,
-        "data:aerodynamics:aircraft:low_speed:CL": cl,
-        "data:aerodynamics:aircraft:takeoff:CD": cd,
-        "data:aerodynamics:aircraft:takeoff:CL": cl,
-        "data:mission:sizing:climb:thrust_rate": 0.9,
-        "data:mission:sizing:descent:thrust_rate": 0.5,
-        "data:mission:sizing:holding:duration": 2000.0,
-        "data:mission:sizing:taxi_in:duration": 300.0,
-        "data:mission:sizing:taxi_in:thrust_rate": 0.5,
-    }
-    mission = mission_builder.build(inputs, mission_name="sizing")
-
-    assert len(mission.flight_sequence) == 4
-    assert mission.flight_sequence[0].name == "sizing:main"
-    assert mission.flight_sequence[1].name == "sizing:diversion"
-    assert mission.flight_sequence[2].name == "sizing:holding"
-    assert mission.flight_sequence[3].name == "sizing:taxi_in"
-
-    main_route = mission.flight_sequence[0]
-    assert isinstance(main_route, FlightSequence)
-    assert len(main_route.flight_sequence) == 4
-    assert main_route.flight_sequence[0].name == "sizing:main:initial_climb"
-    assert main_route.flight_sequence[1].name == "sizing:main:climb"
-    assert main_route.flight_sequence[2].name == "sizing:main:cruise"
-    assert main_route.flight_sequence[3].name == "sizing:main:descent"
-
-    initial_climb = main_route.flight_sequence[0]
-    assert isinstance(initial_climb, FlightSequence)
-    assert len(initial_climb.flight_sequence) == 3
-
-    climb1 = initial_climb.flight_sequence[0]
-    assert isinstance(climb1, AltitudeChangeSegment)
-    assert climb1.target.altitude == pytest.approx(400 * foot)
-    assert climb1.target.equivalent_airspeed == "constant"
-    assert_allclose(climb1.polar.definition_cl, [0.0, 0.5, 1.0])
-    assert_allclose(climb1.polar.cd(), [0.0, 0.03, 0.12])
-
-    acceleration = initial_climb.flight_sequence[1]
-    assert isinstance(acceleration, SpeedChangeSegment)
-    assert acceleration.target.equivalent_airspeed == pytest.approx(250 * knot)
-    assert_allclose(acceleration.polar.definition_cl, cl)
-    assert_allclose(acceleration.polar.cd(), cd)
-
-    climb2 = initial_climb.flight_sequence[2]
-    assert isinstance(climb2, AltitudeChangeSegment)
-    assert_allclose(climb2.polar.definition_cl, cl)
-    assert_allclose(climb2.polar.cd(), cd)
-
-    holding_phase = mission.flight_sequence[2]
-    assert isinstance(holding_phase, FlightSequence)
-    assert len(holding_phase.flight_sequence) == 1
-    holding = holding_phase.flight_sequence[0]
-    assert isinstance(holding, HoldSegment)
-
-    taxi_in_phase = mission.flight_sequence[3]
-    assert isinstance(taxi_in_phase, FlightSequence)
-    assert len(taxi_in_phase.flight_sequence) == 1
-    taxi_in = taxi_in_phase.flight_sequence[0]
-    assert isinstance(taxi_in, TaxiSegment)
-
-
-def test_get_flight_ranges():
-    mission_definition = MissionDefinition(pth.join(DATA_FOLDER_PATH, "mission.yml"))
-    mission_builder = MissionBuilder(
-        mission_definition, propulsion=Mock(IPropulsion), reference_area=100.0
-    )
-
-    cl = np.linspace(0.0, 1.0, 11)
-    cd = 0.5 * cl ** 2
-
-    inputs = {
-        "data:TLAR:cruise_mach": 0.78,
-        "data:mission:sizing:main:range": 8000.0e3,
-        "data:mission:operational:main:range": 500.0e3,
-        "data:mission:sizing:diversion:range": 926.0e3,
-        "initial_climb:final_altitude": 500,
-        "initial_climb:final_equivalent_airspeed": 130.0,
-        "data:aerodynamics:aircraft:cruise:CD": cd,
-        "data:aerodynamics:aircraft:cruise:CL": cl,
-        "data:aerodynamics:aircraft:low_speed:CD": cd,
-        "data:aerodynamics:aircraft:low_speed:CL": cl,
-        "data:aerodynamics:aircraft:takeoff:CD": cd,
-        "data:aerodynamics:aircraft:takeoff:CL": cl,
-        "data:mission:sizing:climb:thrust_rate": 0.9,
-        "data:mission:sizing:descent:thrust_rate": 0.5,
-        "data:mission:sizing:holding:duration": 2000.0,
-        "data:mission:sizing:taxi_in:duration": 300.0,
-        "data:mission:sizing:taxi_in:thrust_rate": 0.5,
-    }
-
-    assert_allclose(mission_builder.get_route_ranges(inputs, "sizing"), [8000.0e3, 926.0e3])
-    assert_allclose(mission_builder.get_route_ranges(inputs, "operational"), [500.0e3])
diff --git a/src/fastoad/models/performances/mission/mission_definition/tests/test_schema.py b/src/fastoad/models/performances/mission/mission_definition/tests/test_schema.py
index 546ff6923..29eabf5f5 100644
--- a/src/fastoad/models/performances/mission/mission_definition/tests/test_schema.py
+++ b/src/fastoad/models/performances/mission/mission_definition/tests/test_schema.py
@@ -1,5 +1,5 @@
 #  This file is part of FAST-OAD : A framework for rapid Overall Aircraft Design
-#  Copyright (C) 2021 ONERA & ISAE-SUPAERO
+#  Copyright (C) 2022 ONERA & ISAE-SUPAERO
 #  FAST is free software: you can redistribute it and/or modify
 #  it under the terms of the GNU General Public License as published by
 #  the Free Software Foundation, either version 3 of the License, or
@@ -26,7 +26,7 @@ def test_schema():
     # considered equal when the order of key differs.
     # To check order, we need to convert both dictionaries to OrderedDict (recursively!)
     obtained_dict = _to_ordered_dict(obtained_dict)
-    expected_dict = _to_ordered_dict(_get_expected_dict())
+    expected_dict = _get_expected_dict()
 
     assert obtained_dict == expected_dict
 
@@ -38,204 +38,660 @@ def _to_ordered_dict(item):
         for key, value in ordered_dict.items():
             ordered_dict[key] = _to_ordered_dict(value)
         return ordered_dict
-    elif isinstance(item, list):
-        for i, value in enumerate(item):
-            item[i] = _to_ordered_dict(value)
     else:
+        if isinstance(item, list):
+            for i, value in enumerate(item):
+                item[i] = _to_ordered_dict(value)
         return item
 
 
 def _get_expected_dict():
-    return {
-        "phases": {
-            "initial_climb": {
-                "engine_setting": "takeoff",
-                "polar": {"CL": [0.0, 0.5, 1.0], "CD": [0.0, 0.03, 0.12]},
-                "thrust_rate": {"value": 1.0},
-                "parts": [
-                    {
-                        "segment": "altitude_change",
-                        "target": {
-                            "altitude": {"value": 400.0, "unit": "ft"},
-                            "equivalent_airspeed": {"value": "constant"},
-                        },
-                    },
-                    {
-                        "segment": "speed_change",
-                        "polar": {
-                            "CL": "data:aerodynamics:aircraft:takeoff:CL",
-                            "CD": "data:aerodynamics:aircraft:takeoff:CD",
-                        },
-                        "target": {"equivalent_airspeed": {"value": 250.0, "unit": "kn"}},
-                    },
-                    {
-                        "segment": "altitude_change",
-                        "polar": {
-                            "CL": "data:aerodynamics:aircraft:takeoff:CL",
-                            "CD": "data:aerodynamics:aircraft:takeoff:CD",
-                        },
-                        "target": {
-                            "altitude": {"value": 1500.0, "unit": "ft"},
-                            "equivalent_airspeed": {"value": "constant"},
-                        },
-                    },
-                ],
-            },
-            "climb": {
-                "engine_setting": "climb",
-                "polar": {
-                    "CL": "data:aerodynamics:aircraft:cruise:CL",
-                    "CD": "data:aerodynamics:aircraft:cruise:CD",
-                },
-                "thrust_rate": "data:propulsion:climb:thrust_rate",
-                "parts": [
-                    {
-                        "segment": "altitude_change",
-                        "target": {
-                            "altitude": {"value": 10000.0, "unit": "ft"},
-                            "equivalent_airspeed": {"value": "constant"},
-                        },
-                    },
-                    {
-                        "segment": "speed_change",
-                        "target": {"equivalent_airspeed": {"value": 300.0, "unit": "kn"}},
-                    },
-                    {
-                        "segment": "altitude_change",
-                        "target": {
-                            "equivalent_airspeed": "constant",
-                            "mach": "data:TLAR:cruise_mach",
-                        },
-                    },
-                    {
-                        "segment": "altitude_change",
-                        "target": {"mach": "constant", "altitude": {"value": -20000.0}},
-                    },
-                ],
-            },
-            "diversion_climb": {
-                "engine_setting": "climb",
-                "polar": {
-                    "CL": "data:aerodynamics:aircraft:cruise:CL",
-                    "CD": "data:aerodynamics:aircraft:cruise:CD",
-                },
-                "thrust_rate": 0.93,
-                "time_step": {"value": 5.0, "unit": "s"},
-                "parts": [
-                    {
-                        "segment": "altitude_change",
-                        "target": {
-                            "altitude": {"value": 10000.0, "unit": "ft"},
-                            "equivalent_airspeed": "constant",
-                        },
-                    },
-                    {
-                        "segment": "speed_change",
-                        "target": {"equivalent_airspeed": {"value": 300.0, "unit": "kn"}},
-                    },
-                    {
-                        "segment": "altitude_change",
-                        "target": {
-                            "altitude": {"value": 22000.0, "unit": "ft"},
-                            "equivalent_airspeed": "constant",
-                        },
-                    },
-                ],
-            },
-            "descent": {
-                "engine_setting": {"value": "idle"},
-                "polar": {
-                    "CL": "data:aerodynamics:aircraft:cruise:CL",
-                    "CD": "data:aerodynamics:aircraft:cruise:CD",
-                },
-                "thrust_rate": "data:propulsion:descent:thrust_rate",
-                "parts": [
-                    {
-                        "segment": "altitude_change",
-                        "target": {
-                            "equivalent_airspeed": {"value": 300.0, "unit": "kn"},
-                            "mach": {"value": "constant"},
-                        },
-                    },
-                    {
-                        "segment": "altitude_change",
-                        "target": {
-                            "altitude": {"value": 10000.0, "unit": "ft"},
-                            "equivalent_airspeed": {"value": "constant"},
-                        },
-                    },
-                    {
-                        "segment": "speed_change",
-                        "target": {"equivalent_airspeed": {"value": 250.0, "unit": "kn"}},
-                    },
-                    {
-                        "segment": "altitude_change",
-                        "target": {
-                            "equivalent_airspeed": {"value": "constant"},
-                            "altitude": "~final_altitude",
-                        },
-                    },
-                ],
-            },
-            "holding": {
-                "parts": [
-                    {
-                        "segment": "holding",
-                        "polar": {
-                            "CL": "data:aerodynamics:aircraft:cruise:CL",
-                            "CD": "data:aerodynamics:aircraft:cruise:CD",
-                        },
-                        "target": {"time": "~duration"},
-                    }
-                ]
-            },
-            "taxi_out": {
-                "parts": [{"segment": "taxi", "thrust_rate": None, "target": {"time": "~duration"}}]
-            },
-            "taxi_in": {
-                "thrust_rate": None,
-                "parts": [{"segment": "taxi", "target": {"time": "~duration"}}],
-            },
-        },
-        "routes": {
-            "main": {
-                "range": None,
-                "climb_parts": [{"phase": "initial_climb"}, {"phase": "climb"}],
-                "cruise_part": {
-                    "segment": "optimal_cruise",
-                    "engine_setting": "cruise",
-                    "polar": {
-                        "CL": "data:aerodynamics:aircraft:cruise:CL",
-                        "CD": "data:aerodynamics:aircraft:cruise:CD",
-                    },
-                },
-                "descent_parts": [{"phase": "descent"}],
-            },
-            "diversion": {
-                "range": None,
-                "climb_parts": [{"phase": "diversion_climb"}],
-                "cruise_part": {
-                    "segment": "cruise",
-                    "engine_setting": "cruise",
-                    "polar": {
-                        "CL": "data:aerodynamics:aircraft:cruise:CL",
-                        "CD": "data:aerodynamics:aircraft:cruise:CD",
-                    },
-                },
-                "descent_parts": [{"phase": "descent"}],
-            },
-        },
-        "missions": {
-            "sizing": {
-                "parts": [
-                    {"route": "main"},
-                    {"route": "diversion"},
-                    {"phase": "holding"},
-                    {"phase": "taxi_in"},
-                ]
-            },
-            "operational": {
-                "parts": [{"phase": "taxi_out"}, {"route": "main"}, {"phase": "taxi_in"}]
-            },
-        },
-    }
+    return OrderedDict(
+        [
+            (
+                "phases",
+                OrderedDict(
+                    [
+                        (
+                            "initial_climb",
+                            OrderedDict(
+                                [
+                                    ("engine_setting", "takeoff"),
+                                    (
+                                        "polar",
+                                        OrderedDict(
+                                            [("CL", [0.0, 0.5, 1.0]), ("CD", [0.0, 0.03, 0.12])]
+                                        ),
+                                    ),
+                                    (
+                                        "thrust_rate",
+                                        OrderedDict(
+                                            [
+                                                (
+                                                    "value",
+                                                    "data:propulsion:initial_climb:thrust_rate",
+                                                ),
+                                                ("default", 1.0),
+                                            ]
+                                        ),
+                                    ),
+                                    (
+                                        "time_step",
+                                        OrderedDict(
+                                            [("value", "settings:mission:~"), ("unit", "s")]
+                                        ),
+                                    ),
+                                    (
+                                        "parts",
+                                        [
+                                            OrderedDict(
+                                                [
+                                                    ("segment", "altitude_change"),
+                                                    (
+                                                        "target",
+                                                        OrderedDict(
+                                                            [
+                                                                (
+                                                                    "altitude",
+                                                                    OrderedDict(
+                                                                        [
+                                                                            ("value", 400.0),
+                                                                            ("unit", "ft"),
+                                                                        ]
+                                                                    ),
+                                                                ),
+                                                                (
+                                                                    "equivalent_airspeed",
+                                                                    OrderedDict(
+                                                                        [("value", "constant")]
+                                                                    ),
+                                                                ),
+                                                            ]
+                                                        ),
+                                                    ),
+                                                ]
+                                            ),
+                                            OrderedDict(
+                                                [
+                                                    ("segment", "speed_change"),
+                                                    (
+                                                        "polar",
+                                                        "data:aerodynamics:aircraft:takeoff",
+                                                    ),
+                                                    (
+                                                        "target",
+                                                        OrderedDict(
+                                                            [
+                                                                (
+                                                                    "equivalent_airspeed",
+                                                                    OrderedDict(
+                                                                        [
+                                                                            ("value", 250),
+                                                                            ("unit", "kn"),
+                                                                        ]
+                                                                    ),
+                                                                )
+                                                            ]
+                                                        ),
+                                                    ),
+                                                ]
+                                            ),
+                                            OrderedDict(
+                                                [
+                                                    ("segment", "altitude_change"),
+                                                    (
+                                                        "polar",
+                                                        OrderedDict(
+                                                            [
+                                                                (
+                                                                    "CL",
+                                                                    "data:aerodynamics:aircraft:takeoff:CL",
+                                                                ),
+                                                                (
+                                                                    "CD",
+                                                                    "data:aerodynamics:aircraft:takeoff:CD",
+                                                                ),
+                                                            ]
+                                                        ),
+                                                    ),
+                                                    (
+                                                        "target",
+                                                        OrderedDict(
+                                                            [
+                                                                (
+                                                                    "altitude",
+                                                                    OrderedDict(
+                                                                        [
+                                                                            ("value", 1500.0),
+                                                                            ("unit", "ft"),
+                                                                        ]
+                                                                    ),
+                                                                ),
+                                                                (
+                                                                    "equivalent_airspeed",
+                                                                    OrderedDict(
+                                                                        [("value", "constant")]
+                                                                    ),
+                                                                ),
+                                                            ]
+                                                        ),
+                                                    ),
+                                                ]
+                                            ),
+                                        ],
+                                    ),
+                                ]
+                            ),
+                        ),
+                        (
+                            "climb",
+                            OrderedDict(
+                                [
+                                    ("engine_setting", "climb"),
+                                    (
+                                        "polar",
+                                        "data:aerodynamics:aircraft:cruise",
+                                    ),
+                                    ("thrust_rate", "data:propulsion:climb:thrust_rate"),
+                                    ("time_step", OrderedDict([("value", "~"), ("unit", "s")])),
+                                    (
+                                        "parts",
+                                        [
+                                            OrderedDict(
+                                                [
+                                                    ("segment", "altitude_change"),
+                                                    (
+                                                        "target",
+                                                        OrderedDict(
+                                                            [
+                                                                (
+                                                                    "altitude",
+                                                                    OrderedDict(
+                                                                        [
+                                                                            ("value", 10000.0),
+                                                                            ("unit", "ft"),
+                                                                        ]
+                                                                    ),
+                                                                ),
+                                                                (
+                                                                    "equivalent_airspeed",
+                                                                    OrderedDict(
+                                                                        [("value", "constant")]
+                                                                    ),
+                                                                ),
+                                                            ]
+                                                        ),
+                                                    ),
+                                                ]
+                                            ),
+                                            OrderedDict(
+                                                [
+                                                    ("segment", "speed_change"),
+                                                    (
+                                                        "target",
+                                                        OrderedDict(
+                                                            [
+                                                                (
+                                                                    "equivalent_airspeed",
+                                                                    OrderedDict(
+                                                                        [
+                                                                            ("value", 300.0),
+                                                                            ("unit", "kn"),
+                                                                        ]
+                                                                    ),
+                                                                )
+                                                            ]
+                                                        ),
+                                                    ),
+                                                ]
+                                            ),
+                                            OrderedDict(
+                                                [
+                                                    ("segment", "altitude_change"),
+                                                    (
+                                                        "target",
+                                                        OrderedDict(
+                                                            [
+                                                                ("equivalent_airspeed", "constant"),
+                                                                ("mach", "data:TLAR:cruise_mach"),
+                                                            ]
+                                                        ),
+                                                    ),
+                                                ]
+                                            ),
+                                            OrderedDict(
+                                                [
+                                                    ("segment", "altitude_change"),
+                                                    (
+                                                        "target",
+                                                        OrderedDict(
+                                                            [
+                                                                ("mach", "constant"),
+                                                                (
+                                                                    "altitude",
+                                                                    OrderedDict(
+                                                                        [("value", -20000.0)]
+                                                                    ),
+                                                                ),
+                                                            ]
+                                                        ),
+                                                    ),
+                                                ]
+                                            ),
+                                        ],
+                                    ),
+                                ]
+                            ),
+                        ),
+                        (
+                            "diversion_climb",
+                            OrderedDict(
+                                [
+                                    ("engine_setting", "climb"),
+                                    (
+                                        "polar",
+                                        "data:aerodynamics:aircraft:cruise",
+                                    ),
+                                    ("thrust_rate", 0.93),
+                                    (
+                                        "time_step",
+                                        OrderedDict(
+                                            [("value", "settings:mission~t_step"), ("unit", "s")]
+                                        ),
+                                    ),
+                                    (
+                                        "parts",
+                                        [
+                                            OrderedDict(
+                                                [
+                                                    ("segment", "altitude_change"),
+                                                    (
+                                                        "target",
+                                                        OrderedDict(
+                                                            [
+                                                                (
+                                                                    "altitude",
+                                                                    OrderedDict(
+                                                                        [
+                                                                            ("value", 10000.0),
+                                                                            ("unit", "ft"),
+                                                                        ]
+                                                                    ),
+                                                                ),
+                                                                ("equivalent_airspeed", "constant"),
+                                                            ]
+                                                        ),
+                                                    ),
+                                                ]
+                                            ),
+                                            OrderedDict(
+                                                [
+                                                    ("segment", "speed_change"),
+                                                    (
+                                                        "target",
+                                                        OrderedDict(
+                                                            [
+                                                                (
+                                                                    "equivalent_airspeed",
+                                                                    OrderedDict(
+                                                                        [
+                                                                            ("value", 300.0),
+                                                                            ("unit", "kn"),
+                                                                        ]
+                                                                    ),
+                                                                )
+                                                            ]
+                                                        ),
+                                                    ),
+                                                ]
+                                            ),
+                                            OrderedDict(
+                                                [
+                                                    ("segment", "altitude_change"),
+                                                    (
+                                                        "target",
+                                                        OrderedDict(
+                                                            [
+                                                                (
+                                                                    "altitude",
+                                                                    OrderedDict(
+                                                                        [
+                                                                            ("value", 22000.0),
+                                                                            ("unit", "ft"),
+                                                                        ]
+                                                                    ),
+                                                                ),
+                                                                ("equivalent_airspeed", "constant"),
+                                                            ]
+                                                        ),
+                                                    ),
+                                                ]
+                                            ),
+                                        ],
+                                    ),
+                                ]
+                            ),
+                        ),
+                        (
+                            "descent",
+                            OrderedDict(
+                                [
+                                    ("engine_setting", OrderedDict([("value", "idle")])),
+                                    (
+                                        "polar",
+                                        "data:aerodynamics:aircraft:cruise",
+                                    ),
+                                    ("thrust_rate", "data:propulsion:descent:thrust_rate"),
+                                    (
+                                        "parts",
+                                        [
+                                            OrderedDict(
+                                                [
+                                                    ("segment", "altitude_change"),
+                                                    (
+                                                        "target",
+                                                        OrderedDict(
+                                                            [
+                                                                (
+                                                                    "equivalent_airspeed",
+                                                                    OrderedDict(
+                                                                        [
+                                                                            ("value", 300),
+                                                                            ("unit", "kn"),
+                                                                        ]
+                                                                    ),
+                                                                ),
+                                                                (
+                                                                    "mach",
+                                                                    OrderedDict(
+                                                                        [("value", "constant")]
+                                                                    ),
+                                                                ),
+                                                            ]
+                                                        ),
+                                                    ),
+                                                ]
+                                            ),
+                                            OrderedDict(
+                                                [
+                                                    ("segment", "altitude_change"),
+                                                    (
+                                                        "target",
+                                                        OrderedDict(
+                                                            [
+                                                                (
+                                                                    "altitude",
+                                                                    OrderedDict(
+                                                                        [
+                                                                            ("value", 10000.0),
+                                                                            ("unit", "ft"),
+                                                                        ]
+                                                                    ),
+                                                                ),
+                                                                (
+                                                                    "equivalent_airspeed",
+                                                                    OrderedDict(
+                                                                        [("value", "constant")]
+                                                                    ),
+                                                                ),
+                                                            ]
+                                                        ),
+                                                    ),
+                                                ]
+                                            ),
+                                            OrderedDict(
+                                                [
+                                                    ("segment", "speed_change"),
+                                                    (
+                                                        "target",
+                                                        OrderedDict(
+                                                            [
+                                                                (
+                                                                    "equivalent_airspeed",
+                                                                    OrderedDict(
+                                                                        [
+                                                                            ("value", 250.0),
+                                                                            ("unit", "kn"),
+                                                                        ]
+                                                                    ),
+                                                                )
+                                                            ]
+                                                        ),
+                                                    ),
+                                                ]
+                                            ),
+                                            OrderedDict(
+                                                [
+                                                    ("segment", "altitude_change"),
+                                                    (
+                                                        "target",
+                                                        OrderedDict(
+                                                            [
+                                                                (
+                                                                    "equivalent_airspeed",
+                                                                    OrderedDict(
+                                                                        [("value", "constant")]
+                                                                    ),
+                                                                ),
+                                                                ("altitude", "~:final_altitude"),
+                                                            ]
+                                                        ),
+                                                    ),
+                                                ]
+                                            ),
+                                        ],
+                                    ),
+                                ]
+                            ),
+                        ),
+                        (
+                            "holding",
+                            OrderedDict(
+                                [
+                                    (
+                                        "parts",
+                                        [
+                                            OrderedDict(
+                                                [
+                                                    ("segment", "holding"),
+                                                    (
+                                                        "polar",
+                                                        "data:aerodynamics:aircraft:cruise",
+                                                    ),
+                                                    (
+                                                        "target",
+                                                        OrderedDict([("delta_time", "~duration")]),
+                                                    ),
+                                                ]
+                                            )
+                                        ],
+                                    )
+                                ]
+                            ),
+                        ),
+                        (
+                            "taxi_out",
+                            OrderedDict(
+                                [
+                                    (
+                                        "parts",
+                                        [
+                                            OrderedDict(
+                                                [
+                                                    ("segment", "taxi"),
+                                                    ("thrust_rate", "~"),
+                                                    ("true_airspeed", 0.0),
+                                                    (
+                                                        "target",
+                                                        OrderedDict(
+                                                            [
+                                                                ("delta_time", "~:duration"),
+                                                                (
+                                                                    "mass",
+                                                                    OrderedDict(
+                                                                        [
+                                                                            ("value", "~TOW"),
+                                                                            ("unit", "kg"),
+                                                                        ]
+                                                                    ),
+                                                                ),
+                                                            ]
+                                                        ),
+                                                    ),
+                                                ]
+                                            )
+                                        ],
+                                    )
+                                ]
+                            ),
+                        ),
+                        (
+                            "taxi_in",
+                            OrderedDict(
+                                [
+                                    ("thrust_rate", "~"),
+                                    (
+                                        "parts",
+                                        [
+                                            OrderedDict(
+                                                [
+                                                    ("segment", "taxi"),
+                                                    ("true_airspeed", 0.0),
+                                                    (
+                                                        "target",
+                                                        OrderedDict([("delta_time", "~duration")]),
+                                                    ),
+                                                ]
+                                            )
+                                        ],
+                                    ),
+                                ]
+                            ),
+                        ),
+                    ]
+                ),
+            ),
+            (
+                "routes",
+                OrderedDict(
+                    [
+                        (
+                            "main",
+                            OrderedDict(
+                                [
+                                    ("range", "~"),
+                                    ("distance_accuracy", 500),
+                                    (
+                                        "climb_parts",
+                                        [
+                                            OrderedDict([("phase", "initial_climb")]),
+                                            OrderedDict([("phase", "climb")]),
+                                        ],
+                                    ),
+                                    (
+                                        "cruise_part",
+                                        OrderedDict(
+                                            [
+                                                ("segment", "optimal_cruise"),
+                                                ("engine_setting", "cruise"),
+                                                (
+                                                    "polar",
+                                                    "data:aerodynamics:aircraft:cruise",
+                                                ),
+                                            ]
+                                        ),
+                                    ),
+                                    ("descent_parts", [OrderedDict([("phase", "descent")])]),
+                                ]
+                            ),
+                        ),
+                        (
+                            "diversion",
+                            OrderedDict(
+                                [
+                                    ("range", "~"),
+                                    (
+                                        "distance_accuracy",
+                                        OrderedDict([("value", 0.1), ("unit", "km")]),
+                                    ),
+                                    ("climb_parts", [OrderedDict([("phase", "diversion_climb")])]),
+                                    (
+                                        "cruise_part",
+                                        OrderedDict(
+                                            [
+                                                ("segment", "cruise"),
+                                                ("engine_setting", "cruise"),
+                                                (
+                                                    "polar",
+                                                    "data:aerodynamics:aircraft:cruise",
+                                                ),
+                                            ]
+                                        ),
+                                    ),
+                                    ("descent_parts", [OrderedDict([("phase", "descent")])]),
+                                ]
+                            ),
+                        ),
+                    ]
+                ),
+            ),
+            (
+                "missions",
+                OrderedDict(
+                    [
+                        (
+                            "sizing",
+                            OrderedDict(
+                                [
+                                    (
+                                        "parts",
+                                        [
+                                            OrderedDict([("route", "main")]),
+                                            OrderedDict([("route", "diversion")]),
+                                            OrderedDict([("phase", "holding")]),
+                                            OrderedDict([("phase", "taxi_in")]),
+                                            OrderedDict(
+                                                [
+                                                    (
+                                                        "reserve",
+                                                        OrderedDict(
+                                                            [("ref", "main"), ("multiplier", 0.03)]
+                                                        ),
+                                                    )
+                                                ]
+                                            ),
+                                        ],
+                                    )
+                                ]
+                            ),
+                        ),
+                        (
+                            "operational",
+                            OrderedDict(
+                                [
+                                    (
+                                        "parts",
+                                        [
+                                            OrderedDict([("phase", "taxi_out")]),
+                                            OrderedDict([("route", "main")]),
+                                            OrderedDict([("phase", "taxi_in")]),
+                                            OrderedDict(
+                                                [
+                                                    (
+                                                        "reserve",
+                                                        OrderedDict(
+                                                            [("ref", "main"), ("multiplier", 0.02)]
+                                                        ),
+                                                    )
+                                                ]
+                                            ),
+                                        ],
+                                    )
+                                ]
+                            ),
+                        ),
+                    ]
+                ),
+            ),
+        ]
+    )
diff --git a/src/fastoad/models/performances/mission/openmdao/tests/data/test_breguet.xml b/src/fastoad/models/performances/mission/openmdao/tests/data/test_breguet.xml
new file mode 100644
index 000000000..815d1ced5
--- /dev/null
+++ b/src/fastoad/models/performances/mission/openmdao/tests/data/test_breguet.xml
@@ -0,0 +1,62 @@
+<!--
+  ~ This file is part of FAST-OAD : A framework for rapid Overall Aircraft Design
+  ~ Copyright (C) 2022 ONERA & ISAE-SUPAERO
+  ~ FAST is free software: you can redistribute it and/or modify
+  ~ it under the terms of the GNU General Public License as published by
+  ~ the Free Software Foundation, either version 3 of the License, or
+  ~ (at your option) any later version.
+  ~ This program is distributed in the hope that it will be useful,
+  ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
+  ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  ~ GNU General Public License for more details.
+  ~ You should have received a copy of the GNU General Public License
+  ~ along with this program.  If not, see <https://www.gnu.org/licenses/>.
+  -->
+
+<FASTOAD_model>
+  <data>
+    <geometry>
+      <aircraft>
+        <reference_area units="m**2" is_input="True">130.33777836207364<!--aircraft reference surface area--></reference_area>
+      </aircraft>
+      <propulsion>
+        <engine>
+          <count>2.0<!--number of engines--></count>
+        </engine>
+      </propulsion>
+    </geometry>
+    <aerodynamics>
+      <aircraft>
+        <cruise>
+          <CD is_input="True">[0.019977462877486692, 0.01998022034546169, 0.019990024530454103, 0.020006974566650067, 0.020031169588235725, 0.020062708729397215, 0.02010169112432067, 0.020148215907192235, 0.020202382212198052, 0.02026428917352425, 0.02033403592535697, 0.020411721601882357, 0.020497445337286552, 0.02059130626575568, 0.02069340352147589, 0.02080383623863332, 0.020922703551414103, 0.021050104594004384, 0.0211861385005903, 0.02133090440535799, 0.021484501442493597, 0.02164702874618325, 0.021818585450613093, 0.021999270689969264, 0.022189183598437905, 0.022388423310205147, 0.02259708895945714, 0.022815279680380017, 0.02304309460715991, 0.02328063287398297, 0.023527993615035322, 0.02378527596450312, 0.024052579056572494, 0.024330002025429582, 0.02461764400526053, 0.02491560413025147, 0.02522457637582134, 0.025545262334798906, 0.02587778415640925, 0.026222279880813847, 0.02657890427773951, 0.026947830055772483, 0.02732924947916581, 0.02772337644195902, 0.0281304490641342, 0.028550732892056674, 0.028984524806323762, 0.029432157765291588, 0.02989400654310173, 0.03037049465840181, 0.030862102735915907, 0.031369378599802863, 0.031892949468157725, 0.03243353670563835, 0.03299197370057832, 0.033569227569877125, 0.0341664255668556, 0.034784887283641056, 0.03542616401273506, 0.036092086977987575, 0.036784826583637575, 0.03750696538779544, 0.03826158821804987, 0.03905239375647542, 0.039883833087557635, 0.04076128220185182, 0.04169125738085106, 0.042681684886571564, 0.04374223961697995, 0.044884771596099535, 0.04612384465127632, 0.04747741879653079, 0.048967717232939995, 0.050622331220637445, 0.05247563234621992, 0.0545705832141888, 0.056961066098748236, 0.05971488699344479, 0.06291766303391438, 0.0666778688557273, 0.07113340810534952, 0.07646019828585256, 0.08288342169647966, 0.09069231797897755, 0.10025969619799759, 0.11206775620558623, 0.1267423715836807, 0.14509875727064964, 0.16820250446142873, 0.1974514262050255, 0.23468567770330778, 0.28233641898530626, 0.3436271902908642, 0.4228476203450364, 0.5257267226326535, 0.659943765513565, 0.835829832900332, 1.0673345972735961, 1.373363207118953, 1.7796314520376315, 2.321249176241929, 3.046330509643145, 4.0210569125490565, 5.336802916685095, 7.120200702128098, 9.547406478033079, 12.86439554307351, 17.415937754737225, 23.687115815565242, 32.363031863946134, 44.41498301871845, 61.22529443125085, 84.76881388396411, 117.87775634750399, 164.62960078101173, 230.91731165255382, 325.2906907573328, 460.202390201523, 653.8600897536481, 932.9900111992134, 1336.9756191037327, 1924.07909611123, 2780.8289219716025, 4036.238198542151, 5883.420958185787, 8612.580199156862, 12661.541074437848, 18693.455308732322, 27716.74195998986, 41270.92915999995, 61715.707656757375, 92682.24089078103, 139780.5185367864, 211712.2812603048, 322028.80581812176, 491917.92023137305, 754639.9807120938, 1162615.784503843, 1798795.7539565805, 2794967.2537128744, 4361348.829636682, 6834616.520685008, 10756146.475549214, 16999983.131426774, 26982954.419353325, 43011019.35521255, 68852418.09203142, 110689871.99210262, 178708742.6258718, 289756334.5594224]<!--drag coefficient in cruise conditions w.r.t. data:aerodynamics:aircraft:cruise:CL--></CD>
+          <CL is_input="True">[0.0, 0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08, 0.09, 0.1, 0.11, 0.12, 0.13, 0.14, 0.15, 0.16, 0.17, 0.18, 0.19, 0.2, 0.21, 0.22, 0.23, 0.24, 0.25, 0.26, 0.27, 0.28, 0.29, 0.3, 0.31, 0.32, 0.33, 0.34, 0.35000000000000003, 0.36, 0.37, 0.38, 0.39, 0.4, 0.41000000000000003, 0.42, 0.43, 0.44, 0.45, 0.46, 0.47000000000000003, 0.48, 0.49, 0.5, 0.51, 0.52, 0.53, 0.54, 0.55, 0.56, 0.5700000000000001, 0.58, 0.59, 0.6, 0.61, 0.62, 0.63, 0.64, 0.65, 0.66, 0.67, 0.68, 0.6900000000000001, 0.7000000000000001, 0.71, 0.72, 0.73, 0.74, 0.75, 0.76, 0.77, 0.78, 0.79, 0.8, 0.81, 0.8200000000000001, 0.8300000000000001, 0.84, 0.85, 0.86, 0.87, 0.88, 0.89, 0.9, 0.91, 0.92, 0.93, 0.9400000000000001, 0.9500000000000001, 0.96, 0.97, 0.98, 0.99, 1.0, 1.01, 1.02, 1.03, 1.04, 1.05, 1.06, 1.07, 1.08, 1.09, 1.1, 1.11, 1.12, 1.1300000000000001, 1.1400000000000001, 1.1500000000000001, 1.16, 1.17, 1.18, 1.19, 1.2, 1.21, 1.22, 1.23, 1.24, 1.25, 1.26, 1.27, 1.28, 1.29, 1.3, 1.31, 1.32, 1.33, 1.34, 1.35, 1.36, 1.37, 1.3800000000000001, 1.3900000000000001, 1.4000000000000001, 1.41, 1.42, 1.43, 1.44, 1.45, 1.46, 1.47, 1.48, 1.49]<!--scale of lift coefficient values for drag computations in cruise conditions--></CL>
+        </cruise>
+        <low_speed>
+          <CD is_input="True">[0.02139564485960914, 0.02139944408291914, 0.021409492798864647, 0.021425899749722883, 0.021448773677771083, 0.02147822332528649, 0.02151435743454633, 0.02155728474782784, 0.02160711400740826, 0.021663953955564814, 0.021727913334574748, 0.021799100886715283, 0.021877625354263668, 0.021963595479497135, 0.022057120004692905, 0.02215830767212823, 0.02226726722408033, 0.022384107402826453, 0.022508936950643824, 0.022641864609809683, 0.02278299912260126, 0.022932449231295796, 0.023090323678170512, 0.023256731205502656, 0.023431780555569465, 0.02361558047064816, 0.02380823969301598, 0.024009866964950166, 0.02422057102872795, 0.024440460626626564, 0.02466964450092324, 0.024908231393895225, 0.02515633004781974, 0.025414049204974025, 0.025681497607635315, 0.025958783998080843, 0.026246017118587844, 0.026543305711433558, 0.026850758518895203, 0.027168484283250034, 0.02749659174677527, 0.027835189651748162, 0.028184386740445926, 0.02854429175514581, 0.028915013438125043, 0.029296660531660856, 0.029689341778030495, 0.030093165919511184, 0.030508241698380158, 0.030934677856914658, 0.03137258313739191, 0.03182206628208917, 0.03228323603328364, 0.032756201133252574, 0.03324107032427321, 0.03373795234862278, 0.0342469559485785, 0.03476818986641763, 0.03530176284441739, 0.035847783624855016, 0.03640636095000775, 0.03697760356215282, 0.03756162020356746, 0.03815851961652891, 0.038768410543314404, 0.039391401726201175, 0.040027601907466445, 0.04067711982938747, 0.041340064234241476, 0.042016543864305686, 0.04270666746185735, 0.04341054376917369, 0.04412828152853196, 0.04485998948220937, 0.045605776372483184, 0.04636575094163061, 0.047140021931928885, 0.047928698085655266, 0.048731888145086964, 0.049549700852501224, 0.05038224495017529, 0.05122962918038636, 0.052091962285411714, 0.05296935300752856, 0.05386191008901412, 0.05476974227214566, 0.055692958299200415, 0.056631666912455594, 0.05758597685418845, 0.058555996866676216, 0.05954183569219611, 0.06054360207302539, 0.06156140475144127, 0.062595352469721, 0.06364555397014182, 0.06471211799498092, 0.06579515328651558, 0.06689476858702303, 0.06801107263878049, 0.06914417418406522, 0.07029418196515443, 0.07146120472432534, 0.07264535120385522, 0.07384673014602128, 0.07506545029310079, 0.07630162038737093, 0.07755534917110898, 0.07882674538659216, 0.0801159177760977, 0.08142297508190281, 0.08274802604628478, 0.08409117941152082, 0.08545254391988816, 0.08683222831366404, 0.08823034133512567, 0.0896469917265503, 0.09108228823021516, 0.09253633958839749, 0.09400925454337457, 0.09550114183742359, 0.09701211021282177, 0.09854226841184635, 0.10009172517677459, 0.10166058924988372, 0.10324896937345096, 0.10485697428975356, 0.10648471274106872, 0.1081322934696737, 0.10979982521784576, 0.1114874167278621, 0.11319517674199994, 0.11492321400253658, 0.11667163725174917, 0.118440555231915, 0.1202300766853113, 0.12204031035421531, 0.12387136498090424, 0.1257233493076553, 0.1275963720767458, 0.1294905420304529, 0.1314059679110539, 0.13334275846082597, 0.1353010224220464, 0.1372808685369924, 0.1392824055479412, 0.14130574219717007, 0.14335098722695616, 0.1454182493795768, 0.1475076373973092, 0.14961926002243056]<!--drag coefficient in low speed conditions (no high-lift) w.r.t. data:aerodynamics:aircraft:low_speed:CL--></CD>
+          <CL is_input="True">[0.0, 0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08, 0.09, 0.1, 0.11, 0.12, 0.13, 0.14, 0.15, 0.16, 0.17, 0.18, 0.19, 0.2, 0.21, 0.22, 0.23, 0.24, 0.25, 0.26, 0.27, 0.28, 0.29, 0.3, 0.31, 0.32, 0.33, 0.34, 0.35000000000000003, 0.36, 0.37, 0.38, 0.39, 0.4, 0.41000000000000003, 0.42, 0.43, 0.44, 0.45, 0.46, 0.47000000000000003, 0.48, 0.49, 0.5, 0.51, 0.52, 0.53, 0.54, 0.55, 0.56, 0.5700000000000001, 0.58, 0.59, 0.6, 0.61, 0.62, 0.63, 0.64, 0.65, 0.66, 0.67, 0.68, 0.6900000000000001, 0.7000000000000001, 0.71, 0.72, 0.73, 0.74, 0.75, 0.76, 0.77, 0.78, 0.79, 0.8, 0.81, 0.8200000000000001, 0.8300000000000001, 0.84, 0.85, 0.86, 0.87, 0.88, 0.89, 0.9, 0.91, 0.92, 0.93, 0.9400000000000001, 0.9500000000000001, 0.96, 0.97, 0.98, 0.99, 1.0, 1.01, 1.02, 1.03, 1.04, 1.05, 1.06, 1.07, 1.08, 1.09, 1.1, 1.11, 1.12, 1.1300000000000001, 1.1400000000000001, 1.1500000000000001, 1.16, 1.17, 1.18, 1.19, 1.2, 1.21, 1.22, 1.23, 1.24, 1.25, 1.26, 1.27, 1.28, 1.29, 1.3, 1.31, 1.32, 1.33, 1.34, 1.35, 1.36, 1.37, 1.3800000000000001, 1.3900000000000001, 1.4000000000000001, 1.41, 1.42, 1.43, 1.44, 1.45, 1.46, 1.47, 1.48, 1.49]<!--scale of lift coefficient values for drag computations in low speed conditions--></CL>
+        </low_speed>
+        <takeoff>
+          <CD>[0.04631002472472129, 0.046716605413723925, 0.047129445504808035, 0.04754865372332344, 0.04797433879461999, 0.04840660944404752, 0.04884557439695583, 0.049291342378694786, 0.04974402211461421, 0.05020372233006394, 0.05067055175039379, 0.0511446191009536, 0.05162603310709321, 0.052114902494162445, 0.05261133598751114, 0.05311544231248913, 0.05362733019444624, 0.05414710835873231, 0.05467488553069716, 0.05521077043569063, 0.05575487179906256, 0.056307298346162764, 0.056868158802341084, 0.05743756189294737, 0.05801561634333142, 0.058602430878843086, 0.059198114224832205, 0.059802775106648595, 0.06041652224964209, 0.06103946437916253, 0.06167171022055974, 0.06231336849918356, 0.06296454794038382, 0.06362535726951035, 0.06429590521191299, 0.06497630049294156, 0.06566665183794589, 0.06636706797227582, 0.06707765762128119, 0.06779852951031182, 0.06852979236471754, 0.06927155490984818, 0.07002392587105359, 0.07078701397368359, 0.07156092794308803, 0.07234577650461671, 0.07314166838361948, 0.07394871230544617, 0.0747670169954466, 0.07559669117897064, 0.07643784358136807, 0.07729058292798878, 0.07815501794418253, 0.07903125735529926, 0.07991940988668866, 0.0808195842637007, 0.0817318892116851, 0.08265643345599179, 0.08359332572197051, 0.08454267473497115, 0.08550458922034353, 0.0864791779034375, 0.08746654950960285, 0.08846681276418945, 0.0894800763925471, 0.09050644912002567, 0.09154603967197496, 0.09259895677374481, 0.09366530915068508, 0.09474520552814555, 0.09583875463147609, 0.09694606518602651, 0.09806724591714668, 0.09920240555018638, 0.1003516528104955, 0.10151509642342381, 0.1026928451143212, 0.10388500760853744, 0.10509169263142246, 0.10631300890832596, 0.10754906516459789, 0.10879997012558801, 0.1100658325166462, 0.11134676106312222, 0.11264286449036598, 0.11395425152372729, 0.11528103088855597, 0.11662331131020186, 0.11798120151401478, 0.11935481022534458, 0.12074424616954108, 0.12214961807195412, 0.12357103465793352, 0.12500860465282912, 0.12646243678199076, 0.12793263977076824, 0.12941932234451145, 0.13092259322857017, 0.13244256114829425, 0.13397933482903354, 0.13553302299613781, 0.13710373437495701, 0.13869157769084087, 0.14029666166913923, 0.14191909503520195, 0.1435589865143789, 0.1452164448320198, 0.1468915787134746, 0.14858449688409303, 0.15029530806922506, 0.15202412099422036, 0.15377104438442887, 0.15553618696520038, 0.15731965746188478, 0.1591215645998318, 0.16094201710439132, 0.1627811237009132, 0.16463899311474722, 0.1665157340712433, 0.16841145529575116, 0.17032626551362076, 0.17226027345020178, 0.17421358783084423, 0.17618631738089777, 0.17817857082571237, 0.18019045689063773, 0.1822220843010238, 0.18427356178222032, 0.18634499805957722, 0.18843650185844418, 0.19054818190417128, 0.19268014692210808, 0.1948325056376046, 0.19700536677601055, 0.19919883906267588, 0.2014130312229503, 0.2036480519821838, 0.20590401006572598, 0.20818101419892693, 0.2104791731071363, 0.21279859551570401, 0.2151393901499798, 0.21750166573531354, 0.21988553099705518, 0.2222910946605544, 0.2247184654511612, 0.22716775209422518, 0.22963906331509637, 0.23213250783912448, 0.23464819439165943]</CD>
+          <CL>[0.5117406560537386, 0.5217406560537386, 0.5317406560537387, 0.5417406560537387, 0.5517406560537387, 0.5617406560537387, 0.5717406560537386, 0.5817406560537386, 0.5917406560537386, 0.6017406560537386, 0.6117406560537386, 0.6217406560537386, 0.6317406560537386, 0.6417406560537386, 0.6517406560537387, 0.6617406560537387, 0.6717406560537387, 0.6817406560537387, 0.6917406560537387, 0.7017406560537387, 0.7117406560537387, 0.7217406560537386, 0.7317406560537386, 0.7417406560537386, 0.7517406560537386, 0.7617406560537386, 0.7717406560537386, 0.7817406560537387, 0.7917406560537387, 0.8017406560537386, 0.8117406560537386, 0.8217406560537386, 0.8317406560537386, 0.8417406560537386, 0.8517406560537386, 0.8617406560537386, 0.8717406560537386, 0.8817406560537386, 0.8917406560537386, 0.9017406560537387, 0.9117406560537387, 0.9217406560537387, 0.9317406560537387, 0.9417406560537387, 0.9517406560537387, 0.9617406560537387, 0.9717406560537387, 0.9817406560537387, 0.9917406560537386, 1.0017406560537387, 1.0117406560537385, 1.0217406560537388, 1.0317406560537385, 1.0417406560537388, 1.0517406560537386, 1.0617406560537388, 1.0717406560537386, 1.0817406560537388, 1.0917406560537386, 1.1017406560537386, 1.1117406560537386, 1.1217406560537386, 1.1317406560537386, 1.1417406560537386, 1.1517406560537387, 1.1617406560537387, 1.1717406560537387, 1.1817406560537387, 1.1917406560537387, 1.2017406560537387, 1.2117406560537387, 1.2217406560537385, 1.2317406560537387, 1.2417406560537385, 1.2517406560537387, 1.2617406560537385, 1.2717406560537388, 1.2817406560537385, 1.2917406560537388, 1.3017406560537386, 1.3117406560537388, 1.3217406560537386, 1.3317406560537388, 1.3417406560537386, 1.3517406560537386, 1.3617406560537386, 1.3717406560537386, 1.3817406560537386, 1.3917406560537386, 1.4017406560537387, 1.4117406560537387, 1.4217406560537387, 1.4317406560537387, 1.4417406560537387, 1.4517406560537387, 1.4617406560537387, 1.4717406560537385, 1.4817406560537387, 1.4917406560537385, 1.5017406560537387, 1.5117406560537385, 1.5217406560537388, 1.5317406560537385, 1.5417406560537388, 1.5517406560537386, 1.5617406560537388, 1.5717406560537386, 1.5817406560537388, 1.5917406560537386, 1.6017406560537388, 1.6117406560537386, 1.6217406560537388, 1.6317406560537386, 1.6417406560537389, 1.6517406560537387, 1.6617406560537389, 1.6717406560537387, 1.6817406560537385, 1.6917406560537387, 1.7017406560537385, 1.7117406560537387, 1.7217406560537385, 1.7317406560537387, 1.7417406560537385, 1.7517406560537387, 1.7617406560537385, 1.7717406560537388, 1.7817406560537385, 1.7917406560537388, 1.8017406560537386, 1.8117406560537388, 1.8217406560537386, 1.8317406560537388, 1.8417406560537386, 1.8517406560537388, 1.8617406560537386, 1.8717406560537388, 1.8817406560537386, 1.8917406560537389, 1.9017406560537387, 1.9117406560537389, 1.9217406560537387, 1.9317406560537385, 1.9417406560537387, 1.9517406560537385, 1.9617406560537387, 1.9717406560537385, 1.9817406560537387, 1.9917406560537385, 2.0017406560537387]</CL>
+        </takeoff>
+      </aircraft>
+    </aerodynamics>
+    <mission>
+      <operational>
+        <ramp_weight units="kg">70000.0</ramp_weight>
+        <OWE units="kg">40000.0</OWE>
+        <payload units="kg">15000.0</payload>
+        <takeoff>
+          <V2 units="m/s" is_input="True">80.0<!--safety speed before reaching 35 feet height in sizing mission--></V2>
+          <altitude units="m" is_input="True">0.0<!--altitude at takeoff in sizing mission--></altitude>
+          <fuel units="kg">95.0<!--mass of consumed fuel during takeoff phase in sizing mission--></fuel>
+        </takeoff>
+      </operational>
+    </mission>
+    <weight>
+      <aircraft>
+        <OWE units="kg">40000.0</OWE>
+      </aircraft>
+    </weight>
+  </data>
+</FASTOAD_model>
diff --git a/src/fastoad/models/performances/mission/openmdao/tests/data/test_breguet.yml b/src/fastoad/models/performances/mission/openmdao/tests/data/test_breguet.yml
index 6c7084e0d..f7480b6cd 100644
--- a/src/fastoad/models/performances/mission/openmdao/tests/data/test_breguet.yml
+++ b/src/fastoad/models/performances/mission/openmdao/tests/data/test_breguet.yml
@@ -1,4 +1,28 @@
 phases:
+  start:
+    parts:
+      - segment: start
+        target:
+          altitude:
+            value: ~
+            unit: ft
+            default: 0.0
+          true_airspeed:
+            value: ~
+            unit: m/s
+            default: 0.0
+          mass:
+            value: data:mission:operational:ramp_weight
+            unit: kg
+  takeoff:
+    parts:
+      - segment: transition
+        target:
+          delta_altitude:
+            value: 35
+            unit: ft
+          delta_mass: -data:mission:operational:takeoff:fuel
+          true_airspeed: data:mission:operational:takeoff:V2
   climb:
     parts:
       - segment: transition
@@ -40,4 +64,6 @@ routes:
 missions:
   operational:
     parts:
+      - phase: start
+      - phase: takeoff
       - route: main_route
diff --git a/src/fastoad/models/performances/mission/openmdao/tests/data/test_breguet_from_block_fuel.yml b/src/fastoad/models/performances/mission/openmdao/tests/data/test_breguet_from_block_fuel.yml
new file mode 100644
index 000000000..3812bcce5
--- /dev/null
+++ b/src/fastoad/models/performances/mission/openmdao/tests/data/test_breguet_from_block_fuel.yml
@@ -0,0 +1,94 @@
+phases:
+  start:
+    parts:
+      - segment: start
+        target:
+          altitude:
+            value: ~
+            unit: ft
+            default: 0.0
+          true_airspeed:
+            value: ~
+            unit: m/s
+            default: 0.0
+          mass:
+            value: data:mission:operational:ramp_weight
+            unit: kg
+  taxi_out:
+    parts:
+      - segment: taxi
+        thrust_rate: data:mission:operational:taxi_out:thrust_rate
+        true_airspeed: 10.
+        target:
+          time: data:mission:operational:taxi_out:duration
+  takeoff:
+    parts:
+      - segment: transition
+        target:
+          delta_altitude:
+            value: 35
+            unit: ft
+          delta_mass: -data:mission:operational:takeoff:fuel
+          true_airspeed: data:mission:operational:takeoff:V2
+  climb1:
+    parts:
+      - segment: transition
+        target:
+          altitude:
+            value: 10000
+            unit: ft
+          mach: 0.78
+          ground_distance:
+            value: 200
+            unit: NM
+        mass_ratio: 0.988
+  climb2:
+    parts:
+      - segment: transition
+        target:
+          altitude:
+            value: 34000
+            unit: ft
+          mach: 0.78
+          ground_distance:
+            value: 50
+            unit: NM
+        mass_ratio: 1.0
+  climb:
+    parts:
+      - phase: climb1
+      - phase: climb2
+  descent:
+    parts:
+      - segment: transition
+        target:
+          altitude: 0.
+          mach: 0.
+          ground_distance:
+            value: 250
+            unit: NM
+        mass_ratio: 0.997
+        reserve_mass_ratio: 0.013
+
+routes:
+  main_route:
+    range:
+      value: 2000.
+      unit: NM
+    climb_parts:
+      - phase: climb
+    cruise_part:
+      segment: breguet
+      engine_setting: cruise
+      polar: data:aerodynamics:aircraft:cruise
+    descent_parts:
+      - phase: descent
+
+missions:
+  operational:
+    isa_offset: ~ISA_offset
+    parts:
+      - phase: start
+      - phase: taxi_out
+      - phase: takeoff
+      - route: main_route
diff --git a/src/fastoad/models/performances/mission/openmdao/tests/data/test_mission.xml b/src/fastoad/models/performances/mission/openmdao/tests/data/test_mission.xml
index bc294917d..03aa6af46 100644
--- a/src/fastoad/models/performances/mission/openmdao/tests/data/test_mission.xml
+++ b/src/fastoad/models/performances/mission/openmdao/tests/data/test_mission.xml
@@ -46,6 +46,7 @@
         <TOW units="kg">70000.0</TOW>
         <OWE units="kg">40000.0</OWE>
         <payload units="kg">15000.0</payload>
+        <ISA_offset>10.0</ISA_offset>
         <takeoff>
           <V2 units="m/s" is_input="True">80.0<!--safety speed before reaching 35 feet height in sizing mission--></V2>
           <altitude units="m" is_input="True">0.0<!--altitude at takeoff in sizing mission--></altitude>
diff --git a/src/fastoad/models/performances/mission/openmdao/tests/data/test_mission.yml b/src/fastoad/models/performances/mission/openmdao/tests/data/test_mission.yml
index c8a92d711..b5309f1c7 100644
--- a/src/fastoad/models/performances/mission/openmdao/tests/data/test_mission.yml
+++ b/src/fastoad/models/performances/mission/openmdao/tests/data/test_mission.yml
@@ -1,9 +1,45 @@
 phases:
+  start:
+    parts:
+      - segment: start
+        target:
+          altitude:
+            value: ~
+            unit: ft
+            default: 0.0
+          true_airspeed:
+            value: ~
+            unit: m/s
+            default: 0.0
+  taxi_out:
+    parts:
+      - segment: taxi
+        isa_offset: 0.0
+        thrust_rate: data:mission:operational:taxi_out:thrust_rate
+        true_airspeed:
+          value: data:mission:operational:taxi_out:true_airspeed
+          default: 0.
+        target:
+          time: data:mission:operational:taxi_out:duration
+      - segment: mass_input
+        target:
+          mass: data:mission:operational:TOW
+  takeoff:
+    parts:
+      - segment: transition
+        target:
+          delta_altitude:
+            value: data:mission:operational:takeoff:safety_altitude
+            default: 35
+            unit: ft
+          delta_mass: -data:mission:operational:takeoff:fuel
+          true_airspeed: data:mission:operational:takeoff:V2
   initial_climb:
     engine_setting: takeoff
     polar: data:aerodynamics:aircraft:takeoff
     thrust_rate: 1.0
     time_step: 0.2
+    isa_offset: 0.0
     parts:
       - segment: altitude_change
         target:
@@ -99,6 +135,9 @@ missions:
       - route: main_route
   operational:
     parts:
+      - phase: start
+      - phase: taxi_out
+      - phase: takeoff
       - route: main_route
       - reserve:
           ref: main_route
diff --git a/src/fastoad/models/performances/mission/openmdao/tests/data/test_with_custom_segment.xml b/src/fastoad/models/performances/mission/openmdao/tests/data/test_with_custom_segment.xml
new file mode 100644
index 000000000..c97588342
--- /dev/null
+++ b/src/fastoad/models/performances/mission/openmdao/tests/data/test_with_custom_segment.xml
@@ -0,0 +1,27 @@
+<!--
+  ~ This file is part of FAST-OAD : A framework for rapid Overall Aircraft Design
+  ~ Copyright (C) 2022 ONERA & ISAE-SUPAERO
+  ~ FAST is free software: you can redistribute it and/or modify
+  ~ it under the terms of the GNU General Public License as published by
+  ~ the Free Software Foundation, either version 3 of the License, or
+  ~ (at your option) any later version.
+  ~ This program is distributed in the hope that it will be useful,
+  ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
+  ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  ~ GNU General Public License for more details.
+  ~ You should have received a copy of the GNU General Public License
+  ~ along with this program.  If not, see <https://www.gnu.org/licenses/>.
+  -->
+
+<FASTOAD_model>
+    <data>
+        <array1 units="kg">[1.0, 2.0, 3.0]</array1>
+        <array2 units="kg">[10.0, 20.0]</array2>
+        <geometry>
+            <wing>
+                <area>120.0</area>
+            </wing>
+        </geometry>
+        <input_mass units="kg">50000.0</input_mass>
+    </data>
+</FASTOAD_model>
diff --git a/src/fastoad/models/performances/mission/openmdao/tests/data/test_with_custom_segment.yml b/src/fastoad/models/performances/mission/openmdao/tests/data/test_with_custom_segment.yml
new file mode 100644
index 000000000..d166d595d
--- /dev/null
+++ b/src/fastoad/models/performances/mission/openmdao/tests/data/test_with_custom_segment.yml
@@ -0,0 +1,27 @@
+phases:
+  test_phase:
+    parts:
+      - segment: start
+        target:
+          altitude: 0.0
+          true_airspeed: 0.0
+          mass:
+            value: data:input_mass
+            unit: kg
+      - segment: test_segment_A
+        scalar_parameter: 42.0
+        vector_parameter_1:
+          value: data:array1
+          unit: g
+        vector_parameter_2: data:array2
+        vector_parameter_3:
+          value: [10.0, 20.0, 30.0]
+          unit: km
+        target:
+          altitude: 0.0
+          true_airspeed: 10.0
+
+missions:
+  test:
+    parts:
+      - phase: test_phase
diff --git a/src/fastoad/models/performances/mission/openmdao/tests/test_mission.py b/src/fastoad/models/performances/mission/openmdao/tests/test_mission.py
index 8bcccbc41..ab91d0352 100644
--- a/src/fastoad/models/performances/mission/openmdao/tests/test_mission.py
+++ b/src/fastoad/models/performances/mission/openmdao/tests/test_mission.py
@@ -17,11 +17,12 @@
 
 import pytest
 from numpy.testing import assert_allclose
-from scipy.constants import foot, knot
+from scipy.constants import foot, knot, nautical_mile
 
 from fastoad._utils.testing import run_system
 from fastoad.io import DataFile
-from ..mission import Mission, MissionComponent
+from ..mission import Mission
+from ..mission_component import MissionComponent
 from ..mission_wrapper import MissionWrapper
 from ...mission_definition.exceptions import FastMissionFileMissingMissionNameError
 
@@ -104,7 +105,8 @@ def test_mission_component(cleanup, with_dummy_plugin_2):
         ivc,
     )
     # plot_flight(problem.model.component.flight_points, "test_mission.png")
-    assert_allclose(problem["data:mission:operational:needed_block_fuel"], 6590.0, atol=1.0)
+
+    # Note: tested value are obtained by asking 1 meter of accuracy for distance routes
 
     assert_allclose(
         problem["data:mission:operational:main_route:initial_climb:duration"], 34.0, atol=1.0
@@ -113,61 +115,72 @@ def test_mission_component(cleanup, with_dummy_plugin_2):
         problem["data:mission:operational:main_route:initial_climb:fuel"], 121.0, atol=1.0
     )
     assert_allclose(
-        problem["data:mission:operational:main_route:initial_climb:distance"], 3600.0, atol=1.0
+        problem["data:mission:operational:main_route:initial_climb:distance"], 3594.0, atol=1.0
     )
 
     assert_allclose(problem["data:mission:operational:main_route:climb:duration"], 236.0, atol=1.0)
     assert_allclose(problem["data:mission:operational:main_route:climb:fuel"], 727.0, atol=1.0)
     assert_allclose(
-        problem["data:mission:operational:main_route:climb:distance"], 43065.0, atol=1.0
+        problem["data:mission:operational:main_route:climb:distance"], 42995.0, atol=1.0
     )
 
     assert_allclose(
-        problem["data:mission:operational:main_route:cruise:duration"], 14734.0, atol=1.0
+        problem["data:mission:operational:main_route:cruise:duration"], 14736.0, atol=10.0
     )
-    assert_allclose(problem["data:mission:operational:main_route:cruise:fuel"], 5167.0, atol=1.0)
+    assert_allclose(problem["data:mission:operational:main_route:cruise:fuel"], 5161.0, atol=1.0)
     assert_allclose(
-        problem["data:mission:operational:main_route:cruise:distance"], 3392590.0, atol=1.0
+        problem["data:mission:operational:main_route:cruise:distance"], 3392995.0, atol=500.0
     )
 
     assert_allclose(
-        problem["data:mission:operational:main_route:descent:duration"], 1424.0, atol=1.0
+        problem["data:mission:operational:main_route:descent:duration"], 1424.0, atol=10.0
     )
     assert_allclose(problem["data:mission:operational:main_route:descent:fuel"], 192.0, atol=1.0)
     assert_allclose(
-        problem["data:mission:operational:main_route:descent:distance"], 264451.0, atol=1.0
+        problem["data:mission:operational:main_route:descent:distance"], 264416.0, atol=500.0
+    )
+
+    assert_allclose(problem["data:mission:operational:needed_block_fuel"], 6581.0, atol=1.0)
+    assert_allclose(
+        problem["data:mission:operational:distance"], 2000.0 * nautical_mile, atol=500.0
     )
+    assert_allclose(problem["data:mission:operational:duration"], 16573.0, atol=10.0)
 
 
 def test_mission_component_breguet(cleanup, with_dummy_plugin_2):
 
     input_file_path = pth.join(DATA_FOLDER_PATH, "test_mission.xml")
     ivc = DataFile(input_file_path).to_ivc()
+    ivc.add_output("data:mission:operational:ramp_weight", 70100.0, units="kg")
 
     problem = run_system(
         MissionComponent(
             propulsion_id="test.wrapper.propulsion.dummy_engine",
             out_file=pth.join(RESULTS_FOLDER_PATH, "breguet.csv"),
             use_initializer_iteration=False,
-            mission_wrapper=MissionWrapper(pth.join(DATA_FOLDER_PATH, "test_breguet.yml")),
+            mission_wrapper=MissionWrapper(
+                pth.join(DATA_FOLDER_PATH, "test_breguet_from_block_fuel.yml")
+            ),
             mission_name="operational",
             reference_area_variable="data:geometry:aircraft:reference_area",
         ),
         ivc,
     )
-    # plot_flight(problem.model.component.flight_points, "test_mission.png")
-    assert_allclose(problem["data:mission:operational:needed_block_fuel"], 6353.0, atol=1.0)
+    # plot_flight(problem.model.component.flight_points, "test_mission_component_breguet.png")
+    assert_allclose(problem["data:mission:operational:needed_block_fuel"], 6256.0, atol=1.0)
+
+    assert_allclose(problem["data:mission:operational:taxi_out:fuel"], 100.0, atol=1.0)
 
     assert_allclose(problem["data:mission:operational:main_route:climb:duration"], 0.0, atol=1.0)
-    assert_allclose(problem["data:mission:operational:main_route:climb:fuel"], 840.0, atol=1.0)
+    assert_allclose(problem["data:mission:operational:main_route:climb:fuel"], 839.0, atol=1.0)
     assert_allclose(
         problem["data:mission:operational:main_route:climb:distance"], 463000.0, atol=1.0
     )
 
     assert_allclose(
-        problem["data:mission:operational:main_route:cruise:duration"], 11956.0, atol=1.0
+        problem["data:mission:operational:main_route:cruise:duration"], 11695.0, atol=1.0
     )
-    assert_allclose(problem["data:mission:operational:main_route:cruise:fuel"], 4294.0, atol=1.0)
+    assert_allclose(problem["data:mission:operational:main_route:cruise:fuel"], 4197.0, atol=1.0)
     assert_allclose(
         problem["data:mission:operational:main_route:cruise:distance"], 2778000.0, atol=1.0
     )
@@ -179,7 +192,7 @@ def test_mission_component_breguet(cleanup, with_dummy_plugin_2):
     )
 
 
-def test_mission_group_without_loop(cleanup, with_dummy_plugin_2):
+def test_mission_group_without_fuel_adjustment(cleanup, with_dummy_plugin_2):
     input_file_path = pth.join(DATA_FOLDER_PATH, "test_mission.xml")
     ivc = DataFile(input_file_path).to_ivc()
 
@@ -192,6 +205,7 @@ def test_mission_group_without_loop(cleanup, with_dummy_plugin_2):
                 mission_file_path=pth.join(DATA_FOLDER_PATH, "test_mission.yml"),
                 adjust_fuel=False,
                 reference_area_variable="data:geometry:aircraft:reference_area",
+                add_solver=True,
             ),
             ivc,
         )
@@ -205,15 +219,20 @@ def test_mission_group_without_loop(cleanup, with_dummy_plugin_2):
             mission_name="operational",
             adjust_fuel=False,
             reference_area_variable="data:geometry:aircraft:reference_area",
+            add_solver=True,
         ),
         ivc,
     )
-    assert_allclose(problem["data:mission:operational:needed_block_fuel"], 6589.0, atol=1.0)
-    assert_allclose(problem["data:mission:operational:block_fuel"], 15195.0, atol=1.0)
+    assert_allclose(
+        problem["data:mission:operational:consumed_fuel_before_input_weight"], 100.4, atol=0.1
+    )
+    assert_allclose(problem["data:mission:operational:ZFW"], 55000.0, atol=1.0)
+    assert_allclose(problem["data:mission:operational:needed_block_fuel"], 6581.0, atol=1.0)
+    assert_allclose(problem["data:mission:operational:block_fuel"], 15100.0, atol=1.0)
 
 
-def test_mission_group_breguet_without_loop(cleanup, with_dummy_plugin_2):
-    input_file_path = pth.join(DATA_FOLDER_PATH, "test_mission.xml")
+def test_mission_group_breguet_without_fuel_adjustment(cleanup, with_dummy_plugin_2):
+    input_file_path = pth.join(DATA_FOLDER_PATH, "test_breguet.xml")
     ivc = DataFile(input_file_path).to_ivc()
 
     problem = run_system(
@@ -227,11 +246,13 @@ def test_mission_group_breguet_without_loop(cleanup, with_dummy_plugin_2):
         ),
         ivc,
     )
-    assert_allclose(problem["data:mission:operational:needed_block_fuel"], 6353.0, atol=1.0)
-    assert_allclose(problem["data:mission:operational:block_fuel"], 15195.0, atol=1.0)
+    assert_allclose(problem["data:mission:operational:consumed_fuel_before_input_weight"], 0.0)
+    assert_allclose(problem["data:mission:operational:needed_block_fuel"], 6245.0, atol=1.0)
+    assert_allclose(problem["data:mission:operational:ZFW"], 55000.0, atol=1.0)
+    assert_allclose(problem["data:mission:operational:block_fuel"], 15000.0, atol=1.0)
 
 
-def test_mission_group_with_loop(cleanup, with_dummy_plugin_2):
+def test_mission_group_with_fuel_adjustment(cleanup, with_dummy_plugin_2):
 
     input_file_path = pth.join(DATA_FOLDER_PATH, "test_mission.xml")
     vars = DataFile(input_file_path)
@@ -253,32 +274,30 @@ def test_mission_group_with_loop(cleanup, with_dummy_plugin_2):
 
     # check loop
     assert_allclose(
-        problem["data:mission:operational:TOW"],
-        problem["data:mission:operational:OWE"]
-        + problem["data:mission:operational:payload"]
-        + problem["data:mission:operational:onboard_fuel_at_takeoff"],
+        problem["data:mission:operational:ZFW"],
+        problem["data:mission:operational:OWE"] + problem["data:mission:operational:payload"],
         atol=1.0,
     )
     assert_allclose(
-        problem["data:mission:operational:needed_onboard_fuel_at_takeoff"],
-        problem["data:mission:operational:onboard_fuel_at_takeoff"],
+        problem["data:mission:operational:block_fuel"] + problem["data:mission:operational:ZFW"],
+        problem["data:mission:operational:TOW"]
+        + problem["data:mission:operational:consumed_fuel_before_input_weight"],
         atol=1.0,
     )
     assert_allclose(
         problem["data:mission:operational:needed_block_fuel"],
-        problem["data:mission:operational:needed_onboard_fuel_at_takeoff"]
-        + problem["data:mission:operational:taxi_out:fuel"]
-        + problem["data:mission:operational:takeoff:fuel"],
+        problem["data:mission:operational:block_fuel"],
         atol=1.0,
     )
+
     assert_allclose(problem["data:mission:operational:needed_block_fuel"], 5682.0, atol=1.0)
 
 
-def test_mission_group_breguet_with_loop(cleanup, with_dummy_plugin_2):
+def test_mission_group_breguet_with_fuel_adjustment(cleanup, with_dummy_plugin_2):
 
-    input_file_path = pth.join(DATA_FOLDER_PATH, "test_mission.xml")
+    input_file_path = pth.join(DATA_FOLDER_PATH, "test_breguet.xml")
     vars = DataFile(input_file_path)
-    del vars["data:mission:operational:TOW"]
+    del vars["data:mission:operational:ramp_weight"]
     ivc = vars.to_ivc()
 
     problem = run_system(
@@ -295,25 +314,20 @@ def test_mission_group_breguet_with_loop(cleanup, with_dummy_plugin_2):
 
     # check loop
     assert_allclose(
-        problem["data:mission:operational:TOW"],
-        problem["data:mission:operational:OWE"]
-        + problem["data:mission:operational:payload"]
-        + problem["data:mission:operational:onboard_fuel_at_takeoff"],
+        problem["data:mission:operational:ZFW"],
+        problem["data:mission:operational:OWE"] + problem["data:mission:operational:payload"],
         atol=1.0,
     )
     assert_allclose(
-        problem["data:mission:operational:needed_onboard_fuel_at_takeoff"],
-        problem["data:mission:operational:onboard_fuel_at_takeoff"],
+        problem["data:mission:operational:block_fuel"] + problem["data:mission:operational:ZFW"],
+        problem["data:mission:operational:ramp_weight"]
+        + problem["data:mission:operational:consumed_fuel_before_input_weight"],
         atol=1.0,
     )
+    assert_allclose(problem["data:mission:operational:consumed_fuel_before_input_weight"], 0.0)
     assert_allclose(
         problem["data:mission:operational:needed_block_fuel"],
-        problem["data:mission:operational:needed_onboard_fuel_at_takeoff"]
-        + problem["data:mission:operational:taxi_out:fuel"]
-        + problem["data:mission:operational:takeoff:fuel"],
+        problem["data:mission:operational:block_fuel"],
         atol=1.0,
     )
-    assert_allclose(problem["data:mission:operational:needed_block_fuel"], 5626.0, atol=1.0)
-    assert_allclose(
-        problem["data:mission:operational:needed_onboard_fuel_at_takeoff"], 5430.0, atol=1.0
-    )
+    assert_allclose(problem["data:mission:operational:needed_block_fuel"], 5525.0, atol=1.0)
diff --git a/src/fastoad/models/performances/mission/openmdao/tests/test_sizing_breguet.py b/src/fastoad/models/performances/mission/openmdao/tests/test_sizing_breguet.py
index c500f2a36..11342d488 100644
--- a/src/fastoad/models/performances/mission/openmdao/tests/test_sizing_breguet.py
+++ b/src/fastoad/models/performances/mission/openmdao/tests/test_sizing_breguet.py
@@ -46,6 +46,7 @@ def test_sizing_breguet(cleanup, with_dummy_plugin_2):
     ivc.add_output("data:mission:sizing:takeoff:altitude", 0.0)
     ivc.add_output("data:mission:sizing:takeoff:V2", 0.0, units="m/s")
     ivc.add_output("data:mission:sizing:takeoff:fuel", 0.0, units="kg")
+    ivc.add_output("data:mission:sizing:takeoff:duration", 0, units="s")
     ivc.add_output("data:mission:sizing:taxi_out:thrust_rate", 0)
     ivc.add_output("data:mission:sizing:taxi_out:duration", 0, units="s")
     ivc.add_output("data:geometry:wing:area", 0.0, units="m**2")
diff --git a/src/fastoad/models/performances/mission/openmdao/tests/test_sizing_mission.py b/src/fastoad/models/performances/mission/openmdao/tests/test_sizing_mission.py
index c76361704..d9cd0a8e1 100644
--- a/src/fastoad/models/performances/mission/openmdao/tests/test_sizing_mission.py
+++ b/src/fastoad/models/performances/mission/openmdao/tests/test_sizing_mission.py
@@ -42,6 +42,7 @@ def test_sizing_mission(cleanup, with_dummy_plugin_2):
     ivc.add_output("data:mission:sizing:takeoff:altitude", 0.0)
     ivc.add_output("data:mission:sizing:takeoff:V2", 80.0, units="m/s")
     ivc.add_output("data:mission:sizing:takeoff:fuel", 80.0, units="kg")
+    ivc.add_output("data:mission:sizing:takeoff:duration", 0, units="s")
     ivc.add_output("data:mission:sizing:taxi_out:thrust_rate", 0.3)
     ivc.add_output("data:mission:sizing:climb:thrust_rate", 0.9)
     ivc.add_output("data:mission:sizing:descent:thrust_rate", 0.05)
@@ -53,10 +54,12 @@ def test_sizing_mission(cleanup, with_dummy_plugin_2):
 
     ivc.add_output("data:geometry:wing:area", 100.0, units="m**2")
 
-    # Ensure L/D ratio == 16.0
+    # Ensure L/D ratio ~ 17.0 and optimal CL ~ 0.6 (needed for optimal altitude search)
     ivc.add_output("data:aerodynamics:aircraft:cruise:CL", np.linspace(0, 1.5, 150))
-    ivc.add_output("data:aerodynamics:aircraft:cruise:CD", np.linspace(0, 1.5, 150) / 16.0)
-
+    ivc.add_output(
+        "data:aerodynamics:aircraft:cruise:CD", (np.linspace(0, 1.5, 150)) ** 2 * 0.05 + 0.017
+    )
+    # For low speed polar, ensuring L/D ratio = 16.0 is enough
     ivc.add_output("data:aerodynamics:aircraft:takeoff:CL", np.linspace(0, 1.5, 150) + 0.5)
     ivc.add_output("data:aerodynamics:aircraft:takeoff:CD", np.linspace(0, 1.5, 150) / 16.0)
 
@@ -71,6 +74,12 @@ def test_sizing_mission(cleanup, with_dummy_plugin_2):
         ),
         ivc,
     )
+    # import pandas as pd
+    #
+    # plot_flight(
+    #     pd.read_csv(pth.join(RESULTS_FOLDER_PATH, "sizing_mission.csv")),
+    #     pth.join(RESULTS_FOLDER_PATH, "sizing_mission.png"),
+    # )
 
     assert_allclose(problem["data:mission:sizing:taxi_out:fuel"], 351.0, atol=1)
     assert_allclose(problem["data:mission:sizing:taxi_out:duration"], 500.0, atol=1)
@@ -82,35 +91,35 @@ def test_sizing_mission(cleanup, with_dummy_plugin_2):
         problem["data:mission:sizing:main_route:initial_climb:distance"], 3168.0, atol=1
     )
 
-    assert_allclose(problem["data:mission:sizing:main_route:climb:fuel"], 992.0, atol=1)
-    assert_allclose(problem["data:mission:sizing:main_route:climb:duration"], 322.0, atol=1)
-    assert_allclose(problem["data:mission:sizing:main_route:climb:distance"], 61782.0, atol=1)
+    assert_allclose(problem["data:mission:sizing:main_route:climb:fuel"], 657.0, atol=1)
+    assert_allclose(problem["data:mission:sizing:main_route:climb:duration"], 213.0, atol=1)
+    assert_allclose(problem["data:mission:sizing:main_route:climb:distance"], 37466.0, atol=1)
 
-    assert_allclose(problem["data:mission:sizing:main_route:cruise:fuel"], 5454.0, atol=1)
-    assert_allclose(problem["data:mission:sizing:main_route:cruise:duration"], 14344.0, atol=1)
-    assert_allclose(problem["data:mission:sizing:main_route:cruise:distance"], 3301322.0, atol=1)
+    assert_allclose(problem["data:mission:sizing:main_route:cruise:fuel"], 5169.0, atol=1)
+    assert_allclose(problem["data:mission:sizing:main_route:cruise:duration"], 14555.0, atol=10)
+    assert_allclose(problem["data:mission:sizing:main_route:cruise:distance"], 3441990.0, atol=500)
 
-    assert_allclose(problem["data:mission:sizing:main_route:descent:fuel"], 161.0, atol=1)
-    assert_allclose(problem["data:mission:sizing:main_route:descent:duration"], 1705.0, atol=1)
-    assert_allclose(problem["data:mission:sizing:main_route:descent:distance"], 337731.0, atol=1)
+    assert_allclose(problem["data:mission:sizing:main_route:descent:fuel"], 115.0, atol=1)
+    assert_allclose(problem["data:mission:sizing:main_route:descent:duration"], 1220.0, atol=10)
+    assert_allclose(problem["data:mission:sizing:main_route:descent:distance"], 221228.0, atol=500)
 
     assert_allclose(problem["data:mission:sizing:diversion:diversion_climb:fuel"], 471.0, atol=1)
     assert_allclose(
         problem["data:mission:sizing:diversion:diversion_climb:duration"], 153.0, atol=1
     )
     assert_allclose(
-        problem["data:mission:sizing:diversion:diversion_climb:distance"], 24747.0, atol=1
+        problem["data:mission:sizing:diversion:diversion_climb:distance"], 24768.0, atol=1
     )
 
-    assert_allclose(problem["data:mission:sizing:diversion:cruise:fuel"], 316.0, atol=1)
-    assert_allclose(problem["data:mission:sizing:diversion:cruise:duration"], 880.0, atol=1)
-    assert_allclose(problem["data:mission:sizing:diversion:cruise:distance"], 192547.0, atol=1)
+    assert_allclose(problem["data:mission:sizing:diversion:cruise:fuel"], 289.0, atol=1)
+    assert_allclose(problem["data:mission:sizing:diversion:cruise:duration"], 832.0, atol=1)
+    assert_allclose(problem["data:mission:sizing:diversion:cruise:distance"], 182125.0, atol=1)
 
-    assert_allclose(problem["data:mission:sizing:diversion:descent:fuel"], 86.0, atol=1)
-    assert_allclose(problem["data:mission:sizing:diversion:descent:duration"], 908.0, atol=1)
-    assert_allclose(problem["data:mission:sizing:diversion:descent:distance"], 153105.0, atol=1)
+    assert_allclose(problem["data:mission:sizing:diversion:descent:fuel"], 92.0, atol=1)
+    assert_allclose(problem["data:mission:sizing:diversion:descent:duration"], 973.0, atol=1)
+    assert_allclose(problem["data:mission:sizing:diversion:descent:distance"], 163507.0, atol=1)
 
-    assert_allclose(problem["data:mission:sizing:holding:fuel"], 639.0, atol=1)
+    assert_allclose(problem["data:mission:sizing:holding:fuel"], 601.0, atol=1)
     assert_allclose(problem.get_val("data:mission:sizing:holding:duration", "s"), 1800.0, atol=1)
     assert_allclose(problem["data:mission:sizing:holding:distance"], 236664.0, atol=1)
 
@@ -118,26 +127,20 @@ def test_sizing_mission(cleanup, with_dummy_plugin_2):
     assert_allclose(problem["data:mission:sizing:taxi_in:duration"], 500.0, atol=1)
     assert_allclose(problem["data:mission:sizing:taxi_in:distance"], 0.0, atol=1)
 
-    assert_allclose(problem["data:mission:sizing:main_route:fuel"], 6715.0, atol=1)
-    assert_allclose(problem["data:mission:sizing:main_route:duration"], 16401.0, atol=1)
-    assert_allclose(problem["data:mission:sizing:main_route:distance"], 3704004.0, atol=10)
+    assert_allclose(problem["data:mission:sizing:main_route:fuel"], 6049.0, atol=1)
+    assert_allclose(problem["data:mission:sizing:main_route:duration"], 16019.0, atol=10)
+    assert_allclose(problem["data:mission:sizing:main_route:distance"], 3703852.0, atol=500)
 
-    assert_allclose(problem["data:mission:sizing:diversion:fuel"], 873.0, atol=1)
-    assert_allclose(problem["data:mission:sizing:diversion:duration"], 1941.0, atol=1)
+    assert_allclose(problem["data:mission:sizing:diversion:fuel"], 852.0, atol=1)
+    assert_allclose(problem["data:mission:sizing:diversion:duration"], 1958.0, atol=10)
     assert_allclose(
-        problem.get_val("data:mission:sizing:diversion:distance", "m"), 370400.0, atol=10
+        problem.get_val("data:mission:sizing:diversion:distance", "m"), 370400.0, atol=500
     )
 
-    assert_allclose(
-        problem["data:mission:sizing:fuel"]
-        + problem["data:mission:sizing:taxi_out:fuel"]
-        + problem["data:mission:sizing:takeoff:fuel"],
-        9009.0,
-        atol=1,
-    )
-    assert_allclose(problem["data:mission:sizing:duration"], 20643.0, atol=1)
-    assert_allclose(problem["data:mission:sizing:distance"], 4311067.0, atol=10)
+    assert_allclose(problem["data:mission:sizing:fuel"], 8286.0, atol=1)
+    assert_allclose(problem["data:mission:sizing:duration"], 20777.0, atol=10)
+    assert_allclose(problem["data:mission:sizing:distance"], 4310916.0, atol=500)
 
-    assert_allclose(problem["data:mission:sizing:reserve:fuel"], 201.0, atol=1)
+    assert_allclose(problem["data:mission:sizing:reserve:fuel"], 181.0, atol=1)
 
-    assert_allclose(problem["data:mission:sizing:needed_block_fuel"], 9210.0, atol=1)
+    assert_allclose(problem["data:mission:sizing:needed_block_fuel"], 8467.0, atol=1)
diff --git a/src/fastoad/models/performances/mission/openmdao/tests/test_with_custom_segment.py b/src/fastoad/models/performances/mission/openmdao/tests/test_with_custom_segment.py
new file mode 100644
index 000000000..7d1a7558d
--- /dev/null
+++ b/src/fastoad/models/performances/mission/openmdao/tests/test_with_custom_segment.py
@@ -0,0 +1,73 @@
+#  This file is part of FAST-OAD : A framework for rapid Overall Aircraft Design
+#  Copyright (C) 2022 ONERA & ISAE-SUPAERO
+#  FAST is free software: you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation, either version 3 of the License, or
+#  (at your option) any later version.
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#  You should have received a copy of the GNU General Public License
+#  along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+import os.path as pth
+from dataclasses import dataclass
+from shutil import rmtree
+
+import numpy as np
+import pandas as pd
+import pytest
+
+from fastoad._utils.testing import run_system
+from fastoad.io import DataFile
+from fastoad.model_base.datacls import MANDATORY_FIELD
+from ..mission_component import MissionComponent
+from ..mission_wrapper import MissionWrapper
+from ...segments.base import AbstractFlightSegment
+
+# noinspection PyUnresolvedReferences
+from ...segments.start import Start
+
+DATA_FOLDER_PATH = pth.join(pth.dirname(__file__), "data")
+RESULTS_FOLDER_PATH = pth.join(pth.dirname(__file__), "results")
+
+
+@pytest.fixture(scope="module")
+def cleanup():
+    rmtree(RESULTS_FOLDER_PATH, ignore_errors=True)
+
+
+@dataclass
+class TestSegment(AbstractFlightSegment, mission_file_keyword="test_segment_A"):
+    scalar_parameter: float = MANDATORY_FIELD
+    vector_parameter_1: np.ndarray = MANDATORY_FIELD
+    vector_parameter_2: np.ndarray = MANDATORY_FIELD
+    vector_parameter_3: np.ndarray = MANDATORY_FIELD
+
+    def compute_from(self, start) -> pd.DataFrame:
+        return pd.DataFrame([start])
+
+    def compute_from_start_to_target(self, start, target) -> pd.DataFrame:
+        pass
+
+
+TestSegment._attribute_units["vector_parameter_1"] = "kg"
+TestSegment._attribute_units["vector_parameter_3"] = "m"
+
+
+def test_with_custom_segment(cleanup, with_dummy_plugin_2):
+    input_file_path = pth.join(DATA_FOLDER_PATH, "test_with_custom_segment.xml")
+    ivc = DataFile(input_file_path).to_ivc()
+
+    problem = run_system(
+        MissionComponent(
+            propulsion_id="test.wrapper.propulsion.dummy_engine",
+            use_initializer_iteration=False,
+            mission_wrapper=MissionWrapper(
+                pth.join(DATA_FOLDER_PATH, "test_with_custom_segment.yml")
+            ),
+            mission_name="test",
+        ),
+        ivc,
+    )
diff --git a/src/fastoad/models/performances/mission/segments/tests/test_altitude_change.py b/src/fastoad/models/performances/mission/segments/tests/test_altitude_change.py
index 49bb1f10d..94a4eff0f 100644
--- a/src/fastoad/models/performances/mission/segments/tests/test_altitude_change.py
+++ b/src/fastoad/models/performances/mission/segments/tests/test_altitude_change.py
@@ -35,24 +35,31 @@ def test_climb_fixed_altitude_at_constant_TAS(polar):
     )  # 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
+    def run():
+        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
+
+    run()
+
+    # A second call is done to ensure first run did not modify anything (like target definition)
+    run()
 
 
 def test_climb_fixed_altitude_at_constant_EAS(polar):
     propulsion = FuelEngineSet(DummyEngine(1.0e5, 1.0e-5), 2)
 
-    flight_points = AltitudeChangeSegment(
+    segment = AltitudeChangeSegment(
         target=FlightPoint(altitude=10000.0, equivalent_airspeed="constant"),
         propulsion=propulsion,
         reference_area=120.0,
@@ -60,25 +67,47 @@ def test_climb_fixed_altitude_at_constant_EAS(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))
+    )
+
+    def run():
+        flight_points = segment.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
+        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.ground_distance, 20915.0, rtol=1e-3)
+        assert last_point.engine_setting == EngineSetting.CRUISE
+
+        if segment.isa_offset == 0.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)
+
+        if segment.isa_offset == 15.0:
+            assert_allclose(last_point.time, 141.2, rtol=1e-2)
+            assert_allclose(first_point.true_airspeed, 132.7, atol=0.1)
+            assert_allclose(last_point.true_airspeed, 178.0, atol=0.1)
+            assert_allclose(last_point.mass, 69718.0, rtol=1e-4)
+
+    run()
+
+    # A second call is done to ensure first run did not modify anything (like target definition)
+    run()
+
+    # Test with non-zero dISA
+    segment.isa_offset = 15.0
+    run()
 
 
 def test_climb_optimal_altitude_at_fixed_TAS(polar):
     propulsion = FuelEngineSet(DummyEngine(1.0e5, 1.0e-5), 2)
 
-    flight_points = AltitudeChangeSegment(
+    segment = AltitudeChangeSegment(
         target=FlightPoint(
             altitude=AltitudeChangeSegment.OPTIMAL_ALTITUDE, true_airspeed="constant"
         ),
@@ -87,22 +116,32 @@ def test_climb_optimal_altitude_at_fixed_TAS(polar):
         polar=polar,
         thrust_rate=1.0,
         time_step=2.0,
-    ).compute_from(FlightPoint(altitude=5000.0, true_airspeed=250.0, mass=70000.0))
+    )
+
+    def run():
+        flight_points = segment.compute_from(
+            FlightPoint(altitude=5000.0, true_airspeed=250.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, 10085.0, atol=0.1)
+        assert_allclose(last_point.true_airspeed, 250.0)
+        assert_allclose(last_point.time, 84.1, rtol=1e-2)
+        assert_allclose(last_point.mach, 0.8359, rtol=1e-4)
+        assert_allclose(last_point.mass, 69832.0, rtol=1e-4)
+        assert_allclose(last_point.ground_distance, 20401.0, rtol=1e-3)
 
-    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, 10085.0, atol=0.1)
-    assert_allclose(last_point.true_airspeed, 250.0)
-    assert_allclose(last_point.time, 84.1, rtol=1e-2)
-    assert_allclose(last_point.mach, 0.8359, rtol=1e-4)
-    assert_allclose(last_point.mass, 69832.0, rtol=1e-4)
-    assert_allclose(last_point.ground_distance, 20401.0, rtol=1e-3)
+    run()
+
+    # A second call is done to ensure first run did not modify anything (like target definition)
+    run()
 
 
 def test_climb_optimal_flight_level_at_fixed_TAS(polar):
     propulsion = FuelEngineSet(DummyEngine(1.0e5, 1.0e-5), 2)
 
-    flight_points = AltitudeChangeSegment(
+    segment = AltitudeChangeSegment(
         target=FlightPoint(
             altitude=AltitudeChangeSegment.OPTIMAL_FLIGHT_LEVEL, true_airspeed="constant"
         ),
@@ -111,44 +150,62 @@ def test_climb_optimal_flight_level_at_fixed_TAS(polar):
         polar=polar,
         thrust_rate=1.0,
         time_step=2.0,
-    ).compute_from(FlightPoint(altitude=5000.0, true_airspeed=250.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(flight_points.true_airspeed, 250.0)
-    assert_allclose(last_point.altitude / foot, 32000.0, atol=0.1)
-    assert_allclose(last_point.time, 78.7, rtol=1e-2)
-    assert_allclose(last_point.mach, 0.8318, rtol=1e-4)
-    assert_allclose(last_point.mass, 69843.0, rtol=1e-4)
-    assert_allclose(last_point.ground_distance, 19091.0, rtol=1e-3)
+    def run():
+        flight_points = segment.compute_from(
+            FlightPoint(altitude=5000.0, true_airspeed=250.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(flight_points.true_airspeed, 250.0)
+        assert_allclose(last_point.altitude / foot, 32000.0, atol=0.1)
+        assert_allclose(last_point.time, 78.7, rtol=1e-2)
+        assert_allclose(last_point.mach, 0.8318, rtol=1e-4)
+        assert_allclose(last_point.mass, 69843.0, rtol=1e-4)
+        assert_allclose(last_point.ground_distance, 19091.0, rtol=1e-3)
+
+    run()
+
+    # A second call is done to ensure first run did not modify anything (like target definition)
+    run()
 
 
 def test_climb_optimal_flight_level_at_fixed_mach(polar):
     propulsion = FuelEngineSet(DummyEngine(1.0e5, 1.0e-5), 2)
 
-    flight_points = AltitudeChangeSegment(
+    segment = 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))
+    )
+
+    def run():
+        flight_points = segment.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)
+        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)
+
+    run()
+
+    # A second call is done to ensure first run did not modify anything (like target definition)
+    run()
 
 
 def test_climb_optimal_flight_level_at_fixed_mach_with_capped_flight_level(polar):
     propulsion = FuelEngineSet(DummyEngine(1.0e5, 1.0e-5), 2)
 
-    flight_points = AltitudeChangeSegment(
+    segment = AltitudeChangeSegment(
         target=FlightPoint(altitude=AltitudeChangeSegment.OPTIMAL_FLIGHT_LEVEL, mach="constant"),
         propulsion=propulsion,
         reference_area=120.0,
@@ -156,16 +213,24 @@ def test_climb_optimal_flight_level_at_fixed_mach_with_capped_flight_level(polar
         thrust_rate=1.0,
         maximum_flight_level=300.0,
         time_step=0.01,
-    ).compute_from(FlightPoint(altitude=5000.0, mach=0.82, mass=70000.0))
+    )
+
+    def run():
+        flight_points = segment.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, 30000.0, atol=0.1)
+        assert_allclose(last_point.time, 67.5, rtol=1e-2)
+        assert_allclose(last_point.true_airspeed, 248.6, rtol=1e-4)
+        assert_allclose(last_point.mass, 69865.0, rtol=1e-4)
+        assert_allclose(last_point.ground_distance, 16762.0, rtol=1e-3)
 
-    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, 30000.0, atol=0.1)
-    assert_allclose(last_point.time, 67.5, rtol=1e-2)
-    assert_allclose(last_point.true_airspeed, 248.6, rtol=1e-4)
-    assert_allclose(last_point.mass, 69865.0, rtol=1e-4)
-    assert_allclose(last_point.ground_distance, 16762.0, rtol=1e-3)
+    run()
+
+    # A second call is done to ensure first run did not modify anything (like target definition)
+    run()
 
 
 def test_climb_not_enough_thrust(polar):
@@ -187,65 +252,91 @@ def test_climb_not_enough_thrust(polar):
 def test_descent_to_fixed_altitude_at_constant_TAS(polar):
     propulsion = FuelEngineSet(DummyEngine(1.0e5, 1.0e-5), 2)
 
-    flight_points = AltitudeChangeSegment(
+    segment = AltitudeChangeSegment(
         target=FlightPoint(altitude=5000.0, true_airspeed="constant"),
         propulsion=propulsion,
         reference_area=100.0,
         polar=polar,
         thrust_rate=0.1,
         time_step=2.0,
-    ).compute_from(
-        FlightPoint(altitude=10000.0, true_airspeed=200.0, mass=70000.0, time=2000.0)
-    )  # And we define a non-null start time
+    )
+
+    def run():
+        flight_points = segment.compute_from(
+            FlightPoint(altitude=10000.0, true_airspeed=200.0, mass=70000.0, time=2000.0)
+        )  # And we define a non-null start time
+
+        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.true_airspeed, 200.0)
+        assert_allclose(last_point.time, 3370.4, rtol=1e-2)
+        assert_allclose(last_point.mass, 69849.0, rtol=1e-4)
+        assert_allclose(last_point.ground_distance, 274043.0, rtol=1e-3)
+
+    run()
 
-    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.true_airspeed, 200.0)
-    assert_allclose(last_point.time, 3370.4, rtol=1e-2)
-    assert_allclose(last_point.mass, 69849.0, rtol=1e-4)
-    assert_allclose(last_point.ground_distance, 274043.0, rtol=1e-3)
+    # A second call is done to ensure first run did not modify anything (like target definition)
+    run()
 
 
 def test_descent_to_fixed_altitude_at_constant_EAS(polar):
     propulsion = FuelEngineSet(DummyEngine(1.0e5, 1.0e-5), 2)
 
-    flight_points = AltitudeChangeSegment(
+    segment = 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))
+    )
+
+    def run():
+        flight_points = segment.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)
+        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)
+
+    run()
+
+    # A second call is done to ensure first run did not modify anything (like target definition)
+    run()
 
 
 def test_descent_to_fixed_EAS_at_constant_mach(polar):
     propulsion = FuelEngineSet(DummyEngine(1.0e5, 1.0e-5), 2)
 
-    flight_points = AltitudeChangeSegment(
+    segment = 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)
+    )
+
+    def run():
+        flight_points = segment.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)
+
+    run()
+
+    # A second call is done to ensure first run did not modify anything (like target definition)
+    run()
diff --git a/src/fastoad/models/performances/mission/segments/tests/test_cruise.py b/src/fastoad/models/performances/mission/segments/tests/test_cruise.py
index 853da5b6a..a65c52a43 100644
--- a/src/fastoad/models/performances/mission/segments/tests/test_cruise.py
+++ b/src/fastoad/models/performances/mission/segments/tests/test_cruise.py
@@ -17,7 +17,7 @@
 from fastoad.constants import EngineSetting
 from fastoad.model_base import FlightPoint
 from fastoad.model_base.propulsion import FuelEngineSet
-from .conftest import DummyEngine, print_dataframe
+from .conftest import DummyEngine
 from ..altitude_change import AltitudeChangeSegment
 from ..cruise import (
     BreguetCruiseSegment,
@@ -37,21 +37,30 @@ def test_cruise_at_constant_altitude(polar):
         polar=polar,
         engine_setting=EngineSetting.CRUISE,  # The engine model does not use this setting
     )
-    flight_points = segment.compute_from(FlightPoint(mass=70000.0, altitude=10000.0, mach=0.78))
 
-    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, 10000.0)
-    assert_allclose(first_point.true_airspeed, 233.6, atol=0.1)
-    assert first_point.engine_setting == EngineSetting.CRUISE
+    def run():
+        flight_points = segment.compute_from(
+            FlightPoint(mass=70000.0, altitude=10000.0, mach=0.78, ground_distance=1000.0)
+        )
 
-    assert_allclose(last_point.ground_distance, 500000.0)
-    assert_allclose(last_point.altitude, 10000.0)
-    assert_allclose(last_point.time, 2141.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
+        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, 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, 501000.0)
+        assert_allclose(last_point.altitude, 10000.0)
+        assert_allclose(last_point.time, 2141.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
+
+    run()
+
+    # A second call is done to ensure first run did not modify anything (like target definition)
+    run()
 
 
 def test_breguet_cruise(polar):
@@ -64,26 +73,31 @@ def test_breguet_cruise(polar):
         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)
+    def run():
+        flight_points = segment.compute_from(
+            FlightPoint(time=10000.0, mass=70000.0, altitude=10000.0, mach=0.78)
+        )
+
+        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
 
-    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
 
-    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
+    run()
+
+    # A second call is done to ensure first run did not modify anything (like target definition)
+    run()
 
 
 def test_optimal_cruise(polar):
@@ -96,26 +110,33 @@ def test_optimal_cruise(polar):
         polar=polar,
         engine_setting=EngineSetting.CRUISE,
     )
-    flight_points = segment.compute_from(
-        FlightPoint(mass=70000.0, time=1000.0, ground_distance=1e5, mach=0.78)
-    )
 
-    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
+    def run():
+        flight_points = segment.compute_from(
+            FlightPoint(mass=70000.0, time=1000.0, ground_distance=1e5, mach=0.78)
+        )
+
+        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
 
-    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
+    run()
+
+    # A second call is done to ensure first run did not modify anything (like target definition)
+    run()
 
 
 def test_climb_and_cruise_at_optimal_flight_level(polar):
@@ -140,26 +161,32 @@ def test_climb_and_cruise_at_optimal_flight_level(polar):
         ),
     )
 
-    flight_points = segment.compute_from(
-        FlightPoint(mass=70000.0, altitude=8000.0, mach=0.78, ground_distance=1.0e6)
-    )
+    def run():
+        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
 
-    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(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.ground_distance, 11.0e6)
+        assert_allclose(last_point.altitude, 9753.6)
+        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
 
-    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
+    run()
+
+    # A second call is done to ensure first run did not modify anything (like target definition)
+    run()
 
 
 def test_climb_and_cruise_at_optimal_flight_level_with_capped_flight_level(polar):
@@ -183,24 +210,30 @@ def test_climb_and_cruise_at_optimal_flight_level_with_capped_flight_level(polar
         maximum_flight_level=300.0,
     )
 
-    flight_points = segment.compute_from(
-        FlightPoint(mass=70000.0, altitude=8000.0, mach=0.78, ground_distance=1.0e6)
-    )
+    def run():
+        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
+        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_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_allclose(flight_points.mach, 0.78)
-    assert_allclose(last_point.altitude, 9144.0)
-    assert_allclose(last_point.ground_distance, 11.0e6)
-    assert_allclose(last_point.time, 42287.0, rtol=1e-3)
-    assert_allclose(last_point.true_airspeed, 236.5, atol=0.1)
-    assert_allclose(last_point.mass, 48807.0, rtol=1e-4)
+        assert_allclose(flight_points.mach, 0.78)
+        assert_allclose(last_point.ground_distance, 11.0e6)
+        assert_allclose(last_point.altitude, 9144.0)
+        assert_allclose(last_point.time, 42287.0, rtol=1e-3)
+        assert_allclose(last_point.true_airspeed, 236.5, atol=0.1)
+        assert_allclose(last_point.mass, 48807.0, rtol=1e-4)
+
+    run()
+
+    # A second call is done to ensure first run did not modify anything (like target definition)
+    run()
 
 
 def test_climb_and_cruise_at_optimal_flight_level_with_start_at_exact_flight_level(polar):
@@ -223,20 +256,26 @@ def test_climb_and_cruise_at_optimal_flight_level_with_start_at_exact_flight_lev
         ),
     )
 
-    # Start at exact optimum flight level ==========================================================
-    flight_points = segment.compute_from(FlightPoint(mass=70000.0, altitude=9753.6, mach=0.78))
+    def run():
+        # Start at exact optimum flight level ======================================================
+        flight_points = segment.compute_from(FlightPoint(mass=70000.0, altitude=9753.6, mach=0.78))
+
+        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, 9753.6)
+        assert_allclose(first_point.thrust_rate, 0.9)
+        assert_allclose(first_point.true_airspeed, 234.4, atol=0.1)
 
-    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(flight_points.mach, 0.78)
+        assert_allclose(last_point.altitude, 9753.6)
+        assert_allclose(last_point.ground_distance, 10.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, 48987.0, rtol=1e-4)
 
-    assert_allclose(first_point.altitude, 9753.6)
-    assert_allclose(first_point.thrust_rate, 0.9)
-    assert_allclose(first_point.true_airspeed, 234.4, atol=0.1)
+    run()
 
-    assert_allclose(flight_points.mach, 0.78)
-    assert_allclose(last_point.altitude, 9753.6)
-    assert_allclose(last_point.ground_distance, 10.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, 48987.0, rtol=1e-4)
+    # A second call is done to ensure first run did not modify anything (like target definition)
+    run()
diff --git a/src/fastoad/models/performances/mission/segments/tests/test_hold.py b/src/fastoad/models/performances/mission/segments/tests/test_hold.py
index 57b645779..54f3f7249 100644
--- a/src/fastoad/models/performances/mission/segments/tests/test_hold.py
+++ b/src/fastoad/models/performances/mission/segments/tests/test_hold.py
@@ -31,14 +31,30 @@ def test_hold(polar):
         polar=polar,
         engine_setting=EngineSetting.CRUISE,  # The engine model does not use this setting
     )
-    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)
-    assert last_point.engine_setting == EngineSetting.CRUISE
+    def run():
+        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)
+        if segment.isa_offset == 0.0:
+            assert_allclose(last_point.true_airspeed, 256.1, atol=0.1)
+            assert_allclose(last_point.ground_distance, 768323.0, rtol=1.0e-3)
+        if segment.isa_offset == 15.0:
+            assert_allclose(last_point.true_airspeed, 262.8, atol=0.1)
+            assert_allclose(last_point.ground_distance, 788289.0, rtol=1.0e-3)
+        assert last_point.engine_setting == EngineSetting.CRUISE
+
+    run()
+
+    # A second call is done to ensure first run did not modify anything (like target definition)
+    run()
+
+    # Test with non-zero dISA
+    segment.isa_offset = 15.0
+    run()
diff --git a/src/fastoad/models/performances/mission/segments/tests/test_mass_target.py b/src/fastoad/models/performances/mission/segments/tests/test_mass_target.py
new file mode 100644
index 000000000..06fac33ba
--- /dev/null
+++ b/src/fastoad/models/performances/mission/segments/tests/test_mass_target.py
@@ -0,0 +1,31 @@
+#  This file is part of FAST-OAD : A framework for rapid Overall Aircraft Design
+#  Copyright (C) 2022 ONERA & ISAE-SUPAERO
+#  FAST is free software: you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation, either version 3 of the License, or
+#  (at your option) any later version.
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#  You should have received a copy of the GNU General Public License
+#  along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+from numpy.testing import assert_allclose
+
+from fastoad.model_base import FlightPoint
+from ..mass_input import MassTargetSegment
+
+
+def test_dummy_target_mass():
+    dummy_target_mass = MassTargetSegment(target=FlightPoint(mass=70.0e5))
+
+    flight_points = dummy_target_mass.compute_from(
+        FlightPoint(altitude=10.0, time=1000.0, mach=0.3, mass=100.0e5)
+    )
+    assert len(flight_points) == 1
+    point = flight_points.iloc[-1]
+    assert_allclose(point.altitude, 10.0, atol=1.0)
+    assert_allclose(point.time, 1000.0, rtol=1e-2)
+    assert_allclose(point.mach, 0.3, atol=0.001)
+    assert_allclose(point.mass, 70.0e5, rtol=1e-4)
diff --git a/src/fastoad/models/performances/mission/segments/tests/test_speed_change.py b/src/fastoad/models/performances/mission/segments/tests/test_speed_change.py
index b6fa5a697..817b74cca 100644
--- a/src/fastoad/models/performances/mission/segments/tests/test_speed_change.py
+++ b/src/fastoad/models/performances/mission/segments/tests/test_speed_change.py
@@ -33,24 +33,31 @@ def test_acceleration_to_TAS(polar):
         time_step=0.2,
         engine_setting=EngineSetting.CLIMB,  # The engine model does not use this setting
     )
-    flight_points = segment.compute_from(
-        FlightPoint(altitude=5000.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, 103.3, rtol=1e-2)
-    assert_allclose(last_point.altitude, 5000.0)
-    assert_allclose(last_point.true_airspeed, 250.0)
-    assert_allclose(last_point.mass, 69896.0, rtol=1e-4)
-    assert_allclose(last_point.ground_distance, 20697.0, rtol=1e-3)
-    assert last_point.engine_setting == EngineSetting.CLIMB
+    def run():
+        flight_points = segment.compute_from(
+            FlightPoint(altitude=5000.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, 103.3, rtol=1e-2)
+        assert_allclose(last_point.altitude, 5000.0)
+        assert_allclose(last_point.true_airspeed, 250.0)
+        assert_allclose(last_point.mass, 69896.0, rtol=1e-4)
+        assert_allclose(last_point.ground_distance, 20697.0, rtol=1e-3)
+        assert last_point.engine_setting == EngineSetting.CLIMB
+
+    run()
+
+    # A second call is done to ensure first run did not modify anything (like target definition)
+    run()
 
 
 def test_acceleration_to_EAS(polar):
     propulsion = FuelEngineSet(DummyEngine(0.5e5, 1.0e-5), 2)
 
-    flight_points = SpeedChangeSegment(
+    segment = SpeedChangeSegment(
         target=FlightPoint(equivalent_airspeed=250.0),
         propulsion=propulsion,
         reference_area=120.0,
@@ -58,37 +65,57 @@ def test_acceleration_to_EAS(polar):
         thrust_rate=1.0,
         time_step=0.2,
         engine_setting=EngineSetting.CRUISE,  # The engine model does not use this setting
-    ).compute_from(FlightPoint(altitude=1000.0, true_airspeed=150.0, mass=70000.0))
+    )
+
+    def run():
+        flight_points = segment.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, 128.2, rtol=1e-2)
+        assert_allclose(last_point.altitude, 1000.0)
+        assert_allclose(last_point.true_airspeed, 262.4, atol=1e-1)
+        assert_allclose(last_point.mass, 69872.0, rtol=1e-4)
+        assert_allclose(last_point.ground_distance, 26868.0, rtol=1e-3)
+        assert last_point.engine_setting == EngineSetting.CRUISE
+
+    run()
 
-    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, 128.2, rtol=1e-2)
-    assert_allclose(last_point.altitude, 1000.0)
-    assert_allclose(last_point.true_airspeed, 262.4, atol=1e-1)
-    assert_allclose(last_point.mass, 69872.0, rtol=1e-4)
-    assert_allclose(last_point.ground_distance, 26868.0, rtol=1e-3)
-    assert last_point.engine_setting == EngineSetting.CRUISE
+    # A second call is done to ensure first run did not modify anything (like target definition)
+    run()
 
 
 def test_acceleration_to_mach(polar):
     propulsion = FuelEngineSet(DummyEngine(0.5e5, 1.0e-5), 2)
 
-    flight_points = SpeedChangeSegment(
+    segment = 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))
+    )
+
+    def run():
+        flight_points = segment.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)
+
+    run()
 
-    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)
+    # A second call is done to ensure first run did not modify anything (like target definition)
+    run()
 
 
 def test_acceleration_not_enough_thrust(polar):
@@ -118,14 +145,21 @@ def test_deceleration_not_enough_thrust(polar):
         time_step=1.0,
     )
     segment.time_step = 1.0
-    flight_points = segment.compute_from(
-        FlightPoint(altitude=5000.0, true_airspeed=250.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, 315.8, rtol=1e-2)
-    assert_allclose(last_point.altitude, 5000.0)
-    assert_allclose(last_point.true_airspeed, 150.0)
-    assert_allclose(last_point.mass, 69982.0, rtol=1e-4)
-    assert_allclose(last_point.ground_distance, 62804.0, rtol=1e-3)
+    def run():
+        flight_points = segment.compute_from(
+            FlightPoint(altitude=5000.0, true_airspeed=250.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, 315.8, rtol=1e-2)
+        assert_allclose(last_point.altitude, 5000.0)
+        assert_allclose(last_point.true_airspeed, 150.0)
+        assert_allclose(last_point.mass, 69982.0, rtol=1e-4)
+        assert_allclose(last_point.ground_distance, 62804.0, rtol=1e-3)
+
+    run()
+
+    # A second call is done to ensure first run did not modify anything (like target definition)
+    run()
diff --git a/src/fastoad/models/performances/mission/segments/tests/test_start.py b/src/fastoad/models/performances/mission/segments/tests/test_start.py
new file mode 100644
index 000000000..b12a5f46e
--- /dev/null
+++ b/src/fastoad/models/performances/mission/segments/tests/test_start.py
@@ -0,0 +1,53 @@
+#  This file is part of FAST-OAD : A framework for rapid Overall Aircraft Design
+#  Copyright (C) 2022 ONERA & ISAE-SUPAERO
+#  FAST is free software: you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation, either version 3 of the License, or
+#  (at your option) any later version.
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#  You should have received a copy of the GNU General Public License
+#  along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+from numpy.testing import assert_allclose
+
+from fastoad.model_base import FlightPoint
+from ..start import Start
+
+
+def test_start():
+    start_point = FlightPoint(time=500.0, mass=500, altitude=10)
+    segment = Start(target=start_point)
+
+    def run1():
+        flight_points = segment.compute_from(FlightPoint())
+        assert len(flight_points) == 1
+        assert flight_points.time[0] == 500.0
+        assert flight_points.mass[0] == 500.0
+        assert flight_points.altitude[0] == 10.0
+        assert flight_points.true_airspeed[0] == 0.0
+        assert flight_points.mach[0] == 0.0
+
+    run1()
+
+    # A second call is done to ensure first run did not modify anything (like target definition)
+    run1()
+
+    start_point = FlightPoint(altitude=0, true_airspeed=100.0, mass=0.0)
+    segment = Start(target=start_point)
+
+    def run2():
+        flight_points = segment.compute_from(FlightPoint(mass=1e9))
+        assert len(flight_points) == 1
+        assert flight_points.time[0] == 0.0
+        assert flight_points.mass[0] == 0.0
+        assert flight_points.altitude[0] == 0.0
+        assert flight_points.true_airspeed[0] == 100.0
+        assert_allclose(flight_points.mach, 0.29386, rtol=1e-4)
+
+    run2()
+
+    # A second call is done to ensure first run did not modify anything (like target definition)
+    run2()
diff --git a/src/fastoad/models/performances/mission/segments/tests/test_taxi.py b/src/fastoad/models/performances/mission/segments/tests/test_taxi.py
index ed48c67d3..954d7f6d7 100644
--- a/src/fastoad/models/performances/mission/segments/tests/test_taxi.py
+++ b/src/fastoad/models/performances/mission/segments/tests/test_taxi.py
@@ -31,12 +31,19 @@ def test_taxi():
         true_airspeed=10.0,
         engine_setting=EngineSetting.IDLE,  # The engine model does not use this setting
     )
-    flight_points = segment.compute_from(FlightPoint(altitude=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)
-    assert last_point.engine_setting == EngineSetting.IDLE
+
+    def run():
+        flight_points = segment.compute_from(FlightPoint(altitude=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)
+        assert last_point.engine_setting == EngineSetting.IDLE
+
+    run()
+
+    # A second call is done to ensure first run did not modify anything (like target definition)
+    run()
diff --git a/src/fastoad/models/performances/mission/segments/tests/test_transition.py b/src/fastoad/models/performances/mission/segments/tests/test_transition.py
index ba9ec045f..f066844b6 100644
--- a/src/fastoad/models/performances/mission/segments/tests/test_transition.py
+++ b/src/fastoad/models/performances/mission/segments/tests/test_transition.py
@@ -18,20 +18,54 @@
 from ..transition import DummyTransitionSegment
 
 
+def test_dummy_takeoff():
+    target = FlightPoint(
+        time=45,
+        true_airspeed=50.0,
+        mass=-100,
+        altitude=10,
+    )
+    target.set_as_relative(["mass", "altitude"])
+    dummy_takeoff = DummyTransitionSegment(target=target)
+
+    def run():
+        flight_points = dummy_takeoff.compute_from(
+            FlightPoint(time=500, altitude=0.0, mass=50000, mach=0.0, ground_distance=100.0e3)
+        )
+
+        assert_allclose(flight_points.time, [500, 545])
+        assert_allclose(flight_points.mass, [50000, 49900])
+        assert_allclose(flight_points.altitude, [0.0, 10.0])
+        assert_allclose(flight_points.ground_distance, [100.0e3, 100.0e3])
+        assert_allclose(flight_points.true_airspeed, [0.0, 50.0])
+        assert_allclose(flight_points.mach, [0.0, 0.1469], rtol=1.0e-3)
+
+    run()
+
+    # A second call is done to ensure first run did not modify anything (like target definition)
+    run()
+
+
 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)
-    )
+    def run():
+        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)
 
-    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)
+    run()
+
+    # A second call is done to ensure first run did not modify anything (like target definition)
+    run()
 
 
 def test_dummy_descent_with_reserve():
@@ -40,23 +74,37 @@ def test_dummy_descent_with_reserve():
         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)
+
+    def run():
+        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)
+
+    run()
+
+    # A second call is done to ensure first run did not modify anything (like target definition)
+    run()
 
 
 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)
+
+    def run():
+        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)
+
+    run()
+
+    # A second call is done to ensure first run did not modify anything (like target definition)
+    run()
diff --git a/src/fastoad/models/performances/mission/tests/test_flight_sequence.py b/src/fastoad/models/performances/mission/tests/test_flight_sequence.py
new file mode 100644
index 000000000..b592c02e6
--- /dev/null
+++ b/src/fastoad/models/performances/mission/tests/test_flight_sequence.py
@@ -0,0 +1,112 @@
+#  This file is part of FAST-OAD : A framework for rapid Overall Aircraft Design
+#  Copyright (C) 2022 ONERA & ISAE-SUPAERO
+#  FAST is free software: you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation, either version 3 of the License, or
+#  (at your option) any later version.
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#  You should have received a copy of the GNU General Public License
+#  along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+import pytest
+from numpy.testing import assert_allclose
+
+from fastoad.model_base import FlightPoint
+from ..base import FlightSequence
+from ..segments.mass_input import MassTargetSegment
+from ..segments.taxi import TaxiSegment
+
+
+@pytest.fixture(scope="module")
+def propulsion():
+    from tests.dummy_plugins.dist_2.dummy_plugin_2.models.subpackage.dummy_engine import DummyEngine
+
+    return DummyEngine(1.0e5, 1.0e-4)
+
+
+def get_taxi_definition(propulsion, target_mass=None):
+    return TaxiSegment(
+        "taxi_out",
+        target=FlightPoint(time=100.0, mass=target_mass),
+        thrust_rate=0.3,
+        propulsion=propulsion,
+    )
+
+
+@pytest.fixture(scope="module")
+def taxi_consumption(propulsion):
+    masses = (
+        get_taxi_definition(propulsion).compute_from(FlightPoint(altitude=0.0, mass=100000.0)).mass
+    )
+    return masses.iloc[0] - masses.iloc[-1]
+
+
+def get_sequence_without_target_mass(propulsion):
+    return [get_taxi_definition(propulsion), get_taxi_definition(propulsion)]
+
+
+def get_sequence_with_target_mass(propulsion):
+    return [
+        get_taxi_definition(propulsion),
+        get_taxi_definition(propulsion),
+        MassTargetSegment(target=FlightPoint(mass=70000.0)),
+    ]
+
+
+def get_nested_flight_sequence_without_target_mass(propulsion):
+    return [
+        get_taxi_definition(propulsion),
+        FlightSequence(flight_sequence=get_sequence_without_target_mass(propulsion)),
+        FlightSequence(flight_sequence=get_sequence_without_target_mass(propulsion)),
+    ]
+
+
+def get_nested_flight_sequence_with_target_mass(propulsion):
+    return [
+        get_taxi_definition(propulsion),
+        FlightSequence(flight_sequence=get_sequence_with_target_mass(propulsion)),
+        FlightSequence(flight_sequence=get_sequence_without_target_mass(propulsion)),
+    ]
+
+
+def test_flight_sequence_without_target_mass(propulsion, taxi_consumption):
+    sequence = FlightSequence()
+    sequence.flight_sequence = get_sequence_without_target_mass(propulsion)
+    flight_points = sequence.compute_from(FlightPoint(altitude=0.0, mass=50000.0))
+
+    assert_allclose(flight_points.mass.iloc[0] - flight_points.mass.iloc[-1], taxi_consumption * 2)
+    assert_allclose(flight_points.mass.iloc[-1], 50000.0 - taxi_consumption * 2)
+    assert_allclose(sequence.consumed_mass_before_input_weight, 0.0)
+
+
+def test_flight_sequence_with_target_mass(propulsion, taxi_consumption):
+    sequence = FlightSequence()
+    sequence.flight_sequence = get_sequence_with_target_mass(propulsion)
+    flight_points = sequence.compute_from(FlightPoint(altitude=0.0, mass=50000.0))
+
+    assert_allclose(flight_points.mass.iloc[0] - flight_points.mass.iloc[-1], taxi_consumption * 2)
+    assert_allclose(flight_points.mass.iloc[-1], 70000.0)
+    assert_allclose(sequence.consumed_mass_before_input_weight, taxi_consumption * 2)
+
+
+def test_nested_flight_sequence_without_target_mass(propulsion, taxi_consumption):
+    sequence = FlightSequence()
+    sequence.flight_sequence = get_nested_flight_sequence_without_target_mass(propulsion)
+    flight_points = sequence.compute_from(FlightPoint(altitude=0.0, mass=50000.0))
+
+    assert_allclose(flight_points.mass.iloc[0] - flight_points.mass.iloc[-1], taxi_consumption * 5)
+    assert_allclose(flight_points.mass.iloc[-1], 50000.0 - taxi_consumption * 5)
+    assert_allclose(sequence.consumed_mass_before_input_weight, 0.0)
+
+
+def test_nested_flight_sequence_with_target_mass(propulsion, taxi_consumption):
+    sequence = FlightSequence()
+    sequence.flight_sequence = get_nested_flight_sequence_with_target_mass(propulsion)
+    flight_points = sequence.compute_from(FlightPoint(altitude=0.0, mass=50000.0))
+
+    assert_allclose(flight_points.mass.iloc[0] - flight_points.mass.iloc[-1], taxi_consumption * 5)
+    assert_allclose(flight_points.mass.iloc[-1], 70000.0 - taxi_consumption * 2)
+    assert_allclose(sequence.consumed_mass_before_input_weight, taxi_consumption * 3)
diff --git a/src/fastoad/models/performances/mission/tests/test_routes.py b/src/fastoad/models/performances/mission/tests/test_routes.py
index 1031a6997..169be29ec 100644
--- a/src/fastoad/models/performances/mission/tests/test_routes.py
+++ b/src/fastoad/models/performances/mission/tests/test_routes.py
@@ -21,10 +21,10 @@
 #  along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 import os.path as pth
-from abc import ABC
+from dataclasses import InitVar, dataclass, field
 from os import mkdir
 from shutil import rmtree
-from typing import List, Union
+from typing import Union
 
 import numpy as np
 import pandas as pd
@@ -34,8 +34,9 @@
 
 from fastoad.constants import EngineSetting, FlightPhase
 from fastoad.model_base import FlightPoint
+from fastoad.model_base.datacls import MANDATORY_FIELD
 from fastoad.model_base.propulsion import AbstractFuelPropulsion, FuelEngineSet, IPropulsion
-from fastoad.models.performances.mission.base import IFlightPart
+from fastoad.models.performances.mission.base import FlightSequence
 from fastoad.models.performances.mission.polar import Polar
 from fastoad.models.performances.mission.routes import RangedRoute
 from fastoad.models.performances.mission.segments.altitude_change import AltitudeChangeSegment
@@ -189,7 +190,12 @@ def test_ranged_route(low_speed_polar, high_speed_polar, cleanup):
         time_step=5.0,
     )
 
-    flight_calculator = RangedRoute([initial_climb, climb], cruise, [descent], total_distance)
+    flight_calculator = RangedRoute(
+        climb_phases=[initial_climb, climb],
+        cruise_segment=cruise,
+        descent_phases=[descent],
+        flight_distance=total_distance,
+    )
     assert flight_calculator.cruise_speed == ("mach", 0.78)
 
     start = FlightPoint(
@@ -207,25 +213,28 @@ def test_ranged_route(low_speed_polar, high_speed_polar, cleanup):
 
 
 # We define here in Python the flight phases that feed the test of RangedRoute ============
-
-
-class AbstractManualThrustFlightPhase(ABC):
+@dataclass
+class AbstractManualThrustFlightPhase(FlightSequence):
     """
     Base class for climb and descent phases.
     """
 
-    def __init__(
+    propulsion: InitVar[IPropulsion] = MANDATORY_FIELD
+    reference_area: InitVar[float] = MANDATORY_FIELD
+    polar: InitVar[Polar] = MANDATORY_FIELD
+    thrust_rate: InitVar[float] = 1.0
+    time_step: InitVar[float] = None
+    segment_kwargs: dict = field(default_factory=dict, init=False)
+
+    def __post_init__(
         self,
-        *,
         propulsion: IPropulsion,
         reference_area: float,
         polar: Polar,
         thrust_rate: float = 1.0,
-        name="",
-        time_step=None
+        time_step=None,
     ):
         """
-        Initialization is done only with keyword arguments.
 
         :param propulsion:
         :param reference_area:
@@ -233,14 +242,13 @@ def __init__(
         :param thrust_rate:
         :param time_step: if provided, this time step will be applied for all segments.
         """
-
-        super().__init__()
+        super().__post_init__()
         self.segment_kwargs = {
             "propulsion": propulsion,
             "reference_area": reference_area,
             "polar": polar,
             "thrust_rate": thrust_rate,
-            "name": name,
+            "name": self.name,
             "time_step": time_step,
         }
 
@@ -264,6 +272,7 @@ def compute_from(self, start: FlightPoint) -> pd.DataFrame:
             return pd.concat(parts).reset_index(drop=True)
 
 
+@dataclass
 class InitialClimbPhase(AbstractManualThrustFlightPhase):
     """
     Preset for initial climb phase.
@@ -273,9 +282,10 @@ class InitialClimbPhase(AbstractManualThrustFlightPhase):
     - Climbs up to 1500ft at constant EAS
     """
 
-    @property
-    def flight_sequence(self) -> List[Union[IFlightPart, str]]:
-        return [
+    def __post_init__(self, *args, **kwargs):
+        super().__post_init__(*args, **kwargs)
+
+        self.flight_sequence = [
             AltitudeChangeSegment(
                 target=FlightPoint(equivalent_airspeed="constant", altitude=400.0 * foot),
                 engine_setting=EngineSetting.TAKEOFF,
@@ -294,6 +304,7 @@ def flight_sequence(self) -> List[Union[IFlightPart, str]]:
         ]
 
 
+@dataclass
 class ClimbPhase(AbstractManualThrustFlightPhase):
     """
     Preset for climb phase.
@@ -303,27 +314,14 @@ class ClimbPhase(AbstractManualThrustFlightPhase):
     - Climbs up to target altitude at constant EAS
     """
 
-    def __init__(self, **kwargs):
-        """
-        Uses keyword arguments as for :meth:`AbstractManualThrustFlightPhase` with
-        these additional keywords:
+    maximum_mach: float = field(default=5.0)
+    target_altitude: Union[float, str] = MANDATORY_FIELD
 
-        :param maximum_mach: Mach number that won't be exceeded during climb
-        :param target_altitude: target altitude in meters, can be a float or
-                                AltitudeChangeSegment.OPTIMAL_ALTITUDE to target
-                                altitude with maximum lift/drag ratio
-        """
+    def __post_init__(self, *args, **kwargs):
+        super().__post_init__(*args, **kwargs)
 
-        if "maximum_mach" in kwargs:
-            self.maximum_mach = kwargs.pop("maximum_mach")
-        self.target_altitude = kwargs.pop("target_altitude")
-        super().__init__(**kwargs)
-
-    @property
-    def flight_sequence(self) -> List[Union[IFlightPart, str]]:
         self.segment_kwargs["engine_setting"] = EngineSetting.CLIMB
-
-        return [
+        self.flight_sequence = [
             AltitudeChangeSegment(
                 target=FlightPoint(equivalent_airspeed="constant", altitude=10000.0 * foot),
                 **self.segment_kwargs,
@@ -342,6 +340,7 @@ def flight_sequence(self) -> List[Union[IFlightPart, str]]:
         ]
 
 
+@dataclass
 class DescentPhase(AbstractManualThrustFlightPhase):
     """
     Preset for descent phase.
@@ -352,21 +351,13 @@ class DescentPhase(AbstractManualThrustFlightPhase):
     - Descends down to target altitude at constant EAS
     """
 
-    def __init__(self, **kwargs):
-        """
-        Uses keyword arguments as for :meth:`AbstractManualThrustFlightPhase` with
-        this additional keyword:
+    target_altitude: Union[float, str] = MANDATORY_FIELD
 
-        :param target_altitude: target altitude in meters
-        """
-
-        self.target_altitude = kwargs.pop("target_altitude")
-        super().__init__(**kwargs)
-
-    @property
-    def flight_sequence(self) -> List[Union[IFlightPart, str]]:
+    def __post_init__(self, *args, **kwargs):
+        super().__post_init__(*args, **kwargs)
         self.segment_kwargs["engine_setting"] = EngineSetting.IDLE
-        return [
+
+        self.flight_sequence = [
             AltitudeChangeSegment(
                 target=FlightPoint(equivalent_airspeed=300.0 * knot, mach="constant"),
                 **self.segment_kwargs,
diff --git a/tests/integration_tests/oad_process/data/CeRAS01_legacy_mission_only_result.xml b/tests/integration_tests/oad_process/data/CeRAS01_legacy_mission_only_result.xml
index ebb057a4a..1a03a6184 100644
--- a/tests/integration_tests/oad_process/data/CeRAS01_legacy_mission_only_result.xml
+++ b/tests/integration_tests/oad_process/data/CeRAS01_legacy_mission_only_result.xml
@@ -101,15 +101,16 @@
           <V2 units="m/s" is_input="True">86.1<!--takeoff safety speed for mission "sizing"--></V2>
           <altitude units="m" is_input="True">0.0<!--altitude of airport for mission "sizing"--></altitude>
           <fuel units="kg" is_input="True">93.6891205669<!--burned fuel during takeoff phase of mission "sizing"--></fuel>
+          <duration units="min">0.668666666667<!--duration of takeoff phase in sizing mission--></duration>
         </takeoff>
         <taxi_in>
-          <distance units="m" is_input="False">0.0<!--covered ground distance during phase "taxi_in" in mission "sizing"--></distance>
+          <distance units="km" is_input="False">0.0<!--covered ground distance during phase "taxi_in" in mission "sizing"--></distance>
           <duration units="s" is_input="True">300.0<!--Input defined by the mission.--></duration>
           <fuel units="kg" is_input="False">421.1999999999898<!--burned fuel during phase "taxi_in" in mission "sizing"--></fuel>
           <thrust_rate is_input="True">0.3<!--Input defined by the mission.--></thrust_rate>
         </taxi_in>
         <taxi_out>
-          <distance units="m" is_input="False">0.0<!--distance during taxi-out of mission "sizing"--></distance>
+          <distance units="km" is_input="False">0.0<!--distance during taxi-out of mission "sizing"--></distance>
           <duration units="s" is_input="True">540.0<!--duration of taxi-out in mission "sizing"--></duration>
           <fuel units="kg" is_input="False">758.1600000000471<!--burned fuel during taxi-out of mission "sizing"--></fuel>
           <thrust_rate is_input="True">0.3<!--thrust rate during taxi-out in mission "sizing"--></thrust_rate>
diff --git a/tests/integration_tests/oad_process/data/CeRAS01_notebooks.xml b/tests/integration_tests/oad_process/data/CeRAS01_notebooks.xml
index c692017f4..a23d0ef88 100644
--- a/tests/integration_tests/oad_process/data/CeRAS01_notebooks.xml
+++ b/tests/integration_tests/oad_process/data/CeRAS01_notebooks.xml
@@ -460,6 +460,7 @@
                         82.4<!--mass of consumed fuel during takeoff phase in sizing mission--></fuel>
                     <slat_angle units="deg">
                         18.0<!--slat angle during takeoff phase in sizing mission--></slat_angle>
+                    <duration units="s">0.0</duration>
                 </takeoff>
                 <taxi_in>
                     <distance units="m">39443.94626640342</distance>
diff --git a/tests/integration_tests/oad_process/test_oad_process.py b/tests/integration_tests/oad_process/test_oad_process.py
index df0ff6d79..a1e7d1325 100644
--- a/tests/integration_tests/oad_process/test_oad_process.py
+++ b/tests/integration_tests/oad_process/test_oad_process.py
@@ -27,8 +27,8 @@
 import pytest
 from numpy.testing import assert_allclose
 
-from fastoad.cmd import api
-from fastoad.io import DataFile
+import fastoad.api as oad
+import fastoad.cmd.api
 from fastoad.io.configuration.configuration import (
     FASTOADProblemConfigurator,
     _IConfigurationModifier,
@@ -152,7 +152,7 @@ def run_non_regression_test(
     configuration_file_path = pth.join(results_folder_path, conf_file)
 
     # Copy of configuration file and generation of problem instance ------------------
-    api.generate_configuration_file(configuration_file_path)  # just ensure folders are created...
+    oad.generate_configuration_file(configuration_file_path)  # just ensure folders are created...
     shutil.copy(pth.join(DATA_FOLDER_PATH, conf_file), configuration_file_path)
     configurator = FASTOADProblemConfigurator(configuration_file_path)
     configurator._set_configuration_modifier(XFOILConfigurator(use_xfoil, xfoil_path))
@@ -176,7 +176,7 @@ def run_non_regression_test(
     if check_weight_perfo_loop:
         _check_weight_performance_loop(problem)
 
-    ref_data = DataFile(pth.join(DATA_FOLDER_PATH, legacy_result_file))
+    ref_data = oad.DataFile(pth.join(DATA_FOLDER_PATH, legacy_result_file))
 
     row_list = []
     for ref_var in ref_data:
@@ -218,15 +218,15 @@ def test_api_eval_breguet(cleanup):
     configuration_file_path = pth.join(results_folder_path, "oad_process.yml")
 
     # Generation of configuration file ----------------------------------------
-    api.generate_configuration_file(configuration_file_path, True)
+    oad.generate_configuration_file(configuration_file_path, True)
 
     # Generation of inputs ----------------------------------------------------
     # We get the same inputs as in tutorial notebook
     source_xml = pth.join(DATA_FOLDER_PATH, "CeRAS01_notebooks.xml")
-    api.generate_inputs(configuration_file_path, source_xml, overwrite=True)
+    oad.generate_inputs(configuration_file_path, source_xml, overwrite=True)
 
     # Run model ---------------------------------------------------------------
-    problem = api.evaluate_problem(configuration_file_path, True)
+    problem = oad.evaluate_problem(configuration_file_path, True)
 
     # Check that weight-performances loop correctly converged
     _check_weight_performance_loop(problem)
@@ -239,6 +239,8 @@ def test_api_eval_breguet(cleanup):
     assert_allclose(problem["data:geometry:horizontal_tail:area"], 35.884, atol=1e-2)
     assert_allclose(problem["data:mission:sizing:needed_block_fuel"], 19527, atol=1)
 
+    _run_plots(problem.output_file_path)
+
 
 class MissionConfigurator(_IConfigurationModifier):
     """Modifies configuration to activate mission computation."""
@@ -254,30 +256,32 @@ def modify(self, problem: om.Problem):
 def test_api_eval_mission(cleanup):
     results_folder_path = pth.join(RESULTS_FOLDER_PATH, "api_eval_mission")
     configuration_file_path = pth.join(results_folder_path, "oad_process.yml")
-    api._PROBLEM_CONFIGURATOR = MissionConfigurator()
+    fastoad.cmd.api._PROBLEM_CONFIGURATOR = MissionConfigurator()
 
     # Generation of configuration file ----------------------------------------
-    api.generate_configuration_file(configuration_file_path, True)
+    oad.generate_configuration_file(configuration_file_path, True)
 
     # Generation of inputs ----------------------------------------------------
     # We get the same inputs as in tutorial notebook
     source_xml = pth.join(DATA_FOLDER_PATH, "CeRAS01_notebooks.xml")
-    api.generate_inputs(configuration_file_path, source_xml, overwrite=True)
+    oad.generate_inputs(configuration_file_path, source_xml, overwrite=True)
 
     # Run model ---------------------------------------------------------------
-    problem = api.evaluate_problem(configuration_file_path, True)
-    api._PROBLEM_CONFIGURATOR = None
+    problem = oad.evaluate_problem(configuration_file_path, True)
+    fastoad.cmd.api._PROBLEM_CONFIGURATOR = None
 
     # Check that weight-performances loop correctly converged
     _check_weight_performance_loop(problem)
 
     assert_allclose(problem["data:handling_qualities:static_margin"], 0.05, atol=1e-2)
     assert_allclose(problem["data:geometry:wing:MAC:at25percent:x"], 17.149, atol=1e-2)
-    assert_allclose(problem["data:weight:aircraft:MTOW"], 74695, atol=1)
-    assert_allclose(problem["data:geometry:wing:area"], 126.083, atol=1e-2)
+    assert_allclose(problem["data:weight:aircraft:MTOW"], 74695, atol=10)
+    assert_allclose(problem["data:geometry:wing:area"], 126.083, atol=1e-1)
     assert_allclose(problem["data:geometry:vertical_tail:area"], 27.437, atol=1e-2)
     assert_allclose(problem["data:geometry:horizontal_tail:area"], 35.731, atol=1e-2)
-    assert_allclose(problem["data:mission:sizing:needed_block_fuel"], 19390, atol=1)
+    assert_allclose(problem["data:mission:sizing:needed_block_fuel"], 19390, atol=10)
+
+    _run_plots(problem.output_file_path)
 
 
 def test_api_optim(cleanup):
@@ -285,22 +289,22 @@ def test_api_optim(cleanup):
     configuration_file_path = pth.join(results_folder_path, "oad_process.yml")
 
     # Generation of configuration file ----------------------------------------
-    api.generate_configuration_file(configuration_file_path, True)
+    oad.generate_configuration_file(configuration_file_path, True)
 
     # Generation of inputs ----------------------------------------------------
     # We get the same inputs as in tutorial notebook
     source_xml = pth.join(DATA_FOLDER_PATH, "CeRAS01_notebooks.xml")
-    api.generate_inputs(configuration_file_path, source_xml, overwrite=True)
+    oad.generate_inputs(configuration_file_path, source_xml, overwrite=True)
 
     # Run optim ---------------------------------------------------------------
-    problem = api.optimize_problem(configuration_file_path, True)
+    problem = oad.optimize_problem(configuration_file_path, True)
     assert not problem.optim_failed
 
     # Check that weight-performances loop correctly converged
     _check_weight_performance_loop(problem)
 
     # Design Variable
-    assert_allclose(problem["data:geometry:wing:aspect_ratio"], 14.52, atol=1e-2)
+    assert_allclose(problem["data:geometry:wing:aspect_ratio"], 14.53, atol=2e-2)
 
     # Constraint
     assert_allclose(problem["data:geometry:wing:span"], 44.88, atol=1e-2)
@@ -328,6 +332,14 @@ def _check_weight_performance_loop(problem):
         problem["data:weight:aircraft:MTOW"],
         problem["data:weight:aircraft:OWE"]
         + problem["data:weight:aircraft:payload"]
-        + problem["data:weight:aircraft:sizing_onboard_fuel_at_takeoff"],
+        + problem["data:weight:aircraft:sizing_onboard_fuel_at_input_weight"],
         atol=1,
     )
+
+
+def _run_plots(xml_file_path: str):
+    oad.wing_geometry_plot(xml_file_path)
+    oad.aircraft_geometry_plot(xml_file_path)
+    oad.mass_breakdown_bar_plot(xml_file_path)
+    oad.mass_breakdown_sun_plot(xml_file_path)
+    oad.drag_polar_plot(xml_file_path)
diff --git a/tests/memory_tests/data/CeRAS01.xml b/tests/memory_tests/data/CeRAS01.xml
index 477925c84..475ab417a 100644
--- a/tests/memory_tests/data/CeRAS01.xml
+++ b/tests/memory_tests/data/CeRAS01.xml
@@ -281,6 +281,7 @@
         </main_route>
         <takeoff>
           <V2 units="m/s" is_input="True">79.3<!--takeoff safety speed for mission "sizing"--></V2>
+          <duration units="s" is_input="True">0.<!--Input defined by the mission.--></duration>
           <altitude units="m" is_input="True">0.0<!--altitude of airport for mission "sizing"--></altitude>
           <fuel units="kg" is_input="True">82.4<!--burned fuel during takeoff phase of mission "sizing"--></fuel>
         </takeoff>