Level 3#

Level 3 represents the highest level of user control and customization in Aviary’s user interface. At this level, users have full access to Python and OpenMDAO methods that Aviary calls. They can use the complete set of Aviary’s methods, functionalities, and classes to construct and fine-tune their aircraft models. Level 3 enables users to have supreme control over every aspect of the model, including subsystems, connections, and advanced optimization techniques.

Level 3 is the most complex but specific methods defined at this level are used in levels 1 and 2, hopefully reducing user activation energy when learning more about Aviary. This progressive approach helps users gradually enhance their analysis capabilities and adapt to more complex modeling requirements as they gain proficiency and experience.

We will show two approaches of level 3 development using two examples: The first one will implement all methods differently from what are available in aviary/interface/methods_for_level2.py but will follow the same steps. It will show that you don’t have to have a .csv input file and you don’t have to have a phase_info dictionary. The second is a solved_2DOF mission approach that is embedded into all the methods of aviary/interface/methods_for_level2.py.

Note

For each of these examples we have set max_iter = 0, which means that the optimization will not run. This is done to reduce the computational time for the examples. If you want to run the optimization, you can set max_iter = 100 or some similar value.

A level 3 example: N3CC#

In level 2, we have shown how to follow the standard steps to build an Aviary model, but sometimes you may find that the model you have in mind does not match that predefined structure. If you start a new model that cannot been embedded in those methods, we can write special methods ourselves. This example will show you how to do that.

In aviary/validation_cases/benchmark_tests folder, there is an N3CC aircraft full mission benchmark test test_FLOPS_based_sizing_N3CC.py. Now, we will show how to create an N3CC example in level3. The key is that we follow the same steps:

  • __init__()

  • load_inputs()

  • check_and_preprocess_inputs()

  • build_model()

  • add_pre_mission_systems()

  • add_phases()

  • add_post_mission_systems()

  • link_phases()

  • add_driver()

  • add_design_variables() (optional)

  • add_objective()

  • setup()

  • run_aviary_problem()

Examining an N3CC design case#

"""
NOTES:
Includes:
Takeoff, Climb, Cruise, Descent, Landing
Computed Aero
advanced_single_aisle data
"""

import dymos as dm
import openmdao.api as om
import scipy.constants as _units

import aviary.api as av
from aviary.validation_cases.validation_tests import get_flops_inputs
from aviary.variable_info.functions import setup_model_options

prob = om.Problem(model=om.Group())
driver = prob.driver = om.pyOptSparseDriver()
driver.options['optimizer'] = 'IPOPT'
driver.opt_settings['max_iter'] = 100
driver.opt_settings['tol'] = 1e-3
driver.opt_settings['print_level'] = 4

########################################
# Aircraft Input Variables and Options #
########################################

aviary_inputs = get_flops_inputs('AdvancedSingleAisle')

aviary_inputs.set_val(av.Mission.Landing.LIFT_COEFFICIENT_MAX, 2.4, units='unitless')
aviary_inputs.set_val(av.Mission.Takeoff.LIFT_COEFFICIENT_MAX, 2.0, units='unitless')
aviary_inputs.set_val(av.Mission.Takeoff.ROLLING_FRICTION_COEFFICIENT, val=0.0175, units='unitless')

takeoff_fuel_burned = 577  # lbm
takeoff_thrust_per_eng = 24555.5  # lbf
takeoff_L_over_D = 17.35

aviary_inputs.set_val(av.Mission.Takeoff.FUEL_SIMPLE, takeoff_fuel_burned, units='lbm')
aviary_inputs.set_val(av.Mission.Takeoff.LIFT_OVER_DRAG, takeoff_L_over_D, units='unitless')
aviary_inputs.set_val(av.Mission.Design.THRUST_TAKEOFF_PER_ENG, takeoff_thrust_per_eng, units='lbf')

alt_airport = 0  # ft
cruise_mach = 0.79

alt_i_climb = 0 * _units.foot  # m
alt_f_climb = 35000.0 * _units.foot  # m
mass_i_climb = 131000 * _units.lb  # kg
mass_f_climb = 126000 * _units.lb  # kg
# initial mach set to lower value so it can intersect with takeoff end mach
# mach_i_climb = 0.3
mach_i_climb = 0.2
mach_f_climb = cruise_mach
range_i_climb = 0 * _units.nautical_mile  # m
range_f_climb = 160.3 * _units.nautical_mile  # m
t_i_climb = 2 * _units.minute  # sec
t_f_climb = 26.20 * _units.minute  # sec
t_duration_climb = t_f_climb - t_i_climb

alt_i_cruise = 35000 * _units.foot  # m
alt_f_cruise = 35000 * _units.foot  # m
alt_min_cruise = 35000 * _units.foot  # m
alt_max_cruise = 35000 * _units.foot  # m
mass_i_cruise = 126000 * _units.lb  # kg
mass_f_cruise = 102000 * _units.lb  # kg
cruise_mach = cruise_mach
range_i_cruise = 160.3 * _units.nautical_mile  # m
range_f_cruise = 3243.9 * _units.nautical_mile  # m
t_i_cruise = 26.20 * _units.minute  # sec
t_f_cruise = 432.38 * _units.minute  # sec
t_duration_cruise = t_f_cruise - t_i_cruise

alt_i_descent = 35000 * _units.foot
# final altitude set to 35 to ensure landing is feasible point
# alt_f_descent = 0*_units.foot
alt_f_descent = 35 * _units.foot
mach_i_descent = 0.79
mach_f_descent = 0.3
mass_i_descent = 102000 * _units.pound
mass_f_descent = 101000 * _units.pound
distance_i_descent = 3243.9 * _units.nautical_mile
distance_f_descent = 3378.7 * _units.nautical_mile
t_i_descent = 432.38 * _units.minute
t_f_descent = 461.62 * _units.minute
t_duration_descent = t_f_descent - t_i_descent

engines = [av.build_engine_deck(aviary_inputs)]
av.preprocess_options(aviary_inputs, engine_models=engines)

# define subsystems
aero = av.CoreAerodynamicsBuilder(code_origin=av.LegacyCode('FLOPS'))
geom = av.CoreGeometryBuilder(code_origin=av.LegacyCode('FLOPS'))
mass = av.CoreMassBuilder(code_origin=av.LegacyCode('FLOPS'))
prop = av.CorePropulsionBuilder(engine_models=engines)

premission_subsystems = [prop, geom, aero, mass]
mission_subsystems = [aero, prop]

####################
# Design Variables #
####################

# Nudge it a bit off the correct answer to verify that the optimize takes us there.
aviary_inputs.set_val(av.Mission.Design.GROSS_MASS, 135000.0, units='lbm')
aviary_inputs.set_val(av.Mission.Summary.GROSS_MASS, 135000.0, units='lbm')

prob.model.add_design_var(
    av.Mission.Design.GROSS_MASS, units='lbm', lower=100000.0, upper=200000.0, ref=135000
)
prob.model.add_design_var(
    av.Mission.Summary.GROSS_MASS, units='lbm', lower=100000.0, upper=200000.0, ref=135000
)

takeoff_options = av.HeightEnergyTakeoffPhaseBuilder(
    airport_altitude=alt_airport,  # ft
    # no units
    num_engines=aviary_inputs.get_val(av.Aircraft.Engine.NUM_ENGINES),
)

#################
# Define Phases #
#################
num_segments_climb = 6
num_segments_cruise = 1
num_segments_descent = 5

climb_seg_ends, _ = dm.utils.lgl.lgl(num_segments_climb + 1)
descent_seg_ends, _ = dm.utils.lgl.lgl(num_segments_descent + 1)

transcription_climb = dm.Radau(
    num_segments=num_segments_climb, order=3, compressed=True, segment_ends=climb_seg_ends
)
transcription_cruise = dm.Radau(num_segments=num_segments_cruise, order=3, compressed=True)
transcription_descent = dm.Radau(
    num_segments=num_segments_descent, order=3, compressed=True, segment_ends=descent_seg_ends
)

climb_options = av.HeightEnergyPhaseBuilder(
    'test_climb',
    user_options={
        'altitude_optimize': (False, 'unitless'),
        'altitude_initial': (alt_i_climb, 'm'),
        'altitude_final': (alt_f_climb, 'm'),
        'mach_optimize': (False, 'unitless'),
        'mach_initial': (mach_i_climb, 'unitless'),
        'mach_final': (mach_f_climb, 'unitless'),
    },
    subsystems=mission_subsystems,
    subsystem_options={'aerodynamics': {'method': 'computed'}},
    transcription=transcription_climb,
)

cruise_options = av.HeightEnergyPhaseBuilder(
    'test_cruise',
    user_options={
        'altitude_optimize': (False, 'unitless'),
        'altitude_polynomial_order': 3,
        'altitude_initial': (alt_min_cruise, 'm'),
        'altitude_final': (alt_max_cruise, 'm'),
        'mach_optimize': (False, 'unitless'),
        'mach_polynomial_order': 3,
        'mach_initial': (cruise_mach, 'unitless'),
        'mach_final': (cruise_mach, 'unitless'),
        'required_available_climb_rate': (300, 'ft/min'),
    },
    subsystems=mission_subsystems,
    subsystem_options={'aerodynamics': {'method': 'computed'}},
    transcription=transcription_cruise,
)

descent_options = av.HeightEnergyPhaseBuilder(
    'test_descent',
    user_options={
        'altitude_optimize': (False, 'unitless'),
        'altitude_final': (alt_f_descent, 'm'),
        'altitude_initial': (alt_i_descent, 'm'),
        'mach_optimize': (False, 'unitless'),
        'mach_initial': (mach_i_descent, 'unitless'),
        'mach_final': (mach_f_descent, 'unitless'),
    },
    subsystems=mission_subsystems,
    subsystem_options={'aerodynamics': {'method': 'computed'}},
    transcription=transcription_descent,
)

landing_options = av.HeightEnergyLandingPhaseBuilder(
    ref_wing_area=aviary_inputs.get_val(av.Aircraft.Wing.AREA, units='ft**2'),
    Cl_max_ldg=aviary_inputs.get_val(av.Mission.Landing.LIFT_COEFFICIENT_MAX),  # no units
)

# Upstream pre-mission analysis for aero
prob.model.add_subsystem(
    'pre_mission',
    av.CorePreMission(aviary_options=aviary_inputs, subsystems=premission_subsystems),
    promotes_inputs=['aircraft:*', 'mission:*'],
    promotes_outputs=['aircraft:*', 'mission:*'],
)

# directly connect phases (strong_couple = True), or use linkage constraints (weak
# coupling / strong_couple=False)
strong_couple = False

takeoff = takeoff_options.build_phase(False)

climb = climb_options.build_phase(aviary_options=aviary_inputs)
climb.set_state_options('mass', fix_initial=False, input_initial=True)
climb.set_state_options('distance', fix_initial=False, input_initial=True)
climb.set_control_options('altitude', fix_initial=False)
climb.set_control_options('mach', fix_initial=False)

cruise = cruise_options.build_phase(aviary_options=aviary_inputs)
cruise.set_state_options('mass', fix_initial=False, input_initial=False)
cruise.set_state_options('distance', fix_initial=False, input_initial=False)
cruise.set_control_options('mach', fix_initial=False)

descent = descent_options.build_phase(aviary_options=aviary_inputs)
descent.set_state_options('mass', fix_initial=False, input_initial=False)
descent.set_state_options('distance', fix_initial=False, input_initial=False)
descent.set_control_options('mach', fix_initial=False)

landing = landing_options.build_phase(False)

prob.model.add_subsystem(
    'takeoff', takeoff, promotes_inputs=['aircraft:*', 'mission:*'], promotes_outputs=['mission:*']
)

traj = prob.model.add_subsystem('traj', dm.Trajectory())

# if fix_initial is false, can we always set input_initial to be true for
# necessary states, and then ignore if we use a linkage?
climb.set_time_options(
    fix_initial=True,
    fix_duration=False,
    units='s',
    duration_bounds=(t_duration_climb * 0.5, t_duration_climb * 2),
    duration_ref=t_duration_climb,
)
cruise.set_time_options(
    fix_initial=False,
    fix_duration=False,
    units='s',
    duration_bounds=(t_duration_cruise * 0.5, t_duration_cruise * 2),
    duration_ref=t_duration_cruise,
    initial_bounds=(t_duration_climb * 0.5, t_duration_climb * 2),
)
descent.set_time_options(
    fix_initial=False,
    fix_duration=False,
    units='s',
    duration_bounds=(t_duration_descent * 0.5, t_duration_descent * 2),
    duration_ref=t_duration_descent,
    initial_bounds=(
        (t_duration_cruise + t_duration_climb) * 0.5,
        (t_duration_cruise + t_duration_climb) * 2,
    ),
)

traj.add_phase('climb', climb)

traj.add_phase('cruise', cruise)

traj.add_phase('descent', descent)

prob.model.add_subsystem(
    'landing', landing, promotes_inputs=['aircraft:*', 'mission:*'], promotes_outputs=['mission:*']
)

###############
# link phases #
###############
phases = ['climb', 'cruise', 'descent']
traj.link_phases(
    phases, ['time', av.Dynamic.Vehicle.MASS, av.Dynamic.Mission.DISTANCE], connected=strong_couple
)

# loop through phases and get all subsystem parameters
external_parameters = {}
for phase_name in phases:
    external_parameters[phase_name] = {}
    for subsystem in mission_subsystems:
        parameter_dict = subsystem.get_parameters(phase_info={}, aviary_inputs=aviary_inputs)
        for parameter in parameter_dict:
            external_parameters[phase_name][parameter] = parameter_dict[parameter]

traj = av.setup_trajectory_params(
    prob.model, traj, aviary_inputs, phases, external_parameters=external_parameters
)

##################################
# Connect in Takeoff and Landing #
##################################
prob.model.connect(av.Mission.Takeoff.FINAL_MASS, 'traj.climb.initial_states:mass')
prob.model.connect(av.Mission.Takeoff.GROUND_DISTANCE, 'traj.climb.initial_states:distance')

prob.model.connect('traj.descent.states:mass', av.Mission.Landing.TOUCHDOWN_MASS, src_indices=[-1])
prob.model.connect(
    'traj.descent.control_values:altitude', av.Mission.Landing.INITIAL_ALTITUDE, src_indices=[-1]
)

###############
# Constraints #
###############

ecomp = om.ExecComp(
    'fuel_burned = initial_mass - descent_mass_final',
    initial_mass={'units': 'lbm', 'shape': 1},
    descent_mass_final={'units': 'lbm', 'shape': 1},
    fuel_burned={'units': 'lbm', 'shape': 1},
)

prob.model.add_subsystem(
    'fuel_burn',
    ecomp,
    promotes_inputs=[('initial_mass', av.Mission.Design.GROSS_MASS)],
    promotes_outputs=['fuel_burned'],
)

prob.model.connect('traj.descent.states:mass', 'fuel_burn.descent_mass_final', src_indices=[-1])

ecomp = om.ExecComp(
    'overall_fuel = fuel_burned + fuel_reserve',
    fuel_burned={'units': 'lbm', 'shape': 1},
    fuel_reserve={'units': 'lbm', 'val': 2173.0},
    overall_fuel={'units': 'lbm'},
)
prob.model.add_subsystem(
    'fuel_calc', ecomp, promotes_inputs=['fuel_burned'], promotes_outputs=['overall_fuel']
)

ecomp = om.ExecComp(
    'mass_resid = operating_empty_mass + overall_fuel + payload_mass - initial_mass',
    operating_empty_mass={'units': 'lbm'},
    overall_fuel={'units': 'lbm'},
    payload_mass={'units': 'lbm'},
    initial_mass={'units': 'lbm'},
    mass_resid={'units': 'lbm'},
)

prob.model.add_subsystem(
    'mass_constraint',
    ecomp,
    promotes_inputs=[
        ('operating_empty_mass', av.Mission.Summary.OPERATING_MASS),
        'overall_fuel',
        ('payload_mass', av.Aircraft.CrewPayload.TOTAL_PAYLOAD_MASS),
        ('initial_mass', av.Mission.Design.GROSS_MASS),
    ],
    promotes_outputs=['mass_resid'],
)

prob.model.add_constraint('mass_resid', equals=0.0, ref=1.0)

prob.model.add_subsystem(
    'gtow_constraint',
    om.EQConstraintComp(
        'GTOW',
        eq_units='lbm',
        normalize=True,
        add_constraint=True,
    ),
    promotes_inputs=[
        ('lhs:GTOW', av.Mission.Design.GROSS_MASS),
        ('rhs:GTOW', av.Mission.Summary.GROSS_MASS),
    ],
)

##########################
# Add Objective Function #
##########################

# This is an example of a overall mission objective
# create a compound objective that minimizes climb time and maximizes final mass
# we are maxing final mass b/c we don't have an independent value for fuel_mass yet
# we are going to normalize these (making each of the sub-objectives approx = 1 )
prob.model.add_subsystem(
    'regularization',
    om.ExecComp(
        'reg_objective = fuel_mass/1500',
        reg_objective=0.0,
        fuel_mass={'units': 'lbm', 'shape': 1},
    ),
    promotes_outputs=['reg_objective'],
)
# connect the final mass from cruise into the objective
prob.model.connect(av.Mission.Summary.FUEL_MASS, 'regularization.fuel_mass')

prob.model.add_objective('reg_objective', ref=1)

# Set initial default values for all LEAPS aircraft variables.
varnames = [
    av.Aircraft.Engine.SCALE_FACTOR,
    av.Aircraft.Wing.MAX_CAMBER_AT_70_SEMISPAN,
    av.Aircraft.Wing.SWEEP,
    av.Aircraft.Wing.TAPER_RATIO,
    av.Aircraft.Wing.THICKNESS_TO_CHORD,
    av.Mission.Design.GROSS_MASS,
    av.Mission.Summary.GROSS_MASS,
]
av.set_aviary_input_defaults(prob.model, varnames, aviary_inputs)

av.setup_model_options(prob, aviary_inputs)

prob.setup()

phase = prob.model.traj.phases.cruise
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)

phase = prob.model.traj.phases.descent
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)

av.set_aviary_initial_values(prob, aviary_inputs)

############################################
# Initial Settings for States and Controls #
############################################

prob.set_val('traj.climb.t_initial', t_i_climb, units='s')
prob.set_val('traj.climb.t_duration', t_duration_climb, units='s')

prob.set_val(
    'traj.climb.controls:altitude',
    climb.interp(av.Dynamic.Mission.ALTITUDE, ys=[alt_i_climb, alt_f_climb]),
    units='m',
)
prob.set_val(
    'traj.climb.controls:mach',
    climb.interp(av.Dynamic.Atmosphere.MACH, ys=[mach_i_climb, mach_f_climb]),
    units='unitless',
)
prob.set_val(
    'traj.climb.states:mass',
    climb.interp(av.Dynamic.Vehicle.MASS, ys=[mass_i_climb, mass_f_climb]),
    units='kg',
)
prob.set_val(
    'traj.climb.states:distance',
    climb.interp(av.Dynamic.Mission.DISTANCE, ys=[range_i_climb, range_f_climb]),
    units='m',
)

prob.set_val('traj.cruise.t_initial', t_i_cruise, units='s')
prob.set_val('traj.cruise.t_duration', t_duration_cruise, units='s')

prob.set_val(
    'traj.cruise.controls:altitude',
    cruise.interp(av.Dynamic.Mission.ALTITUDE, ys=[alt_i_cruise, alt_f_cruise]),
    units='m',
)
prob.set_val(
    'traj.cruise.controls:mach',
    cruise.interp(av.Dynamic.Atmosphere.MACH, ys=[cruise_mach, cruise_mach]),
    units='unitless',
)
prob.set_val(
    'traj.cruise.states:mass',
    cruise.interp(av.Dynamic.Vehicle.MASS, ys=[mass_i_cruise, mass_f_cruise]),
    units='kg',
)
prob.set_val(
    'traj.cruise.states:distance',
    cruise.interp(av.Dynamic.Mission.DISTANCE, ys=[range_i_cruise, range_f_cruise]),
    units='m',
)

prob.set_val('traj.descent.t_initial', t_i_descent, units='s')
prob.set_val('traj.descent.t_duration', t_duration_descent, units='s')

prob.set_val(
    'traj.descent.controls:altitude',
    descent.interp(av.Dynamic.Mission.ALTITUDE, ys=[alt_i_descent, alt_f_descent]),
    units='m',
)
prob.set_val(
    'traj.descent.controls:mach',
    descent.interp(av.Dynamic.Atmosphere.MACH, ys=[mach_i_descent, mach_f_descent]),
    units='unitless',
)
prob.set_val(
    'traj.descent.states:mass',
    descent.interp(av.Dynamic.Vehicle.MASS, ys=[mass_i_descent, mass_f_descent]),
    units='kg',
)
prob.set_val(
    'traj.descent.states:distance',
    descent.interp(av.Dynamic.Mission.DISTANCE, ys=[distance_i_descent, distance_f_descent]),
    units='m',
)

# Turn off solver printing so that the optimizer output is readable.
prob.set_solver_print(level=0)

dm.run_problem(
    prob,
    simulate=False,
    make_plots=False,
    solution_record_file='N3CC_sizing.db',
)
prob.record('final')
prob.cleanup()

times_climb = prob.get_val('traj.climb.timeseries.time', units='s')
altitudes_climb = prob.get_val('traj.climb.timeseries.altitude', units='m')
masses_climb = prob.get_val('traj.climb.timeseries.mass', units='kg')
ranges_climb = prob.get_val('traj.climb.timeseries.distance', units='m')
velocities_climb = prob.get_val('traj.climb.timeseries.velocity', units='m/s')
thrusts_climb = prob.get_val('traj.climb.timeseries.thrust_net_total', units='N')
times_cruise = prob.get_val('traj.cruise.timeseries.time', units='s')
altitudes_cruise = prob.get_val('traj.cruise.timeseries.altitude', units='m')
masses_cruise = prob.get_val('traj.cruise.timeseries.mass', units='kg')
ranges_cruise = prob.get_val('traj.cruise.timeseries.distance', units='m')
velocities_cruise = prob.get_val('traj.cruise.timeseries.velocity', units='m/s')
thrusts_cruise = prob.get_val('traj.cruise.timeseries.thrust_net_total', units='N')
times_descent = prob.get_val('traj.descent.timeseries.time', units='s')
altitudes_descent = prob.get_val('traj.descent.timeseries.altitude', units='m')
masses_descent = prob.get_val('traj.descent.timeseries.mass', units='kg')
ranges_descent = prob.get_val('traj.descent.timeseries.distance', units='m')
velocities_descent = prob.get_val('traj.descent.timeseries.velocity', units='m/s')
thrusts_descent = prob.get_val('traj.descent.timeseries.thrust_net_total', units='N')


print('-------------------------------')
print(f'times_climb: {times_climb[-1]} (s)')
print(f'altitudes_climb: {altitudes_climb[-1]} (m)')
print(f'masses_climb: {masses_climb[-1]} (kg)')
print(f'ranges_climb: {ranges_climb[-1]} (m)')
print(f'velocities_climb: {velocities_climb[-1]} (m/s)')
print(f'thrusts_climb: {thrusts_climb[-1]} (N)')
print(f'times_cruise: {times_cruise[-1]} (s)')
print(f'altitudes_cruise: {altitudes_cruise[-1]} (m)')
print(f'masses_cruise: {masses_cruise[-1]} (kg)')
print(f'ranges_cruise: {ranges_cruise[-1]} (m)')
print(f'velocities_cruise: {velocities_cruise[-1]} (m/s)')
print(f'thrusts_cruise: {thrusts_cruise[-1]} (N)')
print(f'times_descent: {times_descent[-1]} (s)')
print(f'altitudes_descent: {altitudes_descent[-1]} (m)')
print(f'masses_descent: {masses_descent[-1]} (kg)')
print(f'ranges_descent: {ranges_descent[-1]} (m)')
print(f'velocities_descent: {velocities_descent[-1]} (m/s)')
print(f'thrusts_descent: {thrusts_descent[-1]} (N)')
print('-------------------------------')
/usr/share/miniconda/envs/test/lib/python3.12/site-packages/openmdao/core/system.py:4656: OpenMDAOWarning:Calling `list_inputs` before `final_setup` will only display the default values of variables and will not show the result of any `set_val` calls.
/usr/share/miniconda/envs/test/lib/python3.12/site-packages/openmdao/core/system.py:2769: PromotionWarning:'pre_mission.propulsion.turbofan_22k' <class SizeEngine>: input variable 'aircraft:engine:scale_factor', promoted using 'aircraft:engine:scale_factor', was already promoted using 'aircraft:engine:scale_factor'.
/usr/share/miniconda/envs/test/lib/python3.12/site-packages/openmdao/core/system.py:2769: PromotionWarning:'pre_mission.geometry.canard' <class Canard>: input variable 'aircraft:canard:area', promoted using 'aircraft:canard:area', was already promoted using 'aircraft*'.
/usr/share/miniconda/envs/test/lib/python3.12/site-packages/openmdao/core/system.py:2769: PromotionWarning:'pre_mission.geometry.canard' <class Canard>: input variable 'aircraft:canard:thickness_to_chord', promoted using 'aircraft:canard:thickness_to_chord', was already promoted using 'aircraft*'.
/usr/share/miniconda/envs/test/lib/python3.12/site-packages/openmdao/core/system.py:2769: PromotionWarning:'pre_mission.geometry.canard' <class Canard>: input variable 'aircraft:canard:wetted_area_scaler', promoted using 'aircraft:canard:wetted_area_scaler', was already promoted using 'aircraft*'.
/usr/share/miniconda/envs/test/lib/python3.12/site-packages/openmdao/core/system.py:2769: PromotionWarning:'pre_mission.geometry.fus_ratios' <class _FuselageRatios>: input variable 'aircraft:fuselage:ref_diameter', promoted using 'aircraft:fuselage:ref_diameter', was already promoted using 'aircraft*'.
/usr/share/miniconda/envs/test/lib/python3.12/site-packages/openmdao/core/system.py:2769: PromotionWarning:'pre_mission.geometry.fus_ratios' <class _FuselageRatios>: input variable 'aircraft:fuselage:length', promoted using 'aircraft:fuselage:length', was already promoted using 'aircraft*'.
/usr/share/miniconda/envs/test/lib/python3.12/site-packages/openmdao/core/system.py:2769: PromotionWarning:'pre_mission.geometry.fus_ratios' <class _FuselageRatios>: input variable 'aircraft:wing:area', promoted using 'aircraft:wing:area', was already promoted using 'aircraft*'.
/usr/share/miniconda/envs/test/lib/python3.12/site-packages/openmdao/core/system.py:2769: PromotionWarning:'pre_mission.geometry.fus_ratios' <class _FuselageRatios>: input variable 'aircraft:wing:aspect_ratio', promoted using 'aircraft:wing:aspect_ratio', was already promoted using 'aircraft*'.
/usr/share/miniconda/envs/test/lib/python3.12/site-packages/openmdao/core/system.py:2769: PromotionWarning:'pre_mission.geometry.fus_ratios' <class _FuselageRatios>: input variable 'aircraft:wing:glove_and_bat', promoted using 'aircraft:wing:glove_and_bat', was already promoted using 'aircraft*'.
/usr/share/miniconda/envs/test/lib/python3.12/site-packages/openmdao/core/system.py:2769: PromotionWarning:'pre_mission.geometry.fuselage' <class _Fuselage>: input variable 'aircraft:fuselage:ref_diameter', promoted using 'aircraft:fuselage:ref_diameter', was already promoted using 'aircraft*'.
/usr/share/miniconda/envs/test/lib/python3.12/site-packages/openmdao/core/system.py:2769: PromotionWarning:'pre_mission.geometry.fuselage' <class _Fuselage>: input variable 'aircraft:fuselage:length', promoted using 'aircraft:fuselage:length', was already promoted using 'aircraft*'.
/usr/share/miniconda/envs/test/lib/python3.12/site-packages/openmdao/core/system.py:2769: PromotionWarning:'pre_mission.geometry.fuselage' <class _Fuselage>: input variable 'aircraft:fuselage:wetted_area_scaler', promoted using 'aircraft:fuselage:wetted_area_scaler', was already promoted using 'aircraft*'.
/usr/share/miniconda/envs/test/lib/python3.12/site-packages/openmdao/core/system.py:2769: PromotionWarning:'pre_mission.geometry.fuselage' <class _Fuselage>: input variable 'aircraft:horizontal_tail:thickness_to_chord', promoted using 'aircraft:horizontal_tail:thickness_to_chord', was already promoted using 'aircraft*'.
/usr/share/miniconda/envs/test/lib/python3.12/site-packages/openmdao/core/system.py:2769: PromotionWarning:'pre_mission.geometry.fuselage' <class _Fuselage>: input variable 'aircraft:horizontal_tail:vertical_tail_fraction', promoted using 'aircraft:horizontal_tail:vertical_tail_fraction', was already promoted using 'aircraft*'.
/usr/share/miniconda/envs/test/lib/python3.12/site-packages/openmdao/core/system.py:2769: PromotionWarning:'pre_mission.geometry.fuselage' <class _Fuselage>: input variable 'aircraft:vertical_tail:thickness_to_chord', promoted using 'aircraft:vertical_tail:thickness_to_chord', was already promoted using 'aircraft*'.
/usr/share/miniconda/envs/test/lib/python3.12/site-packages/openmdao/core/system.py:2769: PromotionWarning:'pre_mission.geometry.fuselage' <class _Fuselage>: input variable 'aircraft:wing:thickness_to_chord', promoted using 'aircraft:wing:thickness_to_chord', was already promoted using 'aircraft*'.
/usr/share/miniconda/envs/test/lib/python3.12/site-packages/openmdao/core/system.py:2769: PromotionWarning:'pre_mission.geometry.nacelle_characteristic_lengths' <class NacelleCharacteristicLength>: input variable 'aircraft:nacelle:avg_diameter', promoted using 'aircraft:nacelle:avg_diameter', was already promoted using 'aircraft*'.
/usr/share/miniconda/envs/test/lib/python3.12/site-packages/openmdao/core/system.py:2769: PromotionWarning:'pre_mission.geometry.nacelle_characteristic_lengths' <class NacelleCharacteristicLength>: input variable 'aircraft:nacelle:avg_length', promoted using 'aircraft:nacelle:avg_length', was already promoted using 'aircraft*'.
/usr/share/miniconda/envs/test/lib/python3.12/site-packages/openmdao/core/system.py:2769: PromotionWarning:'pre_mission.geometry.nacelles' <class Nacelles>: input variable 'aircraft:nacelle:avg_diameter', promoted using 'aircraft:nacelle:avg_diameter', was already promoted using 'aircraft*'.
/usr/share/miniconda/envs/test/lib/python3.12/site-packages/openmdao/core/system.py:2769: PromotionWarning:'pre_mission.geometry.nacelles' <class Nacelles>: input variable 'aircraft:nacelle:avg_length', promoted using 'aircraft:nacelle:avg_length', was already promoted using 'aircraft*'.
/usr/share/miniconda/envs/test/lib/python3.12/site-packages/openmdao/core/system.py:2769: PromotionWarning:'pre_mission.geometry.nacelles' <class Nacelles>: input variable 'aircraft:nacelle:wetted_area_scaler', promoted using 'aircraft:nacelle:wetted_area_scaler', was already promoted using 'aircraft*'.
/usr/share/miniconda/envs/test/lib/python3.12/site-packages/openmdao/core/system.py:2769: PromotionWarning:'pre_mission.geometry.tail' <class _Tail>: input variable 'aircraft:horizontal_tail:area', promoted using 'aircraft:horizontal_tail:area', was already promoted using 'aircraft*'.
/usr/share/miniconda/envs/test/lib/python3.12/site-packages/openmdao/core/system.py:2769: PromotionWarning:'pre_mission.geometry.tail' <class _Tail>: input variable 'aircraft:horizontal_tail:vertical_tail_fraction', promoted using 'aircraft:horizontal_tail:vertical_tail_fraction', was already promoted using 'aircraft*'.
/usr/share/miniconda/envs/test/lib/python3.12/site-packages/openmdao/core/system.py:2769: PromotionWarning:'pre_mission.geometry.tail' <class _Tail>: input variable 'aircraft:horizontal_tail:wetted_area_scaler', promoted using 'aircraft:horizontal_tail:wetted_area_scaler', was already promoted using 'aircraft*'.
/usr/share/miniconda/envs/test/lib/python3.12/site-packages/openmdao/core/system.py:2769: PromotionWarning:'pre_mission.geometry.tail' <class _Tail>: input variable 'aircraft:vertical_tail:area', promoted using 'aircraft:vertical_tail:area', was already promoted using 'aircraft*'.
/usr/share/miniconda/envs/test/lib/python3.12/site-packages/openmdao/core/system.py:2769: PromotionWarning:'pre_mission.geometry.tail' <class _Tail>: input variable 'aircraft:vertical_tail:wetted_area_scaler', promoted using 'aircraft:vertical_tail:wetted_area_scaler', was already promoted using 'aircraft*'.
/usr/share/miniconda/envs/test/lib/python3.12/site-packages/openmdao/core/system.py:2769: PromotionWarning:'pre_mission.geometry.wing' <class _Wing>: input variable 'aircraft:wing:area', promoted using 'aircraft:wing:area', was already promoted using 'aircraft*'.
/usr/share/miniconda/envs/test/lib/python3.12/site-packages/openmdao/core/system.py:2769: PromotionWarning:'pre_mission.geometry.wing' <class _Wing>: input variable 'aircraft:wing:wetted_area_scaler', promoted using 'aircraft:wing:wetted_area_scaler', was already promoted using 'aircraft*'.
/usr/share/miniconda/envs/test/lib/python3.12/site-packages/openmdao/core/system.py:2769: PromotionWarning:'pre_mission.geometry.wing_characteristic_lengths' <class WingCharacteristicLength>: input variable 'aircraft:wing:area', promoted using 'aircraft:wing:area', was already promoted using 'aircraft*'.
/usr/share/miniconda/envs/test/lib/python3.12/site-packages/openmdao/core/system.py:2769: PromotionWarning:'pre_mission.geometry.wing_characteristic_lengths' <class WingCharacteristicLength>: input variable 'aircraft:wing:aspect_ratio', promoted using 'aircraft:wing:aspect_ratio', was already promoted using 'aircraft*'.
/usr/share/miniconda/envs/test/lib/python3.12/site-packages/openmdao/core/system.py:2769: PromotionWarning:'pre_mission.geometry.wing_characteristic_lengths' <class WingCharacteristicLength>: input variable 'aircraft:wing:glove_and_bat', promoted using 'aircraft:wing:glove_and_bat', was already promoted using 'aircraft*'.
/usr/share/miniconda/envs/test/lib/python3.12/site-packages/openmdao/core/system.py:2769: PromotionWarning:'pre_mission.geometry.wing_characteristic_lengths' <class WingCharacteristicLength>: input variable 'aircraft:wing:taper_ratio', promoted using 'aircraft:wing:taper_ratio', was already promoted using 'aircraft*'.
/usr/share/miniconda/envs/test/lib/python3.12/site-packages/openmdao/core/system.py:2769: PromotionWarning:'pre_mission.geometry.wing_characteristic_lengths' <class WingCharacteristicLength>: input variable 'aircraft:wing:thickness_to_chord', promoted using 'aircraft:wing:thickness_to_chord', was already promoted using 'aircraft*'.
/usr/share/miniconda/envs/test/lib/python3.12/site-packages/dymos/transcriptions/transcription_base.py:449: OpenMDAOWarning:'traj.phases.climb' <class Phase>
  Non-default solvers are required
    implicit parameters: False
    solved segments: False
    input initial: True
  Setting `traj.phases.climb.nonlinear_solver = om.NewtonSolver(iprint=0, solve_subsystems=True, maxiter=1000, stall_limit=3)`
  Explicitly set traj.phases.climb.nonlinear_solver to override.
  Setting `traj.phases.climb.linear_solver = om.DirectSolver(iprint=2)`
  Explicitly set traj.phases.climb.linear_solver to override.
  Set `traj.phases.climb.options["auto_solvers"] = False` to disable this behavior.
/usr/share/miniconda/envs/test/lib/python3.12/site-packages/openmdao/utils/relevance.py:1295: OpenMDAOWarning:The following groups have a nonlinear solver that computes gradients and will be treated as atomic for the purposes of determining which systems are included in the optimization iteration: 
traj.phases.climb
traj.phases.cruise.rhs_all.solver_sub
traj.phases.descent.rhs_all.solver_sub
List of user-set options:

                                    Name   Value                used
                        file_print_level = 5                     yes
                   hessian_approximation = limited-memory        yes
                           linear_solver = mumps                 yes
                                max_iter = 100                   yes
                      nlp_scaling_method = user-scaling          yes
                             output_file = /home/runner/work/Aviary/Aviary/aviary/docs/getting_started/problem_out/IPOPT.out  yes
                             print_level = 4                     yes
                      print_user_options = yes                   yes
                                      sb = yes                   yes
                                     tol = 0.001                 yes

Number of Iterations....: 0

Number of objective function evaluations             = 0
Number of objective gradient evaluations             = 0
Number of equality constraint evaluations            = 0
Number of inequality constraint evaluations          = 1
Number of equality constraint Jacobian evaluations   = 0
Number of inequality constraint Jacobian evaluations = 0
Number of Lagrangian Hessian evaluations             = 0
Total seconds in IPOPT                               = 0.213

EXIT: Invalid number in NLP function or derivative detected.


Optimization Problem -- Optimization using pyOpt_sparse
================================================================================
    Objective Function: _objfunc

    Solution: 
--------------------------------------------------------------------------------
    Total Time:                    0.2147
       User Objective Time :       0.2125
       User Sensitivity Time :     0.0000
       Interface Time :            0.0003
       Opt Solver Time:            0.0019
    Calls to Objective Function :       1
    Calls to Sens Function :            0


   Objectives
      Index  Name                     Value
          0  reg_objective     0.000000E+00

   Variables (c - continuous, i - integer, d - discrete)
      Index  Name                              Type      Lower Bound            Value      Upper Bound     Status
          0  mission:design:gross_mass_0          c     7.407407E-01     1.000000E+00     1.481481E+00           
          1  mission:summary:gross_mass_0         c     7.407407E-01     1.000000E+00     1.481481E+00           
          2  traj.climb.t_duration_0              c     5.000000E-01     1.000000E+00     2.000000E+00           
          3  traj.climb.states:mass_0             c     0.000000E+00     5.935225E+00     1.000000E+17           
          4  traj.climb.states:mass_1             c     0.000000E+00     5.925793E+00     1.000000E+17           
          5  traj.climb.states:mass_2             c     0.000000E+00     5.922808E+00     1.000000E+17           
          6  traj.climb.states:mass_3             c     0.000000E+00     5.908258E+00     1.000000E+17           
          7  traj.climb.states:mass_4             c     0.000000E+00     5.888182E+00     1.000000E+17           
          8  traj.climb.states:mass_5             c     0.000000E+00     5.881829E+00     1.000000E+17           
          9  traj.climb.states:mass_6             c     0.000000E+00     5.862952E+00     1.000000E+17           
         10  traj.climb.states:mass_7             c     0.000000E+00     5.836905E+00     1.000000E+17           
         11  traj.climb.states:mass_8             c     0.000000E+00     5.828662E+00     1.000000E+17           
         12  traj.climb.states:mass_9             c     0.000000E+00     5.809785E+00     1.000000E+17           
         13  traj.climb.states:mass_10            c     0.000000E+00     5.783739E+00     1.000000E+17           
         14  traj.climb.states:mass_11            c     0.000000E+00     5.775495E+00     1.000000E+17           
         15  traj.climb.states:mass_12            c     0.000000E+00     5.760946E+00     1.000000E+17           
         16  traj.climb.states:mass_13            c     0.000000E+00     5.740870E+00     1.000000E+17           
         17  traj.climb.states:mass_14            c     0.000000E+00     5.734516E+00     1.000000E+17           
         18  traj.climb.states:mass_15            c     0.000000E+00     5.727681E+00     1.000000E+17           
         19  traj.climb.states:mass_16            c     0.000000E+00     5.718249E+00     1.000000E+17           
         20  traj.climb.states:mass_17            c     0.000000E+00     5.715264E+00     1.000000E+17           
         21  traj.climb.states:distance_0         c     0.000000E+00     8.947709E-03     1.000000E+15           
         22  traj.climb.states:distance_1         c     0.000000E+00     2.129372E-02     1.000000E+15           
         23  traj.climb.states:distance_2         c     0.000000E+00     2.520119E-02     1.000000E+15           
         24  traj.climb.states:distance_3         c     0.000000E+00     4.424674E-02     1.000000E+15           
         25  traj.climb.states:distance_4         c     0.000000E+00     7.052571E-02     1.000000E+15           
         26  traj.climb.states:distance_5         c     0.000000E+00     7.884292E-02     1.000000E+15           
         27  traj.climb.states:distance_6         c     0.000000E+00     1.035527E-01     1.000000E+15           
         28  traj.climb.states:distance_7         c     0.000000E+00     1.376470E-01     1.000000E+15           
         29  traj.climb.states:distance_8         c     0.000000E+00     1.484378E-01     1.000000E+15           
         30  traj.climb.states:distance_9         c     0.000000E+00     1.731475E-01     1.000000E+15           
         31  traj.climb.states:distance_10        c     0.000000E+00     2.072419E-01     1.000000E+15           
         32  traj.climb.states:distance_11        c     0.000000E+00     2.180327E-01     1.000000E+15           
         33  traj.climb.states:distance_12        c     0.000000E+00     2.370782E-01     1.000000E+15           
         34  traj.climb.states:distance_13        c     0.000000E+00     2.633572E-01     1.000000E+15           
         35  traj.climb.states:distance_14        c     0.000000E+00     2.716744E-01     1.000000E+15           
         36  traj.climb.states:distance_15        c     0.000000E+00     2.806221E-01     1.000000E+15           
         37  traj.climb.states:distance_16        c     0.000000E+00     2.929681E-01     1.000000E+15           
         38  traj.climb.states:distance_17        c     0.000000E+00     2.968756E-01     1.000000E+15           
         39  traj.cruise.t_initial_0              c     7.260000E+02     1.572000E+03     2.904000E+03           
         40  traj.cruise.t_duration_0             c     5.000000E-01     1.000000E+00     2.000000E+00           
         41  traj.cruise.states:mass_0            c     0.000000E+00     5.715264E+00     1.000000E+17           
         42  traj.cruise.states:mass_1            c     0.000000E+00     5.328748E+00     1.000000E+17           
         43  traj.cruise.states:mass_2            c     0.000000E+00     4.795434E+00     1.000000E+17           
         44  traj.cruise.states:mass_3            c     0.000000E+00     4.626642E+00     1.000000E+17           
         45  traj.cruise.states:distance_0        c     0.000000E+00     2.968756E-01     1.000000E+15           
         46  traj.cruise.states:distance_1        c     0.000000E+00     2.324511E+00     1.000000E+15           
         47  traj.cruise.states:distance_2        c     0.000000E+00     5.122233E+00     1.000000E+15           
         48  traj.cruise.states:distance_3        c     0.000000E+00     6.007703E+00     1.000000E+15           
         49  traj.descent.t_initial_0             c     1.291140E+04     2.594280E+04     5.164560E+04           
         50  traj.descent.t_duration_0            c     5.000000E-01     1.000000E+00     2.000000E+00           
         51  traj.descent.states:mass_0           c     0.000000E+00     4.626642E+00     1.000000E+17           
         52  traj.descent.states:mass_1           c     0.000000E+00     4.624750E+00     1.000000E+17           
         53  traj.descent.states:mass_2           c     0.000000E+00     4.622140E+00     1.000000E+17           
         54  traj.descent.states:mass_3           c     0.000000E+00     4.621314E+00     1.000000E+17           
         55  traj.descent.states:mass_4           c     0.000000E+00     4.617450E+00     1.000000E+17           
         56  traj.descent.states:mass_5           c     0.000000E+00     4.612119E+00     1.000000E+17           
         57  traj.descent.states:mass_6           c     0.000000E+00     4.610431E+00     1.000000E+17           
         58  traj.descent.states:mass_7           c     0.000000E+00     4.605838E+00     1.000000E+17           
         59  traj.descent.states:mass_8           c     0.000000E+00     4.599500E+00     1.000000E+17           
         60  traj.descent.states:mass_9           c     0.000000E+00     4.597494E+00     1.000000E+17           
         61  traj.descent.states:mass_10          c     0.000000E+00     4.593630E+00     1.000000E+17           
         62  traj.descent.states:mass_11          c     0.000000E+00     4.588299E+00     1.000000E+17           
         63  traj.descent.states:mass_12          c     0.000000E+00     4.586611E+00     1.000000E+17           
         64  traj.descent.states:mass_13          c     0.000000E+00     4.584720E+00     1.000000E+17           
         65  traj.descent.states:mass_14          c     0.000000E+00     4.582109E+00     1.000000E+17           
         66  traj.descent.states:mass_15          c     0.000000E+00     4.581283E+00     1.000000E+17           
         67  traj.descent.states:distance_0       c     0.000000E+00     6.007703E+00     1.000000E+15           
         68  traj.descent.states:distance_1       c     0.000000E+00     6.018115E+00     1.000000E+15           
         69  traj.descent.states:distance_2       c     0.000000E+00     6.032483E+00     1.000000E+15           
         70  traj.descent.states:distance_3       c     0.000000E+00     6.037030E+00     1.000000E+15           
         71  traj.descent.states:distance_4       c     0.000000E+00     6.058295E+00     1.000000E+15           
         72  traj.descent.states:distance_5       c     0.000000E+00     6.087637E+00     1.000000E+15           
         73  traj.descent.states:distance_6       c     0.000000E+00     6.096924E+00     1.000000E+15           
         74  traj.descent.states:distance_7       c     0.000000E+00     6.122206E+00     1.000000E+15           
         75  traj.descent.states:distance_8       c     0.000000E+00     6.157091E+00     1.000000E+15           
         76  traj.descent.states:distance_9       c     0.000000E+00     6.168132E+00     1.000000E+15           
         77  traj.descent.states:distance_10      c     0.000000E+00     6.189397E+00     1.000000E+15           
         78  traj.descent.states:distance_11      c     0.000000E+00     6.218739E+00     1.000000E+15           
         79  traj.descent.states:distance_12      c     0.000000E+00     6.228025E+00     1.000000E+15           
         80  traj.descent.states:distance_13      c     0.000000E+00     6.238438E+00     1.000000E+15           
         81  traj.descent.states:distance_14      c     0.000000E+00     6.252805E+00     1.000000E+15           
         82  traj.descent.states:distance_15      c     0.000000E+00     6.257352E+00     1.000000E+15           

   Constraints (i - inequality, e - equality)
      Index  Name                                                         Type          Lower           Value           Upper    Status  Lagrange Multiplier
          0  mass_resid                                                      e   0.000000E+00             NAN    0.000000E+00               0.00000E+00
          1  gtow_constraint.GTOW                                            e   0.000000E+00             NAN    0.000000E+00               0.00000E+00
          2  traj.linkages.climb:time_final|cruise:time_initial              e   0.000000E+00             NAN    0.000000E+00               0.00000E+00
          3  traj.linkages.climb:mass_final|cruise:mass_initial              e   0.000000E+00             NAN    0.000000E+00               0.00000E+00
          4  traj.linkages.climb:distance_final|cruise:distance_initial      e   0.000000E+00             NAN    0.000000E+00               0.00000E+00
          5  traj.linkages.cruise:time_final|descent:time_initial            e   0.000000E+00             NAN    0.000000E+00               0.00000E+00
          6  traj.linkages.cruise:mass_final|descent:mass_initial            e   0.000000E+00             NAN    0.000000E+00               0.00000E+00
          7  traj.linkages.cruise:distance_final|descent:distance_initial    e   0.000000E+00             NAN    0.000000E+00               0.00000E+00
          8  traj.climb.collocation_constraint.defects:mass                  e   0.000000E+00             NAN    0.000000E+00               0.00000E+00
          9  traj.climb.collocation_constraint.defects:mass                  e   0.000000E+00             NAN    0.000000E+00               0.00000E+00
         10  traj.climb.collocation_constraint.defects:mass                  e   0.000000E+00             NAN    0.000000E+00               0.00000E+00
         11  traj.climb.collocation_constraint.defects:mass                  e   0.000000E+00             NAN    0.000000E+00               0.00000E+00
         12  traj.climb.collocation_constraint.defects:mass                  e   0.000000E+00             NAN    0.000000E+00               0.00000E+00
         13  traj.climb.collocation_constraint.defects:mass                  e   0.000000E+00             NAN    0.000000E+00               0.00000E+00
         14  traj.climb.collocation_constraint.defects:mass                  e   0.000000E+00             NAN    0.000000E+00               0.00000E+00
         15  traj.climb.collocation_constraint.defects:mass                  e   0.000000E+00             NAN    0.000000E+00               0.00000E+00
         16  traj.climb.collocation_constraint.defects:mass                  e   0.000000E+00             NAN    0.000000E+00               0.00000E+00
         17  traj.climb.collocation_constraint.defects:mass                  e   0.000000E+00             NAN    0.000000E+00               0.00000E+00
         18  traj.climb.collocation_constraint.defects:mass                  e   0.000000E+00             NAN    0.000000E+00               0.00000E+00
         19  traj.climb.collocation_constraint.defects:mass                  e   0.000000E+00             NAN    0.000000E+00               0.00000E+00
         20  traj.climb.collocation_constraint.defects:mass                  e   0.000000E+00             NAN    0.000000E+00               0.00000E+00
         21  traj.climb.collocation_constraint.defects:mass                  e   0.000000E+00             NAN    0.000000E+00               0.00000E+00
         22  traj.climb.collocation_constraint.defects:mass                  e   0.000000E+00             NAN    0.000000E+00               0.00000E+00
         23  traj.climb.collocation_constraint.defects:mass                  e   0.000000E+00             NAN    0.000000E+00               0.00000E+00
         24  traj.climb.collocation_constraint.defects:mass                  e   0.000000E+00             NAN    0.000000E+00               0.00000E+00
         25  traj.climb.collocation_constraint.defects:mass                  e   0.000000E+00             NAN    0.000000E+00               0.00000E+00
         26  traj.climb.collocation_constraint.defects:distance              e   0.000000E+00             NAN    0.000000E+00               0.00000E+00
         27  traj.climb.collocation_constraint.defects:distance              e   0.000000E+00             NAN    0.000000E+00               0.00000E+00
         28  traj.climb.collocation_constraint.defects:distance              e   0.000000E+00             NAN    0.000000E+00               0.00000E+00
         29  traj.climb.collocation_constraint.defects:distance              e   0.000000E+00             NAN    0.000000E+00               0.00000E+00
         30  traj.climb.collocation_constraint.defects:distance              e   0.000000E+00             NAN    0.000000E+00               0.00000E+00
         31  traj.climb.collocation_constraint.defects:distance              e   0.000000E+00             NAN    0.000000E+00               0.00000E+00
         32  traj.climb.collocation_constraint.defects:distance              e   0.000000E+00             NAN    0.000000E+00               0.00000E+00
         33  traj.climb.collocation_constraint.defects:distance              e   0.000000E+00             NAN    0.000000E+00               0.00000E+00
         34  traj.climb.collocation_constraint.defects:distance              e   0.000000E+00             NAN    0.000000E+00               0.00000E+00
         35  traj.climb.collocation_constraint.defects:distance              e   0.000000E+00             NAN    0.000000E+00               0.00000E+00
         36  traj.climb.collocation_constraint.defects:distance              e   0.000000E+00             NAN    0.000000E+00               0.00000E+00
         37  traj.climb.collocation_constraint.defects:distance              e   0.000000E+00             NAN    0.000000E+00               0.00000E+00
         38  traj.climb.collocation_constraint.defects:distance              e   0.000000E+00             NAN    0.000000E+00               0.00000E+00
         39  traj.climb.collocation_constraint.defects:distance              e   0.000000E+00             NAN    0.000000E+00               0.00000E+00
         40  traj.climb.collocation_constraint.defects:distance              e   0.000000E+00             NAN    0.000000E+00               0.00000E+00
         41  traj.climb.collocation_constraint.defects:distance              e   0.000000E+00             NAN    0.000000E+00               0.00000E+00
         42  traj.climb.collocation_constraint.defects:distance              e   0.000000E+00             NAN    0.000000E+00               0.00000E+00
         43  traj.climb.collocation_constraint.defects:distance              e   0.000000E+00             NAN    0.000000E+00               0.00000E+00
         44  traj.cruise.collocation_constraint.defects:mass                 e   0.000000E+00             NAN    0.000000E+00               0.00000E+00
         45  traj.cruise.collocation_constraint.defects:mass                 e   0.000000E+00             NAN    0.000000E+00               0.00000E+00
         46  traj.cruise.collocation_constraint.defects:mass                 e   0.000000E+00             NAN    0.000000E+00               0.00000E+00
         47  traj.cruise.collocation_constraint.defects:distance             e   0.000000E+00             NAN    0.000000E+00               0.00000E+00
         48  traj.cruise.collocation_constraint.defects:distance             e   0.000000E+00             NAN    0.000000E+00               0.00000E+00
         49  traj.cruise.collocation_constraint.defects:distance             e   0.000000E+00             NAN    0.000000E+00               0.00000E+00
         50  traj.descent.collocation_constraint.defects:mass                e   0.000000E+00             NAN    0.000000E+00               0.00000E+00
         51  traj.descent.collocation_constraint.defects:mass                e   0.000000E+00             NAN    0.000000E+00               0.00000E+00
         52  traj.descent.collocation_constraint.defects:mass                e   0.000000E+00             NAN    0.000000E+00               0.00000E+00
         53  traj.descent.collocation_constraint.defects:mass                e   0.000000E+00             NAN    0.000000E+00               0.00000E+00
         54  traj.descent.collocation_constraint.defects:mass                e   0.000000E+00             NAN    0.000000E+00               0.00000E+00
         55  traj.descent.collocation_constraint.defects:mass                e   0.000000E+00             NAN    0.000000E+00               0.00000E+00
         56  traj.descent.collocation_constraint.defects:mass                e   0.000000E+00             NAN    0.000000E+00               0.00000E+00
         57  traj.descent.collocation_constraint.defects:mass                e   0.000000E+00             NAN    0.000000E+00               0.00000E+00
         58  traj.descent.collocation_constraint.defects:mass                e   0.000000E+00             NAN    0.000000E+00               0.00000E+00
         59  traj.descent.collocation_constraint.defects:mass                e   0.000000E+00             NAN    0.000000E+00               0.00000E+00
         60  traj.descent.collocation_constraint.defects:mass                e   0.000000E+00             NAN    0.000000E+00               0.00000E+00
         61  traj.descent.collocation_constraint.defects:mass                e   0.000000E+00             NAN    0.000000E+00               0.00000E+00
         62  traj.descent.collocation_constraint.defects:mass                e   0.000000E+00             NAN    0.000000E+00               0.00000E+00
         63  traj.descent.collocation_constraint.defects:mass                e   0.000000E+00             NAN    0.000000E+00               0.00000E+00
         64  traj.descent.collocation_constraint.defects:mass                e   0.000000E+00             NAN    0.000000E+00               0.00000E+00
         65  traj.descent.collocation_constraint.defects:distance            e   0.000000E+00             NAN    0.000000E+00               0.00000E+00
         66  traj.descent.collocation_constraint.defects:distance            e   0.000000E+00             NAN    0.000000E+00               0.00000E+00
         67  traj.descent.collocation_constraint.defects:distance            e   0.000000E+00             NAN    0.000000E+00               0.00000E+00
         68  traj.descent.collocation_constraint.defects:distance            e   0.000000E+00             NAN    0.000000E+00               0.00000E+00
         69  traj.descent.collocation_constraint.defects:distance            e   0.000000E+00             NAN    0.000000E+00               0.00000E+00
         70  traj.descent.collocation_constraint.defects:distance            e   0.000000E+00             NAN    0.000000E+00               0.00000E+00
         71  traj.descent.collocation_constraint.defects:distance            e   0.000000E+00             NAN    0.000000E+00               0.00000E+00
         72  traj.descent.collocation_constraint.defects:distance            e   0.000000E+00             NAN    0.000000E+00               0.00000E+00
         73  traj.descent.collocation_constraint.defects:distance            e   0.000000E+00             NAN    0.000000E+00               0.00000E+00
         74  traj.descent.collocation_constraint.defects:distance            e   0.000000E+00             NAN    0.000000E+00               0.00000E+00
         75  traj.descent.collocation_constraint.defects:distance            e   0.000000E+00             NAN    0.000000E+00               0.00000E+00
         76  traj.descent.collocation_constraint.defects:distance            e   0.000000E+00             NAN    0.000000E+00               0.00000E+00
         77  traj.descent.collocation_constraint.defects:distance            e   0.000000E+00             NAN    0.000000E+00               0.00000E+00
         78  traj.descent.collocation_constraint.defects:distance            e   0.000000E+00             NAN    0.000000E+00               0.00000E+00
         79  traj.descent.collocation_constraint.defects:distance            e   0.000000E+00             NAN    0.000000E+00               0.00000E+00
         80  traj.climb.throttle[path]                                       i   0.000000E+00             NAN    1.000000E+00        lu     0.00000E+00
         81  traj.climb.throttle[path]                                       i   0.000000E+00             NAN    1.000000E+00        lu     0.00000E+00
         82  traj.climb.throttle[path]                                       i   0.000000E+00             NAN    1.000000E+00        lu     0.00000E+00
         83  traj.climb.throttle[path]                                       i   0.000000E+00             NAN    1.000000E+00        lu     0.00000E+00
         84  traj.climb.throttle[path]                                       i   0.000000E+00             NAN    1.000000E+00        lu     0.00000E+00
         85  traj.climb.throttle[path]                                       i   0.000000E+00             NAN    1.000000E+00        lu     0.00000E+00
         86  traj.climb.throttle[path]                                       i   0.000000E+00             NAN    1.000000E+00        lu     0.00000E+00
         87  traj.climb.throttle[path]                                       i   0.000000E+00             NAN    1.000000E+00        lu     0.00000E+00
         88  traj.climb.throttle[path]                                       i   0.000000E+00             NAN    1.000000E+00        lu     0.00000E+00
         89  traj.climb.throttle[path]                                       i   0.000000E+00             NAN    1.000000E+00        lu     0.00000E+00
         90  traj.climb.throttle[path]                                       i   0.000000E+00             NAN    1.000000E+00        lu     0.00000E+00
         91  traj.climb.throttle[path]                                       i   0.000000E+00             NAN    1.000000E+00        lu     0.00000E+00
         92  traj.climb.throttle[path]                                       i   0.000000E+00             NAN    1.000000E+00        lu     0.00000E+00
         93  traj.climb.throttle[path]                                       i   0.000000E+00             NAN    1.000000E+00        lu     0.00000E+00
         94  traj.climb.throttle[path]                                       i   0.000000E+00             NAN    1.000000E+00        lu     0.00000E+00
         95  traj.climb.throttle[path]                                       i   0.000000E+00             NAN    1.000000E+00        lu     0.00000E+00
         96  traj.climb.throttle[path]                                       i   0.000000E+00             NAN    1.000000E+00        lu     0.00000E+00
         97  traj.climb.throttle[path]                                       i   0.000000E+00             NAN    1.000000E+00        lu     0.00000E+00
         98  traj.climb.throttle[path]                                       i   0.000000E+00             NAN    1.000000E+00        lu     0.00000E+00
         99  traj.climb.throttle[path]                                       i   0.000000E+00             NAN    1.000000E+00        lu     0.00000E+00
        100  traj.climb.throttle[path]                                       i   0.000000E+00             NAN    1.000000E+00        lu     0.00000E+00
        101  traj.climb.throttle[path]                                       i   0.000000E+00             NAN    1.000000E+00        lu     0.00000E+00
        102  traj.climb.throttle[path]                                       i   0.000000E+00             NAN    1.000000E+00        lu     0.00000E+00
        103  traj.climb.throttle[path]                                       i   0.000000E+00             NAN    1.000000E+00        lu     0.00000E+00
        104  traj.cruise.altitude_rate_max[path]                             i   5.000000E+00             NAN    1.000000E+30        lu     0.00000E+00
        105  traj.cruise.altitude_rate_max[path]                             i   5.000000E+00             NAN    1.000000E+30        lu     0.00000E+00
        106  traj.cruise.altitude_rate_max[path]                             i   5.000000E+00             NAN    1.000000E+30        lu     0.00000E+00
        107  traj.cruise.altitude_rate_max[path]                             i   5.000000E+00             NAN    1.000000E+30        lu     0.00000E+00
        108  traj.cruise.throttle[path]                                      i   0.000000E+00             NAN    1.000000E+00        lu     0.00000E+00
        109  traj.cruise.throttle[path]                                      i   0.000000E+00             NAN    1.000000E+00        lu     0.00000E+00
        110  traj.cruise.throttle[path]                                      i   0.000000E+00             NAN    1.000000E+00        lu     0.00000E+00
        111  traj.cruise.throttle[path]                                      i   0.000000E+00             NAN    1.000000E+00        lu     0.00000E+00
        112  traj.descent.throttle[path]                                     i   0.000000E+00             NAN    1.000000E+00        lu     0.00000E+00
        113  traj.descent.throttle[path]                                     i   0.000000E+00             NAN    1.000000E+00        lu     0.00000E+00
        114  traj.descent.throttle[path]                                     i   0.000000E+00             NAN    1.000000E+00        lu     0.00000E+00
        115  traj.descent.throttle[path]                                     i   0.000000E+00             NAN    1.000000E+00        lu     0.00000E+00
        116  traj.descent.throttle[path]                                     i   0.000000E+00             NAN    1.000000E+00        lu     0.00000E+00
        117  traj.descent.throttle[path]                                     i   0.000000E+00             NAN    1.000000E+00        lu     0.00000E+00
        118  traj.descent.throttle[path]                                     i   0.000000E+00             NAN    1.000000E+00        lu     0.00000E+00
        119  traj.descent.throttle[path]                                     i   0.000000E+00             NAN    1.000000E+00        lu     0.00000E+00
        120  traj.descent.throttle[path]                                     i   0.000000E+00             NAN    1.000000E+00        lu     0.00000E+00
        121  traj.descent.throttle[path]                                     i   0.000000E+00             NAN    1.000000E+00        lu     0.00000E+00
        122  traj.descent.throttle[path]                                     i   0.000000E+00             NAN    1.000000E+00        lu     0.00000E+00
        123  traj.descent.throttle[path]                                     i   0.000000E+00             NAN    1.000000E+00        lu     0.00000E+00
        124  traj.descent.throttle[path]                                     i   0.000000E+00             NAN    1.000000E+00        lu     0.00000E+00
        125  traj.descent.throttle[path]                                     i   0.000000E+00             NAN    1.000000E+00        lu     0.00000E+00
        126  traj.descent.throttle[path]                                     i   0.000000E+00             NAN    1.000000E+00        lu     0.00000E+00
        127  traj.descent.throttle[path]                                     i   0.000000E+00             NAN    1.000000E+00        lu     0.00000E+00
        128  traj.descent.throttle[path]                                     i   0.000000E+00             NAN    1.000000E+00        lu     0.00000E+00
        129  traj.descent.throttle[path]                                     i   0.000000E+00             NAN    1.000000E+00        lu     0.00000E+00
        130  traj.descent.throttle[path]                                     i   0.000000E+00             NAN    1.000000E+00        lu     0.00000E+00
        131  traj.descent.throttle[path]                                     i   0.000000E+00             NAN    1.000000E+00        lu     0.00000E+00


   Exit Status
      Inform  Description
         -13  Invalid Number Detected
--------------------------------------------------------------------------------
/home/runner/work/Aviary/Aviary/aviary/mission/height_energy/ode/required_thrust.py:58: RuntimeWarning: divide by zero encountered in divide
  thrust_required = drag + (altitude_rate * gravity / velocity + velocity_rate) * mass
/home/runner/work/Aviary/Aviary/aviary/mission/height_energy/ode/required_thrust.py:58: RuntimeWarning: invalid value encountered in divide
  thrust_required = drag + (altitude_rate * gravity / velocity + velocity_rate) * mass
/usr/share/miniconda/envs/test/lib/python3.12/site-packages/openmdao/vectors/default_vector.py:421: RuntimeWarning: invalid value encountered in add
  data[idxs] += val
-------------------------------
times_climb: [1572.] (s)
altitudes_climb: [10668.] (m)
masses_climb: [57152.63862] (kg)
ranges_climb: [296875.6] (m)
velocities_climb: [234.30259077] (m/s)
thrusts_climb: [53436.1467367] (N)
times_cruise: [1.] (s)
altitudes_cruise: [0.3048] (m)
masses_cruise: [1.] (kg)
ranges_cruise: [1.] (m)
velocities_cruise: [1.] (m/s)
thrusts_cruise: [4.44822162] (N)
times_descent: [1.] (s)
altitudes_descent: [0.3048] (m)
masses_descent: [1.] (kg)
ranges_descent: [1.] (m)
velocities_descent: [1.] (m/s)
thrusts_descent: [4.44822162] (N)
-------------------------------

This model demonstrates the flexibility of level 3. For example, we do not load the aircraft model from a .csv file but from a Python file using the get_flops_inputs() method.

This function not only reads Aviary and mission variables but also builds the engine. More information can be found in models/aircraft/advanced_single_aisle/advanced_single_aisle_data.py.

Note that we can read large single aisle aircraft inputs this way as well:

aviary_inputs = get_flops_inputs('LargeSingleAisle1FLOPS')
aviary_inputs = get_flops_inputs('LargeSingleAisle2FLOPS')
aviary_inputs = get_flops_inputs('LargeSingleAisle2FLOPSdw')
aviary_inputs = get_flops_inputs('LargeSingleAisle2FLOPSalt')

The data files are at:

aviary/models/aircraft/large_single_aisle_1/large_single_aisle_1_FLOPS_data.py
aviary/models/aircraft/large_single_aisle_2/large_single_aisle_2_FLOPS_data.py
aviary/models/aircraft/large_single_aisle_2/large_single_aisle_2_detailwing_FLOPS_data.py
aviary/models/aircraft/large_single_aisle_2/large_single_aisle_2_altwt_FLOPS_data.py

respectively.

Discussing this example in more detail#

We move all the code blocks on taxi to add_pre_mission_systems() function because it is how it is done in aviary/interface/methods_for_level2.py. Similarly, all the code blocks on landing are moved to add_post_mission_systems() function. Be careful! Generally speaking, not all components can be moved around due to the expected order of execution.

In aviary/validation_cases/benchmark_tests folder, there is another N3CC model test_FLOPS_based_sizing_N3CC.py. If we had started from that model, you would need to have an add_design_variables() function.

Summary#

We have shown in an example that users have total control over every aspect of the model in level 3. There is one big feature that hasn’t been covered: adding external subsystems. We will move on to discuss how to run Aviary External Subsystem next.