alkahest
Python API

Units API

Physical units, quantities and dimensional analysis (units). Every scale is exact — a rational times a power of π — so no conversion ever introduces a float. See the Units and dimensional analysis chapter of the user guide for the full list of units and rules.

Units

class
class Unit(spec)

A physical unit: a product of named units with rational exponents, an exact scale relative to SI, and a dimension over the seven SI base dimensions.

Build one from a string — Unit("mg*h/L"), Unit("m/s^2"), Unit("kg m s^-2"), Unit("m^(1/2)"), Unit("µg/mL") — or by combining units with *, / and ** (rational exponent).

Equality is semantic: Unit("N") == Unit("kg*m/s^2") is True; str(u) keeps the spelling. degC / degF are affine scales and may only stand alone and be converted (E-UNIT-003 otherwise).

Raises UnitError (E-UNIT-002) on an unknown or malformed unit.

Properties

The unit's spelling as a product, e.g. "mg*h/L".

dimension

The dimension, e.g. "mass/length^3" or "dimensionless".

dimension_exponents

{base_dimension: Fraction} for the non-zero exponents.

factors

[(name, Fraction exponent), …] as written.

scale

(ratio, pi_power): one of this unit is ratio · π**pi_power of the coherent SI unit of the same dimension (both Fraction).

is_affine

True for degC / degF.

is_pure_number

True when dimensionless with scale 1 (1, rad, m/m).

Methods
dimensionless()
staticmethod

The unit of a pure number, written 1.

same_dimension(other)

True when other has the same dimension.

check_same_dimension(other, operation="add")

Raise UnitError E-UNIT-001 (naming both dimensions) unless other has the same dimension.

require_pure_number(context)

Raise UnitError E-UNIT-004 unless this is a pure number; context names where the value is used ("argument of exp").

conversion(target)

The exact map x [self] = ratio·π**pi_power·x + shift [target] as (ratio, pi_power, shift) (Fractions; shift is 0 except between affine temperature scales). E-UNIT-001 on a dimension mismatch.

to_base()

The coherent SI unit of the same dimension in base units (Unit("J").to_base() is kg*m^2/s^2).

simplify(derived=False, allowed=None)

Merge factors of equal dimension (mg/kg*g → mg); returns (ratio, pi_power, unit) with x [self] = ratio·π**pi_power·x [unit].

With derived=True the merged unit is also renamed to the named SI derived unit it exactly equals (kg*m/s^2 → N), see to_derived(); allowed restricts the candidate names.

to_derived(allowed=None)

The named SI derived unit this unit exactly equals, or None.

Unit("kg*m/s^2").to_derived() is N; g*m/s^2 is mN (prefixes in steps of 1000 only); kg*cm/s^2 is not renamed (its scale is not a power of 1000 times N). The candidates are N, Pa, J, W, C, V, ohm, F, H, T, Wb, S, Hz` (`allowed restricts them). A unit that is already one named factor, is dimensionless or affine, or contains a dimensionless factor (rad/s is not Hz) is left alone, and Gy/Sv/Bq are never produced (they share a dimension with m^2/s^2 and Hz). N*m is a J.

function
parse_unit(spec)

Parse a unit string into a Unit.

Accepts products and quotients ("mg*h/L", "kg m s^-2", "kg*m^2/(s^2*A)"), exponents with ^ or ** (fractional ones in parentheses, "m^(1/2)") or Unicode superscripts ("m s⁻²"), SI prefixes from yocto to yotta (µ, μ and u all mean micro), and exact numeric factors ("1000*m"). / divides by the single factor that follows it.

Raises UnitError: E-UNIT-002 for a malformed string or an unknown name ("m2" is refused, not read as m^2), E-UNIT-003 for an affine temperature unit inside a compound unit ("degC/s").

Examples
Python
>>> parse_unit("mg*h/L").dimension
'mass*time/length^3'

Quantities

class
class Quantity(value, unit='1')

A value together with its Unit.

Parameters
valueExpr, int, Fraction or float

The magnitude. Use int / Fraction / Expr for exact work; a float is kept as given (conversions never introduce one).

unitUnit or str, default "1"

The unit, e.g. "mg", "L/h", Unit("m/s^2").

Arithmetic checks dimensions (see the module docstring). Comparison with == is identity; compare convert(a, b.unit).value with b.value instead.

Examples
Python
>>> m = Quantity(2, "kg"); a = Quantity(3, "m/s^2")
>>> (m * a).unit == Unit("N")
True
Properties
dimension: str

The dimension of unit, e.g. "mass/length^3".

Methods
convert(unit, *, _like=None) → Quantity

Express in unit (exact). See convert().

to_base() → Quantity

Express in coherent SI base units (J → kg*m^2/s^2).

simplify_units(derived: bool=True, allowed=None) → Quantity

Merge factors of equal dimension (mg/kg*g → mg, km/m → 1) and, with derived=True (default), rename an exact match to a named SI derived unit (kg*m/s^2 → N). See simplify_units().

simplify() → Quantity

Simplify the value (alkahest.simplify); the unit is unchanged.

function
convert_units(q: Quantity, unit) → Quantity

Express q in unit, exactly.

The value is multiplied by the exact ratio of the two scales (and, between degC/degF/K/degR, shifted by the exact offset).

Raises UnitError E-UNIT-001 when the dimensions differ.

Examples
Python
>>> convert(Quantity(100, "degC"), "degF").value
212
function
to_base_units(q: Quantity) → Quantity

Express q in coherent SI base units (kg, m, s, A, K, mol, cd); degC/degF go to K.

function
simplify_units(q: Quantity, derived: bool=True, allowed=None) → Quantity

Merge the factors of q.unit that share a dimension into the first one (mg/kg*g → mg, L/h*min → L, km/m → 1), folding the exact conversion ratio into the value; then, unless derived=False, rename the result to the named SI derived unit it exactly equals.

Parameters
qQuantity
derivedbool, default True

kg*m/s^2 → N, kg*m^2/s^2 → J, A*s → C, kg*m^2/(A*s^3) → V`; a power-of-1000 prefix is allowed (`g*m/s^2 → mN). The candidates are N, Pa, J, W, C, V, ohm, F, H, T, Wb, S, Hz (their dimensions are pairwise distinct, so there is no ambiguity among them; N*m is a J). A unit that is already a single named factor, carries a dimensionless factor (rad/s is not Hz) or whose scale is not a power of 1000 times the derived unit (kg*cm/s^2) is left as is. Gy, Sv and Bq are never produced.

allowediterable of str, optional

Restrict the candidates, e.g. allowed=["N", "J"].

Examples
Python
>>> simplify_units(Quantity(3, "kg*m/s^2")).unit
Unit('N')
>>> simplify_units(Quantity(3, "kg*m/s^2"), derived=False).unit
Unit('kg*m/s^2')

Logarithmic scales

dB, Np and pH are not units (Unit("dB") is E-UNIT-002); they are converted explicitly in units.

function
db_to_ratio(db, kind="power")

Documented together with ratio_to_np.

function
ratio_to_db(ratio, kind="power")

Documented together with ratio_to_np.

function
np_to_ratio(np_, kind="field")

Documented together with ratio_to_np.

function
ratio_to_np(ratio, kind="field")

10**(db/10) (power) or 10**(db/20) (field) and its inverse; exp(np) (field) or exp(2 np) (power) and its inverse. Exact for exact powers of ten, symbolic otherwise, a float only for a float input.

function
ph_to_concentration(ph) → Quantity

Documented together with concentration_to_ph.

function
concentration_to_ph(c: Quantity)

10**(-pH) in mol/L and -log10(c / (1 mol/L)).

Dimensional analysis

function
dimension_of(expr, units)

The unit of a symbolic formula, given the unit of each of its symbols.

Checks the dimensional consistency of expr and returns the unit its value is in when every symbol is given in its listed unit.

Parameters
exprExpr

The formula.

unitsdict[Expr, Unit | str]

The unit of each symbol. Keys may be any sub-expression, e.g. an applied undefined function C(t).

Returns
Unit
Raises
UnitError

E-UNIT-001 the terms of a sum have different dimensions (both are named); E-UNIT-004 the argument of exp/log/sin/… or a non-literal exponent is not a pure number (an angle must be in rad); E-UNIT-005 a symbol or function with no known unit; E-UNIT-006 the terms of a sum share a dimension but not a unit (h and min); E-UNIT-003 an affine temperature inside arithmetic.

Examples
Python
>>> p = ExprPool(); D, V = p.symbol("D"), p.symbol("V")
>>> dimension_of(D / V, {D: "mg", V: "L"})
Unit('mg/L')
function
units.unify_units(expr, units, prefer: str='finest') → Quantity

Like dimension_of(), but convert a sum whose terms share a dimension and differ in unit instead of refusing it (E-UNIT-006).

a in h plus b in min becomes 60*a + b in min: each such term is multiplied by the exact ratio of its unit to the target unit given by prefer — "finest" (default, the smallest unit among the terms, so factors are integers wherever possible), "coarsest" or "first" (the first term in canonical order). min/max/atan2 arguments and Piecewise values are treated the same way.

Returns a Quantity whose value is the rewritten formula and whose unit is the unit that value is in when every symbol is in its listed unit. A formula without mixed sums comes back unchanged; every other refusal of dimension_of() stands (E-UNIT-001, -003, -004, -005, -007).

Examples
Python
>>> p = ExprPool(); a, b = p.symbol("a"), p.symbol("b")
>>> q = unify_units(a + b, {a: "h", b: "min"})   # 60*a + b
>>> str(q.unit)
'min'

Calculus with units

alkahest.units.diff(q, var) and alkahest.units.integrate(q, var, lower=None, upper=None)` take the variable as a `Quantity whose value is a symbol, and return q.unit / var.unit and q.unit · var.unit respectively. Bounds are quantities (converted into the variable's unit) or the unit-free 0 and ±oo. alkahest.units.exp, log, sin, … (and apply(f, q)) require a dimensionless argument and convert deg/% to a pure number first.

Refusals raise UnitError (E-UNIT-001 … E-UNIT-007); see errors.