Skip to content

Commit

Permalink
Merge pull request #166 from johnjasa/combine_builders
Browse files Browse the repository at this point in the history
Combine phase builders using shared class
  • Loading branch information
johnjasa authored Mar 20, 2024
2 parents 1d78783 + 66a8f60 commit 545f58f
Show file tree
Hide file tree
Showing 11 changed files with 353 additions and 507 deletions.
2 changes: 1 addition & 1 deletion aviary/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@
# Phase builders
from aviary.mission.phase_builder_base import PhaseBuilderBase
# note that this is only for simplified right now
from aviary.mission.flops_based.phases.energy_phase import EnergyPhase as HeightEnergyPhaseBuilder
from aviary.mission.energy_phase import EnergyPhase as HeightEnergyPhaseBuilder
from aviary.mission.flops_based.phases.build_landing import Landing as HeightEnergyLandingPhaseBuilder
# note that this is only for simplified right now
from aviary.mission.flops_based.phases.build_takeoff import Takeoff as HeightEnergyTakeoffPhaseBuilder
Expand Down
24 changes: 15 additions & 9 deletions aviary/examples/run_new_detailed_landing.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,12 @@
'duration_ref': (1.e3, 'ft'),
'duration_bounds': ((500., 5.e3), 'ft'),
'mach_bounds': ((0.1, 0.5), 'unitless'),
'initial_mach': (0.15, 'unitless'),
'final_mach': (0.15, 'unitless'),
'initial_altitude': (500., 'ft'),
'final_altitude': (394., 'ft'),
'altitude_bounds': ((0., 1000.), 'ft'),
'control_order': 1,
'polynomial_control_order': 1,
'throttle_enforcement': 'bounded',
'optimize_mach': optimize_mach,
'optimize_altitude': optimize_altitude,
Expand All @@ -59,10 +63,8 @@
'subsystem_options': subsystem_options,
'initial_guesses': {
'distance': [(0.e3, 2.e3), 'ft'],
'mach': [(0.15, 0.15), 'unitless'],
'time': [(0., 12.), 's'],
'mass': [(120.e3, 119.8e3), 'lbm'],
'altitude': [(500., 394.), 'ft'],
},
},
'HI': {
Expand All @@ -78,7 +80,11 @@
'duration_bounds': ((500., 15.e3), 'ft'),
'mach_bounds': ((0.1, 0.5), 'unitless'),
'altitude_bounds': ((0., 1000.), 'ft'),
'control_order': 1,
'initial_mach': (0.15, 'unitless'),
'final_mach': (0.15, 'unitless'),
'initial_altitude': (394., 'ft'),
'final_altitude': (50., 'ft'),
'polynomial_control_order': 1,
'throttle_enforcement': 'bounded',
'optimize_mach': optimize_mach,
'optimize_altitude': optimize_altitude,
Expand All @@ -95,10 +101,8 @@
'subsystem_options': subsystem_options,
'initial_guesses': {
'distance': [(2.e3, 6.5e3), 'ft'],
'mach': [(0.15, 0.15), 'unitless'],
'time': [(12., 50.), 's'],
'mass': [(119.8e3, 119.7e3), 'lbm'],
'altitude': [(394., 50.), 'ft'],
},
},
'IJ': {
Expand All @@ -114,7 +118,11 @@
'duration_bounds': ((500., 15.e3), 'ft'),
'mach_bounds': ((0.1, 0.5), 'unitless'),
'altitude_bounds': ((0., 1000.), 'ft'),
'control_order': 2,
'initial_mach': (0.15, 'unitless'),
'final_mach': (0.15, 'unitless'),
'initial_altitude': (50., 'ft'),
'final_altitude': (0., 'ft'),
'polynomial_control_order': 2,
'throttle_enforcement': 'path_constraint',
'optimize_mach': False,
'optimize_altitude': True,
Expand All @@ -125,10 +133,8 @@
'subsystem_options': subsystem_options_landing,
'initial_guesses': {
'distance': [(8.5e3, 2.e3), 'ft'],
'mach': [(0.15, 0.15), 'unitless'],
'time': [(50., 60.), 's'],
'mass': [(119.7e3, 119.67e3), 'lbm'],
'altitude': [(50., 0.), 'ft'],
},
},
"post_mission": {
Expand Down
47 changes: 30 additions & 17 deletions aviary/examples/run_new_detailed_takeoff.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,15 @@
'duration_ref': (1.e3, 'ft'),
'duration_bounds': ((200., 2.e3), 'ft'),
'mach_bounds': ((0.18, 0.2), 'unitless'),
'control_order': 1,
'polynomial_control_order': 1,
'throttle_enforcement': 'bounded',
'optimize_mach': optimize_mach,
'optimize_altitude': optimize_altitude,
'rotation': True,
'initial_mach': (0.18, 'unitless'),
'final_mach': (0.2, 'unitless'),
'initial_altitude': (0., 'ft'),
'final_altitude': (0., 'ft'),
'constraints': {
'normal_force': {
'equals': 0.,
Expand All @@ -79,7 +83,6 @@
'subsystem_options': subsystem_options,
'initial_guesses': {
'distance': [(2.e3, 1.e3), 'ft'],
'mach': [(0.18, 0.2), 'unitless'],
'time': [(20., 25.), 's'],
'mass': [(174.85e3, 174.84e3), 'lbm'],
'alpha': [(0., 12.), 'deg'],
Expand All @@ -98,7 +101,11 @@
'duration_bounds': ((500., 1500.), 'ft'),
'mach_bounds': ((0.2, 0.22), 'unitless'),
'altitude_bounds': ((0., 250.), 'ft'),
'control_order': 1,
'initial_mach': (0.2, 'unitless'),
'final_mach': (0.22, 'unitless'),
'initial_altitude': (0., 'ft'),
'final_altitude': (50., 'ft'),
'polynomial_control_order': 1,
'throttle_enforcement': 'bounded',
'optimize_mach': optimize_mach,
'optimize_altitude': optimize_altitude,
Expand All @@ -107,10 +114,8 @@
'subsystem_options': subsystem_options,
'initial_guesses': {
'distance': [(3.e3, 1.e3), 'ft'],
'mach': [(0.2, 0.22), 'unitless'],
'time': [(25., 35.), 's'],
'mass': [(174.84e3, 174.82e3), 'lbm'],
'altitude': [(0., 50.), 'ft'],
},
},
'CD_to_P2': {
Expand All @@ -126,7 +131,11 @@
'duration_bounds': ((3.e3, 20.e3), 'ft'),
'mach_bounds': ((0.22, 0.3), 'unitless'),
'altitude_bounds': ((0., 985.), 'ft'),
'control_order': 1,
'initial_mach': (0.22, 'unitless'),
'final_mach': (0.3, 'unitless'),
'initial_altitude': (50., 'ft'),
'final_altitude': (985., 'ft'),
'polynomial_control_order': 1,
'throttle_enforcement': 'bounded',
'optimize_mach': optimize_mach,
'optimize_altitude': optimize_altitude,
Expand All @@ -135,8 +144,6 @@
'initial_guesses': {
'distance': [(4.e3, 14.e3), 'ft'],
'time': [(35., 80.), 's'],
'mach': [(0.22, 0.3), 'unitless'],
'altitude': [(50., 985.), 'ft'],
'mass': [(174.82e3, 174.5e3), 'lbm'],
},
},
Expand All @@ -153,7 +160,11 @@
'duration_bounds': ((50., 5000.), 'ft'),
'mach_bounds': ((0.24, 0.32), 'unitless'),
'altitude_bounds': ((985., 1.5e3), 'ft'),
'control_order': 1,
'initial_mach': (0.3, 'unitless'),
'final_mach': (0.3, 'unitless'),
'initial_altitude': (985., 'ft'),
'final_altitude': (1100., 'ft'),
'polynomial_control_order': 1,
'optimize_mach': optimize_mach,
'optimize_altitude': optimize_altitude,
'throttle_enforcement': 'bounded',
Expand All @@ -169,10 +180,8 @@
'subsystem_options': subsystem_options,
'initial_guesses': {
'distance': [(18.e3, 2.e3), 'ft'],
'mach': [(0.3, 0.3), 'unitless'],
'mass': [(174.5e3, 174.4e3), 'lbm'],
'time': [(80., 85.), 's'],
'altitude': [(985., 1100.), 'ft'],
},
},
'EF_to_P1': {
Expand All @@ -188,7 +197,11 @@
'duration_bounds': ((1.e3, 20.e3), 'ft'),
'mach_bounds': ((0.24, 0.32), 'unitless'),
'altitude_bounds': ((1.1e3, 1.2e3), 'ft'),
'control_order': 1,
'initial_mach': (0.3, 'unitless'),
'final_mach': (0.3, 'unitless'),
'initial_altitude': (1100., 'ft'),
'final_altitude': (1200., 'ft'),
'polynomial_control_order': 1,
'optimize_mach': optimize_mach,
'optimize_altitude': optimize_altitude,
'throttle_enforcement': 'bounded',
Expand All @@ -205,10 +218,8 @@
'subsystem_options': subsystem_options,
'initial_guesses': {
'distance': [(20.e3, 1325.), 'ft'],
'mach': [(0.3, 0.3), 'unitless'],
'mass': [(174.4e3, 174.3e3), 'lbm'],
'time': [(85., 90.), 's'],
'altitude': [(1100., 1200.), 'ft'],
},
},
'EF_past_P1': {
Expand All @@ -224,7 +235,11 @@
'duration_bounds': ((100., 50.e3), 'ft'),
'mach_bounds': ((0.24, 0.32), 'unitless'),
'altitude_bounds': ((1.e3, 3.e3), 'ft'),
'control_order': 1,
'initial_mach': (0.3, 'unitless'),
'final_mach': (0.3, 'unitless'),
'initial_altitude': (1200., 'ft'),
'final_altitude': (2000., 'ft'),
'polynomial_control_order': 1,
'optimize_mach': optimize_mach,
'optimize_altitude': optimize_altitude,
'throttle_enforcement': 'bounded',
Expand All @@ -241,9 +256,7 @@
'subsystem_options': subsystem_options,
'initial_guesses': {
'distance': [(21325., 40.e3), 'ft'],
'mach': [(0.3, 0.3), 'unitless'],
'mass': [(174.3e3, 174.2e3), 'lbm'],
'altitude': [(1200, 2000.), 'ft'],
'time': [(90., 180.), 's'],
},
},
Expand Down
73 changes: 42 additions & 31 deletions aviary/interface/methods_for_level2.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@
from aviary.constants import GRAV_ENGLISH_LBM, RHO_SEA_LEVEL_ENGLISH
from aviary.mission.flops_based.phases.build_landing import Landing
from aviary.mission.flops_based.phases.build_takeoff import Takeoff
from aviary.mission.flops_based.phases.energy_phase import EnergyPhase
from aviary.mission.flops_based.phases.two_dof_phase import TwoDOFPhase
from aviary.mission.energy_phase import EnergyPhase
from aviary.mission.twodof_phase import TwoDOFPhase
from aviary.mission.gasp_based.ode.params import ParamPort
from aviary.mission.gasp_based.phases.time_integration_traj import FlexibleTraj
from aviary.mission.gasp_based.phases.time_integration_phases import SGMCruise
Expand Down Expand Up @@ -1930,37 +1930,39 @@ def set_initial_guesses(self):
for idx, (phase_name, phase) in enumerate(phase_items):
if self.mission_method is SOLVED_2DOF:
self.phase_objects[idx].apply_initial_guesses(self, 'traj', phase)
if self.phase_info[phase_name]['user_options']['ground_roll'] and self.phase_info[phase_name]['user_options']['fix_initial']:
continue

# If not, fetch the initial guesses specific to the phase
# check if guesses exist for this phase
if "initial_guesses" in self.phase_info[phase_name]:
guesses = self.phase_info[phase_name]['initial_guesses']
else:
# If not, fetch the initial guesses specific to the phase
# check if guesses exist for this phase
if "initial_guesses" in self.phase_info[phase_name]:
guesses = self.phase_info[phase_name]['initial_guesses']
else:
guesses = {}

if 'cruise' in phase_name and self.mission_method is TWO_DEGREES_OF_FREEDOM:
for guess_key, guess_data in guesses.items():
val, units = guess_data

if 'mass' == guess_key:
# Set initial and duration mass for the analytic cruise phase.
# Note we are integrating over mass, not time for this phase.
self.set_val(f'traj.{phase_name}.t_initial',
val[0], units=units)
self.set_val(f'traj.{phase_name}.t_duration',
val[1], units=units)
else:
# Otherwise, set the value of the parameter in the trajectory phase
self.set_val(f'traj.{phase_name}.parameters:{guess_key}',
val, units=units)
guesses = {}

if 'cruise' in phase_name and self.mission_method is TWO_DEGREES_OF_FREEDOM:
for guess_key, guess_data in guesses.items():
val, units = guess_data

if 'mass' == guess_key:
# Set initial and duration mass for the analytic cruise phase.
# Note we are integrating over mass, not time for this phase.
self.set_val(f'traj.{phase_name}.t_initial',
val[0], units=units)
self.set_val(f'traj.{phase_name}.t_duration',
val[1], units=units)
else:
# Otherwise, set the value of the parameter in the trajectory phase
self.set_val(f'traj.{phase_name}.parameters:{guess_key}',
val, units=units)

continue
continue

# If not cruise and GASP, add subsystem guesses
self._add_subsystem_guesses(phase_name, phase)
# If not cruise and GASP, add subsystem guesses
self._add_subsystem_guesses(phase_name, phase)

# Set initial guesses for states and controls for each phase
self._add_guesses(phase_name, phase, guesses)
# Set initial guesses for states and controls for each phase
self._add_guesses(phase_name, phase, guesses)

def _process_guess_var(self, val, key, phase):
"""
Expand Down Expand Up @@ -2082,7 +2084,7 @@ def _add_guesses(self, phase_name, phase, guesses):
rotation_mass = self.initial_guesses['rotation_mass']
flight_duration = self.initial_guesses['flight_duration']

if self.mission_method is HEIGHT_ENERGY:
if self.mission_method in (HEIGHT_ENERGY, SOLVED_2DOF):
control_keys = ["mach", "altitude"]
state_keys = ["mass", Dynamic.Mission.DISTANCE]
else:
Expand All @@ -2096,7 +2098,7 @@ def _add_guesses(self, phase_name, phase, guesses):
prob_keys = ["tau_gear", "tau_flaps"]

# for the simple mission method, use the provided initial and final mach and altitude values from phase_info
if self.mission_method is HEIGHT_ENERGY:
if self.mission_method in (HEIGHT_ENERGY, SOLVED_2DOF):
initial_altitude = wrapped_convert_units(
self.phase_info[phase_name]['user_options']['initial_altitude'], 'ft')
final_altitude = wrapped_convert_units(
Expand All @@ -2107,6 +2109,7 @@ def _add_guesses(self, phase_name, phase, guesses):
guesses["mach"] = ([initial_mach[0], final_mach[0]], "unitless")
guesses["altitude"] = ([initial_altitude, final_altitude], 'ft')

if self.mission_method is HEIGHT_ENERGY:
# if times not in initial guesses, set it to the average of the initial_bounds and the duration_bounds
if 'times' not in guesses:
initial_bounds = wrapped_convert_units(
Expand Down Expand Up @@ -2151,6 +2154,11 @@ def _add_guesses(self, phase_name, phase, guesses):
self.set_val(f'traj.{phase_name}.bspline_controls:{guess_key}', self._process_guess_var(
val, guess_key, phase), units=units)

if self.mission_method is SOLVED_2DOF:
continue

if guess_key in control_keys:
pass
# Set initial guess for state variables
elif guess_key in state_keys:
self.set_val(f'traj.{phase_name}.states:{guess_key}', self._process_guess_var(
Expand All @@ -2165,6 +2173,9 @@ def _add_guesses(self, phase_name, phase, guesses):
raise ValueError(
f"Initial guess key {guess_key} in {phase_name} is not recognized.")

if self.mission_method is SOLVED_2DOF:
return

# We need some special logic for these following variables because GASP computes
# initial guesses using some knowledge of the mission duration and other variables
# that are only available after calling `create_vehicle`. Thus these initial guess
Expand Down
2 changes: 1 addition & 1 deletion aviary/interface/test/test_height_energy_mission.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

from aviary.interface.methods_for_level1 import run_aviary
from aviary.subsystems.test.test_dummy_subsystem import ArrayGuessSubsystemBuilder
from aviary.mission.flops_based.phases.energy_phase import EnergyPhase
from aviary.mission.energy_phase import EnergyPhase
from aviary.variable_info.variables import Dynamic
from aviary.variable_info.enums import Verbosity

Expand Down
27 changes: 27 additions & 0 deletions aviary/mission/energy_phase.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import dymos as dm

from aviary.mission.flight_phase_builder import FlightPhaseBase, register
from aviary.mission.initial_guess_builders import InitialGuessIntegrationVariable, InitialGuessState

from aviary.utils.aviary_values import AviaryValues
from aviary.mission.flops_based.ode.mission_ODE import MissionODE


# TODO: support/handle the following in the base class
# - phase.set_time_options()
# - currently handled in level 3 interface implementation
# - self.external_subsystems
# - self.meta_data, with cls.default_meta_data customization point
@register
class EnergyPhase(FlightPhaseBase):

default_ode_class = MissionODE


EnergyPhase._add_initial_guess_meta_data(
InitialGuessIntegrationVariable(),
desc='initial guess for initial time and duration specified as a tuple')

EnergyPhase._add_initial_guess_meta_data(
InitialGuessState('distance'),
desc='initial guess for horizontal distance traveled')
Loading

0 comments on commit 545f58f

Please sign in to comment.