Mission Optimization with Many Phases for a Commercial Aircraft#
So far within these example docs we have been building up the complexity of our coupled aircraft-mission design problem. In the simple mission example, we flew the aircraft in straight line phases. In the more advanced mission example, we allowed the optimizer to find the optimal Mach profiles for phases.
In this example, we will build on the prior examples by adding more phases to the mission. This will allow us to model more complex missions, such as a commercial aircraft flying a long-haul route with multiple cruise segments, intermediary climb segments, and a cruise-climb segment.
Problem Formulation#
We use the aviary draw_mission
GUI as shown below:
This results in the following phase_info
dictionary:
phase_info = {
"pre_mission": {"include_takeoff": False, "optimize_mass": True},
"climb_1": {
"subsystem_options": {"core_aerodynamics": {"method": "computed"}},
"user_options": {
"optimize_mach": False,
"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": (31000.0, "ft"),
"altitude_bounds": ((0.0, 31500.0), "ft"),
"throttle_enforcement": "path_constraint",
"fix_initial": True,
"constrain_final": False,
"fix_duration": False,
"initial_bounds": ((0.0, 0.0), "min"),
"duration_bounds": ((25.5, 76.5), "min"),
},
"initial_guesses": {"time": ([0, 51], "min")},
},
"cruise_1": {
"subsystem_options": {"core_aerodynamics": {"method": "computed"}},
"user_options": {
"optimize_mach": False,
"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": (31000.0, "ft"),
"final_altitude": (31000.0, "ft"),
"altitude_bounds": ((30500.0, 31500.0), "ft"),
"throttle_enforcement": "boundary_constraint",
"fix_initial": False,
"constrain_final": False,
"fix_duration": False,
"initial_bounds": ((25.5, 76.5), "min"),
"duration_bounds": ((23.5, 70.5), "min"),
},
"initial_guesses": {"time": ([51, 47], "min")},
},
"climb_2": {
"subsystem_options": {"core_aerodynamics": {"method": "computed"}},
"user_options": {
"optimize_mach": False,
"optimize_altitude": False,
"polynomial_control_order": 1,
"num_segments": 3,
"order": 3,
"solve_for_distance": False,
"initial_mach": (0.72, "unitless"),
"final_mach": (0.74, "unitless"),
"mach_bounds": ((0.7, 0.76), "unitless"),
"initial_altitude": (31000.0, "ft"),
"final_altitude": (33000.0, "ft"),
"altitude_bounds": ((30500.0, 33500.0), "ft"),
"throttle_enforcement": "boundary_constraint",
"fix_initial": False,
"constrain_final": False,
"fix_duration": False,
"initial_bounds": ((49.0, 147.0), "min"),
"duration_bounds": ((5.0, 15.0), "min"),
},
"initial_guesses": {"time": ([98, 10], "min")},
},
"cruise_2": {
"subsystem_options": {"core_aerodynamics": {"method": "computed"}},
"user_options": {
"optimize_mach": False,
"optimize_altitude": False,
"polynomial_control_order": 1,
"num_segments": 3,
"order": 3,
"solve_for_distance": False,
"initial_mach": (0.74, "unitless"),
"final_mach": (0.74, "unitless"),
"mach_bounds": ((0.72, 0.76), "unitless"),
"initial_altitude": (33000.0, "ft"),
"final_altitude": (33000.0, "ft"),
"altitude_bounds": ((32500.0, 33500.0), "ft"),
"throttle_enforcement": "boundary_constraint",
"fix_initial": False,
"constrain_final": False,
"fix_duration": False,
"initial_bounds": ((54.0, 162.0), "min"),
"duration_bounds": ((24.0, 72.0), "min"),
},
"initial_guesses": {"time": ([108, 48], "min")},
},
"climb_3": {
"subsystem_options": {"core_aerodynamics": {"method": "computed"}},
"user_options": {
"optimize_mach": False,
"optimize_altitude": False,
"polynomial_control_order": 1,
"num_segments": 3,
"order": 3,
"solve_for_distance": False,
"initial_mach": (0.74, "unitless"),
"final_mach": (0.76, "unitless"),
"mach_bounds": ((0.72, 0.78), "unitless"),
"initial_altitude": (33000.0, "ft"),
"final_altitude": (34500.0, "ft"),
"altitude_bounds": ((32500.0, 35000.0), "ft"),
"throttle_enforcement": "boundary_constraint",
"fix_initial": False,
"constrain_final": False,
"fix_duration": False,
"initial_bounds": ((78.0, 234.0), "min"),
"duration_bounds": ((7.0, 21.0), "min"),
},
"initial_guesses": {"time": ([156, 14], "min")},
},
"climb_4": {
"subsystem_options": {"core_aerodynamics": {"method": "computed"}},
"user_options": {
"optimize_mach": False,
"optimize_altitude": False,
"polynomial_control_order": 1,
"num_segments": 3,
"order": 3,
"solve_for_distance": False,
"initial_mach": (0.76, "unitless"),
"final_mach": (0.76, "unitless"),
"mach_bounds": ((0.74, 0.78), "unitless"),
"initial_altitude": (34500.0, "ft"),
"final_altitude": (36000.0, "ft"),
"altitude_bounds": ((34000.0, 36500.0), "ft"),
"throttle_enforcement": "boundary_constraint",
"fix_initial": False,
"constrain_final": False,
"fix_duration": False,
"initial_bounds": ((85.0, 255.0), "min"),
"duration_bounds": ((43.0, 129.0), "min"),
},
"initial_guesses": {"time": ([170, 86], "min")},
},
"descent_1": {
"subsystem_options": {"core_aerodynamics": {"method": "computed"}},
"user_options": {
"optimize_mach": False,
"optimize_altitude": False,
"polynomial_control_order": 1,
"num_segments": 3,
"order": 3,
"solve_for_distance": False,
"initial_mach": (0.76, "unitless"),
"final_mach": (0.2, "unitless"),
"mach_bounds": ((0.18, 0.78), "unitless"),
"initial_altitude": (36000.0, "ft"),
"final_altitude": (500.0, "ft"),
"altitude_bounds": ((0.0, 36500.0), "ft"),
"throttle_enforcement": "path_constraint",
"fix_initial": False,
"constrain_final": True,
"fix_duration": False,
"initial_bounds": ((128.0, 384.0), "min"),
"duration_bounds": ((41.0, 123.0), "min"),
},
"initial_guesses": {"time": ([256, 82], "min")},
},
"post_mission": {
"include_landing": False,
"constrain_range": True,
"target_range": (2393, "nmi"),
},
}
Running Aviary with Updated Parameters#
Let’s now run Aviary with this multiphase mission and view the results.
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/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 'mach' 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 '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 'mach' in phase 'cruise_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_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 'mach' in phase 'climb_2': 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 'climb_2': 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 'mach' in phase 'cruise_2': 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_2': 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 'mach' in phase 'climb_3': 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 'climb_3': 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 'mach' in phase 'climb_4': 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 'climb_4': 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 'mach' in phase 'descent_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 '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 ---
[path] 0.0000e+00 <= throttle <= 1.0000e+00 [unitless]
--- cruise_1 ---
[initial] 0.0000e+00 <= throttle <= 1.0000e+00 [unitless]
[final] 0.0000e+00 <= throttle <= 1.0000e+00 [unitless]
--- climb_2 ---
[initial] 0.0000e+00 <= throttle <= 1.0000e+00 [unitless]
[final] 0.0000e+00 <= throttle <= 1.0000e+00 [unitless]
--- cruise_2 ---
[initial] 0.0000e+00 <= throttle <= 1.0000e+00 [unitless]
[final] 0.0000e+00 <= throttle <= 1.0000e+00 [unitless]
--- climb_3 ---
[initial] 0.0000e+00 <= throttle <= 1.0000e+00 [unitless]
[final] 0.0000e+00 <= throttle <= 1.0000e+00 [unitless]
--- climb_4 ---
[initial] 0.0000e+00 <= throttle <= 1.0000e+00 [unitless]
[final] 0.0000e+00 <= throttle <= 1.0000e+00 [unitless]
--- descent_1 ---
[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_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/solvers/linear/linear_rhs_checker.py:178: SolverWarning:DirectSolver in 'traj.phases.climb_2.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.cruise_2.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.climb_3.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.climb_4.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.
Model viewer data has already been recorded for Driver.
Full total jacobian for problem 'aircraft_for_bench_FwFm' was computed 3 times, taking 1.664703194000026 seconds.
Total jacobian shape: (164, 135)
Jacobian shape: (164, 135) (4.89% nonzero)
FWD solves: 10 REV solves: 0
Total colors vs. total size: 10 vs 135 (92.59% improvement)
Sparsity computed using tolerance: 1e-25
Time to compute sparsity: 1.6647 sec
Time to compute coloring: 0.0575 sec
Memory to compute coloring: 0.0000 MB
Coloring created on: 2024-09-16 15:49:34
Optimization terminated successfully (Exit mode 0)
Current function value: 3.105029798355184
Iterations: 16
Function evaluations: 16
Gradient evaluations: 16
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:
Note
Remember, we did not allow the optimizer to control either the Mach or the altitude profiles. The optimizer varied the phase durations until the optimal mission profile was found.
What Next?#
The point of this doc page is to show that missions can be arbitrarily complex in terms of the number of phases and how they’re classified. If you want multiple climb, cruise, descent phases, that’s absolutely something Aviary can handle.
There are a lot of options for how you could modify this example. You could:
enable the
optimize_mach
oroptimize_altitude
flagsincrease the
polynomial_control_order
so there’s more flexibility in the optimized missiontry different
target_range
values for the full mission rangeadd an external subsystem to the phases
Playing around with a model and seeing how different settings affect the optimization and resulting aircraft design is always an enlightening experience.