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:897: 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:2323: 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:2323: 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:2323: 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:2323: 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:2323: 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:2323: 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:2323: 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:2323: 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:2323: 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:2323: 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:2323: 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:2323: 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:2323: 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:2323: 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 152800 lbm
'aircraft:engine:mass [7400.] lbm
'aircraft:fins:mass 0 lbm
'aircraft:fuel:auxiliary_fuel_capacity 0 lbm
'aircraft:fuel:fuselage_fuel_capacity 0 lbm
'aircraft:fuel:total_capacity 45694 lbm
'aircraft:fuselage:planform_area 1578.24 ft**2
'aircraft:fuselage:wetted_area 4158.62 ft**2
'aircraft:horizontal_tail:wetted_area 592.65 ft**2
'aircraft:landing_gear:main_gear_oleo_length 102 inch
'aircraft:landing_gear:nose_gear_oleo_length 67 inch
'aircraft:vertical_tail:wetted_area 581.13 ft**2
'aircraft:wing:aspect_ratio 11.22091 unitless
'aircraft:wing:control_surface_area 137 ft**2
'aircraft:wing:wetted_area 2396.56 ft**2
--- 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.7281579270000407 seconds.
Total jacobian shape: (165, 136)
Jacobian shape: (165, 136) (4.86% nonzero)
FWD solves: 11 REV solves: 0
Total colors vs. total size: 11 vs 136 (91.91% improvement)
Sparsity computed using tolerance: 1e-25
Time to compute sparsity: 1.7282 sec
Time to compute coloring: 0.0592 sec
Memory to compute coloring: 0.1250 MB
Coloring created on: 2024-11-04 17:13:20
Optimization terminated successfully (Exit mode 0)
Current function value: 2.947086608178998
Iterations: 17
Function evaluations: 17
Gradient evaluations: 17
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.