Skip to content

Commit

Permalink
Merge pull request OpenMDAO#651 from Kenneth-T-Moore/phase_info_revamp
Browse files Browse the repository at this point in the history
Methods for level 2 revamp part 1.
  • Loading branch information
Kenneth-T-Moore authored Feb 26, 2025
2 parents 8f4f570 + 83621af commit 45c73c2
Show file tree
Hide file tree
Showing 12 changed files with 2,544 additions and 1,432 deletions.
124 changes: 124 additions & 0 deletions aviary/core/AviaryGroup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import openmdao.api as om
from openmdao.utils.mpi import MPI

from aviary.utils.aviary_values import AviaryValues
from aviary.variable_info.enums import EquationsOfMotion
from aviary.variable_info.variables import Aircraft, Mission, Dynamic, Settings

HEIGHT_ENERGY = EquationsOfMotion.HEIGHT_ENERGY


class AviaryGroup(om.Group):
"""
A standard OpenMDAO group that handles Aviary's promotions in the configure
method. This assures that we only call set_input_defaults on variables
that are present in the model.
"""

def initialize(self):
"""declare options"""
self.options.declare(
'aviary_options', types=AviaryValues,
desc='collection of Aircraft/Mission specific options')
self.options.declare(
'aviary_metadata', types=dict,
desc='metadata dictionary of the full aviary problem.')
self.options.declare(
'phase_info', types=dict,
desc='phase-specific settings.')
self.builder = []

def configure(self):
"""
Configure the Aviary group
"""
aviary_options = self.options['aviary_options']
aviary_metadata = self.options['aviary_metadata']

# Find promoted name of every input in the model.
all_prom_inputs = []

# We can call list_inputs on the subsystems.
for system in self.system_iter(recurse=False):
var_abs = system.list_inputs(out_stream=None, val=False)
var_prom = [v['prom_name'] for k, v in var_abs]
all_prom_inputs.extend(var_prom)

# Calls to promotes aren't handled until this group resolves.
# Here, we address anything promoted with an alias in AviaryProblem.
input_meta = system._var_promotes['input']
var_prom = [v[0][1] for v in input_meta if isinstance(v[0], tuple)]
all_prom_inputs.extend(var_prom)
var_prom = [v[0] for v in input_meta if not isinstance(v[0], tuple)]
all_prom_inputs.extend(var_prom)

if MPI and self.comm.size > 1:
# Under MPI, promotion info only lives on rank 0, so broadcast.
all_prom_inputs = self.comm.bcast(all_prom_inputs, root=0)

for key in aviary_metadata:

if ':' not in key or key.startswith('dynamic:'):
continue

if aviary_metadata[key]['option']:
continue

# Skip anything that is not presently an input.
if key not in all_prom_inputs:
continue

if key in aviary_options:
val, units = aviary_options.get_item(key)
else:
val = aviary_metadata[key]['default_value']
units = aviary_metadata[key]['units']

if val is None:
# optional, but no default value
continue

self.set_input_defaults(key, val=val, units=units)

# try to get all the possible EOMs from the Enums rather than specifically calling the names here
# This will require some modifications to the enums
mission_method = aviary_options.get_val(
Settings.EQUATIONS_OF_MOTION)

# Temporarily add extra stuff here, probably patched soon
if mission_method is HEIGHT_ENERGY:
phase_info = self.options['phase_info']

# Set a more appropriate solver for dymos when the phases are linked.
if MPI and isinstance(self.traj.phases.linear_solver, om.PETScKrylov):

# When any phase is connected with input_initial = True, dymos puts
# a jacobi solver in the phases group. This is necessary in case
# the phases are cyclic. However, this causes some problems
# with the newton solvers in Aviary, exacerbating issues with
# solver tolerances at multiple levels. Since Aviary's phases
# are basically in series, the jacobi solver is a much better
# choice and should be able to handle it in a couple of
# iterations.
self.traj.phases.linear_solver = om.LinearBlockJac(maxiter=5)

# Due to recent changes in dymos, there is now a solver in any phase
# that has connected initial states. It is not clear that this solver
# is necessary except in certain corner cases that do not apply to the
# Aviary trajectory. In our case, this solver merely addresses a lag
# in the state input component. Since this solver can cause some
# numerical problems, and can slow things down, we need to move it down
# into the state interp component.
# TODO: Future updates to dymos may make this unneccesary.
for phase in self.traj.phases.system_iter(recurse=False):

# Don't move the solvers if we are using solve segements.
if phase_info[phase.name]['user_options'].get('solve_for_distance'):
continue

phase.nonlinear_solver = om.NonlinearRunOnce()
phase.linear_solver = om.LinearRunOnce()
if isinstance(phase.indep_states, om.ImplicitComponent):
phase.indep_states.nonlinear_solver = \
om.NewtonSolver(solve_subsystems=True)
phase.indep_states.linear_solver = om.DirectSolver(rhs_checking=True)
14 changes: 14 additions & 0 deletions aviary/core/PostMissionGroup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import openmdao.api as om
from aviary.utils.aviary_values import AviaryValues
from aviary.utils.functions import promote_aircraft_and_mission_vars


class PostMissionGroup(om.Group):
"""OpenMDAO group that holds all post-mission systems"""

def configure(self):
"""
Congigure this group for post-mission.
Promote aircraft and mission variables.
"""
promote_aircraft_and_mission_vars(self)
24 changes: 24 additions & 0 deletions aviary/core/PreMissionGroup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import openmdao.api as om
from aviary.utils.aviary_values import AviaryValues
from aviary.utils.functions import promote_aircraft_and_mission_vars
from aviary.variable_info.functions import override_aviary_vars


class PreMissionGroup(om.Group):
"""OpenMDAO group that holds all pre-mission systems"""

def configure(self):
"""
Configure this group for pre-mission.
Promote aircraft and mission variables.
Override output aviary variables.
"""
external_outputs = promote_aircraft_and_mission_vars(self)

pre_mission = self.core_subsystems
override_aviary_vars(
pre_mission,
pre_mission.options["aviary_options"],
external_overrides=external_outputs,
manual_overrides=pre_mission.manual_overrides,
)
Empty file added aviary/core/__init__.py
Empty file.
1 change: 1 addition & 0 deletions aviary/docs/examples/modified_aircraft.csv
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ aircraft:engine:num_fuselage_engines,0,unitless
aircraft:engine:num_wing_engines,2,unitless
aircraft:engine:reference_mass,7400,lbm
aircraft:engine:reference_sls_thrust,28928.1,lbf
aircraft:engine:scale_factor,1.0,unitless
aircraft:engine:scale_mass,True,unitless
aircraft:engine:scale_performance,True,unitless
aircraft:engine:scaled_sls_thrust,28928.1,lbf
Expand Down
10 changes: 7 additions & 3 deletions aviary/docs/getting_started/onboarding_level2.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
"import openmdao.api as om\n",
"from aviary.api import Mission\n",
"from aviary.variable_info.enums import ProblemType as PT, EquationsOfMotion as EOM\n",
"from aviary.interface.methods_for_level2 import AviaryProblem\n",
"from aviary.interface.methods_for_level2 import AviaryProblem, TwoDOFProblemConfigurator\n",
"from aviary.utils.doctape import check_contains\n",
"\n",
"EOM.HEIGHT_ENERGY;\n",
Expand All @@ -56,6 +56,8 @@
" dummy_prob = om.Problem()\n",
" dummy_prob.mission_method = EOM.TWO_DEGREES_OF_FREEDOM\n",
" dummy_prob.problem_type = ptype\n",
" dummy_prob.builder = TwoDOFProblemConfigurator()\n",
" dummy_prob.target_range = 0\n",
" AviaryProblem.add_objective(dummy_prob)\n",
" dummy_prob.setup()\n",
" objectives = dummy_prob.model._responses.keys()\n",
Expand Down Expand Up @@ -654,7 +656,7 @@
"import openmdao.api as om\n",
"from aviary.api import Mission, Dynamic\n",
"from aviary.variable_info.enums import EquationsOfMotion as EOM, AnalysisScheme as AS\n",
"from aviary.interface.methods_for_level2 import AviaryProblem\n",
"from aviary.interface.methods_for_level2 import AviaryProblem, TwoDOFProblemConfigurator\n",
"from aviary.utils.aviary_values import AviaryValues\n",
"from aviary.utils.doctape import check_contains\n",
"\n",
Expand Down Expand Up @@ -683,6 +685,8 @@
"\n",
"for otype, obj in expected_objective.items():\n",
" dummy_prob = dprob()\n",
" dummy_prob.builder = TwoDOFProblemConfigurator()\n",
" dummy_prob.target_range = 0\n",
" AviaryProblem.add_objective(dummy_prob, otype)\n",
" dummy_prob.setup()\n",
" # traj timeseries values are promoted to the top in the real problem\n",
Expand Down Expand Up @@ -1006,7 +1010,7 @@
],
"metadata": {
"kernelspec": {
"display_name": "latest_env",
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
Expand Down
Loading

0 comments on commit 45c73c2

Please sign in to comment.