Skip to content

Commit

Permalink
Merge pull request OpenMDAO#46 from johnjasa/add_simple_mission_benches
Browse files Browse the repository at this point in the history
Add simple mission benchmarks
  • Loading branch information
jdgratz10 authored Jan 16, 2024
2 parents 1be4116 + bbbd796 commit 0d674b4
Show file tree
Hide file tree
Showing 12 changed files with 595 additions and 51 deletions.
4 changes: 4 additions & 0 deletions aviary/docs/getting_started/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ If you also want to install all packages used for the Aviary tests _and_ externa

If you are a developer and plan to modify parts of the Aviary code, install in an "editable mode" with pip:

pip install .

If you are a developer and plan to modify parts of the Aviary code, install in an "editable mode" with ``pip``:

pip install -e .

This installs the package in the current environment such that changes to the Python code don't require re-installation.
Expand Down
1 change: 1 addition & 0 deletions aviary/interface/default_phase_info/simple.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
"fix_duration": False,
"initial_bounds": ((0.0, 0.0), "min"),
"duration_bounds": ((64.0, 192.0), "min"),
"add_initial_mass_constraint": False,
},
"initial_guesses": {"times": ([0, 128], "min")},
},
Expand Down
1 change: 1 addition & 0 deletions aviary/interface/graphical_input.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ def create_bounds(center):
'optimize_mach': optimize_mach_phase_vars[i].get(),
'optimize_altitude': optimize_altitude_phase_vars[i].get(),
'polynomial_control_order': polynomial_order,
'use_polynomial_control': True,
'num_segments': num_segments,
'order': 3,
'solve_for_range': False,
Expand Down
31 changes: 24 additions & 7 deletions aviary/interface/methods_for_level2.py
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,9 @@ def load_inputs(self, input_filename, phase_info=None, engine_builder=None):
elif self.mission_method is SOLVED:
from aviary.interface.default_phase_info.solved import phase_info

print('Loaded default phase_info for'
f'{self.mission_method.value.lower()} equations of motion')

# create a new dictionary that only contains the phases from phase_info
self.phase_info = {}

Expand Down Expand Up @@ -1196,7 +1199,7 @@ def add_post_mission_systems(self, include_landing=True):
"""

if include_landing and self.post_mission_info['include_landing']:
if self.mission_method is HEIGHT_ENERGY:
if self.mission_method is HEIGHT_ENERGY or self.mission_method is SIMPLE:
self._add_flops_landing_systems()
elif self.mission_method is TWO_DEGREES_OF_FREEDOM:
self._add_gasp_landing_systems()
Expand Down Expand Up @@ -2412,16 +2415,30 @@ def _add_flops_landing_systems(self):
'traj.climb.initial_states:mass')
self.model.connect(Mission.Takeoff.GROUND_DISTANCE,
'traj.climb.initial_states:range')
self.model.connect(Mission.Takeoff.FINAL_VELOCITY,
'traj.climb.initial_states:velocity')
self.model.connect(Mission.Takeoff.FINAL_ALTITUDE,
'traj.climb.initial_states:altitude')
if self.mission_method is HEIGHT_ENERGY:
self.model.connect(Mission.Takeoff.FINAL_VELOCITY,
'traj.climb.initial_states:velocity')
self.model.connect(Mission.Takeoff.FINAL_ALTITUDE,
'traj.climb.initial_states:altitude')
else:
pass
# TODO: connect this correctly
# mass is the most important to connect but these others should
# be connected as well
# self.model.connect(Mission.Takeoff.FINAL_VELOCITY,
# 'traj.climb.initial_states:mach')
# self.model.connect(Mission.Takeoff.FINAL_ALTITUDE,
# 'traj.climb.controls:altitude')

self.model.connect('traj.descent.states:mass',
Mission.Landing.TOUCHDOWN_MASS, src_indices=[-1])
# TODO: approach velocity should likely be connected
self.model.connect('traj.descent.states:altitude', Mission.Landing.INITIAL_ALTITUDE,
src_indices=[-1])
if self.mission_method is HEIGHT_ENERGY:
self.model.connect('traj.descent.states:altitude', Mission.Landing.INITIAL_ALTITUDE,
src_indices=[-1])
else:
self.model.connect('traj.descent.control_values:altitude', Mission.Landing.INITIAL_ALTITUDE,
src_indices=[0])

def _add_gasp_landing_systems(self):
self.model.add_subsystem(
Expand Down
6 changes: 6 additions & 0 deletions aviary/interface/test/test_check_phase_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@ def test_correct_input_gasp(self):
self.assertTrue(check_phase_info(phase_info_gasp,
mission_method=TWO_DEGREES_OF_FREEDOM))

def test_incorrect_mission_method(self):
# Let's pass an incorrect mission_method name
incorrect_mission_method = 'INVALID_METHOD'
with self.assertRaises(ValueError):
check_phase_info(phase_info_flops, mission_method=incorrect_mission_method)


if __name__ == '__main__':
unittest.main()
6 changes: 2 additions & 4 deletions aviary/interface/test/test_phase_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,7 @@ def test_phase_info_parameterization_gasp(self):

prob = AviaryProblem()

csv_path = pkg_resources.resource_filename(
"aviary", "models/small_single_aisle/small_single_aisle_GwGm.csv")
csv_path = "models/small_single_aisle/small_single_aisle_GwGm.csv"

prob.load_inputs(csv_path, phase_info)
prob.check_and_preprocess_inputs()
Expand Down Expand Up @@ -131,8 +130,7 @@ def test_phase_info_parameterization_flops(self):

prob = AviaryProblem()

csv_path = pkg_resources.resource_filename(
"aviary", "models/test_aircraft/aircraft_for_bench_FwFm.csv")
csv_path = "models/test_aircraft/aircraft_for_bench_FwFm.csv"

prob.load_inputs(csv_path, phase_info)
prob.check_and_preprocess_inputs()
Expand Down
6 changes: 4 additions & 2 deletions aviary/interface/utils/check_phase_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -226,8 +226,10 @@ def check_phase_info(phase_info, mission_method):
elif mission_method is SIMPLE:
return
else:
raise ValueError(
"Invalid mission_method. Please choose either 'FLOPS', 'GASP', 'simple', or 'solved'.")
possible_values = ["'"+e.value+"'" for e in EquationsOfMotion]
possible_values[-1] = "or " + possible_values[-1]
raise ValueError("Invalid mission_method. Please choose from " +
", ".join(possible_values) + ".")

# Check if all phases exist in phase_info
for phase in phase_info:
Expand Down
70 changes: 54 additions & 16 deletions aviary/mission/flops_based/phases/simple_energy_phase.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from math import isclose

import dymos as dm

from aviary.mission.flops_based.phases.phase_builder_base import (
Expand Down Expand Up @@ -154,6 +152,7 @@ def build_phase(self, aviary_options: AviaryValues = None):
optimize_altitude = user_options.get_val('optimize_altitude')
input_initial = user_options.get_val('input_initial')
polynomial_control_order = user_options.get_item('polynomial_control_order')[0]
use_polynomial_control = user_options.get_val('use_polynomial_control')
throttle_enforcement = user_options.get_val('throttle_enforcement')
mach_bounds = user_options.get_item('mach_bounds')
altitude_bounds = user_options.get_item('altitude_bounds')
Expand All @@ -162,6 +161,8 @@ def build_phase(self, aviary_options: AviaryValues = None):
initial_altitude = user_options.get_item('initial_altitude')[0]
final_altitude = user_options.get_item('final_altitude')[0]
solve_for_range = user_options.get_val('solve_for_range')
no_descent = user_options.get_val('no_descent')
no_climb = user_options.get_val('no_climb')

##############
# Add States #
Expand Down Expand Up @@ -205,13 +206,22 @@ def build_phase(self, aviary_options: AviaryValues = None):
################
# Add Controls #
################
phase.add_polynomial_control(
Dynamic.Mission.MACH,
targets=Dynamic.Mission.MACH, units=mach_bounds[1],
opt=optimize_mach, lower=mach_bounds[0][0], upper=mach_bounds[0][1],
rate_targets=[Dynamic.Mission.MACH_RATE],
order=polynomial_control_order, ref=0.5,
)
if use_polynomial_control:
phase.add_polynomial_control(
Dynamic.Mission.MACH,
targets=Dynamic.Mission.MACH, units=mach_bounds[1],
opt=optimize_mach, lower=mach_bounds[0][0], upper=mach_bounds[0][1],
rate_targets=[Dynamic.Mission.MACH_RATE],
order=polynomial_control_order, ref=0.5,
)
else:
phase.add_control(
Dynamic.Mission.MACH,
targets=Dynamic.Mission.MACH, units=mach_bounds[1],
opt=optimize_mach, lower=mach_bounds[0][0], upper=mach_bounds[0][1],
rate_targets=[Dynamic.Mission.MACH_RATE],
ref=0.5,
)

if optimize_mach and fix_initial:
phase.add_boundary_constraint(
Expand All @@ -224,13 +234,22 @@ def build_phase(self, aviary_options: AviaryValues = None):
)

# Add altitude rate as a control
phase.add_polynomial_control(
Dynamic.Mission.ALTITUDE,
targets=Dynamic.Mission.ALTITUDE, units=altitude_bounds[1],
opt=optimize_altitude, lower=altitude_bounds[0][0], upper=altitude_bounds[0][1],
rate_targets=[Dynamic.Mission.ALTITUDE_RATE],
order=polynomial_control_order, ref=altitude_bounds[0][1],
)
if use_polynomial_control:
phase.add_polynomial_control(
Dynamic.Mission.ALTITUDE,
targets=Dynamic.Mission.ALTITUDE, units=altitude_bounds[1],
opt=optimize_altitude, lower=altitude_bounds[0][0], upper=altitude_bounds[0][1],
rate_targets=[Dynamic.Mission.ALTITUDE_RATE],
order=polynomial_control_order, ref=altitude_bounds[0][1],
)
else:
phase.add_control(
Dynamic.Mission.ALTITUDE,
targets=Dynamic.Mission.ALTITUDE, units=altitude_bounds[1],
opt=optimize_altitude, lower=altitude_bounds[0][0], upper=altitude_bounds[0][1],
rate_targets=[Dynamic.Mission.ALTITUDE_RATE],
ref=altitude_bounds[0][1],
)

if optimize_altitude and fix_initial:
phase.add_boundary_constraint(
Expand Down Expand Up @@ -278,9 +297,20 @@ def build_phase(self, aviary_options: AviaryValues = None):
output_name=Dynamic.Mission.THROTTLE, units='unitless'
)

phase.add_timeseries_output(
Dynamic.Mission.VELOCITY,
output_name=Dynamic.Mission.VELOCITY, units='m/s'
)

###################
# Add Constraints #
###################
if no_descent:
phase.add_path_constraint(Dynamic.Mission.ALTITUDE_RATE, lower=0.0)

if no_climb:
phase.add_path_constraint(Dynamic.Mission.ALTITUDE_RATE, upper=0.0)

required_available_climb_rate, units = user_options.get_item(
'required_available_climb_rate')

Expand Down Expand Up @@ -344,6 +374,8 @@ def _extra_ode_init_kwargs(self):

EnergyPhase._add_meta_data('polynomial_control_order', val=None)

EnergyPhase._add_meta_data('use_polynomial_control', val=True)

EnergyPhase._add_meta_data('add_initial_mass_constraint', val=False)

EnergyPhase._add_meta_data('fix_initial', val=True)
Expand All @@ -360,6 +392,12 @@ def _extra_ode_init_kwargs(self):
'required_available_climb_rate', val=None, units='m/s',
desc='minimum avaliable climb rate')

EnergyPhase._add_meta_data(
'no_climb', val=False, desc='aircraft is not allowed to climb during phase')

EnergyPhase._add_meta_data(
'no_descent', val=False, desc='aircraft is not allowed to descend during phase')

EnergyPhase._add_meta_data('constrain_final', val=False)

EnergyPhase._add_meta_data('input_initial', val=False)
Expand Down
Loading

0 comments on commit 0d674b4

Please sign in to comment.