# Source code for scimath.units.convert

```# (C) Copyright 2005-2021 Enthought, Inc., Austin, TX
#
# This software is provided without warranty under the terms of the BSD
# is also available online at http://www.enthought.com/licenses/BSD.txt
#
# Thanks for using Enthought open source!

""" Defines the convert, convert_str and parser function
"""

import numpy
from .unit import InvalidConversion

#####################################################################
# Definitions:
#####################################################################

[docs]def convert(value, from_unit, to_unit):
""" Coverts value from one unit to another.

Parameters
----------
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

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

Description
-----------

Checks first to see if from_unit and to_unit are equal and passes value
back in that case. Then convert() 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.
"""

# TODO: it would be nice if this function could handle inversion as well as
# just scaling and also gave better error messages. For example:
'''
res = from_unit / to_unit

if  from_unit / to_unit is dimensionless:
# straight convert\
pass
elif  from_unit * to_unit is dimensionless:
# invert
pass
else:
# dimensionally incompatible
pass
'''

# TODO: This is my guess at what Lowell did to improve performance...
# I need to confirm that I'm doing the same thing he did (TNV)

if from_unit == to_unit:
return value
else:
try:
# try a straight conversion
factor = float(from_unit / to_unit)
except InvalidConversion as ex:
# try an inversion
factor = from_unit * to_unit
if not isinstance(factor, float):
raise ex
try:
offset = (from_unit.offset * factor) - to_unit.offset
except AttributeError:
offset = 0.0

# test if it is a UnitArray without importing UnitArray to keep
# the dependencies low for this module
if isinstance(value, numpy.ndarray) and hasattr(value, 'units'):
return value * factor + value.units * offset

return value * factor + offset

def convert_str(value, from_unit_string, to_unit_string):
""" Convert functions to take in strings and conveniently parse them to
units, to return the conversion factor

Parameters
----------
from_unit_string: Str
String representing from_unit, eg. 'm'
to_unit_string: Str
String representing to_unit, eg. 'ft'

Returns:
--------
return_value: Float
Conversion factor between the from_unit and the to_unit
"""
from scimath.units import unit_parser
return convert(value,
unit_parser.unit_parser.parse_unit(from_unit_string),
unit_parser.unit_parser.parse_unit(to_unit_string))

# Function to return a parser instance

def parser():
from scimath.units import unit_parser
return unit_parser.unit_parser()
```