Use this page when choosing between a named mass-length-time unit system and an explicit unit-aware value at a public scientific boundary.
Representation contract¶
| Contract field | Current representation |
|---|---|
| Mathematical object | A UnitSystem is a named mass-length-time basis; a Quantity is a JAX value paired with one immutable Unit. |
| Physical convention | CGS is the canonical ecosystem base; named canonical ecosystem unit systems declare mass, length, and time scales, while quantity units store exact dimensions and a scale to CGS. |
| Runtime owner | jaxstro.units and jaxstro.quantity own the two additive, importable surfaces. |
| Shape and unit policy | UnitSystem methods convert scalar or array values by explicit scale factors; one Quantity array has one unit and one shared dimension vector. |
| Transform boundary | Fixed scale conversion and quantity arithmetic support JAX array transforms; registry lookup, parsing, basis-role choice, and migration policy remain static host decisions. |
| Evidence | Unit conversion tests, quantity algebra/parser/serialization tests, and compatibility tests verify behavior without claiming downstream adoption. |
| Downstream interpretation boundary | Domain packages choose their default systems, code units, roles, and migration schedule; Jaxstro does not hide a domain default in core kernels. |
Two current representations¶
jaxstro.units.UnitSystem represents a basis through the CGS value of one mass,
length, and time unit. If a physical quantity has dimensions
, conversion between two systems follows
Named systems include CGS, ASTRO_STELLAR, ASTRO_DYNAMICAL, and
ASTRO_PLANETARY, with documented aliases. DEFAULT_UNITS is CGS because Jaxstro
is the domain-agnostic foundation. Downstream packages may select another default for
their own domain, but core Jaxstro APIs require explicit units or an explicit
physical constant such as G.
jaxstro.quantity represents the unit on the value itself:
import jax.numpy as jnp
import jaxstro.quantity as q
mass = q.Quantity(1.0, q.Msun)
radius = 2.0 * q.Rsun
speed = jnp.array([10.0, 20.0]) * q.km / q.s
density = (mass / radius**3).to(q.g / q.cm**3)The system is additive. Existing jaxstro.units behavior remains stable while
downstream packages evaluate quantity migration at their own boundaries. The in-house
design rationale is recorded in ADR 0006 - Build own Quantity, not unxt/quax.
Quantity data model¶
A Quantity is a registered JAX PyTree whose dynamic child is the value. Its unit is
static auxiliary metadata. A Unit is immutable and records a symbol, CGS scale,
exact fixed-vector dimensions, and small semantic metadata such as the angle tag.
Unit identity includes dimensions, exact stored scale, and all unit metadata. Metadata
contains immutable JSON scalar values. The display symbol and name do not affect
identity.
CGS scales must be finite and positive.
This split allows numeric values to trace while units remain inspectable. One
Quantity array has one unit. Mixed-unit table columns and row-wise unit tags require
a different container and are not part of the primitive.
The implementation is layered:
jaxstro.quantity
dimensions.py exact canonical dimension vectors
unit.py immutable Unit and algebra
quantity.py Quantity PyTree and arithmetic
units.py core CGS and SI units
astro.py astronomical units
registry.py layered exact-symbol registries
parser.py expression parser and canonical formatter
bases.py role-aware presentation bases
constants.py quantity constants and metadata
equivalencies.py explicit cross-dimension relations
math.py dimension-aware JAX wrappers
serialization.py compact and structured scalar payloads
errors.py structured quantity exceptionsArithmetic and static metadata¶
Addition and subtraction require compatible dimensions and the same semantic tag. They convert the right operand to the left operand’s unit and preserve the left unit. Multiplication and division combine dimensions. Powers require integer or exact rational exponents. Raw scalars can scale any quantity but can only add to untagged dimensionless quantities. In addition and subtraction, a raw scalar has the canonical dimensionless scale of 1 and is converted to the quantity’s unit.
Angle units are dimensionless for scale algebra but carry an angle semantic tag.
q.math.sin and q.math.cos require the tag and convert to radians before
evaluation. log and exp require untagged dimensionless input and convert
scaled inputs to the canonical unit before evaluation. Conversion, addition,
subtraction, and where do not mix tagged angles with untagged dimensionless
quantities. Multiplying an angle by an untagged dimensionless quantity in either
order, or dividing an angle by one, preserves the tag and composes the scales.
Multiplying two angles, dividing an untagged value by an angle, or raising an
angle to a power other than one produces an untagged dimensionless unit. Products
with dimensional units follow dimensional algebra and do not retain the angle tag.
to_cgs() converts a tagged angle to radians without dropping the tag. To assign
an angle meaning to a raw numerical value, construct Quantity(value, q.rad)
explicitly; to use its numerical radian value, call angle.to_value(q.rad).
Raw jax.grad differentiates the numeric PyTree leaf. If its input is a
Quantity, JAX returns the gradient in that input’s PyTree structure, with the
input unit attached. That unit is not generally the physical derivative unit.
Use q.grad for a function of one Quantity argument that returns a scalar
Quantity. It returns the JAX derivative with unit [f]/[x] for output unit [f]
and input unit [x]. For example, q.grad(lambda x: x**3)(3.0 * q.cm) returns
27 cm^2. The wrapper composes with jit and vmap; multi-argument functions,
auxiliary output, and Jacobian or Hessian units are outside this contract.
Registries, parsing, and bases¶
Core and astronomy registries use exact symbols with a small documented alias set.
The parser accepts products, divisions, parentheses, sqrt(...), rational powers,
and decimal powers that rationalize under the documented bound. Canonical formatting
writes exact rational exponents.
Registries are layered so downstream packages can build scoped extensions. Global registration is reserved for interactive convenience, not reproducible package code. Unknown symbols fail with structured errors rather than loose normalization.
Named quantity bases such as CGS, SI, STELLAR, PLANETARY, and
STAR_CLUSTER are role-aware presentation profiles. They do not replace direct unit
conversion. A planetary profile can map a stellar mass, a planet mass, and an orbit
to different units because those roles are explicit.
Migration boundary¶
Each legacy UnitSystem exposes representative quantity_units and
quantity_scales bridges. They support boundary conversion and migration notebooks;
they do not change aliases, emit deprecations, or approve a cutover.
The current evidence verifies implementation behavior, compatibility, serialization, and JAX transformations in Jaxstro. Ecosystem adoption requires separate downstream parity, performance, serialization, ergonomics, and migration-cost evidence. See (1) for the scale relation both surfaces must preserve.
The planned unit-aware scientific computing program uses existing Progenax to Informax examples to collect that downstream evidence.