Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
e1e2a90
Added a new enum
Kenneth-T-Moore Jan 28, 2026
6e8f9a5
moving towards getting Breguet phase to work with external dynamic su…
Kenneth-T-Moore Jan 30, 2026
95aa4bd
Merge branch 'main' of github.com:OpenMDAO/Aviary into 2dof_3
Kenneth-T-Moore Feb 3, 2026
220d680
Beginning support for a new simple cruise
Kenneth-T-Moore Feb 4, 2026
24286c1
Merge branch 'main' of github.com:OpenMDAO/Aviary into 2dof_3
Kenneth-T-Moore Feb 4, 2026
3a45cbe
Need to commit to merge
Kenneth-T-Moore Feb 4, 2026
f3cd271
Merge branch 'main' of github.com:OpenMDAO/Aviary into 2dof_3
Kenneth-T-Moore Feb 4, 2026
33eb05c
initial
Kenneth-T-Moore Feb 5, 2026
01267a8
First test passes
Kenneth-T-Moore Feb 6, 2026
65f0c36
Merge branch 'main' of github.com:OpenMDAO/Aviary into 2dof_3
Kenneth-T-Moore Feb 6, 2026
cee1bec
Added unit tests
Kenneth-T-Moore Feb 6, 2026
4ec81f7
Down to a couple failures
Kenneth-T-Moore Feb 6, 2026
eaaa956
experimental revamp on scaling
Kenneth-T-Moore Feb 6, 2026
c6cffe2
playing with refs
Kenneth-T-Moore Feb 6, 2026
47d24e7
My old nemesis: upper bound on cruise phase.
Kenneth-T-Moore Feb 6, 2026
3467ed5
One more test passes
Kenneth-T-Moore Feb 9, 2026
e9a198b
Doc fixes
Kenneth-T-Moore Feb 10, 2026
6e96100
Docs are working
Kenneth-T-Moore Feb 11, 2026
6a99f32
Cleanup and added a bench for Breguet Range
Kenneth-T-Moore Feb 11, 2026
d493452
RUFF
Kenneth-T-Moore Feb 11, 2026
95d03d6
RUFF
Kenneth-T-Moore Feb 11, 2026
53b6629
RUFF
Kenneth-T-Moore Feb 11, 2026
331d529
RUFF
Kenneth-T-Moore Feb 11, 2026
aa0b313
SLSQP works with Height Energy, but doesn't seem to be able to handle…
Kenneth-T-Moore Feb 11, 2026
fe0cec6
SLSQP works with Height Energy, but doesn't seem to be able to handle…
Kenneth-T-Moore Feb 11, 2026
f93c8d2
SLSQP works with Height Energy, but doesn't seem to be able to handle…
Kenneth-T-Moore Feb 11, 2026
ceacb6d
Forgot to add the ode test file to the repo
Kenneth-T-Moore Feb 13, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 16 additions & 22 deletions aviary/core/aviary_group.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,13 @@
update_GASP_options,
)
from aviary.utils.utils import wrapped_convert_units
from aviary.variable_info.enums import EquationsOfMotion, LegacyCode, ProblemType, Verbosity
from aviary.variable_info.enums import (
EquationsOfMotion,
LegacyCode,
PhaseType,
ProblemType,
Verbosity,
)
from aviary.variable_info.functions import setup_trajectory_params
from aviary.variable_info.variables import Aircraft, Mission, Settings

Expand Down Expand Up @@ -345,19 +351,6 @@ def check_and_preprocess_inputs(self, verbosity=None):
# Sets duration_bounds, initial_guesses, and fixed_duration
for phase_name, phase in self.mission_info.items():
if 'user_options' in phase:
analytic = False
if self.mission_method is EquationsOfMotion.TWO_DEGREES_OF_FREEDOM:
try:
# if the user provided an option, use it
analytic = phase['user_options']['analytic']
except KeyError:
# if it isn't specified, only the default 2DOF cruise for
# collocation is analytic
if 'cruise' in phase_name:
analytic = phase['user_options']['analytic'] = True
else:
analytic = phase['user_options']['analytic'] = False

if 'time_duration' in phase['user_options']:
time_duration, units = phase['user_options']['time_duration']

Expand Down Expand Up @@ -952,9 +945,9 @@ def add_post_mission_systems(self, verbosity=None):
# this is only used for analytic phases with a target duration
time_duration = user_options.get('time_duration', (None, 'min'))
time_duration = wrapped_convert_units(time_duration, 'min')
analytic = user_options.get('analytic', False)
integrates_mass = user_options['phase_builder'] is PhaseType.BREGUET_RANGE

if analytic and time_duration is not None:
if integrates_mass and time_duration is not None:
post_mission.add_subsystem(
f'{phase_name}_duration_constraint',
om.ExecComp(
Expand Down Expand Up @@ -1129,15 +1122,16 @@ def link_phases(self, verbosity=None, comm=None):
# and configurators
for ii in range(len(phases) - 1):
phase1, phase2 = phases[ii : ii + 2]
analytic1 = self.mission_info[phase1]['user_options'].get('analytic', False)
analytic2 = self.mission_info[phase2]['user_options'].get('analytic', False)

if not (analytic1 or analytic2):
self.traj.link_phases(phases=[phase1, phase2], vars=[var], connected=True)
opt1 = self.mission_info[phase1]['user_options']
opt2 = self.mission_info[phase2]['user_options']
integrates_mass1 = opt1['phase_builder'] is PhaseType.BREGUET_RANGE
integrates_mass2 = opt2['phase_builder'] is PhaseType.BREGUET_RANGE

else:
if integrates_mass1 or integrates_mass2:
# TODO need ref value for these linkage constraints
self.traj.add_linkage_constraint(phase1, phase2, var, var, connected=False)
else:
self.traj.link_phases(phases=[phase1, phase2], vars=[var], connected=True)

self.configurator.link_phases(self, phases, connect_directly=true_unless_mpi)

Expand Down
4 changes: 2 additions & 2 deletions aviary/docs/getting_started/input_csv_phase_info.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -140,10 +140,10 @@
"from aviary.mission.height_energy.phases.groundroll_phase import GroundrollPhaseOptions as FGopt\n",
"from aviary.mission.two_dof.phases.accel_phase import AccelPhaseOptions\n",
"from aviary.mission.two_dof.phases.ascent_phase import AscentPhaseOptions\n",
"from aviary.mission.two_dof.phases.cruise_phase import CruisePhaseOptions\n",
"from aviary.mission.two_dof.phases.flight_phase import FlightPhaseOptions\n",
"from aviary.mission.two_dof.phases.groundroll_phase import GroundrollPhaseOptions as GGopt\n",
"from aviary.mission.two_dof.phases.rotation_phase import RotationPhaseOptions\n",
"from aviary.mission.two_dof.phases.simple_cruise_phase import SimpleCruisePhaseOptions\n",
"from aviary.mission.solved_two_dof.phases.solved_twodof_phase import SolvedTwoDOFPhaseOptions\n",
"from aviary.utils.doctape import glue_keys\n",
"\n",
Expand All @@ -167,12 +167,12 @@
"\n",
"dummy_phase_info.update(AccelPhaseOptions().items())\n",
"dummy_phase_info.update(AscentPhaseOptions().items())\n",
"dummy_phase_info.update(CruisePhaseOptions().items())\n",
"dummy_phase_info.update(FlightPhaseOptions().items())\n",
"dummy_phase_info.update(FGopt().items())\n",
"dummy_phase_info.update(FlightPhaseOptions().items())\n",
"dummy_phase_info.update(GGopt().items())\n",
"dummy_phase_info.update(RotationPhaseOptions().items())\n",
"dummy_phase_info.update(SimpleCruisePhaseOptions().items())\n",
"dummy_phase_info.update(SolvedTwoDOFPhaseOptions().items())\n",
"glue_keys(dummy_phase_info)"
]
Expand Down
5 changes: 1 addition & 4 deletions aviary/docs/getting_started/onboarding_level2.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -856,10 +856,7 @@
"print('Accel Final Mass (lbm)', prob.get_val('traj.accel.states:mass', units='lbm')[-1])\n",
"print('Climb1 Final Mass (lbm)', prob.get_val('traj.climb1.states:mass', units='lbm')[-1])\n",
"print('Climb2 Final Mass (lbm)', prob.get_val('traj.climb2.states:mass', units='lbm')[-1])\n",
"print(\n",
" 'Cruise Final Mass (lbm)',\n",
" prob.get_val('traj.phases.cruise.rhs.calc_weight.mass', units='lbm')[-1],\n",
")\n",
"print('Cruise Final Mass (lbm)', prob.get_val('traj.cruise.states:mass', units='lbm')[-1])\n",
"print('Desc1 Final Mass (lbm)', prob.get_val('traj.desc1.states:mass', units='lbm')[-1])\n",
"print('Desc2 Final Mass (lbm)', prob.get_val('traj.desc2.states:mass', units='lbm')[-1])\n",
"print('done')"
Expand Down
2 changes: 2 additions & 0 deletions aviary/docs/getting_started/onboarding_level3.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,8 @@
"\n",
"av.setup_model_options(prob, aviary_inputs)\n",
"\n",
"prob.model.set_input_defaults(av.Aircraft.Wing.AREA, val=1.0, units='ft**2')\n",
"\n",
"prob.setup()\n",
"\n",
"phase = prob.model.traj.phases.cruise\n",
Expand Down
4 changes: 2 additions & 2 deletions aviary/docs/user_guide/phase_info.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,22 @@
"from aviary.mission.height_energy.phases.groundroll_phase import GroundrollPhaseOptions as FGopt\n",
"from aviary.mission.two_dof.phases.accel_phase import AccelPhaseOptions\n",
"from aviary.mission.two_dof.phases.ascent_phase import AscentPhaseOptions\n",
"from aviary.mission.two_dof.phases.cruise_phase import CruisePhaseOptions\n",
"from aviary.mission.two_dof.phases.flight_phase import FlightPhaseOptions\n",
"from aviary.mission.two_dof.phases.groundroll_phase import GroundrollPhaseOptions as GGopt\n",
"from aviary.mission.two_dof.phases.rotation_phase import RotationPhaseOptions\n",
"from aviary.mission.two_dof.phases.simple_cruise_phase import SimpleCruisePhaseOptions\n",
"from aviary.mission.solved_two_dof.phases.solved_twodof_phase import SolvedTwoDOFPhaseOptions\n",
"from aviary.utils.doctape import glue_keys\n",
"\n",
"dummy_phase_info = {}\n",
"dummy_phase_info.update(AccelPhaseOptions().items())\n",
"dummy_phase_info.update(AscentPhaseOptions().items())\n",
"dummy_phase_info.update(CruisePhaseOptions().items())\n",
"dummy_phase_info.update(FlightPhaseOptions().items())\n",
"dummy_phase_info.update(FGopt().items())\n",
"dummy_phase_info.update(FlightPhaseOptions().items())\n",
"dummy_phase_info.update(GGopt().items())\n",
"dummy_phase_info.update(RotationPhaseOptions().items())\n",
"dummy_phase_info.update(SimpleCruisePhaseOptions().items())\n",
"dummy_phase_info.update(SolvedTwoDOFPhaseOptions().items())\n",
"glue_keys(dummy_phase_info)"
]
Expand Down
21 changes: 12 additions & 9 deletions aviary/docs/user_guide/phase_info_conversion.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,22 @@
"from aviary.mission.height_energy.phases.groundroll_phase import GroundrollPhaseOptions as FGopt\n",
"from aviary.mission.two_dof.phases.accel_phase import AccelPhaseOptions\n",
"from aviary.mission.two_dof.phases.ascent_phase import AscentPhaseOptions\n",
"from aviary.mission.two_dof.phases.cruise_phase import CruisePhaseOptions\n",
"from aviary.mission.two_dof.phases.flight_phase import FlightPhaseOptions\n",
"from aviary.mission.two_dof.phases.groundroll_phase import GroundrollPhaseOptions as GGopt\n",
"from aviary.mission.two_dof.phases.rotation_phase import RotationPhaseOptions\n",
"from aviary.mission.two_dof.phases.simple_cruise_phase import SimpleCruisePhaseOptions\n",
"from aviary.mission.solved_two_dof.phases.solved_twodof_phase import SolvedTwoDOFPhaseOptions\n",
"from aviary.utils.doctape import glue_keys\n",
"\n",
"dummy_phase_info = {}\n",
"dummy_phase_info.update(AccelPhaseOptions().items())\n",
"dummy_phase_info.update(AscentPhaseOptions().items())\n",
"dummy_phase_info.update(CruisePhaseOptions().items())\n",
"dummy_phase_info.update(FlightPhaseOptions().items())\n",
"dummy_phase_info.update(FGopt().items())\n",
"dummy_phase_info.update(FlightPhaseOptions().items())\n",
"dummy_phase_info.update(GGopt().items())\n",
"dummy_phase_info.update(RotationPhaseOptions().items())\n",
"dummy_phase_info.update(SimpleCruisePhaseOptions().items())\n",
"dummy_phase_info.update(SolvedTwoDOFPhaseOptions().items())\n",
"glue_keys(dummy_phase_info)"
]
Expand Down Expand Up @@ -613,6 +613,8 @@
"metadata": {},
"outputs": [],
"source": [
"from aviary.variable_info.enums import PhaseType, SpeedType\n",
"\n",
"new_phase_info = {\n",
" 'groundroll': {\n",
" 'subsystem_options': {'core_aerodynamics': {'method': 'low_speed'}},\n",
Expand Down Expand Up @@ -787,18 +789,19 @@
" },\n",
" },\n",
" 'cruise': {\n",
" 'subsystem_options': {'core_aerodynamics': {'method': 'cruise'}},\n",
" 'subsystem_options': {'aerodynamics': {'method': 'cruise'}},\n",
" 'user_options': {\n",
" 'phase_builder': PhaseType.SIMPLE_CRUISE,\n",
" 'alt_cruise': (37.5e3, 'ft'),\n",
" 'mach_cruise': 0.8,\n",
" 'mass_bounds': ((0, None), 'lbm'),\n",
" 'mass_ref': (150_000, 'lbm'),\n",
" 'time_duration_bounds': ((0.0, 15.0), 'h'),\n",
" 'time_duration_ref': (8, 'h'),\n",
" },\n",
" 'initial_guesses': {\n",
" # [Initial mass, delta mass] for special cruise phase.\n",
" 'mass': ([171481.0, -35000], 'lbm'),\n",
" 'initial_distance': (200.0e3, 'ft'),\n",
" 'initial_time': (1516.0, 's'),\n",
" 'altitude': (37.5e3, 'ft'),\n",
" 'mach': (0.8, 'unitless'),\n",
" 'mass': ([171481.0, 135000], 'lbm'),\n",
" 'time': ([1516.0, 26500.0], 's'),\n",
" },\n",
" },\n",
" 'desc1': {\n",
Expand Down
30 changes: 27 additions & 3 deletions aviary/docs/user_guide/phase_info_detailed.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"### CruisePhase\n"
"### SimpleCruisePhase\n",
"\n",
"This is the best choice for a level cruise when using the two DOF equations. SimpleCruisePhase integrates mass using the specified transcription, but the distance is computed from the fixed velocity. This can be selected by setting the \"phase_type\" option to `PhaseType.SIMPLE_CRUISE`.\n"
]
},
{
Expand All @@ -110,14 +112,36 @@
"metadata": {},
"outputs": [],
"source": [
"om.show_options_table('aviary.mission.two_dof.phases.cruise_phase.CruisePhaseOptions')"
"om.show_options_table('aviary.mission.two_dof.phases.simple_cruise_phase.SimpleCruisePhaseOptions')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### FlightPhase\n"
"### BreguetCruisePhase (Optional)\n",
"\n",
"The `BreguetCruisePhase` computes distance analytically using a form of the Breguet Range equation. This will generally be a little faster than `SimpleCruisePhase`, but it comes with a limitation that no external subsystems can contain a state that requires integration. This can be selected by setting the \"phase_type\" option to `PhaseType.BREGUET_RANGE`.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"om.show_options_table(\n",
" 'aviary.mission.two_dof.phases.breguet_cruise_phase.BreguetCruisePhaseOptions'\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### FlightPhase\n",
"\n",
"The FlightPhase can be used for ascending and descending mission phases."
]
},
{
Expand Down
4 changes: 2 additions & 2 deletions aviary/docs/user_guide/propulsion.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -437,7 +437,7 @@
],
"metadata": {
"kernelspec": {
"display_name": "aviary",
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
Expand All @@ -451,7 +451,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.12.8"
"version": "3.12.3"
}
},
"nbformat": 4,
Expand Down
18 changes: 5 additions & 13 deletions aviary/docs/user_guide/reserve_missions.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,10 @@
" file_path = os.path.join(str(gasp_phase_path), file)\n",
" phase_name = file.split('_phase.py')[0].capitalize()\n",
" module = SourceFileLoader(phase_name, file_path).load_module()\n",
" if phase_name == 'Simple_cruise':\n",
" phase_name = 'SimpleCruise'\n",
" elif phase_name == 'Breguet_cruise':\n",
" phase_name = 'BreguetCruise'\n",
" phases.append(getattr(module, phase_name + 'Phase'))\n",
"\n",
"for phase in phases:\n",
Expand Down Expand Up @@ -204,16 +208,10 @@
"## Theory\n",
"\n",
"When adding a reserve phase, {glue:md}`check_and_preprocess_inputs()` divides all the phases into two dictionaries: `regular_phases` which contain your nominal phases and `reserve_phases` which contains any phases with the `reserve` flag set to `True`.\n",
"Additionally, {glue:md}`check_and_preprocess_inputs()` will add the `\"analytic\"` flag to each phase.\n",
"This is used to indicate if a phase is an analytic phase (i.e. Breguet range) or a ordinary differential equation (ODE).\n",
"\n",
"Only the final mission mass and range from `regular_phases` are automatically connected to the first point of the `reserve_phases`.\n",
"All other state variables (i.e. altitude, mach) are not automatically connected, allowing you to start the reserve mission at whatever altitude you want.\n",
"\n",
"The `\"analytic\"` flag helps to properly connect phases for {glue:md}`2DOF` missions.\n",
"{glue:md}`2DOF` `cruise` missions are analytic because they use a Breguet range calculation instead of integrating an EOM. \n",
"Analytic phases have a slightly different naming convention in order to access state/timeseries variables like distance, mass, and range compared with their non-analytic counterparts.\n",
"\n",
"You cannot create a reserve mission that enforces time or range constraints over multiple phases (i.e specify the total range covered by a climb + cruise + descent).\n",
"This is because each constraint `\"target_distance\"` or `\"target_time\"` is only enforced on a single phase.\n",
"It is essential that you run {glue:md}`prob.check_and_preprocess_inputs()` after {glue:md}`prob.load_inputs()` to make sure that regular and reserve phases are separated via `phase_separator()`."
Expand Down Expand Up @@ -248,13 +246,7 @@
" if '_phase.py' in file and 'twodof' not in file:\n",
" phase_name = file.split('_phase.py')[0].capitalize()\n",
" file_path = os.path.join(str(gasp_phase_path), file)\n",
" module = SourceFileLoader(phase_name, file_path).load_module()\n",
" check_contains(\n",
" 'analytic',\n",
" getattr(module, phase_name + 'PhaseOptions')(),\n",
" error_string=f'analytic is not a valid key for {phase_name}',\n",
" error_type=NameError,\n",
" )"
" module = SourceFileLoader(phase_name, file_path).load_module()"
]
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ def get_parameters(self, aviary_inputs=None, phase_info=None):

Optional, used if subsystems have fixed values.

Used in the phase builders (e.g. cruise_phase.py) when other parameters are added to the phase.
Used in the phase builders when other parameters are added to the phase.

This is distinct from `get_design_vars` in a nuanced way. Design variables
are variables that are optimized by the problem that are not at the phase level.
Expand Down
Loading
Loading