natu.core

Core classes of natu

As shown in the diagram below, there are two types of units (Unit): scalar units (ScalarUnit) and lambda units (LambdaUnit). Scalar units are quantities, but lambda units are not. Lambda units are essentially invertible functions that map between numbers and quantities (Quantity). All units and quantities are dimensioned objects (DimObject), meaning that their physical dimension is recorded (even if dimensionless).

Inheritance diagram of DimObject, Unit, ScalarUnit, LambdaUnit, Quantity

Numbers, scalar units, and quantities can be used together in mathematical expressions as long as the expression is dimensionally consistent. Numbers (float, int, Fraction, etc.) are considered dimensionless.

Lambda units can only be used with numbers and quantities in certain ways. A lambda unit can be applied to generate a quantity for use in the rest of a mathematical expression. The multiplication operator (*) is overloaded so that a number times a lambda unit calls the unit’s method to map the number to a quantity. Likewise, the division operator (/) uses a lambda unit to map a quantity to a number.

The tables below summarize the type of result given by simple mathematical operations among numbers (N), scalar units (S), lambda units (L), and quantities (Q). The product or quotient of a number and a quantity or scalar unit is a quantity. This is the case even if the number is zero. For example, the product of zero and a unit still carries the dimension of the unit. In a Boolean expression, a quantity is cast as False if its value is zero. However, it is only equal to zero if its value is zero and it is dimensionless. The display unit of the first term in addition or subtraction takes precedence. If the first term is a number and the second term is a dimensionless quantity, then the result is a number.

Results of multiplication and division:

1st argument 2nd argument
N Q S L
N N Q Q Q
Q Q Q [1] Q [1] N
S Q Q [1] S [1]
L

Results of addition and subtraction:

1st argument 2nd argument
N Q S L
N N N [2] N [2]
Q Q [2] Q Q
S Q [2] Q Q
L

Key:

[1](1, 2, 3, 4) If the result is dimensionless, then it is returned as a number instead.
[2](1, 2, 3, 4) The quantity must be dimensionless.

Here are some consequences of this design:

  • Is 0*ppm equal to zero? Yes
  • Is 0*m equal to zero? No
  • Does 0*m evaluate to False? Yes
  • Is 0*m dimensionless? No
  • Is m/ft a quantity or a number? Number (float)
  • Is ohm*S a quantity or a number? Number (1.0)
  • Is m*m a unit? Yes (with display unit 'm2')
  • Is 1*m*m a unit? No (only a quantity)
  • Is 1*ppm a unit or a number? Neither (a dimensionless quantity)
  • Is m + 0 (or 0 + m) valid? No
  • Is ppm + 0 (and 0 + ppm) valid? Yes
  • Is ppm + 0 a quantity or a number? Quantity (1 ppm)
  • Is 0 + ppm a quantity or a number? Number (1e-6)
  • What is the display unit of ft + m? 'ft'
  • Is m*10 valid? Yes (Quantity(10, 'L', 'm') (10.0 m))
  • Is degC*10 valid? No (use 10*degC instead)

Classes in this module:

  • CoherentRelations - List of coherent relations among units
  • DimObject - Base class that records physical dimension and display unit
  • LambdaUnit - Unit that involves an offset or other operations besides scaling
  • Quantity - Class to represent a physical quantity
  • ScalarUnit - Unit that involves only a scaling factor
  • Unit - Base class for a unit
  • UnitExponents - Dictionary that contains the base units of a display unit as keys and exponents of those units as values
  • Units - Dictionary of units with dynamic prefixing
  • UnitsModule - Class that wraps a Units dictionary as a module

Warning

Be careful if you use this package to produce conversion factors. The factor may be the reciprocal of what you first think. By the phrase “convert from x to y” (where x and y are units), we mean “convert from an expression of the quantity in x to an expression of the quantity in y.” “Quantity in unit” is typically equivalent to “quantity divided by unit”, and to get from Q/x (where Q is a quantity) to Q/y, we must multiply by x/y. For example, the conversion factor from feet to metres is ft/m (0.3048) using natu.units.

Note

Known issues:

  • Python’s built-in sum() will only accept dimensionless quantities. Consider using natu.math.fsum() instead.
class natu.core.DimObject(dimension, display_unit)

Bases: object

Base class that records physical dimension and display unit (aka “dimensioned object”)

Initialization parameters:

  • dimension: Physical dimension

    This can be an Exponents instance, a dict of similar form, or a string accepted by the fromstr() constructor.

  • display_unit: Display unit

    This can be an Exponents instance, a UnitExponents instance, a dict of similar form, or a string accepted by the fromstr() constructor.

Here, the physical dimension and the display unit are not checked for consistency.

dimension and dimensionless are read-only attributes (see below), but display_unit can be set using the same format as the display_unit argument above.

dimension

Physical dimension as an Exponents instance

dimensionless

True if the instance is dimensionless

display_unit

Display unit as an Exponents instance

classmethod quicknew(dimension, display_unit)

Initialize by directly setting the physical dimension and display unit.

Parameters:

  • dimension: Physical dimension as an Exponents instance
  • display_unit: Display unit as a UnitExponents instance
class natu.core.Quantity(value, dimension, display_unit)

Bases: natu.core.DimObject

Class to represent a physical quantity

Initialization parameters:

  • value: Value of the quantity (a numbers.Number instance)

    This be expressed as the product of a number and the value of a unit. It is independent of the unit since the number scales inversely to the unit.

  • dimension: Physical dimension

    This can be an Exponents instance, a dict of similar form, or a string accepted by the fromstr() constructor.

  • display_unit: Display unit

    This can be an Exponents instance, a UnitExponents instance, a dict of similar form, or a string accepted by the fromstr() constructor.

Examples:

Instantiation and simple operations:

>>> import natu.units
>>> mass = Quantity(1, 'M', 'kg')
>>> velocity = Quantity(1, 'L/T', 'm/s')
>>> energy = mass*velocity**2
>>> print(energy.dimension)
L2*M/T2
>>> print(energy.display_unit)
kg*m2/s2

However, it is easier to create a quantity as the product of a number and unit(s); see natu.units.

Changing the display unit:

>>> mass.display_unit = 'lb'
>>> print(mass) 
2.20462... lb

Note that the value has not changed:

>>> mass 
Quantity(1, 'M', 'lb') (2.20462... lb)

Although the display_unit property can be changed, quantities are otherwise immutable. The in-place operators create new instances:

>>> initial_id = id(velocity)
>>> velocity *= 0.5
>>> id(velocity) == initial_id
False
__getitem__(x, y)
Index the value and put it in a new quantity with the same dimension
and display unit.

The dimension and display_unit properties of the first term are propagated to the result.

__len__()

Return the length of the value if it is defined.

classmethod quicknew(value, dimension, display_unit)

Initialize by directly setting the value, physical dimension, and display unit.

  • value: Value of the quantity (a numbers.Number instance)

    This be expressed as the product of a number and the value of a unit. It is independent of the unit since the number scales inversely to the unit.

  • dimension: Physical dimension as an Exponents instance

  • display_unit: Display unit as a UnitExponents instance

class natu.core.Unit(dimension, display_unit, prefixable=False)

Bases: natu.core.DimObject

Base class for a unit

In-place operations are blocked because the value of a unit is predefined and should not be changed.

Initialization parameters:

  • dimension: Physical dimension

    This can be an Exponents instance, a dict of similar form, or a string accepted by the fromstr() constructor.

  • display_unit: Display unit

    This can be an Exponents instance, a UnitExponents instance, a dict of similar form, or a string accepted by the fromstr() constructor.

  • prefixable: True if the unit can be prefixed

dimension, dimensionless, and prefixable are read-only attributes, but display_unit can be set using the same format as the display_unit argument above.

prefixable

True if the unit can be prefixed

Example:

>>> from natu.units import m, ft
>>> m.prefixable
True
>>> ft.prefixable
False
class natu.core.ScalarUnit(value, dimension, display_unit={}, prefixable=False)

Bases: natu.core.Quantity, natu.core.Unit

Unit that involves just a scaling factor

Initialization parameters:

  • value: Value of the unit (a numbers.Number instance)

  • dimension: Physical dimension

    This can be an Exponents instance, a dict of similar form, or a string accepted by the fromstr() constructor.

  • display_unit: Display unit

    This can be an Exponents instance, a UnitExponents instance, a dict of similar form, or a string accepted by the fromstr() constructor.

  • prefixable: True if the unit can be prefixed

Examples:

>>> from natu.units import kg, m, s
>>> # F
>>> kg
ScalarUnit(1, 'M', 'kg', False) (kg)
>>> kg*m**2/s**2
ScalarUnit(1, 'L2*M/T2', 'J', False) (J)

Note that the display unit can be changed:

>>> m.display_unit = 'ft'
>>> m 
ScalarUnit(1, 'L', 'ft', True) (3.2808... ft)

Now any quantity generated from the metre (m) will display in feet (ft) instead. However, the value is unchanged; the metre still represents the same length.

__getitem__(item)

Raise an error upon indexing.

classmethod from_quantity(quantity, display_unit, prefixable=False)

Convert a quantity (instance of Quantity) to a scalar unit.

The value and dimension are taken from quantity. The display unit must be provided (via display_unit).

Example:

>>> from natu.units import ns
>>> shake = ScalarUnit.from_quantity(10*ns, 'shake')
class natu.core.LambdaUnit(toquantity, tonumber, dimension, display_unit='', prefixable=False)

Bases: natu.core.Unit

Unit that involves a offset or other operations besides scaling

Initialization parameters:

  • toquantity: Function that maps a number to a quantity

  • tonumber: Function that maps a quantity to a number

  • dimension: Physical dimension

    This can be an Exponents instance, a dict of similar form, or a string accepted by the fromstr() constructor.

  • display_unit: Display unit

    This can be an Exponents instance, a UnitExponents instance, a dict of similar form, or a string accepted by the fromstr() constructor.

  • prefixable: True if the unit can be prefixed

Examples:

>>> from natu.units import degC, K
>>> degC 
LambdaUnit(...)
>>> 25*degC/K
298.15
class natu.core.Units(*args, **kwargs)

Bases: dict

Dictionary of units with dynamic prefixing (upon access)

Properties:

  • coherent_relations - List of coherent relations among the units

    Each entry is an UnitExponents instance that evaluates to unity.

__call__(**factors)

Generate a compound, coherent unit from existing units.

Call signature (where units is a :class:`Units` instance):

  • units(unit1=exp1, unit2=exp2, ...): Returns the product of unit1 raised to the power exp1, unit2 raised to the power exp2, etc.

    If there are no arguments, then unity (1.0) is returned.

Example:

>>> from natu.units import _units
>>> _units(lbf=1, inch=-2)
ScalarUnit lbf/inch2 with dimension M/(L*T2) (not prefixable)
__getitem__(symbol)

Access a simple (not compound) unit by symbol (a string).

Prefixes are supported.

Example:

>>> from natu.units import _units
>>> _units['psi']
ScalarUnit(6894.76, 'M/(L*T2)', 'psi', False) (psi)
load_ini(files)

Add units to the unit dictionary from a *.ini file or list of files (files).

Examples:

>>> from natu.core import Units
>>> from natu.config import definitions

>>> units = Units()
>>> units.load_ini([definitions[0]])
>>> units.keys() 
['R', 'R_K', 'R_inf', 'c', 'k_Aprime', 'k_F', 'k_J', 'rational']
simplify(unit, level=1)

Simplify a compound unit.

This function seeks to minimize the sum of the absolute values of the exponents of the base factors by substituting coherently related units. It uses the internal coherent_relations list which is generated while parsing the *.ini files (in load_ini()). It will not always find the simplest representation because some simplifications involve first making the representation more complex.

Parameters:

  • unit: Unit to be simplified

    This can be an UnitExponents instance or a dict of similar form.

  • level: Number of levels of recursion to perform

    This is the number of non-minimizing substitutions that can be made while seeking the simplest representation of the unit. The default is

Returns: The new representation of the unit as an instance of the same class as the original representation (unit).

Example:

>>> # High-level/indirect:
>>> from natu.units import *
>>> print(kg*m**2/s**2)
J
>>> # Low-level/direct:
>>> from natu.units import _units
>>> print(_units.simplify('kg*m2/s2'))
J
class natu.core.UnitsModule(module, definitions)

Bases: module

Class that wraps a Units dictionary as a module

Initialization parameters:

  • module: The module into which the units should be wrapped

    This must have the attributes __name__ and __doc__.

  • definitions: A *.ini file, list of files, or dictionary with units that should be inserted into the module

    It is not necessary to provide prefixed versions of the units unless they should be available via wildcard import (e.g., from units_module import *).

Only one UnitsModule can be instantiated directly from *.ini files per Python session. This prevents conflicts that could arise if the units were reloaded with different base constants.

class natu.core.UnitExponents(iterable=None, **kwds)

Bases: natu.exponents.Exponents

Dictionary that contains the base units of a display unit as keys and exponents of those units as values

This is natu.exponents.Exponents, except that special replacements (see unit_replacements in mod:~natu.config) are made in certain formatted strings.

Example:

>>> unit = UnitExponents('angstrom/s')*2
>>> dict(unit)
{'angstrom': 2, 's': -2}
>>> unit
angstrom2/s2
>>> print(format(unit, 'U'))
Ų s⁻²