coordinax library#
import coordinax as cx.
- final class coordinax.Distance(value: Any, unit: Any, *, check_negative: bool = True)
Bases:
AbstractDistanceDistance quantities.
The distance is a quantity with dimensions of length.
Examples
>>> import coordinax.distances as cxd >>> cxd.Distance(10, "km") Distance(10, 'km')
The units are checked to have length dimensions.
>>> try: cxd.Distance(10, "s") ... except ValueError as e: print(e) Distance must have dimensions length.
- value: Shaped[Array, '*shape']
The distance value.
- unit: UnitBase | FunctionUnitBase
The unit associated with this value.
- check_negative: bool = True
Whether to check that the distance is strictly non-negative.
- property T: AbstractQuantity
Transpose of the array.
Examples
>>> import unxt as u >>> q = u.Q([[0, 1], [1, 2]], "m") >>> q.T Quantity(Array([[0, 1], [1, 2]], dtype=int32), unit='m')
- argmax(*args: Any, **kwargs: Any)
Return the indices of the maximum value.
Examples
>>> import unxt as u >>> q = u.Q([1, 2, 3], "m") >>> q.argmax() Array(2, dtype=int32)
- argmin(*args: Any, **kwargs: Any)
Return the indices of the minimum value.
Examples
>>> import unxt as u >>> q = u.Q([1, 2, 3], "m") >>> q.argmin() Array(0, dtype=int32)
- astype(*args: Any, **kwargs: Any)
Copy the array and cast to a specified dtype.
Examples
>>> import unxt as u >>> q = u.Q([1, 2, 3], "m") >>> q.dtype dtype('int32')
>>> q.astype(float) Quantity(Array([1., 2., 3.], dtype=float32), unit='m')
- Parameters:
- Return type:
- property at: _QuantityIndexUpdateHelper
Helper property for index update functionality.
The
atproperty provides a functionally pure equivalent of in-place array modifications.In particular:
Alternate syntax
Equivalent In-place expression
x = x.at[idx].set(y)x[idx] = yx = x.at[idx].add(y)x[idx] += yx = x.at[idx].subtract(y)x[idx] -= yx = x.at[idx].multiply(y)x[idx] *= yx = x.at[idx].divide(y)x[idx] /= yx = x.at[idx].power(y)x[idx] **= yx = x.at[idx].min(y)x[idx] = minimum(x[idx], y)x = x.at[idx].max(y)x[idx] = maximum(x[idx], y)x = x.at[idx].apply(ufunc)ufunc.at(x, idx)x = x.at[idx].get()x = x[idx]None of the
x.atexpressions modify the originalx; instead they return a modified copy ofx. However, inside ajit()compiled function, expressions likex = x.at[idx].set(y)are guaranteed to be applied in-place.Unlike NumPy in-place operations such as
x[idx] += y, if multiple indices refer to the same location, all updates will be applied (NumPy would only apply the last update, rather than applying all updates.) The order in which conflicting updates are applied is implementation-defined and may be nondeterministic (e.g., due to concurrency on some hardware platforms).By default, JAX assumes that all indices are in-bounds. Alternative out-of-bound index semantics can be specified via the
modeparameter (see below).- Parameters:
mode –
string specifying out-of-bound indexing mode. Options are:
"promise_in_bounds": (default) The user promises that indices are in bounds. No additional checking will be performed. In practice, this means that out-of-bounds indices inget()will be clipped, and out-of-bounds indices inset(),add(), etc. will be dropped."clip": clamp out of bounds indices into valid range."drop": ignore out-of-bound indices."fill": alias for"drop". For get(), the optionalfill_valueargument specifies the value that will be returned.
See
jax.lax.GatherScatterModefor more details.wrap_negative_indices – If True (default) then negative indices indicate position from the end of the array, similar to Python and NumPy indexing. If False, then negative indices are considered out-of-bounds and behave according to the
modeparameter.fill_value – Only applies to the
get()method: the fill value to return for out-of-bounds slices whenmodeis'fill'. Ignored otherwise. Defaults toNaNfor inexact types, the largest negative value for signed types, the largest positive value for unsigned types, andTruefor booleans.indices_are_sorted – If True, the implementation will assume that the (normalized) indices passed to
at[]are sorted in ascending order, which can lead to more efficient execution on some backends. If True but the indices are not actually sorted, the output is undefined.unique_indices – If True, the implementation will assume that the (normalized) indices passed to
at[]are unique, which can result in more efficient execution on some backends. If True but the indices are not actually unique, the output is undefined.
Examples
>>> x = jnp.arange(5.0) >>> x Array([0., 1., 2., 3., 4.], dtype=float32) >>> x.at[2].get() Array(2., dtype=float32) >>> x.at[2].add(10) Array([ 0., 1., 12., 3., 4.], dtype=float32)
By default, out-of-bound indices are ignored in updates, but this behavior can be controlled with the
modeparameter:>>> x.at[10].add(10) # dropped Array([0., 1., 2., 3., 4.], dtype=float32) >>> x.at[20].add(10, mode='clip') # clipped Array([ 0., 1., 2., 3., 14.], dtype=float32)
For
get(), out-of-bound indices are clipped by default:>>> x.at[20].get() # out-of-bounds indices clipped Array(4., dtype=float32) >>> x.at[20].get(mode='fill') # out-of-bounds indices filled with NaN Array(nan, dtype=float32) >>> x.at[20].get(mode='fill', fill_value=-1) # custom fill value Array(-1., dtype=float32)
Negative indices count from the end of the array, but this behavior can be disabled by setting
wrap_negative_indices = False:>>> x.at[-1].set(99) Array([ 0., 1., 2., 3., 99.], dtype=float32) >>> x.at[-1].set(99, wrap_negative_indices=False, mode='drop') # dropped! Array([0., 1., 2., 3., 4.], dtype=float32)
- block_until_ready()
Block until the array is ready.
- Return type:
Examples
>>> import unxt as u >>> q = u.Q(1, "m") >>> q.block_until_ready() is q True
- decompose(bases: Sequence[UnitBase | FunctionUnitBase | str], /)
Decompose the quantity into the given bases.
Examples
>>> from unxt import Quantity
>>> q = Quantity(1, "m") >>> q.decompose(["cm", "s"]) Quantity(Array(100., dtype=float32, ...), unit='cm')
- Parameters:
bases (
Sequence[UnitBase|FunctionUnitBase|str])- Return type:
- property device: Device
Device where the array is located.
Examples
>>> import unxt as u >>> u.Q(1, "m").device CpuDevice(id=0)
- devices()
Return the devices where the array is located.
Examples
>>> import unxt as u >>> q = u.Q(1, "m") >>> q.devices() {CpuDevice(id=0)}
- property distance: AbstractDistance
The distance.
Examples
>>> import coordinax.distances as cxd >>> d = cxd.Distance(10, "km") >>> d.distance is d True
>>> import coordinaxs.astro as cxastro >>> cxastro.DistanceModulus(10, "mag").distance Distance(1000., 'pc')
>>> p = cxastro.Parallax(1, "mas") >>> p.distance.to("kpc") Distance(1., 'kpc')
- property dtype: dtype
Data type of the array.
Examples
>>> import unxt as u >>> u.Q(1, "m").dtype dtype('int32')
- flatten()
Return a flattened version of the array.
- Return type:
Examples
>>> import unxt as u >>> q = u.Q([[1, 2], [3, 4]], "m") >>> q.flatten() Quantity(Array([1, 2, 3, 4], dtype=int32), unit='m')
- classmethod from_(*args, **kwargs)
- from_(cls: type[AbstractQuantity], value: ArrayLike | list[jaxtyping.Shaped[Array, ''] | jaxtyping.Shaped[ndarray, ''] | bool | number | bool | int | float | complex] | tuple[jaxtyping.Shaped[Array, ''] | jaxtyping.Shaped[ndarray, ''] | bool | number | bool | int | float | complex, ...], unit: Any, /, *, dtype: Any = None) AbstractQuantity
Construct a unxt.AbstractQuantity from an array-like value and a unit.
- Parameters:
- Return type:
Examples
For this example we’ll use the Quantity class. The same applies to any subclass of AbstractQuantity.
>>> import jax.numpy as jnp >>> import unxt as u
>>> x = jnp.array([1.0, 2, 3]) >>> u.Q.from_(x, "m") Quantity(Array([1., 2., 3.], dtype=float32), unit='m')
>>> u.Q.from_([1.0, 2, 3], "m") Quantity(Array([1., 2., 3.], dtype=float32), unit='m')
>>> u.Q.from_((1.0, 2, 3), "m") Quantity(Array([1., 2., 3.], dtype=float32), unit='m')
- from_(cls: type[AbstractQuantity], value: ArrayLike | list[jaxtyping.Shaped[Array, ''] | jaxtyping.Shaped[ndarray, ''] | bool | number | bool | int | float | complex] | tuple[jaxtyping.Shaped[Array, ''] | jaxtyping.Shaped[ndarray, ''] | bool | number | bool | int | float | complex, ...], /, *, unit: Any, dtype: Any = None) AbstractQuantity
Make a unxt.AbstractQuantity from an array-like value and a unit kwarg.
Examples
For this example we’ll use the unxt.Quantity class. The same applies to any subclass of unxt.AbstractQuantity.
>>> import unxt as u >>> u.Q.from_([1.0, 2, 3], unit="m") Quantity(Array([1., 2., 3.], dtype=float32), unit='m')
- from_(cls: type[AbstractQuantity], *, value: Any, unit: Any, dtype: Any = None) AbstractQuantity
Construct a AbstractQuantity from value and unit kwargs.
Examples
For this example we’ll use the Quantity class. The same applies to any subclass of AbstractQuantity.
>>> import unxt as u >>> u.Q.from_(value=[1.0, 2, 3], unit="m") Quantity(Array([1., 2., 3.], dtype=float32), unit='m')
- from_(cls: type[AbstractQuantity], mapping: Mapping[str, Any]) AbstractQuantity
Construct a quantity from a Mapping.
Examples
For this example we’ll use the Quantity class. The same applies to any subclass of AbstractQuantity.
>>> import jax.numpy as jnp >>> import unxt as u
>>> x = jnp.array([1.0, 2, 3]) >>> q = u.Q.from_({"value": x, "unit": "m"}) >>> q Quantity(Array([1., 2., 3.], dtype=float32), unit='m')
>>> u.Q.from_({"value": q, "unit": "km"}) Quantity(Array([0.001, 0.002, 0.003], dtype=float32), unit='km')
- from_(cls: type[AbstractQuantity], value: AbstractQuantity, unit: Any, /, *, dtype: Any = None) AbstractQuantity
Construct a quantity from another quantity.
The value is converted to the new unit.
Examples
>>> import unxt as u
>>> q = u.Q(1, "m") >>> u.Q.from_(q, "cm") Quantity(Array(100., dtype=float32, ...), unit='cm')
- from_(cls: type[AbstractQuantity], value: AbstractQuantity, unit: NoneType, /, *, dtype: Any = None) AbstractQuantity
Construct a quantity from another quantity.
The value is converted to the new unit.
Examples
>>> import unxt as u
>>> q = u.Q(1, "m") >>> u.Q.from_(q, None) Quantity(Array(1, dtype=int32...), unit='m')
- from_(cls: type[AbstractQuantity], value: AbstractQuantity, /, *, unit: Any | None = None, dtype: Any = None) AbstractQuantity
Construct a quantity from another quantity.
The unit is unchanged.
- from_(cls: type[StaticQuantity], value: ArrayLike | list[jaxtyping.Shaped[Array, ''] | jaxtyping.Shaped[ndarray, ''] | bool | number | bool | int | float | complex] | tuple[jaxtyping.Shaped[Array, ''] | jaxtyping.Shaped[ndarray, ''] | bool | number | bool | int | float | complex, ...], unit: Any, /, *, dtype: Any = None) StaticQuantity
Construct a StaticQuantity, keeping the value on NumPy dtypes.
The generic
AbstractQuantity.from_routes the value throughjnp.asarray, which applies JAX’s x64-disabled dtype rules and silently downcasts int64 / float64 to int32 / float32. A StaticQuantity stores its value verbatim, so hand the value straight to__init__and let theStaticValue.from_converter convert it – that preserves the NumPy dtype. Delegating (rather than callingnp.asarrayhere) also keeps.from_and__init__under the same policy for JAX inputs, instead of materialising an array the constructor would reject. (The keyword-unitoverload delegates here, so it is covered too.)Examples
>>> import numpy as np >>> import unxt as u
>>> u.StaticQuantity.from_( ... np.array([1, 2, 3], dtype=np.int64), "m" ... ).value.array.dtype dtype('int64')
.from_applies the same JAX-input policy as__init__– it delegates rather than converting first, so the two cannot drift:>>> import jax.numpy as jnp >>> u.StaticQuantity.from_(jnp.array([1.0, 2.0]), "m") StaticQuantity(array([1., 2.], dtype=float32), unit='m')
- from_(cls: type[AbstractQuantity], value: Quantity, /, **kwargs: Any) AbstractQuantity
Construct a quantity from an astropy Quantity.
The value is converted to the new unit.
Examples
>>> import unxt as u >>> import astropy.units as apyu
>>> u.Q.from_(apyu.Quantity(1, "m")) Quantity(Array(1., dtype=float32), unit='m')
- from_(cls: type[AbstractQuantity], value: Quantity, u: Any, /, **kwargs: Any) AbstractQuantity
Construct a quantity from an astropy Quantity, converting to a target unit.
The value is converted to the new unit.
Examples
>>> import unxt as u >>> import astropy.units as apyu
>>> u.Q.from_(apyu.Quantity(1, "m"), "cm") Quantity(Array(100., dtype=float32), unit='cm')
Construct a distance.
>>> import unxt as u >>> import coordinax.distances as cxd >>> cxd.Distance.from_(1, "kpc") Distance(1, 'kpc')
Compute distance from distance.
>>> import unxt as u >>> import coordinax.distances as cxd
>>> d = cxd.Distance(1, "kpc") >>> cxd.Distance.from_(d) is d True
>>> cxd.Distance.from_(d, dtype=float) Distance(1., 'kpc')
- from_(cls: type[Distance], q: AbstractQuantity, /, **kw: Any) Distance
Construct a distance from a quantity, dispatching on its dimensions.
From a length quantity:
>>> import unxt as u >>> import coordinax.distances as cxd >>> q = u.Q(1, "kpc") >>> cxd.Distance.from_(q, dtype=float) Distance(1., 'kpc')
From a parallax angle:
>>> q = u.Q(1, "mas") >>> cxd.Distance.from_(q).uconvert("pc").round(2) Distance(1000., 'pc')
From a distance modulus:
>>> q = u.Q(10, "mag") >>> cxd.Distance.from_(q).uconvert("pc").round(2) Distance(1000., 'pc')
- from_(cls: type[DistanceModulus], value: ArrayLike, unit: Any, /, **kw: Any) DistanceModulus
Construct a distance.
>>> import unxt as u >>> from coordinaxs.astro import DistanceModulus
>>> DistanceModulus.from_(1, "mag") DistanceModulus(1, 'mag')
- from_(cls: type[DistanceModulus], dm: DistanceModulus, /, **kw: Any) DistanceModulus
Compute distance modulus from distance modulus.
>>> import unxt as u >>> from coordinaxs.astro import DistanceModulus
>>> dm = DistanceModulus(1, "mag") >>> DistanceModulus.from_(dm) is dm True
>>> DistanceModulus.from_(dm, dtype=float) DistanceModulus(1., 'mag')
- from_(cls: type[DistanceModulus], q: AbstractQuantity, /, **kw: Any) DistanceModulus
Construct a distance modulus from a quantity, dispatching on dimensions.
From a distance:
>>> import unxt as u >>> import coordinax.distances as cxd >>> from coordinaxs.astro import DistanceModulus
>>> d = cxd.Distance(1, "pc") >>> DistanceModulus.from_(d) DistanceModulus(-5., 'mag')
>>> q = u.Q(1, "pc") >>> DistanceModulus.from_(q) DistanceModulus(-5., 'mag')
From a parallax angle:
>>> q = u.Q(1, "mas") >>> DistanceModulus.from_(q) DistanceModulus(10., 'mag')
From a distance modulus (magnitude):
>>> q = u.Q(1, "mag") >>> DistanceModulus.from_(q) DistanceModulus(1, 'mag')
- from_(cls: type[Distance], dm: DistanceModulus, /, **kw: Any) Distance
Compute distance from distance modulus.
>>> import coordinax.distances as cxd >>> from coordinaxs.astro import DistanceModulus
>>> dm = DistanceModulus(10, "mag") >>> cxd.Distance.from_(dm).uconvert("pc").round(2) Distance(1000., 'pc')
Construct a distance.
>>> import unxt as u >>> from coordinaxs.astro import Parallax
>>> Parallax.from_(1, "mas") Parallax(1, 'mas')
Compute parallax from parallax.
>>> import unxt as u >>> from coordinaxs.astro import Parallax
>>> p = Parallax(1, "mas") >>> Parallax.from_(p) is p True
>>> Parallax.from_(p, dtype=float) Parallax(1., 'mas')
- from_(cls: type[Parallax], q: AbstractQuantity, /, **kw: Any) Parallax
Construct a parallax from a quantity, dispatching on its dimensions.
From a parallax angle:
>>> import unxt as u >>> import coordinax.distances as cxd >>> from coordinaxs.astro import Parallax
>>> q = u.Q(1, "mas") >>> Parallax.from_(q, dtype=float) Parallax(1., 'mas')
From a distance:
>>> d = cxd.Distance(10, "pc") >>> Parallax.from_(d).uconvert("mas").round(2) Parallax(100., 'mas')
>>> q = u.Q(10, "pc") >>> Parallax.from_(q).uconvert("mas").round(2) Parallax(100., 'mas')
From a distance modulus:
>>> dm = u.Q(10, "mag") >>> Parallax.from_(dm).uconvert("mas").round(2) Parallax(1., 'mas')
Compute distance from parallax.
>>> import coordinax.distances as cxd >>> from coordinaxs.astro import Parallax
>>> p = Parallax(1, "mas") >>> cxd.Distance.from_(p).uconvert("pc").round(2) Distance(1000., 'pc')
- from_(cls: type[DistanceModulus], p: Parallax, /, **kw: Any) DistanceModulus
Compute distance modulus from parallax.
>>> from coordinaxs.astro import DistanceModulus, Parallax >>> p = Parallax(1, "mas") >>> DistanceModulus.from_(p) DistanceModulus(10., 'mag')
- from_(cls: type[Parallax], dm: DistanceModulus, /, **kw: Any) Parallax
Convert distance modulus to parallax.
>>> import unxt as u >>> from coordinaxs.astro import DistanceModulus, Parallax >>> dm = DistanceModulus(10, "mag") >>> Parallax.from_(dm).uconvert("mas").round(2) Parallax(1., 'mas')
- Parameters:
cls (
type[AbstractQuantity])args (
Any)kwargs (
Any)
- Return type:
AbstractQuantity
- is_equivalent(other: AbstractQuantity, /)
Whether
selfandotherare physically equal (unit-aware).The method form of unxt.equivalent; unlike
==(which is unit-blind for StaticValue-backed quantities) this accounts for unit conversion.Examples
>>> import unxt as u >>> u.Q(1000.0, "m").is_equivalent(u.Q(1.0, "km")) Quantity(Array(True, dtype=bool...), unit='')
- Parameters:
other (
AbstractQuantity)- Return type:
- property mT: AbstractQuantity
Matrix transpose of the array.
Examples
>>> import unxt as u >>> q = u.Q([[0, 1], [2, 3]], "m") >>> q.mT Quantity(Array([[0, 2], [1, 3]], dtype=int32), unit='m')
It also works for a
StaticQuantity:>>> u.StaticQuantity([[0, 1], [2, 3]], "m").mT.value.tolist() [[0, 2], [1, 3]]
- max(*args: Any, **kwargs: Any)
Return the maximum value.
Examples
>>> import unxt as u >>> q = u.Q([1, 2, 3], "m") >>> q.max() Quantity(Array(3, dtype=int32), unit='m')
- Parameters:
- Return type:
- mean(*args: Any, **kwargs: Any)
Return the mean value.
Examples
>>> import unxt as u >>> q = u.Q([1, 2, 3], "m") >>> q.mean() Quantity(Array(2., dtype=float32), unit='m')
- Parameters:
- Return type:
- min(*args: Any, **kwargs: Any)
Return the minimum value.
Examples
>>> import unxt as u >>> q = u.Q([1, 2, 3], "m") >>> q.min() Quantity(Array(1, dtype=int32), unit='m')
- Parameters:
- Return type:
- property ndim: int
Number of dimensions.
Examples
>>> import unxt as u >>> q = u.Q([[1]], "m") >>> q.ndim 2
- ravel()
Return a flattened version of the array.
- Return type:
Examples
>>> import unxt as u >>> q = u.Q([[1, 2], [3, 4]], "m") >>> q.ravel() Quantity(Array([1, 2, 3, 4], dtype=int32), unit='m')
- reshape(*args: Any, order: str = 'C')
Return a reshaped version of the array.
Examples
>>> import unxt as u >>> q = u.Q([1, 2, 3, 4], "m") >>> q.reshape(2, 2) Quantity(Array([[1, 2], [3, 4]], dtype=int32), unit='m')
- Parameters:
- Return type:
- round(*args: Any, **kwargs: Any)
Round the array to the given number of decimals.
Examples
>>> import unxt as u >>> q = u.Q([1.1, 2.2, 3.3], "m") >>> q.round(0) Quantity(Array([1., 2., 3.], dtype=float32), unit='m')
- Parameters:
- Return type:
- property sharding: Any
Return the sharding configuration of the array.
Examples
>>> import unxt as u >>> q = u.Q([1, 2, 3], "m") >>> q.sharding SingleDeviceSharding(device=..., memory_kind=...)
- property size: int
Total number of elements.
Examples
>>> import unxt as u >>> q = u.Q([1, 2, 3], "m") >>> q.size 3
- squeeze(*args: Any, **kwargs: Any)
Return the array with all single-dimensional entries removed.
Examples
>>> import unxt as u >>> q = u.Q([[[1], [2], [3]]], "m") >>> q.squeeze() Quantity(Array([1, 2, 3], dtype=int32), unit='m')
- Parameters:
- Return type:
- to(u: Any, /)
Convert the quantity to the given units.
See unxt.quantity.AbstractQuantity.uconvert.
Examples
>>> from unxt import Quantity
>>> q = Quantity(1, "m") >>> q.to("cm") Quantity(Array(100., dtype=float32, ...), unit='cm')
- Parameters:
u (
Any)- Return type:
- to_device(device: None | Device = None)
Move the array to a new device.
Examples
>>> import unxt as u >>> q = u.Q(1, "m") >>> q.to_device(None) Quantity(Array(1, dtype=int32...), unit='m')
- Parameters:
- Return type:
- to_value(u: Any, /)
Return the value in the given units.
See unxt.AbstractQuantity.ustrip.
Examples
>>> from unxt import Quantity
>>> q = Quantity(1, "m") >>> q.to_value("cm") Array(100., dtype=float32, weak_type=True)
- final class coordinax.Angle(value: Any, unit: Any)
Bases:
AbstractAngleAngular quantity.
Examples
>>> import unxt as u
Create an Angle:
>>> q = u.Angle(1, "rad") >>> q Angle(Array(1, dtype=int32...), unit='rad')
Wrap an Angle to a range:
>>> q = u.Angle(370, "deg") >>> q.wrap_to(u.Q(0, "deg"), u.Q(360, "deg")) Angle(Array(10, dtype=int32...), unit='deg')
Create an Angle array:
>>> q = u.Angle([1, 2, 3], "deg") >>> q Angle(Array([1, 2, 3], dtype=int32), unit='deg')
Do math on an Angle:
>>> 2 * q Angle(Array([2, 4, 6], dtype=int32), unit='deg')
>>> q % u.Q(4, "deg") Angle(Array([1, 2, 3], dtype=int32), unit='deg')
- value: Real[Array, '*shape'] | Real[StaticValue, '*shape']
The value of the unxt.AbstractQuantity.
- property T: AbstractQuantity
Transpose of the array.
Examples
>>> import unxt as u >>> q = u.Q([[0, 1], [1, 2]], "m") >>> q.T Quantity(Array([[0, 1], [1, 2]], dtype=int32), unit='m')
- argmax(*args: Any, **kwargs: Any)
Return the indices of the maximum value.
Examples
>>> import unxt as u >>> q = u.Q([1, 2, 3], "m") >>> q.argmax() Array(2, dtype=int32)
- argmin(*args: Any, **kwargs: Any)
Return the indices of the minimum value.
Examples
>>> import unxt as u >>> q = u.Q([1, 2, 3], "m") >>> q.argmin() Array(0, dtype=int32)
- astype(*args: Any, **kwargs: Any)
Copy the array and cast to a specified dtype.
Examples
>>> import unxt as u >>> q = u.Q([1, 2, 3], "m") >>> q.dtype dtype('int32')
>>> q.astype(float) Quantity(Array([1., 2., 3.], dtype=float32), unit='m')
- Parameters:
- Return type:
- property at: _QuantityIndexUpdateHelper
Helper property for index update functionality.
The
atproperty provides a functionally pure equivalent of in-place array modifications.In particular:
Alternate syntax
Equivalent In-place expression
x = x.at[idx].set(y)x[idx] = yx = x.at[idx].add(y)x[idx] += yx = x.at[idx].subtract(y)x[idx] -= yx = x.at[idx].multiply(y)x[idx] *= yx = x.at[idx].divide(y)x[idx] /= yx = x.at[idx].power(y)x[idx] **= yx = x.at[idx].min(y)x[idx] = minimum(x[idx], y)x = x.at[idx].max(y)x[idx] = maximum(x[idx], y)x = x.at[idx].apply(ufunc)ufunc.at(x, idx)x = x.at[idx].get()x = x[idx]None of the
x.atexpressions modify the originalx; instead they return a modified copy ofx. However, inside ajit()compiled function, expressions likex = x.at[idx].set(y)are guaranteed to be applied in-place.Unlike NumPy in-place operations such as
x[idx] += y, if multiple indices refer to the same location, all updates will be applied (NumPy would only apply the last update, rather than applying all updates.) The order in which conflicting updates are applied is implementation-defined and may be nondeterministic (e.g., due to concurrency on some hardware platforms).By default, JAX assumes that all indices are in-bounds. Alternative out-of-bound index semantics can be specified via the
modeparameter (see below).- Parameters:
mode –
string specifying out-of-bound indexing mode. Options are:
"promise_in_bounds": (default) The user promises that indices are in bounds. No additional checking will be performed. In practice, this means that out-of-bounds indices inget()will be clipped, and out-of-bounds indices inset(),add(), etc. will be dropped."clip": clamp out of bounds indices into valid range."drop": ignore out-of-bound indices."fill": alias for"drop". For get(), the optionalfill_valueargument specifies the value that will be returned.
See
jax.lax.GatherScatterModefor more details.wrap_negative_indices – If True (default) then negative indices indicate position from the end of the array, similar to Python and NumPy indexing. If False, then negative indices are considered out-of-bounds and behave according to the
modeparameter.fill_value – Only applies to the
get()method: the fill value to return for out-of-bounds slices whenmodeis'fill'. Ignored otherwise. Defaults toNaNfor inexact types, the largest negative value for signed types, the largest positive value for unsigned types, andTruefor booleans.indices_are_sorted – If True, the implementation will assume that the (normalized) indices passed to
at[]are sorted in ascending order, which can lead to more efficient execution on some backends. If True but the indices are not actually sorted, the output is undefined.unique_indices – If True, the implementation will assume that the (normalized) indices passed to
at[]are unique, which can result in more efficient execution on some backends. If True but the indices are not actually unique, the output is undefined.
Examples
>>> x = jnp.arange(5.0) >>> x Array([0., 1., 2., 3., 4.], dtype=float32) >>> x.at[2].get() Array(2., dtype=float32) >>> x.at[2].add(10) Array([ 0., 1., 12., 3., 4.], dtype=float32)
By default, out-of-bound indices are ignored in updates, but this behavior can be controlled with the
modeparameter:>>> x.at[10].add(10) # dropped Array([0., 1., 2., 3., 4.], dtype=float32) >>> x.at[20].add(10, mode='clip') # clipped Array([ 0., 1., 2., 3., 14.], dtype=float32)
For
get(), out-of-bound indices are clipped by default:>>> x.at[20].get() # out-of-bounds indices clipped Array(4., dtype=float32) >>> x.at[20].get(mode='fill') # out-of-bounds indices filled with NaN Array(nan, dtype=float32) >>> x.at[20].get(mode='fill', fill_value=-1) # custom fill value Array(-1., dtype=float32)
Negative indices count from the end of the array, but this behavior can be disabled by setting
wrap_negative_indices = False:>>> x.at[-1].set(99) Array([ 0., 1., 2., 3., 99.], dtype=float32) >>> x.at[-1].set(99, wrap_negative_indices=False, mode='drop') # dropped! Array([0., 1., 2., 3., 4.], dtype=float32)
- block_until_ready()
Block until the array is ready.
- Return type:
Examples
>>> import unxt as u >>> q = u.Q(1, "m") >>> q.block_until_ready() is q True
- decompose(bases: Sequence[UnitBase | FunctionUnitBase | str], /)
Decompose the quantity into the given bases.
Examples
>>> from unxt import Quantity
>>> q = Quantity(1, "m") >>> q.decompose(["cm", "s"]) Quantity(Array(100., dtype=float32, ...), unit='cm')
- Parameters:
bases (
Sequence[UnitBase|FunctionUnitBase|str])- Return type:
- property device: Device
Device where the array is located.
Examples
>>> import unxt as u >>> u.Q(1, "m").device CpuDevice(id=0)
- devices()
Return the devices where the array is located.
Examples
>>> import unxt as u >>> q = u.Q(1, "m") >>> q.devices() {CpuDevice(id=0)}
- property dtype: dtype
Data type of the array.
Examples
>>> import unxt as u >>> u.Q(1, "m").dtype dtype('int32')
- flatten()
Return a flattened version of the array.
- Return type:
Examples
>>> import unxt as u >>> q = u.Q([[1, 2], [3, 4]], "m") >>> q.flatten() Quantity(Array([1, 2, 3, 4], dtype=int32), unit='m')
- classmethod from_(*args, **kwargs)
- from_(cls: type[AbstractQuantity], value: ArrayLike | list[jaxtyping.Shaped[Array, ''] | jaxtyping.Shaped[ndarray, ''] | bool | number | bool | int | float | complex] | tuple[jaxtyping.Shaped[Array, ''] | jaxtyping.Shaped[ndarray, ''] | bool | number | bool | int | float | complex, ...], unit: Any, /, *, dtype: Any = None) AbstractQuantity
Construct a unxt.AbstractQuantity from an array-like value and a unit.
- Parameters:
- Return type:
Examples
For this example we’ll use the Quantity class. The same applies to any subclass of AbstractQuantity.
>>> import jax.numpy as jnp >>> import unxt as u
>>> x = jnp.array([1.0, 2, 3]) >>> u.Q.from_(x, "m") Quantity(Array([1., 2., 3.], dtype=float32), unit='m')
>>> u.Q.from_([1.0, 2, 3], "m") Quantity(Array([1., 2., 3.], dtype=float32), unit='m')
>>> u.Q.from_((1.0, 2, 3), "m") Quantity(Array([1., 2., 3.], dtype=float32), unit='m')
- from_(cls: type[AbstractQuantity], value: ArrayLike | list[jaxtyping.Shaped[Array, ''] | jaxtyping.Shaped[ndarray, ''] | bool | number | bool | int | float | complex] | tuple[jaxtyping.Shaped[Array, ''] | jaxtyping.Shaped[ndarray, ''] | bool | number | bool | int | float | complex, ...], /, *, unit: Any, dtype: Any = None) AbstractQuantity
Make a unxt.AbstractQuantity from an array-like value and a unit kwarg.
Examples
For this example we’ll use the unxt.Quantity class. The same applies to any subclass of unxt.AbstractQuantity.
>>> import unxt as u >>> u.Q.from_([1.0, 2, 3], unit="m") Quantity(Array([1., 2., 3.], dtype=float32), unit='m')
- from_(cls: type[AbstractQuantity], *, value: Any, unit: Any, dtype: Any = None) AbstractQuantity
Construct a AbstractQuantity from value and unit kwargs.
Examples
For this example we’ll use the Quantity class. The same applies to any subclass of AbstractQuantity.
>>> import unxt as u >>> u.Q.from_(value=[1.0, 2, 3], unit="m") Quantity(Array([1., 2., 3.], dtype=float32), unit='m')
- from_(cls: type[AbstractQuantity], mapping: Mapping[str, Any]) AbstractQuantity
Construct a quantity from a Mapping.
Examples
For this example we’ll use the Quantity class. The same applies to any subclass of AbstractQuantity.
>>> import jax.numpy as jnp >>> import unxt as u
>>> x = jnp.array([1.0, 2, 3]) >>> q = u.Q.from_({"value": x, "unit": "m"}) >>> q Quantity(Array([1., 2., 3.], dtype=float32), unit='m')
>>> u.Q.from_({"value": q, "unit": "km"}) Quantity(Array([0.001, 0.002, 0.003], dtype=float32), unit='km')
- from_(cls: type[AbstractQuantity], value: AbstractQuantity, unit: Any, /, *, dtype: Any = None) AbstractQuantity
Construct a quantity from another quantity.
The value is converted to the new unit.
Examples
>>> import unxt as u
>>> q = u.Q(1, "m") >>> u.Q.from_(q, "cm") Quantity(Array(100., dtype=float32, ...), unit='cm')
- from_(cls: type[AbstractQuantity], value: AbstractQuantity, unit: NoneType, /, *, dtype: Any = None) AbstractQuantity
Construct a quantity from another quantity.
The value is converted to the new unit.
Examples
>>> import unxt as u
>>> q = u.Q(1, "m") >>> u.Q.from_(q, None) Quantity(Array(1, dtype=int32...), unit='m')
- from_(cls: type[AbstractQuantity], value: AbstractQuantity, /, *, unit: Any | None = None, dtype: Any = None) AbstractQuantity
Construct a quantity from another quantity.
The unit is unchanged.
- from_(cls: type[StaticQuantity], value: ArrayLike | list[jaxtyping.Shaped[Array, ''] | jaxtyping.Shaped[ndarray, ''] | bool | number | bool | int | float | complex] | tuple[jaxtyping.Shaped[Array, ''] | jaxtyping.Shaped[ndarray, ''] | bool | number | bool | int | float | complex, ...], unit: Any, /, *, dtype: Any = None) StaticQuantity
Construct a StaticQuantity, keeping the value on NumPy dtypes.
The generic
AbstractQuantity.from_routes the value throughjnp.asarray, which applies JAX’s x64-disabled dtype rules and silently downcasts int64 / float64 to int32 / float32. A StaticQuantity stores its value verbatim, so hand the value straight to__init__and let theStaticValue.from_converter convert it – that preserves the NumPy dtype. Delegating (rather than callingnp.asarrayhere) also keeps.from_and__init__under the same policy for JAX inputs, instead of materialising an array the constructor would reject. (The keyword-unitoverload delegates here, so it is covered too.)Examples
>>> import numpy as np >>> import unxt as u
>>> u.StaticQuantity.from_( ... np.array([1, 2, 3], dtype=np.int64), "m" ... ).value.array.dtype dtype('int64')
.from_applies the same JAX-input policy as__init__– it delegates rather than converting first, so the two cannot drift:>>> import jax.numpy as jnp >>> u.StaticQuantity.from_(jnp.array([1.0, 2.0]), "m") StaticQuantity(array([1., 2.], dtype=float32), unit='m')
- from_(cls: type[AbstractQuantity], value: Quantity, /, **kwargs: Any) AbstractQuantity
Construct a quantity from an astropy Quantity.
The value is converted to the new unit.
Examples
>>> import unxt as u >>> import astropy.units as apyu
>>> u.Q.from_(apyu.Quantity(1, "m")) Quantity(Array(1., dtype=float32), unit='m')
- from_(cls: type[AbstractQuantity], value: Quantity, u: Any, /, **kwargs: Any) AbstractQuantity
Construct a quantity from an astropy Quantity, converting to a target unit.
The value is converted to the new unit.
Examples
>>> import unxt as u >>> import astropy.units as apyu
>>> u.Q.from_(apyu.Quantity(1, "m"), "cm") Quantity(Array(100., dtype=float32), unit='cm')
Construct a distance.
>>> import unxt as u >>> import coordinax.distances as cxd >>> cxd.Distance.from_(1, "kpc") Distance(1, 'kpc')
Compute distance from distance.
>>> import unxt as u >>> import coordinax.distances as cxd
>>> d = cxd.Distance(1, "kpc") >>> cxd.Distance.from_(d) is d True
>>> cxd.Distance.from_(d, dtype=float) Distance(1., 'kpc')
- from_(cls: type[Distance], q: AbstractQuantity, /, **kw: Any) Distance
Construct a distance from a quantity, dispatching on its dimensions.
From a length quantity:
>>> import unxt as u >>> import coordinax.distances as cxd >>> q = u.Q(1, "kpc") >>> cxd.Distance.from_(q, dtype=float) Distance(1., 'kpc')
From a parallax angle:
>>> q = u.Q(1, "mas") >>> cxd.Distance.from_(q).uconvert("pc").round(2) Distance(1000., 'pc')
From a distance modulus:
>>> q = u.Q(10, "mag") >>> cxd.Distance.from_(q).uconvert("pc").round(2) Distance(1000., 'pc')
- from_(cls: type[DistanceModulus], value: ArrayLike, unit: Any, /, **kw: Any) DistanceModulus
Construct a distance.
>>> import unxt as u >>> from coordinaxs.astro import DistanceModulus
>>> DistanceModulus.from_(1, "mag") DistanceModulus(1, 'mag')
- from_(cls: type[DistanceModulus], dm: DistanceModulus, /, **kw: Any) DistanceModulus
Compute distance modulus from distance modulus.
>>> import unxt as u >>> from coordinaxs.astro import DistanceModulus
>>> dm = DistanceModulus(1, "mag") >>> DistanceModulus.from_(dm) is dm True
>>> DistanceModulus.from_(dm, dtype=float) DistanceModulus(1., 'mag')
- from_(cls: type[DistanceModulus], q: AbstractQuantity, /, **kw: Any) DistanceModulus
Construct a distance modulus from a quantity, dispatching on dimensions.
From a distance:
>>> import unxt as u >>> import coordinax.distances as cxd >>> from coordinaxs.astro import DistanceModulus
>>> d = cxd.Distance(1, "pc") >>> DistanceModulus.from_(d) DistanceModulus(-5., 'mag')
>>> q = u.Q(1, "pc") >>> DistanceModulus.from_(q) DistanceModulus(-5., 'mag')
From a parallax angle:
>>> q = u.Q(1, "mas") >>> DistanceModulus.from_(q) DistanceModulus(10., 'mag')
From a distance modulus (magnitude):
>>> q = u.Q(1, "mag") >>> DistanceModulus.from_(q) DistanceModulus(1, 'mag')
- from_(cls: type[Distance], dm: DistanceModulus, /, **kw: Any) Distance
Compute distance from distance modulus.
>>> import coordinax.distances as cxd >>> from coordinaxs.astro import DistanceModulus
>>> dm = DistanceModulus(10, "mag") >>> cxd.Distance.from_(dm).uconvert("pc").round(2) Distance(1000., 'pc')
Construct a distance.
>>> import unxt as u >>> from coordinaxs.astro import Parallax
>>> Parallax.from_(1, "mas") Parallax(1, 'mas')
Compute parallax from parallax.
>>> import unxt as u >>> from coordinaxs.astro import Parallax
>>> p = Parallax(1, "mas") >>> Parallax.from_(p) is p True
>>> Parallax.from_(p, dtype=float) Parallax(1., 'mas')
- from_(cls: type[Parallax], q: AbstractQuantity, /, **kw: Any) Parallax
Construct a parallax from a quantity, dispatching on its dimensions.
From a parallax angle:
>>> import unxt as u >>> import coordinax.distances as cxd >>> from coordinaxs.astro import Parallax
>>> q = u.Q(1, "mas") >>> Parallax.from_(q, dtype=float) Parallax(1., 'mas')
From a distance:
>>> d = cxd.Distance(10, "pc") >>> Parallax.from_(d).uconvert("mas").round(2) Parallax(100., 'mas')
>>> q = u.Q(10, "pc") >>> Parallax.from_(q).uconvert("mas").round(2) Parallax(100., 'mas')
From a distance modulus:
>>> dm = u.Q(10, "mag") >>> Parallax.from_(dm).uconvert("mas").round(2) Parallax(1., 'mas')
Compute distance from parallax.
>>> import coordinax.distances as cxd >>> from coordinaxs.astro import Parallax
>>> p = Parallax(1, "mas") >>> cxd.Distance.from_(p).uconvert("pc").round(2) Distance(1000., 'pc')
- from_(cls: type[DistanceModulus], p: Parallax, /, **kw: Any) DistanceModulus
Compute distance modulus from parallax.
>>> from coordinaxs.astro import DistanceModulus, Parallax >>> p = Parallax(1, "mas") >>> DistanceModulus.from_(p) DistanceModulus(10., 'mag')
- from_(cls: type[Parallax], dm: DistanceModulus, /, **kw: Any) Parallax
Convert distance modulus to parallax.
>>> import unxt as u >>> from coordinaxs.astro import DistanceModulus, Parallax >>> dm = DistanceModulus(10, "mag") >>> Parallax.from_(dm).uconvert("mas").round(2) Parallax(1., 'mas')
- Parameters:
cls (
type[AbstractQuantity])args (
Any)kwargs (
Any)
- Return type:
AbstractQuantity
- is_equivalent(other: AbstractQuantity, /)
Whether
selfandotherare physically equal (unit-aware).The method form of unxt.equivalent; unlike
==(which is unit-blind for StaticValue-backed quantities) this accounts for unit conversion.Examples
>>> import unxt as u >>> u.Q(1000.0, "m").is_equivalent(u.Q(1.0, "km")) Quantity(Array(True, dtype=bool...), unit='')
- Parameters:
other (
AbstractQuantity)- Return type:
- property mT: AbstractQuantity
Matrix transpose of the array.
Examples
>>> import unxt as u >>> q = u.Q([[0, 1], [2, 3]], "m") >>> q.mT Quantity(Array([[0, 2], [1, 3]], dtype=int32), unit='m')
It also works for a
StaticQuantity:>>> u.StaticQuantity([[0, 1], [2, 3]], "m").mT.value.tolist() [[0, 2], [1, 3]]
- max(*args: Any, **kwargs: Any)
Return the maximum value.
Examples
>>> import unxt as u >>> q = u.Q([1, 2, 3], "m") >>> q.max() Quantity(Array(3, dtype=int32), unit='m')
- Parameters:
- Return type:
- mean(*args: Any, **kwargs: Any)
Return the mean value.
Examples
>>> import unxt as u >>> q = u.Q([1, 2, 3], "m") >>> q.mean() Quantity(Array(2., dtype=float32), unit='m')
- Parameters:
- Return type:
- min(*args: Any, **kwargs: Any)
Return the minimum value.
Examples
>>> import unxt as u >>> q = u.Q([1, 2, 3], "m") >>> q.min() Quantity(Array(1, dtype=int32), unit='m')
- Parameters:
- Return type:
- property ndim: int
Number of dimensions.
Examples
>>> import unxt as u >>> q = u.Q([[1]], "m") >>> q.ndim 2
- ravel()
Return a flattened version of the array.
- Return type:
Examples
>>> import unxt as u >>> q = u.Q([[1, 2], [3, 4]], "m") >>> q.ravel() Quantity(Array([1, 2, 3, 4], dtype=int32), unit='m')
- reshape(*args: Any, order: str = 'C')
Return a reshaped version of the array.
Examples
>>> import unxt as u >>> q = u.Q([1, 2, 3, 4], "m") >>> q.reshape(2, 2) Quantity(Array([[1, 2], [3, 4]], dtype=int32), unit='m')
- Parameters:
- Return type:
- round(*args: Any, **kwargs: Any)
Round the array to the given number of decimals.
Examples
>>> import unxt as u >>> q = u.Q([1.1, 2.2, 3.3], "m") >>> q.round(0) Quantity(Array([1., 2., 3.], dtype=float32), unit='m')
- Parameters:
- Return type:
- property sharding: Any
Return the sharding configuration of the array.
Examples
>>> import unxt as u >>> q = u.Q([1, 2, 3], "m") >>> q.sharding SingleDeviceSharding(device=..., memory_kind=...)
- property size: int
Total number of elements.
Examples
>>> import unxt as u >>> q = u.Q([1, 2, 3], "m") >>> q.size 3
- squeeze(*args: Any, **kwargs: Any)
Return the array with all single-dimensional entries removed.
Examples
>>> import unxt as u >>> q = u.Q([[[1], [2], [3]]], "m") >>> q.squeeze() Quantity(Array([1, 2, 3], dtype=int32), unit='m')
- Parameters:
- Return type:
- to(u: Any, /)
Convert the quantity to the given units.
See unxt.quantity.AbstractQuantity.uconvert.
Examples
>>> from unxt import Quantity
>>> q = Quantity(1, "m") >>> q.to("cm") Quantity(Array(100., dtype=float32, ...), unit='cm')
- Parameters:
u (
Any)- Return type:
- to_device(device: None | Device = None)
Move the array to a new device.
Examples
>>> import unxt as u >>> q = u.Q(1, "m") >>> q.to_device(None) Quantity(Array(1, dtype=int32...), unit='m')
- Parameters:
- Return type:
- to_value(u: Any, /)
Return the value in the given units.
See unxt.AbstractQuantity.ustrip.
Examples
>>> from unxt import Quantity
>>> q = Quantity(1, "m") >>> q.to_value("cm") Array(100., dtype=float32, weak_type=True)
- uconvert(u: Any, /)
Convert the quantity to the given units.
See also
Noneconvert a quantity to a new unit.
Examples
>>> import unxt as u
>>> q = u.Q(1, "m") >>> q.uconvert("cm") Quantity(Array(100., dtype=float32, ...), unit='cm')
- Parameters:
u (
Any)- Return type:
- ustrip(u: Any, /)
Return the value in the given units.
See also
Nonestrip the units from a quantity.
Examples
>>> import unxt as u
>>> q = u.Q(1, "m") >>> q.ustrip("cm") Array(100., dtype=float32, weak_type=True)
- wrap_to(min: AbstractQuantity, max: AbstractQuantity)
Wrap the angle to the range [min, max).
- Parameters:
min (
AbstractQuantity) – The minimum, maximum value of the range.max (
AbstractQuantity) – The minimum, maximum value of the range.
- Return type:
See also
Nonefunctional version of this method.
- Return type:
- Parameters:
min (AbstractQuantity)
max (AbstractQuantity)
Examples
>>> import unxt as u >>> angle = u.Angle(370, "deg") >>> angle.wrap_to(min=u.Q(0, "deg"), max=u.Q(360, "deg")) Angle(Array(10, dtype=int32...), unit='deg')
- unit: UnitBase | FunctionUnitBase
The unit associated with this value.
- exception coordinax.NoGlobalCartesianChartError
Bases:
ExceptionRaised when a chart has no global Cartesian representation.
Some charts represent coordinates on curved manifolds (e.g., 2-sphere) that cannot be globally mapped to a flat Cartesian space without singularities or discontinuities.
Examples
2-sphere charts (intrinsic coordinates on a spherical surface) have no global Cartesian 2D representation. To work with these charts:
Use an
EmbeddedChartto embed in 3D Euclidean spaceUse local projections when available
Work directly in the intrinsic coordinates
- add_note()
Exception.add_note(note) – add a note to the exception
- args
- with_traceback()
Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.
- final class coordinax.CartesianProductChart(factors: tuple[AbstractChart[Any, Any, Any], ...], factor_names: tuple[str, ...])
Bases:
AbstractCartesianProductChart[Ks,Ds]Concrete Cartesian product chart with dot-delimited component keys.
Constructs a product chart from a tuple of factor charts and factor names. Components are dot-delimited string keys
"factor_name.component_name"to avoid collisions (e.g., phase space with repeated Cart3D factors).- Parameters:
Examples
>>> import coordinax.charts as cxc >>> chart = cxc.CartesianProductChart((cxc.cart3d, cxc.cart3d), ("q", "p")) >>> chart.components ('q.x', 'q.y', 'q.z', 'p.x', 'p.y', 'p.z') >>> chart.ndim 6
>>> chart["p"] Cart3D(M=Rn(3))
>>> chart.M CartesianProductManifold(factors=(Rn(3), Rn(3)), factor_names=('q', 'p'))
- factors: tuple[AbstractChart[Any, Any, Any], ...]
Ordered tuple of factor charts.
- factor_names: tuple[str, ...]
Factor names for namespaced keys. Must be unique and aligned with factors.
- property M: CartesianProductManifold
Return the product manifold of the factor charts’ manifolds.
Examples
>>> import coordinax.charts as cxc >>> chart = cxc.CartesianProductChart((cxc.cart3d, cxc.cart3d), ("q", "p")) >>> chart.M CartesianProductManifold(factors=(Rn(3), Rn(3)), factor_names=('q', 'p'))
- check_data(data: CDictT, /, *, keys: bool = True, values: bool = False)
Check that the data is compatible with the chart.
- Parameters:
- Return type:
- property components: Ks
Component keys are dot-delimited strings
"factor_name.component".- Components are dot-delimited string keys to avoid collisions:
(f”{name_0}.{c}” for c in factors[0].components, …)
- property coord_dimensions: Ds
Concatenation of factor coordinate dimensions.
- merge_components(parts: tuple[Mapping[str, V], ...], /)
Merge factor CDicts into a single CDict.
For namespaced products: re-attach dot-delimited prefix
"name_i.c"for each factor key.
- property ndim: int
Total dimension, the sum of factor dimensions.
- split_components(p: dict, /)
Partition a CDict by factor components.
For namespaced products: select keys
"name_i.c"and strip prefix to yield factor dict keyed byc.
- property cartesian: CartesianProductChart
Get Cartesian version of a namespaced product chart (factorwise).
Returns a CartesianProductChart with each factor replaced by its cartesian_chart version, preserving factor_names.
Examples
>>> import coordinax.charts as cxc >>> product = cxc.CartesianProductChart((cxc.sph3d, cxc.sph3d), ("q", "p")) >>> cart_product = cxc.cartesian_chart(product) >>> cart_product CartesianProductChart( factors=(Cart3D(M=Rn(3)), Cart3D(M=Rn(3))), factor_names=('q', 'p') )
- coordinax.cartesian_chart(obj, /)
Return the corresponding Cartesian chart for a given chart.
This function provides the canonical flat-space Cartesian chart associated with any coordinate system. It maps each chart to its natural Cartesian equivalent in the same dimensional space.
Mathematical Definition:
For a coordinate chart \(\mathcal{R}\) in \(n\)-dimensional space, this returns the Cartesian chart \(\mathcal{C}_n\) such that:
\[\mathrm{cartesian\_chart}(\mathcal{R}) = \mathcal{C}_n\]where \(\mathcal{C}_n \in \{\text{Cart1D}, \text{Cart2D}, \text{Cart3D}, \text{CartND}\}\) depending on \(n = \text{ndim}\).
The Cartesian chart uses orthonormal basis vectors with components typically denoted \((x)\), \((x, y)\), \((x, y, z)\), or \((q_1, \ldots, q_n)\) for arbitrary dimension.
- Parameters:
obj (
Any) – A coordinate chart instance (e.g., coordinax.charts.sph3d, coordinax.charts.polar2d) or any object for which a Cartesian equivalent is defined.- Returns:
The Cartesian chart in the same dimensional space:
1D charts →
Cart1D(component:x)2D charts →
Cart2D(components:x,y)3D charts →
Cart3D(components:x,y,z)N-D charts →
CartND(components:q)
- Return type:
- Raises:
NotImplementedError – If no Cartesian chart is defined for the input object.
Notes
Cartesian charts use the Euclidean metric with orthonormal bases.
This function does not perform coordinate transformation; it only returns the chart type. Use {func}`~coordinax.charts.pt_map` for actual coordinate conversion.
All standard Euclidean coordinate systems (spherical, cylindrical, polar) map to their dimensional Cartesian equivalent.
For embedded manifolds, this returns the Cartesian form of the ambient space, not the intrinsic coordinates.
Intrinsic 2-sphere charts do not have a global Cartesian 2D chart and raise NoGlobalCartesianChartError.
See also
coordinax.charts.pt_mapTransform coordinates between charts
coordinax.charts.AbstractChartBase class for coordinate charts
Examples
>>> import coordinax.charts as cxc
1D coordinate systems map to Cart1D:
>>> cxc.cartesian_chart(cxc.cart1d) Cart1D(M=Rn(1))
>>> cxc.cartesian_chart(cxc.radial1d) Cart1D(M=Rn(1))
2D coordinate systems map to Cart2D:
>>> cxc.cartesian_chart(cxc.cart2d) Cart2D(M=Rn(2))
>>> cxc.cartesian_chart(cxc.polar2d) Cart2D(M=Rn(2))
3D coordinate systems map to Cart3D:
>>> cxc.cartesian_chart(cxc.cart3d) Cart3D(M=Rn(3))
>>> cxc.cartesian_chart(cxc.sph3d) Cart3D(M=Rn(3))
>>> cxc.cartesian_chart(cxc.cyl3d) Cart3D(M=Rn(3))
N-dimensional systems map to CartND:
>>> cxc.cartesian_chart(cxc.cartnd) CartND(M=Rn(True))
- coordinax.cartesian_chart(chart: AbstractChart, /) AbstractChart
- Parameters:
obj (Any)
- Return type:
Return the canonical Cartesian chart for a 0D chart.
>>> import coordinax.charts as cxc >>> cxc.cartesian_chart(cxc.cart0d) is cxc.cart0d True
- coordinax.guess_chart(*_)
Infer a chart from keys or from array/quantity trailing shape.
This is a lightweight heuristic API.
Examples
>>> import jax.numpy as jnp >>> import unxt as u >>> import coordinax.charts as cxc
>>> cxc.guess_chart(frozenset(("x", "y", "z"))) Cart3D(M=Rn(3))
>>> cxc.guess_chart({"x": 1.0, "y": 2.0, "z": 3.0}) Cart3D(M=Rn(3))
>>> q = u.Q([1.0, 2.0, 3.0], "m") >>> cxc.guess_chart(q) Cart3D(M=Rn(3))
>>> x = jnp.array([1.0, 2.0, 3.0]) >>> cxc.guess_chart(x) Cart3D(M=Rn(3))
- coordinax.guess_chart(obj: frozenset[str], /) AbstractChart
- Parameters:
_ (Any)
- Return type:
Infer a chart from the keys of a component dictionary.
Note that many charts may share the same component names (e.g., Spherical3D and MathSpherical3D both use ‘r’, ‘theta’, ‘phi’). These are completely indistinguishable from component names alone, so CANONICAL_CHART_CLASSES names the one to infer – the physics convention in both current cases. Pass the chart explicitly when the convention matters.
>>> import coordinax.charts as cxc >>> d = {"x": 1.0, "y": 2.0, "z": 3.0} >>> chart = cxc.guess_chart(d) >>> chart Cart3D(M=Rn(3))
- coordinax.guess_chart(obj: dict, /) AbstractChart
- Parameters:
_ (Any)
- Return type:
Infer a chart from the keys of a component dictionary.
Note that many charts may share the same component names (e.g., Spherical3D and MathSpherical3D both use ‘r’, ‘theta’, ‘phi’). These are completely indistinguishable from component names alone, so CANONICAL_CHART_CLASSES names the one to infer – the physics convention in both current cases. Pass the chart explicitly when the convention matters.
>>> import coordinax.charts as cxc >>> d = {"x": 1.0, "y": 2.0, "z": 3.0} >>> chart = cxc.guess_chart(d) >>> chart Cart3D(M=Rn(3))
- coordinax.guess_chart(_: jaxtyping.Shaped[Array, '*batch 1'] | jaxtyping.Shaped[ndarray, '*batch 1'] | jaxtyping.Shaped[AbstractQuantity, '*batch 1'], /) AbstractChart
- Parameters:
_ (Any)
- Return type:
Infer a 1D Cartesian chart from last dimension of a value / quantity.
>>> import unxt as u >>> import coordinax.charts as cxc >>> q = u.Q([1.0], "m") >>> cxc.guess_chart(q) Cart1D(M=Rn(1))
- coordinax.guess_chart(_: jaxtyping.Shaped[Array, '*batch 2'] | jaxtyping.Shaped[ndarray, '*batch 2'] | jaxtyping.Shaped[AbstractQuantity, '*batch 2'], /) AbstractChart
- Parameters:
_ (Any)
- Return type:
Infer a 2D Cartesian chart from last dimension of a value / quantity.
>>> import unxt as u >>> import coordinax.charts as cxc >>> q = u.Q([1.0, 2.0], "m") >>> cxc.guess_chart(q) Cart2D(M=Rn(2))
- coordinax.guess_chart(_: jaxtyping.Shaped[Array, '*batch 3'] | jaxtyping.Shaped[ndarray, '*batch 3'] | jaxtyping.Shaped[AbstractQuantity, '*batch 3'], /) AbstractChart
- Parameters:
_ (Any)
- Return type:
Infer a 3D Cartesian chart from last dimension of a value / quantity.
>>> import unxt as u >>> import coordinax.charts as cxc >>> q = u.Q([1.0, 2.0, 3.0], "m") >>> cxc.guess_chart(q) Cart3D(M=Rn(3))
- coordinax.guess_chart(_: jaxtyping.Shaped[Array, '*batch N'] | jaxtyping.Shaped[ndarray, '*batch N'] | jaxtyping.Shaped[AbstractQuantity, '*batch N'], /) AbstractChart
- Parameters:
_ (Any)
- Return type:
Infer a N-dimensional Cartesian chart from last dimension of a value / quantity.
>>> import unxt as u >>> import coordinax.charts as cxc >>> q = u.Q([1.0, 2.0, 3.0, 4.0], "m") >>> cxc.guess_chart(q) CartND(M=Rn(True))
- coordinax.guess_chart(obj: RadialRepresentation) Radial1D
- Parameters:
_ (Any)
- Return type:
Guess astropy.coordinates.RadialRepresentation ->
Radial1D.>>> import astropy.coordinates as apyc >>> import astropy.units as apyu >>> import coordinax.charts as cxc
>>> vec = apyc.RadialRepresentation(distance=1 * apyu.kpc) >>> cxc.guess_chart(vec) Radial1D(M=Rn(1))
- coordinax.guess_chart(obj: UnitSphericalRepresentation) LonLatSphericalTwoSphere
- Parameters:
_ (Any)
- Return type:
Guess
astropy.UnitSphericalRepresentation->LonLatSphericalTwoSphere.>>> import astropy.coordinates as apyc >>> import astropy.units as apyu >>> import coordinax.charts as cxc
>>> vec = apyc.UnitSphericalRepresentation(lon=90 * apyu.deg, lat=45 * apyu.deg) >>> cxc.guess_chart(vec) LonLatSphericalTwoSphere(M=Sn(2))
- coordinax.guess_chart(obj: CartesianRepresentation) Cart3D
- Parameters:
_ (Any)
- Return type:
Guess astropy.coordinates.CartesianRepresentation ->
Cart3D.>>> import astropy.coordinates as apyc >>> import coordinax.charts as cxc
>>> cxc.guess_chart(apyc.CartesianRepresentation(1, 2, 3)) Cart3D(M=Rn(3))
- coordinax.guess_chart(obj: CylindricalRepresentation) Cylindrical3D
- Parameters:
_ (Any)
- Return type:
Guess astropy.coordinates.CylindricalRepresentation ->
Cylindrical3D.>>> import astropy.coordinates as apyc >>> import astropy.units as apyu >>> import coordinax.charts as cxc
>>> vec = apyc.CylindricalRepresentation(1 * apyu.km, 2 * apyu.deg, 3 * apyu.km) >>> cxc.guess_chart(vec) Cylindrical3D(M=Rn(3))
- coordinax.guess_chart(obj: PhysicsSphericalRepresentation) Spherical3D
- Parameters:
_ (Any)
- Return type:
Guess astropy.coordinates.PhysicsSphericalRepresentation ->
Spherical3D.>>> import astropy.coordinates as apyc >>> import astropy.units as apyu >>> import coordinax.charts as cxc
>>> vec = apyc.PhysicsSphericalRepresentation( ... r=1 * apyu.kpc, theta=45 * apyu.deg, phi=90 * apyu.deg) >>> cxc.guess_chart(vec) Spherical3D(M=Rn(3))
- coordinax.guess_chart(obj: SphericalRepresentation) LonLatSpherical3D
- Parameters:
_ (Any)
- Return type:
Guess astropy.coordinates.SphericalRepresentation ->
LonLatSpherical3D.>>> import astropy.coordinates as apyc >>> import astropy.units as apyu >>> import coordinax.charts as cxc
>>> vec = apyc.SphericalRepresentation( ... lon=90 * apyu.deg, lat=45 * apyu.deg, distance=1 * apyu.kpc) >>> cxc.guess_chart(vec) LonLatSpherical3D(M=Rn(3))
- Parameters:
_ (
Any)- Return type:
- coordinax.cdict(_, /)
Normalize an input object into a component dictionary.
Keys are component names and values are corresponding scalars/arrays or quantities.
- Parameters:
obj –
An object to extract a component dictionary from. Dispatch rules include:
collections.abc.Mapping: returned as-is
unxt.Quantity: treated as Cartesian coordinates with components in the last dimension, matched to the appropriate Cartesian chart
Point: extracted from
obj.data
_ (Any)
- Returns:
A mapping from component names to values.
- Return type:
Examples
>>> import jax.numpy as jnp >>> import coordinax as cx >>> import unxt as u
Extract from a Mapping:
>>> d = {"x": u.Q(1.0, "m"), "y": u.Q(2.0, "m"), "z": u.Q(3.0, "m")} >>> cx.cdict(d) {'x': Q(1., 'm'), 'y': Q(2., 'm'), 'z': Q(3., 'm')}
>>> d = {"x": 1.0, "y": 2.0, "z": 3.0} >>> cx.cdict(d) {'x': 1.0, 'y': 2.0, 'z': 3.0}
Extract from a unxt.Quantity treated as Cartesian:
>>> q = u.Q([1, 2, 3], "m") >>> cx.cdict(q) {'x': Q(1, 'm'), 'y': Q(2, 'm'), 'z': Q(3, 'm')}
Specify the chart for a unxt.Quantity. For homogeneous unit Quantities, this must be Cartesian:
>>> cx.cdict(q, cx.cart3d) {'x': Q(1, 'm'), 'y': Q(2, 'm'), 'z': Q(3, 'm')}
Extract from an Array-like object with a registered chart:
>>> arr = jnp.array([1.0, 2.0, 3.0]) >>> cx.cdict(arr, cx.cart3d) {'x': Array(1., dtype=float64), 'y': Array(2., dtype=float64), 'z': Array(3., dtype=float64)}
Return a dictionary as-is.
>>> import coordinax as cx >>> d = {"x": 1.0, "y": 2.0} >>> cx.cdict(d) {'x': 1.0, 'y': 2.0}
- coordinax.cdict(obj: dict, chart: AbstractChart, /) dict
Return a dictionary as-is.
>>> import coordinax.charts as cxc >>> d = {"x": 1.0, "y": 2.0} >>> cxc.cdict(d, cxc.cart2d) {'x': 1.0, 'y': 2.0}
- coordinax.cdict(obj: AbstractQuantity, /) dict
Extract component dictionary from a Quantity.
Treats the Quantity as a Cartesian vector with components in the last dimension. The appropriate Cartesian chart is determined from the last dimension of the quantity.
>>> import coordinax as cx >>> import unxt as u >>> q = u.Q([1.0, 2.0, 3.0], "m") >>> cx.cdict(q) {'x': Q(1., 'm'), 'y': Q(2., 'm'), 'z': Q(3., 'm')}
- coordinax.cdict(obj: AbstractQuantity, keys: tuple, /) dict
Extract component dictionary from a Quantity using specified keys.
Treats the Quantity as a vector with components in the last dimension, splitting along that axis according to the provided keys.
This function requires that:
The last dimension of the quantity matches the number of chart components
The chart has homogeneous coordinate dimensions (all components have the same physical dimension, like Cartesian charts)
- Raises:
ValueError – If the last dimension of the quantity doesn’t match the chart’s component count, or if dimensions don’t match.
- Parameters:
_ (Any)
- Return type:
Examples
>>> import coordinax.charts as cxc >>> import unxt as u
>>> q = u.Q([1.0, 2.0, 3.0], "m") >>> cxc.cdict(q, ('x', 'y', 'z')) {'x': Q(1., 'm'), 'y': Q(2., 'm'), 'z': Q(3., 'm')}
Extract component dictionary from a 1D
QuantityMatrix.This overload supports heterogeneous per-component units by constructing one quantity per chart component from the corresponding numeric slice and unit in the
QuantityMatrix.- Raises:
ValueError – If
objis not 1D, or if the last dimension does not match the number of provided keys.- Parameters:
_ (Any)
- Return type:
Examples
>>> import jax.numpy as jnp >>> import unxt as u >>> import unxts.linalg as ul
>>> q = ul.QuantityMatrix(jnp.array([1.0, 2.0, 3.0]), ... unit=("m", "km/s", "rad")) >>> cxc.cdict(q, ('x', 'y', 'z')) {'x': Q(1., 'm'), 'y': Q(2., 'km / s'), 'z': Q(3., 'rad')}
- coordinax.cdict(obj: AbstractQuantity, chart: AbstractChart, /) dict
Extract component dictionary from a Quantity.
Treats the Quantity as a vector with components in the last dimension, splitting along that axis according to the chart’s component names.
This function requires that:
The last dimension of the quantity matches the number of chart components
The chart has homogeneous coordinate dimensions (all components have the same physical dimension, like Cartesian charts)
>>> import coordinax.charts as cxc >>> import unxt as u
>>> q = u.Q([1.0, 2.0, 3.0], "m") >>> cxc.cdict(q, cxc.cart3d) {'x': Q(1., 'm'), 'y': Q(2., 'm'), 'z': Q(3., 'm')}
A chart whose components have different dimensions cannot be filled from a single unit – every component would take that one unit:
>>> try: ... cxc.cdict(q, cxc.sph3d) ... except ValueError as e: ... print(e) Cannot split a Quantity in 'm' across Spherical3D, whose components have different dimensions ('length', 'angle', 'angle'): every component would take that unit. Pass per-component values instead -- a QuantityMatrix, a component dict, or a Point.
- coordinax.cdict(obj: unxts.linalg._src._quantity_matrix.QuantityMatrix, chart: AbstractChart, /) dict
Extract component dictionary from a 1D
QuantityMatrix.This overload supports heterogeneous per-component units by constructing one quantity per chart component from the corresponding numeric slice and unit in the
QuantityMatrix.>>> import jax.numpy as jnp >>> import coordinax.charts as cxc >>> import unxt as u >>> import unxts.linalg as ul
>>> q = ul.QuantityMatrix( ... jnp.array([1.0, 2.0, 3.0]), ... unit=(u.unit("m"), u.unit("km/s"), u.unit("rad")), ... ) >>> cxc.cdict(q, cxc.cart3d) {'x': Q(1., 'm'), 'y': Q(2., 'km / s'), 'z': Q(3., 'rad')}
Extract component dictionary from an array.
- Raises:
ValueError – If the last dimension of the quantity doesn’t match a known Cartesian chart (0D, 1D, 2D, or 3D).
- Parameters:
_ (Any)
- Return type:
Examples
>>> import coordinax as cx >>> import jax.numpy as jnp >>> arr = jnp.array([1.0, 2.0, 3.0]) >>> cx.cdict(arr, ('x', 'y', 'z')) {'x': Array(1., dtype=float64), 'y': Array(2., dtype=float64), 'z': Array(3., dtype=float64)}
- coordinax.cdict(obj: ArrayLike, chart: AbstractChart, /) dict
Extract component dictionary from an array.
>>> import coordinax as cx >>> import jax.numpy as jnp >>> arr = jnp.array([1.0, 2.0, 3.0]) >>> cx.cdict(arr, cx.cart3d) {'x': Array(1., dtype=float64), 'y': Array(2., dtype=float64), 'z': Array(3., dtype=float64)}
- coordinax.cdict(obj: ArrayLike, unit: UnitBase | FunctionUnitBase | str | unxts.linalg._src._units_matrix.UnitsMatrix | None, keys: tuple, /) dict
Extract component dictionary from an array.
>>> import coordinax as cx >>> import jax.numpy as jnp >>> arr = jnp.array([1.0, 2.0, 3.0]) >>> cx.cdict(arr, "m", ('x', 'y', 'z')) {'x': Q(1., 'm'), 'y': Q(2., 'm'), 'z': Q(3., 'm')}
A unxts.linalg.UnitsMatrix gives each component its own unit:
>>> import unxts.linalg as ul >>> cx.cdict(arr, ul.UnitsMatrix(("kpc", "rad", "rad")), ('r', 'theta', 'phi')) {'r': Q(1., 'kpc'), 'theta': Q(2., 'rad'), 'phi': Q(3., 'rad')}
- coordinax.cdict(obj: ArrayLike, unit: UnitBase | FunctionUnitBase | str | unxts.linalg._src._units_matrix.UnitsMatrix | None, chart: AbstractChart, /) dict
Extract component dictionary from an array.
>>> import coordinax as cx >>> import jax.numpy as jnp >>> arr = jnp.array([1.0, 2.0, 3.0]) >>> cx.cdict(arr, "m", cx.cart3d) {'x': Q(1., 'm'), 'y': Q(2., 'm'), 'z': Q(3., 'm')}
A unxts.linalg.UnitsMatrix gives each component its own unit:
>>> import coordinax.charts as cxc >>> import unxts.linalg as ul >>> cx.cdict(arr, ul.UnitsMatrix(("kpc", "rad", "rad")), cxc.sph3d) {'r': Q(1., 'kpc'), 'theta': Q(2., 'rad'), 'phi': Q(3., 'rad')}
- coordinax.cdict(obj: ArrayLike, unit: UnitBase | FunctionUnitBase | str | unxts.linalg._src._units_matrix.UnitsMatrix, /) dict
Extract component dictionary from an array.
Treats the array as a Cartesian vector with components in the last dimension. The appropriate Cartesian chart is determined from the last dimension of the quantity.
>>> import coordinax.charts as cxc >>> import jax.numpy as jnp >>> arr = jnp.array([1.0, 2.0, 3.0]) >>> cxc.cdict(arr, "m") {'x': Q(1., 'm'), 'y': Q(2., 'm'), 'z': Q(3., 'm')}
A unxts.linalg.UnitsMatrix gives each component its own unit:
>>> import unxts.linalg as ul >>> cxc.cdict(arr, ul.UnitsMatrix(("m", "km/s", "rad"))) {'x': Q(1., 'm'), 'y': Q(2., 'km / s'), 'z': Q(3., 'rad')}
- coordinax.cdict(obj: ArrayLike, usys: AbstractUnitSystem, chart: AbstractChart, /) dict
Extract component dictionary from an array.
- Raises:
ValueError – If the last dimension of the quantity doesn’t match a known Cartesian chart (0D, 1D, 2D, or 3D).
- Parameters:
_ (Any)
- Return type:
Examples
>>> import coordinax as cx >>> import unxt as u >>> import jax.numpy as jnp >>> arr = jnp.array([1.0, 2.0, 3.0]) >>> cx.cdict(arr, u.unitsystems.si, cx.cart3d) {'x': Q(1., 'm'), 'y': Q(2., 'm'), 'z': Q(3., 'm')}
Angular components arrive as unxt.Angle, per the chart’s declaration:
>>> import coordinax.charts as cxc >>> cx.cdict(arr, u.unitsystems.si, cxc.sph3d) {'r': Q(1., 'm'), 'theta': Angle(2., 'rad'), 'phi': Angle(3., 'rad')}
Extract the component dictionary from a Point or Tangent.
>>> import coordinax as cx >>> import coordinax.representations as cxr >>> import coordinax.charts as cxc >>> import unxt as u
>>> list(cx.cdict(cx.Point.from_(u.Q([1, 2, 3], "m"))).keys()) ['x', 'y', 'z']
>>> d = {"x": u.Q(1.0, "m/s"), "y": u.Q(2.0, "m/s"), "z": u.Q(3.0, "m/s")} >>> list(cx.cdict(cx.Tangent.from_(d, cxc.cart3d, cxr.coord_vel)).keys()) ['x', 'y', 'z']
- coordinax.cdict(r: CartesianRepresentation) dict
Convert an astropy CartesianRepresentation to a CDict.
>>> import astropy.coordinates as apyc >>> import astropy.units as apyu >>> import coordinax.charts as cxc
>>> vec = apyc.CartesianRepresentation(1 * apyu.km, 2 * apyu.km, 3 * apyu.km) >>> cxc.cdict(vec) {'x': Q(1., 'km'), 'y': Q(2., 'km'), 'z': Q(3., 'km')}
- coordinax.cdict(r: CylindricalRepresentation) dict
Convert an astropy CylindricalRepresentation to a CDict.
>>> import astropy.coordinates as apyc >>> import astropy.units as apyu >>> import coordinax.charts as cxc
>>> vec = apyc.CylindricalRepresentation(1 * apyu.m, 45 * apyu.deg, 3 * apyu.m) >>> cxc.cdict(vec) {'rho': Q(1., 'm'), 'phi': Q(45., 'deg'), 'z': Q(3., 'm')}
- coordinax.cdict(r: PhysicsSphericalRepresentation) dict
Convert an astropy PhysicsSphericalRepresentation to a CDict.
>>> import astropy.coordinates as apyc >>> import astropy.units as apyu >>> import coordinax.charts as cxc
>>> vec = apyc.PhysicsSphericalRepresentation( ... phi=45 * apyu.deg, theta=30 * apyu.deg, r=1 * apyu.m) >>> cxc.cdict(vec) {'r': Q(1., 'm'), 'theta': Q(30., 'deg'), 'phi': Q(45., 'deg')}
- coordinax.cdict(r: SphericalRepresentation) dict
Convert an astropy SphericalRepresentation to a CDict.
>>> import astropy.coordinates as apyc >>> import astropy.units as apyu >>> import coordinax.charts as cxc
>>> vec = apyc.SphericalRepresentation( ... lon=90 * apyu.deg, lat=45 * apyu.deg, distance=1 * apyu.kpc) >>> cxc.cdict(vec) {'lon': Q(90., 'deg'), 'lat': Q(45., 'deg'), 'distance': Q(1., 'kpc')}
- coordinax.cdict(r: UnitSphericalRepresentation) dict
Convert an astropy UnitSphericalRepresentation to a CDict.
A sky position without a distance – what a catalogue gives you, and what astropy.coordinates.SkyCoord builds when no distance is supplied.
>>> import astropy.coordinates as apyc >>> import astropy.units as apyu >>> import coordinax.charts as cxc
>>> vec = apyc.UnitSphericalRepresentation(lon=90 * apyu.deg, lat=45 * apyu.deg) >>> cxc.cdict(vec) {'lon': Q(90., 'deg'), 'lat': Q(45., 'deg')}
- coordinax.cdict(r: RadialRepresentation) dict
Convert an astropy RadialRepresentation to a CDict.
Astropy calls the component
distance; coordinax.charts.radial1d calls itr.>>> import astropy.coordinates as apyc >>> import astropy.units as apyu >>> import coordinax.charts as cxc
>>> cxc.cdict(apyc.RadialRepresentation(distance=1 * apyu.kpc)) {'r': Q(1., 'kpc')}
- coordinax.carray(p, /, *args)
Pack a component dictionary into a
QuantityMatrix(complement of cdict).- Parameters:
p (
Any) – A component dictionary (keys are component names, values are scalars/arrays or quantities).*args (
Any) –Either
keys(a tuple of component names) or a chart, optionally followed by a unit or unit system controlling the packed units:carray(p, keys)/carray(p, chart)— one unit per component (dimensionless where a component carries no unit)carray(p, keys, usys)/carray(p, chart, usys)— units resolved from a unit system by each component’s dimensioncarray(p, keys, unit)/carray(p, chart, unit)— a single shared unit for every component
- Returns:
The packed 1-D quantity matrix, stacked along the trailing axis, with one unit per component.
- Return type:
QuantityMatrix
Examples
>>> import unxt as u >>> import coordinax as cx
>>> p = {"x": u.Q(1.0, "km"), "y": u.Q(2.0, "km"), "z": u.Q(3.0, "km")} >>> cx.carray(p, ("x", "y", "z")) QM([1., 2., 3.], '(km, km, km)')
Unitless components pack as dimensionless:
>>> cx.carray({"x": 1.0, "y": 2.0}, cx.cart2d) QM([1., 2.], '(, )')
Pack a component dict into a 1-D
QMwith per-component native units.Unitless components are treated as dimensionless, so quantity- and array-valued components can be packed together.
>>> import unxt as u >>> import coordinax as cx >>> p = {"x": u.Q(1.0, "km"), "y": u.Q(2.0, "km"), "z": u.Q(3.0, "km")} >>> cx.carray(p, ("x", "y", "z")) QM([1., 2., 3.], '(km, km, km)')
- coordinax.carray(p: dict, chart: AbstractChart, /) unxts.linalg._src._quantity_matrix.QuantityMatrix
Pack a component dict using
chart.componentsas the keys.>>> import unxt as u >>> import coordinax as cx >>> p = {"x": u.Q(1.0, "km"), "y": u.Q(2.0, "km"), "z": u.Q(3.0, "km")} >>> cx.carray(p, cx.cart3d) QM([1., 2., 3.], '(km, km, km)')
- coordinax.carray(p: dict, keys: tuple, usys: AbstractUnitSystem, /) unxts.linalg._src._quantity_matrix.QuantityMatrix
Pack a component dict, resolving each component’s unit from
usys.>>> import unxt as u >>> import coordinax as cx >>> p = {"x": u.Q(1.0, "km"), "y": u.Q(2.0, "km")} >>> cx.carray(p, ("x", "y"), u.unitsystems.si) QM([1000., 2000.], '(m, m)')
- coordinax.carray(p: dict, chart: AbstractChart, usys: AbstractUnitSystem, /) unxts.linalg._src._quantity_matrix.QuantityMatrix
Pack a component dict, using
chart.componentsand units fromusys.>>> import unxt as u >>> import coordinax as cx >>> p = {"x": u.Q(1.0, "km"), "y": u.Q(2.0, "km"), "z": u.Q(3.0, "km")} >>> cx.carray(p, cx.cart3d, u.unitsystems.si) QM([1000., 2000., 3000.], '(m, m, m)')
- coordinax.carray(p: dict, keys: tuple, unit: UnitBase | FunctionUnitBase, /) unxts.linalg._src._quantity_matrix.QuantityMatrix
Pack a component dict into a single shared
unit(all components converted).>>> import unxt as u >>> import coordinax as cx >>> p = {"x": u.Q(1.0, "km"), "y": u.Q(200.0, "m")} >>> cx.carray(p, ("x", "y"), u.unit("km")) QM([1. , 0.2], '(km, km)')
- coordinax.carray(p: dict, chart: AbstractChart, unit: UnitBase | FunctionUnitBase, /) unxts.linalg._src._quantity_matrix.QuantityMatrix
Pack a component dict, using
chart.componentsand a sharedunit.>>> import unxt as u >>> import coordinax as cx >>> p = {"x": u.Q(1.0, "km"), "y": u.Q(200.0, "m"), "z": u.Q(0.0, "m")} >>> cx.carray(p, cx.cart3d, u.unit("km")) QM([1. , 0.2, 0. ], '(km, km, km)')
- coordinax.pt_map(*args, **kwargs)
Transform position coordinates from one chart to another.
This function implements the most general point-coordinate map between two compatible chart representations of the same geometric point. It is a point-wise map that preserves the physical location while changing the coordinate description.
For charts in the same atlas on the same manifold, this reduces to the ordinary chart transition map handled by coordinax.charts.pt_map. It is the intrinsic coordinate-change operation: the underlying point on the manifold is unchanged, and only its coordinate representation is changed.
However, this function is not restricted to two charts on the same manifold. It may also represent a realization-style map between charts attached to different manifolds when one is a realization of the other, such as an intrinsic chart on an embedded manifold and a chart on its ambient manifold. In that case, this function may change both the chart and the manifold in which the point is being represented.
Mathematical Definition:
Let \((U, \varphi_{\mathrm{from}})\) and \((V, \varphi_{\mathrm{to}})\) be charts on the same manifold \(M\), with overlapping domains. The transition map is
\[\varphi_{\mathrm{to}} \circ \varphi_{\mathrm{from}}^{-1} : \varphi_{\mathrm{from}}(U \cap V) \to \varphi_{\mathrm{to}}(U \cap V).\]If a point \(p \in U \cap V\) has coordinates \(q = \varphi_{\mathrm{from}}(p)\), then this function returns \(p' = \varphi_{\mathrm{to}}(p)\) for the same manifold point.
More generally, if \(\varphi_{\mathrm{from}} : U \subset M \to \mathbb{R}^n\) and \(\psi_{\mathrm{to}} : W \subset N \to \mathbb{R}^m\) are chart maps on manifolds \(M\) and \(N\), and there is a point map \(F : M \supset U \to W \subset N\), then coordinax.charts.pt_map represents the coordinate expression
\[\psi_{\mathrm{to}} \circ F \circ \varphi_{\mathrm{from}}^{-1}.\]3D Spherical → Cartesian:
\[\begin{split}x &= r \sin\theta \cos\phi \\ y &= r \sin\theta \sin\phi \\ z &= r \cos\theta\end{split}\]- Raises:
NotImplementedError – If no transformation rule is registered for the specific pair of charts
(to_chart, from_chart).- Parameters:
- Return type:
Notes
This is a position-only transformation.
This function may map between charts on the same manifold or across manifolds, provided a compatible point map is defined between them.
Transformations preserve physical dimensions. For example, converting from polar to Cartesian preserves that
rhas length dimension and producesxandywith length dimension.Some transformations may introduce singularities (e.g., polar coordinates at the origin, spherical coordinates at poles).
Transformations are composable: transforming \(A \to B \to C\) yields the same result as a direct \(A \to C\) transformation (up to numerical precision).
Identity transformations (same
from_chartandto_chart) return the input unchanged.
Examples
>>> import quaxed.numpy as jnp >>> import coordinax.charts as cxc >>> import unxt as u
Transform from 2D polar to Cartesian:
>>> p_polar = {"r": u.Q(2.0, "m"), "theta": u.Angle(jnp.pi / 4, "rad")} >>> cxc.pt_map(p_polar, cxc.polar2d, cxc.cart2d) {'x': Q(1.41421356, 'm'), 'y': Q(1.41421356, 'm')}
Transform from 3D spherical to Cartesian:
>>> p_sph = {"theta": u.Angle(jnp.pi / 2, "rad"), "phi": u.Angle(0.0, "rad"), ... "r": u.Q(5.0, "km")} >>> cxc.pt_map(p_sph, cxc.sph3d, cxc.cart3d) {'x': Q(5., 'km'), 'y': Q(0., 'km'), 'z': Q(3.061617e-16, 'km')}
Transform from Cartesian to cylindrical:
>>> p_xyz = {"x": u.Q(3.0, "m"), "y": u.Q(4.0, "m"), "z": u.Q(5.0, "m")} >>> cxc.pt_map(p_xyz, cxc.cart3d, cxc.cyl3d) {'rho': Q(5., 'm'), 'phi': Angle(0.92729522, 'rad'), 'z': Q(5., 'm')}
- coordinax.pt_map(p: dict, from_M: CartesianProductManifold, from_chart: AbstractCartesianProductChart, to_M: CartesianProductManifold, to_chart: AbstractCartesianProductChart, /, *, usys: AbstractUnitSystem | None = None) dict
ABC CartesianProductChart -> CartesianProductChart (factorwise).
Transforms between product charts by applying coordinax.charts.pt_map to each factor independently. Requires compatible factor structure (same number of factors, pairwise compatible).
Mathematical definition:
\[\varphi \left(\prod_i S_i,\;\prod_i R_i,\;p\right) = \bigl(\varphi(S_i,\,R_i,\,p_i)\bigr)_i\]where \(\varphi\) denotes {func}`~coordinax.charts.pt_map` and \(p_i\) are the factor dictionaries split from \(p\).
Examples
>>> import coordinax.charts as cxc >>> import unxt as u
Transform a Cartesian product chart between spatial representations:
>>> prod_crt = cxc.CartesianProductChart((cxc.time1d, cxc.cart3d), ("t", "q")) >>> prod_sph = cxc.CartesianProductChart((cxc.time1d, cxc.sph3d), ("t", "q")) >>> p = {"t.t": u.Q(1.0, "s"), "q.x": u.Q(1.0, "m"), "q.y": u.Q(0.0, "m"), ... "q.z": u.Q(0.0, "m")} >>> result = cxc.pt_map(p, prod_crt, prod_sph) >>> result["t.t"] Q(1., 's') >>> result["q.r"] Q(1., 'm')
- coordinax.pt_map(p: dict, from_M: AbstractManifold, from_chart: AbstractChart, to_M: CartesianProductManifold, to_chart: AbstractCartesianProductChart, /, *, usys: AbstractUnitSystem | None = None) dict
AbstractChart -> Cartesian -> AbstractCartesianProductChart.
>>> import coordinax.charts as cxc >>> import unxt as u
>>> chart = cxc.CartesianProductChart((cxc.sph2, cxc.cart1d), ("S2", "R1")) >>> map = cxc.pt_map.invoke(dict[str, u.Q], cxc.Cart3D, cxc.CartesianProductChart) >>> try: map({}, cxc.cart3d, chart) ... except NotImplementedError as e: print(e) No general transform between Cart3D and CartesianProductChart. Define explicit rules for non-product to product conversions.
- coordinax.pt_map(p: dict, from_M: CartesianProductManifold, from_chart: AbstractCartesianProductChart, to_M: AbstractManifold, to_chart: AbstractChart, /, *, usys: AbstractUnitSystem | None = None) dict
AbstractCartesianProductChart -> Cartesian -> AbstractChart.
>>> import coordinax.charts as cxc >>> import unxt as u
>>> chart = cxc.CartesianProductChart((cxc.sph2, cxc.cart1d), ("S2", "R1")) >>> map = cxc.pt_map.invoke(dict[str, u.Q], cxc.CartesianProductChart, cxc.Cart3D) >>> try: map({}, chart, cxc.cart3d) ... except NotImplementedError as e: print(e) No general transform between CartesianProductChart and Cart3D. Define explicit rules for product to non-product conversions.
Return a partial function for point transformation.
>>> import coordinax.charts as cxc >>> import unxt as u
Coordinates without units are the default.
>>> q = {"x": u.Q(1.0, "m"), "y": u.Q(0.0, "m"), "z": u.Q(0.0, "m")} >>> map = cxc.pt_map(None, cxc.cart3d, cxc.sph3d) >>> map(q) {'r': Q(1., 'm'), 'theta': Angle(1.57079633, 'rad'), 'phi': Angle(0., 'rad')}
Coordinates without units are also accepted, interpreted having units of the unxt.AbstractUnitSystem, which must be passed.
>>> q = {"x": 1.0, "y": 0.0, "z": 0.0} >>> map = cxc.pt_map(None, cxc.cart3d, cxc.sph3d, usys=u.unitsystems.si) >>> map(q) {'r': Array(1., dtype=float64, ...), 'theta': Array(1.57079633, dtype=float64, ...), 'phi': Array(0., dtype=float64, ...)}
unxt.Quantity inputs are also accepted, and are interpreted as being in Cartesian coordinates.
>>> p = u.Q([1.0, 0.0, 0.0], "m") >>> map = cxc.pt_map(None, cxc.cart3d, cxc.sph3d) >>> map(p) QM([1. , 1.57079633, 0. ], '(m, rad, rad)')
Array-Like inputs are interpreted as Cartesian coordinates with units from the required unxt.AbstractUnitSystem.
>>> q = [1.0, 0.0, 0.0] >>> map = cxc.pt_map(None, cxc.cart3d, cxc.sph3d, usys=u.unitsystems.si) >>> map(q) Array([1. , 1.57079633, 0. ], dtype=float64)
- coordinax.pt_map(from_chart: AbstractChart, to_chart: AbstractChart, /, **fixed_kw: Any) Callable[..., Any]
Return a partial function for point transformation.
>>> import coordinax.charts as cxc >>> import unxt as u
Coordinates without units are the default.
>>> p = {"x": u.Q(1.0, "m"), "y": u.Q(0.0, "m"), "z": u.Q(0.0, "m")} >>> map = cxc.pt_map(cxc.cart3d, cxc.sph3d) >>> map(p) {'r': Q(1., 'm'), 'theta': Angle(1.57079633, 'rad'), 'phi': Angle(0., 'rad')}
Coordinates without units are also accepted, interpreted having units of the unxt.AbstractUnitSystem, which must be passed.
>>> p = {"x": 1.0, "y": 0.0, "z": 0.0} >>> map = cxc.pt_map(cxc.cart3d, cxc.sph3d, usys=u.unitsystems.si) >>> map(p) {'r': Array(1., dtype=float64, ...), 'theta': Array(1.57079633, dtype=float64, ...), 'phi': Array(0., dtype=float64, ...)}
unxt.Quantity inputs are also accepted, and are interpreted as being in Cartesian coordinates.
>>> p = u.Q([1.0, 0.0, 0.0], "m") >>> map = cxc.pt_map(cxc.cart3d, cxc.sph3d) >>> map(p) QM([1. , 1.57079633, 0. ], '(m, rad, rad)')
Array-Like inputs are interpreted as Cartesian coordinates with units from the required unxt.AbstractUnitSystem.
>>> p = [1.0, 0.0, 0.0] >>> map = cxc.pt_map(cxc.cart3d, cxc.sph3d, usys=u.unitsystems.si) >>> map(p) Array([1. , 1.57079633, 0. ], dtype=float64)
- coordinax.pt_map(x: Any, from_chart: AbstractChart, to_chart: AbstractChart, /, *, usys: AbstractUnitSystem | None = None) Any
Point transformation from chart to chart, using their manifolds.
>>> import coordinax.manifolds as cxm >>> import coordinax.charts as cxc >>> import unxt as u
>>> p = {} >>> cxc.pt_map(p, cxc.cart0d, cxc.cart0d) {}
>>> p = {"r": u.Q(5.0, "m")} >>> cxc.pt_map(p, cxc.radial1d, cxc.cart1d) {'x': Q(5., 'm')}
>>> p = {"r": u.Q(5.0, "m"), "theta": u.Q(90, "deg")} >>> cxc.pt_map(p, cxc.polar2d, cxc.cart2d) {'x': Q(3.061617e-16, 'm'), 'y': Q(5., 'm')}
>>> p = {"r": u.Q(5.0, "m"), "theta": u.Q(90, "deg"), "phi": u.Q(0, "deg")} >>> cxc.pt_map(p, cxc.sph3d, cxc.cart3d) {'x': Q(5., 'm'), 'y': Q(0., 'm'), 'z': Q(3.061617e-16, 'm')}
- coordinax.pt_map(p: dict, from_M: AbstractManifold, from_chart: AbstractChart, to_M: AbstractManifold, to_chart: AbstractChart, /, *, usys: AbstractUnitSystem | None = None) dict
AbstractChart -> Cartesian -> AbstractChart.
>>> import coordinax.manifolds as cxm >>> import coordinax.charts as cxc >>> import unxt as u
>>> p = {"r": u.Q(5.0, "m"), "theta": u.Q(90, "deg")} >>> map = cxc.pt_map.invoke(dict[str, u.Q], cxm.Rn, cxc.AbstractChart, ... cxm.Rn, cxc.AbstractChart) >>> map(p, cxm.R2, cxc.polar2d, cxm.R2, cxc.cart2d) {'x': Q(3.061617e-16, 'm'), 'y': Q(5., 'm')}
- coordinax.pt_map(p: dict, from_M: EuclideanManifold, from_chart: AbstractChart, to_M: EuclideanManifold, to_chart: AbstractChart, /, *, usys: AbstractUnitSystem | None = None) dict
Identity conversion for matching charts.
>>> import coordinax.manifolds as cxm >>> import coordinax.charts as cxc >>> import unxt as u >>> import quaxed.numpy as jnp
>>> q = {} >>> q2 = cxc.pt_map(q, cxm.R0, cxc.cart0d, cxm.R0, cxc.cart0d) >>> q is q2 True
>>> q = {"r": u.Q(3.0, "m")} >>> q2 = cxc.pt_map(q, cxm.R1, cxc.radial1d, cxm.R1, cxc.radial1d) >>> q is q2 True
>>> q = {"x": u.Q(1.0, "m"), "y": u.Q(2.0, "m")} >>> q2 = cxc.pt_map(q, cxm.R2, cxc.cart2d, cxm.R2, cxc.cart2d) >>> q is q2 True
- coordinax.pt_map(p: dict, from_M: EuclideanManifold, from_chart: Radial1D, to_M: EuclideanManifold, to_chart: Cart1D, /, *, usys: AbstractUnitSystem | None = None) dict
Radial1D -> Cart1D.
The r coordinate is converted to the x coordinate of the 1D system.
>>> import coordinax.manifolds as cxm >>> import coordinax.charts as cxc >>> import unxt as u
>>> q = {"r": u.Q(5.0, "m")} >>> cxc.pt_map(q, cxm.R1, cxc.radial1d, cxm.R1, cxc.cart1d) {'x': Q(5., 'm')}
>>> q = {"r": 5.0} # No units >>> cxc.pt_map(q, cxm.R1, cxc.radial1d, cxm.R1, cxc.cart1d) {'x': 5.0}
- coordinax.pt_map(p: dict, from_M: EuclideanManifold, from_chart: Cart1D, to_M: EuclideanManifold, to_chart: Radial1D, /, *, usys: AbstractUnitSystem | None = None) dict
Cart1D -> Radial1D.
The x coordinate is converted to the r coordinate of the 1D system.
Assumptions:
Cart1D and Radial1D are
>>> import coordinax.charts as cxc >>> import unxt as u
>>> p = {"x": u.Q(5.0, "m")} >>> cxc.pt_map(p, cxm.R1, cxc.cart1d, cxm.R1, cxc.radial1d) {'r': Q(5., 'm')}
>>> p = {"x": 5.0} # No units >>> cxc.pt_map(p, cxm.R1, cxc.cart1d, cxm.R1, cxc.radial1d) {'r': 5.0}
- coordinax.pt_map(p: dict, from_M: EuclideanManifold, from_chart: Polar2D, to_M: EuclideanManifold, to_chart: Cart2D, /, *, usys: AbstractUnitSystem | None = None) dict
Polar2D -> Cart2D.
The r and theta coordinates are converted to the x and y coordinates of the 2D Cartesian system.
>>> import coordinax.charts as cxc >>> import unxt as u
>>> p = {"r": u.Q(5.0, "m"), "theta": u.Q(90, "deg")} >>> cxc.pt_map(p, cxm.R2, cxc.polar2d, cxm.R2, cxc.cart2d) {'x': Q(3.061617e-16, 'm'), 'y': Q(5., 'm')}
>>> p = {"r": 5, "theta": 90} # No units >>> usys = u.unitsystem("km", "deg") >>> cxc.pt_map(p, cxm.R2, cxc.polar2d, cxm.R2, cxc.cart2d, usys=usys) {'x': Array(3.061617e-16, dtype=float64, ...), 'y': Array(5., dtype=float64, ...)}
- coordinax.pt_map(p: dict, from_M: EuclideanManifold, from_chart: Cart2D, to_M: EuclideanManifold, to_chart: Polar2D, /, *, usys: AbstractUnitSystem | None = None) dict
Cart2D -> Polar2D.
The x and y coordinates are converted to the r and theta coordinates of the 2D polar system.
>>> import coordinax.charts as cxc >>> import unxt as u
>>> p = {"x": u.Q(3, "m"), "y": u.Q(4, "m")} >>> cxc.pt_map(p, cxm.R2, cxc.cart2d, cxm.R2, cxc.polar2d) {'r': Q(5., 'm'), 'theta': Angle(0.92729522, 'rad')}
>>> p = {"x": 3, "y": 4} # No units >>> cxc.pt_map(p, cxm.R2, cxc.cart2d, cxm.R2, cxc.polar2d) {'r': Array(5., dtype=float64, ...), 'theta': Array(0.92729522, dtype=float64, ...)}
- coordinax.pt_map(p: dict, from_M: EuclideanManifold, from_chart: Cylindrical3D, to_M: EuclideanManifold, to_chart: Cart3D, /, *, usys: AbstractUnitSystem | None = None) dict
Cylindrical3D -> Cart3D.
>>> import coordinax.charts as cxc >>> import unxt as u
>>> p = {"rho": u.Q(1.0, "m"), "phi": u.Q(90, "deg"), "z": u.Q(2.0, "m")} >>> cxc.pt_map(p, cxm.R3, cxc.cyl3d, cxm.R3, cxc.cart3d) {'x': Q(6.123234e-17, 'm'), 'y': Q(1., 'm'), 'z': Q(2., 'm')}
>>> p = {"rho": 1.0, "phi": 90, "z": 2.0} # No units >>> usys = u.unitsystem("m", "deg") >>> cxc.pt_map(p, cxm.R3, cxc.cyl3d, cxm.R3, cxc.cart3d, usys=usys) {'x': Array(6.123234e-17, dtype=float64, ...), 'y': Array(1., dtype=float64, ...), 'z': 2.0}
- coordinax.pt_map(p: dict, from_M: EuclideanManifold, from_chart: Spherical3D, to_M: EuclideanManifold, to_chart: Cart3D, /, *, usys: AbstractUnitSystem | None = None) dict
Spherical3D -> Cart3D.
>>> import coordinax.charts as cxc >>> import unxt as u >>> import quaxed.numpy as jnp
A point on the +z axis (theta=0):
>>> p = {"r": u.Q(1.0, "m"), "theta": u.Q(0, "deg"), "phi": u.Q(0, "deg")} >>> cxc.pt_map(p, cxm.R3, cxc.sph3d, cxm.R3, cxc.cart3d) {'x': Q(0., 'm'), 'y': Q(0., 'm'), 'z': Q(1., 'm')}
A point on the equator (theta=90 deg, phi=0):
>>> p = {"r": 2.0, "theta": 90, "phi": 0} >>> usys = u.unitsystem("m", "deg") >>> cxc.pt_map(p, cxm.R3, cxc.sph3d, cxm.R3, cxc.cart3d, usys=usys) {'x': Array(2., dtype=float64, ...), 'y': Array(0., dtype=float64, ...), 'z': Array(1.2246468e-16, dtype=float64, ...)}
- coordinax.pt_map(p: dict, from_M: EuclideanManifold, from_chart: LonLatSpherical3D, to_M: EuclideanManifold, to_chart: Cart3D, /, *, usys: AbstractUnitSystem | None = None) dict
LonLatSpherical3D -> Cart3D.
>>> import coordinax.charts as cxc >>> import unxt as u
A point at the north pole (lat=90 deg):
>>> p = {"lon": u.Q(0, "deg"), "lat": u.Q(90, "deg"), "distance": u.Q(1.0, "m")} >>> cxc.pt_map(p, cxm.R3, cxc.lonlat_sph3d, cxm.R3, cxc.cart3d) {'x': Q(6.123234e-17, 'm'), 'y': Q(0., 'm'), 'z': Q(1., 'm')}
A point on the equator at lon=0:
>>> p = {"lon": 0, "lat": 0, "distance": 2} >>> cxc.pt_map(p, cxm.R3, cxc.lonlat_sph3d, cxm.R3, cxc.cart3d) {'x': Array(2., dtype=float64, ...), 'y': Array(0., dtype=float64, ...), 'z': Array(0., dtype=float64, ...)}
- coordinax.pt_map(p: dict, from_M: EuclideanManifold, from_chart: LonCosLatSpherical3D, to_M: EuclideanManifold, to_chart: Cart3D, /, *, usys: AbstractUnitSystem | None = None) dict
LonCosLatSpherical3D -> Cart3D.
Components are (lon_coslat, lat, r), where lon_coslat := lon * cos(lat). Longitude is undefined at the poles (cos(lat) == 0); we set lon = 0 by convention there to avoid NaNs.
>>> import coordinax.charts as cxc >>> import unxt as u
A point on the equator (lat=0, so lon_coslat = lon):
>>> p = {"lon_coslat": u.Q(0, "deg"), "lat": u.Q(0, "deg"), ... "distance": u.Q(1.0, "m")} >>> cxc.pt_map(p, cxm.R3, cxc.loncoslat_sph3d, cxm.R3, cxc.cart3d) {'x': Q(1., 'm'), 'y': Q(0., 'm'), 'z': Q(0., 'm')}
At the north pole (lat=90), lon_coslat is effectively 0 regardless of lon:
>>> p = {"lon_coslat": u.Q(0, "deg"), "lat": u.Q(90, "deg"), ... "distance": u.Q(2.0, "m")} >>> cxc.pt_map(p, cxm.R3, cxc.loncoslat_sph3d, cxm.R3, cxc.cart3d) {'x': Q(1.2246468e-16, 'm'), 'y': Q(0., 'm'), 'z': Q(2., 'm')}
- coordinax.pt_map(p: dict, from_M: EuclideanManifold, from_chart: MathSpherical3D, to_M: EuclideanManifold, to_chart: Cart3D, /, *, usys: AbstractUnitSystem | None = None) dict
MathSpherical3D -> Cart3D.
theta: azimuth in the x-y plane (longitude-like)
phi : polar angle from +z, with phi in [0, pi]
>>> import coordinax.charts as cxc >>> import unxt as u
A point on the +z axis (phi=0):
>>> p = {"r": u.Q(1.0, "m"), "theta": u.Q(0, "deg"), "phi": u.Q(0, "deg")} >>> cxc.pt_map(p, cxm.R3, cxc.math_sph3d, cxm.R3, cxc.cart3d) {'x': Q(0., 'm'), 'y': Q(0., 'm'), 'z': Q(1., 'm')}
A point on the +x axis (theta=0, phi=90):
>>> p = {"r": u.Q(2.0, "m"), "theta": u.Q(0, "deg"), "phi": u.Q(90, "deg")} >>> cxc.pt_map(p, cxm.R3, cxc.math_sph3d, cxm.R3, cxc.cart3d) {'x': Q(2., 'm'), 'y': Q(0., 'm'), 'z': Q(1.2246468e-16, 'm')}
- coordinax.pt_map(p: dict, from_M: EuclideanManifold, from_chart: ProlateSpheroidal3D, to_M: EuclideanManifold, to_chart: Cart3D, /, *, usys: AbstractUnitSystem | None = None) dict
ProlateSpheroidal3D -> Cart3D.
We calculate through cylindrical coordinates first:
\(\rho = \sqrt{(\mu-\Delta^2)\left(1-\frac{\lvert\nu\rvert}{\Delta^2}\right)}\) \(z = \sqrt{\mu\,\frac{\lvert\nu\rvert}{\Delta^2}}\;\mathrm{sign}(\nu)\) \(\phi = \phi.\)
Then convert to Cartesian:
\(x=\rho\cos\phi\), \(y=\rho\sin\phi\), \(z=z\).
>>> import coordinax.charts as cxc >>> import unxt as u >>> import quaxed.numpy as jnp
>>> prolatesph3d = cxc.ProlateSpheroidal3D(Delta=u.StaticQuantity(2.0, "m"))
>>> p = {"mu": u.Q(5.0, "m2"), "nu": u.Q(1.0, "m2"), "phi": u.Q(0, "rad")} >>> cxc.pt_map(p, cxm.R3, prolatesph3d, cxm.R3, cxc.cart3d) {'x': Q(0.8660254, 'm'), 'y': Q(0., 'm'), 'z': Q(1.11803399, 'm')}
>>> p = {"mu": 5.0, "nu": 1.0, "phi": 0} # No units >>> usys = u.unitsystem("m", "rad") >>> cxc.pt_map(p, cxm.R3, prolatesph3d, cxm.R3, cxc.cart3d, usys=usys) {'x': Array(0.8660254, dtype=float64), 'y': Array(0., dtype=float64), 'z': Array(1.11803399, dtype=float64)}
- coordinax.pt_map(p: dict, from_M: EuclideanManifold, from_chart: Cart3D, to_M: EuclideanManifold, to_chart: Cylindrical3D, /, *, usys: AbstractUnitSystem | None = None) dict
Cart3D -> Cylindrical3D.
>>> import coordinax as cx >>> import unxt as u
>>> p = {"x": u.Q(3.0, "m"), "y": u.Q(4.0, "m"), "z": u.Q(5.0, "m")} >>> cxc.pt_map(p, cxm.R3, cxc.cart3d, cxm.R3, cxc.cyl3d) {'rho': Q(5., 'm'), 'phi': Angle(0.92729522, 'rad'), 'z': Q(5., 'm')}
>>> p = {"x": 3.0, "y": 4.0, "z": 5.0} # No units >>> cxc.pt_map(p, cxm.R3, cxc.cart3d, cxm.R3, cxc.cyl3d) {'rho': Array(5., dtype=float64, ...), 'phi': Array(0.92729522, dtype=float64, ...), 'z': 5.0}
- coordinax.pt_map(p: dict, from_M: EuclideanManifold, from_chart: Cart3D | Cylindrical3D, to_M: EuclideanManifold, to_chart: AbstractSpherical3D, /, *, usys: AbstractUnitSystem | None = None) dict
Cart3D -> Spherical3D -> AbstractSpherical3D.
>>> import coordinax.charts as cxc >>> import unxt as u
>>> p = {"x": u.Q(0.0, "m"), "y": u.Q(0.0, "m"), "z": u.Q(1.0, "m")} >>> cxc.pt_map(p, cxm.R3, cxc.cart3d, cxm.R3, cxc.loncoslat_sph3d) {'lon_coslat': Angle(0., 'rad'), 'lat': Angle(90., 'deg'), 'distance': Q(1., 'm')}
>>> p = {"rho": 0, "phi": 180, "z": 1} >>> usys = u.unitsystem("m", "deg") >>> cxc.pt_map(p, cxm.R3, cxc.cyl3d, cxm.R3, cxc.loncoslat_sph3d, usys=usys) {'lon_coslat': Array(1.10218212e-14, dtype=float64, ...), 'lat': Array(90., dtype=float64, ...), 'distance': Array(1., dtype=float64, weak_type=True)}
- coordinax.pt_map(p: dict, from_M: EuclideanManifold, from_chart: Cart3D, to_M: EuclideanManifold, to_chart: Spherical3D, /, *, usys: AbstractUnitSystem | None = None) dict
Cart3D -> Spherical3D.
>>> import coordinax.charts as cxc >>> import unxt as u
A point on the +z axis:
>>> p = {"x": u.Q(0.0, "m"), "y": u.Q(0.0, "m"), "z": u.Q(1.0, "m")} >>> cxc.pt_map(p, cxm.R3, cxc.cart3d, cxm.R3, cxc.sph3d) {'r': Q(1., 'm'), 'theta': Angle(0., 'rad'), 'phi': Angle(0., 'rad')}
A point on the +x axis:
>>> p = {"x": 2.0, "y": 0.0, "z": 0.0} # No units >>> cxc.pt_map(p, cxm.R3, cxc.cart3d, cxm.R3, cxc.sph3d) {'r': Array(2., dtype=float64, ...), 'theta': Array(1.57079633, dtype=float64, ...), 'phi': Array(0., dtype=float64, ...)}
- coordinax.pt_map(p: dict, from_M: EuclideanManifold, from_chart: Cylindrical3D, to_M: EuclideanManifold, to_chart: Spherical3D, /, *, usys: AbstractUnitSystem | None = None) dict
Cylindrical3D -> Spherical3D.
>>> import coordinax.manifolds as cxm >>> import coordinax.charts as cxc >>> import unxt as u
A point on the z-axis (rho=0):
>>> p = {"rho": u.Q(0.0, "m"), "phi": u.Q(0, "rad"), "z": u.Q(1.0, "m")} >>> cxc.pt_map(p, cxm.R3, cxc.cyl3d, cxm.R3, cxc.sph3d) {'r': Q(1., 'm'), 'theta': Angle(0., 'rad'), 'phi': Angle(0, 'rad')}
A point in the xy-plane (z=0):
>>> p = {"rho": 3.0, "phi": 0, "z": 0.0} # No units >>> cxc.pt_map(p, cxm.R3, cxc.cyl3d, cxm.R3, cxc.sph3d) {'r': Array(3., dtype=float64, ...), 'theta': Array(1.57079633, dtype=float64, ...), 'phi': 0}
- coordinax.pt_map(p: dict, from_M: EuclideanManifold, from_chart: Spherical3D, to_M: EuclideanManifold, to_chart: Cylindrical3D, /, *, usys: AbstractUnitSystem | None = None) dict
Spherical3D -> Cylindrical3D.
>>> import coordinax.charts as cxc >>> import coordinax.manifolds as cxm >>> import unxt as u
A point on the +z axis (theta=0):
>>> p = {"r": u.Q(1.0, "m"), "theta": u.Q(0, "rad"), "phi": u.Q(0, "rad")} >>> cxc.pt_map(p, cxm.R3, cxc.sph3d, cxm.R3, cxc.cyl3d) {'rho': Q(0., 'm'), 'phi': Angle(0, 'rad'), 'z': Q(1., 'm')}
A point on the equator (theta=90 deg):
>>> p = {"r": 2.0, "theta": 90, "phi": 0} # No units >>> usys = u.unitsystem("m", "deg") >>> cxc.pt_map(p, cxm.R3, cxc.sph3d, cxm.R3, cxc.cyl3d, usys=usys) {'rho': Array(2., dtype=float64, ...), 'phi': 0, 'z': Array(1.2246468e-16, dtype=float64, ...)}
- coordinax.pt_map(p: dict, from_M: EuclideanManifold, from_chart: Spherical3D, to_M: EuclideanManifold, to_chart: LonLatSpherical3D, /, *, usys: AbstractUnitSystem | None = None) dict
Spherical3D -> LonLatSpherical3D.
>>> import coordinax.manifolds as cxm >>> import coordinax.charts as cxc >>> import unxt as u
Spherical theta=0 corresponds to lat=90 (north pole):
>>> p = {"r": u.Q(1.0, "m"), "theta": u.Q(0, "rad"), "phi": u.Q(0, "rad")} >>> cxc.pt_map(p, cxm.R3, cxc.sph3d, cxm.R3, cxc.lonlat_sph3d) {'lon': Angle(0, 'rad'), 'lat': Angle(90., 'deg'), 'distance': Q(1., 'm')}
Spherical theta=90 deg corresponds to lat=0 (equator):
>>> p = {"r": 1.0, "theta": 0, "phi": 0} # No units >>> cxc.pt_map(p, cxm.R3, cxc.sph3d, cxm.R3, cxc.lonlat_sph3d) {'lon': 0, 'lat': 1.5707963267948966, 'distance': 1.0}
- coordinax.pt_map(p: dict, from_M: EuclideanManifold, from_chart: Spherical3D, to_M: EuclideanManifold, to_chart: LonCosLatSpherical3D, /, *, usys: AbstractUnitSystem | None = None) dict
Spherical3D -> LonCosLatSpherical3D.
>>> import coordinax.manifolds as cxm >>> import coordinax.charts as cxc >>> import unxt as u
On the equator (theta=90 deg), lon_coslat equals lon:
>>> p = {"r": u.Q(1.0, "m"), "theta": u.Q(90, "deg"), "phi": u.Q(45, "deg")} >>> cxc.pt_map(p, cxm.R3, cxc.sph3d, cxm.R3, cxc.loncoslat_sph3d) {'lon_coslat': Angle(45., 'deg'), 'lat': Angle(0, 'deg'), 'distance': Q(1., 'm')}
At the north pole (theta=0), lon_coslat = 0 regardless of phi:
>>> p = {"r": 1.0, "theta": 0, "phi": 45} # No units >>> usys = u.unitsystem("m", "deg") >>> cxc.pt_map(p, cxm.R3, cxc.sph3d, cxm.R3, cxc.loncoslat_sph3d, usys=usys) {'lon_coslat': Array(2.7554553e-15, dtype=float64, ...), 'lat': 90.0, 'distance': 1.0}
- coordinax.pt_map(p: dict, from_M: EuclideanManifold, from_chart: Spherical3D, to_M: EuclideanManifold, to_chart: MathSpherical3D, /, *, usys: AbstractUnitSystem | None = None) dict
Spherical3D -> MathSpherical3D.
Swaps theta and phi: Physics (theta=polar, phi=azimuth) to Math (theta=azimuth, phi=polar).
>>> import coordinax.manifolds as cxm >>> import coordinax.charts as cxc >>> import unxt as u
>>> p = {"r": u.Q(1.0, "m"), "theta": u.Q(30, "deg"), "phi": u.Q(60, "deg")} >>> cxc.pt_map(p, cxm.R3, cxc.sph3d, cxm.R3, cxc.math_sph3d) {'r': Q(1., 'm'), 'theta': Angle(60, 'deg'), 'phi': Angle(30, 'deg')}
>>> p = {"r": 1.0, "theta": 30, "phi": 60} # No units >>> usys = u.unitsystem("m", "deg") >>> cxc.pt_map(p, cxm.R3, cxc.sph3d, cxm.R3, cxc.math_sph3d, usys=usys) {'r': 1.0, 'theta': 60, 'phi': 30}
- coordinax.pt_map(p: dict, from_M: EuclideanManifold, from_chart: MathSpherical3D, to_M: EuclideanManifold, to_chart: Spherical3D, /, *, usys: AbstractUnitSystem | None = None) dict
MathSpherical3D -> Spherical3D.
Swaps theta and phi: Math (theta=azimuth, phi=polar) to Physics (theta=polar, phi=azimuth).
>>> import coordinax.manifolds as cxm >>> import coordinax.charts as cxc >>> import unxt as u
>>> p = {"r": u.Q(1.0, "m"), "theta": u.Q(60, "deg"), "phi": u.Q(30, "deg")} >>> cxc.pt_map(p, cxm.R3, cxc.math_sph3d, cxm.R3, cxc.sph3d) {'r': Q(1., 'm'), 'theta': Angle(30, 'deg'), 'phi': Angle(60, 'deg')}
>>> p = {"r": 1.0, "theta": 60, "phi": 30} # No units >>> usys = u.unitsystem("m", "deg") >>> cxc.pt_map(p, cxm.R3, cxc.math_sph3d, cxm.R3, cxc.sph3d, usys=usys) {'r': 1.0, 'theta': 30, 'phi': 60}
- coordinax.pt_map(p: dict, from_M: EuclideanManifold, from_chart: ProlateSpheroidal3D, to_M: EuclideanManifold, to_chart: Cylindrical3D, /, *, usys: AbstractUnitSystem | None = None) dict
ProlateSpheroidal3D -> Cylindrical3D.
Uses the focal length \(\Delta\) stored on
from_chart.Validity constraints (enforced by the representation) are:
\(\Delta > 0\),
\(\mu \ge \Delta^2\),
\(\lvert\nu\rvert \le \Delta^2\).
The conversion proceeds via
\(\rho = \sqrt{(\mu-\Delta^2)\left(1-\frac{\lvert\nu\rvert}{\Delta^2}\right)}\), \(z = \sqrt{\mu\,\frac{\lvert\nu\rvert}{\Delta^2}}\,\mathrm{sign}(\nu)\), \(\phi = \phi\).
>>> import coordinax.manifolds as cxm >>> import coordinax.charts as cxc >>> import unxt as u
>>> prolatesph3d = cxc.ProlateSpheroidal3D(Delta=u.StaticQuantity(2.0, "m"))
>>> p = {"mu": u.Q(5.0, "m2"), "nu": u.Q(1.0, "m2"), "phi": u.Q(0, "rad")} >>> cxc.pt_map(p, cxm.R3, prolatesph3d, cxm.R3, cxc.cyl3d) {'rho': Q(0.8660254, 'm'), 'phi': Angle(0, 'rad'), 'z': Q(1.11803399, 'm')}
>>> p = {"mu": 5.0, "nu": 1.0, "phi": 0} # No units >>> usys = u.unitsystem("m", "rad") >>> cxc.pt_map(p, cxm.R3, prolatesph3d, cxm.R3, cxc.cyl3d, usys=usys) {'rho': Array(0.8660254, dtype=float64), 'phi': 0, 'z': Array(1.11803399, dtype=float64)}
- coordinax.pt_map(p: dict, from_M: EuclideanManifold, from_chart: Cylindrical3D, to_M: EuclideanManifold, to_chart: ProlateSpheroidal3D, /, *, usys: AbstractUnitSystem | None = None) dict
Cylindrical3D -> ProlateSpheroidal3D.
Uses the focal length \(\Delta\) stored on
to_chart.Let \(R^2 = \rho^2\) and \(z^2 = z^2\) and define
\(S = R^2 + z^2 + \Delta^2\), \(D_f = R^2 + z^2 - \Delta^2\), \(D = \sqrt{D_f^2 + 4 R^2 \Delta^2}\).
Then
\(\mu = \Delta^2 + \tfrac12(D + D_f)\) (with numerically-stable branches), \(\lvert\nu\rvert = \dfrac{2\Delta^2}{S + D}\,z^2\), and \(\nu = \lvert\nu\rvert\,\mathrm{sign}(z)\), with a stability fix when \(\Delta^2 - \lvert\nu\rvert\) is small.
>>> import coordinax.manifolds as cxm >>> import coordinax.charts as cxc >>> import unxt as u
>>> prolatesph3d = cxc.ProlateSpheroidal3D(Delta=u.StaticQuantity(2.0, "m"))
A point on the z-axis (rho=0):
>>> p = {"rho": u.Q(0.0, "m"), "phi": u.Q(0, "rad"), "z": u.Q(3.0, "m")} >>> cxc.pt_map(p, cxm.R3, cxc.cyl3d, cxm.R3, prolatesph3d) {'mu': Q(9., 'm2'), 'nu': Q(4., 'm2'), 'phi': Angle(0, 'rad')}
A point in the xy-plane (z=0):
>>> p = {"rho": u.Q(2.0, "m"), "phi": u.Q(0, "rad"), "z": u.Q(0.0, "m")} >>> cxc.pt_map(p, cxm.R3, cxc.cyl3d, cxm.R3, prolatesph3d) {'mu': Q(8., 'm2'), 'nu': Q(0., 'm2'), 'phi': Angle(0, 'rad')}
Without units:
>>> p = {"rho": 2.0, "phi": 0, "z": 3.0} # No units >>> usys = u.unitsystem("m", "rad") >>> cxc.pt_map(p, cxm.R3, cxc.cyl3d, cxm.R3, prolatesph3d, usys=usys) {'mu': Array(14.52079729, dtype=float64), 'nu': Array(2.47920271, dtype=float64), 'phi': 0}
- coordinax.pt_map(p: dict, from_M: EuclideanManifold, from_chart: Cart3D, to_M: EuclideanManifold, to_chart: ProlateSpheroidal3D, /, *, usys: AbstractUnitSystem | None = None) dict
Cart3D -> Cylindrical3D -> ProlateSpheroidal3D.
ProlateSpheroidal3Dis only registered as a target fromCylindrical3D; without this route the genericA -> A.cartesian -> Bfallback would sendCart3D -> Cart3D -> ProlateSpheroidal3Dand recurse forever. Route throughCylindrical3Dinstead (mirrors theCart3D -> AbstractSpherical3Drule).>>> import coordinax.manifolds as cxm >>> import coordinax.charts as cxc >>> import unxt as u
>>> prolate = cxc.ProlateSpheroidal3D(Delta=u.StaticQuantity(2.0, "m")) >>> p = {"x": u.Q(0.0, "m"), "y": u.Q(0.0, "m"), "z": u.Q(3.0, "m")} >>> cxc.pt_map(p, cxm.R3, cxc.cart3d, cxm.R3, prolate) {'mu': Q(9., 'm2'), 'nu': Q(4., 'm2'), 'phi': Angle(0., 'rad')}
- coordinax.pt_map(p: dict, from_M: EuclideanManifold, from_chart: ProlateSpheroidal3D, to_M: EuclideanManifold, to_chart: ProlateSpheroidal3D, /, *, usys: AbstractUnitSystem | None = None) dict
{class}`coordinax.charts.ProlateSpheroidal3D` -> itself.
If the focal length is unchanged (
to_chart.Delta == from_chart.Delta), this is the identity map.If the focal length changes, we convert via cylindrical coordinates:
Prolate(Delta_in) -> Cylindrical -> Prolate(Delta_out).>>> import coordinax.manifolds as cxm >>> import coordinax.charts as cxc >>> import unxt as u
Same focal length (identity transformation):
>>> prolate = cxc.ProlateSpheroidal3D(Delta=u.StaticQuantity(2.0, "m")) >>> p = {"mu": u.Q(5.0, "m2"), "nu": u.Q(1.0, "m2"), "phi": u.Q(0, "rad")} >>> cxc.pt_map(p, cxm.R3, prolate, cxm.R3, prolate) {'mu': Q(5., 'm2'), 'nu': Q(1., 'm2'), 'phi': Angle(0., 'rad')}
Different focal lengths (converts via cylindrical):
>>> prolate_in = cxc.ProlateSpheroidal3D(Delta=u.StaticQuantity(2.0, "m")) >>> prolate_out = cxc.ProlateSpheroidal3D(Delta=u.StaticQuantity(3.0, "m")) >>> p = {"mu": u.Q(5.0, "m2"), "nu": u.Q(1.0, "m2"), "phi": u.Q(0, "rad")} >>> cxc.pt_map(p, cxm.R3, prolate_in, cxm.R3, prolate_out) {'mu': Q(9.85889894, 'm2'), 'nu': Q(1.14110106, 'm2'), 'phi': Angle(0., 'rad')}
- coordinax.pt_map(p: dict, from_M: EuclideanManifold, from_chart: CartND, to_M: EuclideanManifold, to_chart: AbstractChart, /, *, usys: AbstractUnitSystem | None = None) dict
CartND -> AbstractChart.
Converts from N-dimensional Cartesian (with a single ‘q’ array) to any other chart type by first extracting the appropriate fixed-dimensional Cartesian representation.
>>> import coordinax.manifolds as cxm >>> import coordinax.charts as cxc >>> import unxt as u
Convert 3D CartND to Spherical:
>>> p = {"q": u.Q([1.0, 0.0, 0.0], "m")} >>> cxc.pt_map(p, cxm.RN, cxc.cartnd, cxm.R3, cxc.sph3d) {'r': Q(1., 'm'), 'theta': Angle(1.57079633, 'rad'), 'phi': Angle(0., 'rad')}
Convert 2D CartND to Polar:
>>> p = {"q": u.Q([3.0, 4.0], "m")} >>> cxc.pt_map(p, cxm.RN, cxc.cartnd, cxm.R2, cxc.polar2d) {'r': Q(5., 'm'), 'theta': Angle(0.92729522, 'rad')}
Convert 1D CartND to Radial:
>>> p = {"q": u.Q([5.0], "m")} >>> cxc.pt_map(p, cxm.RN, cxc.cartnd, cxm.R1, cxc.radial1d) {'r': Q(5., 'm')}
Convert CartND to Cart3D:
>>> p = {"q": u.Q([1.0, 2.0, 3.0], "m")} >>> cxc.pt_map(p, cxm.RN, cxc.cartnd, cxm.R3, cxc.cart3d) {'x': Q(1., 'm'), 'y': Q(2., 'm'), 'z': Q(3., 'm')}
- coordinax.pt_map(p: dict, from_M: EuclideanManifold, from_chart: AbstractChart, to_M: EuclideanManifold, to_chart: CartND, /, *, usys: AbstractUnitSystem | None = None) dict
AbstractChart -> CartND.
Converts from any chart type to N-dimensional Cartesian (with a single ‘q’ array) by first transforming to the appropriate fixed-dimensional Cartesian representation.
>>> import coordinax.manifolds as cxm >>> import coordinax.charts as cxc >>> import unxt as u
Convert Cart3D to CartND:
>>> p = {"x": u.Q(1.0, "m"), "y": u.Q(2.0, "m"), "z": u.Q(3.0, "m")} >>> cxc.pt_map(p, cxm.R3, cxc.cart3d, cxm.R3, cxc.cartnd) {'q': Q([1., 2., 3.], 'm')}
Convert Cart2D to CartND:
>>> p = {"x": u.Q(3.0, "m"), "y": u.Q(4.0, "m")} >>> cxc.pt_map(p, cxm.R2, cxc.cart2d, cxm.R2, cxc.cartnd) {'q': Q([3., 4.], 'm')}
Convert Radial to CartND:
>>> p = {"r": u.Q(3.0, "m")} >>> cxc.pt_map(p, cxc.radial1d, cxc.cartnd) {'q': Q([3.], 'm')}
Convert Cylindrical to CartND (z-axis point):
>>> p = {"rho": u.Q(0.0, "m"), "phi": u.Q(0, "rad"), "z": u.Q(5.0, "m")} >>> cxc.pt_map(p, cxc.cyl3d, cxc.cartnd) {'q': Q([0., 0., 5.], 'm')}
- coordinax.pt_map(q: AbstractQuantity, from_M: EuclideanManifold, from_chart: AbstractChart, to_M: EuclideanManifold, to_chart: AbstractChart, /, *, usys: AbstractUnitSystem | None = None) AbstractQuantity
Identity point transform for Quantity inputs on uniform-unit charts.
For charts where all components share the same unit (Cartesian charts, 0D/1D charts), a Quantity can be passed directly and is returned unchanged when the source and target charts are the same type.
This dispatch only handles identity transformations (same chart type). For transformations between different chart types with Quantity input, the Quantity must first be converted to a coordinate dictionary.
>>> import coordinax.manifolds as cxm >>> import coordinax.charts as cxc >>> import unxt as u
1D Cartesian (identity):
>>> q = u.Q([5.0], "m") >>> cxc.pt_map(q, cxm.R1, cxc.cart1d, cxm.R1, cxc.cart1d, usys=None) is q True
2D Cartesian (identity):
>>> q = u.Q([3.0, 4.0], "m") >>> cxc.pt_map(q, cxm.R2, cxc.cart2d, cxm.R2, cxc.cart2d, usys=None) is q True
3D Cartesian (identity):
>>> q = u.Q([1.0, 2.0, 3.0], "km") >>> cxc.pt_map(q, cxm.R3, cxc.cart3d, cxm.R3, cxc.cart3d, usys=None) is q True
N-D Cartesian (identity):
>>> q = u.Q([1.0, 2.0, 3.0, 4.0], "m") >>> cxc.pt_map(q, cxm.RN, cxc.cartnd, cxm.RN, cxc.cartnd, usys=None) is q True
- coordinax.pt_map(p: AbstractQuantity, from_M: EuclideanManifold, from_chart: AbstractChart, to_M: EuclideanManifold, to_chart: AbstractChart, /, *, usys: AbstractUnitSystem | None = None) unxts.linalg._src._quantity_matrix.QuantityMatrix
Transform a QuantityMatrix between charts.
Converts the components of a QuantityMatrix from one chart to another, preserving the matrix structure with potentially different units per component.
>>> import coordinax.charts as cxc >>> import unxt as u
2D Cartesian to Polar:
>>> q = u.Q([3.0, 4.0], "m") >>> result = cxc.pt_map(q, cxc.cart2d, cxc.polar2d) >>> result.shape (2,) >>> result.unit UnitsMatrix("(m, rad)")
3D Cartesian to Spherical:
>>> q = u.Q([1.0, 0.0, 0.0], "kpc") >>> result = cxc.pt_map(q, cxc.cart3d, cxc.sph3d) >>> result.shape (3,)
Batched transformation:
>>> q_batch = u.Q([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]], "m") >>> result = cxc.pt_map(q_batch, cxc.cart3d, cxc.sph3d) >>> result.shape (2, 3)
- coordinax.pt_map(p: Array | list, from_M: EuclideanManifold, from_chart: AbstractChart, to_M: EuclideanManifold, to_chart: AbstractChart, /, *, usys: AbstractUnitSystem | None) Array
Point transform for array input.
Transforms a point represented as a raw array (without units) from one chart to another. The unit system
usysprovides the units for interpreting the array components.- Returns:
Array of shape
(..., ndim)containing the transformed coordinates into_chart.- Return type:
- Parameters:
Examples
>>> import coordinax.charts as cxc >>> import unxt as u >>> import jax.numpy as jnp
Cartesian to Spherical (3D):
>>> usys = u.unitsystem("m", "rad") >>> p = jnp.array([1.0, 0.0, 0.0]) # Point on x-axis >>> cxc.pt_map(p, cxc.cart3d, cxc.sph3d, usys=usys) Array([1. , 1.57079633, 0. ], dtype=float64)
The result is [r, theta, phi] = [1, pi/2, 0] (on equator, x-axis).
Spherical to Cartesian (3D):
>>> p = jnp.array([2.0, jnp.pi/4, 0.0]) # r=2, theta=45°, phi=0 >>> cxc.pt_map(p, cxc.sph3d, cxc.cart3d, usys=usys) Array([1.41421356, 0. , 1.41421356], dtype=float64)
Cartesian to Cylindrical:
>>> p = jnp.array([3.0, 4.0, 5.0]) >>> cxc.pt_map(p, cxc.cart3d, cxc.cyl3d, usys=usys) Array([5. , 0.92729522, 5. ], dtype=float64)
The result is [rho, phi, z] = [5, arctan(4/3), 5].
Batched transformation:
>>> p_batch = jnp.array([[1.0, 0.0, 0.0], ... [0.0, 1.0, 0.0], ... [0.0, 0.0, 1.0]]) >>> cxc.pt_map(p_batch, cxc.cart3d, cxc.sph3d, usys=usys) Array([[1. , 1.57079633, 0. ], [1. , 1.57079633, 1.57079633], [1. , 0. , 0. ]], dtype=float64)
2D Cartesian to Polar:
>>> usys_2d = u.unitsystem("m", "rad") >>> p = jnp.array([3.0, 4.0]) >>> cxc.pt_map(p, cxc.cart2d, cxc.polar2d, usys=usys_2d) Array([5. , 0.92729522], dtype=float64)
- coordinax.pt_map(p: dict, from_M: CartesianProductManifold, from_chart: CartesianProductChart, to_M: NoManifold, to_chart: PoincarePolar6D, /, *, usys: AbstractUnitSystem | None = None) dict
Cartesian phase space
cart3d x cart3d->PoincarePolar6D(gala forward).The source is a two-factor Cartesian product chart: factor 0 is position
(x, y, z), factor 1 its velocity(vx, vy, vz). Implements gala’scartesian_to_poincare_polar(Papaphilippou & Laskar 1996):rho = hypot(x, y),phi = atan2(x, y)(gala’s azimuth convention),dt_rho = (x*vx + y*vy) / rho,Lz = x*vy - y*vx,pp_phi = sqrt(2|Lz|) cos(phi),pp_phidot = sqrt(2|Lz|) sin(phi),dt_z = vz.sqrt(|Lz|)discardssign(Lz), so there is no global inverse. A partial inverse (assumingLz >= 0) is registered below.>>> import coordinax.charts as cxc >>> import unxt as u >>> ps = cxc.CartesianProductChart((cxc.cart3d, cxc.cart3d), ("q", "p")) >>> q = {"q.x": u.Q(3.0, "kpc"), "q.y": u.Q(4.0, "kpc"), "q.z": u.Q(5.0, "kpc"), ... "p.x": u.Q(1.0, "kpc/Myr"), "p.y": u.Q(2.0, "kpc/Myr"), ... "p.z": u.Q(0.5, "kpc/Myr")} >>> out = cxc.pt_map(q, ps.M, ps, cxc.poincarepolar6d.M, cxc.poincarepolar6d) >>> sorted(out) ['dt_rho', 'dt_z', 'pp_phi', 'pp_phidot', 'rho', 'z']
Lz = x*vy - y*vx = 2 kpc^2/Myr, so sqrt(2|Lz|) = 2; phi = atan2(3, 4):
>>> out["rho"], out["dt_rho"], out["dt_z"] (Q(5., 'kpc'), Q(2.2, 'kpc / Myr'), Q(0.5, 'kpc / Myr')) >>> out["pp_phi"].round(4), out["pp_phidot"].round(4) (Q(1.6, 'kpc / Myr(1/2)'), Q(1.2, 'kpc / Myr(1/2)'))
A non-Cartesian or wrong-arity product source is rejected:
>>> bad = cxc.CartesianProductChart((cxc.cart3d, cxc.polar2d), ("q", "p")) >>> try: ... cxc.pt_map({}, bad.M, bad, cxc.poincarepolar6d.M, cxc.poincarepolar6d) ... except NotImplementedError: ... print("rejected") rejected
- coordinax.pt_map(p: dict, from_M: NoManifold, from_chart: PoincarePolar6D, to_M: CartesianProductManifold, to_chart: CartesianProductChart, /, *, usys: AbstractUnitSystem | None = None) dict
PoincarePolar6D-> Cartesian phase space (partial inverse of gala map).Inverts the gala forward map. Because the forward uses
sqrt(2|Lz|)the sign of the angular momentum is not recoverable, so this assumesLz >= 0(the standard convention) and is a partial inverse — exact only when the original point had non-negativeLz:s = hypot(pp_phi, pp_phidot),phi = atan2(pp_phidot, pp_phi),Lz = s**2 / 2,x = rho sin(phi),y = rho cos(phi),vx = sin(phi) dt_rho - cos(phi) Lz/rho,vy = cos(phi) dt_rho + sin(phi) Lz/rho,vz = dt_z.(Singular on the axis
rho = 0, inherent to the coordinates.)>>> import coordinax.charts as cxc >>> import unxt as u >>> ps = cxc.CartesianProductChart((cxc.cart3d, cxc.cart3d), ("q", "p"))
Round-trips a forward result whose Lz >= 0:
>>> q = {"q.x": u.Q(3.0, "kpc"), "q.y": u.Q(4.0, "kpc"), "q.z": u.Q(5.0, "kpc"), ... "p.x": u.Q(1.0, "kpc/Myr"), "p.y": u.Q(2.0, "kpc/Myr"), ... "p.z": u.Q(0.5, "kpc/Myr")} >>> pp = cxc.pt_map(q, ps.M, ps, cxc.poincarepolar6d.M, cxc.poincarepolar6d) >>> back = cxc.pt_map(pp, cxc.poincarepolar6d.M, cxc.poincarepolar6d, ps.M, ps) >>> back["q.x"].round(6), back["q.y"].round(6), back["p.x"].round(6) (Q(3., 'kpc'), Q(4., 'kpc'), Q(1., 'kpc / Myr'))
- coordinax.pt_map(p: dict, from_chart: EmbeddedChart, to_chart: EmbeddedChart, /, *, usys: AbstractUnitSystem | None = None) dict
Convert between embedded manifolds with a shared ambient space.
This function transforms intrinsic coordinates from one embedded manifold to another by: 1. Embedding the point into the ambient space of the source manifold 2. Transforming in the ambient space (if the ambient charts differ) 3. Projecting back to the intrinsic coordinates of the target manifold
>>> import coordinax.charts as cxc >>> import coordinax.manifolds as cxm >>> import unxt as u >>> import quaxed.numpy as jnp
Example 1: Two spheres with different radii
Both spheres use the same intrinsic SphericalTwoSphere chart but have different radii:
>>> sphere1 = cxm.EmbeddedChart(cxm.TwoSphereIn3D(radius=u.Q(1.0, "km"))) >>> sphere2 = cxm.EmbeddedChart(cxm.TwoSphereIn3D(radius=u.Q(2.0, "km")))
A point on sphere1 (theta=pi/4, phi=0):
>>> p = {"theta": u.Q(45, "deg"), "phi": u.Q(0, "deg")} >>> p2 = cxc.pt_map(p, sphere1, sphere2) >>> {k: v.uconvert("deg") for k, v in p2.items()} {'theta': Angle(45, 'deg'), 'phi': Angle(0, 'deg')}
The angular coordinates are preserved (both spheres share the same angular parameterization via projection through the shared ambient space).
- coordinax.pt_map(p: dict, from_chart: AbstractChart, to_chart: EmbeddedChart, /, *, usys: AbstractUnitSystem | None = None) dict
Project an ambient position into an embedded chart.
This transforms coordinates from an ambient chart (e.g., Cartesian or Spherical) into the intrinsic coordinates of an embedded manifold.
>>> import coordinax.charts as cxc >>> import coordinax.manifolds as cxm >>> import unxt as u >>> import quaxed.numpy as jnp
From Cartesian ambient to SphericalTwoSphere intrinsic:
>>> sphere = cxm.EmbeddedChart(cxm.TwoSphereIn3D(radius=u.Q(1.0, "m")))
A point on the unit sphere in Cartesian coords (on equator, x-axis):
>>> p_cart = {"x": u.Q(1.0, "m"), "y": u.Q(0.0, "m"), "z": u.Q(0.0, "m")} >>> cxc.pt_map(p_cart, cxc.cart3d, sphere) {'theta': Angle(1.57079633, 'rad'), 'phi': Angle(0., 'rad')}
From Spherical ambient to SphericalTwoSphere intrinsic:
The ambient spherical coords (r, theta, phi) project to intrinsic (theta, phi), discarding the radial component:
>>> p_sph = {"r": 5, "theta": 1, "phi": 0.5} # No units >>> usys = u.unitsystem("m", "rad") >>> cxc.pt_map(p_sph, cxc.sph3d, sphere, usys=usys) {'theta': 1, 'phi': 0.5}
- coordinax.pt_map(p: dict, from_chart: EmbeddedChart, to_chart: AbstractChart, /, *, usys: AbstractUnitSystem | None = None) dict
Embed intrinsic coordinates into an ambient representation.
This transforms intrinsic coordinates of an embedded manifold into coordinates of an ambient chart, which may differ from the embedding’s native ambient chart.
>>> import coordinax.charts as cxc >>> import coordinax.manifolds as cxm >>> import unxt as u >>> import quaxed.numpy as jnp
From SphericalTwoSphere intrinsic to Cartesian ambient:
>>> sphere = cxm.EmbeddedChart(cxm.TwoSphereIn3D(radius=u.Q(1.0, "m")))
A point on the unit sphere (on equator, x-axis):
>>> p_sph = {"theta": u.Q(1.0, "rad"), "phi": u.Q(0.0, "rad")} >>> cxc.pt_map(p_sph, sphere, cxc.cart3d) {'x': Q(0.84147098, 'm'), 'y': Q(0., 'm'), 'z': Q(0.54030231, 'm')}
From SphericalTwoSphere intrinsic to Spherical ambient:
>>> p_sph = {"theta": u.Q(1.0, "rad"), "phi": u.Q(0.5, "rad")} >>> cxc.pt_map(p_sph, sphere, cxc.sph3d) {'r': Q(1., 'm'), 'theta': Angle(1., 'rad'), 'phi': Angle(0.5, 'rad')}
- coordinax.pt_map(p: dict, M: EmbeddedManifold, from_chart: AbstractChart, to_chart: AbstractChart, /, *, usys: AbstractUnitSystem | None = None) dict
Convert between embedded manifolds with a shared ambient space.
>>> import unxt as u >>> import coordinax.charts as cxc >>> import coordinax.manifolds as cxm
>>> M = cxm.embedded_twosphere(radius=u.Q(1, "kpc")) >>> M EmbeddedManifold(intrinsic=HyperSphericalManifold(...), ambient=Rn(3), embed_map=TwoSphereIn3D(radius=Q(1, 'kpc'), ambient=Spherical3D(M=Rn(3)))) >>> x_cart = {"x": u.Q(1, "m"), "y": u.Q(2, "m"), "z": u.Q(3, "m")}
>>> x_sph2 = cxc.pt_map(x_cart, M, cxc.cart3d, cxc.loncoslat_sph2) >>> x_sph2 {'lon_coslat': Angle(0.66164791, 'rad'), 'lat': Angle(53.3007748, 'deg')}
>>> cxc.pt_map(x_sph2, M, cxc.loncoslat_sph2, cxc.cart3d) {'x': Q(0.26726124, 'kpc'), 'y': Q(0.53452248, 'kpc'), 'z': Q(0.80178373, 'kpc')}
- coordinax.pt_map(p: Any, from_chart: Abstract3D, to_chart: AbstractSphericalTwoSphere, /, *, usys: AbstractUnitSystem | None = None) Any
Project a point from the ambient chart to the two-sphere intrinsic chart.
This realization map is a special case for projecting from 3D charts to the two-sphere intrinsic chart, which is a common use case. The projection does not depend on the radius of the embedding, so this projection works in general.
>>> import unxt as u >>> import coordinax.charts as cxc
>>> q = {"x": u.Q(1.0, "km"), "y": u.Q(0.0, "km"), "z": u.Q(0.0, "km")} >>> cxc.pt_map(q, cxc.cart3d, cxc.sph2) {'theta': Angle(1.57079633, 'rad'), 'phi': Angle(0., 'rad')}
- coordinax.pt_map(p: dict, from_M: HyperSphericalManifold, from_chart: AbstractChart, to_M: HyperSphericalManifold, to_chart: AbstractChart, /, *, usys: AbstractUnitSystem | None = None) dict
Identity conversion for matching charts.
Returns the input object itself when it is already canonical – angular components held as unxt.Angle, as the chart declares. A non-canonical input is canonicalised instead, so it necessarily comes back as a new dict: the alternative is pt_map(q, chart, chart) preserving a container that every other route to the same chart would have normalised.
>>> import coordinax.manifolds as cxm >>> import coordinax.charts as cxc >>> import unxt as u
>>> q = {"theta": u.Angle(30, "deg"), "phi": u.Angle(60, "deg")} >>> cxc.pt_map(q, cxc.sph2, cxc.sph2) is q True
>>> q = {"lon": u.Angle(45, "deg"), "lat": u.Angle(10, "deg")} >>> cxc.pt_map(q, cxc.lonlat_sph2, cxc.lonlat_sph2) is q True
>>> q = {"lon_coslat": u.Angle(30, "deg"), "lat": u.Angle(20, "deg")} >>> cxc.pt_map(q, cxc.loncoslat_sph2, cxc.loncoslat_sph2) is q True
>>> q = {"theta": u.Angle(60, "deg"), "phi": u.Angle(30, "deg")} >>> cxc.pt_map(q, cxc.math_sph2, cxc.math_sph2) is q True
- coordinax.pt_map(p: dict, from_M: HyperSphericalManifold, from_chart: AbstractSphericalTwoSphere, to_M: HyperSphericalManifold, to_chart: AbstractSphericalTwoSphere, /, *, usys: AbstractUnitSystem | None = None) dict
Route between two-sphere charts via SphericalTwoSphere.
Each chart registers only its direct
sph2 <-> chartconversion, and the two-sphere has no Cartesian chart, so the generic router cannot bridge two non-canonical charts (it raisesNoGlobalCartesianChartError). GoA -> SphericalTwoSphere -> Binstead. Canonical pairs (either side is SphericalTwoSphere) and matching-type pairs are handled by the more specific direct/identity rules above, so this fallback fires only for distinct non-canonical charts – and covers any future AbstractSphericalTwoSphere subclass automatically.>>> import coordinax.charts as cxc >>> import coordinax.manifolds as cxm >>> import unxt as u
>>> p = {"lon": u.Q(45, "deg"), "lat": u.Q(30, "deg")} >>> out = cxc.pt_map(p, cxm.S2, cxc.lonlat_sph2, cxm.S2, cxc.math_sph2) >>> sorted(out) ['phi', 'theta']
- coordinax.pt_map(p: dict, from_M: HyperSphericalManifold, from_chart: SphericalTwoSphere, to_M: HyperSphericalManifold, to_chart: LonLatSphericalTwoSphere, /, *, usys: AbstractUnitSystem | None = None) dict
SphericalTwoSphere -> LonLatSphericalTwoSphere.
lat = pi/2 - theta, lon = phi.
>>> import coordinax.manifolds as cxm >>> import coordinax.charts as cxc >>> import unxt as u
>>> p = {"theta": u.Q(0, "rad"), "phi": u.Q(0, "rad")} # North pole >>> cxc.pt_map(p, cxm.S2, cxc.sph2, cxm.S2, cxc.lonlat_sph2) {'lon': Angle(0, 'rad'), 'lat': Angle(90., 'deg')}
>>> p = {"theta": u.Q(90, "deg"), "phi": u.Q(45, "deg")} # Equator >>> cxc.pt_map(p, cxm.S2, cxc.sph2, cxm.S2, cxc.lonlat_sph2) {'lon': Angle(45, 'deg'), 'lat': Angle(0, 'deg')}
- coordinax.pt_map(p: dict, from_M: HyperSphericalManifold, from_chart: LonLatSphericalTwoSphere, to_M: HyperSphericalManifold, to_chart: SphericalTwoSphere, /, *, usys: AbstractUnitSystem | None = None) dict
LonLatSphericalTwoSphere -> SphericalTwoSphere.
theta = pi/2 - lat, phi = lon.
>>> import coordinax.manifolds as cxm >>> import coordinax.charts as cxc >>> import unxt as u
>>> p = {"lon": u.Q(45, "deg"), "lat": u.Q(0, "deg")} >>> cxc.pt_map(p, cxm.S2, cxc.lonlat_sph2, cxm.S2, cxc.sph2) {'theta': Angle(90, 'deg'), 'phi': Angle(45, 'deg')}
- coordinax.pt_map(p: dict, from_M: HyperSphericalManifold, from_chart: SphericalTwoSphere, to_M: HyperSphericalManifold, to_chart: LonCosLatSphericalTwoSphere, /, *, usys: AbstractUnitSystem | None = None) dict
SphericalTwoSphere -> LonCosLatSphericalTwoSphere.
lat = pi/2 - theta, lon_coslat = phi * cos(lat).
>>> import coordinax.manifolds as cxm >>> import coordinax.charts as cxc >>> import unxt as u >>> import quaxed.numpy as jnp
>>> p = {"theta": u.Q(90, "deg"), "phi": u.Q(45, "deg")} # equator >>> cxc.pt_map(p, cxm.S2, cxc.sph2, cxm.S2, cxc.loncoslat_sph2) {'lon_coslat': Angle(45., 'deg'), 'lat': Angle(0, 'deg')}
>>> p = {"theta": u.Q(0, "deg"), "phi": u.Q(45, "deg")} # north pole >>> result = cxc.pt_map(p, cxm.S2, cxc.sph2, cxm.S2, cxc.loncoslat_sph2) >>> bool(jnp.allclose(u.ustrip("deg", result["lat"]), 90.0)) True
- coordinax.pt_map(p: dict, from_M: HyperSphericalManifold, from_chart: LonCosLatSphericalTwoSphere, to_M: HyperSphericalManifold, to_chart: SphericalTwoSphere, /, *, usys: AbstractUnitSystem | None = None) dict
LonCosLatSphericalTwoSphere -> SphericalTwoSphere.
theta = pi/2 - lat, phi = lon_coslat / cos(lat).
>>> import coordinax.charts as cxc >>> import coordinax.manifolds as cxm >>> import unxt as u
>>> p = {"lon_coslat": u.Q(45, "deg"), "lat": u.Q(0, "deg")} >>> cxc.pt_map(p, cxm.S2, cxc.loncoslat_sph2, cxm.S2, cxc.sph2) {'theta': Angle(90, 'deg'), 'phi': Angle(45., 'deg')}
- coordinax.pt_map(p: dict, from_M: HyperSphericalManifold, from_chart: SphericalTwoSphere, to_M: HyperSphericalManifold, to_chart: MathSphericalTwoSphere, /, *, usys: AbstractUnitSystem | None = None) dict
SphericalTwoSphere -> MathSphericalTwoSphere.
Swaps theta and phi (physics -> math convention).
>>> import coordinax.manifolds as cxm >>> import coordinax.charts as cxc >>> import unxt as u
>>> p = {"theta": u.Q(30, "deg"), "phi": u.Q(60, "deg")} >>> cxc.pt_map(p, cxm.S2, cxc.sph2, cxm.S2, cxc.math_sph2) {'theta': Angle(60, 'deg'), 'phi': Angle(30, 'deg')}
- coordinax.pt_map(p: dict, from_M: HyperSphericalManifold, from_chart: MathSphericalTwoSphere, to_M: HyperSphericalManifold, to_chart: SphericalTwoSphere, /, *, usys: AbstractUnitSystem | None = None) dict
MathSphericalTwoSphere -> SphericalTwoSphere.
Swaps theta and phi (math -> physics convention).
>>> import coordinax.manifolds as cxm >>> import coordinax.charts as cxc >>> import unxt as u
>>> p = {"theta": u.Q(60, "deg"), "phi": u.Q(30, "deg")} >>> cxc.pt_map(p, cxm.S2, cxc.math_sph2, cxm.S2, cxc.sph2) {'theta': Angle(30, 'deg'), 'phi': Angle(60, 'deg')}
- coordinax.pt_map(x: Any, from_chart: AbstractChart, from_rep: Representation, to_chart: AbstractChart, to_rep: Representation, /, usys: AbstractUnitSystem | None = None) Any
Convert point data between charts.
Convert a point from Cartesian coordinates to spherical coordinates:
>>> import coordinax.representations as cxr >>> import coordinax.charts as cxc
Define a point in Cartesian coordinates:
>>> p = {"x": 1.0, "y": 2.0, "z": 3.0}
Convert it to spherical coordinates:
>>> q = cxc.pt_map(p, cxc.cart3d, cxr.point, cxc.sph3d, cxr.point) >>> q {'r': Array(3.74165739, dtype=float64, ...), 'theta': Array(0.64052231, dtype=float64, ...), 'phi': Array(1.10714872, dtype=float64, ...)}
The output q represents the same geometric point but expressed in the target chart.
The representation remains unchanged; only the chart changes:
>>> cxc.pt_map(q, cxc.sph3d, cxr.point, cxc.cart3d, cxr.point) {'x': Array(1., dtype=float64, ...), 'y': Array(2., dtype=float64, ...), 'z': Array(3., dtype=float64, ...)}
Let’s work through more examples.
Cartesian to Spherical (with units):
>>> import unxt as u >>> p = {"x": u.Q(1.0, "m"), "y": u.Q(0.0, "m"), "z": u.Q(0.0, "m")} >>> cxc.pt_map(p, cxc.cart3d, cxr.point, cxc.sph3d, cxr.point) {'r': Q(1., 'm'), 'theta': Angle(1.57079633, 'rad'), 'phi': Angle(0., 'rad')}
Cylindrical to Cartesian (without units):
>>> p = {"rho": 3.0, "phi": 0, "z": 4.0} >>> cxc.pt_map(p, cxc.cyl3d, cxr.point, cxc.cart3d, cxr.point) {'x': Array(3., dtype=float64, ...), 'y': Array(0., dtype=float64, ...), 'z': 4.0}
Polar to Cartesian (2D):
>>> p = {"r": u.Q(5.0, "m"), "theta": u.Q(90, "deg")} >>> cxc.pt_map(p, cxc.polar2d, cxr.point, cxc.cart2d, cxr.point) {'x': Q(3.061617e-16, 'm'), 'y': Q(5., 'm')}
Between Spherical variants (Spherical to LonLatSpherical):
>>> p = {"r": u.Q(1.0, "m"), "theta": u.Q(45, "deg"), "phi": u.Q(0, "deg")} >>> cxc.pt_map(p, cxc.sph3d, cxr.point, cxc.lonlat_sph3d, cxr.point) {'lon': Angle(0, 'deg'), 'lat': Angle(45, 'deg'), 'distance': Q(1., 'm')}
Identity conversion (same chart):
>>> p = {"x": u.Q(2.0, "m"), "y": u.Q(3.0, "m")} >>> cxc.pt_map(p, cxc.cart2d, cxr.point, cxc.cart2d, cxr.point) is p True
- coordinax.pt_map(x: Any, from_chart: AbstractChart, from_rep: Representation, to_chart: AbstractChart, /, usys: AbstractUnitSystem | None = None) Any
Convert point data between charts.
Convert a point from Cartesian coordinates to spherical coordinates:
>>> import coordinax.representations as cxr >>> import coordinax.charts as cxc
Define a point in Cartesian coordinates:
>>> p = {"x": 1.0, "y": 2.0, "z": 3.0}
Convert it to spherical coordinates:
>>> q = cxc.pt_map(p, cxc.cart3d, cxr.point, cxc.sph3d) >>> q {'r': Array(3.74165739, dtype=float64, ...), 'theta': Array(0.64052231, dtype=float64, ...), 'phi': Array(1.10714872, dtype=float64, ...)}
The output q represents the same geometric point but expressed in the target chart.
The representation remains unchanged; only the chart changes:
>>> cxc.pt_map(q, cxc.sph3d, cxr.point, cxc.cart3d) {'x': Array(1., dtype=float64, ...), 'y': Array(2., dtype=float64, ...), 'z': Array(3., dtype=float64, ...)}
- coordinax.pt_map(x: Any, from_chart: AbstractChart, from_geom: PointGeometry, from_rep: Representation, to_chart: AbstractChart, to_geom: PointGeometry, to_rep: Representation, /, usys: AbstractUnitSystem | None = None) Any
Convert point data between charts.
Convert a point from Cartesian coordinates to spherical coordinates:
>>> import coordinax.representations as cxr >>> import coordinax.charts as cxc
Define a point in Cartesian coordinates:
>>> p = {"x": 1.0, "y": 2.0, "z": 3.0}
Convert it to spherical coordinates:
>>> cxc.pt_map(p, cxc.cart3d, cxr.point_geom, cxr.point, ... cxc.sph3d, cxr.point_geom, cxr.point) {'r': Array(3.74165739, dtype=float64, ...), 'theta': Array(0.64052231, dtype=float64, ...), 'phi': Array(1.10714872, dtype=float64, ...)}
- coordinax.pt_map(from_vec: Point, to_chart: AbstractChart, /, *, usys: AbstractUnitSystem | None = None) Point
Convert a point from one chart to another.
>>> import unxt as u >>> import coordinax as cx >>> import coordinax.charts as cxc
>>> vec = cx.Point.from_([1, 1, 1], "m") >>> print(vec) <Point: chart=Cart3D (x, y, z) [m] [1 1 1]>
>>> sph_vec = cxc.pt_map(vec, cx.sph3d) >>> print(sph_vec) <Point: chart=Spherical3D (r[m], theta[rad], phi[rad]) [1.732 0.955 0.785]>
- coordinax.pt_map(from_vec: Point, from_chart: AbstractChart, to_chart: AbstractChart, /, *, usys: AbstractUnitSystem | None = None) Point
Convert a vector from one chart to another.
>>> import unxt as u >>> import coordinax as cx >>> import coordinax.charts as cxc
>>> vec = cx.Point.from_([1, 1, 1], "m") >>> sph_vec = cxc.pt_map(vec, cxc.cart3d, cx.sph3d) >>> print(sph_vec) <Point: chart=Spherical3D (r[m], theta[rad], phi[rad]) [1.732 0.955 0.785]>
- coordinax.pt_map(p: dict, from_M: EuclideanManifold, from_chart: TubularChart, to_M: EuclideanManifold, to_chart: Cart3D, /, *, usys: AbstractUnitSystem | None = None) dict
TubularChart -> Cart3D: \(\gamma(\tau) + n_1U_1 + n_2U_2\).
Examples
>>> import jax.numpy as jnp >>> import unxt as u >>> import coordinax.charts as cxc >>> import coordinaxs.curveframes as cxfc
>>> def circle(tau): ... t = tau.ustrip("s") ... return u.Q(jnp.stack([jnp.cos(t), jnp.sin(t), jnp.zeros_like(t)]), "km")
>>> chart = cxfc.TubularChart( ... cxfc.BishopBuilder(circle, "s", normal_0="auto"), ... tau_bounds=(u.Q(0.0, "s"), u.Q(2 * jnp.pi, "s")), ... ) >>> p = {"tau": u.Q(0.0, "s"), "n1": u.Q(0.1, "km"), "n2": u.Q(0.0, "km")} >>> cxc.pt_map(p, chart.M, chart, chart.M, cxc.cart3d) {'x': Q(1.1, 'km'), 'y': Q(0., 'km'), 'z': Q(0., 'km')}
Raw coordinates take the same route, given a
usysto say what their numbers mean, and come back raw:>>> usys = u.unitsystem("km", "s", "kg", "rad") >>> p = {"tau": 0.0, "n1": 0.1, "n2": 0.0} >>> cxc.pt_map(p, chart.M, chart, chart.M, cxc.cart3d, usys=usys) {'x': Array(1.1, dtype=float64...), 'y': Array(0., dtype=float64...), 'z': Array(0., dtype=float64...)}
- coordinax.pt_map(p: dict, from_M: EuclideanManifold, from_chart: Cart3D, to_M: EuclideanManifold, to_chart: TubularChart, /, *, usys: AbstractUnitSystem | None = None) dict
Cart3D -> TubularChart, via the nearest-point projection.
Examples
>>> import jax.numpy as jnp >>> import unxt as u >>> import coordinax.charts as cxc >>> import coordinaxs.curveframes as cxfc
>>> def circle(tau): ... t = tau.ustrip("s") ... return u.Q(jnp.stack([jnp.cos(t), jnp.sin(t), jnp.zeros_like(t)]), "km")
>>> chart = cxfc.TubularChart( ... cxfc.BishopBuilder(circle, "s", normal_0="auto"), ... tau_bounds=(u.Q(0.0, "s"), u.Q(2 * jnp.pi, "s")), ... )
A point exactly on the curve has zero offsets. Bishop runs an ODE solve internally, so the recovered values are checked with a tolerance rather than pinned to exact digits:
>>> on_curve = chart.builder.location(u.Q(0.0, "s")) >>> p = {k: on_curve[i] for i, k in enumerate(("x", "y", "z"))} >>> back = cxc.pt_map(p, chart.M, cxc.cart3d, chart.M, chart) >>> bool(jnp.allclose(back["tau"].ustrip("s"), 0.0, atol=1e-6)) True >>> bool(jnp.allclose(back["n1"].ustrip("km"), 0.0, atol=1e-6)) True >>> bool(jnp.allclose(back["n2"].ustrip("km"), 0.0, atol=1e-6)) True
- coordinax.pt_map(p: dict, from_M: EuclideanManifold, from_chart: TubularChart, to_M: EuclideanManifold, to_chart: TubularChart, /, *, usys: AbstractUnitSystem | None = None) dict
TubularChart -> TubularChart. Identity only for the same chart object.
Parameterized charts compare conservatively (equal only when identical), so this declines to a Cartesian round trip whenever the two differ.
Examples
>>> import jax.numpy as jnp >>> import unxt as u >>> import coordinax.charts as cxc >>> import coordinaxs.curveframes as cxfc
>>> def circle(tau): ... t = tau.ustrip("s") ... return u.Q(jnp.stack([jnp.cos(t), jnp.sin(t), jnp.zeros_like(t)]), "km")
>>> chart = cxfc.TubularChart( ... cxfc.BishopBuilder(circle, "s", normal_0="auto"), ... tau_bounds=(u.Q(0.0, "s"), u.Q(2 * jnp.pi, "s")), ... ) >>> p = {"tau": u.Q(0.0, "s"), "n1": u.Q(0.1, "km"), "n2": u.Q(0.0, "km")} >>> cxc.pt_map(p, chart.M, chart, chart.M, chart) is p True
- coordinax.jac_pt_map(*args, **kwargs)
Compute the Jacobian of the chart transition map at a base point.
Examples
>>> import coordinax.charts as cxc >>> import unxt as u >>> J = cxc.jac_pt_map( ... {"x": u.Q(1.0, "m"), "y": u.Q(0.0, "m"), "z": u.Q(0.0, "m")}, ... cxc.cart3d, cxc.sph3d, ... ) >>> J.value.shape (3, 3)
- coordinax.jac_pt_map(at: NoneType, /, *fixed_args: Any, **fixed_kw: Any) Any
Higher-order function for fixed-arg Jacobian point map.
>>> import coordinax.charts as cxc >>> import unxt as u
>>> map = cxc.jac_pt_map(None, cxc.cart3d, cxc.sph3d, usys=u.unitsystems.si) >>> at = {"x": u.Q(1.0, "m"), "y": u.Q(0.0, "m"), "z": u.Q(0.0, "m")} >>> map(at) QM( [[ 1., 0., 0.], [ 0., 0., -1.], [-0., 1., 0.]], '((, , ), (rad / m, rad / m, rad / m), (rad / m, rad / m, rad / m))' )
>>> import jax >>> J = jax.vmap(map)(jax.tree.map(lambda x: x[None], at)) >>> J.shape (1, 3, 3)
- coordinax.jac_pt_map(from_chart: AbstractChart, to_chart: AbstractChart, /, *, usys: AbstractUnitSystem | None) Callable[[object], Any]
Higher-order function for fixed-arg Jacobian point map.
>>> import coordinax.charts as cxc >>> import unxt as u
>>> map = cxc.jac_pt_map(cxc.cart3d, cxc.sph3d, usys=u.unitsystems.si) >>> at = {"x": u.Q(1.0, "m"), "y": u.Q(0.0, "m"), "z": u.Q(0.0, "m")} >>> map(at) QM( [[ 1., 0., 0.], [ 0., 0., -1.], [-0., 1., 0.]], '((, , ), (rad / m, rad / m, rad / m), (rad / m, rad / m, rad / m))' )
>>> import jax >>> J = jax.vmap(map)(jax.tree.map(lambda x: x[None], at)) >>> J.shape (1, 3, 3)
- coordinax.jac_pt_map(at: Array, from_chart: AbstractChart, to_chart: AbstractChart, /, *, usys: AbstractUnitSystem) Array
Compute the Jacobian at a plain-array base point.
Treats at as a flat numeric array whose elements are the
from_chartcoordinates expressed. Returns the JAX array Jacobian \(J^j{}_i = \partial \tau^j / \partial q^i\), without unit annotation.>>> import jax.numpy as jnp >>> import coordinax.charts as cxc >>> import unxt as u
This is the fallback. A pair in _CLOSED_FORM_PAIRS is served by its own closed form instead – including the ones that reach their answer by chaining two others, which is a choice each of those makes internally and not something resolved here. Every other pair is differentiated.
Cylindrical3D -> MathSpherical3D has no closed form, so it comes here.
>>> jac_fn = cxc.jac_pt_map(None, cxc.cyl3d, cxc.math_sph3d, usys=u.unitsystems.si)
>>> at = jnp.array([1.0, 0.0, 0.0]) >>> jac_fn(at) Array([[ 1., 0., 0.], [ 0., 1., 0.], [ 0., 0., -1.]], dtype=float64)
>>> import jax >>> J = jax.vmap(jac_fn)(at[None]) >>> J.shape (1, 3, 3)
- coordinax.jac_pt_map(at: dict, from_chart: AbstractChart, to_chart: AbstractChart, /, *, usys: AbstractUnitSystem | None = None) Array | unxts.linalg._src._quantity_matrix.QuantityMatrix
Compute the Jacobian at a coordinate-dictionary base point.
The primary dict-input dispatch. Takes the first route that applies:
- Closed form registered for the chart pair
_jac_from_dict_via_closed_form canonicalises the components, evaluates the closed form, and restores the units – for unitful and plain-array dicts alike.
- Array-valued branch (no units in any value)
Stacks the dict values into a plain array via
jnp.stack, then forwards tojac_pt_map(at_arr, from_chart, to_chart, usys=usys). A pair with a closed form reads bare angles as radians; any other needs usys to say what the numbers mean, and raises ValueError without one.- Quantity-valued branch (a unitful value, no closed form for the pair)
Packs at into a 1-D
QuantityMatrixviacarray(at, from_chart.components), promotes any integer or boolean leaves to the default floating-point dtype (other dtypes, including complex, are left unchanged and will raise aTypeErrorfromjax.jacfwdif passed), then computesJ_qq = jax.jacfwd(pt_map_fn)(at_in). Becausejacfwdapplied to aQuantityMatrix-in /QuantityMatrix-out function yields a nestedQuantityMatrix,_repack_q_from_jacis called to extract the correct 2-D unit structure.
- Returns:
Plain array when at is array-valued;
QuantityMatrixof shape(n_out, n_in)with per-element units otherwise.- Return type:
- Raises:
ValueError – If at keys do not match
from_chart.components(viacheck_data), or – frompt_map– “usys must be provided for array input” when bare values reach a pair with no closed form.- Parameters:
Examples
>>> import coordinax.charts as cxc >>> import unxt as u
Quantity-valued dict (no usys needed):
>>> at = {"x": u.Q(1.0, "m"), "y": u.Q(0.0, "m"), "z": u.Q(0.0, "m")} >>> J = cxc.jac_pt_map(at, cxc.cart3d, cxc.sph3d) >>> J.value.shape (3, 3)
Plain-array dict (usys required):
>>> import jax.numpy as jnp >>> at_arr = {"x": jnp.array(1.0), "y": jnp.array(0.0), "z": jnp.array(0.0)} >>> J2 = cxc.jac_pt_map(at_arr, cxc.cart3d, cxc.sph3d, usys=u.unitsystems.si) >>> J2.shape (3, 3)
- coordinax.jac_pt_map(at: Array, from_chart: Cart2D, to_chart: Polar2D, /, *, usys: AbstractUnitSystem | None = None) Array
Compute the Jacobian of the transition function between two charts.
\[J = \frac{\partial(r,\theta)}{\partial(x,y)} = ( x/r & y/r \ -y/r^2 & x/r^2 ) = ( \cos\theta & \sin\theta \ -\sin\theta/r & \cos\theta/r )\]as written, in radians per unit length. The result carries no units, so it has to mean the same thing pt_map(…, usys=usys) does: the angular row is scaled by \(d\theta_{usys}/d\theta_{rad}\), which is 1 for radians and \(180/\pi\) for degrees. A Quantity point goes to the overload below instead, which labels the row rad / length and needs no such scaling.
>>> import coordinax.charts as cxc >>> import unxt as u
>>> x = jnp.array([1.0, 1.0]) >>> cxc.jac_pt_map(cxc.cart2d, cxc.polar2d, usys=u.unitsystems.si)(x) Array([[ 0.70710678, 0.70710678], [-0.5 , 0.5 ]], dtype=float64)
The same point under a degree system: the radial row is unchanged, the angular row is the same derivative expressed per degree.
>>> degrees = u.unitsystem("m", "deg", "kg", "s") >>> cxc.jac_pt_map(cxc.cart2d, cxc.polar2d, usys=degrees)(x) Array([[ 0.70710678, 0.70710678], [-28.64788976, 28.64788976]], dtype=float64)
- coordinax.jac_pt_map(at: AbstractQuantity, from_chart: Cart2D, to_chart: Polar2D, /, *, usys: AbstractUnitSystem | None = None) unxts.linalg._src._quantity_matrix.QuantityMatrix
Compute the Jacobian of the transition function between two charts.
\[J = \frac{\partial(r,\theta)}{\partial(x,y)} = ( x/r & y/r \ -y/r^2 & x/r^2 ) = ( \cos\theta & \sin\theta \ -\sin\theta/r & \cos\theta/r )\]>>> import coordinax.charts as cxc >>> import unxt as u
>>> x = u.Q(jnp.array([1.0, 1.0]), "m") >>> cxc.jac_pt_map(cxc.cart2d, cxc.polar2d, usys=u.unitsystems.si)(x) QM([[ 0.70710678, 0.70710678], [-0.5 , 0.5 ]], '((, ), (rad / m, rad / m))')
- coordinax.jac_pt_map(at: Array, from_chart: Cart3D, to_chart: Cylindrical3D, /, *, usys: AbstractUnitSystem | None = None) Array
Compute the Jacobian of
Cart3D -> Cylindrical3D.\[\begin{split}J = \frac{\partial(\rho,\phi,z)}{\partial(x,y,z)} = \begin{pmatrix} x/\rho & y/\rho & 0 \\ -y/\rho^2 & x/\rho^2 & 0 \\ 0 & 0 & 1 \end{pmatrix}\end{split}\]>>> import coordinax.charts as cxc >>> import unxt as u
>>> at = jnp.array([1.0, 0.0, 3.0]) >>> cxc.jac_pt_map(at, cxc.cart3d, cxc.cyl3d, usys=u.unitsystems.si) Array([[ 1., 0., 0.], [-0., 1., 0.], [ 0., 0., 1.]], dtype=float64)
- coordinax.jac_pt_map(at: AbstractQuantity, from_chart: Cart3D, to_chart: Cylindrical3D, /, *, usys: AbstractUnitSystem | None = None) unxts.linalg._src._quantity_matrix.QuantityMatrix
Compute the Jacobian of
Cart3D -> Cylindrical3Dat a unitful point.>>> import coordinax.charts as cxc >>> import unxt as u
>>> at = u.Q(jnp.array([1.0, 0.0, 3.0]), "m") >>> cxc.jac_pt_map(at, cxc.cart3d, cxc.cyl3d).unit UnitsMatrix("((, , ), (rad / m, rad / m, rad / m), (, , ))")
- coordinax.jac_pt_map(at: Array, from_chart: Cylindrical3D, to_chart: Cart3D, /, *, usys: AbstractUnitSystem | None = None) Array
Compute the Jacobian of
Cylindrical3D -> Cart3D.\[\begin{split}J = \frac{\partial(x,y,z)}{\partial(\rho,\phi,z)} = \begin{pmatrix} \cos\phi & -\rho\sin\phi & 0 \\ \sin\phi & \rho\cos\phi & 0 \\ 0 & 0 & 1 \end{pmatrix}\end{split}\]>>> import coordinax.charts as cxc >>> import unxt as u
>>> at = jnp.array([2.0, 0.0, 3.0]) >>> cxc.jac_pt_map(at, cxc.cyl3d, cxc.cart3d, usys=u.unitsystems.si) Array([[ 1., -0., 0.], [ 0., 2., 0.], [ 0., 0., 1.]], dtype=float64)
- coordinax.jac_pt_map(at: Array, from_chart: Cart3D, to_chart: Spherical3D, /, *, usys: AbstractUnitSystem | None = None) Array
Compute the Jacobian of
Cart3D -> Spherical3D.\[\begin{split}J = \frac{\partial(r,\theta,\phi)}{\partial(x,y,z)} = \begin{pmatrix} x/r & y/r & z/r \\ xz/(r^2\rho) & yz/(r^2\rho) & -\rho/r^2 \\ -y/\rho^2 & x/\rho^2 & 0 \end{pmatrix}\end{split}\]with \(\rho=\sqrt{x^2+y^2}\) the cylindrical radius.
>>> import coordinax.charts as cxc >>> import unxt as u
>>> at = jnp.array([1.0, 0.0, 0.0]) >>> cxc.jac_pt_map(at, cxc.cart3d, cxc.sph3d, usys=u.unitsystems.si) Array([[ 1., 0., 0.], [ 0., 0., -1.], [-0., 1., 0.]], dtype=float64)
- coordinax.jac_pt_map(at: AbstractQuantity, from_chart: Cart3D, to_chart: Spherical3D, /, *, usys: AbstractUnitSystem | None = None) unxts.linalg._src._quantity_matrix.QuantityMatrix
Compute the Jacobian of
Cart3D -> Spherical3Dat a unitful point.>>> import coordinax.charts as cxc >>> import unxt as u
>>> at = u.Q(jnp.array([1.0, 0.0, 0.0]), "m") >>> cxc.jac_pt_map(at, cxc.cart3d, cxc.sph3d).value Array([[ 1., 0., 0.], [ 0., 0., -1.], [-0., 1., 0.]], dtype=float64)
- coordinax.jac_pt_map(at: Array, from_chart: Spherical3D, to_chart: Cart3D, /, *, usys: AbstractUnitSystem | None = None) Array
Compute the Jacobian of
Spherical3D -> Cart3D.\[\begin{split}J = \frac{\partial(x,y,z)}{\partial(r,\theta,\phi)} = \begin{pmatrix} s_\theta c_\phi & r c_\theta c_\phi & -r s_\theta s_\phi \\ s_\theta s_\phi & r c_\theta s_\phi & r s_\theta c_\phi \\ c_\theta & -r s_\theta & 0 \end{pmatrix}\end{split}\]>>> import coordinax.charts as cxc >>> import unxt as u
>>> at = jnp.array([1.0, 0.0, 0.0]) >>> cxc.jac_pt_map(at, cxc.sph3d, cxc.cart3d, usys=u.unitsystems.si) Array([[ 0., 1., -0.], [ 0., 0., 0.], [ 1., -0., 0.]], dtype=float64)
- coordinax.jac_pt_map(at: Array, from_chart: LonLatSpherical3D, to_chart: LonCosLatSpherical3D, /, *, usys: AbstractUnitSystem | None = None) Array
Compute the Jacobian of
LonLatSpherical3D -> LonCosLatSpherical3D.\[\begin{split}J = \frac{\partial(\lambda\cos\phi, \phi, d)}{\partial(\lambda, \phi, d)} = \begin{pmatrix} \cos\phi & -\lambda\sin\phi & 0 \\ 0 & 1 & 0 \\ 0 & 0 & 1 \end{pmatrix}\end{split}\]Both angles are converted to radians for the trigonometry. The entries then need no further scaling: each is an angle per angle, so the caller’s unit cancels between numerator and denominator.
>>> import coordinax.charts as cxc >>> import unxt as u >>> import jax.numpy as jnp
>>> at = jnp.array([0.0, 0.0, 2.0]) >>> cxc.jac_pt_map(at, cxc.lonlat_sph3d, cxc.loncoslat_sph3d, ... usys=u.unitsystems.si) Array([[ 1., -0., 0.], [ 0., 1., 0.], [ 0., 0., 1.]], dtype=float64)
- coordinax.jac_pt_map(at: Array, from_chart: LonCosLatSpherical3D, to_chart: LonLatSpherical3D, /, *, usys: AbstractUnitSystem | None = None) Array
Compute the Jacobian of
LonCosLatSpherical3D -> LonLatSpherical3D.\[\begin{split}J = \frac{\partial(\lambda, \phi, d)}{\partial(\lambda\cos\phi, \phi, d)} = \begin{pmatrix} 1/\cos\phi & \lambda\cos\phi\,\sin\phi/\cos^2\phi & 0 \\ 0 & 1 & 0 \\ 0 & 0 & 1 \end{pmatrix}\end{split}\]Singular at the poles, where cos(lat) vanishes and longitude is not recoverable from lon * cos(lat) – the same place the map itself is.
>>> import coordinax.charts as cxc >>> import unxt as u >>> import jax.numpy as jnp
>>> at = jnp.array([0.0, 0.0, 2.0]) >>> cxc.jac_pt_map(at, cxc.loncoslat_sph3d, cxc.lonlat_sph3d, ... usys=u.unitsystems.si) Array([[1., 0., 0.], [0., 1., 0.], [0., 0., 1.]], dtype=float64)
- coordinax.jac_pt_map(at: Array, from_chart: Cart3D, to_chart: LonLatSpherical3D, /, *, usys: AbstractUnitSystem | None = None) Array
Compute the Jacobian of
Cart3D -> LonLatSpherical3D.\[\begin{split}J = \frac{\partial(\lambda, \phi, d)}{\partial(x,y,z)} = \begin{pmatrix} -y/\rho^2 & x/\rho^2 & 0 \\ -xz/(r^2\rho) & -yz/(r^2\rho) & \rho/r^2 \\ x/r & y/r & z/r \end{pmatrix}\end{split}\]The latitude row is the colatitude’s negated, lat increasing towards +z where theta decreases.
>>> import coordinax.charts as cxc >>> import unxt as u >>> import jax.numpy as jnp
>>> at = jnp.array([1.0, 0.0, 0.0]) >>> cxc.jac_pt_map(at, cxc.cart3d, cxc.lonlat_sph3d, usys=u.unitsystems.si) Array([[-0., 1., 0.], [-0., -0., 1.], [ 1., 0., 0.]], dtype=float64)
- coordinax.jac_pt_map(at: Array, from_chart: LonLatSpherical3D, to_chart: Cart3D, /, *, usys: AbstractUnitSystem | None = None) Array
Compute the Jacobian of
LonLatSpherical3D -> Cart3D.\[\begin{split}J = \frac{\partial(x,y,z)}{\partial(\lambda, \phi, d)} = \begin{pmatrix} -d\cos\phi\sin\lambda & -d\sin\phi\cos\lambda & \cos\phi\cos\lambda \\ d\cos\phi\cos\lambda & -d\sin\phi\sin\lambda & \cos\phi\sin\lambda \\ 0 & d\cos\phi & \sin\phi \end{pmatrix}\end{split}\]>>> import coordinax.charts as cxc >>> import unxt as u >>> import jax.numpy as jnp
>>> at = jnp.array([0.0, 0.0, 2.0]) >>> cxc.jac_pt_map(at, cxc.lonlat_sph3d, cxc.cart3d, usys=u.unitsystems.si) Array([[-0., -0., 1.], [ 2., -0., 0.], [ 0., 2., 0.]], dtype=float64)
- coordinax.jac_pt_map(at: Array, from_chart: Cylindrical3D, to_chart: Spherical3D, /, *, usys: AbstractUnitSystem | None = None) Array
Compute the Jacobian of
Cylindrical3D -> Spherical3D.\[\begin{split}J = \frac{\partial(r,\theta,\phi)}{\partial(\rho,\phi,z)} = \begin{pmatrix} \rho/r & 0 & z/r \\ z/r^2 & 0 & -\rho/r^2 \\ 0 & 1 & 0 \end{pmatrix}\end{split}\]>>> import coordinax.charts as cxc >>> import unxt as u >>> import jax.numpy as jnp
>>> at = jnp.array([1.0, 0.0, 0.0]) >>> cxc.jac_pt_map(at, cxc.cyl3d, cxc.sph3d, usys=u.unitsystems.si) Array([[ 1., 0., 0.], [ 0., 0., -1.], [ 0., 1., 0.]], dtype=float64)
- coordinax.jac_pt_map(at: Array, from_chart: Spherical3D, to_chart: Cylindrical3D, /, *, usys: AbstractUnitSystem | None = None) Array
Compute the Jacobian of
Spherical3D -> Cylindrical3D.\[\begin{split}J = \frac{\partial(\rho,\phi,z)}{\partial(r,\theta,\phi)} = \begin{pmatrix} \sin\theta & r\cos\theta & 0 \\ 0 & 0 & 1 \\ \cos\theta & -r\sin\theta & 0 \end{pmatrix}\end{split}\]>>> import coordinax.charts as cxc >>> import unxt as u >>> import jax.numpy as jnp
>>> at = jnp.array([2.0, 0.0, 0.0]) >>> cxc.jac_pt_map(at, cxc.sph3d, cxc.cyl3d, usys=u.unitsystems.si) Array([[ 0., 2., 0.], [ 0., 0., 1.], [ 1., -0., 0.]], dtype=float64)
- coordinax.jac_pt_map(at: Array, from_chart: Cart3D, to_chart: LonCosLatSpherical3D, /, *, usys: AbstractUnitSystem | None = None) Array
Compute the Jacobian of
Cart3D -> LonCosLatSpherical3D.>>> import coordinax.charts as cxc >>> import unxt as u >>> import jax.numpy as jnp
>>> at = jnp.array([1.0, 0.0, 0.0]) >>> cxc.jac_pt_map(at, cxc.cart3d, cxc.loncoslat_sph3d, usys=u.unitsystems.si) Array([[0., 1., 0.], [0., 0., 1.], [1., 0., 0.]], dtype=float64)
- coordinax.jac_pt_map(at: Array, from_chart: LonCosLatSpherical3D, to_chart: Cart3D, /, *, usys: AbstractUnitSystem | None = None) Array
Compute the Jacobian of
LonCosLatSpherical3D -> Cart3D.>>> import coordinax.charts as cxc >>> import unxt as u >>> import jax.numpy as jnp
>>> at = jnp.array([0.0, 0.0, 2.0]) >>> cxc.jac_pt_map(at, cxc.loncoslat_sph3d, cxc.cart3d, usys=u.unitsystems.si) Array([[0., 0., 1.], [2., 0., 0.], [0., 2., 0.]], dtype=float64)
- coordinax.jac_pt_map(at: Array, from_chart: Spherical3D, to_chart: LonLatSpherical3D, /, *, usys: AbstractUnitSystem | None = None) Array
Compute the Jacobian of
Spherical3D -> LonLatSpherical3D.>>> import coordinax.charts as cxc >>> import unxt as u >>> import jax.numpy as jnp
>>> at = jnp.array([3.0, 0.6, 1.1]) >>> cxc.jac_pt_map(at, cxc.sph3d, cxc.lonlat_sph3d, usys=u.unitsystems.si) Array([[ 0., 0., 1.], [ 0., -1., 0.], [ 1., 0., 0.]], dtype=float64)
- coordinax.jac_pt_map(at: Array, from_chart: LonLatSpherical3D, to_chart: Spherical3D, /, *, usys: AbstractUnitSystem | None = None) Array
Compute the Jacobian of
LonLatSpherical3D -> Spherical3D.The same matrix as the forward direction: the relabel is an involution.
>>> import coordinax.charts as cxc >>> import unxt as u >>> import jax.numpy as jnp
>>> at = jnp.array([0.9, 0.35, 2.5]) >>> cxc.jac_pt_map(at, cxc.lonlat_sph3d, cxc.sph3d, usys=u.unitsystems.si) Array([[ 0., 0., 1.], [ 0., -1., 0.], [ 1., 0., 0.]], dtype=float64)
- coordinax.jac_pt_map(at: Array, from_chart: Cylindrical3D, to_chart: LonLatSpherical3D, /, *, usys: AbstractUnitSystem | None = None) Array
Compute the Jacobian of
Cylindrical3D -> LonLatSpherical3D.>>> import coordinax.charts as cxc >>> import unxt as u >>> import jax.numpy as jnp
>>> at = jnp.array([2.0, 0.7, 3.0]) >>> J = cxc.jac_pt_map(at, cxc.cyl3d, cxc.lonlat_sph3d, usys=u.unitsystems.si) >>> J.shape (3, 3)
- coordinax.jac_pt_map(at: Array, from_chart: LonLatSpherical3D, to_chart: Cylindrical3D, /, *, usys: AbstractUnitSystem | None = None) Array
Compute the Jacobian of
LonLatSpherical3D -> Cylindrical3D.>>> import coordinax.charts as cxc >>> import unxt as u >>> import jax.numpy as jnp
>>> at = jnp.array([0.9, 0.35, 2.5]) >>> J = cxc.jac_pt_map(at, cxc.lonlat_sph3d, cxc.cyl3d, usys=u.unitsystems.si) >>> J.shape (3, 3)
- coordinax.jac_pt_map(at: Array, from_chart: Spherical3D, to_chart: LonCosLatSpherical3D, /, *, usys: AbstractUnitSystem | None = None) Array
Compute the Jacobian of
Spherical3D -> LonCosLatSpherical3D.>>> import coordinax.charts as cxc >>> import unxt as u >>> import jax.numpy as jnp
>>> at = jnp.array([3.0, 0.6, 1.1]) >>> J = cxc.jac_pt_map(at, cxc.sph3d, cxc.loncoslat_sph3d, usys=u.unitsystems.si) >>> J.shape (3, 3)
- coordinax.jac_pt_map(at: Array, from_chart: LonCosLatSpherical3D, to_chart: Spherical3D, /, *, usys: AbstractUnitSystem | None = None) Array
Compute the Jacobian of
LonCosLatSpherical3D -> Spherical3D.>>> import coordinax.charts as cxc >>> import unxt as u >>> import jax.numpy as jnp
>>> at = jnp.array([0.8, 0.35, 2.5]) >>> J = cxc.jac_pt_map(at, cxc.loncoslat_sph3d, cxc.sph3d, usys=u.unitsystems.si) >>> J.shape (3, 3)
- coordinax.jac_pt_map(at: Array, from_chart: Cylindrical3D, to_chart: LonCosLatSpherical3D, /, *, usys: AbstractUnitSystem | None = None) Array
Compute the Jacobian of
Cylindrical3D -> LonCosLatSpherical3D.>>> import coordinax.charts as cxc >>> import unxt as u >>> import jax.numpy as jnp
>>> at = jnp.array([2.0, 0.7, 3.0]) >>> J = cxc.jac_pt_map(at, cxc.cyl3d, cxc.loncoslat_sph3d, usys=u.unitsystems.si) >>> J.shape (3, 3)
- coordinax.jac_pt_map(at: Array, from_chart: LonCosLatSpherical3D, to_chart: Cylindrical3D, /, *, usys: AbstractUnitSystem | None = None) Array
Compute the Jacobian of
LonCosLatSpherical3D -> Cylindrical3D.>>> import coordinax.charts as cxc >>> import unxt as u >>> import jax.numpy as jnp
>>> at = jnp.array([0.8, 0.35, 2.5]) >>> J = cxc.jac_pt_map(at, cxc.loncoslat_sph3d, cxc.cyl3d, usys=u.unitsystems.si) >>> J.shape (3, 3)
- final class coordinax.EuclideanManifold(ndim: int, /)
Bases:
AbstractManifoldThe \(n\)-dimensional Euclidean manifold \(\mathbb{R}^n\).
The Euclidean manifold of dimension \(n\) is the smooth manifold \((\mathbb{R}^n, \mathcal{A}_{\mathbb{R}^n})\).
Charts and atlas. The smooth structure is described by an {class}`coordinax.manifolds.EuclideanAtlas` whose charts are local diffeomorphisms
\[\varphi : U \subset \mathbb{R}^n \to \mathbb{R}^n.\]A chart \(C = (U, \varphi)\) is admitted by the atlas when its dimensionality matches \(n\) and either (1) it is explicitly registered with {class}`coordinax.manifolds.EuclideanAtlas`, or (2) it possesses a compatible transition map to the default Cartesian chart. For \(n = 3\) the built-in charts include Cartesian \((x, y, z)\), spherical \((r, \theta, \phi)\), cylindrical \((\rho, \phi, z)\), and several angular variants; see {class}`coordinax.manifolds.EuclideanAtlas` for the full list.
Transition maps. For any two charts \(C_\alpha = (U_\alpha, \varphi_\alpha)\) and \(C_\beta = (U_\beta, \varphi_\beta)\) in the atlas, the transition map is
\[\tau_{\alpha \to \beta} = \varphi_\beta \circ \varphi_\alpha^{-1} : \varphi_\alpha(U_\alpha \cap U_\beta) \to \varphi_\beta(U_\alpha \cap U_\beta).\]Because \(\mathbb{R}^n\) is flat and simply connected, every transition map is a smooth diffeomorphism on a connected open domain. For example, the Cartesian-to-spherical transition on \(\mathbb{R}^3\) is
\[\tau_{C \to S}(x, y, z) = \Bigl(\sqrt{x^2 + y^2 + z^2},\; \arccos\!\tfrac{z}{r},\; \operatorname{atan2}(y, x)\Bigr).\]Pre-built instance. The module exports {obj}`coordinax.manifolds.R3` as a pre-built instance for the common case \(\mathbb{R}^3\).
- Parameters:
ndim (
int) – Intrinsic dimension \(n \geq 0\) of the manifold — the number of independent coordinates required to label a point.- Variables:
atlas (EuclideanAtlas) – The atlas of coordinate charts compatible with this manifold. Its {attr}`~EuclideanAtlas.ndim` equals
ndim.
Examples
Construction
Construct a Euclidean manifold of arbitrary dimension:
>>> import coordinax.manifolds as cxmd >>> M = cxmd.EuclideanManifold(3) >>> M Rn(3)
The intrinsic dimension is accessible via {attr}`~EuclideanManifold.ndim`:
>>> M.ndim 3
The atlas is a {class}`EuclideanAtlas` with matching dimensionality:
>>> M.atlas EuclideanAtlas(ndim=3)
Default chart
The default chart is the standard Cartesian chart for the given dimension:
>>> M.default_chart() Cart3D(M=Rn(3))
>>> cxmd.EuclideanManifold(2).default_chart() Cart2D(M=Rn(2))
>>> cxmd.EuclideanManifold(1).default_chart() Cart1D(M=Rn(1))
Chart membership
Check whether a chart belongs to this manifold’s atlas:
>>> import coordinax.charts as cxc
>>> M.has_chart(cxc.cart3d) True
>>> M.has_chart(cxc.sph3d) True
Charts with the wrong dimensionality are rejected:
>>> M.has_chart(cxc.cart2d) False
{meth}`check_chart` raises if the chart is not supported:
>>> try: ... M.check_chart(cxc.cart2d) ... except ValueError as e: ... print(e) Chart Cart2D(M=Rn(2)) is not supported by this manifold atlas.
Pre-built instances
For the most common case — three-dimensional Euclidean space \(\mathbb{R}^3\) — the module provides a pre-built instance:
>>> cxmd.R3 Rn(3)
- ndim: int
Intrinsic dimension of the manifold.
- angle_between(chart: AbstractChart[Any, Any, Any], uvec: dict, vvec: dict, /, *, at: dict, usys: AbstractUnitSystem | None = None)
Return the metric angle between two tangent vectors at
at.This is a thin convenience wrapper over
cxmapi.angle_between(self.metric, chart, uvec, vvec, at=at, usys=usys).- Parameters:
chart (
AbstractChart[Any,Any,Any])uvec (
dict)vvec (
dict)at (
dict)usys (
AbstractUnitSystem|None)
- Return type:
- check_chart(chart: AbstractChart[Any, Any, Any], /)
Check that
chartbelongs to this manifold atlas.>>> import coordinax.manifolds as cxm >>> M = cxm.Rn(2) >>> M.check_chart(cxc.cart2d) # does not raise
- Parameters:
chart (
AbstractChart[Any,Any,Any])- Return type:
- default_chart()
Return a default chart from the atlas.
This is a convenience property that proxies to the atlas default chart.
- Return type:
AbstractChart[Any,Any,Any]
>>> import coordinax.manifolds as cxm >>> M = cxm.Rn(2) >>> M.default_chart() Cart2D(M=Rn(2))
- has_chart(chart: AbstractChart[Any, Any, Any], /)
Return whether
chartbelongs to this manifold atlas.>>> import coordinax.manifolds as cxm >>> M = cxm.Rn(2) >>> M.has_chart(cxc.cart2d) True >>> M.has_chart(cxc.cart3d) False
- Parameters:
chart (
AbstractChart[Any,Any,Any])- Return type:
- norm(v: Any, *args: Any, at: Any, usys: AbstractUnitSystem | None = None, **kwargs: Any)
Compute the norm \(\|v\|_g = \sqrt{g(v, v)}\).
Convenience wrapper that calls
cxmapi.norm(v, self.metric, chart, at=at, usys=usys)directly. Thechartmust be passed as the second positional argument (afterv).Examples
>>> import jax.numpy as jnp >>> import unxt as u >>> import coordinax.charts as cxc >>> import coordinax.manifolds as cxm
>>> M = cxm.EuclideanManifold(3) >>> chart = cxc.Cart3D(M=M) >>> at = {"x": jnp.array(0.0), "y": jnp.array(0.0), "z": jnp.array(0.0)}
With a stacked
jax.Array(usys required):>>> usys = u.unitsystems.si >>> v = jnp.array([3.0, 4.0, 0.0]) >>> M.norm(v, chart, at=at, usys=usys) Array(5., dtype=float64)
With a CDict of quantities (usys optional):
>>> v = {"x": u.Q(3.0, "m/s"), "y": u.Q(4.0, "m/s"), "z": u.Q(0.0, "m/s")} >>> M.norm(v, chart, at=at) Q(5., 'm / s')
- atlas: AbstractAtlas
Charts compatible with this manifold. This defines the smooth structure.
- metric: AbstractMetricField
The manifold’s metric. This defines the geometric structure.
- coordinax.Rn
alias of
EuclideanManifold
- final class coordinax.FlatMetric(ndim: int = <property object>)
Bases:
AbstractDiagonalMetricFieldEuclidean (flat) Riemannian metric on \(\mathbb{R}^n\).
In Cartesian coordinates the metric is the identity matrix \(g = I_n\). In any other chart, the metric matrix is computed via the pullback
\[g_{ij} = \sum_k \frac{\partial x^k}{\partial q^i} \frac{\partial x^k}{\partial q^j} = (J^T J)_{ij},\]where \(J = \partial x / \partial q\) is the Jacobian of the chart-to-Cartesian transition map.
This pullback is diagonal precisely for orthogonal coordinate charts. FlatMetric is treated as
AbstractDiagonalMetricFieldon that orthogonal chart domain; atlas chart compatibility alone does not imply orthogonality.- Parameters:
ndim (
int) – Dimension of the Euclidean space.
Examples
>>> import jax.numpy as jnp >>> import coordinaxs.api.manifolds as cxmapi >>> import coordinax.charts as cxc >>> import coordinax.manifolds as cxm
>>> m = cxm.FlatMetric(3) >>> m.signature (1, 1, 1) >>> m.ndim 3
The metric matrix is obtained via the dispatch API on the associated manifold:
>>> at = {"x": jnp.array(0.0), "y": jnp.array(0.0), "z": jnp.array(0.0)} >>> cxmapi.metric_matrix(cxm.R3, at, cxc.cart3d).diagonal Array([1., 1., 1.], dtype=float64)
- ndim: int
Dimension of the Euclidean space.
- norm(v: Any, *args: Any, at: Any, usys: AbstractUnitSystem | None = None, **kwargs: Any)
Compute the norm \(\|v\|_g = \sqrt{g(v, v)}\).
Convenience wrapper that calls
cxmapi.norm(v, self, chart, at=at, usys=usys)directly. Thechartmust be passed as the second positional argument (afterv).Examples
>>> import jax.numpy as jnp >>> import unxt as u >>> import coordinax.charts as cxc >>> import coordinax.manifolds as cxm
>>> metric = cxm.FlatMetric(3) >>> at = {"x": jnp.array(0.0), "y": jnp.array(0.0), "z": jnp.array(0.0)}
With a CDict of quantities (usys optional):
>>> v = {"x": u.Q(3.0, "m/s"), "y": u.Q(4.0, "m/s"), "z": u.Q(0.0, "m/s")} >>> metric.norm(v, cxc.cart3d, at=at) Q(5., 'm / s')
With a stacked
jax.Array(usys required):>>> v = jnp.array([3.0, 4.0, 0.0]) >>> metric.norm(v, cxc.cart3d, at=at, usys=u.unitsystems.si) Array(5., dtype=float64)
- coordinax.embedded_twosphere(radius: float | AbstractQuantity, ambient: AbstractChart[Any, Any, Any] = Spherical3D(M=Rn(3)))
Create an coordinax.manifolds.EmbeddedManifold for the two-sphere.
This is a convenience helper that constructs an coordinax.manifolds.EmbeddedManifold with
intrinsic=HyperSphericalManifold()andembedding=TwoSphereIn3D(radius, ambient).- Parameters:
radius (
float|AbstractQuantity) – Sphere radius.ambient (
AbstractChart[Any,Any,Any]) – Ambient chart for the embedding. Defaults to coordinax.charts.Spherical3D.
- Return type:
Examples
>>> import jax.numpy as jnp >>> import coordinax.charts as cxc >>> import coordinax.manifolds as cxm >>> import unxt as u
Default ambient (Spherical3D):
>>> M = cxm.embedded_twosphere(radius=u.Q(2.0, "km")) >>> M EmbeddedManifold(intrinsic=HyperSphericalManifold(...), ambient=Rn(3), embed_map=TwoSphereIn3D(radius=Q(2., 'km'), ambient=Spherical3D(M=Rn(3))))
>>> p = {"theta": u.Angle(jnp.pi / 2, "rad"), "phi": u.Angle(0.0, "rad")} >>> sph = cxm.pt_embed(p, M) >>> sph {'r': Q(2., 'km'), 'theta': Angle(1.57079633, 'rad'), 'phi': Angle(0., 'rad')}
With Cartesian ambient the embedding returns
(x, y, z):>>> M = cxm.embedded_twosphere(radius=u.Q(2.0, "km"), ambient=cxc.cart3d) >>> xyz = cxm.pt_embed(p, M) >>> sorted(xyz) ['x', 'y', 'z'] >>> bool(jnp.allclose(u.ustrip("km", xyz["x"]), 2.0, atol=1e-6)) True
- final class coordinax.EmbeddedChart(embed_map: AbstractEmbeddingMap[IntrinsicT, AmbientT])
Bases:
AbstractParameterizedChart[EmbeddedManifold,Ks,Ds],Generic[IntrinsicT,AmbientT,Ks,Ds]Chart for intrinsic coordinates on an embedding manifold.
This is a convenience wrapper that combines an intrinsic chart with an embedding to an ambient Cartesian chart. It provides the same component and dimension information as the intrinsic chart, but also provides a realization map to Cartesian coordinates via the embedding.
The more correct way to represent an embedding manifold is with {class}`~coordinax.manifolds.EmbeddedManifold`.
Examples
Embed/project {class}`~coordinax.charts.SphericalTwoSphere through an ambient {class}`~coordinax.charts.Spherical3D chart:
>>> import jax.numpy as jnp >>> import coordinax.charts as cxc >>> import coordinax.manifolds as cxm >>> import unxt as u
>>> chart = cxm.EmbeddedChart(cxm.TwoSphereIn3D(radius=u.Q(2.0, "km"))) >>> p = {"theta": u.Angle(jnp.pi / 2, "rad"), "phi": u.Angle(0.0, "rad")} >>> sph = cxm.pt_embed(p, chart) >>> sph {'r': Q(2., 'km'), 'theta': Angle(1.57079633, 'rad'), 'phi': Angle(0., 'rad')}
>>> p2 = cxm.pt_project(sph, chart) >>> p2 {'theta': Angle(1.57079633, 'rad'), 'phi': Angle(0., 'rad')} >>> jnp.allclose(p2["theta"].value, p["theta"].value) Array(True, dtype=bool)
- Parameters:
embed_map (
AbstractEmbeddingMap[TypeVar(IntrinsicT, bound=AbstractChart[Any,Any,Any]),TypeVar(AmbientT, bound=AbstractChart[Any,Any,Any])])
- embed_map: AbstractEmbeddingMap[IntrinsicT, AmbientT]
The embedding that defines the map to the ambient chart.
This is the core data of the EmbeddedChart, as it defines the ambient chart and the embedding parameters (e.g., radius for a sphere). The intrinsic chart is determined by the embedding’s
intrinsicproperty, and the ambient chart is determined by the embedding’sambientproperty.>>> import coordinax.manifolds as cxm >>> import unxt as u >>> chart = cxm.EmbeddedChart(cxm.TwoSphereIn3D(radius=u.Q(2.0, "km"))) >>> chart.embed_map TwoSphereIn3D(radius=Q(2., 'km'), ambient=Spherical3D(M=Rn(3)))
- property M: EmbeddedManifold
The manifold associated with this chart.
This is an EmbeddedManifold that combines the intrinsic and ambient manifolds defined by the embedding map.
>>> import coordinax.manifolds as cxm >>> import unxt as u >>> chart = cxm.EmbeddedChart(cxm.TwoSphereIn3D(radius=u.Q(2.0, "km"))) >>> chart.M EmbeddedManifold(intrinsic=HyperSphericalManifold(ndim=2), ambient=Rn(3), embed_map=TwoSphereIn3D(radius=Q(2., 'km'), ambient=Spherical3D(M=Rn(3))))
- property intrinsic: IntrinsicT
The intrinsic chart.
>>> import coordinax.manifolds as cxm >>> import unxt as u >>> chart = cxm.EmbeddedChart(cxm.TwoSphereIn3D(radius=u.Q(2.0, "km"))) >>> chart.intrinsic SphericalTwoSphere(M=Sn(2))
- property ambient: AmbientT
The ambient chart.
>>> import coordinax.manifolds as cxm >>> import unxt as u >>> chart = cxm.EmbeddedChart(cxm.TwoSphereIn3D(radius=u.Q(2.0, "km"))) >>> chart.ambient Spherical3D(M=Rn(3))
- property components: Ks
Return the components of the intrinsic chart.
>>> import coordinax.manifolds as cxm >>> import unxt as u >>> chart = cxm.EmbeddedChart(cxm.TwoSphereIn3D(radius=u.Q(2.0, "km"))) >>> chart.components ('theta', 'phi')
- property coord_dimensions: Ds
Return the coordinate dimensions of the intrinsic chart.
>>> import coordinax.manifolds as cxm >>> import unxt as u >>> chart = cxm.EmbeddedChart(cxm.TwoSphereIn3D(radius=u.Q(2.0, "km"))) >>> chart.coord_dimensions ('angle', 'angle')
- property cartesian: AbstractChart
The ambient Cartesian chart for the embedding.
>>> import coordinax.manifolds as cxm >>> import unxt as u >>> chart = cxm.EmbeddedChart(cxm.TwoSphereIn3D(radius=u.Q(2.0, "km"))) >>> chart.cartesian Cart3D(M=Rn(3))
- check_data(data: CDictT, /, *, keys: bool = True, values: bool = False)
Check that the data is compatible with the chart.
- Parameters:
- Return type:
- property ndim: int
Number of coordinate components (chart dimension).
- final class coordinax.EmbeddedManifold(intrinsic: AbstractManifold, ambient: AbstractManifold, embed_map: AbstractEmbeddingMap[IntrinsicT, AmbientT])
Bases:
AbstractManifold,Generic[IntrinsicT,AmbientT]Embedded manifold.
Examples
Embed/project {class}`~coordinax.charts.SphericalTwoSphere through an ambient {class}`~coordinax.charts.Spherical3D chart:
>>> import jax.numpy as jnp >>> import coordinax.charts as cxc >>> import coordinax.manifolds as cxm >>> import unxt as u
>>> M = cxm.EmbeddedManifold( ... intrinsic=cxm.S2, ... ambient=cxm.R3, ... embed_map=cxm.TwoSphereIn3D(radius=u.Q(2.0, "km"))) >>> p = {"theta": u.Angle(jnp.pi / 2, "rad"), "phi": u.Angle(0.0, "rad")} >>> sph = cxm.pt_embed(p, M) >>> sph {'r': Q(2., 'km'), 'theta': Angle(1.57079633, 'rad'), 'phi': Angle(0., 'rad')}
>>> p2 = cxm.pt_project(sph, M) >>> p2 {'theta': Angle(1.57079633, 'rad'), 'phi': Angle(0., 'rad')} >>> jnp.allclose(p2["theta"].value, p["theta"].value) Array(True, dtype=bool)
- Parameters:
intrinsic (
AbstractManifold)ambient (
AbstractManifold)embed_map (
AbstractEmbeddingMap[TypeVar(IntrinsicT, bound=AbstractChart[Any,Any,Any]),TypeVar(AmbientT, bound=AbstractChart[Any,Any,Any])])
- intrinsic: AbstractManifold
- ambient: AbstractManifold
- embed_map: AbstractEmbeddingMap[IntrinsicT, AmbientT]
- embed(intrinsic_point: dict, from_intrinsic_chart: AbstractChart[Any, Any, Any], to_ambient_chart: AbstractChart[Any, Any, Any], /, *, usys: AbstractUnitSystem | None = None)
- Parameters:
intrinsic_point (
dict)from_intrinsic_chart (
AbstractChart[Any,Any,Any])to_ambient_chart (
AbstractChart[Any,Any,Any])usys (
AbstractUnitSystem|None)
- Return type:
- project(ambient_point: dict, from_ambient_chart: AbstractChart[Any, Any, Any], to_intrinsic_chart: AbstractChart[Any, Any, Any], /, *, usys: AbstractUnitSystem | None = None)
- Parameters:
ambient_point (
dict)from_ambient_chart (
AbstractChart[Any,Any,Any])to_intrinsic_chart (
AbstractChart[Any,Any,Any])usys (
AbstractUnitSystem|None)
- Return type:
- property metric: PullbackMetric
Induced (pullback) Riemannian metric from the ambient manifold.
- property atlas: AbstractAtlas
- angle_between(chart: AbstractChart[Any, Any, Any], uvec: dict, vvec: dict, /, *, at: dict, usys: AbstractUnitSystem | None = None)
Return the metric angle between two tangent vectors at
at.This is a thin convenience wrapper over
cxmapi.angle_between(self.metric, chart, uvec, vvec, at=at, usys=usys).- Parameters:
chart (
AbstractChart[Any,Any,Any])uvec (
dict)vvec (
dict)at (
dict)usys (
AbstractUnitSystem|None)
- Return type:
- check_chart(chart: AbstractChart[Any, Any, Any], /)
Check that
chartbelongs to this manifold atlas.>>> import coordinax.manifolds as cxm >>> M = cxm.Rn(2) >>> M.check_chart(cxc.cart2d) # does not raise
- Parameters:
chart (
AbstractChart[Any,Any,Any])- Return type:
- default_chart()
Return a default chart from the atlas.
This is a convenience property that proxies to the atlas default chart.
- Return type:
AbstractChart[Any,Any,Any]
>>> import coordinax.manifolds as cxm >>> M = cxm.Rn(2) >>> M.default_chart() Cart2D(M=Rn(2))
- has_chart(chart: AbstractChart[Any, Any, Any], /)
Return whether
chartbelongs to this manifold atlas.>>> import coordinax.manifolds as cxm >>> M = cxm.Rn(2) >>> M.has_chart(cxc.cart2d) True >>> M.has_chart(cxc.cart3d) False
- Parameters:
chart (
AbstractChart[Any,Any,Any])- Return type:
- property ndim: int
Return the dimension of the manifold.
This is a convenience property that proxies to the atlas dimension, since the atlas defines the smooth structure of the manifold and therefore determines its dimension.
>>> import coordinax.manifolds as cxm >>> M = cxm.Rn(3) >>> M.ndim 3
- norm(v: Any, *args: Any, at: Any, usys: AbstractUnitSystem | None = None, **kwargs: Any)
Compute the norm \(\|v\|_g = \sqrt{g(v, v)}\).
Convenience wrapper that calls
cxmapi.norm(v, self.metric, chart, at=at, usys=usys)directly. Thechartmust be passed as the second positional argument (afterv).Examples
>>> import jax.numpy as jnp >>> import unxt as u >>> import coordinax.charts as cxc >>> import coordinax.manifolds as cxm
>>> M = cxm.EuclideanManifold(3) >>> chart = cxc.Cart3D(M=M) >>> at = {"x": jnp.array(0.0), "y": jnp.array(0.0), "z": jnp.array(0.0)}
With a stacked
jax.Array(usys required):>>> usys = u.unitsystems.si >>> v = jnp.array([3.0, 4.0, 0.0]) >>> M.norm(v, chart, at=at, usys=usys) Array(5., dtype=float64)
With a CDict of quantities (usys optional):
>>> v = {"x": u.Q(3.0, "m/s"), "y": u.Q(4.0, "m/s"), "z": u.Q(0.0, "m/s")} >>> M.norm(v, chart, at=at) Q(5., 'm / s')
- final class coordinax.CustomAtlas(charts: tuple[type[AbstractChart[Any, Any, Any]], ...], chart_default: AbstractChart[Any, Any, Any])
Bases:
AbstractAtlasAtlas of explicitly registered charts for a custom manifold.
CustomAtlasis an explicit atlas: chart membership is determined only by the set of chart classes provided at construction time.A chart belongs to the atlas iff:
Its class is in
charts.Its dimensionality matches the atlas
ndim.
The default chart must be one of the registered classes and defines the atlas dimension.
Examples
>>> import coordinax.charts as cxc >>> import coordinax.manifolds as cxm
>>> atlas = cxm.CustomAtlas( ... charts=(cxc.Cart2D, cxc.Polar2D), ... chart_default=cxc.cart2d, ... ) >>> atlas.ndim 2 >>> atlas.default_chart() Cart2D(M=Rn(2)) >>> atlas.has_chart(cxc.polar2d) True >>> atlas.has_chart(cxc.cart3d) False
- Parameters:
- charts: tuple[type[AbstractChart[Any, Any, Any]], ...]
Explicitly registered chart classes for this atlas.
- chart_default: AbstractChart[Any, Any, Any]
Stored default chart instance provided at construction.
- default_chart()
Return the default chart for this atlas.
- Return type:
AbstractChart[Any,Any,Any]
- property ndim: int
Intrinsic dimension of the manifold.
- has_chart(chart: AbstractChart[Any, Any, Any])
Return whether the atlas supports the given chart.
- Parameters:
chart (
AbstractChart[Any,Any,Any])- Return type:
- final class coordinax.CustomManifold(atlas: AbstractAtlas, metric: AbstractMetricField)
Bases:
AbstractManifoldSmooth manifold with a caller-defined explicit atlas.
CustomManifoldis a thin wrapper around {class}`CustomAtlas` and inherits all chart validation and transition wrappers from {class}`~coordinax.manifolds.AbstractManifold`.Examples
>>> import coordinax.charts as cxc >>> import coordinax.manifolds as cxm
>>> atlas = cxm.CustomAtlas( ... charts=(cxc.Cart2D, cxc.Polar2D), ... chart_default=cxc.cart2d, ... ) >>> M = cxm.CustomManifold(atlas=atlas, metric=cxm.FlatMetric(2)) >>> M.ndim 2 >>> M.default_chart() Cart2D(M=Rn(2)) >>> M.has_chart(cxc.polar2d) True
- Parameters:
atlas (
AbstractAtlas)metric (
AbstractMetricField)
- atlas: AbstractAtlas
Atlas defining chart compatibility for this manifold.
- angle_between(chart: AbstractChart[Any, Any, Any], uvec: dict, vvec: dict, /, *, at: dict, usys: AbstractUnitSystem | None = None)
Return the metric angle between two tangent vectors at
at.This is a thin convenience wrapper over
cxmapi.angle_between(self.metric, chart, uvec, vvec, at=at, usys=usys).- Parameters:
chart (
AbstractChart[Any,Any,Any])uvec (
dict)vvec (
dict)at (
dict)usys (
AbstractUnitSystem|None)
- Return type:
- check_chart(chart: AbstractChart[Any, Any, Any], /)
Check that
chartbelongs to this manifold atlas.>>> import coordinax.manifolds as cxm >>> M = cxm.Rn(2) >>> M.check_chart(cxc.cart2d) # does not raise
- Parameters:
chart (
AbstractChart[Any,Any,Any])- Return type:
- default_chart()
Return a default chart from the atlas.
This is a convenience property that proxies to the atlas default chart.
- Return type:
AbstractChart[Any,Any,Any]
>>> import coordinax.manifolds as cxm >>> M = cxm.Rn(2) >>> M.default_chart() Cart2D(M=Rn(2))
- has_chart(chart: AbstractChart[Any, Any, Any], /)
Return whether
chartbelongs to this manifold atlas.>>> import coordinax.manifolds as cxm >>> M = cxm.Rn(2) >>> M.has_chart(cxc.cart2d) True >>> M.has_chart(cxc.cart3d) False
- Parameters:
chart (
AbstractChart[Any,Any,Any])- Return type:
- property ndim: int
Return the dimension of the manifold.
This is a convenience property that proxies to the atlas dimension, since the atlas defines the smooth structure of the manifold and therefore determines its dimension.
>>> import coordinax.manifolds as cxm >>> M = cxm.Rn(3) >>> M.ndim 3
- norm(v: Any, *args: Any, at: Any, usys: AbstractUnitSystem | None = None, **kwargs: Any)
Compute the norm \(\|v\|_g = \sqrt{g(v, v)}\).
Convenience wrapper that calls
cxmapi.norm(v, self.metric, chart, at=at, usys=usys)directly. Thechartmust be passed as the second positional argument (afterv).Examples
>>> import jax.numpy as jnp >>> import unxt as u >>> import coordinax.charts as cxc >>> import coordinax.manifolds as cxm
>>> M = cxm.EuclideanManifold(3) >>> chart = cxc.Cart3D(M=M) >>> at = {"x": jnp.array(0.0), "y": jnp.array(0.0), "z": jnp.array(0.0)}
With a stacked
jax.Array(usys required):>>> usys = u.unitsystems.si >>> v = jnp.array([3.0, 4.0, 0.0]) >>> M.norm(v, chart, at=at, usys=usys) Array(5., dtype=float64)
With a CDict of quantities (usys optional):
>>> v = {"x": u.Q(3.0, "m/s"), "y": u.Q(4.0, "m/s"), "z": u.Q(0.0, "m/s")} >>> M.norm(v, chart, at=at) Q(5., 'm / s')
- metric: AbstractMetricField
Riemannian metric for this manifold, used for norm and distance computations.
- coordinax.chord_distance(*args, **kwargs)
Straight-line distance between two points through their ambient space.
The chord, as opposed to geodesic_distance’s path along the manifold. Defined wherever the manifold carries an embedding; a manifold that is its own ambient space has no distinct chord and is refused.
- coordinax.chord_distance(chart: AbstractChart, a: dict, b: dict, /, *, usys: AbstractUnitSystem | None = None) Any
Return the ambient straight-line distance, on the chart’s manifold.
>>> import jax.numpy as jnp >>> import unxt as u >>> import coordinax.charts as cxc >>> import coordinax.manifolds as cxm
A quarter turn along the equator of the unit sphere: the arc is
pi / 2, the chord through the interior issqrt(2).>>> a = {"theta": u.Angle(jnp.pi / 2, "rad"), "phi": u.Angle(0.0, "rad")} >>> b = {"theta": u.Angle(jnp.pi / 2, "rad"), "phi": u.Angle(jnp.pi / 2, "rad")} >>> round(float(cxm.chord_distance(cxc.sph2, a, b)), 6) 1.414214 >>> round(float(cxm.geodesic_distance(cxc.sph2, a, b).ustrip("rad")), 6) 1.570796
Antipodes are one diameter apart through the middle, half the great-circle distance around the outside:
>>> n = {"theta": u.Angle(0.0, "rad"), "phi": u.Angle(0.0, "rad")} >>> s = {"theta": u.Angle(jnp.pi, "rad"), "phi": u.Angle(0.0, "rad")} >>> round(float(cxm.chord_distance(cxc.sph2, n, s)), 6) 2.0
- coordinax.chord_distance(M: HyperSphericalManifold, chart: AbstractChart, a: dict, b: dict, /, *, usys: AbstractUnitSystem | None = None) Any
Return the chord of the unit hypersphere, through its canonical embedding.
>>> import jax.numpy as jnp >>> import unxt as u >>> import coordinax.charts as cxc >>> import coordinax.manifolds as cxm
Any chart on the sphere gives the same answer:
>>> a = {"lon": u.Angle(0.0, "rad"), "lat": u.Angle(0.0, "rad")} >>> b = {"lon": u.Angle(jnp.pi / 2, "rad"), "lat": u.Angle(0.0, "rad")} >>> round(float(cxm.chord_distance(cxc.lonlat_sph2, a, b)), 6) 1.414214
- coordinax.chord_distance(M: EmbeddedManifold, chart: AbstractChart, a: dict, b: dict, /, *, usys: AbstractUnitSystem | None = None) Any
Return the chord through the manifold’s own ambient space.
>>> import jax.numpy as jnp >>> import unxt as u >>> import coordinax.charts as cxc >>> import coordinax.manifolds as cxm
A sphere of radius 2 m: antipodes are one diameter apart.
>>> M = cxm.EmbeddedManifold( ... intrinsic=cxm.S2, ambient=cxm.R3, ... embed_map=cxm.TwoSphereIn3D(radius=u.Q(2.0, "m")), ... ) >>> n = {"theta": u.Angle(0.0, "rad"), "phi": u.Angle(0.0, "rad")} >>> s = {"theta": u.Angle(jnp.pi, "rad"), "phi": u.Angle(0.0, "rad")} >>> cxm.chord_distance(M, cxc.sph2, n, s).round(6) Distance(4., 'm')
- coordinax.chord_distance(M: EuclideanManifold, chart: AbstractChart, a: dict, b: dict, /, *, usys: AbstractUnitSystem | None = None) Any
Refuse: flat space is its own ambient, so the chord is the geodesic.
Returning the same number under a second name invites the reader to think two things were measured.
>>> import unxt as u >>> import coordinax.charts as cxc >>> import coordinax.manifolds as cxm
>>> a = {"x": u.Q(3.0, "m"), "y": u.Q(0.0, "m"), "z": u.Q(0.0, "m")} >>> b = {"x": u.Q(0.0, "m"), "y": u.Q(4.0, "m"), "z": u.Q(0.0, "m")} >>> try: cxm.chord_distance(cxc.cart3d, a, b) ... except NotImplementedError as e: print(e) chord_distance is a measurement through an ambient space, and Rn(3) is its own ambient -- its chord is the straight line, which is what `geodesic_distance` already returns.
- coordinax.chord_distance(M: AbstractManifold, chart: AbstractChart, a: dict, b: dict, /, *, usys: AbstractUnitSystem | None = None) Any
Refuse: without an embedding there is no ambient space to cut through.
Straight-line distance between two points, through their ambient space.
The counterpart to ~coordinax.geodesic_distance’s Point overload: that one measures along the manifold, this one through the space it is embedded in. The result is invariant to the chart each operand happens to use.
>>> import jax.numpy as jnp >>> import unxt as u >>> import coordinax as cx >>> import coordinax.charts as cxc
A quarter turn along the equator: the arc is
pi / 2, the chord through the interior issqrt(2).>>> p = cx.Point({"theta": u.Angle(jnp.pi / 2, "rad"), ... "phi": u.Angle(0.0, "rad")}, chart=cxc.sph2) >>> q = cx.Point({"theta": u.Angle(jnp.pi / 2, "rad"), ... "phi": u.Angle(jnp.pi / 2, "rad")}, chart=cxc.sph2) >>> round(float(cx.chord_distance(p, q)), 6) 1.414214
The operands need not share a chart:
>>> r = cx.Point({"lon": u.Angle(jnp.pi / 2, "rad"), ... "lat": u.Angle(0.0, "rad")}, chart=cxc.lonlat_sph2) >>> round(float(cx.chord_distance(p, r)), 6) 1.414214
Flat space is its own ambient, so it has no chord distinct from its geodesic:
>>> a = cx.Point.from_([3.0, 0.0, 0.0], "m") >>> b = cx.Point.from_([0.0, 4.0, 0.0], "m") >>> try: cx.chord_distance(a, b) ... except NotImplementedError as e: print(str(e)[:48]) chord_distance is a measurement through an ambie
- coordinax.act(*args, **kwargs)
Apply a transform action to coordinates.
This is the core dispatch function for transform application. Each transform type registers its own implementation via multiple dispatch. Transforms act on various input types (Array, Quantity, Vector, CDict) according to their semantics.
Mathematical Definition:
For a transform \(\mathcal{T}\) parameterized by \(\tau\), this computes:
\[x' = \mathcal{T}(\tau)(x)\]For tau-independent transforms, \(\tau\) is ignored. For composite transforms (e.g.,
Composed), the component transforms are applied sequentially. Time dependence itself is carried by exactly one wrapper,TimeDep(builder), wherebuilder(tau) -> AbstractTransform: every other transform holds only constant parameters, andacton aTimeDepevaluatesbuilder(tau)before applying it.- Parameters:
op (AbstractTransform) –
The transform to apply. This can be any transform type:
Translate: Spatial translation (point geometry)Rotate: Spatial rotationIdentity: No-opComposed: Sequential composition
tau (Any) – Parameter for tau-dependent transforms. Pass
Nonefor tau-independent transforms. OnlyTimeDep(and composites containing one) actually consumetau; it evaluates the wrappedbuilder(tau)before applying it.x (Any) –
The input to transform. Supported types depend on the transform:
Array/ArrayLike: Interpreted as Cartesian point dataQuantity: Unitful array, treated as Cartesian pointVector: Role-aware transformation with chart preservationCDict: Low-level component dict
*args (
Any) – Additional positional/keyword arguments passed to concrete dispatches, e.g.chart,rep,usys.**kwargs (
Any) – Additional positional/keyword arguments passed to concrete dispatches, e.g.chart,rep,usys.
- Returns:
The transformed input, same type as
x.- Return type:
- Raises:
NotImplementedError – If no dispatch is registered for the given
(transform, input)types.
Notes
Transform.__call__: The
__call__method of transforms delegates to this function:op(tau, x)is equivalent toact(op, tau, x).Chart inference: When no chart is provided and the input is an Array or Quantity, the chart is inferred via
coordinax.charts.guess_chart.Composite transforms: For
Composed, the component transforms are applied in sequence (left-to-right).
See also
coordinax.transforms.actConcrete dispatch entrypoint used in practice
coordinax.transforms.composeCompose two transforms into one
coordinax.transforms.simplifySimplify a transform to canonical form
Examples
>>> import unxt as u >>> import coordinax.transforms as cxfm
Apply a rotation to a Quantity:
>>> op = cxfm.Rotate.from_euler("z", u.Q(90, "deg")) >>> q = u.Q([1, 0, 0], "km") >>> cxfm.act(op, None, q).round(3) Q([0., 1., 0.], 'km')
Apply a translation to a Quantity (usys required):
>>> import jax.numpy as jnp >>> op = cxfm.Translate.from_([1, 0, 0], "km") >>> x = jnp.asarray([1.0, 0.0, 0.0]) # metres (dimensionless array) >>> cxfm.act(op, None, x, usys=u.unitsystems.si).round(3) Array([1001., 0., 0.], dtype=float64)
Composite transform:
>>> R = cxfm.Rotate.from_euler("z", u.Q(90, "deg")) >>> T = cxfm.Translate.from_([1, 0, 0], "km") >>> op = R | T # rotate then translate >>> cxfm.act(op, None, q).round(3) Q([1., 1., 0.], 'km')
- coordinax.act(op: Identity, tau: Any, x: Any, /, *args: Any, **kw: Any) Any
Identity operator - returns input unchanged.
>>> import jax.numpy as jnp >>> import unxt as u >>> import coordinax.transforms as cxfm
>>> op = cxfm.identity
>>> q = [1, 2, 3] >>> cxfm.act(op, None, q) is q True
>>> q = u.Q([1, 2, 3], "km") >>> cxfm.act(op, None, q) is q True
>>> data = {"x": u.Q(1, "km"), "y": u.Q(2, "km"), "z": u.Q(3, "km")} >>> cxfm.act(op, None, data) is data True
>>> v = cx.Point.from_(u.Q([1, 2, 3], "m")) >>> cxfm.act(op, None, v) is v True
- coordinax.act(op: AbstractTransform, tau: Any, x: dict, chart: AbstractChart, rep: Representation, /, **kw: Any) dict
Redispatch on the representation’s geometry kind (generic funnel).
- coordinax.act(op: AbstractTransform, tau: Any, x: dict, chart: AbstractChart, geom: PointGeometry, rep: Representation, /, **kw: Any) dict
Raise: a transform must register its own point action (the primitive).
- coordinax.act(op: AbstractTransform, tau: Any, x: dict, chart: AbstractChart, geom: TangentGeometry, rep: Representation, /, *, at: dict | None = None, at_jet: dict[int, dict | None] | None = None, usys: AbstractUnitSystem | None = None, **kw: Any) dict
Transform tangent data: pushforward or kinematic prolongation.
Order-0 (displacement) data and all data under time-independent transforms transform by the frozen-\(\tau\)
pushforward.Under a time-dependent transform, order-\(m\) data (\(m \geq 1\)) transforms by the \(m\)-th prolongation, which requires jet slots \(0 \ldots m-1\).
Supply those slots with
at_jet, a dict keyed by slot order.atis shorthand for slot 0, which it shares withpushforward; every higher slot isat_jet’s alone. Giving slot 0 both ways raises rather than picking one, and so does giving a slot \(\geq 1\) on a pushforward path that cannot read it — use act_jet for the full prolongation there.Examples
>>> import quaxed.numpy as jnp >>> import unxt as u >>> import coordinax.charts as cxc >>> import coordinax.representations as cxr >>> import coordinax.transforms as cxfm
A uniformly rotating frame (angular speed 1 rad/s about z):
>>> zhat = jnp.asarray([0., 0., 1.]) >>> op = cxfm.TimeDep(cxfm.builders.RotationAboutAxis(u.Q(1.0, "rad/s"), axis=zhat))
At tau=0 the rotation is the identity but the velocity still gains the \(\dot R x\) (angular) term:
>>> at = {"x": u.Q(1.0, "m"), "y": u.Q(0.0, "m"), "z": u.Q(0.0, "m")} >>> v = {"x": u.Q(0.0, "m/s"), "y": u.Q(0.0, "m/s"), "z": u.Q(0.0, "m/s")} >>> out = cxfm.act(op, u.Q(0.0, "s"), v, cxc.cart3d, cxr.tangent_geom, ... cxr.coord_vel, at=at) >>> out["y"].round(3) Q(1., 'm / s')
- coordinax.act(op: Composed, tau: Any, x: ArrayLike, chart: AbstractChart, rep: Representation, /, **kw: object) Array
Apply Composed to an ArrayLike by sequentially applying each transform.
>>> import jax.numpy as jnp >>> import unxt as u >>> import coordinax.charts as cxc >>> import coordinax.transforms as cxfm >>> import coordinax.representations as cxr
>>> shift = cxfm.Translate.from_([1, 2, 3], "km") >>> rot = cxfm.Rotate.from_euler("z", u.Q(90, "deg")) >>> pipe = cxfm.Composed((shift,)) >>> x = jnp.array([0.0, 0.0, 0.0]) >>> usys = u.unitsystems.si >>> cxfm.act(pipe, None, x, cxc.cart3d, cxr.point, usys=usys) Array([1000., 2000., 3000.], dtype=float64)
- coordinax.act(op: Composed, tau: Any, x: dict, chart: AbstractChart, rep: Representation, /, **kw: object) dict
Apply Composed to a CDict by sequentially applying each transform.
>>> import unxt as u >>> import coordinax.charts as cxc >>> import coordinax.transforms as cxfm >>> import coordinax.representations as cxr
>>> shift = cxfm.Translate.from_([1, 2, 3], "km") >>> pipe = cxfm.Composed((shift,)) >>> data = {"x": u.Q(0, "km"), "y": u.Q(0, "km"), "z": u.Q(0, "km")} >>> cxfm.act(pipe, None, data, cxc.cart3d, cxr.point) {'x': Q(1, 'km'), 'y': Q(2, 'km'), 'z': Q(3, 'km')}
- coordinax.act(op: Composed, tau: Any, x: AbstractQuantity, chart: AbstractChart, rep: Representation, /, **kw: object) AbstractQuantity
Apply Composed to a Quantity by sequentially applying each transform.
>>> import unxt as u >>> import coordinax.charts as cxc >>> import coordinax.transforms as cxfm >>> import coordinax.representations as cxr
>>> shift = cxfm.Translate.from_([1, 2, 3], "km") >>> pipe = cxfm.Composed((shift,)) >>> q = u.Q([0, 0, 0], "km") >>> cxfm.act(pipe, None, q, cxc.cart3d, cxr.point) Q([1, 2, 3], 'km')
- coordinax.act(op: Composed, tau: Any, x: AbstractQuantity, /, **kw: object) AbstractQuantity
Apply Composed to a Quantity by sequentially applying each transform.
>>> import unxt as u >>> import coordinax.transforms as cxfm
>>> shift = cxfm.Translate.from_([1, 2, 3], "km") >>> pipe = cxfm.Composed((shift,)) >>> q = u.Q([0, 0, 0], "km") >>> cxfm.act(pipe, None, q) Q([1, 2, 3], 'km')
- coordinax.act(op: TimeDep, tau: Any, x: dict, chart: AbstractChart, geom: PointGeometry, rep: Representation, /, **kw: Any) dict
Point action: evaluate the family at tau, then act.
Examples
>>> import jax.numpy as jnp >>> import unxt as u >>> import coordinax.charts as cxc >>> import coordinax.representations as cxr >>> import coordinax.transforms as cxfm
>>> zhat = jnp.array([0.0, 0.0, 1.0]) >>> op = cxfm.TimeDep(cxfm.builders.RotationAboutAxis(u.Q(90, "deg/s"), axis=zhat)) >>> q = {"x": u.Q(1.0, "m"), "y": u.Q(0.0, "m"), "z": u.Q(0.0, "m")} >>> out = cxfm.act(op, u.Q(1.0, "s"), q, cxc.cart3d, cxr.point) >>> out["y"].round(3) Q(1., 'm')
- coordinax.act(op: TimeDep, tau: Any, x: dict, chart: AbstractChart, geom: TangentGeometry, rep: Representation, /, *, at: dict | None = None, at_jet: dict[int, dict | None] | None = None, usys: Any = None, **kw: Any) dict
Tangent action of a TimeDep, with the fibre-offset ladder rule.
A TimeDep family whose value is a fibre offset (ladder order \(k \geq 1\)) applies the ladder rule directly: order-\(m\) data gains \(d^{m-k}\delta/d\tau^{m-k}\). Every other TimeDep — in particular every point-acting one — defers to the generic funnel, which recovers its time dependence by differentiating the point action.
>>> import unxt as u >>> import coordinax.charts as cxc >>> import coordinax.representations as cxr >>> import coordinax.transforms as cxfm
A velocity kick that grows at 5 km/s2 shifts accelerations by its rate:
>>> kick = cxfm.TimeDep.from_(lambda t: cxfm.Translate( ... {"x": u.Q(5.0, "km/s2") * t, "y": u.Q(0.0, "km/s"), ... "z": u.Q(0.0, "km/s")}, ... chart=cxc.cart3d, semantic_kind=cxr.vel)) >>> a = {"x": u.Q(1.0, "km/s2"), "y": u.Q(1.0, "km/s2"), "z": u.Q(1.0, "km/s2")} >>> out = cxfm.act(kick, u.Q(2.0, "s"), a, cxc.cart3d, cxr.coord_acc) >>> out["x"] Q(6., 'km / s2')
- coordinax.act(op: coordinax.transforms._src.actions.linear.AbstractLinearTransform, tau: Any, x: ArrayLike, chart: AbstractChart, rep: Representation, /, **kw: Any) Array
Apply a linear transform to an Array(like) object.
A bare array with no explicit
repdefaults to ~coordinax.point, which this fast path serves directly: a linear map on Cartesian point coordinates is justM x, with no units to track.An explicit non-point
repsays what the data is, so it is not ambiguous and is served by the CDict ladder (#972). Unlike a Translate – where a position shift is the identity on a velocity – a linear map genuinely acts on a tangent, and the CDict path already knows the rule (its pushforward isM v, through the chart Jacobian when the chart is not flat):>>> import quaxed.numpy as jnp >>> import unxt as u >>> import coordinax.charts as cxc >>> import coordinax.representations as cxr >>> import coordinax.transforms as cxfm
>>> op = cxfm.Rotate.from_euler("z", u.Q(90, "deg")) >>> v = jnp.asarray([1.0, 0.0, 0.0]) >>> cxfm.act(op, None, v, cxc.cart3d, cxr.coord_vel, ... usys=u.unitsystems.galactic).round(3) Array([0., 1., 0.], dtype=float64)
- coordinax.act(op: coordinax.transforms._src.actions.linear.AbstractLinearTransform, tau: Any, x: AbstractQuantity, chart: AbstractChart, rep: Representation, /, **kw: Any) AbstractQuantity
Apply a linear transform to a PointGeometry-roled Quantity.
- coordinax.act(op: coordinax.transforms._src.actions.linear.AbstractLinearTransform, tau: Any, x: dict, chart: AbstractChart, geom: PointGeometry, rep: Representation, /, *, usys: AbstractUnitSystem | None = None, **kw: Any) dict
Apply a linear map to a Point-valued coordinate dictionary.
The point is mapped by converting to the chart’s canonical Cartesian chart, applying the matrix in Cartesian components, then converting back. Units are handled by packing Cartesian components into a common unit before the map and restoring it afterward.
- coordinax.act(op: coordinax.transforms._src.actions.linear.AbstractLinearTransform, tau: Any, x: dict, chart: AbstractCartesianProductChart, geom: PointGeometry, rep: Representation, /, *, usys: AbstractUnitSystem | None = None, **kw: Any) dict
Apply a linear map factorwise on Cartesian-product charts.
- coordinax.act(op: Affine, tau: Any, x: dict, chart: AbstractChart, geom: PointGeometry, rep: Representation, /, *, usys: AbstractUnitSystem | None = None, **kw: Any) dict
Apply
A x + bin one kernel, in the chart’s Cartesian components.- coordinax.act(op: Translate, tau: Any, x: ArrayLike, chart: AbstractChart, rep: Representation, /, *, usys: AbstractUnitSystem | None = None, **kw: Any) Array
Apply Translate to an ArrayLike.
The array is interpreted as Cartesian coordinates. The delta is converted to the same unit system to perform the addition.
>>> import jax.numpy as jnp >>> import coordinax.transforms as cxfm
>>> shift = cxfm.Translate.from_([1, 2, 3], "km") >>> x = jnp.array([0.0, 0.0, 0.0]) >>> usys = u.unitsystems.si >>> cxfm.act(shift, None, x, cxc.cart3d, cxr.point, usys=usys) Array([1000., 2000., 3000.], dtype=float64)
A fibre kick (e.g.
semantic_kind=vel) cannot infer whether a bare, unitless array is a position (kick is identity) or the matching tangent data (kick applies) — that ambiguity is rejected loudly:>>> import coordinax.representations as cxr >>> from dataclassish import replace >>> vel_shift = replace(shift, semantic_kind=cxr.vel) >>> try: ... cxfm.act(vel_shift, None, x, cxc.cart3d, cxr.point, usys=usys) ... except TypeError as e: ... print(str(e)[:44]) A fibre offset (Translate with semantic_kind
An explicit tangent
repis not ambiguous, so it is served by the CDict ladder: a position translate leaves velocities alone.>>> cxfm.act(shift, None, x, cxc.cart3d, cxr.coord_vel, usys=usys) Array([0., 0., 0.], dtype=float64)
- coordinax.act(op: Translate, tau: Any, x: AbstractQuantity, chart: AbstractChart, rep: Representation, /, *, usys: AbstractUnitSystem | None = None, **kw: Any) AbstractQuantity
Apply Translate to a Quantity.
The array is interpreted as Cartesian coordinates. The delta is converted to the same unit system to perform the addition.
>>> import jax.numpy as jnp >>> import coordinax.transforms as cxfm >>> import coordinax.representations as cxr >>> from dataclassish import replace >>> import unxt as u
>>> shift = cxfm.Translate.from_([1, 2, 3], "km") >>> x = u.Q([0.0, 0.0, 0.0], "m") >>> cxfm.act(shift, None, x, cxc.cart3d, cxr.point) Q([1000., 2000., 3000.], 'm')
Velocity-semantic translate is identity on point quantities:
>>> vel_shift = replace(shift, semantic_kind=cxr.vel) >>> cxfm.act(vel_shift, None, x, cxc.cart3d, cxr.point) Q([0., 0., 0.], 'm')
A Quantity’s rep is inferred from its dimensions, so a velocity Quantity arrives with a tangent rep — served by the CDict ladder (#942):
>>> v = u.Q([1.0, 0.0, 0.0], "km/s") >>> cxfm.act(shift, None, v, cxc.cart3d, cxr.coord_vel) Q([1., 0., 0.], 'km / s')
A velocity kick does move them:
>>> kick = replace(cxfm.Translate.from_([1, 2, 3], "km/s"), semantic_kind=cxr.vel) >>> cxfm.act(kick, None, v, cxc.cart3d, cxr.coord_vel) Q([2., 2., 3.], 'km / s')
- coordinax.act(op: Translate, tau: Any, x: dict, chart: AbstractChart, rep: Representation, /, *, usys: AbstractUnitSystem | None = None, **kw: Any) dict
Apply Translate to a component dictionary (ladder rule).
The behavior follows the time-derivative ladder: with \(k\) the operator’s
semantic_kindorder and \(m\) the input’s ladder order, the input gains \(d^{m-k}\delta/d\tau^{m-k}\) for \(m \geq k\) (points act as the curve position for \(k = 0\)), and is unaffected for \(m < k\) or forDisplacementdata (\(m = 0\)).>>> import coordinax.transforms as cxfm >>> import unxt as u
Default (displacement-semantic) translate shifts points:
>>> shift = cxfm.Translate.from_([1, 2, 3], "km") >>> x = {"x": u.Q(0, "km"), "y": u.Q(0, "km"), "z": u.Q(0, "km")} >>> cxfm.act(shift, None, x, cxc.cart3d, cxr.point) {'x': Q(1, 'km'), 'y': Q(2, 'km'), 'z': Q(3, 'km')}
A static translate does not affect velocities:
>>> v = {"x": u.Q(1.0, "km/s"), "y": u.Q(0.0, "km/s"), "z": u.Q(0.0, "km/s")} >>> cxfm.act(shift, None, v, cxc.cart3d, cxr.coord_vel) {'x': Q(1., 'km / s'), 'y': Q(0., 'km / s'), 'z': Q(0., 'km / s')}
But a time-dependent translate — a ~coordinax.transforms.TimeDep family — boosts velocities by its rate (the kinematic prolongation):
>>> rate = {"x": u.Q(3.0, "km/s"), "y": u.Q(0.0, "km/s"), "z": u.Q(0.0, "km/s")} >>> moving = cxfm.TimeDep(cxfm.builders.UniformTranslation(rate, chart=cxc.cart3d)) >>> at = {"x": u.Q(0.0, "km"), "y": u.Q(0.0, "km"), "z": u.Q(0.0, "km")} >>> out = cxfm.act(moving, u.Q(2.0, "s"), v, cxc.cart3d, cxr.coord_vel, at=at) >>> out["x"].round(3) Q(4., 'km / s')
- coordinax.act(op: Boost, tau: Any, x: dict, chart: AbstractChart, rep: Representation, /, *, usys: AbstractUnitSystem | None = None, **kw: Any) dict
Apply a Galilean boost to a component dictionary.
The point action is \(x \mapsto x + \Delta v\,\tau\); tangent data of ladder order \(m\) gains \(d^m(\Delta v\,\tau)/d\tau^m\) (so \(\Delta v\) for velocities and, for constant \(\Delta v\), nothing for accelerations). Displacements are invariant.
Examples
>>> import unxt as u >>> import coordinax.charts as cxc >>> import coordinax.representations as cxr >>> import coordinax.transforms as cxfm
>>> dv = {"x": u.Q(1.0, "km/s"), "y": u.Q(0.0, "km/s"), "z": u.Q(0.0, "km/s")} >>> boost = cxfm.Boost(dv, chart=cxc.cart3d)
Boost shifts velocity components:
>>> v = {"x": u.Q(2.0, "km/s"), "y": u.Q(3.0, "km/s"), "z": u.Q(0.0, "km/s")} >>> cxfm.act(boost, u.Q(0.0, "s"), v, cxc.cart3d, cxr.coord_vel) {'x': Q(3., 'km / s'), 'y': Q(3., 'km / s'), 'z': Q(0., 'km / s')}
A static boost leaves accelerations unchanged:
>>> a = {"x": u.Q(1.0, "km/s2"), "y": u.Q(0.0, "km/s2"), "z": u.Q(0.0, "km/s2")} >>> cxfm.act(boost, u.Q(0.0, "s"), a, cxc.cart3d, cxr.coord_acc) {'x': Q(1., 'km / s2'), 'y': Q(0., 'km / s2'), 'z': Q(0., 'km / s2')}
- coordinax.act(op: Boost, tau: Any, x: dict, chart: AbstractChart, geom: TangentGeometry, rep: Representation, /, *, usys: AbstractUnitSystem | None = None, **kw: Any) dict
Boost tangent action (geometry-form): defer to the 5-arg
act.Boost’s 5-arg
actimplements the kinematic prolongation directly (its closed forms avoid the general jet machinery); the geometry-form delegates to it so the twoactforms are identical by construction.- coordinax.act(op: AbstractTransform, tau: Any, x: ArrayLike | AbstractQuantity | unxts.linalg._src._quantity_matrix.QuantityMatrix | dict, /, **kw: Any) Any
Infer the chart and representation, then apply the operator.
A bare input is interpreted as the data for a coordinax.Point in its guessed (Cartesian) chart.
>>> import jax.numpy as jnp >>> import unxt as u >>> import coordinax.transforms as cxfm
>>> usys = u.unitsystems.si >>> x = jnp.asarray([1, 0, 0]) # [m]
>>> T = cxfm.Translate.from_([1, 0, 0], "km") >>> cxfm.act(T, None, x, usys=usys).round(3) # needs usys Array([1001., 0., 0.], dtype=float64)
>>> R = cxfm.Rotate.from_euler("z", u.Q(90, "deg")) >>> cxfm.act(R, None, x).round(3) # no usys required Array([0., 1., 0.], dtype=float64)
>>> op = R | T # rotate then translate >>> cxfm.act(op, None, x, usys=usys).round(3) Array([1000., 1., 0.], dtype=float64)
A Quantity carries units, so its role is inferred from them:
>>> q = u.Q([1, 0, 0], "km") >>> cxfm.act(R, None, q).round(3) Q([0., 1., 0.], 'km')
- coordinax.act(op: AbstractTransform, tau: Any, x: ArrayLike | AbstractQuantity | unxts.linalg._src._quantity_matrix.QuantityMatrix | dict, chart: AbstractChart, /, **kw: Any) Any
Infer the representation, then apply the operator.
>>> import jax.numpy as jnp >>> import unxt as u >>> import coordinax.charts as cxc >>> import coordinax.transforms as cxfm
>>> R = cxfm.Rotate.from_euler("z", u.Q(90, "deg")) >>> q = u.Q([1, 0, 0], "km") >>> cxfm.act(R, None, q, cxc.cart3d).round(3) Q([0., 1., 0.], 'km')
- coordinax.act(op: AbstractTransform, tau: Any, x: ArrayLike, chart: AbstractChart, rep: Representation, /, **kw: Any) Array
Apply an operator to an Array(like) object.
The Array is interpreted as coordinates in
chart, in the units thatusysgives forrep— an array carries none of its own — and the result is written back in those same units.>>> import jax.numpy as jnp >>> import unxt as u >>> import coordinax as cx
>>> op = cx.Rotate.from_euler("z", u.Q(90, "deg")) >>> x = jnp.asarray([1.0, 0.0, 0.0]) >>> cx.act(op, None, x, cx.cart3d, cx.point).round(3) Array([0., 1., 0.], dtype=float64)
Operators with no typed Array fast path land here, and are served by coercion to a CDict rather than by re-dispatch (#948 — every arity-6
acttakes a CDict, so re-dispatching an array found no method):>>> import coordinax.transforms as cxfm >>> import coordinax.charts as cxc >>> rate = {k: u.Q(v, "kpc/Myr") for k, v in [("x", 1.0), ("y", 0.0), ("z", 0.0)]} >>> boost = cxfm.Boost(rate, chart=cxc.cart3d) >>> cxfm.act(boost, u.Q(1.0, "Myr"), x, usys=u.unitsystems.galactic).round(3) Array([2., 0., 0.], dtype=float64)
- coordinax.act(op: AbstractTransform, tau: Any, x: AbstractQuantity, chart: AbstractChart, rep: Representation, /, **kw: Any) AbstractQuantity
Apply operator, routing through the CDict-based implementation.
>>> import jax.numpy as jnp >>> import unxt as u >>> import coordinax.charts as cxc >>> import coordinax.transforms as cxfm >>> import coordinax.representations as cxr
>>> op = cxfm.Rotate.from_euler("z", u.Q(90, "deg")) >>> q = u.Q([1, 0, 0], "km")
Directly access this registered method, bypassing more efficient methods.
>>> func = cxfm.act.invoke(cxfm.Rotate, None, u.Q, cxc.Cart3D, cxr.Representation) >>> func(op, None, q, cxc.cart3d, cxr.point).round(3) Q([0., 1., 0.], 'km')
- coordinax.act(op: AbstractTransform, tau: Any, x: unxts.linalg._src._quantity_matrix.QuantityMatrix, chart: AbstractChart, rep: Representation, /, **kw: Any) unxts.linalg._src._quantity_matrix.QuantityMatrix
Apply an operator to a
QuantityMatrixwith explicit chart and rep.Routes through the CDict-based implementation, then repacks the result into a
QuantityMatrix.>>> import jax.numpy as jnp >>> import unxt as u >>> import coordinax.charts as cxc >>> import coordinax.transforms as cxfm >>> import coordinax.representations as cxr >>> import unxts.linalg as ul
>>> op = cxfm.Rotate.from_euler("z", u.Q(90, "deg")) >>> qm = ul.QuantityMatrix( ... jnp.array([1.0, 0.0, 0.0]), ... unit=("km", "km", "km"), ... ) >>> result = cxfm.act(op, None, qm, cxc.cart3d, cxr.point) >>> result.value.round(3) Array([0., 1., 0.], dtype=float64)
- coordinax.act(op: AbstractTransform, tau: Any, x: Tangent, /, *, at: Any = None, at_jet: dict[int, Any] | None = None, **kw: Any) Tangent
Act a frame transform on a tangent Tangent.
at(the base point) anchors the transformation where one is needed — for Jacobian pushforwards in non-Cartesian charts, and as slot 0 of the kinematic prolongation under time-dependent transforms.at_jetis the general form, a dict keyed by jet slot, and the only spelling that reaches slot 1 and above. Either may hold Point/Tangent instances (whose.datais used, after a chart check) or rawCDictdata —at_jet’s values slot by slot.>>> import jax.numpy as jnp >>> import unxt as u >>> import coordinax as cx >>> import coordinax.frames as cxf
>>> Rz = jnp.asarray([[0, -1, 0], [1, 0, 0], [0, 0, 1]]) >>> op = cx.Rotate(Rz) >>> v = cx.Tangent.from_( ... {"x": u.Q(1.0, "m/s"), "y": u.Q(0.0, "m/s"), "z": u.Q(0.0, "m/s")}, ... cx.cart3d, cx.coord_vel, ... ) >>> transformed = cx.act(op, None, v) >>> print(transformed) <Tangent: chart=Cart3D (x, y, z) [m / s] [0. 1. 0.]>
- coordinax.act(op: AbstractTransform, tau: Any, x: Point, /, **kw: Any) Point
Act a frame transform on a Point.
>>> import jax.numpy as jnp >>> import unxt as u >>> import coordinax as cx
>>> Rz = jnp.asarray([[0, -1, 0], [1, 0, 0], [0, 0, 1]]) >>> op = cx.Rotate(Rz) >>> q = u.Q([1, 0, 0], "km") >>> vec = cx.Point.from_(q) >>> print(vec) <Point: chart=Cart3D (x, y, z) [km] [1 0 0]>
>>> transformed_vec = cx.act(op, None, vec) >>> print(transformed_vec) <Point: chart=Cart3D (x, y, z) [km] [0 1 0]>
- coordinax.act(op: AbstractTransform, tau: Any, x: Coordinate, /, **kw: Any) Coordinate
Act a frame transform on a Coordinate (point + all fibres).
>>> import coordinax as cx >>> import coordinax.frames as cxf >>> import coordinax.charts as cxc >>> import coordinax.representations as cxr >>> import unxt as u
>>> point = cx.Point.from_([1.0, 0.0, 0.0], "m", cxf.alice) >>> vel = cx.Tangent( ... {"x": u.Q(1.0, "m/s"), "y": u.Q(0.0, "m/s"), "z": u.Q(0.0, "m/s")}, ... cxc.cart3d, cxr.coord_basis, cxr.vel, frame=cxf.alice, ... ) >>> pv = cx.Coordinate(point=point, velocity=vel) >>> pv_alex = pv.to_frame(cxf.alex) >>> pv_alex.frame Alex() >>> pv_alex["velocity"].frame Alex()
A time-dependent transform prolongs the whole bundle jointly: a uniformly moving translation boosts the velocity fibre by its rate:
>>> import coordinax.transforms as cxfm >>> rate = {"x": u.Q(3.0, "m/s"), "y": u.Q(0.0, "m/s"), "z": u.Q(0.0, "m/s")} >>> moving = cxfm.TimeDep(cxfm.builders.UniformTranslation(rate, chart=cxc.cart3d)) >>> out = cx.act(moving, u.Q(2.0, "s"), pv) >>> out.point.data["x"], out["velocity"].data["x"] (Q(7., 'm'), Q(4., 'm / s'))
- coordinax.frame_transition(*args, **kwargs)
Return the transform operator that maps coordinates from one frame to another.
Given a source frame and a target frame,
frame_transitioncomputes theAbstractTransform(or composed chain of transforms) that, when applied to coordinates expressed in from_frame, yields coordinates expressed in to_frame.Notes
Each pair of concrete frame types registers its own dispatch. Calling
frame_transition(frame_a, frame_a)returnsIdentity().For
TransformedReferenceFrame, the transition is constructed automatically by composing the base-frame transition with the storedxop.The returned transform is invertible:
op.inversegives the to_frame → from_frame direction.Build the operator outside ``jit``, then pass it in as an argument.
frame_transitionis pure Python: it walks the dispatch table, composes the chain and simplifies it. That work does not belong inside a traced function – do it once, up front, and hand the operator to the jitted function that applies it. Frames and transforms are pytrees, so changing frame parameters reuses the same compiled code; closing over the operator instead makes it static and buys a fresh compile per operator. The payoff is large: applying a precomputed operator underjitruns in microseconds rather than milliseconds (roughly 30x atN=1, and over 100x againstto_frame, which rebuilds the operator on every call). Eager application costs a few milliseconds of fixed Python plus tens of nanoseconds per element, so below ~1e5 elements you are paying almost entirely for Python.This function uses multiple dispatch. To inspect all registered pairs:
>>> import coordinax.frames as cxf >>> cxf.frame_transition.methods List of 20 method(s): [0] frame_transition(from_frame: ...)
See also
coordinax.transforms.actApply a transform to coordinates
coordinax.transforms.composeCompose two transforms into one
Examples
>>> import coordinax.frames as cxf
Same-to-same frame (identity):
>>> cxf.frame_transition(cxf.alice, cxf.alice) Identity()
Alice → Alex:
>>> op = cxf.frame_transition(cxf.alice, cxf.alex) >>> op Composed(( Translate(...), Rotate(...) ))
Alex → Alice (inverse direction):
>>> op = cxf.frame_transition(cxf.alex, cxf.alice) >>> op Composed(( Rotate(...), Translate(...) ))
Using a TransformedReferenceFrame:
>>> import coordinax.transforms as cxfm >>> import quaxed.numpy as jnp >>> from coordinaxs.astro import ICRS
>>> R = cxfm.Rotate(jnp.asarray([[0., -1, 0], [1, 0, 0], [0, 0, 1]])) >>> frame = cxf.TransformedReferenceFrame(ICRS(), R)
>>> op = cxf.frame_transition(ICRS(), frame) >>> type(op).__name__ 'Composed'
Under ``jit`` – operator built outside, passed in:
>>> import equinox as eqx >>> import coordinax as cx >>> import coordinax.transforms as cxfm
>>> op = cxf.frame_transition(cxf.alice, cxf.alex)
>>> @eqx.filter_jit ... def to_alex(op, p): ... return cxfm.act(op, None, p)
>>> p = cx.Point.from_([1, 2, 3], "kpc", cxf.alice) >>> print(to_alex(op, p)) <Point: chart=Cart3D (x, y, z) [kpc] [-2. 1. 3.]>
- coordinax.frame_transition(from_frame: AbstractReferenceFrame, to_frame: AbstractReferenceFrame, /) Identity
Return an identity operator for frames that are the same.
>>> import coordinax.frames as cxf >>> cxf.frame_transition(cxf.alice, cxf.alice) Identity() >>> cxf.frame_transition(cxf.alex, cxf.alex) Identity()
>>> cxf.frame_transition(cxf.bob, cxf.bob) Identity()
>>> cxf.frame_transition(cxf.carol, cxf.carol) Identity()
Transform from Alice’s frame to Alex’s frame.
>>> import unxt as u >>> import coordinax as cx >>> op = cxf.frame_transition(cxf.alice, cxf.alex) >>> print(op) Composed(( Translate(...), Rotate(...) ))
Transform from Alice’s frame to Bob’s frame.
A spacetime translation followed by a Lorentz boost of \(\beta = 0.9\) along Alice’s x-axis. Bob moves too fast for a Galilean velocity kick: adding 0.9 c to a particle already at 0.3 c would give 1.2 c. See Bob for that comparison worked through, and Carol for the slow frame where the kick is the right physics.
Because a boost mixes time into the spatial components, this transition acts on ~coordinax.charts.minkowskict rather than a 3-D chart.
Examples
>>> import coordinax.frames as cxf
>>> op = cxf.frame_transition(cxf.Alice(), cxf.Bob()) >>> [type(t).__name__ for t in op.transforms] ['Translate', 'LorentzBoost']
Transform from Alice’s frame to Carol’s frame.
A spatial translation followed by a velocity kick – the transformation Bob carried before he became relativistic. The kick has well-defined actions on each kinematic role:
Point: identity (a velocity kick does not move points)
Displacement: identity (Galilean invariant)
Velocity: adds \(v_0\)
Acceleration: identity (for a constant kick)
Examples
>>> import coordinax.frames as cxf
>>> op = cxf.frame_transition(cxf.alice, cxf.carol) >>> [type(t).__name__ for t in op.transforms] ['Translate', 'Translate']
- coordinax.frame_transition(from_frame: AbstractReferenceFrame, to_frame: AbstractReferenceFrame, /) Composed
Transform back.
>>> import unxt as u >>> import coordinax as cx
>>> cxf.frame_transition(cxf.alex, cxf.alice) Composed(( Rotate(...), Translate(...) ))
>>> [type(t).__name__ for t in cxf.frame_transition(cxf.bob, cxf.alice).transforms] ['LorentzBoost', 'Translate']
>>> op = cxf.frame_transition(cxf.carol, cxf.alice) >>> [type(t).__name__ for t in op.transforms] ['Translate', 'Translate']
Null-to-null frame transition is always the identity.
When both source and target frames are
noframe(i.e. the vector is frame-agnostic) there is nothing to transform, so the result is the identity operation.>>> import coordinax.frames as cxf >>> import coordinax.transforms as cxfm >>> op = cxf.frame_transition(cxf.noframe, cxf.noframe) >>> isinstance(op, cxfm.Identity) True
- coordinax.frame_transition(from_frame: NoFrame, to_frame: AbstractReferenceFrame, /) NoReturn
Cannot transform from the null frame.
>>> import coordinax.frames as cxf >>> try: ... cxf.frame_transition(cxf.noframe, cxf.alice) ... except cxf.FrameTransformError as e: ... print(e) Cannot transform from the null frame.
- coordinax.frame_transition(from_frame: AbstractReferenceFrame, to_frame: NoFrame, /) NoReturn
Cannot transform to the null frame.
Carries the same precedence as its “from the null frame” sibling above, and for the same reason: without it a rule keyed on the source type – frame_transition(TransformedReferenceFrame, AbstractReferenceFrame) – matches equally well, and plum has no ground to choose. Any transformed frame going to noframe then raised AmbiguousLookupError instead of saying what was wrong, while the mirror direction, already at precedence 1, answered properly.
>>> import coordinax.frames as cxf >>> try: ... cxf.frame_transition(cxf.alice, cxf.noframe) ... except cxf.FrameTransformError as e: ... print(e) Cannot transform to the null frame.
- coordinax.frame_transition(from_frame: AbstractReferenceFrame, to_frame: AbstractTransformedReferenceFrame) AbstractTransform
Return a frame transform operator to a transformed frame.
>>> import quaxed.numpy as jnp >>> import coordinax.vectors as cxv >>> import coordinax.frames as cxf >>> import coordinax.transforms as cxfm >>> from coordinaxs.astro import ICRS
>>> R = cxfm.Rotate(jnp.asarray([[0., -1, 0], [1, 0, 0], [0, 0, 1]])) >>> frame = cxf.TransformedReferenceFrame(ICRS(), R) >>> frame TransformedReferenceFrame(base_frame=ICRS(), xop=Rotate(R=f64[3,3]))
Let’s transform a position from the base frame to the transformed frame:
>>> op = cxf.frame_transition(ICRS(), frame)
>>> q_icrs = cxv.Point.from_([1, 0, 0], "kpc") >>> q_frame = op(q_icrs) >>> print(q_frame) <Point: chart=Cart3D (x, y, z) [kpc] [0. 1. 0.]>
- coordinax.frame_transition(from_frame: AbstractTransformedReferenceFrame, to_frame: AbstractReferenceFrame) AbstractTransform
Return a frame transform operator from a transformed frame.
>>> import quaxed.numpy as jnp >>> import coordinax.vectors as cxv >>> import coordinax.frames as cxf >>> import coordinax.transforms as cxfm >>> from coordinaxs.astro import ICRS
>>> R = cxfm.Rotate(jnp.asarray([[0., -1, 0], [1, 0, 0], [0, 0, 1]])) >>> frame = cxf.TransformedReferenceFrame(ICRS(), R) >>> frame TransformedReferenceFrame(base_frame=ICRS(), xop=Rotate(R=f64[3,3]))
Let’s transform a position from the base frame to the transformed frame:
>>> op = cxf.frame_transition(frame, ICRS())
>>> q_icrs = cxv.Point.from_([0, 1, 0], "kpc") >>> q_frame = op(q_icrs) >>> print(q_frame) <Point: chart=Cart3D (x, y, z) [kpc] [1. 0. 0.]>
- coordinax.frame_transition(from_frame: AbstractTransformedReferenceFrame, to_frame: AbstractTransformedReferenceFrame) AbstractTransform
Return a frame transform operator between two transformed frames.
When sameness is statically knowable – the same object, or equal-but-distinct with concrete leaves – the result is the identity transform. Frames arriving as jax.jit arguments are neither: tracing hands this function two distinct tracer-backed frames, so the shortcut is skipped and the general transform is built instead. That transform is correct either way; only the shortcut is lost.
>>> import quaxed.numpy as jnp >>> import coordinax.vectors as cxv >>> import coordinax.frames as cxf >>> import coordinax.transforms as cxfm >>> from coordinaxs.astro import ICRS
>>> R = cxfm.Rotate(jnp.asarray([[0., -1, 0], [1, 0, 0], [0, 0, 1]])) >>> frame1 = cxf.TransformedReferenceFrame(ICRS(), R)
Same frame → identity:
>>> cxf.frame_transition(frame1, frame1) Identity()
An equal-but-distinct frame is the same frame, so it too is the identity rather than a
Rotate | Identity | Rotatechain:>>> frame1b = cxf.TransformedReferenceFrame( ... ICRS(), cxfm.Rotate(jnp.asarray([[0., -1, 0], [1, 0, 0], [0, 0, 1]])) ... ) >>> cxf.frame_transition(frame1, frame1b) Identity()
>>> shift = cxfm.Translate.from_([1, 0, 0], "kpc") >>> frame2 = cxf.TransformedReferenceFrame(frame1, shift)
>>> op1to2 = cxf.frame_transition(frame1, frame2)
>>> q_frame1 = cxv.Point.from_([0, -1, 0], "kpc") >>> q_frame2 = op1to2(q_frame1) >>> print(q_frame2) <Point: chart=Cart3D (x, y, z) [kpc] [ 1. -1. 0.]>
- coordinax.frame_transition(from_frame: AbstractSpaceFrame, to_frame: AbstractSpaceFrame, /) AbstractTransform
Compute frame transformations with ICRS as the intermediary.
>>> import plum >>> import unxt as u >>> import coordinax.frames as cxf >>> import coordinaxs.astro as cxastro
Routing through ICRS needs both legs registered: one direction alone leaves the other, and the frame’s self-transition, unroutable.
>>> class MySpaceFrame(cxastro.AbstractSpaceFrame): ... pass
>>> @plum.dispatch ... def frame_transition(from_frame: MySpaceFrame, to_frame: ICRS, /) -> cxfm.AbstractTransform: ... return cxfm.Rotate.from_euler("z", u.Q(10, "deg"))
>>> @plum.dispatch ... def frame_transition(from_frame: ICRS, to_frame: MySpaceFrame, /) -> cxfm.AbstractTransform: ... return cxfm.Rotate.from_euler("z", u.Q(-10, "deg"))
We can transform from MySpaceFrame to a Galactocentric frame, even though we don’t have a direct transformation defined:
>>> my_frame = MySpaceFrame() >>> gcf_frame = cxastro.Galactocentric()
>>> op = cxf.frame_transition(my_frame, gcf_frame) >>> op Composed(( Affine( f64[3,3](jax), {...}, Cart3D(M=Rn(3)), coordinax.transforms._src.groups.EuclideanGroup ), Translate( {...}, chart=Cart3D(M=Rn(3)), semantic_kind=vel ) ))
The two rotations and the spatial translation fuse into one Affine kernel; the velocity offset stays separate because it acts on the tangent fibre rather than on the point.
The return leg routes the same way, off the
(ICRS, MySpaceFrame)registration:>>> type(cxf.frame_transition(gcf_frame, my_frame)).__name__ 'Composed'
- coordinax.frame_transition(from_frame: AbstractSpaceFrame, to_frame: ICRS, /) NoReturn
No registered transformation to ICRS.
>>> import coordinax.frames as cxf >>> import coordinaxs.astro as cxastro
>>> class UnregisteredFrame(cxastro.AbstractSpaceFrame): ... pass
>>> try: ... cxf.frame_transition(UnregisteredFrame(), cxastro.icrs) ... except cxf.FrameTransformError as e: ... print(e) No `frame_transition` registered from UnregisteredFrame to ICRS.
- coordinax.frame_transition(from_frame: ICRS, to_frame: AbstractSpaceFrame, /) NoReturn
No registered transformation from ICRS.
>>> import coordinax.frames as cxf >>> import coordinaxs.astro as cxastro
>>> class UnregisteredFrame(cxastro.AbstractSpaceFrame): ... pass
>>> try: ... cxf.frame_transition(cxastro.icrs, UnregisteredFrame()) ... except cxf.FrameTransformError as e: ... print(e) No `frame_transition` registered from ICRS to UnregisteredFrame.
Return an identity operator for the ICRS->ICRS transformation.
>>> import coordinax.frames as cxf >>> import coordinaxs.astro as cxastro
>>> icrs_frame = cxastro.ICRS() >>> frame_op = cxf.frame_transition(icrs_frame, icrs_frame) >>> frame_op Identity()
Return an identity operator for the Galactic->Galactic transformation.
>>> import coordinax.frames as cxf >>> import coordinaxs.astro as cxastro
>>> cxf.frame_transition(cxastro.galactic, cxastro.galactic) Identity()
Return an ICRS to Galactic frame transformation operator.
The Galactic frame is related to ICRS by a fixed rotation (the IAU 1958 definition, via Astropy’s FK4-B1950-derived matrix including the ICRS/FK5 frame bias). Being a pure static rotation, it acts on positions, displacements, velocities, and accelerations alike by the rotation matrix.
Examples
>>> import unxt as u >>> import coordinax as cx >>> import coordinaxs.astro as cxastro
>>> frame_op = cx.frame_transition(cxastro.icrs, cxastro.galactic) >>> frame_op Rotate(f64[3,3](jax))
The North Galactic Pole (in ICRS) maps to the +z axis:
>>> ngp = cx.Point.from_( ... {"lon": u.Q(192.8594812065348, "deg"), "lat": u.Q(27.12825118085622, "deg"), ... "distance": u.Q(1, "kpc")}, cx.lonlat_sph3d) >>> print(frame_op(ngp).cconvert(cx.cart3d).round(6)) <Point: chart=Cart3D (x, y, z) [kpc] [0. 0. 1.]>
Velocities rotate too:
>>> v = cx.Tangent.from_([100.0, 0.0, 0.0], "km/s") >>> print(frame_op(None, v).round(3)) <Tangent: chart=Cart3D (x, y, z) [km / s] [ -5.488 49.411 -86.767]>
Return a Galactic to ICRS frame transformation operator.
The inverse of the ICRS -> Galactic rotation (the matrix transpose).
Examples
>>> import unxt as u >>> import coordinax as cx >>> import coordinaxs.astro as cxastro
>>> fwd = cx.frame_transition(cxastro.icrs, cxastro.galactic) >>> bwd = cx.frame_transition(cxastro.galactic, cxastro.icrs)
>>> q = u.Q([1.0, 2.0, 3.0], "kpc") >>> bwd(fwd(q)).round(6) Q([1., 2., 3.], 'kpc')
- coordinax.frame_transition(from_frame: Galactocentric, to_frame: Galactocentric, /) AbstractTransform
Return the transform between two Galactocentric frames.
The identity when the two are the same frame; otherwise the simplified composition of the routes through ICRS.
>>> import unxt as u >>> import coordinax.frames as cxf >>> import coordinaxs.astro as cxastro
>>> gcf_frame = cxastro.Galactocentric() >>> frame_op = cxf.frame_transition(gcf_frame, gcf_frame) >>> frame_op Identity()
An equal-but-distinct frame is the same frame too:
>>> cxf.frame_transition(gcf_frame, cxastro.Galactocentric()) Identity()
>>> gcf_frame2 = cxastro.Galactocentric(roll=u.Q(10, "deg")) >>> frame_op2 = cxf.frame_transition(gcf_frame, gcf_frame2) >>> frame_op2 Composed(( Translate( {...}, chart=Cart3D(M=Rn(3)), semantic_kind=vel ), Affine( f64[3,3](jax), {...}, Cart3D(M=Rn(3)), coordinax.transforms._src.groups.EuclideanGroup ), Translate( {...}, chart=Cart3D(M=Rn(3)), semantic_kind=vel ) ))
Seven operators become three: the five spatial ones – three rotations and two translations, interleaved so no pairwise rule could reach them – fuse into a single Affine. The velocity offsets bracket it untouched, acting on the tangent fibre rather than the point.
- coordinax.frame_transition(from_frame: ICRS, to_frame: Galactocentric, /) Composed
Return an ICRS to Galactocentric frame transformation operator.
This transformation applies a series of Galilean transformations to convert coordinates from the ICRS frame to a Galactocentric frame. The transformation accounts for:
Rotation to align with the Galactic coordinate system
Translation to the Galactic center
Tilt correction for the Sun’s height above the Galactic plane
Velocity boost to the Galactocentric rest frame
Notes
The transformation is composed of:
R: Combined rotation matrix (longitude x latitude x roll)
offset_q: Translation by the Galactic center distance
H: Rotation to account for Sun’s height above plane
offset_v: Solar-velocity kick to the Galactocentric rest frame (a fibre-only
Translate(semantic_kind=vel))
The default Galactocentric frame uses parameters from the Astropy default values (as of v4.0), which are based on various literature sources.
Examples
>>> import unxt as u >>> import coordinax as cx >>> import coordinaxs.astro as cxastro
Create the frames:
>>> icrs_frame = cxastro.ICRS() >>> gcf_frame = cxastro.Galactocentric()
Define the transformation operator:
>>> frame_op = cx.frame_transition(icrs_frame, gcf_frame) >>> frame_op Composed(( Rotate(f64[3,3](jax)), Translate( {...}, chart=Cart3D(M=Rn(3)) ), Rotate(f64[3,3](jax)), Translate( {...}, chart=Cart3D(M=Rn(3)), semantic_kind=vel ) ))
Transform a position at the origin of ICRS to Galactocentric:
>>> q = cx.Point.from_([0, 0, 0], "pc") >>> print(frame_op(q)) <Point: chart=Cart3D (x, y, z) [pc] [-8121.973 0. 20.8 ]>
The result shows the Sun’s position in Galactocentric coordinates: the Sun is about 8.1 kpc from the Galactic center along the x-axis and about 21 pc above the Galactic plane.
Velocities are transformed too: a star at rest in ICRS moves with the solar velocity in the Galactocentric frame:
>>> point = cx.Point.from_([0, 0, 0], "pc") >>> vel = cx.Tangent.from_([0.0, 0.0, 0.0], "km/s") >>> pv = cx.Coordinate(point=point, velocity=vel) >>> out = frame_op(None, pv) >>> print(out["velocity"]) <Tangent: chart=Cart3D (x, y, z) [km / s] [ 12.9 245.6 7.78]>
Works with unxt Quantities too:
>>> q = u.Q([0, 0, 0], "pc") >>> frame_op(q) Q([-8121.97336612, 0. , 20.8 ], 'pc')
Transform a star position in spherical coordinates:
>>> star_q = cx.Point.from_( ... {"lon": u.Q(279.23, "deg"), "lat": u.Q(38.78, "deg"), ... "distance": u.Q(25, "pc")}, cx.lonlat_sph3d) >>> gcf_q = frame_op(star_q) >>> gcf_q = gcf_q.cconvert(cx.cart3d) >>> print(gcf_q) <Point: chart=Cart3D (x, y, z) [pc] [-8112.898 21.798 29.015]>
- coordinax.frame_transition(from_frame: Galactocentric, to_frame: ICRS, /) AbstractTransform
Return a Galactocentric to ICRS frame transformation operator.
This transformation inverts the ICRS→Galactocentric transformation, converting coordinates from a Galactocentric frame back to ICRS.
Examples
>>> import unxt as u >>> import coordinax as cx >>> import coordinaxs.astro as cxastro
Create the frames:
>>> icrs_frame = cxastro.ICRS() >>> gcf_frame = cxastro.Galactocentric()
Define the transformation operator:
>>> frame_op = cx.frame_transition(gcf_frame, icrs_frame)
Transform from Galactocentric origin to ICRS:
>>> q = cx.Point.from_([0, 0, 0], "pc") >>> print(frame_op(q).round(0)) <Point: chart=Cart3D (x, y, z) [pc] [ -446. -7094. -3930.]>
This shows the Galactic center’s position in ICRS coordinates from the Sun’s perspective.
Works with unxt Quantities:
>>> q = u.Q([0, 0, 0], "pc") >>> frame_op(q).round(0) Q([ -446., -7094., -3930.], 'pc')
Transform a star in Galactocentric coordinates back to ICRS:
>>> star_q = cx.Point.from_([-8112.9, 21.8, 29.0], "pc") >>> icrs_q = frame_op(star_q) >>> icrs_q = icrs_q.cconvert(cx.lonlat_sph3d) >>> print(icrs_q.uconvert({u.dimension("angle"): "deg", u.dimension("length"): "pc"})) <Point: chart=LonLatSpherical3D (lon[deg], lat[deg], distance[pc]) [-80.728 38.775 24.996]>
Notes
This transformation is implemented by computing the inverse of the ICRS→Galactocentric transformation. The operator pipeline is simplified automatically for computational efficiency.
- coordinax.frame_transition(from_frame: AbstractReferenceFrame, to_frame: AbstractParallelTransportFrame) AbstractTransform
Return the composite transform operator to a curve frame.
Composes the transition from
from_frameto the curve frame’sbase_framewith the curve-frame’s forward transformxop:\[\mathcal{A} \to \mathcal{F}_\gamma = (\mathcal{A} \to \mathcal{B}) \circ (\mathcal{B} \to \mathcal{F}_\gamma)\]where \(\mathcal{B}\) is
to_frame.base_frame.- Parameters:
from_frame (AbstractReferenceFrame) – The source (ambient) frame.
to_frame (AbstractParallelTransportFrame) – The target curve-attached frame.
args (Any)
kwargs (Any)
- Returns:
The composed frame-transition operator.
- Return type:
Examples
>>> import jax.numpy as jnp >>> import unxt as u >>> import coordinax.frames as cxf >>> import coordinax.transforms as cxfm >>> import coordinaxs.curveframes as cxfc
>>> def circle(tau): ... t = tau.ustrip("s") ... return u.Q(jnp.stack([jnp.cos(t), jnp.sin(t), ... jnp.zeros_like(t)]), "km")
>>> fs_frame = cxfc.FrenetSerretFrame.from_curve(cxf.Alice(), circle, "s") >>> op = cxf.frame_transition(cxf.Alice(), fs_frame) >>> isinstance(op, cxfm.AbstractTransform) True
- coordinax.frame_transition(from_frame: AbstractParallelTransportFrame, to_frame: AbstractReferenceFrame) AbstractTransform
Return the composite transform operator from a curve frame.
Composes the inverse curve-frame transform
xop_invwith the transition from the base frame toto_frame:\[\mathcal{F}_\gamma \to \mathcal{A} = (\mathcal{F}_\gamma \to \mathcal{B}) \circ (\mathcal{B} \to \mathcal{A})\]Examples
>>> import jax.numpy as jnp >>> import unxt as u >>> import coordinax.frames as cxf >>> import coordinax.transforms as cxfm >>> import coordinaxs.curveframes as cxfc
>>> def circle(tau): ... t = tau.ustrip("s") ... return u.Q(jnp.stack([jnp.cos(t), jnp.sin(t), ... jnp.zeros_like(t)]), "km")
>>> fs_frame = cxfc.FrenetSerretFrame.from_curve(cxf.Alice(), circle, "s") >>> op = cxf.frame_transition(fs_frame, cxf.Alice()) >>> isinstance(op, cxfm.AbstractTransform) True
- coordinax.frame_transition(from_frame: AbstractParallelTransportFrame, to_frame: AbstractParallelTransportFrame) AbstractTransform
Return the composite transform operator between two curve frames.
When both source and target are curve frames, the transition composes three stages:
\[\mathcal{F}_1 \to \mathcal{F}_2 = (\mathcal{F}_1 \to \mathcal{B}_1) \circ (\mathcal{B}_1 \to \mathcal{B}_2) \circ (\mathcal{B}_2 \to \mathcal{F}_2)\]This dispatch has
precedence=1so it takes priority over the more general to-curve-frame and from-curve-frame dispatches when both arguments areAbstractParallelTransportFrame.Examples
>>> import jax.numpy as jnp >>> import unxt as u >>> import coordinax.frames as cxf >>> import coordinax.transforms as cxfm >>> import coordinaxs.curveframes as cxfc
>>> def circle(tau): ... t = tau.ustrip("s") ... return u.Q(jnp.stack([jnp.cos(t), jnp.sin(t), ... jnp.zeros_like(t)]), "km")
>>> fs1 = cxfc.FrenetSerretFrame.from_curve(cxf.Alice(), circle, "s") >>> fs2 = cxfc.FrenetSerretFrame.from_curve(cxf.Alex(), circle, "s") >>> op = cxf.frame_transition(fs1, fs2) >>> isinstance(op, cxfm.AbstractTransform) True
- coordinax.simplify(*args, **kwargs)
Simplify a transform to a canonical form.
This function takes a transform and attempts to simplify it, returning a new, potentially simpler transform. For example, a
Translatewith zero delta simplifies toIdentity.Notes
In general this cannot be called in a JIT’ed context because it generally requires inspecting values to determine if simplifications are possible.
This function uses multiple dispatch. Each operator type registers its own simplification rules.
To see all available dispatches:
>>> import coordinax.transforms as cxfm >>> cxfm.simplify.methods List of 7 method(s): [0] simplify(...)
Examples
>>> import coordinax.transforms as cxfm
Identity (already simple):
>>> op = cxfm.Identity() >>> cxfm.simplify(op) is op True
Translate with zero delta:
>>> op = cxfm.Translate.from_([0, 0, 0], "m") >>> cxfm.simplify(op) Identity()
Translate with non-zero delta (no simplification):
>>> op = cxfm.Translate.from_([1, 2, 3], "m") >>> simplified = cxfm.simplify(op) >>> type(simplified).__name__ 'Translate'
Rotate with identity matrix:
>>> import unxt as u >>> op = cxfm.Rotate.from_euler("z", u.Q(0, "deg")) >>> cxfm.simplify(op) Identity()
Simplify a {class}`coordinax.transforms.Identity` operator.
The coordinax.transforms.Identity operator is the simplest operator and cannot be simplified further:
>>> import coordinax.transforms as cxfm
>>> op = cxfm.identity >>> cxfm.simplify(op) is op True
- coordinax.simplify(op: Composed, /, *, approx: bool = True, **kw: Any) AbstractTransform
Simplify a Composed transform.
Recursively simplifies each sub-operator, strips Identity elements, and merges adjacent same-type operators in a single left-to-right pass: two rotations collapse to one (
R1 @ R2), two same-role static translations to one, and — via the merge plus per-operator identity detection — an inverse pair cancels to Identity.With
approx=True(default) the value-inspecting per-operator collapses run too (an identity rotation matrix, a zero translation). Withapprox=Falseonly value-free structural simplification is done, which is always safe to call insidejax.jit.>>> import unxt as u >>> import coordinax.transforms as cxfm
Identity elements are stripped:
>>> shift = cxfm.Translate.from_([1, 2, 3], "km") >>> cxfm.simplify(cxfm.Composed((shift, cxfm.Identity()))) Translate(...)
Adjacent rotations merge into one:
>>> R1 = cxfm.Rotate.from_euler("z", u.Q(30, "deg")) >>> R2 = cxfm.Rotate.from_euler("z", u.Q(60, "deg")) >>> cxfm.simplify(cxfm.Composed((R1, R2))) Rotate(...)
An inverse pair cancels to Identity:
>>> cxfm.simplify(cxfm.Composed((R1, R1.inverse))) Identity()
- coordinax.simplify(op: TimeDep, /, *, approx: bool = True, **kw: Any) AbstractTransform
Pass a TimeDep through unchanged (its value is unknown until tau).
Examples
>>> import jax.numpy as jnp >>> import unxt as u >>> import coordinax.transforms as cxfm
>>> zhat = jnp.array([0.0, 0.0, 1.0]) >>> op = cxfm.TimeDep(cxfm.builders.RotationAboutAxis(u.Q(1.0, "rad/s"), axis=zhat)) >>> cxfm.simplify(op) is op True
- coordinax.simplify(op: coordinax.transforms._src.actions.add.AbstractAdd, /, *, approx: bool = True, **kw: Any) coordinax.transforms._src.actions.add.AbstractAdd | Identity
Simplify a AbstractAdd operator.
A translation with zero delta simplifies to Identity. This is a value-inspecting rule, so it is skipped when
approx=False, and when any leaf ofdeltais traced – under jax.jit the values are not known, which is exactly when the answer is “do not simplify”.>>> import coordinax.transforms as cxfm
>>> op = cxfm.Translate.from_([1, 2, 3], "km") >>> cxfm.simplify(op) Translate(...)
>>> op = cxfm.Translate.from_([0, 0, 0], "km") >>> cxfm.simplify(op) Identity()
- coordinax.simplify(op: Affine, /, *, approx: bool = True, **kw: Any) AbstractTransform
Collapse to Identity when the map is one; otherwise keep the fusion.
Both checks inspect values, so both are skipped when
approx=False, and whenAorbis traced – the same trace-safety contract the sibling operators honour.- coordinax.simplify(op: Rotate, /, *, approx: bool = True, **kw: Any) AbstractTransform
Simplify the Galilean rotation operator.
>>> import quaxed.numpy as jnp >>> import coordinax as cx
An operator with a non-identity rotation matrix is not simplified:
>>> Rz = jnp.asarray([[0, -1, 0], [1, 0, 0], [0, 0, 1]]) >>> op = cxfm.Rotate(Rz) >>> cxfm.simplify(op) Rotate(i64[3,3](jax))
An operator with an identity rotation matrix is simplified:
>>> op = cxfm.Rotate(jnp.eye(3)) >>> cxfm.simplify(op) Identity()
When two rotations are combined that cancel each other out, the result simplifies to an {class}`coordinax.ops.Identity`:
>>> op = ( cxfm.Rotate.from_euler("z", u.Q(45, "deg")) ... @ cxfm.Rotate.from_euler("z", u.Q(-45, "deg"))) >>> cxfm.simplify(op) Identity()
The identity check inspects values, so it is skipped when
approx=False, and when the matrix is traced – under jax.jit the values are not known, which is exactly when the answer is “do not simplify” rather than an error.- coordinax.simplify(op: Linear, /, *, approx: bool = True, **kw: Any) AbstractTransform
Simplify a general linear transform to identity when its matrix is one.
The identity-matrix check inspects values, so it is skipped when
approx=False, and when the matrix is traced – under jax.jit the values are not known, which is exactly when the answer is “do not simplify” rather than an error.- coordinax.simplify(op: LorentzBoost, /, *, approx: bool = True, **kw: Any) AbstractTransform
Simplify a Lorentz boost to identity when its velocity is zero.
Every other transform has had this rule; LorentzBoost did not, and simplify dispatches per operator with no generic fallback – so
simplifyof a boost, or of any ~coordinax.transforms.Composed containing one, raised NotFoundLookupError rather than returning the operator unchanged.The zero-velocity check inspects values, so it is skipped when
approx=False, and whenbetais traced; the point of the rule is that those paths returnopinstead of raising.Examples
>>> import coordinax.transforms as cxfm
A boost with velocity is left alone:
>>> cxfm.simplify(cxfm.LorentzBoost([0.6, 0.0, 0.0])) LorentzBoost(...)
A zero boost is the identity:
>>> cxfm.simplify(cxfm.LorentzBoost([0.0, 0.0, 0.0])) Identity()
- coordinax.simplify(op: Reflect, /, *, approx: bool = True, **kw: Any) AbstractTransform
Simplify a reflection, collapsing the identity matrix when present.
The identity-matrix check inspects values, so it is skipped when
approx=False, and when the matrix is traced – under jax.jit the values are not known, which is exactly when the answer is “do not simplify” rather than an error.- coordinax.simplify(op: Scale, /, *, approx: bool = True, **kw: Any) AbstractTransform
Simplify a scaling transform to identity when matrix is identity.
The identity-matrix check inspects values, so it is skipped when
approx=False, and when the factor is traced – under jax.jit the values are not known, which is exactly when the answer is “do not simplify” rather than an error.- coordinax.simplify(op: Shear, /, *, approx: bool = True, **kw: Any) AbstractTransform
Simplify a shear transform to identity when matrix is identity.
The identity-matrix check inspects values, so it is skipped when
approx=False, and when the matrix is traced – under jax.jit the values are not known, which is exactly when the answer is “do not simplify” rather than an error.
- final class coordinax.Composed(transforms: Any)
Bases:
AbstractTransform,Generic[Unpack[Ts]]Composition of Transforms.
Piping refers to a process in which the output of one operation is directly passed as the input to another. This is a composite operator that represents a sequence of operations to be applied in order.
Composed transformations can be created using the ‘pipe’ syntax op1 | op2. A Composed transformation created as
FG = F | G, when evaluated, is equivalent to evaluating \(g \circ f = g(f(x))\). Note the order of the transformations!Note
The
|operator works differently from the functional composition operator \(\circ\), which is sadly not supported in Python. The|operator is like the Unix Shell pipe operator, where output is passed left-to-right. This order can be seen in the indexing of the transformations in the Composed object.- Parameters:
transforms (
Any) – The sequence of transformations to apply.
Examples
>>> import coordinax.transforms as cxfm
>>> shift = cxfm.Translate.from_([1, 2, 3], "km") >>> rotate = cxfm.Rotate.from_euler("z", u.Q(90, "deg")) >>> pipe = cxfm.Composed((shift, rotate)) >>> pipe Composed(( Translate(...), Rotate(...) ))
A pipe can also be constructed by
|:>>> pipe2 = shift | rotate >>> pipe2 Composed(( Translate(...), Rotate(...) ))
The pipe can be simplified. Adding an identity operator shows it being stripped; the remaining translation and rotation are both affine, so they then fuse into a single ~coordinax.transforms.Affine kernel.
>>> pipe3 = pipe2 | cxfm.Identity() >>> pipe3 Composed(( Translate(...), Rotate(...), Identity() ))
>>> cxfm.simplify(pipe3) Affine(...)
- property inverse: Composed
The inverse of the operator.
This is the sequence of the inverse of each operator in reverse order.
Examples
>>> import coordinax.transforms as cxfm >>> import unxt as u
>>> shift = cxfm.Translate.from_([1, 2, 3], "km") >>> rotate = cxfm.Rotate.from_euler("z", u.Q(90, "deg")) >>> pipe = cxfm.Composed((shift, rotate)) >>> pipe.inverse Composed((...))
- property is_time_dependent: bool
Whether any component transform is time-dependent.
Examples
>>> import jax.numpy as jnp >>> import coordinax.transforms as cxfm
>>> shift = cxfm.Translate.from_([1, 2, 3], "km") >>> pipe = cxfm.Composed((shift, cxfm.Identity())) >>> pipe.is_time_dependent False
A TimeDep component makes the whole composite time-dependent:
>>> moving = cxfm.TimeDep.from_( ... lambda t: cxfm.Translate.from_(jnp.asarray([1.0, 0.0, 0.0]) * t, "km") ... ) >>> cxfm.Composed((shift, moving)).is_time_dependent True
- groups()
Return the least common supergroup of the component transforms.
- classmethod from_(*args, **kwargs)
Construct from a set of arguments.
- from_(cls: type[AbstractTransform], *args: object, **kwargs: object) AbstractTransform
- Parameters:
- Return type:
Construct from a set of arguments.
This is a low-priority dispatch that will be called if no other dispatch is found. It just tries to pass the arguments to the constructor.
- from_(cls: type[AbstractTransform], obj: Mapping[str, Any], /) AbstractTransform
- Parameters:
- Return type:
Construct from a mapping.
>>> import coordinax.transforms as cxfm >>> cxfm.Composed.from_({"transforms": (cxfm.Identity(), cxfm.Identity())}) Composed((Identity(), Identity()))
- from_(cls: type[AbstractTransform], x: ArrayLike | list[float | int], unit: str, /) AbstractTransform
- Parameters:
- Return type:
Construct from a Quantity’s value and unit.
>>> import coordinax.transforms as cxfm >>> op = cxfm.Translate.from_([1, 1, 1], "km") >>> print(op) Translate( {'x': Q(1, 'km'), 'y': Q(1, 'km'), 'z': Q(1, 'km')}, chart=Cart3D(M=Rn(3)) )
- from_(cls: type[AbstractTransform], obj: AbstractTransform, /) AbstractTransform
- Parameters:
- Return type:
Construct an operator from another operator.
- Raises:
TypeError – If the input object is not a subclass of the target class.
- Parameters:
- Return type:
Examples
>>> import coordinax as cx
If the object is the same type, it should return the object itself.
>>> op = cxfm.Identity() >>> cxfm.Identity.from_(op) is op True
If the object is a different type, it will error.
>>> try: ... cxfm.Translate.from_(op) ... except TypeError as e: ... print(e) Cannot construct <class '...Translate'> from <class '...Identity'>.
Unless the object is a subclass of the target class.
>>> class MyOperator(cxfm.Identity): ... pass
>>> op = MyOperator() >>> op MyOperator()
>>> newop = cxfm.Identity.from_(op) >>> newop is op, isinstance(newop, cxfm.Identity) (False, True)
- from_(cls: type[TimeDep], fn: Callable[..., Any], /, *args: Any, **kw: Any) TimeDep
- Parameters:
- Return type:
Build from a
tau -> transformcallable.tauis a call-time argument, never a stored parameter, so a user-defined function’staudependence is fully differentiated by the kinematic-prolongation engine – even for a bare lambda. act on tangent data and act_jet pick up the resultingd/dtauterms automatically.Storage matters only for the builder’s other parameters. With no extra arguments: a bare function/lambda cannot be a pytree leaf, so it is stored STATIC – anything it closes over is a trace-time constant and a fresh closure forces a jit retrace. A callable that is already a pytree (an equinox.Module, notably equinox.Partial) is used as the builder as-is, keeping its leaves dynamic – differentiable and jit-cached. (Wrapping it would set those leaves static, silently destroying them.)
Given extra
*args/**kw, they are bound ontofnwith equinox.Partial, which keeps them as dynamic leaves. ``fn`` must take ``tau`` LAST –fn(param..., tau, **kw)– because equinox.Partial prepends its bound positionals:partial(tau)callsfn(*bound, tau, **kw). Writingfn(tau, param)instead silently passes the parameter as tau. The resulting builder is a equinox.Partial, so the operator needs equinox.filter_jit, not plain jax.jit.Examples
>>> import equinox as eqx >>> import jax >>> import jax.numpy as jnp >>> import unxt as u >>> import coordinax as cx >>> import coordinax.charts as cxc >>> import coordinax.transforms as cxfm
A bare lambda is enough for the
tauderivatives to flow: this drift at 3 km/s, acted on data at rest, transforms the velocity to exactly the drift rate.>>> rate = {"x": u.Q(3.0, "km/s"), "y": u.Q(0.0, "km/s"), "z": u.Q(0.0, "km/s")} >>> drift = cxfm.TimeDep.from_( ... lambda t: cxfm.Translate({k: v * t for k, v in rate.items()}, ... chart=cxc.cart3d) ... )
>>> at_rest = cx.Coordinate( ... point=cx.Point.from_([0.0, 0.0, 0.0], "km"), ... velocity=cx.Tangent.from_([0.0, 0.0, 0.0], "km/s"), ... ) >>> cxfm.act(drift, u.Q(0.0, "s"), at_rest)["velocity"]["x"] Q(3., 'km / s')
Extra arguments are bound as leaves – keeping them differentiable and jit-cached. Note
tcomes last:>>> def scaled(factor, t) -> cxfm.Scale: ... return cxfm.Scale.from_factors(jnp.full(3, factor))
>>> op = cxfm.TimeDep.from_(scaled, jnp.asarray(2.0)) >>> [float(x) for x in jax.tree.leaves(eqx.filter(op, eqx.is_array))] [2.0]
A pre-built equinox.Partial is equivalent:
>>> op = cxfm.TimeDep.from_(eqx.Partial(scaled, jnp.asarray(2.0))) >>> [float(x) for x in jax.tree.leaves(eqx.filter(op, eqx.is_array))] [2.0]
- from_(cls: type[coordinax.transforms._src.actions.add.AbstractAdd], obj: coordinax.transforms._src.actions.add.AbstractAdd, /) coordinax.transforms._src.actions.add.AbstractAdd
- Parameters:
- Return type:
Construct a AbstractAdd from another AbstractAdd.
>>> import coordinax as cx >>> shift1 = cxfm.Translate.from_([1, 2, 3], "km") >>> cxfm.Translate.from_(shift1) is shift1 True
- from_(cls: type[coordinax.transforms._src.actions.add.AbstractAdd], q: AbstractQuantity, /) coordinax.transforms._src.actions.add.AbstractAdd
- Parameters:
- Return type:
Construct an AbstractAdd subclass from a Quantity.
>>> import unxt as u >>> import coordinax.transforms as cxfm >>> cxfm.Translate.from_(u.Q([1, 2, 3], "km")) Translate( {'x': Q(1, 'km'), 'y': Q(2, 'km'), 'z': Q(3, 'km')}, chart=Cart3D(M=Rn(3)) )
- from_(cls: type[coordinax.transforms._src.actions.add.AbstractAdd], x: ArrayLike, unit: str) coordinax.transforms._src.actions.add.AbstractAdd
- Parameters:
- Return type:
Construct an Add operator from an array-like offset and unit.
>>> import coordinax.transforms as cxfm >>> cxfm.Translate.from_([1, 2, 3], "km") Translate( {'x': Q(1, 'km'), 'y': Q(2, 'km'), 'z': Q(3, 'km')}, chart=Cart3D(M=Rn(3)) )
Construct a Rotate from another Rotate.
>>> import quaxed.numpy as jnp >>> import coordinax.transforms as cxfm >>> R = cxfm.Rotate(jnp.eye(3)) >>> cxfm.Rotate.from_(R) is R True
- from_(cls: type[Rotate], obj: AbstractQuantity, /) Rotate
- Parameters:
- Return type:
Construct a Rotate from a Quantity.
>>> import jax.numpy as jnp >>> import unxt as u >>> import coordinax.transforms as cxfm >>> cxfm.Rotate.from_(u.Q(jnp.eye(3), "")) Rotate(f64[3,3](jax))
Construct a Rotate from an Array.
>>> import jax.numpy as jnp >>> import coordinax.transforms as cxfm >>> cxfm.Rotate.from_(jnp.eye(3)) Rotate(f64[3,3](jax))
Initialize from a jax.scipy.spatial.transform.Rotation.
>>> import jax.numpy as jnp >>> from jax.scipy.spatial.transform import Rotation >>> import coordinax as cx
>>> R = Rotation.from_euler("z", 90, degrees=True) >>> op = cxfm.Rotate.from_(R)
>>> jnp.allclose(op.R, R.as_matrix()) Array(True, dtype=bool)
Construct a Linear from an array matrix.
Registered on the class’s own dispatcher, as the sibling transforms do. Via the global plum.dispatch the ArrayLike union is unfaithful, which would turn off plum’s method cache for every from_ in the library – caught by test_no_new_unfaithful_signatures.
Construct a Reflect from another Reflect.
- from_(cls: type[Reflect], obj: AbstractQuantity, /) Reflect
- Parameters:
- Return type:
Construct a Reflect from a dimensionless quantity matrix.
Construct a Reflect from an array matrix.
Construct a Scale from another Scale.
- from_(cls: type[Scale], obj: AbstractQuantity, /) Scale
- Parameters:
- Return type:
Construct a Scale from a dimensionless quantity matrix.
Construct a Scale from an array matrix.
Construct a Shear from another Shear.
- from_(cls: type[Shear], obj: AbstractQuantity, /) Shear
- Parameters:
- Return type:
Construct a Shear from a dimensionless quantity matrix.
Construct a Shear from an array matrix.
- Parameters:
cls (
type[AbstractTransform])args (
object)kwargs (
object)
- Return type:
- simplify()
Simplify the operator.
This method calls coordinax.ops.simplify to simplify the operator.
- Return type:
Examples
>>> import unxt as u >>> import coordinax.transforms as cxfm
>>> op = cxfm.Identity() >>> op.simplify() is op True
>>> pipe = cxfm.Composed((cxfm.Identity(), cxfm.Identity())) >>> pipe Composed((Identity(), Identity())) >>> pipe.simplify() Identity()
- final class coordinax.Rotate(R: Any)
Bases:
AbstractLinearTransformOperator for Galilean rotations.
The coordinate transform is given by:
\[(t,\mathbf{x}) \mapsto (t, R \mathbf{x})\]where \(R\) is the rotation matrix. Note this is intrinsically time dependent.
- Parameters:
- Raises:
ValueError – If the rotation matrix is not orthogonal.
Notes
The Galilean rotation is intrinsically a time-dependent transformation. This is part of the inhomogeneous Galilean group, which is the group of transformations that leave the space-time interval invariant.
Examples
We start with the required imports:
>>> import jax.numpy as jnp >>> import unxt as u >>> import coordinax as cx >>> import coordinax.transforms as cxfm
We can then create a rotation operator:
>>> Rz = jnp.asarray([[0, -1, 0], [1, 0, 0], [0, 0, 1]]) >>> op = cxfm.Rotate(Rz) >>> op Rotate(i64[3,3](jax))
Rotation operators can be applied to {class}`~coordinax.Point` and other higher-level objects, with behavior depending on the role:
>>> v = cx.Point.from_([1, 0, 0], "m") # A cxr.Point vector >>> t = u.Q(1, "s")
>>> print(op(t, v)) # equivalent to `cx.act(op, t, v)` <Point: chart=Cart3D (x, y, z) [m] [0 1 0]>
This also works for a batch of vectors (as a note, it is more efficient to jax.vmap over the jax.jit-ed operator):
>>> v = cx.Point.from_([[1, 0, 0], [0, 1, 0]], "m") # A Point vector >>> print(op(t, v)) <Point: chart=Cart3D (x, y, z) [m] [[ 0 1 0] [-1 0 0]]>
Rotations can also be applied to low-level coordinate dictionaries:
>>> q = {"x": u.Q(1, "m"), "y": u.Q(0, "m"), "z": u.Q(0, "m")} >>> nq = op(t, q) # inferred chart & rep -> cxr.Point >>> nq {'x': Q(0, 'm'), 'y': Q(1, 'm'), 'z': Q(0, 'm')}
In addition to the standard low-level objects, Rotation operators can be applied to {class}`~unxt.Quantity` and Array-like objects, taken to represent a Cartesian vectors. For Quantity, the role is inferred from the units, while Arrays are always points:
>>> q = u.Q([1, 0, 0], "m") >>> t = u.Q(1, "s") >>> op(t, q) Q([0, 1, 0], 'm')
This also works for a batch of vectors:
>>> q = u.Q([[1, 0, 0], [0, 1, 0]], "m") >>> op(t, q) Q([[ 0, 1, 0], [-1, 0, 0]], 'm')
Ris always a constant matrix. A time-dependent rotation is a ~coordinax.transforms.TimeDep family of Rotate operators — e.g. built by ~coordinax.transforms.builders.RotationAboutAxis:>>> zhat = jnp.array([0.0, 0.0, 1.0]) >>> b = cxfm.builders.RotationAboutAxis(u.Q(45, "deg/s"), axis=zhat) >>> R_op = cxfm.TimeDep(b)
>>> t = u.Q(4, "s") # 180 degrees rotation >>> R_op(t, q).round(3) Q([[-1., 0., 0.], [-0., -1., 0.]], 'm')
- Parameters:
R (
Any)
- R: Shaped[Array, 'N N']
The rotation matrix.
- classmethod from_euler(seq: str, angles: Quantity | Angle, /)
Initialize from Euler angles.
See jax.scipy.spatial.transform.Rotation.from_euler. XYZ are intrinsic rotations, xyz are extrinsic rotations.
Examples
>>> import unxt as u >>> import coordinax as cx
>>> op = cxfm.Rotate.from_euler("z", u.Q(90, "deg")) >>> op.R.round(2) Array([[ 0., -1., 0.], [ 1., 0., 0.], [ 0., 0., 1.]], dtype=float64)
- property inverse: Rotate
The inverse of the operator.
Examples
>>> import quaxed.numpy as jnp >>> import coordinax as cx
>>> Rz = jnp.asarray([[0, -1, 0], [1, 0, 0], [0, 0, 1]]) >>> op = cxfm.Rotate(Rz) >>> op.inverse Rotate(i64[3,3](jax))
>>> jnp.allclose(op.R, op.inverse.R.T) Array(True, dtype=bool)
- classmethod from_(*args, **kwargs)
Construct from a set of arguments.
- from_(cls: type[AbstractTransform], *args: object, **kwargs: object) AbstractTransform
- Parameters:
- Return type:
Construct from a set of arguments.
This is a low-priority dispatch that will be called if no other dispatch is found. It just tries to pass the arguments to the constructor.
- from_(cls: type[AbstractTransform], obj: Mapping[str, Any], /) AbstractTransform
- Parameters:
- Return type:
Construct from a mapping.
>>> import coordinax.transforms as cxfm >>> cxfm.Composed.from_({"transforms": (cxfm.Identity(), cxfm.Identity())}) Composed((Identity(), Identity()))
- from_(cls: type[AbstractTransform], x: ArrayLike | list[float | int], unit: str, /) AbstractTransform
- Parameters:
- Return type:
Construct from a Quantity’s value and unit.
>>> import coordinax.transforms as cxfm >>> op = cxfm.Translate.from_([1, 1, 1], "km") >>> print(op) Translate( {'x': Q(1, 'km'), 'y': Q(1, 'km'), 'z': Q(1, 'km')}, chart=Cart3D(M=Rn(3)) )
- from_(cls: type[AbstractTransform], obj: AbstractTransform, /) AbstractTransform
- Parameters:
- Return type:
Construct an operator from another operator.
- Raises:
TypeError – If the input object is not a subclass of the target class.
- Parameters:
- Return type:
Examples
>>> import coordinax as cx
If the object is the same type, it should return the object itself.
>>> op = cxfm.Identity() >>> cxfm.Identity.from_(op) is op True
If the object is a different type, it will error.
>>> try: ... cxfm.Translate.from_(op) ... except TypeError as e: ... print(e) Cannot construct <class '...Translate'> from <class '...Identity'>.
Unless the object is a subclass of the target class.
>>> class MyOperator(cxfm.Identity): ... pass
>>> op = MyOperator() >>> op MyOperator()
>>> newop = cxfm.Identity.from_(op) >>> newop is op, isinstance(newop, cxfm.Identity) (False, True)
- from_(cls: type[TimeDep], fn: Callable[..., Any], /, *args: Any, **kw: Any) TimeDep
- Parameters:
- Return type:
Build from a
tau -> transformcallable.tauis a call-time argument, never a stored parameter, so a user-defined function’staudependence is fully differentiated by the kinematic-prolongation engine – even for a bare lambda. act on tangent data and act_jet pick up the resultingd/dtauterms automatically.Storage matters only for the builder’s other parameters. With no extra arguments: a bare function/lambda cannot be a pytree leaf, so it is stored STATIC – anything it closes over is a trace-time constant and a fresh closure forces a jit retrace. A callable that is already a pytree (an equinox.Module, notably equinox.Partial) is used as the builder as-is, keeping its leaves dynamic – differentiable and jit-cached. (Wrapping it would set those leaves static, silently destroying them.)
Given extra
*args/**kw, they are bound ontofnwith equinox.Partial, which keeps them as dynamic leaves. ``fn`` must take ``tau`` LAST –fn(param..., tau, **kw)– because equinox.Partial prepends its bound positionals:partial(tau)callsfn(*bound, tau, **kw). Writingfn(tau, param)instead silently passes the parameter as tau. The resulting builder is a equinox.Partial, so the operator needs equinox.filter_jit, not plain jax.jit.Examples
>>> import equinox as eqx >>> import jax >>> import jax.numpy as jnp >>> import unxt as u >>> import coordinax as cx >>> import coordinax.charts as cxc >>> import coordinax.transforms as cxfm
A bare lambda is enough for the
tauderivatives to flow: this drift at 3 km/s, acted on data at rest, transforms the velocity to exactly the drift rate.>>> rate = {"x": u.Q(3.0, "km/s"), "y": u.Q(0.0, "km/s"), "z": u.Q(0.0, "km/s")} >>> drift = cxfm.TimeDep.from_( ... lambda t: cxfm.Translate({k: v * t for k, v in rate.items()}, ... chart=cxc.cart3d) ... )
>>> at_rest = cx.Coordinate( ... point=cx.Point.from_([0.0, 0.0, 0.0], "km"), ... velocity=cx.Tangent.from_([0.0, 0.0, 0.0], "km/s"), ... ) >>> cxfm.act(drift, u.Q(0.0, "s"), at_rest)["velocity"]["x"] Q(3., 'km / s')
Extra arguments are bound as leaves – keeping them differentiable and jit-cached. Note
tcomes last:>>> def scaled(factor, t) -> cxfm.Scale: ... return cxfm.Scale.from_factors(jnp.full(3, factor))
>>> op = cxfm.TimeDep.from_(scaled, jnp.asarray(2.0)) >>> [float(x) for x in jax.tree.leaves(eqx.filter(op, eqx.is_array))] [2.0]
A pre-built equinox.Partial is equivalent:
>>> op = cxfm.TimeDep.from_(eqx.Partial(scaled, jnp.asarray(2.0))) >>> [float(x) for x in jax.tree.leaves(eqx.filter(op, eqx.is_array))] [2.0]
- from_(cls: type[coordinax.transforms._src.actions.add.AbstractAdd], obj: coordinax.transforms._src.actions.add.AbstractAdd, /) coordinax.transforms._src.actions.add.AbstractAdd
- Parameters:
- Return type:
Construct a AbstractAdd from another AbstractAdd.
>>> import coordinax as cx >>> shift1 = cxfm.Translate.from_([1, 2, 3], "km") >>> cxfm.Translate.from_(shift1) is shift1 True
- from_(cls: type[coordinax.transforms._src.actions.add.AbstractAdd], q: AbstractQuantity, /) coordinax.transforms._src.actions.add.AbstractAdd
- Parameters:
- Return type:
Construct an AbstractAdd subclass from a Quantity.
>>> import unxt as u >>> import coordinax.transforms as cxfm >>> cxfm.Translate.from_(u.Q([1, 2, 3], "km")) Translate( {'x': Q(1, 'km'), 'y': Q(2, 'km'), 'z': Q(3, 'km')}, chart=Cart3D(M=Rn(3)) )
- from_(cls: type[coordinax.transforms._src.actions.add.AbstractAdd], x: ArrayLike, unit: str) coordinax.transforms._src.actions.add.AbstractAdd
- Parameters:
- Return type:
Construct an Add operator from an array-like offset and unit.
>>> import coordinax.transforms as cxfm >>> cxfm.Translate.from_([1, 2, 3], "km") Translate( {'x': Q(1, 'km'), 'y': Q(2, 'km'), 'z': Q(3, 'km')}, chart=Cart3D(M=Rn(3)) )
Construct a Rotate from another Rotate.
>>> import quaxed.numpy as jnp >>> import coordinax.transforms as cxfm >>> R = cxfm.Rotate(jnp.eye(3)) >>> cxfm.Rotate.from_(R) is R True
- from_(cls: type[Rotate], obj: AbstractQuantity, /) Rotate
- Parameters:
- Return type:
Construct a Rotate from a Quantity.
>>> import jax.numpy as jnp >>> import unxt as u >>> import coordinax.transforms as cxfm >>> cxfm.Rotate.from_(u.Q(jnp.eye(3), "")) Rotate(f64[3,3](jax))
Construct a Rotate from an Array.
>>> import jax.numpy as jnp >>> import coordinax.transforms as cxfm >>> cxfm.Rotate.from_(jnp.eye(3)) Rotate(f64[3,3](jax))
Initialize from a jax.scipy.spatial.transform.Rotation.
>>> import jax.numpy as jnp >>> from jax.scipy.spatial.transform import Rotation >>> import coordinax as cx
>>> R = Rotation.from_euler("z", 90, degrees=True) >>> op = cxfm.Rotate.from_(R)
>>> jnp.allclose(op.R, R.as_matrix()) Array(True, dtype=bool)
Construct a Linear from an array matrix.
Registered on the class’s own dispatcher, as the sibling transforms do. Via the global plum.dispatch the ArrayLike union is unfaithful, which would turn off plum’s method cache for every from_ in the library – caught by test_no_new_unfaithful_signatures.
Construct a Reflect from another Reflect.
- from_(cls: type[Reflect], obj: AbstractQuantity, /) Reflect
- Parameters:
- Return type:
Construct a Reflect from a dimensionless quantity matrix.
Construct a Reflect from an array matrix.
Construct a Scale from another Scale.
- from_(cls: type[Scale], obj: AbstractQuantity, /) Scale
- Parameters:
- Return type:
Construct a Scale from a dimensionless quantity matrix.
Construct a Scale from an array matrix.
Construct a Shear from another Shear.
- from_(cls: type[Shear], obj: AbstractQuantity, /) Shear
- Parameters:
- Return type:
Construct a Shear from a dimensionless quantity matrix.
Construct a Shear from an array matrix.
- Parameters:
cls (
type[AbstractTransform])args (
object)kwargs (
object)
- Return type:
- property is_time_dependent: bool
Whether the point action depends on the time parameter tau.
A declared trait, not a structural scan: subclasses whose point action varies with tau (
TimeDep,Boost) override this to True; composites (Composed) override it to the disjunction of their components. The default is False.Examples
>>> import coordinax.transforms as cxfm
>>> cxfm.Translate.from_([1, 2, 3], "km").is_time_dependent False
- property matrix: Array
The matrix \(M\) this transform applies, as \(x \mapsto M x\).
The one spelling shared by every subclass, which otherwise keep \(M\) under their own letter (
R,H,S) or, for ~coordinax.transforms.LorentzBoost, derive it from a stored velocity.Validated square, but not against a chart – a chart is what fixes the dimension and this takes none, so the
actpaths keep that check.Examples
>>> import quaxed.numpy as jnp >>> import coordinax.transforms as cxfm
For a stored matrix it is the field under a common name:
>>> Rz = jnp.asarray([[0, -1, 0], [1, 0, 0], [0, 0, 1]]) >>> bool(jnp.array_equal(cxfm.Rotate(Rz).matrix, Rz)) True
For a derived one it is the only way to see it:
>>> cxfm.LorentzBoost([0.6, 0.0, 0.0]).matrix.round(2) Array([[1.25, 0.75, 0. , 0. ], [0.75, 1.25, 0. , 0. ], [0. , 0. , 1. , 0. ], [0. , 0. , 0. , 1. ]], dtype=float64)
- simplify()
Simplify the operator.
This method calls coordinax.ops.simplify to simplify the operator.
- Return type:
Examples
>>> import unxt as u >>> import coordinax.transforms as cxfm
>>> op = cxfm.Identity() >>> op.simplify() is op True
>>> pipe = cxfm.Composed((cxfm.Identity(), cxfm.Identity())) >>> pipe Composed((Identity(), Identity())) >>> pipe.simplify() Identity()
- final class coordinax.Translate(delta: dict, chart: AbstractChart, semantic_kind: AbstractTangentSemanticKind = dpl, *, right_add: bool = True)
Bases:
AbstractAddOperator for translating points.
A Translate operator represents addition of a constant displacement \(\Delta\) in the ambient Euclidean space (or in a chart whose metric is Euclidean and whose canonical Cartesian chart exists).
Think of \(\Delta\) as a displacement vector field that is constant in space and time (unless explicitly time-dependent).
Formally, in a Cartesian chart on \(\mathbb{R}^n\): \(T_\Delta:\; x \mapsto x+\Delta\).
In that flat setting its differential (pushforward) is the identity, \((dT_\Delta)_x = I\). When
deltalives in a non-flat chart, or acts on data in a different or non-flat chart, the point action is base-point dependent and the differential is NOT the identity — see the Notes.- Parameters:
delta (
dict) – The offset to apply. Its physical dimension followssemantic_kind: length for the default displacement kind (dpl), speed for a velocity kick (vel), and so on up the time-derivative ladder.chart (AbstractChart)
semantic_kind (AbstractTangentSemanticKind)
right_add (bool)
Notes
The
semantic_kindfield sets the ladder order \(k\) of the offset (dpl: \(k=0\),vel: \(k=1\), …). Acting on data of ladder order \(m\) (points behave as the curve position for \(k=0\)):\(k = 0\) with
deltain a Cartesian-type (flat) chart: shifts points by \(\delta\) and leaves every tangent order unchanged (the Jacobian of a flat translation is the identity, and a constant \(\delta\) has no \(\tau\)-derivatives).\(k = 0\) with
deltain a non-flat chart: the point action pushesdeltathrough the chart Jacobian at the point, so it is base-point dependent. All tangent data — including displacements — transforms by the generic pushforward/prolongation of the point action, which is generally not the identity and requires the base point (at=, or a ~coordinax.Coordinate bundle).\(k \geq 1\): identity on points and on all orders \(m \neq k\); order \(m = k\) gains \(\delta\). The componentwise rule is definitional (the point action is the identity), independent of chart flatness.
A time-dependent translation is a ~coordinax.transforms.TimeDep family of Translate operators (e.g. built by ~coordinax.transforms.builders.UniformTranslation); its \(d^{m-k}\delta/d\tau^{m-k}\) prolongation terms are recovered by the generic tangent funnel, which differentiates the point action.
Examples
>>> import unxt as u >>> import coordinax as cx >>> import coordinax.transforms as cxfm
Create a translation operator:
>>> shift = cxfm.Translate.from_([1, 2, 3], "km") >>> shift Translate( {'x': Q(1, 'km'), 'y': Q(2, 'km'), 'z': Q(3, 'km')}, chart=Cart3D(M=Rn(3)) )
The inverse negates the displacement:
>>> shift.inverse Translate( {'x': Q(-1, 'km'), 'y': Q(-2, 'km'), 'z': Q(-3, 'km')}, chart=Cart3D(M=Rn(3)) )
Time-dependent translation — a ~coordinax.transforms.TimeDep family:
>>> rate = {"x": u.Q(1.0, "m/s"), "y": u.Q(0.0, "m/s"), "z": u.Q(0.0, "m/s")} >>> moving = cxfm.TimeDep(cxfm.builders.UniformTranslation(rate, chart=cxc.cart3d))
>>> t = u.Q(10, "s") >>> x = cx.cdict(u.Q([0.0, 0.0, 0.0], "m")) >>> moving(t, x) {'x': Q(10., 'm'), 'y': Q(0., 'm'), 'z': Q(0., 'm')}
- Parameters:
chart (
AbstractChart)semantic_kind (
AbstractTangentSemanticKind)right_add (
bool)delta (dict)
- semantic_kind: AbstractTangentSemanticKind = dpl
Semantic kind of tangent data this operator acts on (default Displacement).
- classmethod from_(*args, **kwargs)
Construct from a set of arguments.
- from_(cls: type[AbstractTransform], *args: object, **kwargs: object) AbstractTransform
- Parameters:
- Return type:
Construct from a set of arguments.
This is a low-priority dispatch that will be called if no other dispatch is found. It just tries to pass the arguments to the constructor.
- from_(cls: type[AbstractTransform], obj: Mapping[str, Any], /) AbstractTransform
- Parameters:
- Return type:
Construct from a mapping.
>>> import coordinax.transforms as cxfm >>> cxfm.Composed.from_({"transforms": (cxfm.Identity(), cxfm.Identity())}) Composed((Identity(), Identity()))
- from_(cls: type[AbstractTransform], x: ArrayLike | list[float | int], unit: str, /) AbstractTransform
- Parameters:
- Return type:
Construct from a Quantity’s value and unit.
>>> import coordinax.transforms as cxfm >>> op = cxfm.Translate.from_([1, 1, 1], "km") >>> print(op) Translate( {'x': Q(1, 'km'), 'y': Q(1, 'km'), 'z': Q(1, 'km')}, chart=Cart3D(M=Rn(3)) )
- from_(cls: type[AbstractTransform], obj: AbstractTransform, /) AbstractTransform
- Parameters:
- Return type:
Construct an operator from another operator.
- Raises:
TypeError – If the input object is not a subclass of the target class.
- Parameters:
- Return type:
Examples
>>> import coordinax as cx
If the object is the same type, it should return the object itself.
>>> op = cxfm.Identity() >>> cxfm.Identity.from_(op) is op True
If the object is a different type, it will error.
>>> try: ... cxfm.Translate.from_(op) ... except TypeError as e: ... print(e) Cannot construct <class '...Translate'> from <class '...Identity'>.
Unless the object is a subclass of the target class.
>>> class MyOperator(cxfm.Identity): ... pass
>>> op = MyOperator() >>> op MyOperator()
>>> newop = cxfm.Identity.from_(op) >>> newop is op, isinstance(newop, cxfm.Identity) (False, True)
- from_(cls: type[TimeDep], fn: Callable[..., Any], /, *args: Any, **kw: Any) TimeDep
- Parameters:
- Return type:
Build from a
tau -> transformcallable.tauis a call-time argument, never a stored parameter, so a user-defined function’staudependence is fully differentiated by the kinematic-prolongation engine – even for a bare lambda. act on tangent data and act_jet pick up the resultingd/dtauterms automatically.Storage matters only for the builder’s other parameters. With no extra arguments: a bare function/lambda cannot be a pytree leaf, so it is stored STATIC – anything it closes over is a trace-time constant and a fresh closure forces a jit retrace. A callable that is already a pytree (an equinox.Module, notably equinox.Partial) is used as the builder as-is, keeping its leaves dynamic – differentiable and jit-cached. (Wrapping it would set those leaves static, silently destroying them.)
Given extra
*args/**kw, they are bound ontofnwith equinox.Partial, which keeps them as dynamic leaves. ``fn`` must take ``tau`` LAST –fn(param..., tau, **kw)– because equinox.Partial prepends its bound positionals:partial(tau)callsfn(*bound, tau, **kw). Writingfn(tau, param)instead silently passes the parameter as tau. The resulting builder is a equinox.Partial, so the operator needs equinox.filter_jit, not plain jax.jit.Examples
>>> import equinox as eqx >>> import jax >>> import jax.numpy as jnp >>> import unxt as u >>> import coordinax as cx >>> import coordinax.charts as cxc >>> import coordinax.transforms as cxfm
A bare lambda is enough for the
tauderivatives to flow: this drift at 3 km/s, acted on data at rest, transforms the velocity to exactly the drift rate.>>> rate = {"x": u.Q(3.0, "km/s"), "y": u.Q(0.0, "km/s"), "z": u.Q(0.0, "km/s")} >>> drift = cxfm.TimeDep.from_( ... lambda t: cxfm.Translate({k: v * t for k, v in rate.items()}, ... chart=cxc.cart3d) ... )
>>> at_rest = cx.Coordinate( ... point=cx.Point.from_([0.0, 0.0, 0.0], "km"), ... velocity=cx.Tangent.from_([0.0, 0.0, 0.0], "km/s"), ... ) >>> cxfm.act(drift, u.Q(0.0, "s"), at_rest)["velocity"]["x"] Q(3., 'km / s')
Extra arguments are bound as leaves – keeping them differentiable and jit-cached. Note
tcomes last:>>> def scaled(factor, t) -> cxfm.Scale: ... return cxfm.Scale.from_factors(jnp.full(3, factor))
>>> op = cxfm.TimeDep.from_(scaled, jnp.asarray(2.0)) >>> [float(x) for x in jax.tree.leaves(eqx.filter(op, eqx.is_array))] [2.0]
A pre-built equinox.Partial is equivalent:
>>> op = cxfm.TimeDep.from_(eqx.Partial(scaled, jnp.asarray(2.0))) >>> [float(x) for x in jax.tree.leaves(eqx.filter(op, eqx.is_array))] [2.0]
- from_(cls: type[coordinax.transforms._src.actions.add.AbstractAdd], obj: coordinax.transforms._src.actions.add.AbstractAdd, /) coordinax.transforms._src.actions.add.AbstractAdd
- Parameters:
- Return type:
Construct a AbstractAdd from another AbstractAdd.
>>> import coordinax as cx >>> shift1 = cxfm.Translate.from_([1, 2, 3], "km") >>> cxfm.Translate.from_(shift1) is shift1 True
- from_(cls: type[coordinax.transforms._src.actions.add.AbstractAdd], q: AbstractQuantity, /) coordinax.transforms._src.actions.add.AbstractAdd
- Parameters:
- Return type:
Construct an AbstractAdd subclass from a Quantity.
>>> import unxt as u >>> import coordinax.transforms as cxfm >>> cxfm.Translate.from_(u.Q([1, 2, 3], "km")) Translate( {'x': Q(1, 'km'), 'y': Q(2, 'km'), 'z': Q(3, 'km')}, chart=Cart3D(M=Rn(3)) )
- from_(cls: type[coordinax.transforms._src.actions.add.AbstractAdd], x: ArrayLike, unit: str) coordinax.transforms._src.actions.add.AbstractAdd
- Parameters:
- Return type:
Construct an Add operator from an array-like offset and unit.
>>> import coordinax.transforms as cxfm >>> cxfm.Translate.from_([1, 2, 3], "km") Translate( {'x': Q(1, 'km'), 'y': Q(2, 'km'), 'z': Q(3, 'km')}, chart=Cart3D(M=Rn(3)) )
Construct a Rotate from another Rotate.
>>> import quaxed.numpy as jnp >>> import coordinax.transforms as cxfm >>> R = cxfm.Rotate(jnp.eye(3)) >>> cxfm.Rotate.from_(R) is R True
- from_(cls: type[Rotate], obj: AbstractQuantity, /) Rotate
- Parameters:
- Return type:
Construct a Rotate from a Quantity.
>>> import jax.numpy as jnp >>> import unxt as u >>> import coordinax.transforms as cxfm >>> cxfm.Rotate.from_(u.Q(jnp.eye(3), "")) Rotate(f64[3,3](jax))
Construct a Rotate from an Array.
>>> import jax.numpy as jnp >>> import coordinax.transforms as cxfm >>> cxfm.Rotate.from_(jnp.eye(3)) Rotate(f64[3,3](jax))
Initialize from a jax.scipy.spatial.transform.Rotation.
>>> import jax.numpy as jnp >>> from jax.scipy.spatial.transform import Rotation >>> import coordinax as cx
>>> R = Rotation.from_euler("z", 90, degrees=True) >>> op = cxfm.Rotate.from_(R)
>>> jnp.allclose(op.R, R.as_matrix()) Array(True, dtype=bool)
Construct a Linear from an array matrix.
Registered on the class’s own dispatcher, as the sibling transforms do. Via the global plum.dispatch the ArrayLike union is unfaithful, which would turn off plum’s method cache for every from_ in the library – caught by test_no_new_unfaithful_signatures.
Construct a Reflect from another Reflect.
- from_(cls: type[Reflect], obj: AbstractQuantity, /) Reflect
- Parameters:
- Return type:
Construct a Reflect from a dimensionless quantity matrix.
Construct a Reflect from an array matrix.
Construct a Scale from another Scale.
- from_(cls: type[Scale], obj: AbstractQuantity, /) Scale
- Parameters:
- Return type:
Construct a Scale from a dimensionless quantity matrix.
Construct a Scale from an array matrix.
Construct a Shear from another Shear.
- from_(cls: type[Shear], obj: AbstractQuantity, /) Shear
- Parameters:
- Return type:
Construct a Shear from a dimensionless quantity matrix.
Construct a Shear from an array matrix.
- Parameters:
cls (
type[AbstractTransform])args (
object)kwargs (
object)
- Return type:
- property inverse: AbstractAdd
The inverse operator (negated offset).
Examples
>>> import coordinax.transforms as cxfm
>>> shift = cxfm.Translate.from_([1, 2, 3], "km") >>> shift.inverse Translate( {'x': Q(-1, 'km'), 'y': Q(-2, 'km'), 'z': Q(-3, 'km')}, chart=Cart3D(M=Rn(3)) )
- property is_time_dependent: bool
Whether the point action depends on the time parameter tau.
A declared trait, not a structural scan: subclasses whose point action varies with tau (
TimeDep,Boost) override this to True; composites (Composed) override it to the disjunction of their components. The default is False.Examples
>>> import coordinax.transforms as cxfm
>>> cxfm.Translate.from_([1, 2, 3], "km").is_time_dependent False
- right_add: bool = True
Whether to add on the right (x + offset) or left (offset + x).
- simplify()
Simplify the operator.
This method calls coordinax.ops.simplify to simplify the operator.
- Return type:
Examples
>>> import unxt as u >>> import coordinax.transforms as cxfm
>>> op = cxfm.Identity() >>> op.simplify() is op True
>>> pipe = cxfm.Composed((cxfm.Identity(), cxfm.Identity())) >>> pipe Composed((Identity(), Identity())) >>> pipe.simplify() Identity()
- delta: dict
The additive offset (displacement for Translate, velocity for Boost).
- chart: AbstractChart
Chart in which the offset is expressed.
- final class coordinax.Scale(S: Any)
Bases:
AbstractLinearTransformOperator for Cartesian linear scaling.
A scaling transform applies
\[x \mapsto Sx,\]where
Sis an invertible diagonal matrix: anisotropic scaling with one factor per axis.Diagonality is the whole content of the type. A Scale holding an off-diagonal matrix would be a general linear map wearing a name that promises otherwise, and isinstance(op, Scale) would stop meaning anything. ~coordinax.transforms.Linear is the type for a general matrix.
Examples
>>> import quaxed.numpy as jnp >>> import coordinax.transforms as cxfm
>>> jnp.diagonal(cxfm.Scale.from_factors(jnp.asarray([2.0, 3.0, 4.0])).matrix) Array([2., 3., 4.], dtype=float64)
A matrix that is already diagonal is accepted:
>>> jnp.diagonal(cxfm.Scale(jnp.diag(jnp.asarray([2.0, 1.0, 0.5]))).matrix) Array([2. , 1. , 0.5], dtype=float64)
One that is not is refused, and points at the type that fits:
>>> try: ... cxfm.Scale(jnp.asarray([[1.0, 0.5], [0.0, 1.0]])).matrix ... except Exception as e: ... print("Scale requires a diagonal matrix" in str(e)) True
- Parameters:
S (
Any)
- s: Shaped[Array, 'N']
The scaling factors – the diagonal of \(S\), one per axis.
The diagonal rather than the whole matrix, so diagonality is structural instead of re-checked: a vector cannot have an off-diagonal entry. The matrix is rebuilt on demand by _raw_matrix, the same way ~coordinax.transforms.LorentzBoost derives its own from a stored velocity.
- classmethod from_factors(factors: Any, /)
Construct a diagonal scaling transform from axis factors.
- property inverse: Scale
Return the inverse scaling transform.
Reciprocals of the factors, not jnp.linalg.inv: O(n) rather than O(n^3), and exact where the general solve is not.
Nothing is re-checked here, and s carries no deferred check of its own: __init__ validates the matrix when it takes the diagonal, so an s that exists has already passed. A malformed input therefore reports from construction – eagerly at the call, under jit when the traced graph runs – never from this property.
- classmethod from_(*args, **kwargs)
Construct from a set of arguments.
- from_(cls: type[AbstractTransform], *args: object, **kwargs: object) AbstractTransform
- Parameters:
- Return type:
Construct from a set of arguments.
This is a low-priority dispatch that will be called if no other dispatch is found. It just tries to pass the arguments to the constructor.
- from_(cls: type[AbstractTransform], obj: Mapping[str, Any], /) AbstractTransform
- Parameters:
- Return type:
Construct from a mapping.
>>> import coordinax.transforms as cxfm >>> cxfm.Composed.from_({"transforms": (cxfm.Identity(), cxfm.Identity())}) Composed((Identity(), Identity()))
- from_(cls: type[AbstractTransform], x: ArrayLike | list[float | int], unit: str, /) AbstractTransform
- Parameters:
- Return type:
Construct from a Quantity’s value and unit.
>>> import coordinax.transforms as cxfm >>> op = cxfm.Translate.from_([1, 1, 1], "km") >>> print(op) Translate( {'x': Q(1, 'km'), 'y': Q(1, 'km'), 'z': Q(1, 'km')}, chart=Cart3D(M=Rn(3)) )
- from_(cls: type[AbstractTransform], obj: AbstractTransform, /) AbstractTransform
- Parameters:
- Return type:
Construct an operator from another operator.
- Raises:
TypeError – If the input object is not a subclass of the target class.
- Parameters:
- Return type:
Examples
>>> import coordinax as cx
If the object is the same type, it should return the object itself.
>>> op = cxfm.Identity() >>> cxfm.Identity.from_(op) is op True
If the object is a different type, it will error.
>>> try: ... cxfm.Translate.from_(op) ... except TypeError as e: ... print(e) Cannot construct <class '...Translate'> from <class '...Identity'>.
Unless the object is a subclass of the target class.
>>> class MyOperator(cxfm.Identity): ... pass
>>> op = MyOperator() >>> op MyOperator()
>>> newop = cxfm.Identity.from_(op) >>> newop is op, isinstance(newop, cxfm.Identity) (False, True)
- from_(cls: type[TimeDep], fn: Callable[..., Any], /, *args: Any, **kw: Any) TimeDep
- Parameters:
- Return type:
Build from a
tau -> transformcallable.tauis a call-time argument, never a stored parameter, so a user-defined function’staudependence is fully differentiated by the kinematic-prolongation engine – even for a bare lambda. act on tangent data and act_jet pick up the resultingd/dtauterms automatically.Storage matters only for the builder’s other parameters. With no extra arguments: a bare function/lambda cannot be a pytree leaf, so it is stored STATIC – anything it closes over is a trace-time constant and a fresh closure forces a jit retrace. A callable that is already a pytree (an equinox.Module, notably equinox.Partial) is used as the builder as-is, keeping its leaves dynamic – differentiable and jit-cached. (Wrapping it would set those leaves static, silently destroying them.)
Given extra
*args/**kw, they are bound ontofnwith equinox.Partial, which keeps them as dynamic leaves. ``fn`` must take ``tau`` LAST –fn(param..., tau, **kw)– because equinox.Partial prepends its bound positionals:partial(tau)callsfn(*bound, tau, **kw). Writingfn(tau, param)instead silently passes the parameter as tau. The resulting builder is a equinox.Partial, so the operator needs equinox.filter_jit, not plain jax.jit.Examples
>>> import equinox as eqx >>> import jax >>> import jax.numpy as jnp >>> import unxt as u >>> import coordinax as cx >>> import coordinax.charts as cxc >>> import coordinax.transforms as cxfm
A bare lambda is enough for the
tauderivatives to flow: this drift at 3 km/s, acted on data at rest, transforms the velocity to exactly the drift rate.>>> rate = {"x": u.Q(3.0, "km/s"), "y": u.Q(0.0, "km/s"), "z": u.Q(0.0, "km/s")} >>> drift = cxfm.TimeDep.from_( ... lambda t: cxfm.Translate({k: v * t for k, v in rate.items()}, ... chart=cxc.cart3d) ... )
>>> at_rest = cx.Coordinate( ... point=cx.Point.from_([0.0, 0.0, 0.0], "km"), ... velocity=cx.Tangent.from_([0.0, 0.0, 0.0], "km/s"), ... ) >>> cxfm.act(drift, u.Q(0.0, "s"), at_rest)["velocity"]["x"] Q(3., 'km / s')
Extra arguments are bound as leaves – keeping them differentiable and jit-cached. Note
tcomes last:>>> def scaled(factor, t) -> cxfm.Scale: ... return cxfm.Scale.from_factors(jnp.full(3, factor))
>>> op = cxfm.TimeDep.from_(scaled, jnp.asarray(2.0)) >>> [float(x) for x in jax.tree.leaves(eqx.filter(op, eqx.is_array))] [2.0]
A pre-built equinox.Partial is equivalent:
>>> op = cxfm.TimeDep.from_(eqx.Partial(scaled, jnp.asarray(2.0))) >>> [float(x) for x in jax.tree.leaves(eqx.filter(op, eqx.is_array))] [2.0]
- from_(cls: type[coordinax.transforms._src.actions.add.AbstractAdd], obj: coordinax.transforms._src.actions.add.AbstractAdd, /) coordinax.transforms._src.actions.add.AbstractAdd
- Parameters:
- Return type:
Construct a AbstractAdd from another AbstractAdd.
>>> import coordinax as cx >>> shift1 = cxfm.Translate.from_([1, 2, 3], "km") >>> cxfm.Translate.from_(shift1) is shift1 True
- from_(cls: type[coordinax.transforms._src.actions.add.AbstractAdd], q: AbstractQuantity, /) coordinax.transforms._src.actions.add.AbstractAdd
- Parameters:
- Return type:
Construct an AbstractAdd subclass from a Quantity.
>>> import unxt as u >>> import coordinax.transforms as cxfm >>> cxfm.Translate.from_(u.Q([1, 2, 3], "km")) Translate( {'x': Q(1, 'km'), 'y': Q(2, 'km'), 'z': Q(3, 'km')}, chart=Cart3D(M=Rn(3)) )
- from_(cls: type[coordinax.transforms._src.actions.add.AbstractAdd], x: ArrayLike, unit: str) coordinax.transforms._src.actions.add.AbstractAdd
- Parameters:
- Return type:
Construct an Add operator from an array-like offset and unit.
>>> import coordinax.transforms as cxfm >>> cxfm.Translate.from_([1, 2, 3], "km") Translate( {'x': Q(1, 'km'), 'y': Q(2, 'km'), 'z': Q(3, 'km')}, chart=Cart3D(M=Rn(3)) )
Construct a Rotate from another Rotate.
>>> import quaxed.numpy as jnp >>> import coordinax.transforms as cxfm >>> R = cxfm.Rotate(jnp.eye(3)) >>> cxfm.Rotate.from_(R) is R True
- from_(cls: type[Rotate], obj: AbstractQuantity, /) Rotate
- Parameters:
- Return type:
Construct a Rotate from a Quantity.
>>> import jax.numpy as jnp >>> import unxt as u >>> import coordinax.transforms as cxfm >>> cxfm.Rotate.from_(u.Q(jnp.eye(3), "")) Rotate(f64[3,3](jax))
Construct a Rotate from an Array.
>>> import jax.numpy as jnp >>> import coordinax.transforms as cxfm >>> cxfm.Rotate.from_(jnp.eye(3)) Rotate(f64[3,3](jax))
Initialize from a jax.scipy.spatial.transform.Rotation.
>>> import jax.numpy as jnp >>> from jax.scipy.spatial.transform import Rotation >>> import coordinax as cx
>>> R = Rotation.from_euler("z", 90, degrees=True) >>> op = cxfm.Rotate.from_(R)
>>> jnp.allclose(op.R, R.as_matrix()) Array(True, dtype=bool)
Construct a Linear from an array matrix.
Registered on the class’s own dispatcher, as the sibling transforms do. Via the global plum.dispatch the ArrayLike union is unfaithful, which would turn off plum’s method cache for every from_ in the library – caught by test_no_new_unfaithful_signatures.
Construct a Reflect from another Reflect.
- from_(cls: type[Reflect], obj: AbstractQuantity, /) Reflect
- Parameters:
- Return type:
Construct a Reflect from a dimensionless quantity matrix.
Construct a Reflect from an array matrix.
Construct a Scale from another Scale.
- from_(cls: type[Scale], obj: AbstractQuantity, /) Scale
- Parameters:
- Return type:
Construct a Scale from a dimensionless quantity matrix.
Construct a Scale from an array matrix.
Construct a Shear from another Shear.
- from_(cls: type[Shear], obj: AbstractQuantity, /) Shear
- Parameters:
- Return type:
Construct a Shear from a dimensionless quantity matrix.
Construct a Shear from an array matrix.
- Parameters:
cls (
type[AbstractTransform])args (
object)kwargs (
object)
- Return type:
- property is_time_dependent: bool
Whether the point action depends on the time parameter tau.
A declared trait, not a structural scan: subclasses whose point action varies with tau (
TimeDep,Boost) override this to True; composites (Composed) override it to the disjunction of their components. The default is False.Examples
>>> import coordinax.transforms as cxfm
>>> cxfm.Translate.from_([1, 2, 3], "km").is_time_dependent False
- property matrix: Array
The matrix \(M\) this transform applies, as \(x \mapsto M x\).
The one spelling shared by every subclass, which otherwise keep \(M\) under their own letter (
R,H,S) or, for ~coordinax.transforms.LorentzBoost, derive it from a stored velocity.Validated square, but not against a chart – a chart is what fixes the dimension and this takes none, so the
actpaths keep that check.Examples
>>> import quaxed.numpy as jnp >>> import coordinax.transforms as cxfm
For a stored matrix it is the field under a common name:
>>> Rz = jnp.asarray([[0, -1, 0], [1, 0, 0], [0, 0, 1]]) >>> bool(jnp.array_equal(cxfm.Rotate(Rz).matrix, Rz)) True
For a derived one it is the only way to see it:
>>> cxfm.LorentzBoost([0.6, 0.0, 0.0]).matrix.round(2) Array([[1.25, 0.75, 0. , 0. ], [0.75, 1.25, 0. , 0. ], [0. , 0. , 1. , 0. ], [0. , 0. , 0. , 1. ]], dtype=float64)
- simplify()
Simplify the operator.
This method calls coordinax.ops.simplify to simplify the operator.
- Return type:
Examples
>>> import unxt as u >>> import coordinax.transforms as cxfm
>>> op = cxfm.Identity() >>> op.simplify() is op True
>>> pipe = cxfm.Composed((cxfm.Identity(), cxfm.Identity())) >>> pipe Composed((Identity(), Identity())) >>> pipe.simplify() Identity()
- final class coordinax.Shear(H: Any)
Bases:
AbstractLinearTransformOperator for Cartesian linear shear.
A shear transform applies
\[x \mapsto Hx,\]where
His an invertible shear matrix.- Parameters:
H (
Any)
- H: Shaped[Array, 'N N']
The shear matrix.
- property inverse: Shear
Return the inverse shear transform.
- classmethod from_(*args, **kwargs)
Construct from a set of arguments.
- from_(cls: type[AbstractTransform], *args: object, **kwargs: object) AbstractTransform
- Parameters:
- Return type:
Construct from a set of arguments.
This is a low-priority dispatch that will be called if no other dispatch is found. It just tries to pass the arguments to the constructor.
- from_(cls: type[AbstractTransform], obj: Mapping[str, Any], /) AbstractTransform
- Parameters:
- Return type:
Construct from a mapping.
>>> import coordinax.transforms as cxfm >>> cxfm.Composed.from_({"transforms": (cxfm.Identity(), cxfm.Identity())}) Composed((Identity(), Identity()))
- from_(cls: type[AbstractTransform], x: ArrayLike | list[float | int], unit: str, /) AbstractTransform
- Parameters:
- Return type:
Construct from a Quantity’s value and unit.
>>> import coordinax.transforms as cxfm >>> op = cxfm.Translate.from_([1, 1, 1], "km") >>> print(op) Translate( {'x': Q(1, 'km'), 'y': Q(1, 'km'), 'z': Q(1, 'km')}, chart=Cart3D(M=Rn(3)) )
- from_(cls: type[AbstractTransform], obj: AbstractTransform, /) AbstractTransform
- Parameters:
- Return type:
Construct an operator from another operator.
- Raises:
TypeError – If the input object is not a subclass of the target class.
- Parameters:
- Return type:
Examples
>>> import coordinax as cx
If the object is the same type, it should return the object itself.
>>> op = cxfm.Identity() >>> cxfm.Identity.from_(op) is op True
If the object is a different type, it will error.
>>> try: ... cxfm.Translate.from_(op) ... except TypeError as e: ... print(e) Cannot construct <class '...Translate'> from <class '...Identity'>.
Unless the object is a subclass of the target class.
>>> class MyOperator(cxfm.Identity): ... pass
>>> op = MyOperator() >>> op MyOperator()
>>> newop = cxfm.Identity.from_(op) >>> newop is op, isinstance(newop, cxfm.Identity) (False, True)
- from_(cls: type[TimeDep], fn: Callable[..., Any], /, *args: Any, **kw: Any) TimeDep
- Parameters:
- Return type:
Build from a
tau -> transformcallable.tauis a call-time argument, never a stored parameter, so a user-defined function’staudependence is fully differentiated by the kinematic-prolongation engine – even for a bare lambda. act on tangent data and act_jet pick up the resultingd/dtauterms automatically.Storage matters only for the builder’s other parameters. With no extra arguments: a bare function/lambda cannot be a pytree leaf, so it is stored STATIC – anything it closes over is a trace-time constant and a fresh closure forces a jit retrace. A callable that is already a pytree (an equinox.Module, notably equinox.Partial) is used as the builder as-is, keeping its leaves dynamic – differentiable and jit-cached. (Wrapping it would set those leaves static, silently destroying them.)
Given extra
*args/**kw, they are bound ontofnwith equinox.Partial, which keeps them as dynamic leaves. ``fn`` must take ``tau`` LAST –fn(param..., tau, **kw)– because equinox.Partial prepends its bound positionals:partial(tau)callsfn(*bound, tau, **kw). Writingfn(tau, param)instead silently passes the parameter as tau. The resulting builder is a equinox.Partial, so the operator needs equinox.filter_jit, not plain jax.jit.Examples
>>> import equinox as eqx >>> import jax >>> import jax.numpy as jnp >>> import unxt as u >>> import coordinax as cx >>> import coordinax.charts as cxc >>> import coordinax.transforms as cxfm
A bare lambda is enough for the
tauderivatives to flow: this drift at 3 km/s, acted on data at rest, transforms the velocity to exactly the drift rate.>>> rate = {"x": u.Q(3.0, "km/s"), "y": u.Q(0.0, "km/s"), "z": u.Q(0.0, "km/s")} >>> drift = cxfm.TimeDep.from_( ... lambda t: cxfm.Translate({k: v * t for k, v in rate.items()}, ... chart=cxc.cart3d) ... )
>>> at_rest = cx.Coordinate( ... point=cx.Point.from_([0.0, 0.0, 0.0], "km"), ... velocity=cx.Tangent.from_([0.0, 0.0, 0.0], "km/s"), ... ) >>> cxfm.act(drift, u.Q(0.0, "s"), at_rest)["velocity"]["x"] Q(3., 'km / s')
Extra arguments are bound as leaves – keeping them differentiable and jit-cached. Note
tcomes last:>>> def scaled(factor, t) -> cxfm.Scale: ... return cxfm.Scale.from_factors(jnp.full(3, factor))
>>> op = cxfm.TimeDep.from_(scaled, jnp.asarray(2.0)) >>> [float(x) for x in jax.tree.leaves(eqx.filter(op, eqx.is_array))] [2.0]
A pre-built equinox.Partial is equivalent:
>>> op = cxfm.TimeDep.from_(eqx.Partial(scaled, jnp.asarray(2.0))) >>> [float(x) for x in jax.tree.leaves(eqx.filter(op, eqx.is_array))] [2.0]
- from_(cls: type[coordinax.transforms._src.actions.add.AbstractAdd], obj: coordinax.transforms._src.actions.add.AbstractAdd, /) coordinax.transforms._src.actions.add.AbstractAdd
- Parameters:
- Return type:
Construct a AbstractAdd from another AbstractAdd.
>>> import coordinax as cx >>> shift1 = cxfm.Translate.from_([1, 2, 3], "km") >>> cxfm.Translate.from_(shift1) is shift1 True
- from_(cls: type[coordinax.transforms._src.actions.add.AbstractAdd], q: AbstractQuantity, /) coordinax.transforms._src.actions.add.AbstractAdd
- Parameters:
- Return type:
Construct an AbstractAdd subclass from a Quantity.
>>> import unxt as u >>> import coordinax.transforms as cxfm >>> cxfm.Translate.from_(u.Q([1, 2, 3], "km")) Translate( {'x': Q(1, 'km'), 'y': Q(2, 'km'), 'z': Q(3, 'km')}, chart=Cart3D(M=Rn(3)) )
- from_(cls: type[coordinax.transforms._src.actions.add.AbstractAdd], x: ArrayLike, unit: str) coordinax.transforms._src.actions.add.AbstractAdd
- Parameters:
- Return type:
Construct an Add operator from an array-like offset and unit.
>>> import coordinax.transforms as cxfm >>> cxfm.Translate.from_([1, 2, 3], "km") Translate( {'x': Q(1, 'km'), 'y': Q(2, 'km'), 'z': Q(3, 'km')}, chart=Cart3D(M=Rn(3)) )
Construct a Rotate from another Rotate.
>>> import quaxed.numpy as jnp >>> import coordinax.transforms as cxfm >>> R = cxfm.Rotate(jnp.eye(3)) >>> cxfm.Rotate.from_(R) is R True
- from_(cls: type[Rotate], obj: AbstractQuantity, /) Rotate
- Parameters:
- Return type:
Construct a Rotate from a Quantity.
>>> import jax.numpy as jnp >>> import unxt as u >>> import coordinax.transforms as cxfm >>> cxfm.Rotate.from_(u.Q(jnp.eye(3), "")) Rotate(f64[3,3](jax))
Construct a Rotate from an Array.
>>> import jax.numpy as jnp >>> import coordinax.transforms as cxfm >>> cxfm.Rotate.from_(jnp.eye(3)) Rotate(f64[3,3](jax))
Initialize from a jax.scipy.spatial.transform.Rotation.
>>> import jax.numpy as jnp >>> from jax.scipy.spatial.transform import Rotation >>> import coordinax as cx
>>> R = Rotation.from_euler("z", 90, degrees=True) >>> op = cxfm.Rotate.from_(R)
>>> jnp.allclose(op.R, R.as_matrix()) Array(True, dtype=bool)
Construct a Linear from an array matrix.
Registered on the class’s own dispatcher, as the sibling transforms do. Via the global plum.dispatch the ArrayLike union is unfaithful, which would turn off plum’s method cache for every from_ in the library – caught by test_no_new_unfaithful_signatures.
Construct a Reflect from another Reflect.
- from_(cls: type[Reflect], obj: AbstractQuantity, /) Reflect
- Parameters:
- Return type:
Construct a Reflect from a dimensionless quantity matrix.
Construct a Reflect from an array matrix.
Construct a Scale from another Scale.
- from_(cls: type[Scale], obj: AbstractQuantity, /) Scale
- Parameters:
- Return type:
Construct a Scale from a dimensionless quantity matrix.
Construct a Scale from an array matrix.
Construct a Shear from another Shear.
- from_(cls: type[Shear], obj: AbstractQuantity, /) Shear
- Parameters:
- Return type:
Construct a Shear from a dimensionless quantity matrix.
Construct a Shear from an array matrix.
- Parameters:
cls (
type[AbstractTransform])args (
object)kwargs (
object)
- Return type:
- property is_time_dependent: bool
Whether the point action depends on the time parameter tau.
A declared trait, not a structural scan: subclasses whose point action varies with tau (
TimeDep,Boost) override this to True; composites (Composed) override it to the disjunction of their components. The default is False.Examples
>>> import coordinax.transforms as cxfm
>>> cxfm.Translate.from_([1, 2, 3], "km").is_time_dependent False
- property matrix: Array
The matrix \(M\) this transform applies, as \(x \mapsto M x\).
The one spelling shared by every subclass, which otherwise keep \(M\) under their own letter (
R,H,S) or, for ~coordinax.transforms.LorentzBoost, derive it from a stored velocity.Validated square, but not against a chart – a chart is what fixes the dimension and this takes none, so the
actpaths keep that check.Examples
>>> import quaxed.numpy as jnp >>> import coordinax.transforms as cxfm
For a stored matrix it is the field under a common name:
>>> Rz = jnp.asarray([[0, -1, 0], [1, 0, 0], [0, 0, 1]]) >>> bool(jnp.array_equal(cxfm.Rotate(Rz).matrix, Rz)) True
For a derived one it is the only way to see it:
>>> cxfm.LorentzBoost([0.6, 0.0, 0.0]).matrix.round(2) Array([[1.25, 0.75, 0. , 0. ], [0.75, 1.25, 0. , 0. ], [0. , 0. , 1. , 0. ], [0. , 0. , 0. , 1. ]], dtype=float64)
- simplify()
Simplify the operator.
This method calls coordinax.ops.simplify to simplify the operator.
- Return type:
Examples
>>> import unxt as u >>> import coordinax.transforms as cxfm
>>> op = cxfm.Identity() >>> op.simplify() is op True
>>> pipe = cxfm.Composed((cxfm.Identity(), cxfm.Identity())) >>> pipe Composed((Identity(), Identity())) >>> pipe.simplify() Identity()
- coordinax.cconvert(*args, **kwargs)
Transform the current vector to the target chart.
This is an abstract API definition. See the main coordinax package for concrete implementations.
Examples
>>> import coordinax.representations as cxr >>> import coordinax.charts as cxc
Define a point in Cartesian coordinates:
>>> p = {"x": 1.0, "y": 2.0, "z": 3.0}
Convert it to spherical coordinates:
>>> cxr.cconvert(p, cxc.cart3d, cxr.point, cxc.sph3d, cxr.point) {'r': Array(3.74165739, dtype=float64, ...), 'theta': Array(0.64052231, dtype=float64, ...), 'phi': Array(1.10714872, dtype=float64, ...)}
- coordinax.cconvert(obj: NoneType, /, *fixed_args: Any, **fixed_kw: Any) Any
Return a partial function for vector conversion.
Convert a point from Cartesian coordinates to spherical coordinates:
>>> import coordinax.representations as cxr >>> import coordinax.charts as cxc
Define a point in Cartesian coordinates:
>>> q = {"x": 1.0, "y": 2.0, "z": 3.0}
Convert it to spherical coordinates:
>>> map = cxr.cconvert(None, cxc.cart3d, cxr.point, cxc.sph3d) >>> map(q) {'r': Array(3.74165739, dtype=float64, ...), 'theta': Array(0.64052231, dtype=float64, ...), 'phi': Array(1.10714872, dtype=float64, ...)}
- coordinax.cconvert(x: Any, from_chart: AbstractChart, from_rep: Representation, to_chart: AbstractChart, to_rep: Representation, /, *, at: dict | None = None, usys: AbstractUnitSystem | None = None) Any
Convert point data between charts.
Convert a point from Cartesian coordinates to spherical coordinates:
>>> import coordinax.representations as cxr >>> import coordinax.charts as cxc
Define a point in Cartesian coordinates:
>>> p = {"x": 1.0, "y": 2.0, "z": 3.0}
Convert it to spherical coordinates:
>>> q = cxr.cconvert(p, cxc.cart3d, cxr.point, cxc.sph3d, cxr.point) >>> q {'r': Array(3.74165739, dtype=float64, ...), 'theta': Array(0.64052231, dtype=float64, ...), 'phi': Array(1.10714872, dtype=float64, ...)}
The output q represents the same geometric point but expressed in the target chart.
The representation remains unchanged; only the chart changes:
>>> cxr.cconvert(q, cxc.sph3d, cxr.point, cxc.cart3d, cxr.point) {'x': Array(1., dtype=float64, ...), 'y': Array(2., dtype=float64, ...), 'z': Array(3., dtype=float64, ...)}
Let’s work through more examples.
Cartesian to Spherical (with units):
>>> import unxt as u >>> p = {"x": u.Q(1.0, "m"), "y": u.Q(0.0, "m"), "z": u.Q(0.0, "m")} >>> cxr.cconvert(p, cxc.cart3d, cxr.point, cxc.sph3d, cxr.point) {'r': Q(1., 'm'), 'theta': Angle(1.57079633, 'rad'), 'phi': Angle(0., 'rad')}
Cylindrical to Cartesian (without units):
>>> p = {"rho": 3.0, "phi": 0, "z": 4.0} >>> cxr.cconvert(p, cxc.cyl3d, cxr.point, cxc.cart3d, cxr.point) {'x': Array(3., dtype=float64, ...), 'y': Array(0., dtype=float64, ...), 'z': 4.0}
Polar to Cartesian (2D):
>>> p = {"r": u.Q(5.0, "m"), "theta": u.Q(90, "deg")} >>> cxr.cconvert(p, cxc.polar2d, cxr.point, cxc.cart2d, cxr.point) {'x': Q(3.061617e-16, 'm'), 'y': Q(5., 'm')}
Between Spherical variants (Spherical to LonLatSpherical):
>>> p = {"r": u.Q(1.0, "m"), "theta": u.Q(45, "deg"), "phi": u.Q(0, "deg")} >>> cxr.cconvert(p, cxc.sph3d, cxr.point, cxc.lonlat_sph3d, cxr.point) {'lon': Angle(0, 'deg'), 'lat': Angle(45, 'deg'), 'distance': Q(1., 'm')}
Identity conversion (same chart):
>>> p = {"x": u.Q(2.0, "m"), "y": u.Q(3.0, "m")} >>> cxr.cconvert(p, cxc.cart2d, cxr.point, cxc.cart2d, cxr.point) is p True
- coordinax.cconvert(x: Any, from_chart: AbstractChart, from_rep: Representation, to_chart: AbstractChart, /, *, at: dict | None = None, usys: AbstractUnitSystem | None = None) Any
Convert point data between charts.
Convert a point from Cartesian coordinates to spherical coordinates:
>>> import coordinax.representations as cxr >>> import coordinax.charts as cxc
Define a point in Cartesian coordinates:
>>> p = {"x": 1.0, "y": 2.0, "z": 3.0}
Convert it to spherical coordinates:
>>> q = cxr.cconvert(p, cxc.cart3d, cxr.point, cxc.sph3d) >>> q {'r': Array(3.74165739, dtype=float64, ...), 'theta': Array(0.64052231, dtype=float64, ...), 'phi': Array(1.10714872, dtype=float64, ...)}
The output q represents the same geometric point but expressed in the target chart.
The representation remains unchanged; only the chart changes:
>>> cxr.cconvert(q, cxc.sph3d, cxr.point, cxc.cart3d) {'x': Array(1., dtype=float64, ...), 'y': Array(2., dtype=float64, ...), 'z': Array(3., dtype=float64, ...)}
- coordinax.cconvert(x: Any, from_chart: AbstractChart, from_geom: PointGeometry, from_rep: Representation, to_chart: AbstractChart, to_geom: PointGeometry, to_rep: Representation, /, *, usys: AbstractUnitSystem | None = None) Any
Convert point data between charts.
This function delegates to coordinax.charts.pt_map. The representation arguments are checked to ensure they correspond to canonical point data:
\[(\mathrm{PointGeometry},\, \mathrm{NoBasis},\, \mathrm{Location}).\]Convert a point from Cartesian coordinates to spherical coordinates:
>>> import coordinax.representations as cxr >>> import coordinax.charts as cxc
Define a point in Cartesian coordinates:
>>> p = {"x": 1.0, "y": 2.0, "z": 3.0}
Convert it to spherical coordinates:
>>> cxr.cconvert(p, cxc.cart3d, cxr.point_geom, cxr.point, ... cxc.sph3d, cxr.point_geom, cxr.point) {'r': Array(3.74165739, dtype=float64, ...), 'theta': Array(0.64052231, dtype=float64, ...), 'phi': Array(1.10714872, dtype=float64, ...)}
- coordinax.cconvert(x: Any, from_chart: AbstractChart, from_geom: TangentGeometry, from_rep: Representation, to_chart: AbstractChart, to_geom: TangentGeometry, to_rep: Representation, /, *, at: dict | None = None, usys: AbstractUnitSystem | None = None) Any
Convert tangent data between charts via Jacobian pushforward.
>>> import jax.numpy as jnp >>> import coordinax.charts as cxc >>> import coordinax.representations as cxr
>>> v = {"r": jnp.array(5.0), "theta": jnp.array(1.0), "phi": jnp.array(2.0)} >>> at = {"r": jnp.array(3.0), "theta": jnp.array(0.5), "phi": jnp.array(0.0)} >>> cxr.cconvert(v, cxc.sph3d, cxr.tangent_geom, cxr.coord_disp, ... cxc.sph3d, cxr.tangent_geom, cxr.phys_disp, at=at) {'r': Array(5., dtype=float64, ...), 'theta': Array(3., dtype=float64, ...), 'phi': Array(..., dtype=float64, ...)}
>>> v = {"x": jnp.array(1.0), "y": jnp.array(0.0)} >>> at = {"x": jnp.array(1.0), "y": jnp.array(0.0)} >>> cxr.cconvert(v, cxc.cart2d, cxr.coord_disp, cxc.polar2d, cxr.coord_disp, at=at) {'r': Array(1., ...), 'theta': Array(0., ...)}
- coordinax.cconvert(from_vec: Point, to_chart: AbstractChart, /, *, usys: AbstractUnitSystem | None = None) Point
Convert a point from one chart to another.
>>> import unxt as u >>> import coordinax as cx
>>> vec = cx.Point.from_([1, 1, 1], "m") >>> print(vec) <Point: chart=Cart3D (x, y, z) [m] [1 1 1]>
>>> sph_vec = cx.cconvert(vec, cx.sph3d) >>> print(sph_vec) <Point: chart=Spherical3D (r[m], theta[rad], phi[rad]) [1.732 0.955 0.785]>
- coordinax.cconvert(from_vec: Point, from_chart: AbstractChart, to_chart: AbstractChart, /, *, usys: AbstractUnitSystem | None = None) Point
Convert a vector from one chart to another.
>>> import unxt as u >>> import coordinax as cx
>>> vec = cx.Point.from_([1, 1, 1], "m") >>> sph_vec = cx.cconvert(vec, cx.cart3d, cx.sph3d) >>> print(sph_vec) <Point: chart=Spherical3D (r[m], theta[rad], phi[rad]) [1.732 0.955 0.785]>
- coordinax.cconvert(from_vec: Tangent, to_chart: AbstractChart, /, *, at: Any = None, usys: AbstractUnitSystem | None = None) Tangent
Convert a tangent Tangent from one chart to another.
The
atparameter provides the base point at which the tangent map (Jacobian pushforward) is evaluated. It may be a Point instance (whose.datais used) or a rawCDict.>>> import unxt as u >>> import coordinax as cx >>> import coordinax.charts as cxc >>> import coordinax.representations as cxr
>>> v = cx.Tangent.from_( ... {"x": u.Q(1.0, "m/s"), "y": u.Q(0.0, "m/s"), "z": u.Q(0.0, "m/s")}, ... cxc.cart3d, cxr.coord_basis, cxr.vel, ... ) >>> pt = cx.Point.from_([1.0, 0.0, 0.0], "m") >>> v_sph = cx.cconvert(v, cxc.sph3d, at=pt) >>> v_sph.chart Spherical3D(M=Rn(3))
- coordinax.cconvert(from_vec: Tangent, from_chart: AbstractChart, to_chart: AbstractChart, /, *, at: Any = None, usys: AbstractUnitSystem | None = None) Tangent
Convert a tangent Tangent from one chart to another (explicit from-chart).
>>> import unxt as u >>> import coordinax as cx >>> import coordinax.charts as cxc >>> import coordinax.representations as cxr
>>> v = cx.Tangent.from_( ... {"x": u.Q(1.0, "m/s"), "y": u.Q(0.0, "m/s"), "z": u.Q(0.0, "m/s")}, ... cxc.cart3d, cxr.coord_basis, cxr.vel, ... ) >>> pt = cx.Point.from_([1.0, 0.0, 0.0], "m") >>> v_sph = cx.cconvert(v, cxc.cart3d, cxc.sph3d, at=pt) >>> v_sph.chart Spherical3D(M=Rn(3))
- coordinax.cconvert(pv: Coordinate, to_chart: AbstractChart, /, *, usys: AbstractUnitSystem | None = None) Coordinate
Convert a Coordinate to a new chart.
Delegates to
Coordinate.cconvert.>>> import coordinax as cx >>> import coordinax.charts as cxc
>>> pt = cx.Point.from_([1.0, 0.0, 0.0], "m") >>> pv = cx.Coordinate(point=pt) >>> pv_sph = cx.cconvert(pv, cxc.sph3d) >>> pv_sph.point.chart Spherical3D(M=Rn(3))
- coordinax.add(*args, **kwargs)
Add two coordinate data objects.
This is an abstract API definition. See the main coordinax package for concrete implementations.
- coordinax.add(lhs: Any, lhs_chart: AbstractChart, lhs_rep: Representation, rhs: Any, rhs_chart: AbstractChart, rhs_rep: Representation, /) Any
Add two coordinate data objects via Cartesian round-trip.
Both operands are converted to the ambient Cartesian chart of
lhs_chart, added component-wise, then converted back tolhs_chart. Iflhs_chartis already Cartesian (or has no global Cartesian),rhsis converted intolhs_chartand added directly.>>> import coordinax.representations as cxr >>> import coordinax.charts as cxc >>> import unxt as u
>>> p1 = {"x": u.Q(1, "m"), "y": u.Q(2, "m"), "z": u.Q(3, "m")} >>> p2 = {"x": u.Q(4, "m"), "y": u.Q(5, "m"), "z": u.Q(6, "m")} >>> cxr.add(p1, cxc.cart3d, cxr.point, p2, cxc.cart3d, cxr.point) {'x': Q(5, 'm'), 'y': Q(7, 'm'), 'z': Q(9, 'm')}
Add two points.
For non-Cartesian charts the operation converts both operands to the ambient Cartesian chart, adds there, and converts the result back to the
lhschart. For Cartesian charts the addition is direct.The result keeps the
lhschart and representation.>>> import coordinax as cx >>> v1 = cx.Point.from_([1, 2, 3], "m") >>> v2 = cx.Point.from_([4, 5, 6], "m") >>> print(cxr.add(v1, v2)) <Point: chart=Cart3D (x, y, z) [m] [5 7 9]>
Add two tangent vectors component-wise.
Tangent spaces are genuine vector spaces: addition is component-wise in any chart basis (no Cartesian round-trip is needed or correct). Both operands must share the same chart and representation (basis + semantic) – adding components from two different charts is meaningless even when the charts happen to name their components the same way.
>>> import unxt as u >>> import coordinax as cx >>> import coordinax.charts as cxc >>> import coordinax.representations as cxr
>>> v1 = cx.Tangent.from_( ... {"x": u.Q(1.0, "m/s"), "y": u.Q(2.0, "m/s"), "z": u.Q(3.0, "m/s")}, ... cxc.cart3d, cxr.coord_vel, ... ) >>> v2 = cx.Tangent.from_( ... {"x": u.Q(4.0, "m/s"), "y": u.Q(5.0, "m/s"), "z": u.Q(6.0, "m/s")}, ... cxc.cart3d, cxr.coord_vel, ... ) >>> result = cxr.add(v1, v2) >>> result["x"] Q(5., 'm / s')
- coordinax.subtract(*args, **kwargs)
Subtract two coordinate data objects.
This is an abstract API definition. See the main coordinax package for concrete implementations.
- coordinax.subtract(lhs: Any, lhs_chart: AbstractChart, lhs_rep: Representation, rhs: Any, rhs_chart: AbstractChart, rhs_rep: Representation, /) Any
Subtract two coordinate data objects via Cartesian round-trip.
Both operands are converted to the ambient Cartesian chart of
lhs_chart, subtracted component-wise, then converted back tolhs_chart. Iflhs_chartis already Cartesian (or has no global Cartesian),rhsis converted intolhs_chartand subtracted directly.>>> import coordinax.representations as cxr >>> import coordinax.charts as cxc >>> import unxt as u
>>> p1 = {"x": u.Q(4, "m"), "y": u.Q(5, "m"), "z": u.Q(6, "m")} >>> p2 = {"x": u.Q(1, "m"), "y": u.Q(2, "m"), "z": u.Q(3, "m")} >>> cxr.subtract(p1, cxc.cart3d, cxr.point, p2, cxc.cart3d, cxr.point) {'x': Q(3, 'm'), 'y': Q(3, 'm'), 'z': Q(3, 'm')}
Subtract two vectors.
For non-Cartesian charts the operation converts both operands to the ambient Cartesian chart, subtracts there, and converts the result back to the
lhschart. For Cartesian charts the subtraction is direct.The result keeps the
lhschart and representation.>>> import coordinax as cx >>> v1 = cx.Point.from_([4, 5, 6], "m") >>> v2 = cx.Point.from_([1, 2, 3], "m") >>> print(cxr.subtract(v1, v2)) <Point: chart=Cart3D (x, y, z) [m] [3 3 3]>
Subtract two tangent vectors component-wise.
Tangent spaces are genuine vector spaces: subtraction is component-wise in any chart basis (no Cartesian round-trip is needed or correct). Both operands must share the same chart and representation (basis + semantic) – subtracting components from two different charts is meaningless even when the charts happen to name their components the same way.
>>> import unxt as u >>> import coordinax as cx >>> import coordinax.charts as cxc >>> import coordinax.representations as cxr
>>> v1 = cx.Tangent.from_( ... {"x": u.Q(4.0, "m/s"), "y": u.Q(5.0, "m/s"), "z": u.Q(6.0, "m/s")}, ... cxc.cart3d, cxr.coord_vel, ... ) >>> v2 = cx.Tangent.from_( ... {"x": u.Q(1.0, "m/s"), "y": u.Q(2.0, "m/s"), "z": u.Q(3.0, "m/s")}, ... cxc.cart3d, cxr.coord_vel, ... ) >>> result = cxr.subtract(v1, v2) >>> result["x"] Q(3., 'm / s')
- final class coordinax.PointGeometry
Bases:
AbstractGeometryPoint geometric kind.
A point geometry indicates that component data should be interpreted as the coordinates of a point on a manifold, rather than as components of a vector- or covector-like object.
Mathematical Definition:
Let \(M\) be a smooth manifold. A point is an element \(p \in M\).
Point data is therefore affine, not linear: points do not in general form a vector space, so operations such as adding two points are not geometrically defined. Under a change of chart, point coordinates transform by the ordinary chart transition map.
Examples
Construct the point geometry object directly:
>>> import coordinax.representations as cxr >>> geom = cxr.PointGeometry()
Use it inside a full representation for point data:
>>> rep = cxr.Representation(geom, cxr.no_basis, cxr.loc)
The representation can then be used with cconvert to convert point data between charts while preserving the represented geometric object:
>>> import coordinax.charts as cxc >>> p = {"x": 1.0, "y": 2.0, "z": 3.0} >>> cxr.cconvert(p, cxc.cart3d, rep, cxc.sph3d, rep) {'r': Array(3.74165739, dtype=float64, ...), 'theta': Array(0.64052231, dtype=float64, ...), 'phi': Array(1.10714872, dtype=float64, ...)}
The output is still point data, but expressed in the target chart.
Notes
PointGeometry describes only the geometric kind. The chart still determines the coordinate system, component names, and coordinate domains.
- canonical_name: ClassVar = 'point_geom'
Canonical name for the point geometry kind.
- final class coordinax.TangentGeometry
Bases:
AbstractGeometryTangent-vector geometric kind.
A tangent geometry indicates that component data should be interpreted as the components of a tangent vector at a point on a manifold, rather than as coordinates of a point.
Mathematical Definition:
Let \(M\) be a smooth manifold and \(p \in M\) a point. A tangent vector is an element \(v \in T_p M\) of the tangent space at \(p\).
Tangent data is linear: tangent vectors form a vector space, so adding two tangent vectors at the same point is geometrically well-defined. Under a change of chart, tangent vector components transform by the Jacobian (pushforward) of the chart transition map.
Examples
Construct the tangent geometry object directly:
>>> import coordinax.representations as cxr >>> geom = cxr.TangentGeometry()
Use it inside a full representation for tangent data:
>>> rep = cxr.Representation(geom, cxr.coord_basis, cxr.dpl)
Notes
This is a static dispatch object and carries no runtime numerical data. TangentGeometry describes only the geometric kind. The chart still determines the coordinate system, component names, and coordinate domains.
- canonical_name: ClassVar = 'tangent_geom'
Canonical name for the tangent geometry kind.
- final class coordinax.NoBasis
Bases:
AbstractBasisNo-basis kind.
A no-basis kind indicates that the represented data does not live in a basis-dependent linear space, so there is no meaningful choice of components with respect to a basis.
Mathematical Definition:
NoBasis is used for geometric objects whose representation is not given by expansion in a vector-space basis. The canonical example is a point on a manifold.
Let \(M\) be a smooth manifold. A point is an element \(p \in M\), not a vector in a tangent space \(T_p M\). Accordingly, point data has no associated basis: it is represented by chart coordinates, and under a change of chart those coordinates transform by the ordinary chart transition map, not by a change of basis.
Examples
Construct the no-basis object directly:
>>> import coordinax.representations as cxr >>> basis = cxr.NoBasis()
Use it inside a full representation for point data:
>>> rep = cxr.Representation(cxr.point_geom, basis, cxr.loc)
The representation can then be used with coordinax.representations.cconvert to convert point data between charts while preserving the fact that point data has no basis-dependent components:
>>> import coordinax.charts as cxc >>> p = {"x": 1.0, "y": 2.0, "z": 3.0} >>> q = cxr.cconvert(p, cxc.cart3d, rep, cxc.sph3d)
The output q is still point data, but expressed in the target chart.
>>> import wadler_lindig as wl >>> wl.pprint(basis, canonical=False) NoBasis()
>>> wl.pprint(basis, canonical=True) no_basis
Notes
NoBasis does not mean “no coordinates”. It means that the represented object is not described by components in a basis-dependent linear space.
- canonical_name: ClassVar = 'no_basis'
Canonical name for the no-basis kind.
- class coordinax.AbstractLinearBasis
Bases:
AbstractBasisAbstract base class for linear (basis-dependent) basis kinds.
A linear basis kind indicates that component data is expressed as components with respect to a specific choice of basis vectors in a tangent space or other linear space.
In the representation model used by coordinax, AbstractLinearBasis refines AbstractBasis by indicating that the object lives in a basis-dependent linear space, in contrast to NoBasis which is used for affine point data.
Examples
>>> import coordinax.representations as cxr >>> isinstance(cxr.CoordinateBasis(), cxr.AbstractLinearBasis) True
- final class coordinax.CoordinateBasis
Bases:
AbstractLinearBasisCoordinate basis kind.
A coordinate basis kind indicates that tangent-vector components are expressed in the coordinate basis induced by the current chart. In a coordinate basis, the basis vectors are the partial derivative operators \(\partial/\partial x^i\) associated with the chart coordinates \(x^i\).
Mathematical Definition:
Let \((U, \varphi)\) be a chart on a smooth manifold \(M\). The coordinate basis at a point \(p \in U\) consists of the tangent vectors \(\partial_i = \partial/\partial x^i|_p\). A tangent vector \(v \in T_p M\) expressed in the coordinate basis has components \(v^i\) such that \(v = v^i \partial_i\).
Under a change of chart, coordinate-basis components transform by the Jacobian matrix of the chart transition map.
Examples
Construct the coordinate basis object directly:
>>> import coordinax.representations as cxr >>> basis = cxr.CoordinateBasis()
Use it inside a full representation for tangent data:
>>> rep = cxr.Representation(cxr.tangent_geom, basis, cxr.dpl)
Notes
CoordinateBasis does not carry unit-length information. Components in a coordinate basis are not necessarily dimensionless: for example, the \(\partial_r\) component of a vector in spherical coordinates carries units of inverse length relative to the physical basis.
- canonical_name: ClassVar = 'coord_basis'
Canonical name for the coordinate basis kind.
- final class coordinax.PhysicalBasis
Bases:
AbstractLinearBasisPhysical (orthonormal) basis kind.
A physical basis kind indicates that tangent-vector components are expressed in an orthonormal physical basis. In a physical basis, the basis vectors are unit-length and mutually orthogonal with respect to the metric.
Mathematical Definition:
Let \(M\) carry a Riemannian metric \(g\) and let \((U, \varphi)\) be a chart on \(M\). The physical basis at a point \(p \in U\) consists of unit vectors \(\hat{e}_i = \partial_i / \sqrt{g_{ii}}\) (for orthogonal charts). A tangent vector \(v \in T_p M\) expressed in the physical basis has components \(\hat{v}^i\) such that \(v = \hat{v}^i \hat{e}_i\).
Under a change of chart, physical-basis components transform by both the Jacobian and the normalization factors of the chart transition.
Examples
Construct the physical basis object directly:
>>> import coordinax.representations as cxr >>> basis = cxr.PhysicalBasis()
Use it inside a full representation for tangent data:
>>> rep = cxr.Representation(cxr.tangent_geom, basis, cxr.vel)
Notes
PhysicalBasis components have consistent physical dimensions across all charts, unlike coordinate-basis components.
- canonical_name: ClassVar = 'phys_basis'
Canonical name for the physical basis kind.
- final class coordinax.Location
Bases:
AbstractSemanticKindLocation semantic kind.
A location semantic kind indicates that the represented data specifies where a geometric object is, rather than how it is displaced, how fast it moves, or how it accelerates.
Mathematical Definition:
Location is the canonical semantic kind for point data. Let \(M\) be a smooth manifold. A point is an element \(p \in M\), and Location indicates that the represented data should be interpreted as the coordinates of that point in some chart.
The semantic kind Location therefore does not change the underlying geometric transformation law: for point data, coordinates still transform by the ordinary chart transition map. Instead, it records the interpretation of the point-like data as an actual position on the manifold.
Examples
Construct the location semantic object directly:
>>> import coordinax.representations as cxr >>> semantic = cxr.Location()
Use it inside a full representation for point data:
>>> rep = cxr.Representation(cxr.point_geom, cxr.no_basis, semantic)
The representation can then be used with cconvert to convert point data between charts while preserving the fact that the data represents a location:
>>> import coordinax.charts as cxc >>> p = {"x": 1.0, "y": 2.0, "z": 3.0} >>> cxr.cconvert(p, cxc.cart3d, rep, cxc.sph3d, rep) {'r': Array(3.74165739, dtype=float64, ...), 'theta': Array(0.64052231, dtype=float64, ...), 'phi': Array(1.10714872, dtype=float64, ...)}
The output is still point data representing the same location, but expressed in the target chart.
Notes
Location does not by itself imply that the represented object is a point, but in the current coordinax design it is primarily used as the semantic kind paired with PointGeometry.
- canonical_name: ClassVar = 'loc'
Canonical name for the location semantic kind.
- classmethod coord_dimensions(cls, chart: AbstractChart[Any, Any, Any], /)
Return the physical dimensions of each component for a location.
For a location semantic kind, the dimensions are determined solely by the chart: each component’s dimension is taken directly from
chart.coord_dimensions.Examples
>>> import coordinax.charts as cxc >>> import coordinax.representations as cxr >>> cxr.Location.coord_dimensions(cxc.cart3d) (PhysicalType('length'), PhysicalType('length'), PhysicalType('length'))
- Parameters:
chart (
AbstractChart[Any,Any,Any])- Return type:
tuple[PhysicalType|None,...]
- class coordinax.AbstractTangentSemanticKind
Bases:
AbstractSemanticKindAbstract base class for tangent-vector semantic kinds.
A tangent semantic kind specifies the meaning of a tangent-vector object, distinguishing objects that are geometrically identical (both elements of a tangent space \(T_p M\)) but semantically different in how they are used or interpreted.
Examples include:
Displacement: a finite difference between two nearby points,
Velocity: the rate of change of position with respect to time,
Acceleration: the second derivative of position with respect to time.
All share the same coordinate-transformation law (Jacobian pushforward) but differ in physical dimension and physical interpretation.
Examples
>>> import coordinax.representations as cxr >>> isinstance(cxr.Displacement(), cxr.AbstractTangentSemanticKind) True >>> isinstance(cxr.Velocity(), cxr.AbstractTangentSemanticKind) True
- classmethod coord_dimensions(cls, chart: AbstractChart[Any, Any, Any], /)
Return the physical dimensions of each component for this tangent kind.
Each component’s base dimension is taken from
chart.coord_dimensionsand scaled by the appropriate power of time according tocls.order: dimension / time^order.Examples
>>> import coordinax.charts as cxc >>> import coordinax.representations as cxr
>>> [str(x) for x in cxr.Displacement.coord_dimensions(cxc.cart3d)] ['length', 'length', 'length'] >>> [str(x) for x in cxr.Velocity.coord_dimensions(cxc.cart3d)] ['speed/...', 'speed/...', 'speed/...'] >>> [str(x) for x in cxr.Acceleration.coord_dimensions(cxc.cart3d)] ['acceleration', 'acceleration', 'acceleration']
>>> [str(x) for x in cxr.Displacement.coord_dimensions(cxc.sph3d)] ['length', 'angle', 'angle'] >>> [str(x) for x in cxr.Velocity.coord_dimensions(cxc.sph3d)] ['speed/...', 'angular frequency/...', 'angular frequency/...'] >>> [str(x) for x in cxr.Acceleration.coord_dimensions(cxc.sph3d)] ['acceleration', 'angular acceleration', 'angular acceleration']
- Parameters:
chart (
AbstractChart[Any,Any,Any])- Return type:
tuple[PhysicalType|None,...]
- derivative()
Return the semantic kind one step up the time-derivative ladder.
Looks up
self.order + 1in the internal order registry and returns a fresh instance of the registered class. Raises ValueError if no class is registered at that order.This design is open for extension: registering a new AbstractTangentSemanticKind subclass at order
Nautomatically makeskind_at_N_minus_1.derivative()return an instance of that class.- Raises:
ValueError – If no tangent semantic kind is registered at
self.order + 1.- Return type:
Examples
>>> import coordinax.representations as cxr >>> cxr.Displacement().derivative() vel >>> cxr.Velocity().derivative() acc >>> try: ... cxr.Acceleration().derivative() # no Jerk registered yet ... except ValueError as e: ... print(type(e).__name__) ValueError
- antiderivative()
Return the semantic kind one step down the time-derivative ladder.
Looks up
self.order - 1in the internal order registry and returns a fresh instance of the registered class. Raises ValueError if no class is registered at that order.This design is open for extension: registering a new AbstractTangentSemanticKind subclass at order
Nautomatically makeskind_at_N_plus_1.antiderivative()return an instance of that class. For example, once an Absement class is registered at order -1,Displacement().antiderivative()will returnAbsement().- Raises:
ValueError – If no tangent semantic kind is registered at
self.order - 1.- Return type:
Examples
>>> import coordinax.representations as cxr >>> cxr.Acceleration().antiderivative() vel >>> cxr.Velocity().antiderivative() dpl >>> try: ... cxr.Displacement().antiderivative() # no Absement registered yet ... except ValueError as e: ... print(type(e).__name__) ValueError
- final class coordinax.Displacement
Bases:
AbstractTangentSemanticKindDisplacement semantic kind.
A displacement semantic kind indicates that the represented tangent data is a spatial displacement — a finite difference between two nearby points on a manifold, expressed as a tangent vector.
Mathematical Definition:
Let \(M\) be a smooth manifold and \(p, q \in M\) two nearby points. In a chart \((U, \varphi)\), the displacement from \(p\) to \(q\) is the vector \(\Delta x = \varphi(q) - \varphi(p) \in \mathbb{R}^n\). As a tangent vector, this element of \(T_p M\) transforms by the Jacobian of the chart transition map.
Examples
>>> import coordinax.representations as cxr >>> semantic = cxr.Displacement() >>> semantic.canonical_name 'dpl'
- canonical_name: ClassVar = 'dpl'
Canonical name for the displacement semantic kind.
- derivative()
Return the Velocity semantic kind.
Displacement has time-order 0; its time derivative is always Velocity (order 1). This override avoids a dict lookup and makes the intent explicit.
- Return type:
Examples
>>> import coordinax.representations as cxr >>> cxr.Displacement().derivative() vel
- antiderivative()
Return the semantic kind one step down the time-derivative ladder.
Looks up
self.order - 1in the internal order registry and returns a fresh instance of the registered class. Raises ValueError if no class is registered at that order.This design is open for extension: registering a new AbstractTangentSemanticKind subclass at order
Nautomatically makeskind_at_N_plus_1.antiderivative()return an instance of that class. For example, once an Absement class is registered at order -1,Displacement().antiderivative()will returnAbsement().- Raises:
ValueError – If no tangent semantic kind is registered at
self.order - 1.- Return type:
Examples
>>> import coordinax.representations as cxr >>> cxr.Acceleration().antiderivative() vel >>> cxr.Velocity().antiderivative() dpl >>> try: ... cxr.Displacement().antiderivative() # no Absement registered yet ... except ValueError as e: ... print(type(e).__name__) ValueError
- classmethod coord_dimensions(cls, chart: AbstractChart[Any, Any, Any], /)
Return the physical dimensions of each component for this tangent kind.
Each component’s base dimension is taken from
chart.coord_dimensionsand scaled by the appropriate power of time according tocls.order: dimension / time^order.Examples
>>> import coordinax.charts as cxc >>> import coordinax.representations as cxr
>>> [str(x) for x in cxr.Displacement.coord_dimensions(cxc.cart3d)] ['length', 'length', 'length'] >>> [str(x) for x in cxr.Velocity.coord_dimensions(cxc.cart3d)] ['speed/...', 'speed/...', 'speed/...'] >>> [str(x) for x in cxr.Acceleration.coord_dimensions(cxc.cart3d)] ['acceleration', 'acceleration', 'acceleration']
>>> [str(x) for x in cxr.Displacement.coord_dimensions(cxc.sph3d)] ['length', 'angle', 'angle'] >>> [str(x) for x in cxr.Velocity.coord_dimensions(cxc.sph3d)] ['speed/...', 'angular frequency/...', 'angular frequency/...'] >>> [str(x) for x in cxr.Acceleration.coord_dimensions(cxc.sph3d)] ['acceleration', 'angular acceleration', 'angular acceleration']
- Parameters:
chart (
AbstractChart[Any,Any,Any])- Return type:
tuple[PhysicalType|None,...]
- final class coordinax.Velocity
Bases:
AbstractTangentSemanticKindVelocity semantic kind.
A velocity semantic kind indicates that the represented tangent data is a velocity vector — the first time derivative of position on a manifold.
Mathematical Definition:
Let \(\gamma: \mathbb{R} \to M\) be a smooth curve on manifold \(M\). The velocity at time \(t\) is the tangent vector \(\dot{\gamma}(t) \in T_{\gamma(t)} M\). In a chart, velocity components \(\dot{x}^i = d(x^i \circ \gamma)/dt\) transform by the Jacobian of the chart transition map.
Examples
>>> import coordinax.representations as cxr >>> semantic = cxr.Velocity() >>> semantic.canonical_name 'vel'
- canonical_name: ClassVar = 'vel'
Canonical name for the velocity semantic kind.
- classmethod coord_dimensions(cls, chart: AbstractChart[Any, Any, Any], /)
Return the physical dimensions of each component for this tangent kind.
Each component’s base dimension is taken from
chart.coord_dimensionsand scaled by the appropriate power of time according tocls.order: dimension / time^order.Examples
>>> import coordinax.charts as cxc >>> import coordinax.representations as cxr
>>> [str(x) for x in cxr.Displacement.coord_dimensions(cxc.cart3d)] ['length', 'length', 'length'] >>> [str(x) for x in cxr.Velocity.coord_dimensions(cxc.cart3d)] ['speed/...', 'speed/...', 'speed/...'] >>> [str(x) for x in cxr.Acceleration.coord_dimensions(cxc.cart3d)] ['acceleration', 'acceleration', 'acceleration']
>>> [str(x) for x in cxr.Displacement.coord_dimensions(cxc.sph3d)] ['length', 'angle', 'angle'] >>> [str(x) for x in cxr.Velocity.coord_dimensions(cxc.sph3d)] ['speed/...', 'angular frequency/...', 'angular frequency/...'] >>> [str(x) for x in cxr.Acceleration.coord_dimensions(cxc.sph3d)] ['acceleration', 'angular acceleration', 'angular acceleration']
- Parameters:
chart (
AbstractChart[Any,Any,Any])- Return type:
tuple[PhysicalType|None,...]
- derivative()
Return the semantic kind for the time derivative of this velocity.
Velocity has time-order 1. Its time derivative is acceleration (time-order 2).
- Return type:
Examples
>>> import coordinax.representations as cxr >>> cxr.Velocity().derivative() acc
- antiderivative()
Return the semantic kind for the time antiderivative of this velocity.
Velocity has time-order 1. Its time antiderivative is displacement (time-order 0).
- Return type:
Examples
>>> import coordinax.representations as cxr >>> cxr.Velocity().antiderivative() dpl
- final class coordinax.Acceleration
Bases:
AbstractTangentSemanticKindAcceleration semantic kind.
An acceleration semantic kind indicates that the represented tangent data is an acceleration vector — the covariant second derivative of position along a curve on a manifold.
Mathematical Definition:
Let \(\gamma: \mathbb{R} \to M\) be a smooth curve on manifold \(M\). The acceleration at time \(t\) is the tangent vector \(\nabla_{\dot{\gamma}}\dot{\gamma} \in T_{\gamma(t)} M\).
It coincides with the plain second derivative \(\ddot{q}^i\) of the coordinates only in an affine chart – the condition is the chart, not flatness – so in a curvilinear chart the two differ by the Christoffel term \(\Gamma^i_{jk}\dot{q}^j\dot{q}^k\), nonzero even on flat \(\mathbb{R}^3\). This decides what conversion returns: being a tangent vector, an acceleration pushes forward linearly as \(J a\), so converting a trajectory’s Cartesian \(\ddot{q}\) to a curvilinear chart yields the acceleration vector’s components there, not that chart’s \(\ddot{q}\).
Examples
>>> import coordinax.representations as cxr >>> semantic = cxr.Acceleration() >>> semantic.canonical_name 'acc'
- classmethod coord_dimensions(cls, chart: AbstractChart[Any, Any, Any], /)
Return the physical dimensions of each component for this tangent kind.
Each component’s base dimension is taken from
chart.coord_dimensionsand scaled by the appropriate power of time according tocls.order: dimension / time^order.Examples
>>> import coordinax.charts as cxc >>> import coordinax.representations as cxr
>>> [str(x) for x in cxr.Displacement.coord_dimensions(cxc.cart3d)] ['length', 'length', 'length'] >>> [str(x) for x in cxr.Velocity.coord_dimensions(cxc.cart3d)] ['speed/...', 'speed/...', 'speed/...'] >>> [str(x) for x in cxr.Acceleration.coord_dimensions(cxc.cart3d)] ['acceleration', 'acceleration', 'acceleration']
>>> [str(x) for x in cxr.Displacement.coord_dimensions(cxc.sph3d)] ['length', 'angle', 'angle'] >>> [str(x) for x in cxr.Velocity.coord_dimensions(cxc.sph3d)] ['speed/...', 'angular frequency/...', 'angular frequency/...'] >>> [str(x) for x in cxr.Acceleration.coord_dimensions(cxc.sph3d)] ['acceleration', 'angular acceleration', 'angular acceleration']
- Parameters:
chart (
AbstractChart[Any,Any,Any])- Return type:
tuple[PhysicalType|None,...]
- derivative()
Return the semantic kind one step up the time-derivative ladder.
Looks up
self.order + 1in the internal order registry and returns a fresh instance of the registered class. Raises ValueError if no class is registered at that order.This design is open for extension: registering a new AbstractTangentSemanticKind subclass at order
Nautomatically makeskind_at_N_minus_1.derivative()return an instance of that class.- Raises:
ValueError – If no tangent semantic kind is registered at
self.order + 1.- Return type:
Examples
>>> import coordinax.representations as cxr >>> cxr.Displacement().derivative() vel >>> cxr.Velocity().derivative() acc >>> try: ... cxr.Acceleration().derivative() # no Jerk registered yet ... except ValueError as e: ... print(type(e).__name__) ValueError
- canonical_name: ClassVar = 'acc'
Canonical name for the acceleration semantic kind.
- antiderivative()
Return the semantic kind for the time antiderivative of this acceleration.
Acceleration has time-order 2. Its time antiderivative is velocity (time-order 1).
- Return type:
Examples
>>> import coordinax.representations as cxr >>> cxr.Acceleration().antiderivative() vel
- final class coordinax.Representation(geom_kind: GeomT, basis: BasisT, semantic_kind: SemanticT)
Bases:
Generic[GeomT,BasisT,SemanticT]Representation of geometric component data.
A representation specifies what kind of geometric object component data is meant to represent, independently of the chart used to write down the coordinates or components.
In coordinax, a representation is the ordered triple
\[R = (K, B, S),\]where:
\(K\) is the geometric kind (coordinax.representations.AbstractGeometry),
\(B\) is the basis kind (coordinax.representations.AbstractBasis), and
\(S\) is the semantic kind (coordinax.representations.AbstractSemanticKind).
Thus a Representation answers three distinct questions:
What sort of geometric object is this? For example, a point.
In what basis are its components written? For example, no basis for affine point data.
What does the object mean? For example, a location on a manifold.
A representation is therefore not the same thing as a chart.
A chart specifies how a manifold is coordinatized locally: component names, ordering, dimensionalities, and the coordinate map into \(\mathbb{R}^n\).
A representation specifies how the data should be interpreted geometrically.
Equivalently: the chart determines the coordinate system, while the representation determines the geometric role of the data written in that coordinate system.
For the current point-focused design, the canonical representation is
\[(\mathrm{PointGeometry},\, \mathrm{NoBasis},\, \mathrm{Location}).\]This indicates that the data represents a point on a manifold, that it does not live in a basis-dependent linear space, and that its semantic meaning is a location.
- Parameters:
geom_kind (
TypeVar(GeomT, bound=AbstractGeometry)) – The geometric kind of the represented object.basis (
TypeVar(BasisT, bound=AbstractBasis)) – The basis kind in which components are expressed.semantic_kind (
TypeVar(SemanticT, bound=AbstractSemanticKind)) – The semantic interpretation attached to the represented object.
Examples
Construct the canonical point representation directly:
>>> import coordinax.representations as cxr >>> rep = cxr.Representation(cxr.PointGeometry(), cxr.NoBasis(), cxr.Location())
coordinax also provides the predefined point representation:
>>> rep == cxr.point True
Use the representation with cconvert to convert point data between charts while preserving its geometric interpretation:
>>> import coordinax.charts as cxc >>> p = {"x": 1.0, "y": 2.0, "z": 3.0} >>> cxr.cconvert(p, cxc.cart3d, cxr.point, cxc.sph3d, cxr.point) {'r': Array(3.74165739, dtype=float64, ...), 'theta': Array(0.64052231, dtype=float64, ...), 'phi': Array(1.10714872, dtype=float64, ...)}
The output represents the same point, but in the target chart.
Notes
Representation is a static, immutable descriptor object. It carries no runtime numerical data itself; it only describes how such data should be interpreted.
- geom_kind: GeomT
Geometric kind of the represented object.
- basis: BasisT
Basis kind in which components are expressed.
- semantic_kind: SemanticT
Semantic interpretation attached to the represented object.
- coordinax.tangent_map(*args, **kwargs)
Compute the tangent map (Jacobian) of a chart transition.
This is an abstract API definition. See the main coordinax package for concrete implementations.
Examples
>>> import jax.numpy as jnp >>> import coordinax.charts as cxc >>> import coordinax.representations as cxr
- coordinax.tangent_map(v: Any, from_chart: AbstractChart, basis: CoordinateBasis, to_chart: AbstractChart, /, *, at: dict | None = None, usys: AbstractUnitSystem | None = None) dict
Push a tangent vector forward from one chart to another.
Applies the Jacobian of the chart transition map to the tangent vector components
v, evaluated at the base pointat.Convert a tangent vector from Cartesian to polar 2D at the point (1, 0):
>>> import jax.numpy as jnp >>> import coordinax.charts as cxc >>> import coordinax.representations as cxr
>>> v = {"x": jnp.array(1.0), "y": jnp.array(0.0)} >>> at = {"x": jnp.array(1.0), "y": jnp.array(0.0)} >>> cxr.tangent_map(v, cxc.cart2d, cxr.coord_disp, cxc.polar2d, at=at) {'r': Array(1., dtype=float64), 'theta': Array(0., dtype=float64)}
- coordinax.tangent_map(v: dict, from_chart: AbstractChart, basis: PhysicalBasis, to_chart: AbstractChart, /, *, at: dict | None = None, usys: AbstractUnitSystem | None = None) dict
Push a tangent vector forward in physical-basis components.
This dispatch applies the tangent-map pushforward while preserving the physical-basis convention by composing three steps:
convert source components from physical basis to coordinate basis,
apply the chart Jacobian pushforward,
convert target components back to physical basis.
Convert a physical-basis tangent vector from Cartesian to spherical 3D:
>>> import unxt as u >>> import coordinax.charts as cxc >>> import coordinax.representations as cxr
>>> v = {"x": u.Q(1, "m/s"), "y": u.Q(0, "m/s"), "z": u.Q(0, "m/s")} >>> at = {"x": u.Q(1, "m"), "y": u.Q(0, "m"), "z": u.Q(0, "m")} >>> cxr.tangent_map(v, cxc.cart3d, cxr.phys_basis, cxc.sph3d, at=at) {'r': Q(1., 'm / s'), 'theta': Q(0., 'm / s'), 'phi': Q(0., 'm / s')}
The same call can be made using a physical representation:
>>> v = {"x": 1, "y": 0, "z": 0} >>> at = {"x": 1, "y": 0, "z": 0} >>> usys = u.unitsystems.si >>> cxr.tangent_map(v, cxc.cart3d, cxr.phys_disp, cxc.sph3d, at=at, usys=usys) {'r': Array(1., dtype=float64), 'theta': Array(0., dtype=float64), 'phi': Array(0., dtype=float64)}
- coordinax.tangent_map(v: Any, from_chart: AbstractChart, from_rep: Representation, to_chart: AbstractChart, /, *, at: dict | None = None, usys: AbstractUnitSystem | None = None) dict
Push a tangent vector forward from one chart to another.
Applies the Jacobian of the chart transition map to the tangent vector components
v, evaluated at the base pointat.Convert a tangent vector from Cartesian to polar 2D at the point (1, 0):
>>> import jax.numpy as jnp >>> import coordinax.charts as cxc >>> import coordinax.representations as cxr
>>> v = {"x": jnp.array(1.0), "y": jnp.array(0.0)} >>> at = {"x": jnp.array(1.0), "y": jnp.array(0.0)} >>> cxr.tangent_map(v, cxc.cart2d, cxr.coord_disp, cxc.polar2d, at=at) {'r': Array(1., dtype=float64), 'theta': Array(0., dtype=float64)}
- coordinax.tangent_map(v: Any, from_chart: AbstractChart, from_rep: Representation, to_chart: AbstractChart, to_rep: Representation, /, *, at: dict | None = None, usys: AbstractUnitSystem | None = None) dict
Push a tangent vector forward from one chart to another.
Applies the Jacobian of the chart transition map to the tangent vector components
v, evaluated at the base pointat.Convert a tangent vector from Cartesian to polar 2D at the point (1, 0):
>>> import jax.numpy as jnp >>> import coordinax.charts as cxc >>> import coordinax.representations as cxr
>>> v = {"x": jnp.array(1.0), "y": jnp.array(0.0)} >>> at = {"x": jnp.array(1.0), "y": jnp.array(0.0)} >>> cxr.tangent_map(v, cxc.cart2d, cxr.coord_disp, ... cxc.polar2d, cxr.coord_disp, at=at) {'r': Array(1., dtype=float64), 'theta': Array(0., dtype=float64)}
- coordinax.change_basis(*args, **kwargs)
Change the basis of a tangent vector’s components.
Examples
>>> import coordinax.representations as cxr >>> import coordinax.charts as cxc >>> v = {"x": 1.0, "y": 0.0} >>> at = {"x": 1.0, "y": 0.0} >>> cxr.change_basis(v, cxc.cart2d, cxr.coord_basis, cxr.phys_basis, at=at) {'x': 1.0, 'y': 0.0}
- coordinax.change_basis(v: dict, chart: AbstractChart, M: AbstractManifold, from_basis: CoordinateBasis, to_basis: PhysicalBasis, /, *, at: dict, usys: AbstractUnitSystem | None = None) dict
Change from coordinate basis to physical basis using a manifold.
Retrieves the manifold’s metric and applies the appropriate transformation. For diagonal metrics (e.g. coordinax.manifolds.FlatMetric in orthogonal charts) the fast scale-factor path is taken; for general metrics (e.g. coordinax.manifolds.PullbackMetric) the Cholesky vielbein \(E = L^\top\) is used.
>>> import jax.numpy as jnp >>> import unxt as u >>> import coordinax.charts as cxc >>> import coordinax.manifolds as cxm >>> import coordinax.representations as cxr
Euclidean 3-D manifold in spherical coordinates (diagonal metric):
>>> M3 = cxm.R3 >>> v = {"r": u.Q(5, "m/s"), "theta": u.Q(1, "rad/s"), "phi": u.Q(2, "rad/s")} >>> at = {"r": u.Q(3, "m"), "theta": u.Q(0.5, "rad"), "phi": u.Q(0, "rad")} >>> cxr.change_basis(v, cxc.sph3d, M3, cxr.coord_basis, cxr.phys_basis, at=at) {'r': Q(5, 'm / s'), 'theta': Q(3, 'm / s'), 'phi': Q(2.87655323, 'm / s')}
Embedded two-sphere manifold — non-diagonal
PullbackMetric:>>> M = cxm.EmbeddedManifold( ... intrinsic=cxm.S2, ambient=cxm.R3, ... embed_map=cxm.TwoSphereIn3D(radius=u.Q(1.0, "km")), ... ) >>> v = {"theta": u.Q(1.0, "rad/s"), "phi": u.Q(2.0, "rad/s")} >>> at = {"theta": u.Q(jnp.pi / 3, "rad"), "phi": u.Q(0.0, "rad")} >>> cxr.change_basis(v, cxc.sph2, M, cxr.coord_basis, cxr.phys_basis, at=at) {'theta': Q(..., 'km / s'), 'phi': Q(..., 'km / s')}
- coordinax.change_basis(v: dict, chart: AbstractChart, M: AbstractManifold, from_basis: PhysicalBasis, to_basis: CoordinateBasis, /, *, at: dict, usys: AbstractUnitSystem | None = None) dict
Change from physical basis to coordinate basis using a manifold.
Retrieves the manifold’s metric and applies the inverse transformation. For diagonal metrics the fast scale-factor path is taken; for general metrics the Cholesky vielbein \(E = L^\top\) is solved as a triangular system \(v = E^{-1}\hat{v}\).
>>> import jax.numpy as jnp >>> import unxt as u >>> import coordinax.charts as cxc >>> import coordinax.manifolds as cxm >>> import coordinax.representations as cxr
Euclidean 3-D manifold in spherical coordinates (diagonal metric):
>>> M3 = cxm.R3 >>> v = {"r": u.Q(5, "m/s"), "theta": u.Q(3, "m/s"), "phi": u.Q(2.876553, "m/s")} >>> at = {"r": u.Q(3, "m"), "theta": u.Q(0.5, "rad"), "phi": u.Q(0.5, "rad")} >>> cxr.change_basis(v, cxc.sph3d, M3, cxr.phys_basis, cxr.coord_basis, at=at) {'r': Q(5, 'm / s'), 'theta': Q(1., 'rad / s'), 'phi': Q(1.99999..., 'rad / s')}
Embedded two-sphere manifold — non-diagonal
PullbackMetric:>>> M = cxm.EmbeddedManifold( ... intrinsic=cxm.S2, ambient=cxm.R3, ... embed_map=cxm.TwoSphereIn3D(radius=u.Q(1.0, "km")), ... ) >>> v = {"theta": u.Q(1.0, "km/s"), "phi": u.Q(2.0, "km/s")} >>> at = {"theta": u.Q(jnp.pi / 3, "rad"), "phi": u.Q(0.0, "rad")} >>> cxr.change_basis(v, cxc.sph2, M, cxr.phys_basis, cxr.coord_basis, at=at) {'theta': Q(..., 'rad / s'), 'phi': Q(..., 'rad / s')}
- coordinax.change_basis(v: dict, chart: AbstractChart, from_basis: CoordinateBasis, to_basis: PhysicalBasis, /, *, at: dict, usys: AbstractUnitSystem | None = None) dict
Change from coordinate basis to physical basis using the chart’s manifold.
Falls back to
chart.Mwhen no explicit manifold is supplied, mirroring the physical-to-coordinate rule below. Without it only the charts with a bespoke rule – the Cartesian family and ~coordinax.charts.Spherical3D – could make this change without being handed a manifold, and every other orthogonal chart raised NotFoundLookupError.>>> import unxt as u >>> import coordinax.charts as cxc >>> import coordinax.representations as cxr
>>> v = {"rho": u.Q(1.0, "m/s"), "phi": u.Q(2.0, "rad/s"), "z": u.Q(3.0, "m/s")} >>> at = {"rho": u.Q(2.0, "m"), "phi": u.Q(0.4, "rad"), "z": u.Q(1.0, "m")} >>> cxr.change_basis(v, cxc.cyl3d, cxr.coord_basis, cxr.phys_basis, at=at) {'rho': Q(1., 'm / s'), 'phi': Q(4., 'm / s'), 'z': Q(3., 'm / s')}
- coordinax.change_basis(v: dict, chart: AbstractChart, from_basis: PhysicalBasis, to_basis: CoordinateBasis, /, *, at: dict, usys: AbstractUnitSystem | None = None) dict
Change from physical basis to coordinate basis using the chart’s manifold.
Falls back to
chart.Mwhen no explicit manifold is supplied.>>> import unxt as u >>> import coordinax.charts as cxc >>> import coordinax.representations as cxr
>>> v = {"r": u.Q(5, "m/s"), "theta": u.Q(3, "m/s"), "phi": u.Q(2.876553, "m/s")} >>> at = {"r": u.Q(3, "m"), "theta": u.Q(0.5, "rad"), "phi": u.Q(0.5, "rad")} >>> cxr.change_basis(v, cxc.sph3d, cxr.phys_basis, cxr.coord_basis, at=at) {'r': Q(5, 'm / s'), 'theta': Q(1., 'rad / s'), 'phi': Q(1.99999984, 'rad / s')}
- coordinax.change_basis(v: dict, chart: AbstractChart, from_basis: NoBasis | CoordinateBasis, to_basis: CoordinateBasis, /, **kw: Any) dict
Reinterpret unknown basis components as coordinate-basis components.
This is an identity on component values: no numeric basis transform is possible from an unknown basis, so values are preserved and only the representation-level basis label changes.
>>> import jax.numpy as jnp >>> import coordinax.charts as cxc >>> import coordinax.representations as cxr
The conversion is an identity map on values:
>>> v = {"x": jnp.array(1.0), "y": jnp.array(-2.0)} >>> at = {"x": jnp.array(0.0), "y": jnp.array(0.0)} >>> cxr.change_basis(v, cxc.cart2d, cxr.no_basis, cxr.coord_basis, at=at) {'x': Array(1., dtype=float64, ...), 'y': Array(-2., dtype=float64, ...)}
“at” is accepted and ignored for this basis-only reinterpretation:
>>> cxr.change_basis(v, cxc.cart2d, cxr.no_basis, cxr.coord_basis) {'x': Array(1., dtype=float64, ...), 'y': Array(-2., dtype=float64, ...)}
- coordinax.change_basis(v: dict, chart: AbstractChart, from_basis: NoBasis | PhysicalBasis, to_basis: PhysicalBasis, /, **kw: Any) dict
Reinterpret unknown basis components as physical-basis components.
This conversion is only well-defined when all components share the same physical dimension. No numeric transform is applied; values are preserved.
>>> import jax.numpy as jnp >>> import unxt as u >>> import coordinax.charts as cxc >>> import coordinax.representations as cxr
Same-dimension components (even with different units) are accepted:
>>> v = {"x": u.Q(1.0, "m / s"), "y": u.Q(2.0, "km / s")} >>> at = {"x": jnp.array(0.0), "y": jnp.array(0.0)} >>> cxr.change_basis(v, cxc.cart2d, cxr.no_basis, cxr.phys_basis, at=at) {'x': Q(1., 'm / s'), 'y': Q(2., 'km / s')}
Mixed dimensions are rejected:
>>> v_bad = {"x": u.Q(1.0, "m / s"), "y": u.Q(2.0, "m")} >>> cxr.change_basis(v_bad, cxc.cart2d, cxr.no_basis, cxr.phys_basis) Traceback (most recent call last): ... ValueError: change_basis from NoBasis to PhysicalBasis requires all components to have the same dimension, got ...
- coordinax.change_basis(v: dict, chart: Cart0D | Cart1D | Cart2D | Cart3D | CartND, from_basis: CoordinateBasis | PhysicalBasis, to_basis: CoordinateBasis | PhysicalBasis, /, **kw: Any) dict
Change the basis used to interpret tangent components.
In a Cartesian chart every scale factor equals one,
\[h_x = h_y = h_z = 1,\]so the coordinate basis vectors are already unit vectors (\(\hat{e}_i = \partial_i\)) and the transformation matrix is the identity (\(H = I\)). The coordinate basis is the physical basis, so this conversion is always the identity map and
vis returned unchanged regardless of the direction of the conversion.>>> import unxt as u >>> import coordinax.charts as cxc >>> import coordinax.representations as cxr
Coordinate basis to physical basis in a 2-D Cartesian chart — identity:
>>> v = {"x": u.Q(3.0, "m/s"), "y": u.Q(4.0, "m/s")} >>> at = {"x": u.Q(1.0, "m"), "y": u.Q(2.0, "m")} >>> cxr.change_basis(v, cxc.cart2d, cxr.coord_basis, cxr.phys_basis, at=at) {'x': Q(3., 'm / s'), 'y': Q(4., 'm / s')}
The reverse direction is equally a no-op:
>>> cxr.change_basis(v, cxc.cart2d, cxr.phys_basis, cxr.coord_basis, at=at) {'x': Q(3., 'm / s'), 'y': Q(4., 'm / s')}
Works for any Cartesian dimensionality;
atis optional:>>> v3 = {"x": u.Q(1.0, "m/s"), "y": u.Q(2.0, "m/s"), "z": u.Q(3.0, "m/s")} >>> cxr.change_basis(v3, cxc.cart3d, cxr.coord_basis, cxr.phys_basis) {'x': Q(1., 'm / s'), 'y': Q(2., 'm / s'), 'z': Q(3., 'm / s')}
- coordinax.change_basis(v: dict, chart: Cart0D | Cart1D | Cart2D | Cart3D | CartND, M: AbstractManifold, from_basis: AbstractLinearBasis, to_basis: AbstractLinearBasis, /, **kw: Any) dict
Change the basis used to interpret tangent components.
In a Cartesian chart every scale factor equals one,
\[h_x = h_y = h_z = 1,\]so the coordinate basis vectors are already unit vectors (\(\hat{e}_i = \partial_i\)) and the transformation matrix is the identity (\(H = I\)). The coordinate basis is the physical basis, so this conversion is always the identity map and
vis returned unchanged regardless of the direction of the conversion.>>> import unxt as u >>> import coordinax.charts as cxc >>> import coordinax.manifolds as cxm >>> import coordinax.representations as cxr
Coordinate basis to physical basis in a 2-D Cartesian chart — identity:
>>> v = {"x": u.Q(3.0, "m/s"), "y": u.Q(4.0, "m/s")} >>> at = {"x": u.Q(1.0, "m"), "y": u.Q(2.0, "m")} >>> M2 = cxm.R2 >>> cxr.change_basis(v, cxc.cart2d, M2, cxr.coord_basis, cxr.phys_basis, at=at) {'x': Q(3., 'm / s'), 'y': Q(4., 'm / s')}
The reverse direction is equally a no-op:
>>> cxr.change_basis(v, cxc.cart2d, M2, cxr.phys_basis, cxr.coord_basis, at=at) {'x': Q(3., 'm / s'), 'y': Q(4., 'm / s')}
Works for any Cartesian dimensionality;
atis optional:>>> v3 = {"x": u.Q(1.0, "m/s"), "y": u.Q(2.0, "m/s"), "z": u.Q(3.0, "m/s")} >>> cxr.change_basis(v3, cxc.cart3d, cxm.R3, cxr.coord_basis, cxr.phys_basis) {'x': Q(1., 'm / s'), 'y': Q(2., 'm / s'), 'z': Q(3., 'm / s')}
- coordinax.change_basis(v: dict, chart: Spherical3D, from_basis: CoordinateBasis, to_basis: PhysicalBasis, /, *, at: dict, usys: AbstractUnitSystem | None = None) dict
Change from coordinate basis to physical basis in a 3-D spherical chart.
In spherical coordinates \((r, \theta, \phi)\) the scale factors are
\[h_r = 1, \quad h_\theta = r, \quad h_\phi = r \sin\theta,\]so the transformation matrix is
\[\begin{split}H = \begin{pmatrix} 1 & 0 & 0 \\ 0 & r & 0 \\ 0 & 0 & r\sin\theta \end{pmatrix}.\end{split}\]Given coordinate-basis components \((v^r, v^\theta, v^\phi)\), the physical-basis components are
\[\hat{v} = H v \implies \hat{v}^r = v^r, \quad \hat{v}^\theta = r\, v^\theta, \quad \hat{v}^\phi = r\sin\theta\, v^\phi.\]Examples
>>> import unxt as u >>> import coordinax.charts as cxc >>> import coordinax.representations as cxr
>>> v = {"r": u.Q(5, "m/s"), "theta": u.Q(1, "rad/s"), "phi": u.Q(2, "rad/s")} >>> at = {"r": u.Q(3, "m"), "theta": u.Q(0.5, "rad"), "phi": u.Q(0, "rad")} >>> cxr.change_basis(v, cxc.sph3d, cxr.coord_basis, cxr.phys_basis, at=at) {'r': Q(5, 'm / s'), 'theta': Q(3, 'm / s'), 'phi': Q(2.87655323, 'm / s')}
>>> v = {"r": 5, "theta": 1, "phi": 2} # unitless >>> at = {"r": 3, "theta": 0.5, "phi": 0} # unitless >>> cxr.change_basis(v, cxc.sph3d, cxr.coord_basis, cxr.phys_basis, at=at) {'r': 5, 'theta': 3, 'phi': Array(2.87655323, dtype=float64, ...)}
- coordinax.change_basis(v: dict, chart: Spherical3D, M: EuclideanManifold, from_basis: CoordinateBasis, to_basis: PhysicalBasis, /, *, at: dict, usys: AbstractUnitSystem | None = None) dict
Change from coordinate basis to physical basis in a 3-D spherical chart.
Delegates to the chart-specific implementation for coordinax.manifolds.EuclideanManifold.
>>> import unxt as u >>> import coordinax.charts as cxc >>> import coordinax.manifolds as cxm >>> import coordinax.representations as cxr
>>> v = {"r": u.Q(5, "m/s"), "theta": u.Q(1, "rad/s"), "phi": u.Q(2, "rad/s")} >>> at = {"r": u.Q(3, "m"), "theta": u.Q(0.5, "rad"), "phi": u.Q(0, "rad")} >>> cxr.change_basis(v, cxc.sph3d, cxm.R3, cxr.coord_basis, cxr.phys_basis, at=at) {'r': Q(5, 'm / s'), 'theta': Q(3, 'm / s'), 'phi': Q(2.87655323, 'm / s')}
>>> v = {"r": 5, "theta": 1, "phi": 2} # unitless >>> at = {"r": 3, "theta": 0.5, "phi": 0} # unitless >>> cxr.change_basis(v, cxc.sph3d, cxm.R3, cxr.coord_basis, cxr.phys_basis, at=at) {'r': 5, 'theta': 3, 'phi': Array(2.87655323, dtype=float64, ...)}
- coordinax.change_basis(v: dict, chart: Spherical3D, from_basis: PhysicalBasis, to_basis: CoordinateBasis, /, *, at: dict, usys: AbstractUnitSystem | None = None) dict
Change from physical basis to coordinate basis in a 3-D spherical chart.
In spherical coordinates \((r, \theta, \phi)\) the inverse transformation matrix is
\[\begin{split}H^{-1} = \begin{pmatrix} 1 & 0 & 0 \\ 0 & 1/r & 0 \\ 0 & 0 & 1/(r\sin\theta) \end{pmatrix}.\end{split}\]Given physical-basis components \((\hat{v}^r, \hat{v}^\theta, \hat{v}^\phi)\), the coordinate-basis components are
\[v = H^{-1} \hat{v} \implies v^r = \hat{v}^r, \quad v^\theta = \hat{v}^\theta / r, \quad v^\phi = \hat{v}^\phi / (r\sin\theta).\]Examples
>>> import unxt as u >>> import coordinax.charts as cxc >>> import coordinax.representations as cxr
>>> v = {"r": u.Q(5, "m/s"), "theta": u.Q(3, "m/s"), "phi": u.Q(2.876553, "m/s")} >>> at = {"r": u.Q(3, "m"), "theta": u.Q(0.5, "rad"), "phi": u.Q(0.5, "rad")} >>> cxr.change_basis(v, cxc.sph3d, cxr.phys_basis, cxr.coord_basis, at=at) {'r': Q(5, 'm / s'), 'theta': Q(1., 'rad / s'), 'phi': Q(1.99999984, 'rad / s')}
>>> v = {"r": 5, "theta": 3, "phi": 2.876553} # unitless >>> at = {"r": 3, "theta": 0.5, "phi": 0.5} # unitless >>> cxr.change_basis(v, cxc.sph3d, cxr.phys_basis, cxr.coord_basis, at=at) {'r': 5, 'theta': 1.0, 'phi': Array(1.99999984, dtype=float64, ...)}
- coordinax.change_basis(v: dict, chart: Spherical3D, M: EuclideanManifold, from_basis: PhysicalBasis, to_basis: CoordinateBasis, /, *, at: dict, usys: AbstractUnitSystem | None = None) dict
Change from physical basis to coordinate basis in a 3-D spherical chart.
Delegates to the chart-specific implementation for
EuclideanManifold.>>> import unxt as u >>> import coordinax.charts as cxc >>> import coordinax.manifolds as cxm >>> import coordinax.representations as cxr
>>> v = {"r": u.Q(5, "m/s"), "theta": u.Q(3, "m/s"), "phi": u.Q(2.876553, "m/s")} >>> at = {"r": u.Q(3, "m"), "theta": u.Q(0.5, "rad"), "phi": u.Q(0.5, "rad")} >>> cxr.change_basis(v, cxc.sph3d, cxm.R3, cxr.phys_basis, cxr.coord_basis, at=at) {'r': Q(5, 'm / s'), 'theta': Q(1., 'rad / s'), 'phi': Q(1.99999984, 'rad / s')}
>>> v = {"r": 5, "theta": 3, "phi": 2.876553} # unitless >>> at = {"r": 3, "theta": 0.5, "phi": 0.5} # unitless >>> cxr.change_basis(v, cxc.sph3d, cxm.R3, cxr.phys_basis, cxr.coord_basis, at=at) {'r': 5, 'theta': 1.0, 'phi': Array(1.99999984, dtype=float64, ...)}
- coordinax.change_basis(v: dict, chart: AbstractChart, from_rep: Representation, to_rep: Representation, /, *, at: dict, usys: AbstractUnitSystem | None = None) dict
Change basis using source and/or target
Representationobjects.This is a convenience overload: the caller may pass full
Representationobjects forfrom_rep/to_repinstead of bareAbstractBasisinstances. The basis is extracted from each argument and the appropriatechange_basis()overload is called.>>> import unxt as u >>> import coordinax.charts as cxc >>> import coordinax.representations as cxr
Coordinate-basis displacement to physical-basis displacement in a spherical chart, passing full
Representationobjects:>>> v = {"r": u.Q(5, "m/s"), "theta": u.Q(1, "rad/s"), "phi": u.Q(2, "rad/s")} >>> at = {"r": u.Q(3, "m"), "theta": u.Q(0.5, "rad"), "phi": u.Q(0, "rad")} >>> cxr.change_basis(v, cxc.sph3d, cxr.coord_disp, cxr.phys_disp, at=at) {'r': Q(5, 'm / s'), 'theta': Q(3, 'm / s'), 'phi': Q(2.87655323, 'm / s')}
>>> v = {"r": 5, "theta": 1, "phi": 2} # unitless >>> at = {"r": 3, "theta": 0.5, "phi": 0} # unitless >>> cxr.change_basis(v, cxc.sph3d, cxr.coord_disp, cxr.phys_disp, at=at) {'r': 5, 'theta': 3, 'phi': Array(2.87655323, dtype=float64, ...)}
- coordinax.change_basis(v: Tangent, to_basis: AbstractLinearBasis, /, *, at: Any = None, usys: AbstractUnitSystem | None = None) Tangent
Change the basis of a Tangent vector.
Converts the component data from the current basis to
to_basisusing the registeredchange_basisoverload for dicts, then returns a new Tangent with the updated data and basis.The
atparameter provides the base point at which the scale factors are evaluated. It may be a Point instance (whose.datais used) or a rawCDict.>>> import unxt as u >>> import coordinax as cx >>> import coordinax.charts as cxc >>> import coordinax.representations as cxr
Convert a coordinate-basis spherical velocity to the physical basis:
>>> point = cx.Point.from_( ... {"r": u.Q(1.0, "m"), "theta": u.Q(0.5, "rad"), "phi": u.Q(0.0, "rad")}, ... cxc.sph3d, ... ) >>> vel = cx.Tangent.from_({"r": u.Q(1.0, "m/s"), "theta": u.Q(0.0, "rad/s"), ... "phi": u.Q(0.0, "rad/s")}, cxc.sph3d, cxr.coord_vel) >>> vel_phys = cxr.change_basis(vel, cxr.phys_basis, at=point) >>> vel_phys.basis phys_basis >>> vel_phys.rep phys_vel
- coordinax.change_basis(v: Point, to_basis: AbstractLinearBasis, /, *, at: Any = None, usys: Any = None) Tangent
Promote a Point to a Tangent with Displacement semantics.
The component data are unchanged; only the geometric interpretation is recast from a manifold point (PointGeometry) to a tangent-space displacement vector (TangentGeometry, Displacement). The resulting Tangent carries the same chart and frame as the input Point, and its basis is
to_basis.The
atandusysparameters are accepted for API consistency but are not used.>>> import unxt as u >>> import coordinax as cx >>> import coordinax.representations as cxr
>>> pt = cx.Point.from_([1.0, 2.0, 3.0], "m") >>> disp = cxr.change_basis(pt, cxr.coord_basis) >>> disp.semantic dpl >>> disp.basis coord_basis >>> disp.chart == pt.chart True
- final class coordinax.Point(data: dict[str, Any], chart: ChartT, frame: Any = NoFrame())
Bases:
AstropyRepresentationAPIMixin,NumpyInvertMixin[Any],LaxLenMixin,AbstractVector[ChartT,PointGeometry,NoBasis,Location,V],Generic[ChartT,V]A coordinate-carrying geometric point.
A Point stores three pieces of information:
data: a mapping from component name to scalar-like value (typically unxt.Quantity),
chart: a chart object describing the coordinate system and component schema, and
rep: a representation describing the geometric meaning of the components and therefore the correct transformation law.
The design goal is to make the public API simple (construct, convert, index) while keeping the mathematics correct and the numerical kernels JAX-friendly (operate on scalar leaves; rely on jit/vmap).
Mathematical background:
Let \(M\) be a manifold and let \((U,\varphi)\) be a chart with coordinate map \(\varphi: U \to \mathbb{R}^n\). Coordinax distinguishes:
- Point (representation
cxr.point) A point \(p \in M\) represented by its chart coordinates \(q = \varphi(p)\). A point transforms by coordinate change: \(q' = (\varphi' \circ \varphi^{-1})(q)\).
In Euclidean charts, point coordinates may have heterogeneous physical dimensions (e.g. spherical \((r,\theta,\phi)\) mixes length and angle). This is expected.
- Parameters:
data (
dict[str,Any]) – Mapping from chart component name to scalar value. Each leaf may be a unxt.Quantity (recommended) or an array-like. Components are expected to be scalar leaves; batching happens via broadcasting of these leaves.chart (
TypeVar(ChartT, bound=AbstractChart[Any,Any,Any])) – A chart instance (e.g. cxc.cart3d, cxc.sph3d) that defines component names and per-component physical dimensions.frame (AbstractReferenceFrame)
Examples
Construct a point in Cartesian 3D and convert to spherical:
>>> import coordinax as cx >>> import coordinax.charts as cxc >>> import unxt as u >>> cart = cx.Point.from_({"x": u.Q(1, "m"), "y": u.Q(1, "m"), "z": u.Q(1, "m")}, ... cxc.cart3d) >>> sph = cart.cconvert(cxc.sph3d) >>> sph["r"] Q(1.73205081, 'm')
Notes
Notes on units and array shape:
A Point does not require that all components share one unit. This is essential for charts like spherical coordinates where point components naturally mix dimensions.
Batching is represented by broadcasting the component leaves; the conceptual shape of the Point is broadcast_shapes(*(v.shape for v in data.values())).
Core operations:
Indexing:
vec["x"]returns a component leaf.Conversion:
vec.cconvert(target_chart, at=...)converts the vector to target_chart. ForPointthis is a coordinate transform.
- chart: ChartT
The chart of the vector, e.g. cxc.cart3d.
- frame: AbstractReferenceFrame = NoFrame()
The reference frame of the point. Defaults to
cxf.noframe.
- property M: AbstractManifold
The manifold of the vector, from the chart.
- astype(dtype: Any, /, **kwargs: Any)
Cast the vector to a new dtype.
Examples
>>> import quaxed.numpy as jnp >>> import unxt as u >>> import coordinax as cx
We can cast a vector to a new dtype:
>>> vec = cx.Point.from_(u.Q([1, 2, 3], "m")) >>> print(vec.astype(jnp.float32)) <Point: chart=Cart3D (x, y, z) [m] [1. 2. 3.]>
>>> print(jnp.astype(vec, jnp.float32)) <Point: chart=Cart3D (x, y, z) [m] [1. 2. 3.]>
- cconvert(*args, **kwargs)
Represent the vector as another type.
This forwards to coordinax.representations.cconvert.
Examples
>>> import coordinax as cx
>>> vec = cx.Point.from_([1, 2, 3], "m") >>> print(vec) <Point: chart=Cart3D (x, y, z) [m] [1 2 3]>
>>> print(vec.cconvert(cx.sph3d)) <Point: chart=Spherical3D (r[m], theta[rad], phi[rad]) [3.742 0.641 1.107]>
- copy()
Return a copy of the vector.
- Return type:
Self
Examples
>>> import coordinax as cx
>>> vec = cx.Point.from_([1, 2, 3], "m") >>> print(vec.copy()) <Point: chart=Cart3D (x, y, z) [m] [1 2 3]>
- property dtype: Any
- flatten()
Flatten the vector.
- Return type:
Self
- classmethod from_(*args, **kwargs)
Create a vector-like object from arguments.
Construct a point from another point.
>>> import coordinax as cx >>> vec1 = cx.Point.from_([1, 2, 3], "m") >>> vec2 = cx.Point.from_(vec1) >>> print(vec2) <Point: chart=Cart3D (x, y, z) [m] [1 2 3]>
- from_(cls: type[Point], obj: Any, chart: AbstractChart, rep: Representation, /) Point
- Parameters:
- Return type:
Construct a vector from an object, and chart and rep info.
>>> import jax.numpy as jnp >>> import unxt as u >>> import coordinax as cx
>>> xs = {"x": u.Q(1, "m"), "y": u.Q(2, "m"), "z": u.Q(3, "m")} >>> vec = cx.Point.from_(xs, cx.cart3d, cx.point) >>> print(vec) <Point: chart=Cart3D (x, y, z) [m] [1 2 3]>
>>> xs = u.Q(jnp.array([[1, 2, 3], [4, 5, 6]]), "m") >>> vec = cx.Point.from_(xs, cx.cart3d, cx.point) >>> print(vec) <Point: chart=Cart3D (x, y, z) [m] [[1 2 3] [4 5 6]]>
- from_(cls: type[Point], obj: Any, chart: AbstractChart, /) Point
- Parameters:
- Return type:
Construct a point from an object, and chart info.
>>> import jax.numpy as jnp >>> import unxt as u >>> import coordinax as cx
>>> xs = {"x": u.Q(1, "m"), "y": u.Q(2, "m"), "z": u.Q(3, "m")} >>> vec = cx.Point.from_(xs, cx.cart3d) >>> print(vec) <Point: chart=Cart3D (x, y, z) [m] [1 2 3]>
>>> xs = {"x": u.Q([1, 2], "m"), "y": u.Q([3, 4], "m"), "z": u.Q([5, 6], "m")} >>> vec = cx.Point.from_(xs, cx.cart3d) >>> print(vec) <Point: chart=Cart3D (x, y, z) [m] [[1 3 5] [2 4 6]]>
>>> xs = u.Q(jnp.array([[1, 2, 3], [4, 5, 6]]), "m") >>> vec = cx.Point.from_(xs, cx.cart3d) >>> print(vec) <Point: chart=Cart3D (x, y, z) [m] [[1 2 3] [4 5 6]]>
- from_(cls: type[Point], obj: Any, rep: Representation, /) Point
- Parameters:
- Return type:
Construct a point from an object, and rep info.
>>> import jax.numpy as jnp >>> import unxt as u >>> import coordinax as cx
>>> xs = {"x": u.Q(1, "m"), "y": u.Q(2, "m"), "z": u.Q(3, "m")} >>> vec = cx.Point.from_(xs, cx.point) >>> print(vec) <Point: chart=Cart3D (x, y, z) [m] [1 2 3]>
>>> xs = {"x": u.Q([1, 2], "m"), "y": u.Q([3, 4], "m"), "z": u.Q([5, 6], "m")} >>> vec = cx.Point.from_(xs, cx.point) >>> print(vec) <Point: chart=Cart3D (x, y, z) [m] [[1 3 5] [2 4 6]]>
>>> xs = u.Q(jnp.array([[1, 2, 3], [4, 5, 6]]), "m") >>> vec = cx.Point.from_(xs, cx.point) >>> print(vec) <Point: chart=Cart3D (x, y, z) [m] [[1 2 3] [4 5 6]]>
Construct a point from an object.
Note that this is a pretty limited constructor since it often lacks the necessary information to do a proper construction.
>>> import jax.numpy as jnp >>> import unxt as u >>> import coordinax as cx
>>> xs = {"x": u.Q(1, "m"), "y": u.Q(2, "m"), "z": u.Q(3, "m")} >>> vec = cx.Point.from_(xs) >>> print(vec) <Point: chart=Cart3D (x, y, z) [m] [1 2 3]>
>>> vec = cx.Point.from_(u.Q([1, 2, 3], "m")) >>> print(vec) <Point: chart=Cart3D (x, y, z) [m] [1 2 3]>
- from_(cls: type[Point], obj: ArrayLike | list[Any], unit: UnitBase | FunctionUnitBase | str, /) Any
- Parameters:
- Return type:
Construct a cartesian vector from an array and unit.
The
ArrayLike[Any, (*#batch, N), "..."]is expected to have the components as the last dimension.>>> import jax.numpy as jnp >>> import coordinax as cx
>>> vec = cx.Point.from_([1, 2, 3], "meter") >>> print(vec) <Point: chart=Cart3D (x, y, z) [m] [1 2 3]>
>>> xs = jnp.array([[1, 2, 3], [4, 5, 6]]) >>> vec = cx.Point.from_(xs, "meter") >>> print(vec) <Point: chart=Cart3D (x, y, z) [m] [[1 2 3] [4 5 6]]>
- from_(cls: type[Point], obj: ArrayLike | list[Any], unit: UnitBase | FunctionUnitBase | str, chart: AbstractChart, /) Any
- Parameters:
- Return type:
Construct a vector from an array, unit, and chart.
>>> import jax.numpy as jnp >>> import coordinax as cx
>>> vec = cx.Point.from_([1, 2, 3], "m", cx.cart3d) >>> print(vec) <Point: chart=Cart3D (x, y, z) [m] [1 2 3]>
>>> xs = jnp.array([[1, 2, 3], [4, 5, 6]]) >>> vec = cx.Point.from_(xs, "m", cx.cart3d) >>> print(vec) <Point: chart=Cart3D (x, y, z) [m] [[1 2 3] [4 5 6]]>
- from_(cls: type[Point], obj: ArrayLike | list[Any], unit: UnitBase | FunctionUnitBase | str, chart: AbstractChart, rep: Representation, /) Any
- Parameters:
- Return type:
Construct a vector from an array, unit, chart, and rep.
>>> import jax.numpy as jnp >>> import coordinax as cx
>>> vec = cx.Point.from_([1, 2, 3], "m", cx.cart3d, cx.point) >>> print(vec) <Point: chart=Cart3D (x, y, z) [m] [1 2 3]>
>>> xs = jnp.array([[1, 2, 3], [4, 5, 6]]) >>> vec = cx.Point.from_(xs, "m", cx.cart3d, cx.point) >>> print(vec) <Point: chart=Cart3D (x, y, z) [m] [[1 2 3] [4 5 6]]>
- from_(cls: type[Point], obj: Point, frame: AbstractReferenceFrame, /) Point
- Parameters:
- Return type:
Construct a point from another point, replacing its frame.
Every constructor above also accepts a trailing frame, which is attached to the constructed point:
>>> import coordinax as cx >>> import coordinax.charts as cxc >>> import coordinax.frames as cxf >>> import coordinax.representations as cxr >>> import unxt as u
>>> d = {"x": u.Q(1, "km"), "y": u.Q(2, "km"), "z": u.Q(3, "km")} >>> for args in [(cx.Point.from_(d),), (d,), (d, cxc.cart3d), ... (d, cxc.cart3d, cxr.point), ([1, 2, 3], "km")]: ... print(cx.Point.from_(*args, cxf.alice).frame) Alice() Alice() Alice() Alice() Alice()
An existing frame is replaced, not merged:
>>> p_alice = cx.Point.from_(d, cxf.alice) >>> cx.Point.from_(p_alice, cxf.noframe).frame == cxf.noframe True
- from_(cls: type[Point], obj: Any, frame: AbstractReferenceFrame, /) Point
- Parameters:
- Return type:
Construct a point from any object, with a frame.
- from_(cls: type[Point], obj: Any, chart: AbstractChart, frame: AbstractReferenceFrame, /) Point
- Parameters:
- Return type:
Construct a point from an object and chart, with a frame.
- from_(cls: type[Point], obj: Any, chart: AbstractChart, rep: Representation, frame: AbstractReferenceFrame, /) Point
- Parameters:
- Return type:
Construct a point from an object, chart, and representation, with a frame.
- from_(cls: type[Point], obj: ArrayLike | list[Any], unit: UnitBase | FunctionUnitBase | str, frame: AbstractReferenceFrame, /) Point
- Parameters:
- Return type:
Construct a point from an array and unit, with a frame.
Construct a Tangent from another Tangent (identity / fast path).
>>> import coordinax as cx >>> import coordinax.charts as cxc >>> import coordinax.representations as cxr >>> import unxt as u
>>> v = cx.Tangent.from_( ... {"x": u.Q(1.0, "m/s"), "y": u.Q(2.0, "m/s"), "z": u.Q(3.0, "m/s")}, ... cxc.cart3d, cxr.coord_basis, cxr.vel, ... ) >>> v2 = cx.Tangent.from_(v) >>> v2 is v True
- from_(cls: type[Tangent], obj: Any, chart: AbstractChart, basis: AbstractLinearBasis, semantic: AbstractTangentSemanticKind, /) Tangent
- Parameters:
- Return type:
Construct a Tangent from data, chart, basis, and semantic.
>>> import coordinax as cx >>> import coordinax.charts as cxc >>> import coordinax.representations as cxr >>> import unxt as u
>>> d = {"x": u.Q(1.0, "m/s"), "y": u.Q(2.0, "m/s"), "z": u.Q(3.0, "m/s")} >>> v = cx.Tangent.from_(d, cxc.cart3d, cxr.coord_basis, cxr.vel) >>> v.chart Cart3D(M=Rn(3))
- from_(cls: type[Tangent], obj: Any, chart: AbstractChart, rep: Representation, /) Tangent
- Parameters:
- Return type:
Construct a Tangent from data, chart, and a tangent Representation.
Extracts
basisandsemanticfrom the representation. RaisesTypeErrorif the representation’s geometry kind is notTangentGeometry.>>> import coordinax as cx >>> import coordinax.charts as cxc >>> import coordinax.representations as cxr >>> import unxt as u
>>> d = {"x": u.Q(1.0, "m/s"), "y": u.Q(2.0, "m/s"), "z": u.Q(3.0, "m/s")} >>> v = cx.Tangent.from_(d, cxc.cart3d, cxr.coord_vel) >>> v.basis == cxr.coord_basis True >>> v.semantic == cxr.vel True
- from_(cls: type[Tangent], obj: Any, chart: AbstractChart, /) Tangent
- Parameters:
- Return type:
Construct a Tangent from data and chart (rep inferred from data).
>>> import coordinax as cx >>> import coordinax.charts as cxc >>> import unxt as u
>>> d = {"x": u.Q(1.0, "m/s"), "y": u.Q(2.0, "m/s"), "z": u.Q(3.0, "m/s")} >>> v = cx.Tangent.from_(d, cxc.cart3d) >>> isinstance(v, cx.Tangent) True
Construct a Tangent from data alone (chart and rep inferred).
>>> import coordinax as cx >>> import unxt as u
>>> d = {"x": u.Q(1.0, "m/s"), "y": u.Q(2.0, "m/s"), "z": u.Q(3.0, "m/s")} >>> v = cx.Tangent.from_(d) >>> isinstance(v, cx.Tangent) True
- from_(cls: type[Tangent], obj: ArrayLike | list[Any], unit: UnitBase | FunctionUnitBase | str, /) Tangent
- Parameters:
- Return type:
Construct a Tangent from an array and unit (chart inferred).
>>> import coordinax as cx
>>> v = cx.Tangent.from_([1.0, 2.0, 3.0], "m/s") >>> isinstance(v, cx.Tangent) True
- from_(cls: type[Tangent], obj: ArrayLike | list[Any], unit: UnitBase | FunctionUnitBase | str, chart: AbstractChart, /) Tangent
- Parameters:
- Return type:
Construct a Tangent from an array, unit, and chart.
>>> import coordinax as cx >>> import coordinax.charts as cxc
>>> v = cx.Tangent.from_([1.0, 2.0, 3.0], "m/s", cxc.cart3d) >>> isinstance(v, cx.Tangent) True
- from_(cls: type[Tangent], obj: ArrayLike | list[Any], unit: UnitBase | FunctionUnitBase | str, chart: AbstractChart, rep: Representation, /) Tangent
- Parameters:
- Return type:
Construct a Tangent from an array, unit, chart, and Representation.
>>> import coordinax as cx >>> import coordinax.charts as cxc >>> import coordinax.representations as cxr
>>> v = cx.Tangent.from_([1.0, 2.0, 3.0], "m/s", cxc.cart3d, cxr.coord_vel) >>> v.basis == cxr.coord_basis True
- from_(cls: type[Tangent], obj: ArrayLike | list[Any], unit: UnitBase | FunctionUnitBase | str, chart: AbstractChart, rep: Representation, frame: AbstractReferenceFrame, /) Tangent
- Parameters:
- Return type:
Construct a Tangent from an array, unit, chart, Representation, and frame.
- from_(cls: type[Tangent], obj: ArrayLike | list[Any], unit: UnitBase | FunctionUnitBase | str, chart: AbstractChart, basis: AbstractLinearBasis, semantic: AbstractTangentSemanticKind, /) Tangent
- Parameters:
- Return type:
Construct a Tangent from array, unit, chart, basis, and semantic.
>>> import coordinax as cx >>> import coordinax.charts as cxc >>> import coordinax.representations as cxr
>>> v = cx.Tangent.from_( ... [1.0, 2.0, 3.0], "m/s", cxc.cart3d, cxr.coord_basis, cxr.vel ... ) >>> v.basis == cxr.coord_basis True
- from_(cls: type[Tangent], obj: AbstractQuantity, unit: UnitBase | FunctionUnitBase | str, chart: AbstractChart, basis: AbstractLinearBasis, semantic: AbstractTangentSemanticKind, /) Tangent
- Parameters:
- Return type:
Construct a Tangent from a Quantity, unit, chart, basis, and semantic.
The Quantity is converted to the given unit before construction.
>>> import coordinax as cx >>> import coordinax.charts as cxc >>> import coordinax.representations as cxr >>> import unxt as u
>>> v = cx.Tangent.from_( ... u.Q([1.0, 2.0, 3.0], "m/s"), "m/s", cxc.cart3d, cxr.coord_basis, cxr.vel ... ) >>> v.basis == cxr.coord_basis True
- from_(cls: type[Tangent], obj: Tangent, frame: AbstractReferenceFrame, /) Tangent
- Parameters:
- Return type:
Construct a Tangent from another Tangent, replacing its frame.
Every constructor above also accepts a trailing frame, which is attached to the constructed tangent:
>>> import coordinax as cx >>> import coordinax.charts as cxc >>> import coordinax.representations as cxr >>> import coordinax.frames as cxf >>> import unxt as u
>>> d = {"x": u.Q(1.0, "m/s"), "y": u.Q(2.0, "m/s"), "z": u.Q(3.0, "m/s")} >>> for args in [(cx.Tangent.from_(d),), (d,), (d, cxc.cart3d), ... (d, cxc.cart3d, cxr.coord_basis, cxr.vel), ... ([1.0, 2.0, 3.0], "m/s"), ... ([1.0, 2.0, 3.0], "m/s", cxc.cart3d, cxr.coord_vel)]: ... print(cx.Tangent.from_(*args, cxf.alice).frame) Alice() Alice() Alice() Alice() Alice() Alice()
- from_(cls: type[Tangent], obj: Any, frame: AbstractReferenceFrame, /) Tangent
- Parameters:
- Return type:
Construct a Tangent from data with a frame (chart and rep inferred).
- from_(cls: type[Tangent], obj: Any, chart: AbstractChart, frame: AbstractReferenceFrame, /) Tangent
- Parameters:
- Return type:
Construct a Tangent from data and chart, with a frame.
- from_(cls: type[Tangent], obj: Any, chart: AbstractChart, basis: AbstractLinearBasis, semantic: AbstractTangentSemanticKind, frame: AbstractReferenceFrame, /) Tangent
- Parameters:
- Return type:
Construct a Tangent from data, chart, basis, and semantic, with a frame.
- from_(cls: type[Tangent], obj: ArrayLike | list[Any], unit: UnitBase | FunctionUnitBase | str, frame: AbstractReferenceFrame, /) Tangent
- Parameters:
- Return type:
Construct a Tangent from an array and unit, with a frame.
- from_(cls: type[Coordinate], pv: Coordinate, /) Coordinate
- Parameters:
- Return type:
Identity: return the same Coordinate unchanged.
>>> import coordinax as cx >>> pv = cx.Coordinate(point=cx.Point.from_([1.0, 2.0, 3.0], "m")) >>> cx.Coordinate.from_(pv) is pv True
- from_(cls: type[Coordinate], p: Point, /) Coordinate
- Parameters:
- Return type:
Wrap a single
Pointas a point-only bundle (no field vectors).>>> import coordinax as cx >>> p = cx.Point.from_([1.0, 2.0, 3.0], "m") >>> pv = cx.Coordinate.from_(p) >>> pv.point is p True
- from_(cls: type[Coordinate], data: Mapping[str, Any], /, *, point: Point | None = None) Coordinate
- Parameters:
- Return type:
Create a
Coordinatefrom a mapping of named objects.The mapping may contain a
"point"key for the base; the explicitpointkeyword argument takes precedence if both are supplied.>>> import unxt as u >>> import coordinax as cx >>> import coordinax.charts as cxc >>> import coordinax.representations as cxr
>>> p = cx.Point.from_([1.0, 2.0, 3.0], "m") >>> vel = cx.Tangent.from_( ... {"x": u.Q(1.0, "m/s"), "y": u.Q(0.0, "m/s"), "z": u.Q(0.0, "m/s")}, ... cxc.cart3d, cxr.coord_vel) >>> pv = cx.Coordinate.from_({"point": p, "velocity": vel}) >>> pv.point is p True
- from_(cls: type[Point], obj: CartesianRepresentation, /) Point
- Parameters:
- Return type:
Construct Point from Astropy CartesianRepresentation.
>>> import coordinax.vectors as cxv >>> from astropy.coordinates import CartesianRepresentation
>>> vec = CartesianRepresentation(1, 2, 3, unit="km") >>> cxv.Point.from_(vec) Point({'x': Q(1., 'km'), 'y': Q(2., 'km'), 'z': Q(3., 'km')}, chart=Cart3D(M=Rn(3)))
- from_(cls: type[Point], obj: CylindricalRepresentation, /) Point
- Parameters:
- Return type:
Construct Point from Astropy CylindricalRepresentation.
>>> import astropy.units as apyu >>> import coordinax.vectors as cxv >>> from astropy.coordinates import CylindricalRepresentation
>>> vec = CylindricalRepresentation(rho=1 * apyu.km, phi=90 * apyu.deg, ... z=3 * apyu.km) >>> cxv.Point.from_(vec) Point( {'rho': Q(1., 'km'), 'phi': Q(90., 'deg'), 'z': Q(3., 'km')}, chart=Cylindrical3D(M=Rn(3)) )
- from_(cls: type[Point], obj: PhysicsSphericalRepresentation, /) Point
- Parameters:
- Return type:
Construct Point from Astropy PhysicsSphericalRepresentation.
>>> import coordinax.vectors as cxv >>> from astropy.coordinates import PhysicsSphericalRepresentation >>> import astropy.units as apyu
>>> vec = PhysicsSphericalRepresentation( ... r=1 * apyu.kpc, theta=45 * apyu.deg, phi=90 * apyu.deg) >>> cxv.Point.from_(vec) Point( {'r': Q(1., 'kpc'), 'theta': Q(45., 'deg'), 'phi': Q(90., 'deg')}, chart=Spherical3D(M=Rn(3)) )
- from_(cls: type[Point], obj: SphericalRepresentation, /) Point
- Parameters:
- Return type:
Construct Point from Astropy SphericalRepresentation.
>>> import coordinax.vectors as cxv >>> from astropy.coordinates import SphericalRepresentation >>> import astropy.units as apyu
>>> vec = SphericalRepresentation( ... lon=90 * apyu.deg, lat=45 * apyu.deg, distance=1 * apyu.kpc) >>> cxv.Point.from_(vec) Point( {'lon': Q(90., 'deg'), 'lat': Q(45., 'deg'), 'distance': Q(1., 'kpc')}, chart=LonLatSpherical3D(M=Rn(3)) )
- from_(cls: type[Point], obj: UnitSphericalRepresentation, /) Point
- Parameters:
- Return type:
Construct Point from Astropy UnitSphericalRepresentation.
A direction with no distance, so the point lives on the two-sphere rather than in R^3.
>>> import coordinax.vectors as cxv >>> from astropy.coordinates import UnitSphericalRepresentation >>> import astropy.units as apyu
>>> vec = UnitSphericalRepresentation(lon=90 * apyu.deg, lat=45 * apyu.deg) >>> cxv.Point.from_(vec) Point( {'lon': Q(90., 'deg'), 'lat': Q(45., 'deg')}, chart=LonLatSphericalTwoSphere(M=Sn(2)) )
- from_(cls: type[Point], obj: RadialRepresentation, /) Point
- Parameters:
- Return type:
Construct Point from Astropy RadialRepresentation.
>>> import coordinax.vectors as cxv >>> from astropy.coordinates import RadialRepresentation >>> import astropy.units as apyu
>>> cxv.Point.from_(RadialRepresentation(distance=1 * apyu.kpc)) Point({'r': Q(1., 'kpc')}, chart=Radial1D(M=Rn(1)))
- from_(cls: type[Point], obj: BaseCoordinateFrame, /) Point
- Parameters:
- Return type:
Construct Point from Astropy frame with data.
>>> import astropy.units as apyu >>> import astropy.coordinates as apyc >>> import coordinax.vectors as cxv
>>> vec = apyc.ICRS(ra=90 * apyu.deg, dec=45 * apyu.deg, distance=1 * apyu.kpc) >>> cxv.Point.from_(vec) Point( {'lon': Q(90., 'deg'), 'lat': Q(45., 'deg'), 'distance': Q(1., 'kpc')}, chart=LonLatSpherical3D(M=Rn(3)), frame=ICRS() )
>>> vec = apyc.Galactocentric( ... x=1 * apyu.kpc, y=2 * apyu.kpc, z=3 * apyu.kpc ... ) >>> cxv.Point.from_(vec) Point( {'x': Q(1., 'kpc'), 'y': Q(2., 'kpc'), 'z': Q(3., 'kpc')}, chart=Cart3D(M=Rn(3)), frame=Galactocentric(...) )
A ~coordinax.vectors.Point is a position, so any velocity the frame carries is not part of the result. ~coordinax.vectors.Tangent.from_ takes the other half:
>>> vec = apyc.Galactocentric( ... x=1 * apyu.kpc, y=2 * apyu.kpc, z=3 * apyu.kpc, ... v_x=4 * apyu.km / apyu.s, v_y=5 * apyu.km / apyu.s, ... v_z=6 * apyu.km / apyu.s, ... ) >>> cxv.Tangent.from_(vec) Tangent( {'x': Q(4., 'km / s'), 'y': Q(5., 'km / s'), 'z': Q(6., 'km / s')}, chart=Cart3D(M=Rn(3)), basis=coord_basis, semantic=vel )
- from_(cls: type[Tangent], obj: BaseCoordinateFrame, /) Tangent
- Parameters:
- Return type:
Construct a Tangent from the velocity of an Astropy frame.
The position half is ~coordinax.vectors.Point.from_; this is the other half, so a frame carrying both splits into the two coordinax objects.
>>> import astropy.units as apyu >>> import astropy.coordinates as apyc >>> import coordinax.vectors as cxv
>>> vec = apyc.Galactocentric( ... x=1 * apyu.kpc, y=2 * apyu.kpc, z=3 * apyu.kpc, ... v_x=4 * apyu.km / apyu.s, v_y=5 * apyu.km / apyu.s, ... v_z=6 * apyu.km / apyu.s, ... ) >>> cxv.Tangent.from_(vec) Tangent( {'x': Q(4., 'km / s'), 'y': Q(5., 'km / s'), 'z': Q(6., 'km / s')}, chart=Cart3D(M=Rn(3)), basis=coord_basis, semantic=vel )
Angular rates come across too, when the frame states them as plain
d_lonrather than astropy’scos(lat)-scaled convention:>>> vec = apyc.ICRS( ... ra=90 * apyu.deg, dec=45 * apyu.deg, distance=1 * apyu.kpc, ... pm_ra=3 * apyu.mas / apyu.yr, pm_dec=2 * apyu.mas / apyu.yr, ... radial_velocity=10 * apyu.km / apyu.s, ... differential_type=apyc.SphericalDifferential, ... ) >>> cxv.Tangent.from_(vec) Tangent( {'lon': Q(3., 'mas / yr'), 'lat': Q(2., 'mas / yr'), 'distance': Q(10., 'km / s')}, chart=LonLatSpherical3D(M=Rn(3)), basis=coord_basis, semantic=vel )
A frame without velocities says so rather than handing back a zero:
>>> try: ... cxv.Tangent.from_(apyc.ICRS(ra=1 * apyu.deg, dec=2 * apyu.deg)) ... except ValueError as e: ... print(e) ICRS carries no velocity; there is nothing to convert to a Tangent.
Construct a Tangent from the velocity of an Astropy SkyCoord.
>>> import astropy.units as apyu >>> import astropy.coordinates as apyc >>> import coordinax.vectors as cxv
A ~astropy.coordinates.SkyCoord defaults to the
cos(lat)-scaled proper motion convention, which coordinax has no chart for, so the usual astronomical spelling is refused rather than silently reinterpreted:>>> sc = apyc.SkyCoord( ... ra=90 * apyu.deg, dec=45 * apyu.deg, distance=1 * apyu.kpc, ... pm_ra_cosdec=3 * apyu.mas / apyu.yr, pm_dec=2 * apyu.mas / apyu.yr, ... radial_velocity=10 * apyu.km / apyu.s, ... ) >>> try: ... cxv.Tangent.from_(sc) ... except ValueError as e: ... print(e) astropy's SphericalCosLatDifferential is a rate convention ...
Cartesian velocities carry across unchanged:
>>> sc = apyc.SkyCoord( ... x=1 * apyu.kpc, y=2 * apyu.kpc, z=3 * apyu.kpc, ... v_x=4 * apyu.km / apyu.s, v_y=5 * apyu.km / apyu.s, ... v_z=6 * apyu.km / apyu.s, ... representation_type="cartesian", differential_type="cartesian", ... ) >>> cxv.Tangent.from_(sc) Tangent( {'x': Q(4., 'km / s'), 'y': Q(5., 'km / s'), 'z': Q(6., 'km / s')}, chart=Cart3D(M=Rn(3)), basis=coord_basis, semantic=vel )
Construct Point from Astropy SkyCoord.
>>> import astropy.units as apyu >>> import astropy.coordinates as apyc >>> import coordinax.vectors as cxv
>>> vec = apyc.SkyCoord(ra=90 * apyu.deg, dec=45 * apyu.deg, distance=1 * apyu.kpc) >>> cxv.Point.from_(vec) Point( {'lon': Q(90., 'deg'), 'lat': Q(45., 'deg'), 'distance': Q(1., 'kpc')}, chart=LonLatSpherical3D(M=Rn(3)), frame=ICRS() )
>>> vec = vec.transform_to(apyc.Galactocentric()) >>> cxv.Point.from_(vec) Point( {'x': Q(-9.08123957, 'kpc'), 'y': Q(0.21365468, 'kpc'), 'z': Q(0.2056243, 'kpc')}, chart=Cart3D(M=Rn(3)), frame=Galactocentric( galcen=Point( { 'lon': Q(266.4051, 'deg'), 'lat': Q(-28.936175, 'deg'), 'distance': Q(8.122, 'kpc') }, chart=LonLatSpherical3D(M=Rn(3)), frame=ICRS() ), roll=Angle(0., 'deg'), z_sun=Q(20.8, 'pc'), galcen_v_sun=Tangent( {'x': Q(12.9, 'km / s'), 'y': Q(245.6, 'km / s'), 'z': Q(7.78, 'km / s')}, chart=Cart3D(M=Rn(3)), basis=coord_basis, semantic=vel ) ) )
- Parameters:
cls (
type[AbstractVector])args (
Any)kwargs (
Any)
- Return type:
- classmethod is_like(obj: Any, /)
Check if the object is a AbstractVector object.
Examples
>>> import coordinax.vectors as cxv
>>> vec = cxv.Point.from_([1, 2, 3], "m") >>> cxv.AbstractVector.is_like(vec) True
>>> cxv.AbstractVector.is_like(42) False
- Parameters:
obj (
Any)- Return type:
TypeIs[Self]
- property ndim: int
- ravel()
Return a flattened vector.
- Return type:
Self
- property rep: Representation[PointGeometry, NoBasis, Location]
The representation of the vector.
- represent_as(target: Any, *args: Any, **kwargs: Any)
Represent the vector as another type.
This just forwards to coordinax.cconvert.
- Parameters:
target (
Any) – The representation type to convert to, e.g. cxc.sph3d.*args (
Any) – Extra arguments. These are passed to coordinax.cconvert and might be used, depending on the dispatched method. E.g. for transforming a velocity or acceleration vector, generally the first argument is the position vector at which the differential is defined. In general this is a required argument, though it is not for Cartesian-to-Cartesian transforms – see https://en.wikipedia.org/wiki/Tensors_in_curvilinear_coordinates for more information.**kwargs (
Any) – Extra arguments. These are passed to coordinax.cconvert and might be used, depending on the dispatched method. E.g. for transforming a velocity or acceleration vector, generally the first argument is the position vector at which the differential is defined. In general this is a required argument, though it is not for Cartesian-to-Cartesian transforms – see https://en.wikipedia.org/wiki/Tensors_in_curvilinear_coordinates for more information.
- Return type:
Examples
>>> import coordinax as cx >>> import coordinax.charts as cxc
Transforming a Position:
>>> q_cart = cx.Point.from_([1, 2, 3], "m") >>> q_sph = q_cart.represent_as(cxc.sph3d) >>> print(q_sph) <Point: chart=Spherical3D (r[m], theta[rad], phi[rad]) [3.742 0.641 1.107]>
- property size: int
- to_cartesian()
Return the vector in a Cartesian chart.
This just forwards to coordinax.cartesian_chart and cconvert.
- Return type:
Examples
>>> import coordinax as cx
>>> vec = cx.Point.from_([1, 2, 3], "m").cconvert(cx.sph3d) >>> print(vec) <Point: chart=Spherical3D (r[m], theta[rad], phi[rad]) [3.742 0.641 1.107]>
>>> print(vec.to_cartesian()) <Point: chart=Cart3D (x, y, z) [m] [1. 2. 3.]>
- to_frame(toframe: AbstractReferenceFrame, /, t: Quantity | None = None)
Transform the vector to a specified reference frame.
- Parameters:
toframe (
AbstractReferenceFrame) – The target reference frame.t (
Quantity|None) – The evolution parameter (e.g. time). Defaults to 0 s.
- Returns:
New vector with the data transformed into
toframeandframe=toframe.- Return type:
Notes
This is a convenience: it rebuilds the frame-transition operator on every call, which is pure Python and dominates the cost. On a hot path or under
jit, build the operator once outside with coordinax.frames.frame_transition and pass it into the jitted function that applies it – see that function’s notes.Examples
>>> import coordinax as cx >>> import coordinax.frames as cxf
>>> p = cx.Point.from_([1, 2, 3], "kpc", cxf.alice) >>> p.to_frame(cxf.alice) is p True
- uconvert(*args, **kwargs)
Convert the vector to the given units.
This just forwards to unxt.uconvert, reversing the order of the arguments to match the unxt API.
Examples
>>> import coordinax as cx
>>> vec = cx.Point.from_([1, 2, 3], "km") >>> print(vec.uconvert({"length": "km"})) <Point: chart=Cart3D (x, y, z) [km] [1 2 3]>
- uconvert(self, usys: AbstractUnitSystem, /) Any
Convert the vector to the given units.
- Parameters:
- Return type:
Examples
>>> import unxt as u >>> import coordinax as cx
>>> usys = u.unitsystem("m", "s", "kg", "rad")
>>> vec = cx.Point.from_([1, 2, 3], "km")
>>> print(vec.uconvert(usys)) <Point: chart=Cart3D (x, y, z) [m] [1000. 2000. 3000.]>
>>> print(vec.uconvert("galactic")) <Point: chart=Cart3D (x, y, z) [kpc] [3.241e-17 6.482e-17 9.722e-17]>
- final class coordinax.Coordinate(point: Any, **fields: Any)
Bases:
AbstractVectorA vector bundle anchored at a base point.
A Coordinate stores:
A base point \(q \in M\) (a ~coordinax.vectors.Point).
A collection of named fibre vectors \(\{v_i\}\) anchored at \(q\) (each a ~coordinax.vectors.Tangent with
TangentGeometryrep, e.g. velocity, displacement, acceleration).
On construction every fibre vector is automatically frame-aligned to the reference frame of the base point:
Frame-alignment via ~coordinax.vectors.AbstractVector.to_frame ensures
pv["velocity"].frame == pv.point.frame.
Fibre vectors are not chart-aligned on construction; each fibre retains the chart it was supplied with. Chart conversion is handled lazily: ~coordinax.vectors.Coordinate.cconvert pushes each fibre forward using the Jacobian at the base point expressed in the fibre’s current chart.
Coordinate conversion (chart change) is handled automatically: the base converts as a point map, and each fibre vector converts via the Jacobian pushforward at the base.
- Parameters:
Examples
>>> import unxt as u >>> import coordinax as cx >>> import coordinax.charts as cxc >>> import coordinax.representations as cxr
>>> point = cx.Point.from_([1.0, 0.0, 0.0], "m") >>> vel = cx.Tangent.from_( ... {"x": u.Q(1.0, "m/s"), "y": u.Q(0.0, "m/s"), "z": u.Q(0.0, "m/s")}, ... cxc.cart3d, cxr.coord_vel) >>> pv = cx.Coordinate(point=point, velocity=vel) >>> pv.point.chart Cart3D(M=Rn(3))
Convert to spherical — point converts as a point map, velocity via Jacobian:
>>> pv_sph = pv.cconvert(cxc.sph3d) >>> pv_sph.point.chart Spherical3D(M=Rn(3)) >>> pv_sph["velocity"].chart Spherical3D(M=Rn(3))
- point: Point
Base point of the bundle. Must be a
Pointinstance.
- property data: Any
Component data of the base point.
- property chart: AbstractChart
Chart of the base point.
- property rep: Representation
Representation of the base point (always PointGeometry).
- property manifold: AbstractManifold
Manifold of the base point.
- property frame: AbstractReferenceFrame
Reference frame of the bundle — always equal to
point.frame.
- keys()
Return field names (excluding base point).
Examples
>>> import unxt as u >>> import coordinax as cx >>> import coordinax.charts as cxc >>> import coordinax.representations as cxr
>>> base = cx.Point.from_([1.0, 0.0, 0.0], "m") >>> vel = cx.Tangent.from_( ... {"x": u.Q(1.0, "m/s"), "y": u.Q(0.0, "m/s"), "z": u.Q(0.0, "m/s")}, ... cxc.cart3d, cxr.coord_vel) >>> pv = cx.Coordinate(point=base, velocity=vel) >>> list(pv.keys()) ['velocity']
- values()
Return field vectors (excluding base point).
- Return type:
- items()
Return
(name, vector)pairs for fields (excluding base point).
- cconvert(to_chart: AbstractChart, /, *, field_charts: Mapping[str, AbstractChart] | None = None, usys: AbstractUnitSystem | None = None)
Convert the bundle to a new coordinate chart.
Algorithm:
Convert base as a point map:
new_point = cconvert(point, to_chart).For each field vector, apply the tangent pushforward at
pointviacconvert(vec, field_to_chart, at=point).
- Parameters:
to_chart (
AbstractChart) – Target chart for the base and (by default) all fields.field_charts (
Mapping[str,AbstractChart] |None) – Per-field target chart overrides.usys (
AbstractUnitSystem|None) – Unit system for the conversion.
- Return type:
Examples
>>> import coordinax as cx >>> import coordinax.charts as cxc >>> import unxt as u >>> import coordinax.representations as cxr
>>> point = cx.Point.from_([1.0, 0.0, 0.0], "m") >>> vel = cx.Tangent.from_( ... {"x": u.Q(1.0, "m/s"), "y": u.Q(0.0, "m/s"), "z": u.Q(0.0, "m/s")}, ... cxc.cart3d, cxr.coord_vel) >>> pv = cx.Coordinate(point=point, velocity=vel) >>> sph = pv.cconvert(cxc.sph3d) >>> sph.point.chart Spherical3D(M=Rn(3)) >>> sph["velocity"].chart Spherical3D(M=Rn(3))
- property shape: tuple[int, ...]
Broadcast shape of base point and all field vectors.
Examples
>>> import coordinax as cx >>> pv = cx.Coordinate(point=cx.Point.from_([1.0, 2.0, 3.0], "m")) >>> pv.shape ()
- property M: AbstractManifold
The manifold of the vector, from the chart.
- astype(dtype: Any, /, **kwargs: Any)
Cast the vector to a new dtype.
Examples
>>> import quaxed.numpy as jnp >>> import unxt as u >>> import coordinax as cx
We can cast a vector to a new dtype:
>>> vec = cx.Point.from_(u.Q([1, 2, 3], "m")) >>> print(vec.astype(jnp.float32)) <Point: chart=Cart3D (x, y, z) [m] [1. 2. 3.]>
>>> print(jnp.astype(vec, jnp.float32)) <Point: chart=Cart3D (x, y, z) [m] [1. 2. 3.]>
- copy()
Return a copy of the vector.
- Return type:
Self
Examples
>>> import coordinax as cx
>>> vec = cx.Point.from_([1, 2, 3], "m") >>> print(vec.copy()) <Point: chart=Cart3D (x, y, z) [m] [1 2 3]>
- property dtype: Any
- flatten()
Flatten the vector.
- Return type:
Self
- classmethod from_(*args, **kwargs)
Create a vector-like object from arguments.
Construct a point from another point.
>>> import coordinax as cx >>> vec1 = cx.Point.from_([1, 2, 3], "m") >>> vec2 = cx.Point.from_(vec1) >>> print(vec2) <Point: chart=Cart3D (x, y, z) [m] [1 2 3]>
- from_(cls: type[Point], obj: Any, chart: AbstractChart, rep: Representation, /) Point
- Parameters:
- Return type:
Construct a vector from an object, and chart and rep info.
>>> import jax.numpy as jnp >>> import unxt as u >>> import coordinax as cx
>>> xs = {"x": u.Q(1, "m"), "y": u.Q(2, "m"), "z": u.Q(3, "m")} >>> vec = cx.Point.from_(xs, cx.cart3d, cx.point) >>> print(vec) <Point: chart=Cart3D (x, y, z) [m] [1 2 3]>
>>> xs = u.Q(jnp.array([[1, 2, 3], [4, 5, 6]]), "m") >>> vec = cx.Point.from_(xs, cx.cart3d, cx.point) >>> print(vec) <Point: chart=Cart3D (x, y, z) [m] [[1 2 3] [4 5 6]]>
- from_(cls: type[Point], obj: Any, chart: AbstractChart, /) Point
- Parameters:
- Return type:
Construct a point from an object, and chart info.
>>> import jax.numpy as jnp >>> import unxt as u >>> import coordinax as cx
>>> xs = {"x": u.Q(1, "m"), "y": u.Q(2, "m"), "z": u.Q(3, "m")} >>> vec = cx.Point.from_(xs, cx.cart3d) >>> print(vec) <Point: chart=Cart3D (x, y, z) [m] [1 2 3]>
>>> xs = {"x": u.Q([1, 2], "m"), "y": u.Q([3, 4], "m"), "z": u.Q([5, 6], "m")} >>> vec = cx.Point.from_(xs, cx.cart3d) >>> print(vec) <Point: chart=Cart3D (x, y, z) [m] [[1 3 5] [2 4 6]]>
>>> xs = u.Q(jnp.array([[1, 2, 3], [4, 5, 6]]), "m") >>> vec = cx.Point.from_(xs, cx.cart3d) >>> print(vec) <Point: chart=Cart3D (x, y, z) [m] [[1 2 3] [4 5 6]]>
- from_(cls: type[Point], obj: Any, rep: Representation, /) Point
- Parameters:
- Return type:
Construct a point from an object, and rep info.
>>> import jax.numpy as jnp >>> import unxt as u >>> import coordinax as cx
>>> xs = {"x": u.Q(1, "m"), "y": u.Q(2, "m"), "z": u.Q(3, "m")} >>> vec = cx.Point.from_(xs, cx.point) >>> print(vec) <Point: chart=Cart3D (x, y, z) [m] [1 2 3]>
>>> xs = {"x": u.Q([1, 2], "m"), "y": u.Q([3, 4], "m"), "z": u.Q([5, 6], "m")} >>> vec = cx.Point.from_(xs, cx.point) >>> print(vec) <Point: chart=Cart3D (x, y, z) [m] [[1 3 5] [2 4 6]]>
>>> xs = u.Q(jnp.array([[1, 2, 3], [4, 5, 6]]), "m") >>> vec = cx.Point.from_(xs, cx.point) >>> print(vec) <Point: chart=Cart3D (x, y, z) [m] [[1 2 3] [4 5 6]]>
Construct a point from an object.
Note that this is a pretty limited constructor since it often lacks the necessary information to do a proper construction.
>>> import jax.numpy as jnp >>> import unxt as u >>> import coordinax as cx
>>> xs = {"x": u.Q(1, "m"), "y": u.Q(2, "m"), "z": u.Q(3, "m")} >>> vec = cx.Point.from_(xs) >>> print(vec) <Point: chart=Cart3D (x, y, z) [m] [1 2 3]>
>>> vec = cx.Point.from_(u.Q([1, 2, 3], "m")) >>> print(vec) <Point: chart=Cart3D (x, y, z) [m] [1 2 3]>
- from_(cls: type[Point], obj: ArrayLike | list[Any], unit: UnitBase | FunctionUnitBase | str, /) Any
- Parameters:
- Return type:
Construct a cartesian vector from an array and unit.
The
ArrayLike[Any, (*#batch, N), "..."]is expected to have the components as the last dimension.>>> import jax.numpy as jnp >>> import coordinax as cx
>>> vec = cx.Point.from_([1, 2, 3], "meter") >>> print(vec) <Point: chart=Cart3D (x, y, z) [m] [1 2 3]>
>>> xs = jnp.array([[1, 2, 3], [4, 5, 6]]) >>> vec = cx.Point.from_(xs, "meter") >>> print(vec) <Point: chart=Cart3D (x, y, z) [m] [[1 2 3] [4 5 6]]>
- from_(cls: type[Point], obj: ArrayLike | list[Any], unit: UnitBase | FunctionUnitBase | str, chart: AbstractChart, /) Any
- Parameters:
- Return type:
Construct a vector from an array, unit, and chart.
>>> import jax.numpy as jnp >>> import coordinax as cx
>>> vec = cx.Point.from_([1, 2, 3], "m", cx.cart3d) >>> print(vec) <Point: chart=Cart3D (x, y, z) [m] [1 2 3]>
>>> xs = jnp.array([[1, 2, 3], [4, 5, 6]]) >>> vec = cx.Point.from_(xs, "m", cx.cart3d) >>> print(vec) <Point: chart=Cart3D (x, y, z) [m] [[1 2 3] [4 5 6]]>
- from_(cls: type[Point], obj: ArrayLike | list[Any], unit: UnitBase | FunctionUnitBase | str, chart: AbstractChart, rep: Representation, /) Any
- Parameters:
- Return type:
Construct a vector from an array, unit, chart, and rep.
>>> import jax.numpy as jnp >>> import coordinax as cx
>>> vec = cx.Point.from_([1, 2, 3], "m", cx.cart3d, cx.point) >>> print(vec) <Point: chart=Cart3D (x, y, z) [m] [1 2 3]>
>>> xs = jnp.array([[1, 2, 3], [4, 5, 6]]) >>> vec = cx.Point.from_(xs, "m", cx.cart3d, cx.point) >>> print(vec) <Point: chart=Cart3D (x, y, z) [m] [[1 2 3] [4 5 6]]>
- from_(cls: type[Point], obj: Point, frame: AbstractReferenceFrame, /) Point
- Parameters:
- Return type:
Construct a point from another point, replacing its frame.
Every constructor above also accepts a trailing frame, which is attached to the constructed point:
>>> import coordinax as cx >>> import coordinax.charts as cxc >>> import coordinax.frames as cxf >>> import coordinax.representations as cxr >>> import unxt as u
>>> d = {"x": u.Q(1, "km"), "y": u.Q(2, "km"), "z": u.Q(3, "km")} >>> for args in [(cx.Point.from_(d),), (d,), (d, cxc.cart3d), ... (d, cxc.cart3d, cxr.point), ([1, 2, 3], "km")]: ... print(cx.Point.from_(*args, cxf.alice).frame) Alice() Alice() Alice() Alice() Alice()
An existing frame is replaced, not merged:
>>> p_alice = cx.Point.from_(d, cxf.alice) >>> cx.Point.from_(p_alice, cxf.noframe).frame == cxf.noframe True
- from_(cls: type[Point], obj: Any, frame: AbstractReferenceFrame, /) Point
- Parameters:
- Return type:
Construct a point from any object, with a frame.
- from_(cls: type[Point], obj: Any, chart: AbstractChart, frame: AbstractReferenceFrame, /) Point
- Parameters:
- Return type:
Construct a point from an object and chart, with a frame.
- from_(cls: type[Point], obj: Any, chart: AbstractChart, rep: Representation, frame: AbstractReferenceFrame, /) Point
- Parameters:
- Return type:
Construct a point from an object, chart, and representation, with a frame.
- from_(cls: type[Point], obj: ArrayLike | list[Any], unit: UnitBase | FunctionUnitBase | str, frame: AbstractReferenceFrame, /) Point
- Parameters:
- Return type:
Construct a point from an array and unit, with a frame.
Construct a Tangent from another Tangent (identity / fast path).
>>> import coordinax as cx >>> import coordinax.charts as cxc >>> import coordinax.representations as cxr >>> import unxt as u
>>> v = cx.Tangent.from_( ... {"x": u.Q(1.0, "m/s"), "y": u.Q(2.0, "m/s"), "z": u.Q(3.0, "m/s")}, ... cxc.cart3d, cxr.coord_basis, cxr.vel, ... ) >>> v2 = cx.Tangent.from_(v) >>> v2 is v True
- from_(cls: type[Tangent], obj: Any, chart: AbstractChart, basis: AbstractLinearBasis, semantic: AbstractTangentSemanticKind, /) Tangent
- Parameters:
- Return type:
Construct a Tangent from data, chart, basis, and semantic.
>>> import coordinax as cx >>> import coordinax.charts as cxc >>> import coordinax.representations as cxr >>> import unxt as u
>>> d = {"x": u.Q(1.0, "m/s"), "y": u.Q(2.0, "m/s"), "z": u.Q(3.0, "m/s")} >>> v = cx.Tangent.from_(d, cxc.cart3d, cxr.coord_basis, cxr.vel) >>> v.chart Cart3D(M=Rn(3))
- from_(cls: type[Tangent], obj: Any, chart: AbstractChart, rep: Representation, /) Tangent
- Parameters:
- Return type:
Construct a Tangent from data, chart, and a tangent Representation.
Extracts
basisandsemanticfrom the representation. RaisesTypeErrorif the representation’s geometry kind is notTangentGeometry.>>> import coordinax as cx >>> import coordinax.charts as cxc >>> import coordinax.representations as cxr >>> import unxt as u
>>> d = {"x": u.Q(1.0, "m/s"), "y": u.Q(2.0, "m/s"), "z": u.Q(3.0, "m/s")} >>> v = cx.Tangent.from_(d, cxc.cart3d, cxr.coord_vel) >>> v.basis == cxr.coord_basis True >>> v.semantic == cxr.vel True
- from_(cls: type[Tangent], obj: Any, chart: AbstractChart, /) Tangent
- Parameters:
- Return type:
Construct a Tangent from data and chart (rep inferred from data).
>>> import coordinax as cx >>> import coordinax.charts as cxc >>> import unxt as u
>>> d = {"x": u.Q(1.0, "m/s"), "y": u.Q(2.0, "m/s"), "z": u.Q(3.0, "m/s")} >>> v = cx.Tangent.from_(d, cxc.cart3d) >>> isinstance(v, cx.Tangent) True
Construct a Tangent from data alone (chart and rep inferred).
>>> import coordinax as cx >>> import unxt as u
>>> d = {"x": u.Q(1.0, "m/s"), "y": u.Q(2.0, "m/s"), "z": u.Q(3.0, "m/s")} >>> v = cx.Tangent.from_(d) >>> isinstance(v, cx.Tangent) True
- from_(cls: type[Tangent], obj: ArrayLike | list[Any], unit: UnitBase | FunctionUnitBase | str, /) Tangent
- Parameters:
- Return type:
Construct a Tangent from an array and unit (chart inferred).
>>> import coordinax as cx
>>> v = cx.Tangent.from_([1.0, 2.0, 3.0], "m/s") >>> isinstance(v, cx.Tangent) True
- from_(cls: type[Tangent], obj: ArrayLike | list[Any], unit: UnitBase | FunctionUnitBase | str, chart: AbstractChart, /) Tangent
- Parameters:
- Return type:
Construct a Tangent from an array, unit, and chart.
>>> import coordinax as cx >>> import coordinax.charts as cxc
>>> v = cx.Tangent.from_([1.0, 2.0, 3.0], "m/s", cxc.cart3d) >>> isinstance(v, cx.Tangent) True
- from_(cls: type[Tangent], obj: ArrayLike | list[Any], unit: UnitBase | FunctionUnitBase | str, chart: AbstractChart, rep: Representation, /) Tangent
- Parameters:
- Return type:
Construct a Tangent from an array, unit, chart, and Representation.
>>> import coordinax as cx >>> import coordinax.charts as cxc >>> import coordinax.representations as cxr
>>> v = cx.Tangent.from_([1.0, 2.0, 3.0], "m/s", cxc.cart3d, cxr.coord_vel) >>> v.basis == cxr.coord_basis True
- from_(cls: type[Tangent], obj: ArrayLike | list[Any], unit: UnitBase | FunctionUnitBase | str, chart: AbstractChart, rep: Representation, frame: AbstractReferenceFrame, /) Tangent
- Parameters:
- Return type:
Construct a Tangent from an array, unit, chart, Representation, and frame.
- from_(cls: type[Tangent], obj: ArrayLike | list[Any], unit: UnitBase | FunctionUnitBase | str, chart: AbstractChart, basis: AbstractLinearBasis, semantic: AbstractTangentSemanticKind, /) Tangent
- Parameters:
- Return type:
Construct a Tangent from array, unit, chart, basis, and semantic.
>>> import coordinax as cx >>> import coordinax.charts as cxc >>> import coordinax.representations as cxr
>>> v = cx.Tangent.from_( ... [1.0, 2.0, 3.0], "m/s", cxc.cart3d, cxr.coord_basis, cxr.vel ... ) >>> v.basis == cxr.coord_basis True
- from_(cls: type[Tangent], obj: AbstractQuantity, unit: UnitBase | FunctionUnitBase | str, chart: AbstractChart, basis: AbstractLinearBasis, semantic: AbstractTangentSemanticKind, /) Tangent
- Parameters:
- Return type:
Construct a Tangent from a Quantity, unit, chart, basis, and semantic.
The Quantity is converted to the given unit before construction.
>>> import coordinax as cx >>> import coordinax.charts as cxc >>> import coordinax.representations as cxr >>> import unxt as u
>>> v = cx.Tangent.from_( ... u.Q([1.0, 2.0, 3.0], "m/s"), "m/s", cxc.cart3d, cxr.coord_basis, cxr.vel ... ) >>> v.basis == cxr.coord_basis True
- from_(cls: type[Tangent], obj: Tangent, frame: AbstractReferenceFrame, /) Tangent
- Parameters:
- Return type:
Construct a Tangent from another Tangent, replacing its frame.
Every constructor above also accepts a trailing frame, which is attached to the constructed tangent:
>>> import coordinax as cx >>> import coordinax.charts as cxc >>> import coordinax.representations as cxr >>> import coordinax.frames as cxf >>> import unxt as u
>>> d = {"x": u.Q(1.0, "m/s"), "y": u.Q(2.0, "m/s"), "z": u.Q(3.0, "m/s")} >>> for args in [(cx.Tangent.from_(d),), (d,), (d, cxc.cart3d), ... (d, cxc.cart3d, cxr.coord_basis, cxr.vel), ... ([1.0, 2.0, 3.0], "m/s"), ... ([1.0, 2.0, 3.0], "m/s", cxc.cart3d, cxr.coord_vel)]: ... print(cx.Tangent.from_(*args, cxf.alice).frame) Alice() Alice() Alice() Alice() Alice() Alice()
- from_(cls: type[Tangent], obj: Any, frame: AbstractReferenceFrame, /) Tangent
- Parameters:
- Return type:
Construct a Tangent from data with a frame (chart and rep inferred).
- from_(cls: type[Tangent], obj: Any, chart: AbstractChart, frame: AbstractReferenceFrame, /) Tangent
- Parameters:
- Return type:
Construct a Tangent from data and chart, with a frame.
- from_(cls: type[Tangent], obj: Any, chart: AbstractChart, basis: AbstractLinearBasis, semantic: AbstractTangentSemanticKind, frame: AbstractReferenceFrame, /) Tangent
- Parameters:
- Return type:
Construct a Tangent from data, chart, basis, and semantic, with a frame.
- from_(cls: type[Tangent], obj: ArrayLike | list[Any], unit: UnitBase | FunctionUnitBase | str, frame: AbstractReferenceFrame, /) Tangent
- Parameters:
- Return type:
Construct a Tangent from an array and unit, with a frame.
- from_(cls: type[Coordinate], pv: Coordinate, /) Coordinate
- Parameters:
- Return type:
Identity: return the same Coordinate unchanged.
>>> import coordinax as cx >>> pv = cx.Coordinate(point=cx.Point.from_([1.0, 2.0, 3.0], "m")) >>> cx.Coordinate.from_(pv) is pv True
- from_(cls: type[Coordinate], p: Point, /) Coordinate
- Parameters:
- Return type:
Wrap a single
Pointas a point-only bundle (no field vectors).>>> import coordinax as cx >>> p = cx.Point.from_([1.0, 2.0, 3.0], "m") >>> pv = cx.Coordinate.from_(p) >>> pv.point is p True
- from_(cls: type[Coordinate], data: Mapping[str, Any], /, *, point: Point | None = None) Coordinate
- Parameters:
- Return type:
Create a
Coordinatefrom a mapping of named objects.The mapping may contain a
"point"key for the base; the explicitpointkeyword argument takes precedence if both are supplied.>>> import unxt as u >>> import coordinax as cx >>> import coordinax.charts as cxc >>> import coordinax.representations as cxr
>>> p = cx.Point.from_([1.0, 2.0, 3.0], "m") >>> vel = cx.Tangent.from_( ... {"x": u.Q(1.0, "m/s"), "y": u.Q(0.0, "m/s"), "z": u.Q(0.0, "m/s")}, ... cxc.cart3d, cxr.coord_vel) >>> pv = cx.Coordinate.from_({"point": p, "velocity": vel}) >>> pv.point is p True
- from_(cls: type[Point], obj: CartesianRepresentation, /) Point
- Parameters:
- Return type:
Construct Point from Astropy CartesianRepresentation.
>>> import coordinax.vectors as cxv >>> from astropy.coordinates import CartesianRepresentation
>>> vec = CartesianRepresentation(1, 2, 3, unit="km") >>> cxv.Point.from_(vec) Point({'x': Q(1., 'km'), 'y': Q(2., 'km'), 'z': Q(3., 'km')}, chart=Cart3D(M=Rn(3)))
- from_(cls: type[Point], obj: CylindricalRepresentation, /) Point
- Parameters:
- Return type:
Construct Point from Astropy CylindricalRepresentation.
>>> import astropy.units as apyu >>> import coordinax.vectors as cxv >>> from astropy.coordinates import CylindricalRepresentation
>>> vec = CylindricalRepresentation(rho=1 * apyu.km, phi=90 * apyu.deg, ... z=3 * apyu.km) >>> cxv.Point.from_(vec) Point( {'rho': Q(1., 'km'), 'phi': Q(90., 'deg'), 'z': Q(3., 'km')}, chart=Cylindrical3D(M=Rn(3)) )
- from_(cls: type[Point], obj: PhysicsSphericalRepresentation, /) Point
- Parameters:
- Return type:
Construct Point from Astropy PhysicsSphericalRepresentation.
>>> import coordinax.vectors as cxv >>> from astropy.coordinates import PhysicsSphericalRepresentation >>> import astropy.units as apyu
>>> vec = PhysicsSphericalRepresentation( ... r=1 * apyu.kpc, theta=45 * apyu.deg, phi=90 * apyu.deg) >>> cxv.Point.from_(vec) Point( {'r': Q(1., 'kpc'), 'theta': Q(45., 'deg'), 'phi': Q(90., 'deg')}, chart=Spherical3D(M=Rn(3)) )
- from_(cls: type[Point], obj: SphericalRepresentation, /) Point
- Parameters:
- Return type:
Construct Point from Astropy SphericalRepresentation.
>>> import coordinax.vectors as cxv >>> from astropy.coordinates import SphericalRepresentation >>> import astropy.units as apyu
>>> vec = SphericalRepresentation( ... lon=90 * apyu.deg, lat=45 * apyu.deg, distance=1 * apyu.kpc) >>> cxv.Point.from_(vec) Point( {'lon': Q(90., 'deg'), 'lat': Q(45., 'deg'), 'distance': Q(1., 'kpc')}, chart=LonLatSpherical3D(M=Rn(3)) )
- from_(cls: type[Point], obj: UnitSphericalRepresentation, /) Point
- Parameters:
- Return type:
Construct Point from Astropy UnitSphericalRepresentation.
A direction with no distance, so the point lives on the two-sphere rather than in R^3.
>>> import coordinax.vectors as cxv >>> from astropy.coordinates import UnitSphericalRepresentation >>> import astropy.units as apyu
>>> vec = UnitSphericalRepresentation(lon=90 * apyu.deg, lat=45 * apyu.deg) >>> cxv.Point.from_(vec) Point( {'lon': Q(90., 'deg'), 'lat': Q(45., 'deg')}, chart=LonLatSphericalTwoSphere(M=Sn(2)) )
- from_(cls: type[Point], obj: RadialRepresentation, /) Point
- Parameters:
- Return type:
Construct Point from Astropy RadialRepresentation.
>>> import coordinax.vectors as cxv >>> from astropy.coordinates import RadialRepresentation >>> import astropy.units as apyu
>>> cxv.Point.from_(RadialRepresentation(distance=1 * apyu.kpc)) Point({'r': Q(1., 'kpc')}, chart=Radial1D(M=Rn(1)))
- from_(cls: type[Point], obj: BaseCoordinateFrame, /) Point
- Parameters:
- Return type:
Construct Point from Astropy frame with data.
>>> import astropy.units as apyu >>> import astropy.coordinates as apyc >>> import coordinax.vectors as cxv
>>> vec = apyc.ICRS(ra=90 * apyu.deg, dec=45 * apyu.deg, distance=1 * apyu.kpc) >>> cxv.Point.from_(vec) Point( {'lon': Q(90., 'deg'), 'lat': Q(45., 'deg'), 'distance': Q(1., 'kpc')}, chart=LonLatSpherical3D(M=Rn(3)), frame=ICRS() )
>>> vec = apyc.Galactocentric( ... x=1 * apyu.kpc, y=2 * apyu.kpc, z=3 * apyu.kpc ... ) >>> cxv.Point.from_(vec) Point( {'x': Q(1., 'kpc'), 'y': Q(2., 'kpc'), 'z': Q(3., 'kpc')}, chart=Cart3D(M=Rn(3)), frame=Galactocentric(...) )
A ~coordinax.vectors.Point is a position, so any velocity the frame carries is not part of the result. ~coordinax.vectors.Tangent.from_ takes the other half:
>>> vec = apyc.Galactocentric( ... x=1 * apyu.kpc, y=2 * apyu.kpc, z=3 * apyu.kpc, ... v_x=4 * apyu.km / apyu.s, v_y=5 * apyu.km / apyu.s, ... v_z=6 * apyu.km / apyu.s, ... ) >>> cxv.Tangent.from_(vec) Tangent( {'x': Q(4., 'km / s'), 'y': Q(5., 'km / s'), 'z': Q(6., 'km / s')}, chart=Cart3D(M=Rn(3)), basis=coord_basis, semantic=vel )
- from_(cls: type[Tangent], obj: BaseCoordinateFrame, /) Tangent
- Parameters:
- Return type:
Construct a Tangent from the velocity of an Astropy frame.
The position half is ~coordinax.vectors.Point.from_; this is the other half, so a frame carrying both splits into the two coordinax objects.
>>> import astropy.units as apyu >>> import astropy.coordinates as apyc >>> import coordinax.vectors as cxv
>>> vec = apyc.Galactocentric( ... x=1 * apyu.kpc, y=2 * apyu.kpc, z=3 * apyu.kpc, ... v_x=4 * apyu.km / apyu.s, v_y=5 * apyu.km / apyu.s, ... v_z=6 * apyu.km / apyu.s, ... ) >>> cxv.Tangent.from_(vec) Tangent( {'x': Q(4., 'km / s'), 'y': Q(5., 'km / s'), 'z': Q(6., 'km / s')}, chart=Cart3D(M=Rn(3)), basis=coord_basis, semantic=vel )
Angular rates come across too, when the frame states them as plain
d_lonrather than astropy’scos(lat)-scaled convention:>>> vec = apyc.ICRS( ... ra=90 * apyu.deg, dec=45 * apyu.deg, distance=1 * apyu.kpc, ... pm_ra=3 * apyu.mas / apyu.yr, pm_dec=2 * apyu.mas / apyu.yr, ... radial_velocity=10 * apyu.km / apyu.s, ... differential_type=apyc.SphericalDifferential, ... ) >>> cxv.Tangent.from_(vec) Tangent( {'lon': Q(3., 'mas / yr'), 'lat': Q(2., 'mas / yr'), 'distance': Q(10., 'km / s')}, chart=LonLatSpherical3D(M=Rn(3)), basis=coord_basis, semantic=vel )
A frame without velocities says so rather than handing back a zero:
>>> try: ... cxv.Tangent.from_(apyc.ICRS(ra=1 * apyu.deg, dec=2 * apyu.deg)) ... except ValueError as e: ... print(e) ICRS carries no velocity; there is nothing to convert to a Tangent.
Construct a Tangent from the velocity of an Astropy SkyCoord.
>>> import astropy.units as apyu >>> import astropy.coordinates as apyc >>> import coordinax.vectors as cxv
A ~astropy.coordinates.SkyCoord defaults to the
cos(lat)-scaled proper motion convention, which coordinax has no chart for, so the usual astronomical spelling is refused rather than silently reinterpreted:>>> sc = apyc.SkyCoord( ... ra=90 * apyu.deg, dec=45 * apyu.deg, distance=1 * apyu.kpc, ... pm_ra_cosdec=3 * apyu.mas / apyu.yr, pm_dec=2 * apyu.mas / apyu.yr, ... radial_velocity=10 * apyu.km / apyu.s, ... ) >>> try: ... cxv.Tangent.from_(sc) ... except ValueError as e: ... print(e) astropy's SphericalCosLatDifferential is a rate convention ...
Cartesian velocities carry across unchanged:
>>> sc = apyc.SkyCoord( ... x=1 * apyu.kpc, y=2 * apyu.kpc, z=3 * apyu.kpc, ... v_x=4 * apyu.km / apyu.s, v_y=5 * apyu.km / apyu.s, ... v_z=6 * apyu.km / apyu.s, ... representation_type="cartesian", differential_type="cartesian", ... ) >>> cxv.Tangent.from_(sc) Tangent( {'x': Q(4., 'km / s'), 'y': Q(5., 'km / s'), 'z': Q(6., 'km / s')}, chart=Cart3D(M=Rn(3)), basis=coord_basis, semantic=vel )
Construct Point from Astropy SkyCoord.
>>> import astropy.units as apyu >>> import astropy.coordinates as apyc >>> import coordinax.vectors as cxv
>>> vec = apyc.SkyCoord(ra=90 * apyu.deg, dec=45 * apyu.deg, distance=1 * apyu.kpc) >>> cxv.Point.from_(vec) Point( {'lon': Q(90., 'deg'), 'lat': Q(45., 'deg'), 'distance': Q(1., 'kpc')}, chart=LonLatSpherical3D(M=Rn(3)), frame=ICRS() )
>>> vec = vec.transform_to(apyc.Galactocentric()) >>> cxv.Point.from_(vec) Point( {'x': Q(-9.08123957, 'kpc'), 'y': Q(0.21365468, 'kpc'), 'z': Q(0.2056243, 'kpc')}, chart=Cart3D(M=Rn(3)), frame=Galactocentric( galcen=Point( { 'lon': Q(266.4051, 'deg'), 'lat': Q(-28.936175, 'deg'), 'distance': Q(8.122, 'kpc') }, chart=LonLatSpherical3D(M=Rn(3)), frame=ICRS() ), roll=Angle(0., 'deg'), z_sun=Q(20.8, 'pc'), galcen_v_sun=Tangent( {'x': Q(12.9, 'km / s'), 'y': Q(245.6, 'km / s'), 'z': Q(7.78, 'km / s')}, chart=Cart3D(M=Rn(3)), basis=coord_basis, semantic=vel ) ) )
- Parameters:
cls (
type[AbstractVector])args (
Any)kwargs (
Any)
- Return type:
- classmethod is_like(obj: Any, /)
Check if the object is a AbstractVector object.
Examples
>>> import coordinax.vectors as cxv
>>> vec = cxv.Point.from_([1, 2, 3], "m") >>> cxv.AbstractVector.is_like(vec) True
>>> cxv.AbstractVector.is_like(42) False
- Parameters:
obj (
Any)- Return type:
TypeIs[Self]
- property ndim: int
- ravel()
Return a flattened vector.
- Return type:
Self
- property size: int
- to_cartesian()
Return the vector in a Cartesian chart.
This just forwards to coordinax.cartesian_chart and cconvert.
- Return type:
Examples
>>> import coordinax as cx
>>> vec = cx.Point.from_([1, 2, 3], "m").cconvert(cx.sph3d) >>> print(vec) <Point: chart=Spherical3D (r[m], theta[rad], phi[rad]) [3.742 0.641 1.107]>
>>> print(vec.to_cartesian()) <Point: chart=Cart3D (x, y, z) [m] [1. 2. 3.]>
- to_frame(toframe: AbstractReferenceFrame, /, t: Quantity | None = None)
Transform the vector to a specified reference frame.
- Parameters:
toframe (
AbstractReferenceFrame) – The target reference frame.t (
Quantity|None) – The evolution parameter (e.g. time). Defaults to 0 s.
- Returns:
New vector with the data transformed into
toframeandframe=toframe.- Return type:
Notes
This is a convenience: it rebuilds the frame-transition operator on every call, which is pure Python and dominates the cost. On a hot path or under
jit, build the operator once outside with coordinax.frames.frame_transition and pass it into the jitted function that applies it – see that function’s notes.Examples
>>> import coordinax as cx >>> import coordinax.frames as cxf
>>> p = cx.Point.from_([1, 2, 3], "kpc", cxf.alice) >>> p.to_frame(cxf.alice) is p True
- uconvert(*args, **kwargs)
Convert the vector to the given units.
This just forwards to unxt.uconvert, reversing the order of the arguments to match the unxt API.
Examples
>>> import coordinax as cx
>>> vec = cx.Point.from_([1, 2, 3], "km") >>> print(vec.uconvert({"length": "km"})) <Point: chart=Cart3D (x, y, z) [km] [1 2 3]>
- uconvert(self, usys: AbstractUnitSystem, /) Any
Convert the vector to the given units.
- Parameters:
- Return type:
Examples
>>> import unxt as u >>> import coordinax as cx
>>> usys = u.unitsystem("m", "s", "kg", "rad")
>>> vec = cx.Point.from_([1, 2, 3], "km")
>>> print(vec.uconvert(usys)) <Point: chart=Cart3D (x, y, z) [m] [1000. 2000. 3000.]>
>>> print(vec.uconvert("galactic")) <Point: chart=Cart3D (x, y, z) [kpc] [3.241e-17 6.482e-17 9.722e-17]>
- final class coordinax.Tangent(data: dict[str, V], chart: ChartT, basis: BasisT, semantic: SemanticT, frame: Any = NoFrame())
Bases:
AstropyRepresentationAPIMixin,NumpyInvertMixin[Any],LaxLenMixin,AbstractVector[ChartT,TangentGeometry,BasisT,SemanticT,V],Generic[ChartT,BasisT,SemanticT,V]A tangent-geometry vector with explicit basis and semantic kind.
A Tangent stores four pieces of information:
data: a mapping from component name to scalar-like value (typically unxt.Quantity),
chart: a chart object describing the coordinate system and component schema,
basis: an ~coordinax.representations.AbstractLinearBasis specifying the basis in which tangent components are expressed (e.g. ~coordinax.representations.CoordinateBasis or ~coordinax.representations.PhysicalBasis), and
semantic: an ~coordinax.representations.AbstractTangentSemanticKind giving the physical interpretation of the tangent vector (e.g. ~coordinax.representations.Velocity, ~coordinax.representations.Displacement).
The representation is computed from these, always with ~coordinax.representations.TangentGeometry as the geometry kind:
\[\mathrm{rep} = ( \mathrm{TangentGeometry},\, \mathrm{basis},\, \mathrm{semantic} ).\]This is contrast to ~coordinax.vectors.Point, which stores a fixed ~coordinax.representations.PointGeometry-flavoured rep and a concrete location on the manifold.
- Parameters:
data (
dict[str,TypeVar(V, bound=HasShape)]) – Mapping from chart component name to scalar value.chart (
TypeVar(ChartT, bound=AbstractChart[Any,Any,Any])) – A chart instance (e.g. cxc.cart3d) that defines the coordinate system.basis (
TypeVar(BasisT, bound=AbstractLinearBasis)) – The linear basis in which the tangent components are expressed.semantic (
TypeVar(SemanticT, bound=AbstractTangentSemanticKind)) – The semantic kind of the tangent vector (velocity, displacement, etc.).frame (
Any) – The reference frame. Defaults tocxf.noframe.
Examples
Construct a coordinate-basis velocity in Cartesian 3D:
>>> import coordinax as cx >>> import coordinax.charts as cxc >>> import coordinax.representations as cxr >>> import unxt as u
>>> v = cx.Tangent.from_( ... {"x": u.Q(1.0, "m/s"), "y": u.Q(2.0, "m/s"), "z": u.Q(3.0, "m/s")}, ... cxc.cart3d, cxr.coord_basis, cxr.vel, ... ) >>> v.rep == cxr.coord_vel True
- chart: ChartT
The chart of the vector, e.g. cxc.cart3d.
- basis: BasisT
The linear basis for tangent components.
- semantic: SemanticT
The semantic kind of the tangent vector.
- property M: AbstractManifold
The manifold of the vector, from the chart.
- astype(dtype: Any, /, **kwargs: Any)
Cast the vector to a new dtype.
Examples
>>> import quaxed.numpy as jnp >>> import unxt as u >>> import coordinax as cx
We can cast a vector to a new dtype:
>>> vec = cx.Point.from_(u.Q([1, 2, 3], "m")) >>> print(vec.astype(jnp.float32)) <Point: chart=Cart3D (x, y, z) [m] [1. 2. 3.]>
>>> print(jnp.astype(vec, jnp.float32)) <Point: chart=Cart3D (x, y, z) [m] [1. 2. 3.]>
- cconvert(*args, **kwargs)
Represent the vector as another type.
This forwards to coordinax.representations.cconvert.
Examples
>>> import coordinax as cx
>>> vec = cx.Point.from_([1, 2, 3], "m") >>> print(vec) <Point: chart=Cart3D (x, y, z) [m] [1 2 3]>
>>> print(vec.cconvert(cx.sph3d)) <Point: chart=Spherical3D (r[m], theta[rad], phi[rad]) [3.742 0.641 1.107]>
- copy()
Return a copy of the vector.
- Return type:
Self
Examples
>>> import coordinax as cx
>>> vec = cx.Point.from_([1, 2, 3], "m") >>> print(vec.copy()) <Point: chart=Cart3D (x, y, z) [m] [1 2 3]>
- property dtype: Any
- flatten()
Flatten the vector.
- Return type:
Self
- frame: AbstractReferenceFrame = NoFrame()
The reference frame. Defaults to
cxf.noframe.
- classmethod from_(*args, **kwargs)
Create a vector-like object from arguments.
Construct a point from another point.
>>> import coordinax as cx >>> vec1 = cx.Point.from_([1, 2, 3], "m") >>> vec2 = cx.Point.from_(vec1) >>> print(vec2) <Point: chart=Cart3D (x, y, z) [m] [1 2 3]>
- from_(cls: type[Point], obj: Any, chart: AbstractChart, rep: Representation, /) Point
- Parameters:
- Return type:
Construct a vector from an object, and chart and rep info.
>>> import jax.numpy as jnp >>> import unxt as u >>> import coordinax as cx
>>> xs = {"x": u.Q(1, "m"), "y": u.Q(2, "m"), "z": u.Q(3, "m")} >>> vec = cx.Point.from_(xs, cx.cart3d, cx.point) >>> print(vec) <Point: chart=Cart3D (x, y, z) [m] [1 2 3]>
>>> xs = u.Q(jnp.array([[1, 2, 3], [4, 5, 6]]), "m") >>> vec = cx.Point.from_(xs, cx.cart3d, cx.point) >>> print(vec) <Point: chart=Cart3D (x, y, z) [m] [[1 2 3] [4 5 6]]>
- from_(cls: type[Point], obj: Any, chart: AbstractChart, /) Point
- Parameters:
- Return type:
Construct a point from an object, and chart info.
>>> import jax.numpy as jnp >>> import unxt as u >>> import coordinax as cx
>>> xs = {"x": u.Q(1, "m"), "y": u.Q(2, "m"), "z": u.Q(3, "m")} >>> vec = cx.Point.from_(xs, cx.cart3d) >>> print(vec) <Point: chart=Cart3D (x, y, z) [m] [1 2 3]>
>>> xs = {"x": u.Q([1, 2], "m"), "y": u.Q([3, 4], "m"), "z": u.Q([5, 6], "m")} >>> vec = cx.Point.from_(xs, cx.cart3d) >>> print(vec) <Point: chart=Cart3D (x, y, z) [m] [[1 3 5] [2 4 6]]>
>>> xs = u.Q(jnp.array([[1, 2, 3], [4, 5, 6]]), "m") >>> vec = cx.Point.from_(xs, cx.cart3d) >>> print(vec) <Point: chart=Cart3D (x, y, z) [m] [[1 2 3] [4 5 6]]>
- from_(cls: type[Point], obj: Any, rep: Representation, /) Point
- Parameters:
- Return type:
Construct a point from an object, and rep info.
>>> import jax.numpy as jnp >>> import unxt as u >>> import coordinax as cx
>>> xs = {"x": u.Q(1, "m"), "y": u.Q(2, "m"), "z": u.Q(3, "m")} >>> vec = cx.Point.from_(xs, cx.point) >>> print(vec) <Point: chart=Cart3D (x, y, z) [m] [1 2 3]>
>>> xs = {"x": u.Q([1, 2], "m"), "y": u.Q([3, 4], "m"), "z": u.Q([5, 6], "m")} >>> vec = cx.Point.from_(xs, cx.point) >>> print(vec) <Point: chart=Cart3D (x, y, z) [m] [[1 3 5] [2 4 6]]>
>>> xs = u.Q(jnp.array([[1, 2, 3], [4, 5, 6]]), "m") >>> vec = cx.Point.from_(xs, cx.point) >>> print(vec) <Point: chart=Cart3D (x, y, z) [m] [[1 2 3] [4 5 6]]>
Construct a point from an object.
Note that this is a pretty limited constructor since it often lacks the necessary information to do a proper construction.
>>> import jax.numpy as jnp >>> import unxt as u >>> import coordinax as cx
>>> xs = {"x": u.Q(1, "m"), "y": u.Q(2, "m"), "z": u.Q(3, "m")} >>> vec = cx.Point.from_(xs) >>> print(vec) <Point: chart=Cart3D (x, y, z) [m] [1 2 3]>
>>> vec = cx.Point.from_(u.Q([1, 2, 3], "m")) >>> print(vec) <Point: chart=Cart3D (x, y, z) [m] [1 2 3]>
- from_(cls: type[Point], obj: ArrayLike | list[Any], unit: UnitBase | FunctionUnitBase | str, /) Any
- Parameters:
- Return type:
Construct a cartesian vector from an array and unit.
The
ArrayLike[Any, (*#batch, N), "..."]is expected to have the components as the last dimension.>>> import jax.numpy as jnp >>> import coordinax as cx
>>> vec = cx.Point.from_([1, 2, 3], "meter") >>> print(vec) <Point: chart=Cart3D (x, y, z) [m] [1 2 3]>
>>> xs = jnp.array([[1, 2, 3], [4, 5, 6]]) >>> vec = cx.Point.from_(xs, "meter") >>> print(vec) <Point: chart=Cart3D (x, y, z) [m] [[1 2 3] [4 5 6]]>
- from_(cls: type[Point], obj: ArrayLike | list[Any], unit: UnitBase | FunctionUnitBase | str, chart: AbstractChart, /) Any
- Parameters:
- Return type:
Construct a vector from an array, unit, and chart.
>>> import jax.numpy as jnp >>> import coordinax as cx
>>> vec = cx.Point.from_([1, 2, 3], "m", cx.cart3d) >>> print(vec) <Point: chart=Cart3D (x, y, z) [m] [1 2 3]>
>>> xs = jnp.array([[1, 2, 3], [4, 5, 6]]) >>> vec = cx.Point.from_(xs, "m", cx.cart3d) >>> print(vec) <Point: chart=Cart3D (x, y, z) [m] [[1 2 3] [4 5 6]]>
- from_(cls: type[Point], obj: ArrayLike | list[Any], unit: UnitBase | FunctionUnitBase | str, chart: AbstractChart, rep: Representation, /) Any
- Parameters:
- Return type:
Construct a vector from an array, unit, chart, and rep.
>>> import jax.numpy as jnp >>> import coordinax as cx
>>> vec = cx.Point.from_([1, 2, 3], "m", cx.cart3d, cx.point) >>> print(vec) <Point: chart=Cart3D (x, y, z) [m] [1 2 3]>
>>> xs = jnp.array([[1, 2, 3], [4, 5, 6]]) >>> vec = cx.Point.from_(xs, "m", cx.cart3d, cx.point) >>> print(vec) <Point: chart=Cart3D (x, y, z) [m] [[1 2 3] [4 5 6]]>
- from_(cls: type[Point], obj: Point, frame: AbstractReferenceFrame, /) Point
- Parameters:
- Return type:
Construct a point from another point, replacing its frame.
Every constructor above also accepts a trailing frame, which is attached to the constructed point:
>>> import coordinax as cx >>> import coordinax.charts as cxc >>> import coordinax.frames as cxf >>> import coordinax.representations as cxr >>> import unxt as u
>>> d = {"x": u.Q(1, "km"), "y": u.Q(2, "km"), "z": u.Q(3, "km")} >>> for args in [(cx.Point.from_(d),), (d,), (d, cxc.cart3d), ... (d, cxc.cart3d, cxr.point), ([1, 2, 3], "km")]: ... print(cx.Point.from_(*args, cxf.alice).frame) Alice() Alice() Alice() Alice() Alice()
An existing frame is replaced, not merged:
>>> p_alice = cx.Point.from_(d, cxf.alice) >>> cx.Point.from_(p_alice, cxf.noframe).frame == cxf.noframe True
- from_(cls: type[Point], obj: Any, frame: AbstractReferenceFrame, /) Point
- Parameters:
- Return type:
Construct a point from any object, with a frame.
- from_(cls: type[Point], obj: Any, chart: AbstractChart, frame: AbstractReferenceFrame, /) Point
- Parameters:
- Return type:
Construct a point from an object and chart, with a frame.
- from_(cls: type[Point], obj: Any, chart: AbstractChart, rep: Representation, frame: AbstractReferenceFrame, /) Point
- Parameters:
- Return type:
Construct a point from an object, chart, and representation, with a frame.
- from_(cls: type[Point], obj: ArrayLike | list[Any], unit: UnitBase | FunctionUnitBase | str, frame: AbstractReferenceFrame, /) Point
- Parameters:
- Return type:
Construct a point from an array and unit, with a frame.
Construct a Tangent from another Tangent (identity / fast path).
>>> import coordinax as cx >>> import coordinax.charts as cxc >>> import coordinax.representations as cxr >>> import unxt as u
>>> v = cx.Tangent.from_( ... {"x": u.Q(1.0, "m/s"), "y": u.Q(2.0, "m/s"), "z": u.Q(3.0, "m/s")}, ... cxc.cart3d, cxr.coord_basis, cxr.vel, ... ) >>> v2 = cx.Tangent.from_(v) >>> v2 is v True
- from_(cls: type[Tangent], obj: Any, chart: AbstractChart, basis: AbstractLinearBasis, semantic: AbstractTangentSemanticKind, /) Tangent
- Parameters:
- Return type:
Construct a Tangent from data, chart, basis, and semantic.
>>> import coordinax as cx >>> import coordinax.charts as cxc >>> import coordinax.representations as cxr >>> import unxt as u
>>> d = {"x": u.Q(1.0, "m/s"), "y": u.Q(2.0, "m/s"), "z": u.Q(3.0, "m/s")} >>> v = cx.Tangent.from_(d, cxc.cart3d, cxr.coord_basis, cxr.vel) >>> v.chart Cart3D(M=Rn(3))
- from_(cls: type[Tangent], obj: Any, chart: AbstractChart, rep: Representation, /) Tangent
- Parameters:
- Return type:
Construct a Tangent from data, chart, and a tangent Representation.
Extracts
basisandsemanticfrom the representation. RaisesTypeErrorif the representation’s geometry kind is notTangentGeometry.>>> import coordinax as cx >>> import coordinax.charts as cxc >>> import coordinax.representations as cxr >>> import unxt as u
>>> d = {"x": u.Q(1.0, "m/s"), "y": u.Q(2.0, "m/s"), "z": u.Q(3.0, "m/s")} >>> v = cx.Tangent.from_(d, cxc.cart3d, cxr.coord_vel) >>> v.basis == cxr.coord_basis True >>> v.semantic == cxr.vel True
- from_(cls: type[Tangent], obj: Any, chart: AbstractChart, /) Tangent
- Parameters:
- Return type:
Construct a Tangent from data and chart (rep inferred from data).
>>> import coordinax as cx >>> import coordinax.charts as cxc >>> import unxt as u
>>> d = {"x": u.Q(1.0, "m/s"), "y": u.Q(2.0, "m/s"), "z": u.Q(3.0, "m/s")} >>> v = cx.Tangent.from_(d, cxc.cart3d) >>> isinstance(v, cx.Tangent) True
Construct a Tangent from data alone (chart and rep inferred).
>>> import coordinax as cx >>> import unxt as u
>>> d = {"x": u.Q(1.0, "m/s"), "y": u.Q(2.0, "m/s"), "z": u.Q(3.0, "m/s")} >>> v = cx.Tangent.from_(d) >>> isinstance(v, cx.Tangent) True
- from_(cls: type[Tangent], obj: ArrayLike | list[Any], unit: UnitBase | FunctionUnitBase | str, /) Tangent
- Parameters:
- Return type:
Construct a Tangent from an array and unit (chart inferred).
>>> import coordinax as cx
>>> v = cx.Tangent.from_([1.0, 2.0, 3.0], "m/s") >>> isinstance(v, cx.Tangent) True
- from_(cls: type[Tangent], obj: ArrayLike | list[Any], unit: UnitBase | FunctionUnitBase | str, chart: AbstractChart, /) Tangent
- Parameters:
- Return type:
Construct a Tangent from an array, unit, and chart.
>>> import coordinax as cx >>> import coordinax.charts as cxc
>>> v = cx.Tangent.from_([1.0, 2.0, 3.0], "m/s", cxc.cart3d) >>> isinstance(v, cx.Tangent) True
- from_(cls: type[Tangent], obj: ArrayLike | list[Any], unit: UnitBase | FunctionUnitBase | str, chart: AbstractChart, rep: Representation, /) Tangent
- Parameters:
- Return type:
Construct a Tangent from an array, unit, chart, and Representation.
>>> import coordinax as cx >>> import coordinax.charts as cxc >>> import coordinax.representations as cxr
>>> v = cx.Tangent.from_([1.0, 2.0, 3.0], "m/s", cxc.cart3d, cxr.coord_vel) >>> v.basis == cxr.coord_basis True
- from_(cls: type[Tangent], obj: ArrayLike | list[Any], unit: UnitBase | FunctionUnitBase | str, chart: AbstractChart, rep: Representation, frame: AbstractReferenceFrame, /) Tangent
- Parameters:
- Return type:
Construct a Tangent from an array, unit, chart, Representation, and frame.
- from_(cls: type[Tangent], obj: ArrayLike | list[Any], unit: UnitBase | FunctionUnitBase | str, chart: AbstractChart, basis: AbstractLinearBasis, semantic: AbstractTangentSemanticKind, /) Tangent
- Parameters:
- Return type:
Construct a Tangent from array, unit, chart, basis, and semantic.
>>> import coordinax as cx >>> import coordinax.charts as cxc >>> import coordinax.representations as cxr
>>> v = cx.Tangent.from_( ... [1.0, 2.0, 3.0], "m/s", cxc.cart3d, cxr.coord_basis, cxr.vel ... ) >>> v.basis == cxr.coord_basis True
- from_(cls: type[Tangent], obj: AbstractQuantity, unit: UnitBase | FunctionUnitBase | str, chart: AbstractChart, basis: AbstractLinearBasis, semantic: AbstractTangentSemanticKind, /) Tangent
- Parameters:
- Return type:
Construct a Tangent from a Quantity, unit, chart, basis, and semantic.
The Quantity is converted to the given unit before construction.
>>> import coordinax as cx >>> import coordinax.charts as cxc >>> import coordinax.representations as cxr >>> import unxt as u
>>> v = cx.Tangent.from_( ... u.Q([1.0, 2.0, 3.0], "m/s"), "m/s", cxc.cart3d, cxr.coord_basis, cxr.vel ... ) >>> v.basis == cxr.coord_basis True
- from_(cls: type[Tangent], obj: Tangent, frame: AbstractReferenceFrame, /) Tangent
- Parameters:
- Return type:
Construct a Tangent from another Tangent, replacing its frame.
Every constructor above also accepts a trailing frame, which is attached to the constructed tangent:
>>> import coordinax as cx >>> import coordinax.charts as cxc >>> import coordinax.representations as cxr >>> import coordinax.frames as cxf >>> import unxt as u
>>> d = {"x": u.Q(1.0, "m/s"), "y": u.Q(2.0, "m/s"), "z": u.Q(3.0, "m/s")} >>> for args in [(cx.Tangent.from_(d),), (d,), (d, cxc.cart3d), ... (d, cxc.cart3d, cxr.coord_basis, cxr.vel), ... ([1.0, 2.0, 3.0], "m/s"), ... ([1.0, 2.0, 3.0], "m/s", cxc.cart3d, cxr.coord_vel)]: ... print(cx.Tangent.from_(*args, cxf.alice).frame) Alice() Alice() Alice() Alice() Alice() Alice()
- from_(cls: type[Tangent], obj: Any, frame: AbstractReferenceFrame, /) Tangent
- Parameters:
- Return type:
Construct a Tangent from data with a frame (chart and rep inferred).
- from_(cls: type[Tangent], obj: Any, chart: AbstractChart, frame: AbstractReferenceFrame, /) Tangent
- Parameters:
- Return type:
Construct a Tangent from data and chart, with a frame.
- from_(cls: type[Tangent], obj: Any, chart: AbstractChart, basis: AbstractLinearBasis, semantic: AbstractTangentSemanticKind, frame: AbstractReferenceFrame, /) Tangent
- Parameters:
- Return type:
Construct a Tangent from data, chart, basis, and semantic, with a frame.
- from_(cls: type[Tangent], obj: ArrayLike | list[Any], unit: UnitBase | FunctionUnitBase | str, frame: AbstractReferenceFrame, /) Tangent
- Parameters:
- Return type:
Construct a Tangent from an array and unit, with a frame.
- from_(cls: type[Coordinate], pv: Coordinate, /) Coordinate
- Parameters:
- Return type:
Identity: return the same Coordinate unchanged.
>>> import coordinax as cx >>> pv = cx.Coordinate(point=cx.Point.from_([1.0, 2.0, 3.0], "m")) >>> cx.Coordinate.from_(pv) is pv True
- from_(cls: type[Coordinate], p: Point, /) Coordinate
- Parameters:
- Return type:
Wrap a single
Pointas a point-only bundle (no field vectors).>>> import coordinax as cx >>> p = cx.Point.from_([1.0, 2.0, 3.0], "m") >>> pv = cx.Coordinate.from_(p) >>> pv.point is p True
- from_(cls: type[Coordinate], data: Mapping[str, Any], /, *, point: Point | None = None) Coordinate
- Parameters:
- Return type:
Create a
Coordinatefrom a mapping of named objects.The mapping may contain a
"point"key for the base; the explicitpointkeyword argument takes precedence if both are supplied.>>> import unxt as u >>> import coordinax as cx >>> import coordinax.charts as cxc >>> import coordinax.representations as cxr
>>> p = cx.Point.from_([1.0, 2.0, 3.0], "m") >>> vel = cx.Tangent.from_( ... {"x": u.Q(1.0, "m/s"), "y": u.Q(0.0, "m/s"), "z": u.Q(0.0, "m/s")}, ... cxc.cart3d, cxr.coord_vel) >>> pv = cx.Coordinate.from_({"point": p, "velocity": vel}) >>> pv.point is p True
- from_(cls: type[Point], obj: CartesianRepresentation, /) Point
- Parameters:
- Return type:
Construct Point from Astropy CartesianRepresentation.
>>> import coordinax.vectors as cxv >>> from astropy.coordinates import CartesianRepresentation
>>> vec = CartesianRepresentation(1, 2, 3, unit="km") >>> cxv.Point.from_(vec) Point({'x': Q(1., 'km'), 'y': Q(2., 'km'), 'z': Q(3., 'km')}, chart=Cart3D(M=Rn(3)))
- from_(cls: type[Point], obj: CylindricalRepresentation, /) Point
- Parameters:
- Return type:
Construct Point from Astropy CylindricalRepresentation.
>>> import astropy.units as apyu >>> import coordinax.vectors as cxv >>> from astropy.coordinates import CylindricalRepresentation
>>> vec = CylindricalRepresentation(rho=1 * apyu.km, phi=90 * apyu.deg, ... z=3 * apyu.km) >>> cxv.Point.from_(vec) Point( {'rho': Q(1., 'km'), 'phi': Q(90., 'deg'), 'z': Q(3., 'km')}, chart=Cylindrical3D(M=Rn(3)) )
- from_(cls: type[Point], obj: PhysicsSphericalRepresentation, /) Point
- Parameters:
- Return type:
Construct Point from Astropy PhysicsSphericalRepresentation.
>>> import coordinax.vectors as cxv >>> from astropy.coordinates import PhysicsSphericalRepresentation >>> import astropy.units as apyu
>>> vec = PhysicsSphericalRepresentation( ... r=1 * apyu.kpc, theta=45 * apyu.deg, phi=90 * apyu.deg) >>> cxv.Point.from_(vec) Point( {'r': Q(1., 'kpc'), 'theta': Q(45., 'deg'), 'phi': Q(90., 'deg')}, chart=Spherical3D(M=Rn(3)) )
- from_(cls: type[Point], obj: SphericalRepresentation, /) Point
- Parameters:
- Return type:
Construct Point from Astropy SphericalRepresentation.
>>> import coordinax.vectors as cxv >>> from astropy.coordinates import SphericalRepresentation >>> import astropy.units as apyu
>>> vec = SphericalRepresentation( ... lon=90 * apyu.deg, lat=45 * apyu.deg, distance=1 * apyu.kpc) >>> cxv.Point.from_(vec) Point( {'lon': Q(90., 'deg'), 'lat': Q(45., 'deg'), 'distance': Q(1., 'kpc')}, chart=LonLatSpherical3D(M=Rn(3)) )
- from_(cls: type[Point], obj: UnitSphericalRepresentation, /) Point
- Parameters:
- Return type:
Construct Point from Astropy UnitSphericalRepresentation.
A direction with no distance, so the point lives on the two-sphere rather than in R^3.
>>> import coordinax.vectors as cxv >>> from astropy.coordinates import UnitSphericalRepresentation >>> import astropy.units as apyu
>>> vec = UnitSphericalRepresentation(lon=90 * apyu.deg, lat=45 * apyu.deg) >>> cxv.Point.from_(vec) Point( {'lon': Q(90., 'deg'), 'lat': Q(45., 'deg')}, chart=LonLatSphericalTwoSphere(M=Sn(2)) )
- from_(cls: type[Point], obj: RadialRepresentation, /) Point
- Parameters:
- Return type:
Construct Point from Astropy RadialRepresentation.
>>> import coordinax.vectors as cxv >>> from astropy.coordinates import RadialRepresentation >>> import astropy.units as apyu
>>> cxv.Point.from_(RadialRepresentation(distance=1 * apyu.kpc)) Point({'r': Q(1., 'kpc')}, chart=Radial1D(M=Rn(1)))
- from_(cls: type[Point], obj: BaseCoordinateFrame, /) Point
- Parameters:
- Return type:
Construct Point from Astropy frame with data.
>>> import astropy.units as apyu >>> import astropy.coordinates as apyc >>> import coordinax.vectors as cxv
>>> vec = apyc.ICRS(ra=90 * apyu.deg, dec=45 * apyu.deg, distance=1 * apyu.kpc) >>> cxv.Point.from_(vec) Point( {'lon': Q(90., 'deg'), 'lat': Q(45., 'deg'), 'distance': Q(1., 'kpc')}, chart=LonLatSpherical3D(M=Rn(3)), frame=ICRS() )
>>> vec = apyc.Galactocentric( ... x=1 * apyu.kpc, y=2 * apyu.kpc, z=3 * apyu.kpc ... ) >>> cxv.Point.from_(vec) Point( {'x': Q(1., 'kpc'), 'y': Q(2., 'kpc'), 'z': Q(3., 'kpc')}, chart=Cart3D(M=Rn(3)), frame=Galactocentric(...) )
A ~coordinax.vectors.Point is a position, so any velocity the frame carries is not part of the result. ~coordinax.vectors.Tangent.from_ takes the other half:
>>> vec = apyc.Galactocentric( ... x=1 * apyu.kpc, y=2 * apyu.kpc, z=3 * apyu.kpc, ... v_x=4 * apyu.km / apyu.s, v_y=5 * apyu.km / apyu.s, ... v_z=6 * apyu.km / apyu.s, ... ) >>> cxv.Tangent.from_(vec) Tangent( {'x': Q(4., 'km / s'), 'y': Q(5., 'km / s'), 'z': Q(6., 'km / s')}, chart=Cart3D(M=Rn(3)), basis=coord_basis, semantic=vel )
- from_(cls: type[Tangent], obj: BaseCoordinateFrame, /) Tangent
- Parameters:
- Return type:
Construct a Tangent from the velocity of an Astropy frame.
The position half is ~coordinax.vectors.Point.from_; this is the other half, so a frame carrying both splits into the two coordinax objects.
>>> import astropy.units as apyu >>> import astropy.coordinates as apyc >>> import coordinax.vectors as cxv
>>> vec = apyc.Galactocentric( ... x=1 * apyu.kpc, y=2 * apyu.kpc, z=3 * apyu.kpc, ... v_x=4 * apyu.km / apyu.s, v_y=5 * apyu.km / apyu.s, ... v_z=6 * apyu.km / apyu.s, ... ) >>> cxv.Tangent.from_(vec) Tangent( {'x': Q(4., 'km / s'), 'y': Q(5., 'km / s'), 'z': Q(6., 'km / s')}, chart=Cart3D(M=Rn(3)), basis=coord_basis, semantic=vel )
Angular rates come across too, when the frame states them as plain
d_lonrather than astropy’scos(lat)-scaled convention:>>> vec = apyc.ICRS( ... ra=90 * apyu.deg, dec=45 * apyu.deg, distance=1 * apyu.kpc, ... pm_ra=3 * apyu.mas / apyu.yr, pm_dec=2 * apyu.mas / apyu.yr, ... radial_velocity=10 * apyu.km / apyu.s, ... differential_type=apyc.SphericalDifferential, ... ) >>> cxv.Tangent.from_(vec) Tangent( {'lon': Q(3., 'mas / yr'), 'lat': Q(2., 'mas / yr'), 'distance': Q(10., 'km / s')}, chart=LonLatSpherical3D(M=Rn(3)), basis=coord_basis, semantic=vel )
A frame without velocities says so rather than handing back a zero:
>>> try: ... cxv.Tangent.from_(apyc.ICRS(ra=1 * apyu.deg, dec=2 * apyu.deg)) ... except ValueError as e: ... print(e) ICRS carries no velocity; there is nothing to convert to a Tangent.
Construct a Tangent from the velocity of an Astropy SkyCoord.
>>> import astropy.units as apyu >>> import astropy.coordinates as apyc >>> import coordinax.vectors as cxv
A ~astropy.coordinates.SkyCoord defaults to the
cos(lat)-scaled proper motion convention, which coordinax has no chart for, so the usual astronomical spelling is refused rather than silently reinterpreted:>>> sc = apyc.SkyCoord( ... ra=90 * apyu.deg, dec=45 * apyu.deg, distance=1 * apyu.kpc, ... pm_ra_cosdec=3 * apyu.mas / apyu.yr, pm_dec=2 * apyu.mas / apyu.yr, ... radial_velocity=10 * apyu.km / apyu.s, ... ) >>> try: ... cxv.Tangent.from_(sc) ... except ValueError as e: ... print(e) astropy's SphericalCosLatDifferential is a rate convention ...
Cartesian velocities carry across unchanged:
>>> sc = apyc.SkyCoord( ... x=1 * apyu.kpc, y=2 * apyu.kpc, z=3 * apyu.kpc, ... v_x=4 * apyu.km / apyu.s, v_y=5 * apyu.km / apyu.s, ... v_z=6 * apyu.km / apyu.s, ... representation_type="cartesian", differential_type="cartesian", ... ) >>> cxv.Tangent.from_(sc) Tangent( {'x': Q(4., 'km / s'), 'y': Q(5., 'km / s'), 'z': Q(6., 'km / s')}, chart=Cart3D(M=Rn(3)), basis=coord_basis, semantic=vel )
Construct Point from Astropy SkyCoord.
>>> import astropy.units as apyu >>> import astropy.coordinates as apyc >>> import coordinax.vectors as cxv
>>> vec = apyc.SkyCoord(ra=90 * apyu.deg, dec=45 * apyu.deg, distance=1 * apyu.kpc) >>> cxv.Point.from_(vec) Point( {'lon': Q(90., 'deg'), 'lat': Q(45., 'deg'), 'distance': Q(1., 'kpc')}, chart=LonLatSpherical3D(M=Rn(3)), frame=ICRS() )
>>> vec = vec.transform_to(apyc.Galactocentric()) >>> cxv.Point.from_(vec) Point( {'x': Q(-9.08123957, 'kpc'), 'y': Q(0.21365468, 'kpc'), 'z': Q(0.2056243, 'kpc')}, chart=Cart3D(M=Rn(3)), frame=Galactocentric( galcen=Point( { 'lon': Q(266.4051, 'deg'), 'lat': Q(-28.936175, 'deg'), 'distance': Q(8.122, 'kpc') }, chart=LonLatSpherical3D(M=Rn(3)), frame=ICRS() ), roll=Angle(0., 'deg'), z_sun=Q(20.8, 'pc'), galcen_v_sun=Tangent( {'x': Q(12.9, 'km / s'), 'y': Q(245.6, 'km / s'), 'z': Q(7.78, 'km / s')}, chart=Cart3D(M=Rn(3)), basis=coord_basis, semantic=vel ) ) )
- Parameters:
cls (
type[AbstractVector])args (
Any)kwargs (
Any)
- Return type:
- classmethod is_like(obj: Any, /)
Check if the object is a AbstractVector object.
Examples
>>> import coordinax.vectors as cxv
>>> vec = cxv.Point.from_([1, 2, 3], "m") >>> cxv.AbstractVector.is_like(vec) True
>>> cxv.AbstractVector.is_like(42) False
- Parameters:
obj (
Any)- Return type:
TypeIs[Self]
- property ndim: int
- ravel()
Return a flattened vector.
- Return type:
Self
- represent_as(target: Any, *args: Any, **kwargs: Any)
Represent the vector as another type.
This just forwards to coordinax.cconvert.
- Parameters:
target (
Any) – The representation type to convert to, e.g. cxc.sph3d.*args (
Any) – Extra arguments. These are passed to coordinax.cconvert and might be used, depending on the dispatched method. E.g. for transforming a velocity or acceleration vector, generally the first argument is the position vector at which the differential is defined. In general this is a required argument, though it is not for Cartesian-to-Cartesian transforms – see https://en.wikipedia.org/wiki/Tensors_in_curvilinear_coordinates for more information.**kwargs (
Any) – Extra arguments. These are passed to coordinax.cconvert and might be used, depending on the dispatched method. E.g. for transforming a velocity or acceleration vector, generally the first argument is the position vector at which the differential is defined. In general this is a required argument, though it is not for Cartesian-to-Cartesian transforms – see https://en.wikipedia.org/wiki/Tensors_in_curvilinear_coordinates for more information.
- Return type:
Examples
>>> import coordinax as cx >>> import coordinax.charts as cxc
Transforming a Position:
>>> q_cart = cx.Point.from_([1, 2, 3], "m") >>> q_sph = q_cart.represent_as(cxc.sph3d) >>> print(q_sph) <Point: chart=Spherical3D (r[m], theta[rad], phi[rad]) [3.742 0.641 1.107]>
- property size: int
- to_cartesian()
Return the vector in a Cartesian chart.
This just forwards to coordinax.cartesian_chart and cconvert.
- Return type:
Examples
>>> import coordinax as cx
>>> vec = cx.Point.from_([1, 2, 3], "m").cconvert(cx.sph3d) >>> print(vec) <Point: chart=Spherical3D (r[m], theta[rad], phi[rad]) [3.742 0.641 1.107]>
>>> print(vec.to_cartesian()) <Point: chart=Cart3D (x, y, z) [m] [1. 2. 3.]>
- to_frame(toframe: AbstractReferenceFrame, /, t: Quantity | None = None)
Transform the vector to a specified reference frame.
- Parameters:
toframe (
AbstractReferenceFrame) – The target reference frame.t (
Quantity|None) – The evolution parameter (e.g. time). Defaults to 0 s.
- Returns:
New vector with the data transformed into
toframeandframe=toframe.- Return type:
Notes
This is a convenience: it rebuilds the frame-transition operator on every call, which is pure Python and dominates the cost. On a hot path or under
jit, build the operator once outside with coordinax.frames.frame_transition and pass it into the jitted function that applies it – see that function’s notes.Examples
>>> import coordinax as cx >>> import coordinax.frames as cxf
>>> p = cx.Point.from_([1, 2, 3], "kpc", cxf.alice) >>> p.to_frame(cxf.alice) is p True
- uconvert(*args, **kwargs)
Convert the vector to the given units.
This just forwards to unxt.uconvert, reversing the order of the arguments to match the unxt API.
Examples
>>> import coordinax as cx
>>> vec = cx.Point.from_([1, 2, 3], "km") >>> print(vec.uconvert({"length": "km"})) <Point: chart=Cart3D (x, y, z) [km] [1 2 3]>
- uconvert(self, usys: AbstractUnitSystem, /) Any
Convert the vector to the given units.
- Parameters:
- Return type:
Examples
>>> import unxt as u >>> import coordinax as cx
>>> usys = u.unitsystem("m", "s", "kg", "rad")
>>> vec = cx.Point.from_([1, 2, 3], "km")
>>> print(vec.uconvert(usys)) <Point: chart=Cart3D (x, y, z) [m] [1000. 2000. 3000.]>
>>> print(vec.uconvert("galactic")) <Point: chart=Cart3D (x, y, z) [kpc] [3.241e-17 6.482e-17 9.722e-17]>
- property rep: Representation
The representation, computed from basis and semantic.
- final class coordinax.ToUnitsOptions(*values)
Bases:
EnumOptions for the units argument of uconvert.
This enum provides named conversion behaviors that are accepted by Point.uconvert.
Examples
Point.uconvert with consistent:
>>> import unxt as u >>> import coordinax as cx
>>> vec = cx.Point.from_({"x": u.Q(1, "m"), "y": u.Q(2, "km")}, cx.cart2d) >>> print(vec.uconvert(cx.ToUnitsOptions.consistent)) <Point: chart=Cart2D (x, y) [m] [1.e+00 2.e+03]>
- consistent = 'consistent'
Convert to consistent units.
- coordinax.equivalent(a, b, /)
Whether two unit systems describe the same physical structure.
Two unit systems are equivalent when they share the same set of base dimensions and, for each dimension, their units are interconvertible. Unlike
==, the specific units need not be identical – only convertible – so systems that measure the same dimensions in different (but compatible) units are equivalent.Examples
>>> from unxt.unitsystems import galactic, solarsystem, si, equivalent
galactic(kpc, Myr, solMass, rad) andsolarsystem(AU, yr, solMass, rad) have the same base dimensions and interconvertible units:>>> equivalent(galactic, solarsystem) True
>>> equivalent(galactic, galactic) True
A system with different base dimensions is not equivalent:
>>> equivalent(galactic, si) False
- coordinax.equivalent(a: AbstractQuantity, b: AbstractQuantity, /) Any
- Parameters:
- Return type:
Whether two quantities are physically equal, accounting for units.
This is the unit-aware counterpart to
==(which is unit-blind for StaticValue-backed quantities – see AbstractQuantity.__eq__). The result mirrors==’s shape: a scalar bool for StaticValue-backed operands, and an element-wise dimensionless Quantity of booleans for array-backed ones. Quantities with incompatible dimensions are never equivalent (and this never raises).Examples
>>> import numpy as np >>> import unxt as u >>> from unxt.quantity import StaticValue
Physically-equal static quantities in different units are equivalent, even though unit-blind
==reportsFalse:>>> a = u.Q(StaticValue(np.array([1.0, 2.0])), "m") >>> b = u.Q(StaticValue(np.array([0.001, 0.002])), "km") >>> a == b False >>> u.equivalent(a, b) True >>> a.is_equivalent(b) True
Array-backed quantities compare element-wise (unit-aware):
>>> u.equivalent(u.Q([1.0, 2.0], "m"), u.Q([0.001, 0.009], "km")) Quantity(Array([ True, False], dtype=bool), unit='')
Incompatible dimensions are never equivalent:
>>> u.equivalent(u.Q(1.0, "m"), u.Q(1.0, "s")) False
- coordinax.equivalent(a: AbstractVector, b: AbstractVector, /, *, rtol: float = 1e-05, atol: float = 1e-08) Any
- Parameters:
- Return type:
Whether two vectors denote the same geometric point.
Unlike
==– which is strict (equal only for matching chart, frame, and data) –equivalentis invariant to the chart and to the component units: it compares the two vectors as points in a common Cartesian chart. It remains frame-strict, since coordinates in different frames describe different physical points. Because chart transitions are trigonometric and square-root heavy, the comparison is tolerance-based (rtol, atol);atolis measured in the Cartesian component units of the first operand (or in raw component units for unitless vectors).Examples
>>> import coordinax as cx >>> import coordinax.charts as cxc
The same point in Cartesian and spherical charts is not
==(the charts differ) but isequivalent:>>> p = cx.Point.from_([1.0, 2.0, 3.0], "m") >>> sph = p.cconvert(cxc.sph3d) >>> bool(p == sph) False >>> bool(cx.equivalent(p, sph)) True
Equivalence is also invariant to the component units:
>>> q = cx.Point.from_([1.0, 2.0, 3.0], "km") >>> mm = cx.Point.from_([1e6, 2e6, 3e6], "mm") >>> bool(cx.equivalent(q, mm)) True
Distinct points are not equivalent:
>>> bool(cx.equivalent(p, cx.Point.from_([1.0, 2.0, 4.0], "m"))) False
- Parameters:
- Return type:
- coordinax.geodesic_distance(*args, **kwargs)
Distance between two points on a manifold.
The length of the shortest path along the manifold: the straight line in flat space, the great circle on a sphere. It is computed from the manifold’s geometry, not from the coordinate difference, whose norm is asymmetric on a curved manifold and so is not a distance at all. A manifold with no closed-form geodesic raises rather than approximating.
Dispatches on the inputs: two ~coordinax.vectors.Point objects (chart/frame extracted automatically), or a
chart/metrictogether with the two points as component dictionaries, packed quantities, or bare arrays.- coordinax.geodesic_distance(chart: AbstractChart, a: dict, b: dict, /, *, usys: AbstractUnitSystem | None = None) Any
Geodesic distance between two points, on the chart’s manifold.
>>> import unxt as u >>> import coordinax.charts as cxc >>> import coordinax.manifolds as cxm
A 3-4-5 triangle in flat space:
>>> a = {"x": u.Q(3.0, "m"), "y": u.Q(0.0, "m"), "z": u.Q(0.0, "m")} >>> b = {"x": u.Q(0.0, "m"), "y": u.Q(4.0, "m"), "z": u.Q(0.0, "m")} >>> cxm.geodesic_distance(cxc.cart3d, a, b).round(2) Distance(5., 'm')
The same two points in a curvilinear chart give the same answer, which the norm of their coordinate difference would not:
>>> a_sph = cxc.pt_map(a, cxc.cart3d, cxc.sph3d) >>> b_sph = cxc.pt_map(b, cxc.cart3d, cxc.sph3d) >>> cxm.geodesic_distance(cxc.sph3d, a_sph, b_sph).round(2) Distance(5., 'm')
On the unit sphere it is the great-circle distance, and symmetric:
>>> import jax.numpy as jnp >>> p = {"theta": u.Angle(jnp.pi / 2, "rad"), "phi": u.Angle(0.0, "rad")} >>> q = {"theta": u.Angle(jnp.pi / 2, "rad"), "phi": u.Angle(1.0, "rad")} >>> round(float(cxm.geodesic_distance(cxc.sph2, p, q).ustrip("rad")), 6) 1.0 >>> round(float(cxm.geodesic_distance(cxc.sph2, q, p).ustrip("rad")), 6) 1.0
- coordinax.geodesic_distance(M: EuclideanManifold, chart: AbstractChart, a: dict, b: dict, /, *, usys: AbstractUnitSystem | None = None) Any
Return the straight-line distance, measured in the Cartesian chart.
Mapping both points into the manifold’s Cartesian chart first is what makes this chart-invariant and symmetric. Measuring
||b - a||in the chart the caller happened to use is neither, unless that chart is already Cartesian.>>> import unxt as u >>> import coordinax.charts as cxc >>> import coordinax.manifolds as cxm
>>> a = {"r": u.Q(2.0, "m"), "theta": u.Angle(1.0, "rad"), ... "phi": u.Angle(0.4, "rad")} >>> b = {"r": u.Q(3.0, "m"), "theta": u.Angle(1.3, "rad"), ... "phi": u.Angle(0.9, "rad")} >>> round(float(cxm.geodesic_distance(cxc.sph3d, a, b).ustrip("m")), 6) 1.651376
- coordinax.geodesic_distance(chart: AbstractChart, a: AbstractQuantity, b: AbstractQuantity, /, *, usys: AbstractUnitSystem | None = None) Any
Distance between two points given as packed unxt.Quantity vectors.
Each quantity’s trailing axis holds the components in
chart.componentsorder; it is unpacked into a component dictionary and delegated to theCDictoverload.>>> import unxt as u >>> import coordinax.charts as cxc >>> import coordinax.manifolds as cxm
>>> a = u.Q([3.0, 0.0, 0.0], "m") >>> b = u.Q([0.0, 4.0, 0.0], "m") >>> cxm.geodesic_distance(cxc.cart3d, a, b).round(2) Distance(5., 'm')
- coordinax.geodesic_distance(chart: AbstractChart, a: Array, b: Array, /, *, usys: AbstractUnitSystem | None = None) Any
Distance between two points given as packed (unitless) arrays.
The trailing axis holds the components in
chart.componentsorder.>>> import jax.numpy as jnp >>> import coordinax.charts as cxc >>> import coordinax.manifolds as cxm
>>> a = jnp.array([3.0, 0.0, 0.0]) >>> b = jnp.array([0.0, 4.0, 0.0]) >>> float(cxm.geodesic_distance(cxc.cart3d, a, b)) 5.0
- coordinax.geodesic_distance(M: HyperSphericalManifold, chart: AbstractChart, a: dict, b: dict, /, *, usys: AbstractUnitSystem | None = None) Any
Return the great-circle distance on the unit hypersphere.
The points are embedded into the ambient Cartesian space as unit vectors and the central angle between them is taken, which for a unit sphere is the arc length. Any chart on the sphere works: the points are routed through the embedding’s own intrinsic chart first, so the answer does not depend on which one the caller used.
>>> import jax.numpy as jnp >>> import unxt as u >>> import coordinax.charts as cxc >>> import coordinax.manifolds as cxm
A quarter turn along the equator is pi / 2:
>>> a = {"theta": u.Angle(jnp.pi / 2, "rad"), "phi": u.Angle(0.0, "rad")} >>> b = {"theta": u.Angle(jnp.pi / 2, "rad"), "phi": u.Angle(jnp.pi / 2, "rad")} >>> round(float(cxm.geodesic_distance(cxc.sph2, a, b).ustrip("rad")), 6) 1.570796
Antipodes are pi apart – the coordinate-difference norm cannot reach this, since it has no way to know the sphere closes up:
>>> n = {"theta": u.Angle(0.0, "rad"), "phi": u.Angle(0.0, "rad")} >>> sth = {"theta": u.Angle(jnp.pi, "rad"), "phi": u.Angle(0.0, "rad")} >>> round(float(cxm.geodesic_distance(cxc.sph2, n, sth).ustrip("rad")), 6) 3.141593
- coordinax.geodesic_distance(M: EmbeddedManifold, chart: AbstractChart, a: dict, b: dict, /, *, usys: AbstractUnitSystem | None = None) Any
Embedded sphere: the arc length, i.e. the radius times the central angle.
Delegates the angle to the intrinsic manifold’s own rule rather than re-deriving it, so there is one implementation of the great circle. Only the radius is applied here, which is what the embedding adds.
>>> import jax.numpy as jnp >>> import unxt as u >>> import coordinax.charts as cxc >>> import coordinax.manifolds as cxm
>>> M = cxm.EmbeddedManifold( ... intrinsic=cxm.S2, ambient=cxm.R3, ... embed_map=cxm.TwoSphereIn3D(radius=u.Q(2.0, "m")), ... ) >>> n = {"theta": u.Angle(0.0, "rad"), "phi": u.Angle(0.0, "rad")} >>> s = {"theta": u.Angle(jnp.pi, "rad"), "phi": u.Angle(0.0, "rad")} >>> cxm.geodesic_distance(M, cxc.sph2, n, s).round(4) Distance(6.2832, 'm')
Reached through an EmbeddedChart too, whose manifold is this one:
>>> chart = cxm.EmbeddedChart(cxm.TwoSphereIn3D(radius=u.Q(2.0, "m"))) >>> cxm.geodesic_distance(chart, n, s).round(4) Distance(6.2832, 'm')
- coordinax.geodesic_distance(M: AbstractManifold, chart: AbstractChart, a: dict, b: dict, /, *, usys: AbstractUnitSystem | None = None) Any
Refuse: no closed-form geodesic is known for this manifold.
Refused rather than approximated: the norm of the coordinate difference, which this used to return, is asymmetric on a curved manifold and so is not a distance at all.
- coordinax.geodesic_distance(M: MinkowskiManifold, chart: AbstractChart, a: dict, b: dict, /, *, usys: AbstractUnitSystem | None = None) Any
Refuse: a pseudo-Riemannian manifold has no Riemannian distance.
The Minkowski metric is indefinite, so
g(v, v)is negative for a timelike pair and its square root is not a length. Returningnanthere while returning a plausible number for a spacelike pair – which is what taking the root unguarded does – hides the failure in exactly the half of spacetime a reader is least likely to probe.>>> import unxt as u >>> import coordinax.charts as cxc >>> import coordinax.manifolds as cxm
>>> origin = {k: u.Q(0.0, "m") for k in ("ct", "x", "y", "z")} >>> event = {"ct": u.Q(5.0, "m"), "x": u.Q(1.0, "m"), ... "y": u.Q(0.0, "m"), "z": u.Q(0.0, "m")} >>> try: cxm.geodesic_distance(cxc.minkowskict, origin, event) ... except NotImplementedError as e: print(e) geodesic_distance() requires a Riemannian (positive-definite) metric; MinkowskiManifold() is pseudo-Riemannian, whose indefinite metric admits no distance. Use `interval` for the signed square, `proper_time` for a timelike pair, or `proper_distance` for a spacelike one.
- coordinax.geodesic_distance(metric: AbstractMetricField, chart: AbstractChart, a: dict, b: dict, /, *, usys: AbstractUnitSystem | None = None) Any
Geodesic distance with the metric stated explicitly.
The geodesic is a property of the manifold, so this checks that
metricis the onechartcarries and then defers to the manifold rule. It exists so a caller can be explicit, and so a mismatched metric is refused rather than silently replaced by the chart’s.>>> import unxt as u >>> import coordinax.charts as cxc >>> import coordinax.manifolds as cxm
>>> a = {"x": u.Q(3.0, "m"), "y": u.Q(0.0, "m"), "z": u.Q(0.0, "m")} >>> b = {"x": u.Q(0.0, "m"), "y": u.Q(4.0, "m"), "z": u.Q(0.0, "m")} >>> cxm.geodesic_distance(cxm.FlatMetric(3), cxc.cart3d, a, b).round(2) Distance(5., 'm')
- coordinax.geodesic_distance(chart: Cart0D | Cart1D | Cart2D | Cart3D | CartND, a: Any, b: Any, /, *, usys: AbstractUnitSystem | None = None) Any
Packed operands in a Cartesian chart: measure without unpacking.
Registered as two signatures rather than one with a
Quantity | Arrayunion: each generic overload it overrides is typed for one of them, and a union would be narrower in the chart but wider in the operands, so plum would rank neither more specific and refuse the call as ambiguous.The generic packed overloads split the operands into component dicts so a curvilinear chart can be mapped to Cartesian. Here there is nothing to map, and the components are already on the trailing axis in the right order, so the norm is taken directly.
>>> import jax.numpy as jnp >>> import unxt as u >>> import coordinax.charts as cxc >>> import coordinax.manifolds as cxm
>>> a = u.Q(jnp.asarray([3.0, 0.0, 0.0]), "m") >>> b = u.Q(jnp.asarray([0.0, 4.0, 0.0]), "m") >>> cxm.geodesic_distance(cxc.cart3d, a, b).round(2) Distance(5., 'm')
>>> float(cxm.geodesic_distance(cxc.cart3d, jnp.asarray([3.0, 0.0, 0.0]), ... jnp.asarray([0.0, 4.0, 0.0]))) 5.0
Distance between two points, via the manifold norm.
The two points are brought into a common Cartesian chart (so the result is invariant to the chart and component units each operand happens to use), and the distance is the manifold ~coordinax.manifolds.norm of their coordinate difference – the Euclidean distance for a flat manifold. A length result is returned as a Distance; a unitless (dimensionless) result is returned as a bare array.
Dimensionality follows the points’ manifold: 2-D points give a 2-D distance, 3-D points a 3-D distance. There is no
separation_3d– to measure in a particular N-D space, map the points into it first, then call geodesic_distance.Examples
>>> import coordinax as cx >>> import coordinax.charts as cxc
A 3-4-5 right triangle:
>>> p = cx.Point.from_([3.0, 0.0, 0.0], "m") >>> q = cx.Point.from_([0.0, 4.0, 0.0], "m") >>> cx.geodesic_distance(p, q).round(2) Distance(5., 'm')
Chart- and unit-invariant – the same points expressed differently give the same distance:
>>> cx.geodesic_distance(p, q.cconvert(cxc.sph3d)).round(2) Distance(5., 'm') >>> q_km = cx.Point.from_([0.0, 0.004, 0.0], "km") >>> cx.geodesic_distance(p, q_km).uconvert("m").round(2) Distance(5., 'm')
The distance lives on the points’ manifold, so 2-D points give a 2-D distance:
>>> p2 = cx.Point.from_([3.0, 0.0], "m") >>> q2 = cx.Point.from_([0.0, 4.0], "m") >>> cx.geodesic_distance(p2, q2).round(2) Distance(5., 'm')