Optimizing the Mission Profile of a Conventional Aircraft#

Building upon our previous example, this notebook introduces more complexity into the Aviary optimization process. Please see the simple mission example if you haven’t already.

Increasing Complexity in Phase Information#

We will now modify the phase_info object from our prior example by increasing num_segments to 3 and setting optimize_mach to True in each of the three phases. This means that we’ll query the aircraft performance at more points along the mission and also give the optimizer the freedom to choose an optimal Mach profile.

Note

We are still using a polynomial_control_order of 1, which means that the optimal Mach profiles for each phase will be linear (straight lines). Later in this example, we increase this order which will allow the optimizer to choose a more complex Mach profile.

phase_info = {
    "pre_mission": {"include_takeoff": False, "optimize_mass": True},
    "climb_1": {
        "subsystem_options": {"core_aerodynamics": {"method": "computed"}},
        "user_options": {
            "optimize_mach": True,
            "optimize_altitude": False,
            "polynomial_control_order": 1,
            "num_segments": 3,
            "order": 3,
            "solve_for_distance": False,
            "initial_mach": (0.2, "unitless"),
            "final_mach": (0.72, "unitless"),
            "mach_bounds": ((0.18, 0.74), "unitless"),
            "initial_altitude": (0.0, "ft"),
            "final_altitude": (30500.0, "ft"),
            "altitude_bounds": ((0.0, 31000.0), "ft"),
            "throttle_enforcement": "path_constraint",
            "fix_initial": True,
            "constrain_final": False,
            "fix_duration": False,
            "initial_bounds": ((0.0, 0.0), "min"),
            "duration_bounds": ((27.0, 81.0), "min"),
        },
        "initial_guesses": {"time": ([0, 54], "min")},
    },
    "cruise": {
        "subsystem_options": {"core_aerodynamics": {"method": "computed"}},
        "user_options": {
            "optimize_mach": True,
            "optimize_altitude": False,
            "polynomial_control_order": 1,
            "num_segments": 3,
            "order": 3,
            "solve_for_distance": False,
            "initial_mach": (0.72, "unitless"),
            "final_mach": (0.72, "unitless"),
            "mach_bounds": ((0.7, 0.74), "unitless"),
            "initial_altitude": (30500.0, "ft"),
            "final_altitude": (31000.0, "ft"),
            "altitude_bounds": ((30000.0, 31500.0), "ft"),
            "throttle_enforcement": "boundary_constraint",
            "fix_initial": False,
            "constrain_final": False,
            "fix_duration": False,
            "initial_bounds": ((27.0, 81.0), "min"),
            "duration_bounds": ((85.5, 256.5), "min"),
        },
        "initial_guesses": {"time": ([54, 171], "min")},
    },
    "descent_1": {
        "subsystem_options": {"core_aerodynamics": {"method": "computed"}},
        "user_options": {
            "optimize_mach": True,
            "optimize_altitude": False,
            "polynomial_control_order": 1,
            "num_segments": 3,
            "order": 3,
            "solve_for_distance": False,
            "initial_mach": (0.72, "unitless"),
            "final_mach": (0.2, "unitless"),
            "mach_bounds": ((0.18, 0.74), "unitless"),
            "initial_altitude": (31000.0, "ft"),
            "final_altitude": (500.0, "ft"),
            "altitude_bounds": ((0.0, 31500.0), "ft"),
            "throttle_enforcement": "path_constraint",
            "fix_initial": False,
            "constrain_final": True,
            "fix_duration": False,
            "initial_bounds": ((112.5, 337.5), "min"),
            "duration_bounds": ((26.5, 79.5), "min"),
        },
        "initial_guesses": {"time": ([225, 53], "min")},
    },
    "post_mission": {
        "include_landing": False,
        "constrain_range": True,
        "target_range": (1915, "nmi"),
    },
}

Running Aviary with Updated Parameters#

Let’s run the Aviary optimization with our updated phase_info object in the same way as before.

import aviary.api as av

prob = av.run_aviary('models/test_aircraft/aircraft_for_bench_FwFm.csv',
                     phase_info, optimizer="SLSQP", make_plots=True)
/usr/share/miniconda/envs/test/lib/python3.12/site-packages/pyoptsparse/pyOpt_MPI.py:68: UserWarning: mpi4py could not be imported. mpi4py is required to use the parallel gradient analysis and parallel objective analysis for non-gradient based optimizers. Continuing using a dummy MPI module from pyOptSparse.
  warnings.warn(warn)
/usr/share/miniconda/envs/test/lib/python3.12/site-packages/dymos/phase/phase.py:902: OMDeprecationWarning:None: The method `add_polynomial_control` is deprecated and will be removed in Dymos 2.1. Please use `add_control` with the appropriate options to define a polynomial control.
/usr/share/miniconda/envs/test/lib/python3.12/site-packages/dymos/phase/phase.py:2328: RuntimeWarning: Invalid options for non-optimal control 'altitude' in phase 'climb_1': lower, upper, ref
  warnings.warn(f"Invalid options for non-optimal control '{name}' in phase "
/usr/share/miniconda/envs/test/lib/python3.12/site-packages/dymos/phase/phase.py:2328: RuntimeWarning: Invalid options for non-optimal control 'altitude' in phase 'cruise': lower, upper, ref
  warnings.warn(f"Invalid options for non-optimal control '{name}' in phase "
/usr/share/miniconda/envs/test/lib/python3.12/site-packages/dymos/phase/phase.py:2328: RuntimeWarning: Invalid options for non-optimal control 'altitude' in phase 'descent_1': lower, upper, ref
  warnings.warn(f"Invalid options for non-optimal control '{name}' in phase "
The following variables have been overridden:
  'aircraft:design:touchdown_mass
  'aircraft:engine:mass
  'aircraft:fins:mass
  'aircraft:fuel:auxiliary_fuel_capacity
  'aircraft:fuel:fuselage_fuel_capacity
  'aircraft:fuel:total_capacity
  'aircraft:fuselage:planform_area
  'aircraft:fuselage:wetted_area
  'aircraft:horizontal_tail:wetted_area
  'aircraft:landing_gear:main_gear_oleo_length
  'aircraft:landing_gear:nose_gear_oleo_length
  'aircraft:vertical_tail:wetted_area
  'aircraft:wing:aspect_ratio
  'aircraft:wing:control_surface_area
  'aircraft:wing:wetted_area

--- Constraint Report [traj] ---
    --- climb_1 ---
        [initial] 2.0000e-01 == mach [unitless]
        [path]    0.0000e+00 <= throttle <= 1.0000e+00  [unitless]
    --- cruise ---
        [initial] 0.0000e+00 <= throttle <= 1.0000e+00  [unitless]
        [final]   0.0000e+00 <= throttle <= 1.0000e+00  [unitless]
    --- descent_1 ---
        [final]   2.0000e-01 == mach [unitless]
        [path]    0.0000e+00 <= throttle <= 1.0000e+00  [unitless]
/usr/share/miniconda/envs/test/lib/python3.12/site-packages/openmdao/solvers/linear/linear_rhs_checker.py:178: SolverWarning:DirectSolver in 'traj.phases.cruise.indep_states' <class StateIndependentsComp>: 'rhs_checking' is active but no redundant adjoint dependencies were found, so caching has been disabled.
/usr/share/miniconda/envs/test/lib/python3.12/site-packages/openmdao/solvers/linear/linear_rhs_checker.py:178: SolverWarning:DirectSolver in 'traj.phases.descent_1.indep_states' <class StateIndependentsComp>: 'rhs_checking' is active but no redundant adjoint dependencies were found, so caching has been disabled.
/usr/share/miniconda/envs/test/lib/python3.12/site-packages/openmdao/recorders/sqlite_recorder.py:226: UserWarning:The existing case recorder file, problem_history.db, is being overwritten.
Model viewer data has already been recorded for Driver.
Full total jacobian for problem 'aircraft_for_bench_FwFm' was computed 3 times, taking 0.45530973899997207 seconds.
Total jacobian shape: (88, 65) 


Jacobian shape: (88, 65)  (11.78% nonzero)
FWD solves: 12   REV solves: 0
Total colors vs. total size: 12 vs 65  (81.54% improvement)

Sparsity computed using tolerance: 1e-25
Time to compute sparsity:   0.4553 sec
Time to compute coloring:   0.0331 sec
Memory to compute coloring:   0.1250 MB
Coloring created on: 2024-09-16 15:52:05
Optimization terminated successfully    (Exit mode 0)
            Current function value: 2.64789538655419
            Iterations: 11
            Function evaluations: 20
            Gradient evaluations: 11
Optimization Complete
-----------------------------------
/usr/share/miniconda/envs/test/lib/python3.12/site-packages/openmdao/core/driver.py:143: OMDeprecationWarning:boolean evaluation of DriverResult is temporarily implemented to mimick the previous `failed` return behavior of run_driver.
Use the `success` attribute of the returned DriverResult object to test for successful driver completion.

Now that we’ve run Aviary, we can look at the results. Open up the automatically generated traj_results_report.html and scroll through it to visualize the results.

Here are the altitude and Mach profiles:

Altitude and Mach Profiles

We note two major changes compared to our first example.

The first is that we have many more points where the flight dynamics were evaluated because we increased num_segments to 3. This means that we have more points shown on the resulting plots.

The second is that the optimizer chose the optimal Mach profile. Again, each phase’s Mach profile is constrained to be linear because we set polynomial_control_order to 1. However, we see that the optimizer chose to decrease the Mach number during the cruise-climb segment to minimize fuel burn.

Note

Remember, we did not allow the optimizer to control the altitude profile, so that remains fixed.

Let’s take a look at the optimization objective, fuel_burned:

print(prob.get_val(av.Mission.Summary.FUEL_BURNED, units='kg')[0])
10649.874328991813

We can print fuel_burned in pounds easily, thanks to OpenMDAO’s automatic unit conversion feature:

print(prob.get_val(av.Mission.Summary.FUEL_BURNED, units='lb')[0])
23478.953865541902

Modifying the Aircraft Configuration#

Next, we’ll modify the aircraft configuration by decreasing the wing aspect ratio by 0.2. This results in a less slender wing, which will increase the induced drag. We’ve made this change and have a modified aircraft data file called modified_aircraft.csv.

Hide code cell source
import csv

filename = 'models/test_aircraft/aircraft_for_bench_FwFm.csv'
filename = av.get_path(filename)

# Read the file
with open(filename, 'r') as file:
    reader = csv.reader(file)
    lines = list(reader)

# Find the index of the line containing 'aircraft:wing:span'
index = None
for i, line in enumerate(lines):
    if 'aircraft:wing:aspect_ratio' in line:
        index = i
        break

# Modify the value in the line
if index is not None:
    aspect_ratio = float(lines[index][1]) - 0.2
    lines[index][1] = str(aspect_ratio)

# Write the modified content to a new CSV file
new_filename = 'modified_aircraft.csv'
with open(new_filename, 'w', newline='') as file:
    writer = csv.writer(file)
    writer.writerows(lines)

Re-running the Optimization with Modified Aircraft#

Now, let’s re-run the optimization with the modified aircraft configuration. We’ll use the same phase_info object as before, but we’ll change the input deck to point to our new aircraft file.

prob = av.run_aviary('modified_aircraft.csv', phase_info,
                     optimizer="SLSQP", make_plots=True)
/usr/share/miniconda/envs/test/lib/python3.12/site-packages/dymos/phase/phase.py:902: OMDeprecationWarning:None: The method `add_polynomial_control` is deprecated and will be removed in Dymos 2.1. Please use `add_control` with the appropriate options to define a polynomial control.
/usr/share/miniconda/envs/test/lib/python3.12/site-packages/dymos/phase/phase.py:2328: RuntimeWarning: Invalid options for non-optimal control 'altitude' in phase 'climb_1': lower, upper, ref
  warnings.warn(f"Invalid options for non-optimal control '{name}' in phase "
/usr/share/miniconda/envs/test/lib/python3.12/site-packages/dymos/phase/phase.py:2328: RuntimeWarning: Invalid options for non-optimal control 'altitude' in phase 'cruise': lower, upper, ref
  warnings.warn(f"Invalid options for non-optimal control '{name}' in phase "
/usr/share/miniconda/envs/test/lib/python3.12/site-packages/dymos/phase/phase.py:2328: RuntimeWarning: Invalid options for non-optimal control 'altitude' in phase 'descent_1': lower, upper, ref
  warnings.warn(f"Invalid options for non-optimal control '{name}' in phase "
The following variables have been overridden:
  'aircraft:design:touchdown_mass
  'aircraft:engine:mass
  'aircraft:fins:mass
  'aircraft:fuel:auxiliary_fuel_capacity
  'aircraft:fuel:fuselage_fuel_capacity
  'aircraft:fuel:total_capacity
  'aircraft:fuselage:planform_area
  'aircraft:fuselage:wetted_area
  'aircraft:horizontal_tail:wetted_area
  'aircraft:landing_gear:main_gear_oleo_length
  'aircraft:landing_gear:nose_gear_oleo_length
  'aircraft:vertical_tail:wetted_area
  'aircraft:wing:aspect_ratio
  'aircraft:wing:control_surface_area
  'aircraft:wing:wetted_area

--- Constraint Report [traj] ---
    --- climb_1 ---
        [initial] 2.0000e-01 == mach [unitless]
        [path]    0.0000e+00 <= throttle <= 1.0000e+00  [unitless]
    --- cruise ---
        [initial] 0.0000e+00 <= throttle <= 1.0000e+00  [unitless]
        [final]   0.0000e+00 <= throttle <= 1.0000e+00  [unitless]
    --- descent_1 ---
        [final]   2.0000e-01 == mach [unitless]
        [path]    0.0000e+00 <= throttle <= 1.0000e+00  [unitless]
/usr/share/miniconda/envs/test/lib/python3.12/site-packages/openmdao/solvers/linear/linear_rhs_checker.py:178: SolverWarning:DirectSolver in 'traj.phases.cruise.indep_states' <class StateIndependentsComp>: 'rhs_checking' is active but no redundant adjoint dependencies were found, so caching has been disabled.
/usr/share/miniconda/envs/test/lib/python3.12/site-packages/openmdao/solvers/linear/linear_rhs_checker.py:178: SolverWarning:DirectSolver in 'traj.phases.descent_1.indep_states' <class StateIndependentsComp>: 'rhs_checking' is active but no redundant adjoint dependencies were found, so caching has been disabled.
/usr/share/miniconda/envs/test/lib/python3.12/site-packages/openmdao/recorders/sqlite_recorder.py:226: UserWarning:The existing case recorder file, problem_history.db, is being overwritten.
Model viewer data has already been recorded for Driver.
Full total jacobian for problem 'modified_aircraft' was computed 3 times, taking 0.4638353840000491 seconds.
Total jacobian shape: (88, 65) 


Jacobian shape: (88, 65)  (11.78% nonzero)
FWD solves: 12   REV solves: 0
Total colors vs. total size: 12 vs 65  (81.54% improvement)

Sparsity computed using tolerance: 1e-25
Time to compute sparsity:   0.4638 sec
Time to compute coloring:   0.0335 sec
Memory to compute coloring:   0.0000 MB
Coloring created on: 2024-09-16 15:52:14
Optimization terminated successfully    (Exit mode 0)
            Current function value: 2.6663530187120137
            Iterations: 13
            Function evaluations: 23
            Gradient evaluations: 13
Optimization Complete
-----------------------------------
/usr/share/miniconda/envs/test/lib/python3.12/site-packages/openmdao/core/driver.py:143: OMDeprecationWarning:boolean evaluation of DriverResult is temporarily implemented to mimick the previous `failed` return behavior of run_driver.
Use the `success` attribute of the returned DriverResult object to test for successful driver completion.

The case again converged in relatively few iterations. Let’s take a look at the fuel burn value:

print(prob.get_val(av.Mission.Summary.FUEL_BURNED, units='kg')[0])
10733.596740142368

As expected, it’s a bit higher than our prior run that had a larger aspect ratio.

Increasing the Polynomial Control Order#

Next, we’ll increase the polynomial_control_order to 3 for the climb and descent phases. This means that the optimizer will be able to choose a cubic Mach profile per phase instead of a line. We’ll use the original aircraft configuration for this run.

Note

We’ll use the IPOPT optimizer for this problem as it will handle the increased complexity better than SLSQP.

phase_info['climb_1']['user_options']['polynomial_control_order'] = 3
phase_info['cruise']['user_options']['polynomial_control_order'] = 1
phase_info['descent_1']['user_options']['polynomial_control_order'] = 3

prob = av.run_aviary('models/test_aircraft/aircraft_for_bench_FwFm.csv',
                     phase_info, optimizer="IPOPT", make_plots=True)
/usr/share/miniconda/envs/test/lib/python3.12/site-packages/dymos/phase/phase.py:902: OMDeprecationWarning:None: The method `add_polynomial_control` is deprecated and will be removed in Dymos 2.1. Please use `add_control` with the appropriate options to define a polynomial control.
/usr/share/miniconda/envs/test/lib/python3.12/site-packages/dymos/phase/phase.py:2328: RuntimeWarning: Invalid options for non-optimal control 'altitude' in phase 'climb_1': lower, upper, ref
  warnings.warn(f"Invalid options for non-optimal control '{name}' in phase "
/usr/share/miniconda/envs/test/lib/python3.12/site-packages/dymos/phase/phase.py:2328: RuntimeWarning: Invalid options for non-optimal control 'altitude' in phase 'cruise': lower, upper, ref
  warnings.warn(f"Invalid options for non-optimal control '{name}' in phase "
/usr/share/miniconda/envs/test/lib/python3.12/site-packages/dymos/phase/phase.py:2328: RuntimeWarning: Invalid options for non-optimal control 'altitude' in phase 'descent_1': lower, upper, ref
  warnings.warn(f"Invalid options for non-optimal control '{name}' in phase "
The following variables have been overridden:
  'aircraft:design:touchdown_mass
  'aircraft:engine:mass
  'aircraft:fins:mass
  'aircraft:fuel:auxiliary_fuel_capacity
  'aircraft:fuel:fuselage_fuel_capacity
  'aircraft:fuel:total_capacity
  'aircraft:fuselage:planform_area
  'aircraft:fuselage:wetted_area
  'aircraft:horizontal_tail:wetted_area
  'aircraft:landing_gear:main_gear_oleo_length
  'aircraft:landing_gear:nose_gear_oleo_length
  'aircraft:vertical_tail:wetted_area
  'aircraft:wing:aspect_ratio
  'aircraft:wing:control_surface_area
  'aircraft:wing:wetted_area
--- Constraint Report [traj] ---
    --- climb_1 ---
        [initial] 2.0000e-01 == mach [unitless]
        [path]    0.0000e+00 <= throttle <= 1.0000e+00  [unitless]
    --- cruise ---
        [initial] 0.0000e+00 <= throttle <= 1.0000e+00  [unitless]
        [final]   0.0000e+00 <= throttle <= 1.0000e+00  [unitless]
    --- descent_1 ---
        [final]   2.0000e-01 == mach [unitless]
        [path]    0.0000e+00 <= throttle <= 1.0000e+00  [unitless]
/usr/share/miniconda/envs/test/lib/python3.12/site-packages/openmdao/solvers/linear/linear_rhs_checker.py:178: SolverWarning:DirectSolver in 'traj.phases.cruise.indep_states' <class StateIndependentsComp>: 'rhs_checking' is active but no redundant adjoint dependencies were found, so caching has been disabled.
/usr/share/miniconda/envs/test/lib/python3.12/site-packages/openmdao/solvers/linear/linear_rhs_checker.py:178: SolverWarning:DirectSolver in 'traj.phases.descent_1.indep_states' <class StateIndependentsComp>: 'rhs_checking' is active but no redundant adjoint dependencies were found, so caching has been disabled.
/usr/share/miniconda/envs/test/lib/python3.12/site-packages/openmdao/recorders/sqlite_recorder.py:226: UserWarning:The existing case recorder file, problem_history.db, is being overwritten.
Model viewer data has already been recorded for Driver.
Model viewer data has already been recorded for Driver.
Full total jacobian for problem 'aircraft_for_bench_FwFm' was computed 3 times, taking 0.47074421399997846 seconds.
Total jacobian shape: (88, 69) 


Jacobian shape: (88, 69)  (13.78% nonzero)
FWD solves: 13   REV solves: 0
Total colors vs. total size: 13 vs 69  (81.16% improvement)

Sparsity computed using tolerance: 1e-25
Time to compute sparsity:   0.4707 sec
Time to compute coloring:   0.0366 sec
Memory to compute coloring:   0.0000 MB
Coloring created on: 2024-09-16 15:52:24
This is Ipopt version 3.14.16, running with linear solver MUMPS 5.7.3.

Number of nonzeros in equality constraint Jacobian...:      528
Number of nonzeros in inequality constraint Jacobian.:      308
Number of nonzeros in Lagrangian Hessian.............:        0

Total number of variables............................:       69
                     variables with only lower bounds:        0
                variables with lower and upper bounds:       69
                     variables with only upper bounds:        0
Total number of equality constraints.................:       61
Total number of inequality constraints...............:       26
        inequality constraints with only lower bounds:        0
   inequality constraints with lower and upper bounds:       26
        inequality constraints with only upper bounds:        0

iter    objective    inf_pr   inf_du lg(mu)  ||d||  lg(rg) alpha_du alpha_pr  ls
   0  3.0000000e-01 1.91e+02 1.16e-01  -5.0 0.00e+00    -  0.00e+00 0.00e+00   0
  10  1.7413645e+00 8.64e+01 9.67e-01  -5.0 1.92e+00    -  4.09e-02 1.67e-01h  1
  20  2.5858861e+00 4.55e-02 3.36e-02  -5.0 2.30e-01    -  2.73e-01 5.31e-01h  1
  30  2.5926924e+00 5.58e-09 6.71e-04  -5.0 1.22e-03    -  1.00e+00 1.00e+00h  1
  40  2.5926658e+00 4.35e-11 4.07e-08  -7.0 1.76e-06    -  1.00e+00 1.00e+00h  1

Number of Iterations....: 40

                                   (scaled)                 (unscaled)
Objective...............:   2.5926657918858953e+00    2.5926657918858953e+00
Dual infeasibility......:   4.0665397462850895e-08    4.0665397462850895e-08
Constraint violation....:   4.3479058470327555e-11    4.3479058470327555e-11
Variable bound violation:   0.0000000000000000e+00    0.0000000000000000e+00
Complementarity.........:   9.0909109516599646e-08    9.0909109516599646e-08
Overall NLP error.......:   9.0909109516599646e-08    9.0909109516599646e-08


Number of objective function evaluations             = 45
Number of objective gradient evaluations             = 41
Number of equality constraint evaluations            = 45
Number of inequality constraint evaluations          = 45
Number of equality constraint Jacobian evaluations   = 41
Number of inequality constraint Jacobian evaluations = 41
Number of Lagrangian Hessian evaluations             = 0
Total seconds in IPOPT                               = 14.989

EXIT: Optimal Solution Found.
minimal_print is not available for this solution


Optimization Problem -- Optimization using pyOpt_sparse
================================================================================
    Objective Function: _objfunc

    Solution: 
--------------------------------------------------------------------------------
    Total Time:                   14.9902
       User Objective Time :      11.4238
       User Sensitivity Time :     3.3482
       Interface Time :            0.0858
       Opt Solver Time:            0.1323
    Calls to Objective Function :      46
    Calls to Sens Function :           42


   Objectives
      Index  Name                               Value
          0  mission:objectives:fuel     2.592666E+00

   Variables (c - continuous, i - integer, d - discrete)
      Index  Name                               Type      Lower Bound            Value      Upper Bound     Status
          0  mission:design:gross_mass_0           c     5.714286E-05     9.125309E-01     2.285714E+00           
          1  mission:summary:gross_mass_0          c     5.714286E-05     9.125309E-01     2.285714E+00           
          2  traj.climb_1.t_duration_0             c     5.000000E-01     5.000015E-01     1.500000E+00           
          3  traj.climb_1.states:mass_0            c     0.000000E+00     7.931662E+00     1.000000E+17           
          4  traj.climb_1.states:mass_1            c     0.000000E+00     7.907364E+00     1.000000E+17           
          5  traj.climb_1.states:mass_2            c     0.000000E+00     7.898871E+00     1.000000E+17           
          6  traj.climb_1.states:mass_3            c     0.000000E+00     7.868957E+00     1.000000E+17           
          7  traj.climb_1.states:mass_4            c     0.000000E+00     7.828223E+00     1.000000E+17           
          8  traj.climb_1.states:mass_5            c     0.000000E+00     7.815965E+00     1.000000E+17           
          9  traj.climb_1.states:mass_6            c     0.000000E+00     7.799516E+00     1.000000E+17           
         10  traj.climb_1.states:mass_7            c     0.000000E+00     7.778635E+00     1.000000E+17           
         11  traj.climb_1.states:mass_8            c     0.000000E+00     7.772448E+00     1.000000E+17           
         12  traj.climb_1.states:distance_0        c     0.000000E+00     1.405035E-02     1.000000E+15           
         13  traj.climb_1.states:distance_1        c     0.000000E+00     4.278300E-02     1.000000E+15           
         14  traj.climb_1.states:distance_2        c     0.000000E+00     5.373909E-02     1.000000E+15           
         15  traj.climb_1.states:distance_3        c     0.000000E+00     1.003646E-01     1.000000E+15           
         16  traj.climb_1.states:distance_4        c     0.000000E+00     1.751719E-01     1.000000E+15           
         17  traj.climb_1.states:distance_5        c     0.000000E+00     2.000973E-01     1.000000E+15           
         18  traj.climb_1.states:distance_6        c     0.000000E+00     2.357258E-01     1.000000E+15           
         19  traj.climb_1.states:distance_7        c     0.000000E+00     2.844956E-01     1.000000E+15           
         20  traj.climb_1.states:distance_8        c     0.000000E+00     2.996270E-01     1.000000E+15           
         21  traj.climb_1.controls:mach_0          c     3.600000E-01     4.000000E-01     1.480000E+00           
         22  traj.climb_1.controls:mach_1          c     3.600000E-01     9.910214E-01     1.480000E+00           
         23  traj.climb_1.controls:mach_2          c     3.600000E-01     1.426425E+00     1.480000E+00           
         24  traj.climb_1.controls:mach_3          c     3.600000E-01     1.432334E+00     1.480000E+00           
         25  traj.cruise.t_duration_0              c     5.000000E-01     1.347063E+00     1.500000E+00           
         26  traj.cruise.states:mass_0             c     0.000000E+00     7.688642E+00     1.000000E+17           
         27  traj.cruise.states:mass_1             c     0.000000E+00     7.574349E+00     1.000000E+17           
         28  traj.cruise.states:mass_2             c     0.000000E+00     7.538496E+00     1.000000E+17           
         29  traj.cruise.states:mass_3             c     0.000000E+00     7.406978E+00     1.000000E+17           
         30  traj.cruise.states:mass_4             c     0.000000E+00     7.228866E+00     1.000000E+17           
         31  traj.cruise.states:mass_5             c     0.000000E+00     7.173287E+00     1.000000E+17           
         32  traj.cruise.states:mass_6             c     0.000000E+00     7.095273E+00     1.000000E+17           
         33  traj.cruise.states:mass_7             c     0.000000E+00     6.988846E+00     1.000000E+17           
         34  traj.cruise.states:mass_8             c     0.000000E+00     6.955453E+00     1.000000E+17           
         35  traj.cruise.states:distance_0         c     0.000000E+00     5.932175E-01     1.000000E+15           
         36  traj.cruise.states:distance_1         c     0.000000E+00     9.975019E-01     1.000000E+15           
         37  traj.cruise.states:distance_2         c     0.000000E+00     1.125261E+00     1.000000E+15           
         38  traj.cruise.states:distance_3         c     0.000000E+00     1.597804E+00     1.000000E+15           
         39  traj.cruise.states:distance_4         c     0.000000E+00     2.247699E+00     1.000000E+15           
         40  traj.cruise.states:distance_5         c     0.000000E+00     2.452876E+00     1.000000E+15           
         41  traj.cruise.states:distance_6         c     0.000000E+00     2.742830E+00     1.000000E+15           
         42  traj.cruise.states:distance_7         c     0.000000E+00     3.142099E+00     1.000000E+15           
         43  traj.cruise.states:distance_8         c     0.000000E+00     3.268271E+00     1.000000E+15           
         44  traj.cruise.controls:mach_0           c     1.400000E+00     1.432334E+00     1.480000E+00           
         45  traj.cruise.controls:mach_1           c     1.400000E+00     1.410916E+00     1.480000E+00           
         46  traj.descent_1.t_duration_0           c     5.000000E-01     6.074534E-01     1.500000E+00           
         47  traj.descent_1.states:mass_0          c     0.000000E+00     6.951343E+00     1.000000E+17           
         48  traj.descent_1.states:mass_1          c     0.000000E+00     6.945746E+00     1.000000E+17           
         49  traj.descent_1.states:mass_2          c     0.000000E+00     6.943952E+00     1.000000E+17           
         50  traj.descent_1.states:mass_3          c     0.000000E+00     6.937152E+00     1.000000E+17           
         51  traj.descent_1.states:mass_4          c     0.000000E+00     6.928181E+00     1.000000E+17           
         52  traj.descent_1.states:mass_5          c     0.000000E+00     6.925670E+00     1.000000E+17           
         53  traj.descent_1.states:mass_6          c     0.000000E+00     6.922376E+00     1.000000E+17           
         54  traj.descent_1.states:mass_7          c     0.000000E+00     6.917747E+00     1.000000E+17           
         55  traj.descent_1.states:mass_8          c     0.000000E+00     6.916074E+00     1.000000E+17           
         56  traj.descent_1.states:distance_0      c     0.000000E+00     3.306223E+00     1.000000E+15           
         57  traj.descent_1.states:distance_1      c     0.000000E+00     3.352395E+00     1.000000E+15           
         58  traj.descent_1.states:distance_2      c     0.000000E+00     3.365999E+00     1.000000E+15           
         59  traj.descent_1.states:distance_3      c     0.000000E+00     3.413639E+00     1.000000E+15           
         60  traj.descent_1.states:distance_4      c     0.000000E+00     3.473653E+00     1.000000E+15           
         61  traj.descent_1.states:distance_5      c     0.000000E+00     3.491466E+00     1.000000E+15           
         62  traj.descent_1.states:distance_6      c     0.000000E+00     3.514485E+00     1.000000E+15           
         63  traj.descent_1.states:distance_7      c     0.000000E+00     3.540347E+00     1.000000E+15           
         64  traj.descent_1.states:distance_8      c     0.000000E+00     3.546580E+00     1.000000E+15           
         65  traj.descent_1.controls:mach_0        c     3.600000E-01     1.410916E+00     1.480000E+00           
         66  traj.descent_1.controls:mach_1        c     3.600000E-01     1.034417E+00     1.480000E+00           
         67  traj.descent_1.controls:mach_2        c     3.600000E-01     7.767552E-01     1.480000E+00           
         68  traj.descent_1.controls:mach_3        c     3.600000E-01     4.000000E-01     1.480000E+00           

   Constraints (i - inequality, e - equality)
      Index  Name                                                      Type          Lower           Value           Upper    Status  Lagrange Multiplier (N/A)
          0  mission:constraints:range_residual                           e   0.000000E+00   -2.273737E-14    0.000000E+00              9.00000E+100
          1  gtow_constraint.GTOW                                         e   0.000000E+00    0.000000E+00    0.000000E+00              9.00000E+100
          2  mission:constraints:mass_residual                            e   0.000000E+00    5.820766E-16    0.000000E+00              9.00000E+100
          3  traj.linkages.climb_1:mach_final|cruise:mach_initial         e   0.000000E+00    0.000000E+00    0.000000E+00              9.00000E+100
          4  traj.linkages.cruise:mach_final|descent_1:mach_initial       e   0.000000E+00    0.000000E+00    0.000000E+00              9.00000E+100
          5  traj.phases.climb_1->initial_boundary_constraint->mach       e   2.000000E-01    2.000000E-01    2.000000E-01              9.00000E+100
          6  traj.climb_1.collocation_constraint.defects:mass             e   0.000000E+00   -4.026604E-18    0.000000E+00              9.00000E+100
          7  traj.climb_1.collocation_constraint.defects:mass             e   0.000000E+00   -3.490151E-13    0.000000E+00              9.00000E+100
          8  traj.climb_1.collocation_constraint.defects:mass             e   0.000000E+00   -1.235393E-12    0.000000E+00              9.00000E+100
          9  traj.climb_1.collocation_constraint.defects:mass             e   0.000000E+00   -2.239736E-12    0.000000E+00              9.00000E+100
         10  traj.climb_1.collocation_constraint.defects:mass             e   0.000000E+00   -6.984285E-13    0.000000E+00              9.00000E+100
         11  traj.climb_1.collocation_constraint.defects:mass             e   0.000000E+00    6.665802E-12    0.000000E+00              9.00000E+100
         12  traj.climb_1.collocation_constraint.defects:mass             e   0.000000E+00    5.388087E-12    0.000000E+00              9.00000E+100
         13  traj.climb_1.collocation_constraint.defects:mass             e   0.000000E+00    6.364516E-12    0.000000E+00              9.00000E+100
         14  traj.climb_1.collocation_constraint.defects:mass             e   0.000000E+00    6.307123E-12    0.000000E+00              9.00000E+100
         15  traj.climb_1.collocation_constraint.defects:distance         e   0.000000E+00    2.863363E-19    0.000000E+00              9.00000E+100
         16  traj.climb_1.collocation_constraint.defects:distance         e   0.000000E+00    3.499665E-19    0.000000E+00              9.00000E+100
         17  traj.climb_1.collocation_constraint.defects:distance         e   0.000000E+00    4.454120E-19    0.000000E+00              9.00000E+100
         18  traj.climb_1.collocation_constraint.defects:distance         e   0.000000E+00    5.147798E-19    0.000000E+00              9.00000E+100
         19  traj.climb_1.collocation_constraint.defects:distance         e   0.000000E+00   -3.088679E-19    0.000000E+00              9.00000E+100
         20  traj.climb_1.collocation_constraint.defects:distance         e   0.000000E+00   -6.177357E-19    0.000000E+00              9.00000E+100
         21  traj.climb_1.collocation_constraint.defects:distance         e   0.000000E+00   -6.363028E-20    0.000000E+00              9.00000E+100
         22  traj.climb_1.collocation_constraint.defects:distance         e   0.000000E+00    1.081715E-18    0.000000E+00              9.00000E+100
         23  traj.climb_1.collocation_constraint.defects:distance         e   0.000000E+00   -6.363028E-19    0.000000E+00              9.00000E+100
         24  traj.cruise.collocation_constraint.defects:mass              e   0.000000E+00    4.347906E-11    0.000000E+00              9.00000E+100
         25  traj.cruise.collocation_constraint.defects:mass              e   0.000000E+00    3.500542E-11    0.000000E+00              9.00000E+100
         26  traj.cruise.collocation_constraint.defects:mass              e   0.000000E+00    2.350292E-11    0.000000E+00              9.00000E+100
         27  traj.cruise.collocation_constraint.defects:mass              e   0.000000E+00    3.221151E-11    0.000000E+00              9.00000E+100
         28  traj.cruise.collocation_constraint.defects:mass              e   0.000000E+00    1.096346E-11    0.000000E+00              9.00000E+100
         29  traj.cruise.collocation_constraint.defects:mass              e   0.000000E+00   -1.757546E-11    0.000000E+00              9.00000E+100
         30  traj.cruise.collocation_constraint.defects:mass              e   0.000000E+00   -1.632995E-11    0.000000E+00              9.00000E+100
         31  traj.cruise.collocation_constraint.defects:mass              e   0.000000E+00   -2.397440E-11    0.000000E+00              9.00000E+100
         32  traj.cruise.collocation_constraint.defects:mass              e   0.000000E+00   -3.434438E-11    0.000000E+00              9.00000E+100
         33  traj.cruise.collocation_constraint.defects:distance          e   0.000000E+00    3.219122E-16    0.000000E+00              9.00000E+100
         34  traj.cruise.collocation_constraint.defects:distance          e   0.000000E+00    2.649125E-16    0.000000E+00              9.00000E+100
         35  traj.cruise.collocation_constraint.defects:distance          e   0.000000E+00    1.737131E-16    0.000000E+00              9.00000E+100
         36  traj.cruise.collocation_constraint.defects:distance          e   0.000000E+00    2.485746E-16    0.000000E+00              9.00000E+100
         37  traj.cruise.collocation_constraint.defects:distance          e   0.000000E+00    7.817364E-17    0.000000E+00              9.00000E+100
         38  traj.cruise.collocation_constraint.defects:distance          e   0.000000E+00   -1.422936E-16    0.000000E+00              9.00000E+100
         39  traj.cruise.collocation_constraint.defects:distance          e   0.000000E+00   -1.275706E-16    0.000000E+00              9.00000E+100
         40  traj.cruise.collocation_constraint.defects:distance          e   0.000000E+00   -1.883702E-16    0.000000E+00              9.00000E+100
         41  traj.cruise.collocation_constraint.defects:distance          e   0.000000E+00   -2.654554E-16    0.000000E+00              9.00000E+100
         42  traj.phases.descent_1->final_boundary_constraint->mach       e   2.000000E-01    2.000000E-01    2.000000E-01              9.00000E+100
         43  traj.descent_1.collocation_constraint.defects:mass           e   0.000000E+00   -4.681951E-12    0.000000E+00              9.00000E+100
         44  traj.descent_1.collocation_constraint.defects:mass           e   0.000000E+00   -1.999194E-12    0.000000E+00              9.00000E+100
         45  traj.descent_1.collocation_constraint.defects:mass           e   0.000000E+00   -4.791063E-13    0.000000E+00              9.00000E+100
         46  traj.descent_1.collocation_constraint.defects:mass           e   0.000000E+00   -3.914893E-13    0.000000E+00              9.00000E+100
         47  traj.descent_1.collocation_constraint.defects:mass           e   0.000000E+00    3.334691E-13    0.000000E+00              9.00000E+100
         48  traj.descent_1.collocation_constraint.defects:mass           e   0.000000E+00    2.456148E-13    0.000000E+00              9.00000E+100
         49  traj.descent_1.collocation_constraint.defects:mass           e   0.000000E+00    8.674350E-14    0.000000E+00              9.00000E+100
         50  traj.descent_1.collocation_constraint.defects:mass           e   0.000000E+00    2.111968E-14    0.000000E+00              9.00000E+100
         51  traj.descent_1.collocation_constraint.defects:mass           e   0.000000E+00   -4.314540E-16    0.000000E+00              9.00000E+100
         52  traj.descent_1.collocation_constraint.defects:distance       e   0.000000E+00    4.984861E-17    0.000000E+00              9.00000E+100
         53  traj.descent_1.collocation_constraint.defects:distance       e   0.000000E+00    8.953023E-18    0.000000E+00              9.00000E+100
         54  traj.descent_1.collocation_constraint.defects:distance       e   0.000000E+00   -8.270165E-18    0.000000E+00              9.00000E+100
         55  traj.descent_1.collocation_constraint.defects:distance       e   0.000000E+00    4.665078E-18    0.000000E+00              9.00000E+100
         56  traj.descent_1.collocation_constraint.defects:distance       e   0.000000E+00    2.087009E-18    0.000000E+00              9.00000E+100
         57  traj.descent_1.collocation_constraint.defects:distance       e   0.000000E+00    8.348034E-18    0.000000E+00              9.00000E+100
         58  traj.descent_1.collocation_constraint.defects:distance       e   0.000000E+00    1.972700E-17    0.000000E+00              9.00000E+100
         59  traj.descent_1.collocation_constraint.defects:distance       e   0.000000E+00    3.793654E-19    0.000000E+00              9.00000E+100
         60  traj.descent_1.collocation_constraint.defects:distance       e   0.000000E+00   -8.080482E-18    0.000000E+00              9.00000E+100
         61  traj.phases.climb_1->path_constraint->throttle               i   0.000000E+00    7.854496E-01    1.000000E+00              9.00000E+100
         62  traj.phases.climb_1->path_constraint->throttle               i   0.000000E+00    5.622694E-01    1.000000E+00              9.00000E+100
         63  traj.phases.climb_1->path_constraint->throttle               i   0.000000E+00    5.627681E-01    1.000000E+00              9.00000E+100
         64  traj.phases.climb_1->path_constraint->throttle               i   0.000000E+00    5.833632E-01    1.000000E+00              9.00000E+100
         65  traj.phases.climb_1->path_constraint->throttle               i   0.000000E+00    5.833632E-01    1.000000E+00              9.00000E+100
         66  traj.phases.climb_1->path_constraint->throttle               i   0.000000E+00    6.648404E-01    1.000000E+00              9.00000E+100
         67  traj.phases.climb_1->path_constraint->throttle               i   0.000000E+00    7.773328E-01    1.000000E+00              9.00000E+100
         68  traj.phases.climb_1->path_constraint->throttle               i   0.000000E+00    8.101373E-01    1.000000E+00              9.00000E+100
         69  traj.phases.climb_1->path_constraint->throttle               i   0.000000E+00    8.101373E-01    1.000000E+00              9.00000E+100
         70  traj.phases.climb_1->path_constraint->throttle               i   0.000000E+00    8.668203E-01    1.000000E+00              9.00000E+100
         71  traj.phases.climb_1->path_constraint->throttle               i   0.000000E+00    9.565312E-01    1.000000E+00              9.00000E+100
         72  traj.phases.climb_1->path_constraint->throttle               i   0.000000E+00    9.999985E-01    1.000000E+00              9.00000E+100
         73  traj.phases.cruise->initial_boundary_constraint->throttle    i   0.000000E+00    7.062893E-01    1.000000E+00              9.00000E+100
         74  traj.phases.cruise->final_boundary_constraint->throttle      i   0.000000E+00    6.599821E-01    1.000000E+00              9.00000E+100
         75  traj.phases.descent_1->path_constraint->throttle             i   0.000000E+00    2.104685E-01    1.000000E+00              9.00000E+100
         76  traj.phases.descent_1->path_constraint->throttle             i   0.000000E+00    1.919436E-01    1.000000E+00              9.00000E+100
         77  traj.phases.descent_1->path_constraint->throttle             i   0.000000E+00    1.725431E-01    1.000000E+00              9.00000E+100
         78  traj.phases.descent_1->path_constraint->throttle             i   0.000000E+00    1.666172E-01    1.000000E+00              9.00000E+100
         79  traj.phases.descent_1->path_constraint->throttle             i   0.000000E+00    1.666172E-01    1.000000E+00              9.00000E+100
         80  traj.phases.descent_1->path_constraint->throttle             i   0.000000E+00    1.402176E-01    1.000000E+00              9.00000E+100
         81  traj.phases.descent_1->path_constraint->throttle             i   0.000000E+00    8.864794E-02    1.000000E+00              9.00000E+100
         82  traj.phases.descent_1->path_constraint->throttle             i   0.000000E+00    5.254340E-02    1.000000E+00              9.00000E+100
         83  traj.phases.descent_1->path_constraint->throttle             i   0.000000E+00    5.254340E-02    1.000000E+00              9.00000E+100
         84  traj.phases.descent_1->path_constraint->throttle             i   0.000000E+00    1.754336E-06    1.000000E+00              9.00000E+100
         85  traj.phases.descent_1->path_constraint->throttle             i   0.000000E+00    4.633770E-06    1.000000E+00              9.00000E+100
         86  traj.phases.descent_1->path_constraint->throttle             i   0.000000E+00    8.458018E-02    1.000000E+00              9.00000E+100

--------------------------------------------------------------------------------
/usr/share/miniconda/envs/test/lib/python3.12/site-packages/openmdao/core/driver.py:143: OMDeprecationWarning:boolean evaluation of DriverResult is temporarily implemented to mimick the previous `failed` return behavior of run_driver.
Use the `success` attribute of the returned DriverResult object to test for successful driver completion.

And let’s print out the objective value, fuel burned:

print(prob.get_val(av.Mission.Summary.FUEL_BURNED, units='kg')[0])
10399.3571015945

The added flexibility in the mission allowed the optimizer to reduce the fuel burn compared to the linear Mach profile case.

Looking at the altitude and Mach profiles, we see that the optimizer chose a more subtly complex Mach profile:

Altitude and Mach Profiles

Conclusion#

This example demonstrated how to use Aviary to optimize a more complex mission. We increased the number of segments in the mission, allowed the optimizer to choose the optimal Mach profile, and increased the polynomial control order to allow for more complex Mach profiles. We also modified the aircraft configuration to demonstrate how Aviary can be used to quickly evaluate the impact of design changes on the mission performance.