Units API¶
Reference for bilt.units, bilt.units.dimensions and unit-related exceptions in
bilt.errors. For model declarations and a guided example, see Units in models.
Imports and declarations¶
from typing import Annotated
from bilt import Quantity, Tensor, tensor
from bilt.units import Metadata, Prefix, Unit, kilometer, meter
from bilt.units.dimensions import ABSOLUTE, LENGTH, TEMPERATURE
Length = Annotated[Quantity, LENGTH]
LengthSeries = Annotated[Tensor, LENGTH]
AbsoluteTemperature = Annotated[Quantity, TEMPERATURE, ABSOLUTE]
Declare dimensions, not units, on model ports. A bare Quantity is refused;
a bare Tensor declares no physical dimension. A declared quantity port can accept a compatible
unit-carrying tensor; a declared tensor port requires a tensor.
Plain ports, such as float, stand outside the unit system.
Built-in units¶
All names below are exported from bilt.units. Names use the spelling shown
(meter, liter, and siemen). Prefix variants are available only where listed;
use Unit.new_named() to define others.
| Quantity | Available names |
|---|---|
| Dimensionless and angle | UNITLESS, fraction, percent, radian, degree |
| Mass | milligram, gram, kilogram, megagram, gigagram, teragram, tonne, kilotonne, megatonne |
| Length | millimeter, centimeter, meter, kilometer, inch, foot, yard, mile |
| Time | millisecond, second, minute, hour, day, year |
| Temperature change | kelvin, celsius, fahrenheit |
| Absolute temperature | absolute_kelvin, absolute_celsius, absolute_fahrenheit |
| Absolute time | absolute_year |
| Electric current | ampere |
| Amount of substance | mole, kilomole |
| Luminous intensity | candela |
| Area | acre, hectare |
| Volume | liter |
| Force | millinewton, newton, kilonewton, meganewton |
| Pressure | pascal, kilopascal, megapascal, bar, atmosphere, torr |
| Power | watt, kilowatt, megawatt, gigawatt, terawatt, petawatt, exawatt |
| Energy | joule, kilojoule, megajoule, gigajoule, terajoule, petajoule, exajoule, watthour, kilowatthour, megawatthour, gigawatthour, british_thermal_unit, therm, million_british_thermal_unit |
| Voltage | volt, millivolt |
| Electric charge | coulomb |
| Electric resistance | ohm |
| Electric conductance | siemen |
| Dynamic viscosity | poiseuille, poise, centipoise |
| Annual currencies | USD[year], GBP[year], EUR[year], JPY[year], for integer years 2000–2025 |
Compose other units with *, /, integer powers, and reciprocals:
meter / second, kilogram / meter**3, meter**2, 1 / second.
Conventions:
percentrepresents0.01 * UNITLESS;radianis dimensionless and a degree is π/180 radians.yearis 365.25 days.absolute_yearexists as a unit, but model evaluation currently has no coherent absolute-second counterpart and rejects absolute time inputs.- Use
celsiusfor a temperature difference andabsolute_celsiusfor a reading. Absolute and relative units cannot be interconverted. - Currency conversions use fixed, bundled annual exchange and inflation data,
relative to USD in 2025. They do not fetch live rates. Index a currency before
using it as a unit; an unsupported year raises
AttributeError.
from bilt.units import GBP, USD, UNITLESS, percent
assert (50.0 * percent).convert_to(UNITLESS).value == 0.5
cost_in_gbp = (100.0 * USD[2024]).convert_to(GBP[2024])
Quantities¶
Quantity is available from both bilt and bilt.units. Methods return a new
quantity without changing the original.
| API | Meaning |
|---|---|
Quantity(value, unit) |
Construct a scalar quantity with a numerical value and a Unit. |
5.0 * kilometer |
Shorthand for Quantity(5.0, kilometer); use floating-point literals. |
q.value |
Numerical value in q.unit. |
q.unit |
The quantity's unit; q.unit.dimension gives its dimension. |
q.convert_to(unit) |
Rescale the value (and apply absolute offsets) to a compatible unit. Dimensions, including tags, must agree. |
q.cast_to(unit) |
Keep the number unchanged and relabel with an equivalent unit, or a unit equivalent after removing metadata. Does not convert scales. |
q.strip_metadata() |
Remove all unit metadata tags, preserving the number and physical scale. |
q * r, q / r |
Multiply or divide quantities, combining their values and units. |
q * 2.0, q / 2.0 |
Scale a quantity. |
q**2 |
Integer power. Floating-point powers are supported for dimensionless quantities. |
+q, -q, abs(q), round(q, ndigits) |
Apply the numerical operation and preserve the unit. |
Outside models, convert operands to the same unit before addition, subtraction,
ordering or equality checks. These operations do not automatically convert
representations; distinct but equivalent unit objects may require cast_to()
for addition. Equality compares the numerical values and unit equivalence.
Absolute + relative and absolute − relative produce absolute quantities; absolute − absolute produces a relative quantity. For direct Python arithmetic, align the absolute units and the relative step scales first. Absolute + absolute and relative − absolute are refused.
During model evaluation, Bilt converts inputs to coherent units and returns calculated outputs in coherent units. For ordinary physical dimensions these are SI units; currency uses USD2025 and metadata tags are retained.
Inside transforms, extract numbers with q.convert_to(unit).value and construct
quantities with Quantity(value, unit) or a float multiplied by a unit. Bare
quantity .value, representation-dependent .unit access, and cast_to() are
refused. Transform checks do not prove dimensional correctness of arbitrary Python.
Defining and inspecting units¶
| API | Meaning |
|---|---|
Unit.new_named(name, relation, prefix=None) |
Define a named unit from a Unit, a scaled Quantity, or an internal unit composition. Its dimension follows from the relation. |
Unit.new_absolute(name, scale, zero=None, metadata=None) |
Define an absolute unit with a relative step scale. zero, when supplied, is an absolute quantity locating its origin. An absolute unit based on a non-SI-base scale needs a zero point for conversion. |
abs(unit) |
Look up the existing absolute counterpart, e.g. abs(celsius) is absolute_celsius. Does not create one. |
unit.to_si_base_units() |
Express the unit's multiplicative relation in base units, retaining its scale factor and tags. For absolute offsets, convert a quantity instead. |
unit.strip_metadata() |
Remove all tags from a unit. |
unit.name, str(unit) |
Name or printable representation. |
unit.dimension, unit.metadata, unit.prefix |
Physical dimension (including tags), directly attached metadata, and prefix, if any. |
unit.relation, unit.offset, unit.category |
Definition, optional absolute offset, and internal category (BASE, NAMED, DERIVED, ABSOLUTE). |
unit.is_dimensionless, unit.is_relative |
Whether the unit is dimensionless or contains only relative units. |
unit.is_si_base_unit, unit.is_si_derived_unit, unit.is_coherent |
Classification against Bilt's base unit system. |
Define shared custom units in a project module and import them where needed:
from bilt.units import Unit, hour, meter
nautical_mile = Unit.new_named("nautical_mile", relation=1852.0 * meter)
knot = Unit.new_named("knot", relation=nautical_mile / hour)
assert (2.0 * nautical_mile).convert_to(meter).value == 3704.0
Names are cached process-wide. Repeating the same definition returns the cached
unit; a different relation under an existing name raises BiltUnitError.
Only one absolute counterpart may be registered for a given relative scale.
Use dimensions, such as LENGTH / TIME, in port declarations for these units too.
Prefixes¶
Prefix is an integer enum whose values are powers of ten:
| Members | Exponents |
|---|---|
ATTO, FEMTO, PICO, NANO, MICRO |
−18, −15, −12, −9, −6 |
MILLI, CENTI, DECI |
−3, −2, −1 |
DECA, HECTO, KILO |
1, 2, 3 |
MEGA, GIGA, TERA, PETA, EXA |
6, 9, 12, 15, 18 |
from bilt.units import Prefix, Unit, second
microsecond = Unit.new_named("microsecond", relation=second, prefix=Prefix.MICRO)
The name must start with the prefix's lowercase name. Use an unprefixed reference unit as above; do not also multiply the relation by the prefix factor.
Dimensions and metadata¶
Import dimension constants and ABSOLUTE from bilt.units.dimensions.
| Group | Available names |
|---|---|
| Primary | MASS, LENGTH, TIME, TEMPERATURE, CURRENT, AMOUNT, LUMINOSITY, CURRENCY |
| Derived | AREA, VOLUME, FORCE, ENERGY, POWER, PRESSURE, DYNAMIC_VISCOSITY, CHARGE, VOLTAGE, RESISTANCE, CONDUCTANCE |
| Dimensionless | DIMENSIONLESS |
| Absolute declaration marker | ABSOLUTE (a marker, not a dimension or unit) |
| API | Meaning |
|---|---|
dimension * other, dimension / other, dimension**n |
Compose dimensions using integer exponents. |
Dimension(mass=0, length=0, time=0, temperature=0, current=0, amount=0, luminosity=0, currency=0) |
Construct a dimension explicitly from integer exponents. Import Dimension from bilt.units. |
dimension.mass, .length, .time, .temperature, .current, .amount, .luminosity, .currency |
Individual exponents; .dimensions returns their tuple in this order. |
dimension.tagged |
Mapping of tagged dimension factors to exponents. |
dimension.is_dimensionless, .is_primary, .is_derived |
Dimension classification properties. |
Metadata(value) |
Create or retrieve a tag by a non-empty string. |
tag.value |
The tag's string value. |
dimension * tag, unit * tag, quantity * tag |
Attach a tag to a dimension, unit or quantity. |
from bilt.units import Metadata, kilogram
from bilt.units.dimensions import MASS
fuel = Metadata("fuel")
FuelMass = Annotated[Quantity, MASS * fuel]
fuel_mass = 2.0 * kilogram * fuel
assert fuel_mass.strip_metadata().unit == kilogram
Tags are part of dimensional compatibility: tagged and untagged masses cannot
be converted directly. Use strip_metadata() deliberately, or cast_to() when
the units are equivalent after removing tags. Put tags into the dimension in
annotations; Annotated[Quantity, MASS, fuel] is refused.
Equations express tag changes through tagged unit factors; transforms support
strip_metadata() and multiplication by a tag.
Unit-carrying tensors¶
Import tensor and Tensor from bilt.
| API | Meaning |
|---|---|
tensor(values, *, over=None, domain=None, unit=None) |
Label array values with a unit. Non-scalar values require over or domain; provide only one. |
Tensor.scalar(value, unit=None) |
Construct a rank-zero tensor, optionally with a unit. |
t.unit, t.values, t.domain |
Unit (or None), numerical array, and named domain. |
t.convert_to(unit) |
Convert all values, retaining the domain. Requires an existing compatible unit. |
t * unit |
Label an unlabelled tensor, or compose with its existing unit, without rescaling its values. |
t / unit |
Divide an existing tensor unit by another unit, without rescaling values. |
Unit multiplication labels values; convert_to() rescales them. Concrete tensor
arithmetic is otherwise dispatched through Bilt models. Tensors do not expose
the quantity helpers cast_to() or strip_metadata().
Errors and messages¶
Import exceptions from bilt.errors. Catch classes rather than matching full
message strings; messages include the offending ports, quantities and units.
| Exception | When raised / action |
|---|---|
BiltUnitsError |
Base for unit and dimensionality failures. |
BiltUnitError |
Incompatible representations, invalid named/absolute definitions, unsupported conversion or missing tensor unit. Convert operands, fix the definition, or label the tensor. |
BiltDimensionalityError |
Incompatible physical dimensions or metadata tags; base for the three model dimensionality errors below. |
BiltMetadataError |
Invalid metadata, such as an empty tag. |
BiltEquationDimensionalityError |
An equation is dimensionally inconsistent. Check operations, constants, tags and absolute/relative quantities. |
BiltImplementationDimensionalityError |
Shared ports have conflicting declarations or the implementation makes an equation inconsistent. Declare the same dimension everywhere. |
BiltInputDimensionalityError |
An input has the wrong dimension, tags, absolute/relative kind or scalar/tensor carrier, or is unlabelled. |
BiltPortDeclarationError |
Invalid annotation: use exactly one dimension on a quantity/tensor, not a unit. This is a BiltDefinitionError, not a BiltUnitsError. |
BiltDefinitionError |
Unsupported unit operations inside a transform, including bare quantity .value or cast_to(). |
BiltUnresolvedAnnotationError |
An annotation name is missing when the decorator runs. Import or define it at runtime, outside if TYPE_CHECKING:. |
Common message excerpts (… stands for context-dependent details):
| Message excerpt | Meaning / remedy |
|---|---|
Cannot convert unit with dimensionality … as they are different |
Dimensions or tags differ; a scale conversion cannot fix this. |
use the … Quantity.convert_to() … to convert them to consistent units |
Convert operands to the same representation before direct arithmetic. |
use the … Quantity.cast_to() … to cast them to consistent units |
Equivalent unit objects differ; explicitly align their labels. |
Cannot cast quantity … as the two units are not equivalent |
Use convert_to() to change scale; check tags and dimensions. |
relative and absolute units cannot be interconverted |
Use the correct unit kind for a reading or a difference. |
Cannot add two quantities with absolute units |
Subtract readings to obtain a difference, or add a relative change. |
No absolute unit corresponding to … exists |
abs(unit) cannot find a registered counterpart. |
has no offset (zero point) set |
Anchor a custom absolute scale to an existing absolute unit with zero=. |
already exists with the same scale |
An absolute counterpart is already registered for this relative scale. Reuse it or define a separate relative scale. |
Cannot instantiate unit … with the same name |
A name is already registered with a different relation. |
does not start with its defined prefix |
Match the unit name to Prefix, e.g. microsecond with Prefix.MICRO. |
Metadata must be a non-empty value |
Supply a non-empty tag string. |
Cannot convert a tensor with no unit |
Supply unit= or label the tensor before conversion. |
Input … is a plain port, outside the unit system |
Supply a plain value or declare a quantity dimension on the port. |
BiltPortDeclarationError, BiltDefinitionError and
BiltUnresolvedAnnotationError are definition failures; catching BiltUnitsError
alone does not catch them. See the units guide for model examples.