Working With Quantity Objects As Coordinates#

This tutorial covers using unxt Quantities β€” arrays with units β€” as coordinate data in coordinax. A Quantity (e.g. u.Q([1, 2, 3], "km")) carries units but not component names, chart, or frame metadata. Coordinax can infer charts from the array shape when possible, making quantities a convenient middle ground between bare arrays and full vectors.

You will learn how to:

  • Pass quantities to act for transforms

  • Decompose quantities into CDicts with cdict()

  • Upgrade quantities to AbstractVector objects

  • Convert units with u.uconvert()

  • Use quantities with JAX

Prerequisites: Working With Quantities (Angles & Distances).

Object Levels

Coordinax supports five levels of coordinate representation, each adding more metadata. This tutorial covers Quantity.

Level

Type

See tutorial

Coordinate

Coordinate

Coordinate tutorial

Point

Point

Point tutorial

CDict

dict[str, Quantity]

CDict tutorial

Quantity

unxt.Quantity

this page

Array

jax.Array

Array tutorial

Setup#

>>> import coordinax.main as cx
>>> import coordinax.charts as cxc
>>> import coordinax.frames as cxf
>>> import coordinax.representations as cxr
>>> import coordinax.transforms as cxfm
>>> import unxt as u
>>> import jax.numpy as jnp
>>> import jax

What A Quantity Brings#

A Quantity attaches units to an array. This prevents silent unit confusion β€” is 1.0 in metres, kilometres, or degrees?

>>> q = u.Q([1.0, 2.0, 3.0], "km")
>>> q.unit
Unit("km")

Quantities do not carry component names (no β€œx”, β€œy”, β€œz” labels), chart, representation, or frame β€” that metadata must be provided externally or inferred.

Applying Transforms To Quantities#

Use cxfm.act() with a quantity. Coordinax infers the chart from the array shape (length 3 β†’ cart3d) and assumes point representation:

>>> rot90z = cxfm.Rotate.from_euler("z", u.Q(90, "deg"))

>>> q = u.Q([1.0, 0.0, 0.0], "km")
>>> result = cxfm.act(rot90z, None, q)
>>> result.unit
Unit("km")

With Explicit Chart#

Override the inferred chart:

>>> result = cxfm.act(rot90z, None, q, cxc.cart3d)
>>> result.unit
Unit("km")

With Explicit Chart And Representation#

Full control:

>>> result = cxfm.act(rot90z, None, q, cxc.cart3d, cxr.point)
>>> result.unit
Unit("km")

Translation#

>>> shift = cxfm.Translate.from_([1, 2, 3], "km")

>>> q_origin = u.Q([0.0, 0.0, 0.0], "km")
>>> result = cxfm.act(shift, None, q_origin)
>>> result.unit
Unit("km")

Identity#

The identity transform returns the exact same object:

>>> q = u.Q([1.0, 2.0, 3.0], "km")
>>> result = cxfm.act(cxfm.Identity(), None, q)
>>> result is q
True

Decomposing To A CDict#

Use cxc.cdict() to split a quantity into named components:

>>> q = u.Q([1, 2, 3], "km")

>>> d = cxc.cdict(q)
>>> sorted(d.keys())
['x', 'y', 'z']

>>> d["x"]
Q(1, 'km')

With an explicit chart:

>>> d = cxc.cdict(u.Q([1, 2, 3], "km"), cxc.cart3d)
>>> sorted(d.keys())
['x', 'y', 'z']

Once decomposed into a CDict, you can change charts:

>>> d_sph = cxc.pt_map(d, cxc.cart3d, cxc.sph3d)
>>> sorted(d_sph.keys())
['phi', 'r', 'theta']

Upgrading To A Vector#

Promote a quantity to a Vector:

>>> q = u.Q([1, 2, 3], "m")

>>> v = cx.Point.from_(q)
>>> v.chart
Cart3D(M=Rn(3))

>>> isinstance(v, cx.Point)
True

With an explicit chart:

>>> v = cx.Point.from_(q, cxc.cart3d)
>>> v.chart
Cart3D(M=Rn(3))

Upgrading To A Coordinate#

Go all the way from a quantity to a Coordinate:

>>> q = u.Q([1, 2, 3], "km")

>>> v = cx.Point.from_(q)
>>> coord = cx.Point.from_(v, cxf.alice)
>>> coord.frame
Alice()
>>> coord.chart
Cart3D(M=Rn(3))

Unit Conversion#

Standard unxt API:

>>> q_m = u.Q([1000, 2000, 3000], "m")
>>> q_km = u.uconvert("km", q_m)
>>> q_km.unit
Unit("km")

JAX Integration#

Quantities are Quax ArrayValue objects and work with JAX transformations:

>>> rot90z = cxfm.Rotate.from_euler("z", u.Q(90, "deg"))

>>> @jax.jit
... def rotate_qty(q):
...     return cxfm.act(rot90z, None, q)

>>> q = u.Q([1.0, 0.0, 0.0], "km")
>>> result = rotate_qty(q)
>>> result.unit
Unit("km")

When To Use Quantity#

Choose Quantity when:

  • You want unit safety without the overhead of named components or chart/representation metadata.

  • You are doing quick one-off computations (e.g. rotating a single point).

  • You are passing data to act and are happy with chart inference from the array shape.

  • You are working with existing unxt-based code and want coordinax transforms to β€œjust work”.

Trade-off: Chart inference depends on array shape β€” it only works for trailing axis sizes 1, 2, or 3, and always assumes Cartesian. For non-Cartesian data or explicit chart control, decompose to a CDict or upgrade to a Point. See the CDict tutorial or the Point tutorial.

If you need even less overhead and are willing to manage units yourself, use a bare array. See the Array tutorial.