Home

Awesome

Units

codecov Build Status CircleCI Documentation Status pre-commit.ci status

What's new

Documentation

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.

This software was developed for use in LLNL/GridDyn, and HELICS and is currently a work in progress (though getting close). Namespaces, function names, and code organization is subject to change though is fairly stable at this point, input is welcome. A set of documentation is available.

Table of contents

Purpose

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

  1. converting units, often represented by strings, to a standardized unit set when dealing with user input and output.
  2. 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.
  3. 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.
  4. The library has its origins in power systems so support for per-unit operations was also lacking in the alternatives.
  5. Capture uncertainty and uncertainty calculations directly with a measurement

It was desired that the unit representation be a compact type(<=8 bytes) that is typically passed by value, that can represent a wide assortment of units and arbitrary combinations of units. The primary use of the conversions is at run-time to convert user input/output to/from internal units, it is not to provide strict type safety or dimensional analysis, though it can provide some of that. This library does NOT provide compile time checking of units. The units library supports units and operations on units where many of the units in use are unknown at compile time and conversions and definitions are dealt with at run time, and may be of a wide variety of units.

This library is an engineering library, created to represent a huge variety of units and measurements in a simple data type instead of a proliferation of templates. It supports conversion of units to and from strings. It supports mathematical operations on units and measurements which are constexpr where possible. It supports units used in a wide variety of scientific and non-scientific contexts. Supports conversions between different units of the same type as well as some typical assumptions for supporting conversions of a few dissimilar types. In some cases it also has some notion of commodities, and support for existing unit standards for strings and naming.

Basic use case

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

#include <units/units.hpp>

double GetInputValueAs(const std::string &input, precise_units out)
{
   auto meas=measurement_from_string(input);
   return meas.value_as(out);
}

The return value can be checked for validity as an invalid conversion would result in constants::invalid_conversion or Nan so can be checked by std::isnan or

if (!meas.units().is_convertible(out))
{
    throw(std::invalid_argument);
}

Limitations

Alternatives

If you are looking for compile time and prevention of unit errors in equations for dimensional analysis one of these libraries might work for you.

These libraries will work well if the number of units being dealt with is known at compile time. Many also produce zero overhead operations and checking. Therefore in situations where this is possible other libraries are a preferred alternative.

Reasons to choose this units library over another option

  1. Conversions to and from regular strings are required
  2. The number of units in use is large
  3. A specific single unit or measurement type is required to handle many different kinds of units or measurements
  4. Uncertainties are needed to be included with the measurements
  5. Working with per unit values
  6. Dealing with commodities in addition to regular units. i.e. differentiate between a gallon of water and a gallon of gasoline
  7. Dealing with equation type units
  8. Complete C++ type safety is NOT a critical design requirement.
  9. Support is needed for some funky custom unit with bizarre base units.

Reasons to choose something else

  1. Type safety and dimensional analysis IS a design requirement
  2. Performance is absolutely critical (many other libraries are zero runtime overhead)
  3. You are only working with a small number of known units
  4. You cannot use C++11 yet.
  5. You need to operate on arbitrary or general fractional powers of base units
  6. You need support for arbitrary datum shifts in the unit library

Types

There are only a few types in the library

Unit representation

The unit class consists of a multiplier and a representation of base units. The seven SI units + radians + currency units + count units. In addition a unit has 4 flags, per-unit for per unit or ratio units. One flag[i_flag] that is a representation of imaginary units, one flags for a variety of purposes and to differentiate otherwise similar units[e_flag]. And a flag to indicate an equation unit. Due to the requirement that the base units fit into a 4-byte type the represented powers of the units are limited. The table below shows the bit representation range and observed range of use in equations and observed usage

Base UnitBitsRepresentable rangeNormal RangeIntermediate Operations
meter4[-8,+7][-4,+4][-6,+6]
kilogram3[-4,+3][-1,+1][-2,+2]
second4[-8,+7][-4,+4][-6,+6]
ampere3[-4,+3][-2,+2]
kelvin3[-4,+3][-4,+1]
mole2[-2,+1][-1,+1]
candela2[-2,+1][-1,+1]
currency2[-2,+1][-1,+1]
count2[-2,+1][-1,+1]
radians3[-4,+3][-2,+2]

These ranges were chosen to represent nearly all physical quantities that could be found in various disciplines we have encountered. See Unit Details for additional details on the unit base representation.

The CMake variable UNITS_BASE_TYPE, if set to a 64-bit type like uint64_t, will double the space requirements but also change the ranges to be at least a power of 4 larger than the above table. See CMake Reference for more details.

Discussion points

Defined units

There are 2 sets of defined units, many common units are defined in the units namespace, many others are defined in units::precise and subnamespaces. See Defined Units for details on the available units.

Physics constants

A set of physical and numerical constants are defined in the units::constants namespace. More details and a list of available constants are described in Physical Units. Some of the available constants that are measured vs. defined have an uncertain_measurement version available as well that includes the uncertainty.

Building the library

There are two parts of the library a header only portion that can simply be copied and used. There are 5 headers units_decl.hpp declares the underlying classes. units_util.hpp defines some additional helper functions, unit_defintions.hpp declares constants for many of the units, and units.hpp which is the primary public interface to units,units_math.hpp is an optional extra header that includes additional mathematical operations. If units.hpp is included in another file and the variable UNITS_HEADER_ONLY is defined then none of the functions that require the cpp files are defined. These header files can simply be included in your project and used with no additional building required.

The second part is a few cpp files that can add some additional functionality. The primary additions from the cpp file are an ability to take roots of units and measurements and convert to and from strings. The units_conversion_maps.hpp file defines many of string conversions used in the converters to and from strings. These files can be built as a standalone static library or included in the source code of whatever project want to use them. The code should build with an C++11 or greater compiler. It currently defaults to build with C++14. Most of the library is tagged with constexpr so can be run at compile time to link units that are known at compile time. Unit numerical conversions are not at compile time, so will have a run-time cost. A quick_convert function is available to do simple conversions. with a requirement that the units have the same base and not be an equation unit. The cpp code also includes some functions for commodities and will eventually have r20 and x12 conversions, though this is not complete yet.

It builds by default with the static library. Using UNIT_BUILD_SHARED_LIBRARY or BUILD_SHARED_LIBS will build the shared library instead. Either one can be used with CMake as a units::units target. The header only library target is also generated units::header_only. The shared/static library has a CMake target units::units.

Try it out

If you want to try out the string conversion components. There is server running that can do the string conversions

Unit String Conversions

For more details see the documentation

Converter Application

A converter command line application can be built as part the units library by setting UNITS_BUILD_CONVERTER_APP=ON in the CMake build. This is a simple command line script that takes a measurement entered on the command line and a unit to convert to and returns the new value by itself or part of a string output with the units either simplified or in original form. If you want to run your own converter web server, a docker container is available on dockerhub.

Usage

Many units are defined as constexpr objects and can be used directly

#include "units/units.hpp"
using namespace units

measurement length1=45.0*m;
measurement length2=20.0*m;

measurement area=length1*length2;

std::cout<<"the area is "<<area<< " or "<<area.convert_to(ft.pow(2))<<".\n";

Unit methods

These operations apply to units and precise_units

For precise_units only

Unit Operators

There are also several operator overloads that apply to units and precise_units.

precise_units can usually operate with a precise_unit or unit, unit usually can't operate on precise_unit.

Unit free functions

These functions are not class methods but operate on units

Measurement Operations

Uncertain measurement methods

Uncertain measurements have a few additional functions to support the uncertainty calculations

Measurement operators

There are several operator overloads which work on measurements or units to produce measurements.

Notes: for regular measurements, + and - are not defined for doubles due to ambiguity of what that operation means. For fixed_measurement types this is defined as the units are known at construction and cannot change. For fixed_measurement types if the operator would produce a new measurement with the same units it will be a fixed measurement, if not it reverts to a regular measurement.

Measurement functions

These free functions work on any of different measurement types.

Additional math operations

A few additional math operations are available in the "unit_math.hpp" header on all measurement types. This is a header only and is not included by default. It adds math operations including ceil,floor,trunc,round,fmod,sin,cos,tan. The trigonometric operations are only defined for measurements that are convertible to radians. Additionally, three type traits are defined including is_measurement<X>, is_precise_measurement<X> and is_unit<X>. These traits are only true for defined measurement types and unit types respectively.

Available library functions

String Conversions

The flags argument is optional in all cases. If not specified it uses the default flags, which can be user declared at run or compile time.

For more description of the possible flags see flags. The default flags can be set through setDefaultFlags(std::uint32_t flags) and retrieved through getDefaultFlags(). The initial default flag is OU but can be modified through UNITS_DEFAULT_MATCH_FLAGS compile flag.

User Defined Units

For more details see User Defined Units.

Unit Domains

For more description of the Unit Domains supported see Domains. Use the constants available in units::domains as the argument. The numerical value is subject to change in future releases as this gets refined.

Commodities

The units library has some support for commodities, more might be added in the future. Commodities are supported in precise_units.

Other unit definitions

These are all only partially implemented, not recommended for use yet

Contributions

Contributions are welcome. See Contributing for more details and Contributors for a list of the current and past Contributors to this project.

Project Using the Units Library

Anyone else using the units library? Please let us know.

Release

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