Skip to content

Commit 8ddd879

Browse files
authored
Merge branch 'OpenMDAO:main' into multi_mission_opt
2 parents 531c41f + 1b2867d commit 8ddd879

File tree

20 files changed

+142
-120
lines changed

20 files changed

+142
-120
lines changed

aviary/docs/misc_resources/comparison_to_flops.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,8 @@ complex than for a traditional tube-and-wing configuration.
9393

9494
FLOPS allows for the input of a maximum of two engine types, each with its own
9595
engine deck, whereas in Aviary an unlimited number of engine types may be
96-
defined. Aviary retains many of the relations used by FLOPS to scale engine mass and
96+
defined. This means that Aviary supports arbitrary numbers of heterogenous
97+
engines, each of which has unique characteristics and can use different methodologies for sizing and performance estimation. Aviary retains many of the relations used by FLOPS to scale engine mass and
9798
performance as a function of change in target thrust. Aviary introduces the
9899
capability to define these scaling relations on a per-engine-type basis, instead
99100
of using the same scaling relations for all engine types.
@@ -180,7 +181,7 @@ Since the detailed takeoff and landing trajectories are built up from modular
180181
sequences of phases, they are more flexible than the fixed sequence defined in
181182
FLOPS and can be used to model more complex operations. For example, a
182183
higher-fidelity aerodynamic model can be used, or rotation can be controlled
183-
using thrust vectoring.
184+
using thrust vectoring.
184185

185186
Aviary also includes the ability to calculate extended takeoff and landing
186187
profiles for use in noise analysis. This capability exists within the same set

aviary/docs/misc_resources/glossary.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,18 @@ design variable
3939
flight envelope
4040
The limits of speed, altitude, and load factor that an aircraft can safely operate within. Understanding the flight envelope is critical for aircraft design and operation.
4141
42+
homogeneous engines
43+
An individual engine or set of engines that all have identical performance characteristics.
44+
E.g. you might have four engines on an aircraft, but they are all assumed to have the same thrust, fuel consumption, etc.
45+
All engines in a homogenous set use the same `EngineModel` object in Aviary.
46+
See also; heterogeneous engines.
47+
48+
heterogeneous engines
49+
Multiple sets of homogenous engines, with each set having unique performance characteristics.
50+
This is useful if you are modeling an aircraft with engines of different sizes or types.
51+
Each set of unique engines uses a different `EngineModel` object in Aviary.
52+
See also; homogeneous engines.
53+
4254
mission analysis
4355
The process of determining the trajectory of an aircraft.
4456
This includes determining the aircraft's flight path, velocity, and acceleration as a function of time.

aviary/docs/user_guide/propulsion.ipynb

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,11 @@
1313
"\n",
1414
"Aviary supports an arbitrary number of propulsor models on a vehicle, each with their own unique properties such as performance characteristics, scaling behaviors, and number of propulsors for that given type. \n",
1515
"<!-- A diagram would be helpful here, showing X propulsors of type A and Y propulsors of type B on an outline of an aircraft -->\n",
16-
"Each unique type of engine is referred to as an engine model<!-- Link to wherever this class is described - theory guide? -->. In Aviary, an engine model contains information on how to size and estimate performance for a single instance of an engine of that type. During analysis, Aviary handles summing performance data to a system level. This way, information is available on the performance of both a single instance of an engine, as well as aircraft-level totals, for other Aviary subsystems to utilize."
16+
"Each unique type of engine is referred to as an engine model<!-- Link to wherever this class is described - theory guide? -->. In Aviary, an engine model contains information on how to size and estimate performance for a single instance of an engine of that type. During analysis, Aviary handles summing performance data to a system level. This way, information is available on the performance of both a single instance of an engine, as well as aircraft-level totals, for other Aviary subsystems to utilize.\n",
17+
"\n",
18+
"```{note}\n",
19+
"In Aviary, when we refer to multiple engines of the same type, we say they are homogenous. When you are dealing with multiple engines of different types, we say they are heterogeneous.\n",
20+
"``` "
1721
]
1822
},
1923
{
@@ -202,7 +206,7 @@
202206
"\n",
203207
"Engine models are defined in Aviary using an `EngineModel` object. An `EngineModel` is responsible for handling many tasks required to prepare an engine for use in Aviary, such as reading engine data from a file in the case of an `EngineDeck` (which is a child class of `EngineModel`). <!-- link to theory guide? -->\n",
204208
"\n",
205-
"An `EngineModel` (and classes inheriting it) can be manually created and added to the Aviary problem. This is extremely useful when setting up an aircraft with multiple engine types, each with unique properties, or using a custom engine model. An `EngineModel` requires an `AviaryValues` object containing the variables required for that engine (such as those outlined in the Beginner Guide example for `EngineDecks`)<!-- link to that subsection -->."
209+
"An `EngineModel` (and classes inheriting it) can be manually created and added to the Aviary problem. This is extremely useful when setting up an aircraft with multiple heterogenous types, each with unique properties, or using a custom engine model. An `EngineModel` requires an `AviaryValues` object containing the variables required for that engine (such as those outlined in the Beginner Guide example for `EngineDecks`)<!-- link to that subsection -->."
206210
]
207211
},
208212
{
@@ -278,7 +282,7 @@
278282
"id": "bbabdf30",
279283
"metadata": {},
280284
"source": [
281-
"In this example, *aviary_options* is modified in-place with updated values from *engine_model*, as well as properly configuring engine-related variables into vectors. When working with multiple engines, simply provide `preprocess_propulsion()` with a list of all `EngineModels`, like so:"
285+
"In this example, *aviary_options* is modified in-place with updated values from *engine_model*, as well as properly configuring engine-related variables into vectors. When working with multiple heterogenous engines, simply provide `preprocess_propulsion()` with a list of all `EngineModels`, like so:"
282286
]
283287
},
284288
{

aviary/examples/external_subsystems/battery/run_multiphase_mission.py

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
from aviary.interface.methods_for_level2 import AviaryProblem
2-
import os
3-
import pkg_resources
42

53
from aviary.examples.external_subsystems.battery.battery_builder import BatteryBuilder
6-
from aviary.examples.external_subsystems.battery.battery_variables import Aircraft
74
from aviary.examples.external_subsystems.battery.battery_variable_meta_data import ExtendedMetaData
85
from aviary.api import default_height_energy_phase_info as phase_info
6+
from aviary.utils.functions import get_aviary_resource_path
97

108

119
battery_builder = BatteryBuilder(include_constraints=False)
@@ -15,17 +13,13 @@
1513
phase_info['cruise']['external_subsystems'] = [battery_builder]
1614
phase_info['descent']['external_subsystems'] = [battery_builder]
1715

18-
up_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
19-
input_file = pkg_resources.resource_filename(
20-
"aviary", "models/test_aircraft/aircraft_for_bench_FwFm.csv")
21-
2216
prob = AviaryProblem()
2317

2418
# Load aircraft and options data from user
2519
# Allow for user overrides here
20+
input_file = get_aviary_resource_path('models/test_aircraft/aircraft_for_bench_FwFm.csv')
2621
prob.load_inputs(input_file, phase_info, meta_data=ExtendedMetaData)
2722

28-
2923
# Preprocess inputs
3024
prob.check_and_preprocess_inputs()
3125

aviary/examples/external_subsystems/engine_NPSS/table_engine_builder.py

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from aviary.examples.external_subsystems.engine_NPSS.engine_variable_meta_data import ExtendedMetaData
88
from aviary.examples.external_subsystems.engine_NPSS.engine_variables import Aircraft, Dynamic
99
from aviary.examples.external_subsystems.engine_NPSS.table_engine_connected_variables import vars_to_connect
10-
import pkg_resources
10+
from aviary.utils.functions import get_aviary_resource_path
1111
import os
1212
import numpy as np
1313

@@ -57,12 +57,9 @@ def build_mission(self, num_nodes, aviary_inputs):
5757
engine = om.MetaModelSemiStructuredComp(
5858
method=interp_method, extrapolate=True, vec_size=num_nodes, training_data_gradients=True)
5959

60-
csv_path = pkg_resources.resource_filename(
61-
"aviary",
62-
os.path.join("examples", "external_subsystems", "engine_NPSS",
63-
"NPSS_Model", "Output", "RefEngine.outputAviary")
64-
)
65-
60+
ref = os.path.join("examples", "external_subsystems", "engine_NPSS",
61+
"NPSS_Model", "Output", "RefEngine.outputAviary")
62+
csv_path = get_aviary_resource_path(ref)
6663
engine_data = np.genfromtxt(csv_path, skip_header=0)
6764

6865
# Sort the data by Mach, then altitude, then throttle

aviary/interface/download_models.py

Lines changed: 1 addition & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,8 @@
11
import os
22
from pathlib import Path
33
import argparse
4-
import pkg_resources
54
import shutil
6-
7-
8-
def aviary_resource(resource_name: str) -> str:
9-
return pkg_resources.resource_filename("aviary", resource_name)
10-
11-
12-
def get_model(file_name: str, verbose=False) -> Path:
13-
'''
14-
This function attempts to find the path to a file or folder in aviary/models
15-
If the path cannot be found in any of the locations, a FileNotFoundError is raised.
16-
17-
Parameters
18-
----------
19-
path : str or Path
20-
The input path, either as a string or a Path object.
21-
22-
Returns
23-
-------
24-
aviary_path
25-
The absolute path to the file.
26-
27-
Raises
28-
------
29-
FileNotFoundError
30-
If the path is not found.
31-
'''
32-
33-
# Get the path to Aviary's models
34-
path = Path('models', file_name)
35-
aviary_path = Path(aviary_resource(str(path)))
36-
37-
# If the file name was provided without a path, check in the subfolders
38-
if not aviary_path.exists():
39-
sub_dirs = [x[0] for x in os.walk(aviary_resource('models'))]
40-
for sub_dir in sub_dirs:
41-
temp_path = Path(sub_dir, file_name)
42-
if temp_path.exists():
43-
# only return the first matching file
44-
aviary_path = temp_path
45-
continue
46-
47-
# If the path still doesn't exist, raise an error.
48-
if not aviary_path.exists():
49-
raise FileNotFoundError(
50-
f"File or Folder not found in Aviary's hangar"
51-
)
52-
if verbose:
53-
print('found', aviary_path, '\n')
54-
return aviary_path
5+
from aviary.utils.functions import get_model
556

567

578
def save_file(aviary_path: Path, outdir: Path, verbose=False) -> Path:

aviary/interface/test/test_cmd_entry_points.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import unittest
33
from pathlib import Path
44

5-
import pkg_resources
5+
from aviary.utils.functions import get_aviary_resource_path
66
from openmdao.utils.testing_utils import require_pyoptsparse, use_tempdirs
77

88

@@ -18,7 +18,7 @@ def run_and_test_cmd(self, cmd):
1818
self.fail(f"Command '{cmd}' failed. Return code: {err.returncode}")
1919

2020
def get_file(self, filename):
21-
filepath = pkg_resources.resource_filename('aviary', filename)
21+
filepath = get_aviary_resource_path(filename)
2222
if not Path(filepath).exists():
2323
self.skipTest(f"couldn't find {filepath}")
2424
return filepath
@@ -50,34 +50,34 @@ def bench_test_phase_info_cmd(self):
5050

5151
class fortran_to_aviaryTestCases(CommandEntryPointsTestCases):
5252
def test_diff_configuration_conversion(self):
53-
filepath = pkg_resources.resource_filename('aviary',
54-
'models/test_aircraft/converter_configuration_test_data_GwGm.dat')
53+
filepath = get_aviary_resource_path(
54+
'models/test_aircraft/converter_configuration_test_data_GwGm.dat')
5555
outfile = Path.cwd() / 'test_aircraft/converter_configuration_test_data_GwGm' / 'output.dat'
5656
cmd = f'aviary fortran_to_aviary {filepath} -o {outfile} -l GASP'
5757
self.run_and_test_cmd(cmd)
5858

5959
def test_small_single_aisle_conversion(self):
60-
filepath = pkg_resources.resource_filename('aviary',
61-
'models/small_single_aisle/small_single_aisle_GwGm.dat')
60+
filepath = get_aviary_resource_path(
61+
'models/small_single_aisle/small_single_aisle_GwGm.dat')
6262
outfile = Path.cwd() / 'small_single_aisle' / 'output.dat'
6363
cmd = f'aviary fortran_to_aviary {filepath} -o {outfile} -l GASP'
6464
self.run_and_test_cmd(cmd)
6565

6666
def test_FLOPS_conversion(self):
67-
filepath = pkg_resources.resource_filename('aviary',
68-
'models/N3CC/N3CC_generic_low_speed_polars_FLOPSinp.txt')
67+
filepath = get_aviary_resource_path(
68+
'models/N3CC/N3CC_generic_low_speed_polars_FLOPSinp.txt')
6969
outfile = Path.cwd() / 'N3CC' / 'output.dat'
7070
cmd = f'aviary fortran_to_aviary {filepath} -o {outfile} -l FLOPS'
7171
self.run_and_test_cmd(cmd)
7272

7373
def test_force_conversion(self):
74-
filepath = pkg_resources.resource_filename('aviary',
75-
'models/test_aircraft/converter_configuration_test_data_GwGm.dat')
74+
filepath = get_aviary_resource_path(
75+
'models/test_aircraft/converter_configuration_test_data_GwGm.dat')
7676
outfile = Path.cwd() / 'output.dat'
7777
cmd1 = f'aviary fortran_to_aviary {filepath} -o {outfile} -l GASP'
7878
self.run_and_test_cmd(cmd1)
79-
filepath = pkg_resources.resource_filename('aviary',
80-
'models/test_aircraft/converter_configuration_test_data_GwGm.dat')
79+
filepath = get_aviary_resource_path(
80+
'models/test_aircraft/converter_configuration_test_data_GwGm.dat')
8181
cmd2 = f'aviary fortran_to_aviary {filepath} -o {outfile} --force -l GASP'
8282
self.run_and_test_cmd(cmd2)
8383

aviary/interface/test/test_download_models.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from pathlib import Path
44
import shutil
55

6-
import pkg_resources
6+
from aviary.utils.functions import get_aviary_resource_path
77
from openmdao.utils.testing_utils import use_tempdirs
88
from aviary.interface.download_models import get_model, save_file
99

@@ -54,12 +54,11 @@ def test_single_file_custom_outdir(self):
5454
shutil.rmtree(out_dir)
5555

5656
def test_expected_path(self):
57-
filename = f'test_aircraft/converter_configuration_test_data_GwGm.dat'
5857
aviary_path = get_model('converter_configuration_test_data_GwGm.dat')
5958

60-
expected_path = pkg_resources.resource_filename('aviary',
61-
'models/test_aircraft/converter_configuration_test_data_GwGm.dat')
62-
self.assertTrue(str(aviary_path) == expected_path)
59+
expected_path = get_aviary_resource_path(
60+
'models/test_aircraft/converter_configuration_test_data_GwGm.dat')
61+
self.assertTrue(str(aviary_path) == str(expected_path))
6362

6463

6564
if __name__ == "__main__":

aviary/interface/test/test_interface_bugs.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
from copy import deepcopy
2-
import pkg_resources
32
import unittest
43

54
import openmdao.api as om
@@ -9,6 +8,7 @@
98
from aviary.interface.default_phase_info.height_energy import phase_info as ph_in
109
from aviary.variable_info.variables import Aircraft
1110
from aviary.variable_info.enums import Verbosity
11+
from aviary.utils.functions import get_aviary_resource_path
1212
from openmdao.utils.testing_utils import use_tempdirs
1313

1414

@@ -70,13 +70,11 @@ def test_post_mission_promotion(self):
7070

7171
prob = AviaryProblem()
7272

73-
csv_path = pkg_resources.resource_filename(
74-
"aviary", "models/test_aircraft/aircraft_for_bench_GwFm.csv")
75-
73+
csv_path = get_aviary_resource_path(
74+
'models/test_aircraft/aircraft_for_bench_GwFm.csv')
7675
prob.load_inputs(csv_path, phase_info)
7776

78-
79-
# Preprocess inputs
77+
# Preprocess inputs
8078
prob.check_and_preprocess_inputs()
8179

8280
prob.add_pre_mission_systems()

aviary/mission/flight_phase_builder.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ def build_phase(self, aviary_options: AviaryValues = None, phase_type=EquationsO
175175
rate_targets = ['dh_dr']
176176
rate2_targets = ['d2h_dr2']
177177

178-
# For multi-engine cases, we may have throttle allocation control.
178+
# For heterogeneous-engine cases, we may have throttle allocation control.
179179
if phase_type is EquationsOfMotion.HEIGHT_ENERGY and num_engine_type > 1:
180180
allocation = user_options.get_val('throttle_allocation')
181181

aviary/subsystems/aerodynamics/flops_based/skin_friction.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ def setup(self):
4747
Aircraft.VerticalTail.NUM_TAILS, zero_count)
4848
num_fuselages, _ = aviary_options.get_item(
4949
Aircraft.Fuselage.NUM_FUSELAGES, zero_count)
50-
# TODO does not used vectorized multi-engine. Temp using single engine
50+
# TODO does not used vectorized heterogeneous engines. Temp using single engine
5151
num_engines, _ = aviary_options.get_item(
5252
Aircraft.Engine.NUM_ENGINES, zero_count)
5353
self.nc = nc = 2 + num_tails + num_fuselages + int(sum(num_engines))

aviary/subsystems/aerodynamics/gasp_based/test/test_table_based.py

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,14 @@
11
import unittest
2-
import os
32

43
import numpy as np
54
import openmdao
65
import openmdao.api as om
7-
import pkg_resources
6+
from aviary.utils.functions import get_aviary_resource_path
87
from openmdao.utils.assert_utils import assert_check_partials, assert_near_equal
98
from packaging import version
109

1110
from aviary.subsystems.aerodynamics.gasp_based.table_based import (
1211
TabularCruiseAero, TabularLowSpeedAero)
13-
from aviary.variable_info.options import get_option_defaults
1412
from aviary.variable_info.variables import Aircraft, Dynamic
1513

1614

@@ -52,8 +50,8 @@ def test_climb(self):
5250
"Older version of OpenMDAO does not properly skip Metamodel.")
5351
def test_cruise(self):
5452
prob = om.Problem()
55-
fp = pkg_resources.resource_filename(
56-
"aviary", f"subsystems/aerodynamics/gasp_based/data/large_single_aisle_1_aero_free.txt")
53+
ref = 'subsystems/aerodynamics/gasp_based/data/large_single_aisle_1_aero_free.txt'
54+
fp = get_aviary_resource_path(ref)
5755
prob.model = TabularCruiseAero(num_nodes=2, aero_data=fp)
5856
prob.setup(force_alloc_complex=True)
5957

@@ -81,12 +79,12 @@ class TestLowSpeedAero(unittest.TestCase):
8179
# takeoff flap deflection (deg)
8280
flap_defl_to = 10
8381

84-
free_data = pkg_resources.resource_filename(
85-
"aviary", f"subsystems/aerodynamics/gasp_based/data/large_single_aisle_1_aero_free.txt")
86-
flaps_data = pkg_resources.resource_filename(
87-
"aviary", f"subsystems/aerodynamics/gasp_based/data/large_single_aisle_1_aero_flaps.txt")
88-
ground_data = pkg_resources.resource_filename(
89-
"aviary", f"subsystems/aerodynamics/gasp_based/data/large_single_aisle_1_aero_ground.txt")
82+
free_data = get_aviary_resource_path(
83+
"subsystems/aerodynamics/gasp_based/data/large_single_aisle_1_aero_free.txt")
84+
flaps_data = get_aviary_resource_path(
85+
"subsystems/aerodynamics/gasp_based/data/large_single_aisle_1_aero_flaps.txt")
86+
ground_data = get_aviary_resource_path(
87+
"subsystems/aerodynamics/gasp_based/data/large_single_aisle_1_aero_ground.txt")
9088

9189
@unittest.skipIf(version.parse(openmdao.__version__) < version.parse("3.26"),
9290
"Older version of OpenMDAO does not properly skip Metamodel.")

aviary/subsystems/mass/flops_based/landing_gear.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,7 @@ def setup_partials(self):
299299

300300
def compute(self, inputs, outputs, discrete_inputs=None, discrete_outputs=None):
301301
options: AviaryValues = self.options['aviary_options']
302-
# TODO temp using first engine, multi-engine not supported
302+
# TODO temp using first engine, heterogeneous engines not supported
303303
num_eng = options.get_val(Aircraft.Engine.NUM_ENGINES)[0]
304304
num_wing_eng = options.get_val(Aircraft.Engine.NUM_WING_ENGINES)[0]
305305

@@ -339,7 +339,7 @@ def compute(self, inputs, outputs, discrete_inputs=None, discrete_outputs=None):
339339

340340
def compute_partials(self, inputs, partials, discrete_inputs=None):
341341
options: AviaryValues = self.options['aviary_options']
342-
# TODO temp using first engine, multi-engine not supported
342+
# TODO temp using first engine, heterogeneous engines not supported
343343
num_eng = options.get_val(Aircraft.Engine.NUM_ENGINES)[0]
344344
num_wing_eng = options.get_val(Aircraft.Engine.NUM_WING_ENGINES)[0]
345345

aviary/subsystems/mass/flops_based/misc_engine.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ class EngineMiscMass(om.ExplicitComponent):
1616
Calculates total sum of all misc engine mass on the aircraft
1717
1818
Currently using engine-level additional mass (scaled by num_engines)
19-
and propulsion-level starter and controls mass, not multi-engine safe!
19+
and propulsion-level starter and controls mass, not heterogeneous engine safe!
2020
'''
2121

2222
def initialize(self):

0 commit comments

Comments
 (0)