Skip to content

Commit

Permalink
Merge pull request #650 from jkirk5/units
Browse files Browse the repository at this point in the history
Added unit conversion capability to add_aviary_input, output, option
  • Loading branch information
jkirk5 authored Jan 31, 2025
2 parents 70a2ea5 + 290275d commit 7472c64
Show file tree
Hide file tree
Showing 3 changed files with 135 additions and 16 deletions.
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
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
63 changes: 63 additions & 0 deletions aviary/variable_info/test/test_functions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import unittest
import openmdao.api as om

from aviary.utils.develop_metadata import add_meta_data
from aviary.variable_info.functions import (
add_aviary_input,
add_aviary_output,
add_aviary_option,
)
from openmdao.utils.assert_utils import assert_near_equal


class InputOutputOptionTest(unittest.TestCase):
"""
Test the use of unit conversion when adding Aviary inputs, outputs, options
"""

def test_unit_conversion(self):
comp = DummyComp()
prob = om.Problem()
prob.model.add_subsystem('comp', comp, promotes=['*'])
prob.setup()
prob.run_model()
tol = 1e-4 # whether or not a unit is converted does not need high tol
assert_near_equal(43.0556, prob.get_val('area'), tolerance=tol)
assert_near_equal(9.84252, prob.get_val('length_out'), tolerance=tol)
assert_near_equal(5000, prob.get_val('mass'), tolerance=tol)
assert_near_equal(11.0231, prob.get_val('mass_out'), tolerance=tol)


class DummyComp(om.ExplicitComponent):
"""
Simple component to test unit conversion
"""

def initialize(self):
add_aviary_option(self, 'mass', units='lbm', meta_data=dummy_metadata)

def setup(self):
add_aviary_input(self, 'length', units='ft', meta_data=dummy_metadata)

add_aviary_output(self, 'area', units='ft**2', meta_data=dummy_metadata)
self.add_output('length_out', val=0)
add_aviary_output(self, 'mass', units='g', meta_data=dummy_metadata)
self.add_output('mass_out', val=0)

def compute(self, inputs, outputs):
mass = self.options['mass'][0] # should be 5 kg -> lbm
length = inputs['length'] # should be 3 m -> ft

# outputs['area'] should default to 4 m**2 -> ft**2
outputs['length_out'] = length # should return input 'length' in ft, NOT 3
# outputs['mass'] should default to 5 kg -> g
outputs['mass_out'] = mass # should return option 'mass' in lbm, NOT 5


dummy_metadata = {}
add_meta_data('mass', dummy_metadata, units='kg', default_value=5)
add_meta_data('length', dummy_metadata, units='m', default_value=3)
add_meta_data('area', dummy_metadata, units='m**2', default_value=4)

if __name__ == '__main__':
unittest.main()

0 comments on commit 7472c64

Please sign in to comment.