# Testing Cell
from pathlib import Path
from aviary.api import get_path
from aviary.core.aviary_problem import AviaryProblem
from aviary.interface.cmd_entry_points import _command_map
from aviary.interface.run_aviary import run_aviary
from aviary.utils.doctape import glue_class_methods, glue_keys, glue_variable
if 'run_mission' in _command_map:
glue_variable('run_mission', md_code=True)
glue_variable(run_aviary.__name__, md_code=True)
glue_variable(f'{run_aviary.__name__}()', md_code=True)
glue_variable(AviaryProblem.__name__, md_code=True)
glue_variable(f'{AviaryProblem.build_model.__name__}()', md_code=True)
glue_variable(f'{AviaryProblem.setup.__name__}()', md_code=True)
# glue_class_methods(AviaryProblem, [], prefix='prob', md_code=True)
Path.exists(get_path('advanced_single_aisle.csv'))
Path.exists(get_path('models/missions/energy_state_default.py'))
run_mission
run_aviary
run_aviary()
AviaryProblem
build_model()
setup()
True
Running a Custom Optimization#
Aviary’s Python API allows for significantly more customization over what Aviary does. In this example, we will dig further into the Python API to optimize an aircraft with new design variables and constraints.
The Optimization Problem#
In this toy problem, we have two constraints that we want our aircraft to meet. These are arbitrarily chosen for this example, but could represent some specific performance criteria we are trying to meet.
Thrust-to-Weight ratio greater than 0.35
Wing loading greater than 120 lbf./ft.2
In order for our aircraft to meet these performance goals, we will need to adjust some related properties of the aircraft. The chosen design variables for this problem are:
Engine scale factor, which will adjust how much thrust our engines are sized to output
Wing area
We will be using Aviary’s default objective function, which minimizes fuel burn over the design mission.
Setting Up Aviary#
For this example we cannot use the run_mission command or the run_aviary() function in Python. The tradeoff of the “single-line” simplicity of those methods is a lack of customizability over how the problem is set up, which is needed for this problem definition. Instead we will go one level deeper into Aviary’s Python API and directly utilize the AviaryProblem object. In brief, an AviaryProblem is an OpenMDAO Problem that is extended to have additional Aviary-specific features. Creating and running an AviaryProblem is very similar to the steps needed to use a pure OpenMDAO problem.
import aviary.api as av
from aviary.models.missions.energy_state_default import phase_info
# Suppress outputs by setting verbosity as zero (quiet mode)
prob = av.AviaryProblem(verbosity=0)
# Load aircraft and options data from provided sources
prob.load_inputs('models/aircraft/test_aircraft/aircraft_for_bench_FwFm.csv', phase_info)
# Sanity check inputs and guess initial conditions for mission phases
prob.check_and_preprocess_inputs()
# Have Aviary build the OpenMDAO model with pre-mission, mission, and post-mission components
prob.build_model()
# Selecting optimizer and iteration limit are optional
prob.add_driver('SLSQP', max_iter=20)
# Add the default design variables needed to size the aircraft
prob.add_design_variables()
# Add wing area and engine scaling as additional design variables
prob.model.add_design_var(av.Aircraft.Engine.SCALE_FACTOR, lower=0.8, upper=1.2, ref=1)
prob.model.add_design_var(av.Aircraft.Wing.AREA, lower=1200, upper=1800, units='ft**2', ref=1400)
# Add the default objective function (minimum fuel burn)
prob.add_objective()
# Add constraints for wing loading and thrust-to-weight ratio
prob.model.add_constraint(av.Aircraft.Design.WING_LOADING, lower=120, units='lbf/ft**2')
prob.model.add_constraint(av.Aircraft.Design.THRUST_TO_WEIGHT_RATIO, lower=0.35)
# Standard OpenMDAO problem setup step
prob.setup()
# Run the optimization problem
prob.run_aviary_problem()
This code mirrors what run_aviary() is actually doing behind the scenes - these steps can be broken down even further, but that isn’t necessary for this example.
There is a combination of AviaryProblem-specific methods and some standard OpenMDAO ones mixed together here. add_design_var() and add_constraint() are two OpenMDAO methods used to define our optimization problem. Because the AviaryProblem is based on the OpenMDAO Problem, we can directly use any method that exists for the OpenMDAO Problem too.
The order these steps are executed in is very important! Flipping around the order will break Aviary and result in an immediate error or important parts of the problem definition not being done correctly. The exception is our custom optimization problem setup steps (adding design variables and constraints). In this example, the order does not matter too much, as long as it is done between build_model() and setup(). In general, when customizing an AviaryProblem it is good practice to do any modifications in the same place you would make similar changes to a pure OpenMDAO problem. This takes some familiarity with OpenMDAO to master, but the reward is extremely broad power over exactly what optimization problem Aviary runs.
Now let’s take a look at some results of the problem:
Takeoff Gross Weight = 158022.1897357919 lbm
Design Variables
---------------
Engine Scale Factor (started at 1) = 0.9559522818216057
Wing Area (started at 1370) = 1316.8515811315995 ft^2
Constraints
-----------
Wing Loading = 120.0 lbf/ft^2
Thrust/Weight Ratio = 0.35000000000000003
We can see that the value for engine scale factor and wing area have changed from their initial values, which is expected since they were design variables for the problem. In addition, the values for our new constraints are right against their specified bounds. This example was designed such that both constraints are simultaneously active, allowing us to easily see how they affected the aircraft design.