Scimath Units User Reference

Internal Representation

Internally, a scimath unit is a unit object:

class unit(value, derivation)
value

a scalar quantity which holds the magnitude of the unit, relative to the derivation in SI units.

derivation

a 7-tuple holding the power of each fundamental quantity in the unit: (length, mass, time, electrical current, temperature, amount of substance, luminous intensity). The labels of the fundamental quantities are given in the attribute _labels=(‘m’, ‘kg’, ‘s’, ‘A’, ‘K’, ‘mol’, ‘cd’)

label

the display name of the unit.

For example, the predefined unit Newton has the following attributes:

>>> from scimath.units.force import newton, lbf
>>> newton.value
1.0
>>> newton.derivation
(1, 1, -2, 0, 0, 0, 0)
>>> newton.label
'newton'
>>> lbf.value
4.44822
>>> lbf.derivation
(1, 1, -2, 0, 0, 0, 0)

Limited API reference

Convert

scimath.units.convert.convert(value, from_unit, to_unit)[source]

Coverts value from one unit to another.

value : float
value to convert
from_unit : scimath.unit object
implied units of ‘value’
to_unit : scimath.unit object
implied units of the returned float

value * conversion_factor + offset : data type is the same as was passed in.

Checks first to see if from_unit and to_unit are equal and passes value back in that case. Then conver() forms a conversion factor by dividing the units. The offset is zero unless explicitly set otherwise in the unit definition. Handling of UnitArrays is done by checking whether value is a numpy.ndarray.

Note: Enthought has extended the original units implementation to handle temperature conversions. Temperature units are a special case because they can have a different origin.

This causes a fundamental ambiguity. What behavior do we expect when we convert temperature?

Option #1 When we convert 0 degrees centigrade we get 32 fahrenheit.

Option #2 When we convert a temperature difference of 0 degrees centigrade we get a temperature difference of 0 degrees fahrenheit.

By convention we have made the units system behave like in Option #1 so that convert() handles absolute temperatures, not temperature differences.

HasUnits

scimath.units.has_units.has_units(func=None, summary='', doc='', inputs=None, outputs=None)[source]

Function decorator: Wrap a standard python function for unit conversion. Note that conversion arguments must be supplied through the decorator aguments or in a formatted docstring as shown below.

func : function, optional
The function to wrap. Usually, has_units will be used as a bare “@has_units” decorator, so this argument will usually be supplied by the interpreter as it interprets that syntax.
summary : str, optional
A short string describing the function.
doc : str, optional
A longer string describing the function in detail.
inputs : str, optional

A string containing information about unit conversion, etc. for arguments in the function. The argument names in this string must match those in the python signature. The order the arguments are specified in does not matter as the order of arguments from the wrapped function is always used. The format of the string is as follows:

"a: notes on a:units=m/s;b: notes on b:units=m/s"

That is, information about each argument is separated by a semi-colon (‘;’). Each argument has three fields that are separated by colons (‘:’). The first is the name of the variable. The 2nd is a string. The 3rd specified the units. Other fields may be added later.

outputs : str, optional
A string with the same format as the ‘inputs’ string that specifies the output variables. This is an ordered list as there is no way to determine the functions outputs from the function object.

This decorator adds a unit conversion step to input and output variables of a python function. The resulting function can be used as a standard python function and has an identical calling signature to the wrapped function. If passed standard scalars and arrays as input, it will behave identically. If “unitted” objects, such as UnitArray, are handed in, however, they will be unit converted from their given units to the units expected by the function. In this case, output variables are converted from arrays or scalars to UnitArray or UnitScalar so that the results carry the units with them. Note that these objects are derived from standard numpy.ndarray and float objects, so they will behave exactly like them. The only caveat to this is that you should use isinstance(value, ndarray) instead of “type(array) is ndarray” when testing for their type, but you should be doing that anyways.

Regardless of whether the inputs have units or not, the actual values passed to the function will be stripped of units. The wrapped function will only deal with regular numpy arrays and scalars.

If units are not assigned to a variable, absolutely no conversion is applied to that variable.

Example definition of a unitted addition function:

>>> from scimath.units.api import has_units, UnitArray
>>> @has_units
... def add(a,b):
...     ''' Add two arrays in ft and convert them to m.
...
...         Parameters
...         ----------
...         a : array : units=ft
...             An array
...         b : array : units=ft
...             Another array
...
...         Returns
...         -------
...         c : array : units=m
...             c = a + b
...     '''
...     return (a+b)*0.3048

>>> from numpy import array
>>> a = array((1,2,3))
>>> add(a,a) 
array([...0.6096, 1.2192, 1.8288])

>>> from scimath.units.length import m
>>> a = UnitArray((1,2,3), units=m)
>>> add(a,a) 
UnitArray([...2., 4., 6.], units='1.0*m')
>>> add(a,a).units
1.0*m

Alternatively, parameter information can be specified in the decorator:

>>> from numpy import array
>>> from scimath.units.api import has_units
>>> @has_units(inputs="a:an array:units=ft;b:array:units=ft",
...            outputs="result:an array:units=m")
... def add(a,b):
...     " Add two arrays in ft and convert them to m. "
...     return (a+b)*0.3048

The returned function has several attributes attached to it:

summary: A short description of the function. This is taken from the
‘summary’ argument to the decorator.
doc: A longer description of the function. This is taken from the
‘doc’ argument to the decorator.
inputs: A list of Variable objects, for each argument to the function
in the order they are defined in the python function signature. If you did not supply any information about the argument in “inputs”, then a default Variable object is created.
outputs: A list of Variable objects, for each output of the function
in the order they you specify them in the “outputs” variable in the argument list.

UnitScalar

scimath.units.unit_scalar.UnitScalar(data, dtype=None, copy=True, units=None)[source]

Scalars with units.

>>> from scimath.units.length import cm
>>> x = UnitScalar(5, units=cm)
>>> x, x.units
(UnitScalar(5, units='0.01*m'), 0.01*m)
>>> x**2, (x**2).units
(UnitScalar(25, units='0.0001*m**2'), 0.0001*m**2)

UnitArray

scimath.units.unit_scalar.UnitArray(data, dtype=None, copy=True, units=None)[source]

Define a UnitArray that subclasses from a Numpy array

This class simply adds a “units” object to a numpy array. It deals with unit adjustments involving basic arithmetic and comparison operations between arrays with units. If incompatible units are used, an error will be raised.

Note that the “units” may hold some amount of the magnitude, which can be particularly significant for dimensionless quantities.

You should always convert a UnitArray to the required units before extracting the numerical value.

This code is shamelessly copied from the numpy matrix class. It is of course modified to suite our purposes.