Specifying Units for Variables#
As we saw in Declaring Continuous Variables, we can specify units for inputs, outputs, and residuals. There is a units
argument to add_input
to specify input units, and there are units
and res_units
arguments on add_output
to specify output and residual units, respectively. A complete listing of all available units is given here.
Note
Residual units, if not specified, default to the same units as the output variable. res_units
is very rarely specified.
Specifying units has the following result:
Unit conversions occur during data passing. For instance, let’s say we have a
TimeComp
component that outputstime1
in hours and aSpeedComp
component takestime2
as an input but in seconds. If we connectTimeComp.time1
toSpeedComp.time2
with hours/seconds specified during the correspondingadd_output
/add_input
calls, then OpenMDAO automatically converts from hours to seconds.The user always gets/sets the variable in the specified units. Declaring an input, output, or residual to have certain units means that any value ‘set’ into the variable is assumed to be in the given units and any time the user asks to ‘get’ the variable, the value is return in the given units. This is the case not only in <Component> methods such as
compute
,apply_nonlinear
, andapply_linear
, but everywhere, including the user’s run script.In
add_input
andadd_output
, all arguments are assumed to be given in the specified units. In the case ofadd_input
, ifunits
is specified, thenval
is assumed to be given in those units. In the case ofadd_output
, ifunits
is specified, thenval
,lower
,upper
,ref
, andref0
are all assumed to be given in those units. Also inadd_output
, ifres_units
is specified, thenres_ref
is assumed to be given inres_units
.
Units syntax#
Units are specified as a string that adheres to the following syntax. The string is a composition of numbers and known units that are combined with multiplication (*), division (/), and exponentiation (**) operators. The known units can be prefixed by kilo (k), Mega (M), and so on. The list of units and valid prefixes can be found in the units library.
For example, each of the following is a valid unit string representing the same quantity:
N
0.224809 * lbf
kg * m / s ** 2
kg * m * s ** -2
kkg * mm / s ** 2
Note
If units are not specified, or are specified as None
then the variable is assumed to be unitless. If such a variable is connected to a variable with units, the connection will be allowed, but a warning will be issued.
Example#
This example illustrates how we can compute speed from distance and time given in km
and h
using a component that computes speed using m
and s
.
We first define the component.
import openmdao.api as om
class SpeedComp(om.ExplicitComponent):
"""Simple speed computation from distance and time with unit conversations."""
def setup(self):
self.add_input('distance', val=1.0, units='km')
self.add_input('time', val=1.0, units='h')
self.add_output('speed', val=1.0, units='km/h')
def compute(self, inputs, outputs):
outputs['speed'] = inputs['distance'] / inputs['time']
In the overall problem, the first component, c1
, defines distance and time in m
and s
. OpenMDAO handles the unit conversions when passing these two variables into c2
, our ‘SpeedComp’. There is a further unit conversion from c2
to c3
since speed must be converted now to m/s
.
import openmdao.api as om
from openmdao.core.tests.test_units import SpeedComp
prob = om.Problem()
prob.model.add_subsystem('c1', SpeedComp())
prob.model.add_subsystem('c2', om.ExecComp('f=speed',speed={'units': 'm/s'}))
prob.model.set_input_defaults('c1.distance', val=1., units='m')
prob.model.set_input_defaults('c1.time', val=1., units='s')
prob.model.connect('c1.speed', 'c2.speed')
prob.setup()
prob.run_model()
print(prob.get_val('c1.distance')) # units: km
[0.001]
print(prob.get_val('c1.time')) # units: h
[0.00027778]
print(prob.get_val('c1.speed')) # units: km/h
[3.6]
print(prob.get_val('c2.f')) # units: m/s
[1.]