Skip to content

Commit

Permalink
Merge branch 'main' into glue_getting_started
Browse files Browse the repository at this point in the history
  • Loading branch information
xjjiang authored Jan 31, 2025
2 parents b596152 + 7472c64 commit 8bd9b68
Show file tree
Hide file tree
Showing 9 changed files with 168 additions and 55 deletions.
2 changes: 1 addition & 1 deletion aviary/docs/developer_guide/doctape_examples.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -421,7 +421,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"If you want to glue the name of a variable, instead of the value that variable holds, you can use the {glue:md}`get_variable_name` to extract it.\n",
"If you want to glue the name of a variable, instead of the value that variable holds, you can use the {glue:md}`get_variable_name` function to extract it.\n",
"\n",
"For example:\n",
"Using {glue:md}`var_value_code` will result in {glue:md}`value`, whereas using {glue:md}`var_name_code` will result in {glue:md}`Aircraft.Design.EMPTY_MASS`\n",
Expand Down
5 changes: 3 additions & 2 deletions aviary/docs/getting_started/onboarding_level1.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -228,9 +228,10 @@
"source": [
"Let us discuss these in more detail:\n",
"\n",
"- {glue:md}`-o --outdir`: Use specified directory to write output. The default is the current directory.\n",
"- {glue:md}`--optimizer`: Name of optimizer. Choices are: {glue:md}`SNOPT`, {glue:md}`IPOPT`, {glue:md}`SLSQP`, and `None`. The default is {glue:md}`SNOPT`. If optimizer is `None`, it will be set to {glue:md}`IPOPT` or {glue:md}`SNOPT` depending on the analysis scheme. The optimization objective is fuel burn for level 1 runs. The objective is\n",
" - `mission:objectives:fuel` if `mission_method` is `GASP` \n",
" - `fuel_burned` if `mission_method` is `FLOPS`.\n",
"\n",
"- {glue:md}`--optimizer`: Name of optimizer. Choices are: {glue:md}`SNOPT`, {glue:md}`IPOPT`, {glue:md}`SLSQP`, and `None`. The default is {glue:md}`SNOPT`. If optimizer is `None`, it will be set to {glue:md}`IPOPT` or {glue:md}`SNOPT` depending on the analysis scheme. \n",
"- {glue:md}`--phase_info`: Path to phase info file. If not provided, it is {glue:md}`default_phase_info/two_dof.py` if mission origin is {glue:md}`TWO_DEGREES_OF_FREEDOM` and {glue:md}`default_phase_info/height_energy.py` for `simple`.\n",
"\n",
"- {glue:md}`--max_iter`: Maximum number of iterations. Default is {glue:md}`num_max_iter`.\n",
Expand Down
42 changes: 12 additions & 30 deletions aviary/interface/methods_for_level1.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
"""
This file contains functions needed to run Aviary using the Level 1 interface.
"""
import os

from importlib.machinery import SourceFileLoader
from pathlib import Path

import openmdao.api as om
from aviary.variable_info.enums import AnalysisScheme, Verbosity
from aviary.interface.methods_for_level2 import AviaryProblem
from aviary.utils.functions import get_path
Expand Down Expand Up @@ -110,7 +109,6 @@ def run_aviary(aircraft_filename, phase_info, optimizer=None,

def run_level_1(
input_deck,
outdir='output',
optimizer='SNOPT',
phase_info=None,
max_iter=50,
Expand Down Expand Up @@ -144,32 +142,23 @@ def run_level_1(

prob = run_aviary(input_deck, phase_info, **kwargs)

# update n2 diagram after run.
outfile = os.path.join(outdir, "n2.html")
if outdir != '':
os.makedirs(outdir, exist_ok=True)
om.n2(
prob,
outfile=outfile,
show_browser=False,
)

return prob


def _setup_level1_parser(parser):
def_outdir = os.path.join(os.getcwd(), "output")
parser.add_argument('input_deck', metavar='indeck', type=str,
nargs=1, help='Name of vehicle input deck file')
parser.add_argument(
"-o", "--outdir", default=def_outdir, help="Directory to write outputs"
'input_deck',
metavar='indeck',
type=str,
nargs=1,
help='Name of vehicle input deck file',
)
parser.add_argument(
"--optimizer",
type=str,
default='SNOPT',
default='IPOPT',
help="Name of optimizer",
choices=("SNOPT", "IPOPT", "SLSQP", "None")
choices=("SNOPT", "IPOPT", "SLSQP", "None"),
)
parser.add_argument(
"--phase_info",
Expand All @@ -178,10 +167,8 @@ def _setup_level1_parser(parser):
help="Path to phase info file"
)
parser.add_argument(
"--max_iter",
type=int,
default=50,
help="maximum number of iterations")
"--max_iter", type=int, default=50, help="maximum number of iterations"
)
parser.add_argument(
"--shooting",
action="store_true",
Expand All @@ -192,7 +179,8 @@ def _setup_level1_parser(parser):
type=int,
default=1,
help="verbosity settings: 0=quiet, 1=brief, 2=verbose, 3=debug",
choices=(0, 1, 2, 3))
choices=(0, 1, 2, 3),
)


def _exec_level1(args, user_args):
Expand All @@ -208,14 +196,8 @@ def _exec_level1(args, user_args):
if isinstance(args.input_deck, list):
args.input_deck = args.input_deck[0]

if args.outdir == os.path.join(os.getcwd(), "output"):
# if default outdir, add the input deck name
file_name_stem = Path(args.input_deck).stem
args.outdir = args.outdir + os.sep + file_name_stem

prob = run_level_1(
input_deck=args.input_deck,
outdir=args.outdir,
optimizer=args.optimizer,
phase_info=args.phase_info,
max_iter=args.max_iter,
Expand Down
10 changes: 10 additions & 0 deletions aviary/interface/methods_for_level2.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import sys
import json
import enum
import os

import numpy as np

Expand Down Expand Up @@ -2541,6 +2542,15 @@ def run_aviary_problem(self, record_filename="problem_history.db",
failed = self.run_model()
warnings.filterwarnings('default', category=UserWarning)

# update n2 diagram after run.
outdir = Path(self.get_reports_dir(force=True))
outfile = os.path.join(outdir, "n2.html")
om.n2(
self,
outfile=outfile,
show_browser=False,
)

if self.aviary_inputs.get_val(Settings.VERBOSITY).value >= 2:
with open('output_list.txt', 'w') as outfile:
self.model.list_outputs(out_stream=outfile)
Expand Down
6 changes: 0 additions & 6 deletions aviary/interface/test/test_cmd_entry_points.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,6 @@ def bench_test_IPOPT_cmd(self):
cmd = 'aviary run_mission models/test_aircraft/aircraft_for_bench_GwGm.csv --optimizer IPOPT --max_iter 1'
self.run_and_test_cmd(cmd)

@require_pyoptsparse(optimizer="IPOPT")
def bench_test_IPOPT_cmd(self):
cmd = 'aviary run_mission models/test_aircraft/aircraft_for_bench_GwGm.csv' \
' --optimizer IPOPT --max_iter 1 --shooting'
self.run_and_test_cmd(cmd)

@require_pyoptsparse(optimizer="IPOPT")
def bench_test_phase_info_cmd(self):
cmd = 'aviary run_mission models/test_aircraft/aircraft_for_bench_GwGm.csv --optimizer IPOPT --max_iter 1' \
Expand Down
14 changes: 9 additions & 5 deletions aviary/subsystems/mass/gasp_based/design_load.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ def setup(self):

if self.options[Aircraft.Design.PART25_STRUCTURAL_CATEGORY] < 3:

add_aviary_input(self, Aircraft.Wing.LOADING, val=128)
add_aviary_input(self, Aircraft.Wing.LOADING, val=128, units='lbf/ft**2')

self.add_output("max_airspeed", val=0, units="kn",
desc="VM0: maximum operating equivalent airspeed",
Expand Down Expand Up @@ -634,7 +634,9 @@ def setup(self):
add_aviary_input(self, Aircraft.Wing.SWEEP, val=0.436, units="rad")
add_aviary_input(self, Mission.Design.MACH, val=0.8)

add_aviary_output(self, Aircraft.Design.LIFT_CURVE_SLOPE, val=7.1765)
add_aviary_output(
self, Aircraft.Design.LIFT_CURVE_SLOPE, val=7.1765, units='1/rad'
)

self.declare_partials(Aircraft.Design.LIFT_CURVE_SLOPE, "*")

Expand Down Expand Up @@ -673,7 +675,7 @@ def initialize(self):

def setup(self):

add_aviary_input(self, Aircraft.Wing.LOADING, val=128)
add_aviary_input(self, Aircraft.Wing.LOADING, val=128, units='lbf/ft**2')

self.add_input(
"density_ratio",
Expand All @@ -695,8 +697,10 @@ def setup(self):
desc="EMLF: maximum maneuver load factor, units are in g`s",
)

add_aviary_input(self, Aircraft.Wing.AVERAGE_CHORD, val=12.6131)
add_aviary_input(self, Aircraft.Design.LIFT_CURVE_SLOPE, val=7.1765)
add_aviary_input(self, Aircraft.Wing.AVERAGE_CHORD, val=12.6131, units='ft')
add_aviary_input(
self, Aircraft.Design.LIFT_CURVE_SLOPE, val=7.1765, units='1/rad'
)

add_aviary_output(self, Aircraft.Wing.ULTIMATE_LOAD_FACTOR, val=3.5)

Expand Down
7 changes: 7 additions & 0 deletions aviary/utils/test/test_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from aviary.variable_info.options import get_option_defaults
from aviary.variable_info.variables import Aircraft, Mission
from aviary.utils.functions import add_opts2vals, create_opts2vals, get_path
from aviary.api import top_dir


class TestOpts2Vals(unittest.TestCase):
Expand Down Expand Up @@ -104,5 +105,11 @@ def test_non_existent_path(self):
get_path('nonexistentfile.txt')


class TestTopDir(unittest.TestCase):
def test_top_dir(self):
result = Path(__file__).parent.parent.parent
self.assertEqual(result, top_dir)


if __name__ == "__main__":
unittest.main()
74 changes: 63 additions & 11 deletions aviary/variable_info/functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ def add_aviary_input(comp, varname, val=None, units=None, desc=None, shape_by_co
val: float or ndarray
Default value for variable.
units: str
(Optional) when speficying val, units should also be specified.
(Optional) when specifying val, units should also be specified.
desc: str
(Optional) description text for the variable.
shape_by_conn: bool
Expand All @@ -47,13 +47,16 @@ def add_aviary_input(comp, varname, val=None, units=None, desc=None, shape_by_co
(Optional) shape for this input.
"""
meta = meta_data[varname]
# units of None are overwritten with defaults. Overwriting units with None is
# unnecessary as it will cause errors down the line if the default is not already
# None
default_units = meta['units']

if units:
input_units = units
else:
# units of None are overwritten with defaults. Overwriting units with None is
# unecessary as it will cause errors down the line if the default is not already
# None
input_units = meta['units']
input_units = default_units

if desc:
input_desc = desc
else:
Expand All @@ -69,6 +72,18 @@ def add_aviary_input(comp, varname, val=None, units=None, desc=None, shape_by_co
val = np.zeros(shape)
else:
val = np.ones(shape) * val

# val was not provided but different units were
if input_units != default_units:
try:
# convert the default units to requested units
val = convert_units(val, default_units, input_units)
except ValueError:
raise ValueError(
f'The requested units {units} for input {varname} in component '
f'{comp.name} are invalid.'
)

comp.add_input(varname, val=val, units=input_units,
desc=input_desc, shape_by_conn=shape_by_conn, shape=shape)

Expand Down Expand Up @@ -104,13 +119,16 @@ def add_aviary_output(comp, varname, val=None, units=None, desc=None, shape_by_c
(Optional) shape for this input.
"""
meta = meta_data[varname]
# units of None are overwritten with defaults. Overwriting units with None is
# unnecessary as it will cause errors down the line if the default is not already
# None
default_units = meta['units']

if units:
output_units = units
else:
# units of None are overwritten with defaults. Overwriting units with None is
# unecessary as it will cause errors down the line if the default is not already
# None
output_units = meta['units']
output_units = default_units

if desc:
output_desc = desc
else:
Expand All @@ -126,6 +144,18 @@ def add_aviary_output(comp, varname, val=None, units=None, desc=None, shape_by_c
val = np.zeros(shape)
else:
val = np.ones(shape) * val

# val was not provided but different units were
if output_units != default_units:
try:
# convert the default units to requested units
val = convert_units(val, default_units, output_units)
except ValueError:
raise ValueError(
f'The requested units {units} for output {varname} in component '
f'{comp.name} are invalid.'
)

comp.add_output(varname, val=val, units=output_units,
desc=output_desc, shape_by_conn=shape_by_conn)

Expand Down Expand Up @@ -226,10 +256,18 @@ def add_aviary_option(comp, name, val=_unspecified, units=None, desc=None, meta_
be used.
"""
meta = meta_data[name]
# units of None are overwritten with defaults. Overwriting units with None is
# unnecessary as it will cause errors down the line if the default is not already
# None
default_units = meta['units']

if units:
option_units = units
else:
option_units = default_units

if not desc:
desc = meta['desc']
if val is _unspecified:
val = meta['default_value']

types = meta['types']
if meta['multivalue']:
Expand All @@ -238,6 +276,20 @@ def add_aviary_option(comp, name, val=_unspecified, units=None, desc=None, meta_
else:
types = (list, types)

if val is _unspecified:
val = meta['default_value']

# val was not provided but different units were
if option_units != default_units:
try:
# convert the default units to requested units
val = convert_units(val, default_units, option_units)
except ValueError:
raise ValueError(
f'The requested units {units} for output {name} in component '
f'{comp.name} are invalid.'
)

if units not in [None, 'unitless']:
types = tuple
comp.options.declare(name, default=(val, units),
Expand Down
Loading

0 comments on commit 8bd9b68

Please sign in to comment.