The Units library provides a means of working with units of measurement at runtime, including conversion to and from strings. It provides a small number of types for working with units and measurements and operations necessary for user input and output with units. For additional description and discussion see Readme. The Python library is a wrapper around the C++ library using nanobind.
A units library was needed to be able to represent units from a wide range of disciplines and be able to separate them from the numerical values for use in calculations when needed. The main drivers are
- converting units, often represented by strings, to a standardized unit set when dealing with user input and output.
- Being able to use the unit as a singular type that could contain any unit, and not introduce a huge number of types to represent all possible units.
- Being able to associate a completely arbitrary unit given by users with a generic interface and support conversions between those user defined units and other units.
- The library has its origins in power systems so support for per-unit operations was also lacking in the alternatives.
The python wrapper around the library is mainly intended to be able to handle various string representations and easily handle conversions, along with some support for commodities and packaging. As it is being developed the Python library will maintain compatibility with quantity-dev.
The primary use case for the library is string operations and conversion. For example if you have a library that does some computations with physical units. In the library code itself the units are standardized and well defined. For example take a velocity, internally everything is in meters per second, but there is a configuration file that takes in the initial data and you would like to broadly support different units on the input
from units_llnl import Unit
u1 = Unit("m")
u2 = Unit("cm")
v1 = u1.convert(10, u2)
assert v1 == 10 * 100
v2 = u1.convert(unit_out=u2, value=20)
assert v2 == 2000from units_llnl import Measurement
m1 = Measurement("10 m")
m2 = Measurement("2.5 s")
m3 = m1 / m2
m4 = Measurement("4.0 m/s")
assert m3 == m4If you want to try out the string conversion components. There is server running that can do the string conversions
For more details see the documentation
These operations apply the Units object in Python. It maps to a precise_unit in C++. The Unit object is immutable like a python string so a new one is created for methods that modify the unit in some way.
Unit(unit_str:str)construct from a stringUnit(unit_str:str,commodity_str:str)construct a unit from a unit string and commodity stringUnit(float multiplier, unit:Unit)construct a unit using another unit as a base along with a multiplier
is_exactly_the_same(other:Unit)->boolcompare two units and check for exact equivalence in both the unit_data and the multiplier.has_same_base(other:Unit)->boolcheck if the units have the same base units.equivalent_non_counting(other:Unit)->boolcheck if the units are equivalent ignoring the counting bases.is_convertible_to(other:Unit)->boolcheck if the units are convertible to each other, currently checksequivalent_non_counting(), but some additional conditions might be allowed in the future to better match convert.convert(value:float,unit_out:Unit|str)->floatconvert a value from the existing unit to another, can also be a string.is_per_unit()->booltrue if the unit has the per_unit flag active.is_equation()->booltrue if the unit has the equation flag active.is_valid()->booltrue if the unit is a valid unit.is_normal()->booltrue if the unit is a normal unit (not error, nan, or subnormal).is_error()->booltrue if the unit is an error unit (e.g invalid conversion).isfinite()->booltrue if the unit does not have an infinite multiplier.isinf()->booltrue if the unit does have an infinite multiplier.root(power:int)->Unitreturn a new unit taken to the root power.sqrt()->Unitreturns a new unit which is the square root of the current unit.set_multiplier(mult:float)->Unitgenerate a new Unit with the set multiplier.set_commodity(int commodity)generate a new unit with the assigned commodity.
multiplier->floatreturn the unit multiplier as a floating point numbercommodity->strget the commodity of the unitbase_units->Unit gets the base units (no multiplier) associated with a unit
*,/with other units produces a new unit~produces the inverse of the unit**is an exponentiation operator and produces a new unit*,/with a floating point generates aMeasurement==and!=produce the appropriate comparison operators- f string formatting also works with units and returns the string representation of the unit. This string is guaranteed to produce the same unit as the current unit, but may not be the same string as was used to create it.
str,boolare defined,boolindicates that the unit is valid, and non-zeroUnitsmay also be used as the indexing element in a dictionary
Measurement(measurement_str:str)construct from a stringMeasurement(value:float, unit:Unit|str)construct aMeasurementfrom a value and aUnitor string representing aUnit
is_normal()->booltrue if the unit is a normal unit (not error, nan, or subnormal)is_valid()->booltrue if theMeasurementis a valid Measurement (not error)root(power:int)->Measurementreturn a new unit taken to the root powersqrt()->Unitreturns a new unit which is the square root of the current unitset_value(value:float)->Measurementgenerate a newMeasurementwith the new Valueset_units(unit:Unit|str)generate a newMeasurementwith the new unitsvalue_as(unit:Unit|str)->floatconvert the value of theMeasurementto a newUnitconvert_to(unit:Unit|str)->Measurementcreate a newMeasurementwith the new units and the value converted to those unitsconvert_to_base()->Measurementcreate a newMeasurementwith the units as the base measurement unitsis_close(other:Measurement)->boolreturn true if the two measurements are close (both converted to non precise measurement and compared)
value->floatreturn the numerical portion of aMeasurementunits->Unitget theUnitassociated with aMeasurement
*,/with otherMeasurementsproduces a new Measurement~inverts the measurement equivalent to1/measurement+,-with otherMeasurementsensures the units are in the same base unit and performs the appropriate action**is an exponentiation operator and produces a newMeasurement(NOTE: beware of limits on power representations of some units, things will always wrap so it is defined but may not produce what you expect). Can be negative.*,/,%with a floating point generates aMeasurement//produces the floor of the resulting unit of division==,!=,>,<,>=,<=produce the appropriate comparison operatorsstr,float,boolare defined,boolindicates that the measurement is non zero and is validround,math.ceil,math.floor, andmath.truncwork as expected- f string formatting also works with measurement. Some special formatters are available
f"{m1:-}"will remove the unit and just display the value.f"{m1:new_unit}"will convert to a new unit before displaying.f"{m1:-new_unit}"will do the conversion but just display the numerical value after the conversion.
convert(value:float,unit_in:Unit|str,unit_out:Unit|str)->floatgenerate a value represented by one unit in terms of anotherconvert_pu(value:float,unit_in:Unit|str,unit_out:Unit|str, base:float)->float"generate a value represented by one unit in terms of another if one of the units is in per-unit, the base_value is used in part of the conversion"default_unit(unit_type:str)->Unitgenerate a unit used for a particular type of measurementadd_user_defined_unit(unit_name|str,unit_definition:str|Unit)add a custom string representing a particular unit to use in future string translationsadd_units_from_file(file|str)inject a list of user defined units from a file
Uncertain measurements will likely be added, potentially some trig functions on measurements. Also some more commodity operations, and x12 and r20 unit types.
Contributions are welcome. See Contributing for more details and Contributors for a list of the current and past Contributors to this project.
Anyone else using the units library? Please let us know.
This units library is distributed under the terms of the BSD-3 clause license. All new contributions must be made under this license. LICENSE
SPDX-License-Identifier: BSD-3-Clause
LLNL-CODE-773786