coordinax.manifolds#
The coordinax.manifolds module provides manifold and atlas objects, plus manifold-level point operations.
Overview#
In coordinax, a manifold is represented as a pair \((M, \mathcal{A})\):
\(M\): the geometric manifold
\(\mathcal{A}\): an atlas describing compatible charts
Manifold objects are responsible for compatibility checks (which charts belong on the manifold) and for manifold-level wrappers around chart operations.
For a step-by-step walkthrough, see Working With Manifolds.
Quick Start#
import coordinax.charts as cxc
import coordinax.manifolds as cxm
import unxt as u
# Euclidean manifold in 3 dimensions.
M = cxm.EuclideanManifold(3)
# Check chart compatibility.
assert M.has_chart(cxc.cart3d)
assert not M.has_chart(cxc.cart2d)
# Chart-level point transition map.
p = {"x": u.Q(1, "km"), "y": u.Q(2, "km"), "z": u.Q(3, "km")}
p_sph = cxc.pt_map(p, cxc.cart3d, cxc.sph3d)
# Guess manifold from data/chart.
M2 = cxm.guess_manifold(p)
M3 = cxm.guess_manifold(cxc.sph2)
# Metric angle between two tangent vectors.
at = {"x": u.Q(0, "km"), "y": u.Q(0, "km"), "z": u.Q(0, "km")}
uvec = {"x": u.Q(1, "km"), "y": u.Q(0, "km"), "z": u.Q(0, "km")}
vvec = {"x": u.Q(0, "km"), "y": u.Q(1, "km"), "z": u.Q(0, "km")}
ang = cxm.angle_between(cxc.cart3d, uvec, vvec, at=at)
Functional API#
guess_manifold: infer a manifold from manifold/chart/data inputsscale_factors: return the metric diagonal in a chart at a base pointangle_between: return the metric angle between two tangent-vector CDictspt_embed: embed intrinsic coordinates into ambient coordinatespt_project: project ambient coordinates back to intrinsic chart coordinatespt_map: manifold-related re-export of point realization map
Available Objects#
Manifolds#
AbstractManifold: base manifold interfaceEuclideanManifold/R3: Euclidean manifold family and 3D convenienceHyperSphericalManifold: intrinsic two-sphere manifoldCartesianProductManifold: Cartesian product manifoldEmbeddedManifold: manifold with explicit embedding into an ambient manifoldCustomManifold: manifold backed by a caller-provided atlas
Atlases#
AbstractAtlas: base atlas interfaceEuclideanAtlas: atlas for Euclidean charts of fixed dimensionHyperSphericalAtlas: atlas for intrinsic two-sphere chartsCartesianProductAtlas: atlas for product manifoldsCustomAtlas: explicit atlas with caller-controlled chart membership
Embeddings and Embedded Charts#
AbstractEmbeddingMap: base embedding map interfaceCustomEmbeddingMap: user-defined embedding mapsTwoSphereIn3D/embedded_twosphere: standard two-sphere embedding in 3DEmbeddedChart: convenience chart wrapper combining intrinsic chart and embedding
Notes#
Manifold methods delegate chart transitions to
cxc.pt_map.For intrinsic two-sphere workflows, use
HyperSphericalManifoldand intrinsic two-sphere charts (sph2,lonlat_sph2, etc.) rather than Euclidean 2D charts.
coordinax.manifolds module.
- coordinax.manifolds.guess_manifold(*args: Any, **kwargs: Any)#
Guess the manifold from arguments.
>>> import coordinax.charts as cxc >>> import coordinax.manifolds as cxm
>>> M = cxm.EuclideanManifold(2) >>> guess_manifold(M) is M True
>>> cxm.guess_manifold({"x": 1, "y": 2, "z": 3}) Rn(3)
>>> cxm.guess_manifold(cxc.sph3d) Rn(3)
>>> cxm.guess_manifold(cxc.sph2) HyperSphericalManifold(ndim=2)
- coordinax.manifolds.guess_manifold(obj: EuclideanAtlas, /) EuclideanManifold
- Parameters:
- Return type:
Return the manifold of a Euclidean atlas.
>>> import coordinax.manifolds as cxm >>> atlas = cxm.EuclideanAtlas(3) >>> cxm.guess_manifold(atlas) Rn(3)
- coordinax.manifolds.guess_manifold(_: type[MinkowskiCT], /) MinkowskiManifold
- Parameters:
- Return type:
Infer manifold from a MinkowskiCT chart class.
>>> import coordinax.charts as cxc >>> import coordinax.manifolds as cxm >>> cxm.guess_manifold(cxc.MinkowskiCT) MinkowskiManifold(ndim=4)
- coordinax.manifolds.guess_manifold(obj: HyperSphericalAtlas, /) HyperSphericalManifold
- Parameters:
- Return type:
Return the manifold of a HyperSphericalAtlas.
>>> import coordinax.manifolds as cxm >>> atlas = cxm.HyperSphericalAtlas() >>> cxm.guess_manifold(atlas) HyperSphericalManifold(ndim=2)
- coordinax.manifolds.guess_manifold(obj: AbstractSphericalTwoSphere, /) HyperSphericalManifold
- Parameters:
- Return type:
Return a HyperSphericalManifold manifold.
>>> import coordinax.charts as cxc >>> import coordinax.manifolds as cxm
>>> cxm.guess_manifold(cxc.SphericalTwoSphere()) HyperSphericalManifold(ndim=2)
- coordinax.manifolds.guess_manifold(obj: AbstractManifold, /) AbstractManifold
- Parameters:
- Return type:
Return the manifold of a manifold.
>>> import coordinax.manifolds as cxm >>> M = cxm.Rn(3) >>> cxm.guess_manifold(M) is M True
- coordinax.manifolds.guess_manifold(_: type[AbstractChart], /) AbstractManifold
- Parameters:
- Return type:
Infer manifold from a chart class.
>>> import coordinax.charts as cxc >>> import coordinax.manifolds as cxm >>> cxm.guess_manifold(cxc.Cart3D) Rn(3)
- coordinax.manifolds.guess_manifold(chart: AbstractChart, /) AbstractManifold
- Parameters:
- Return type:
Infer manifold from a chart class.
>>> import coordinax.charts as cxc >>> import coordinax.manifolds as cxm >>> cxm.guess_manifold(cxc.Cart3D) Rn(3)
- coordinax.manifolds.guess_manifold(_: type[Cart0D], /) EuclideanManifold
- Parameters:
- Return type:
Infer manifold from a chart class.
>>> import coordinax.charts as cxc >>> import coordinax.manifolds as cxm >>> cxm.guess_manifold(cxc.Cart0D) Rn(0)
- coordinax.manifolds.guess_manifold(_: type[Cart1D | Radial1D], /) EuclideanManifold
- Parameters:
- Return type:
Infer manifold from a chart class.
>>> import coordinax.charts as cxc >>> import coordinax.manifolds as cxm >>> cxm.guess_manifold(cxc.Cart1D) Rn(1)
- coordinax.manifolds.guess_manifold(_: type[Cart2D | Polar2D], /) EuclideanManifold
- Parameters:
- Return type:
Infer manifold from a chart class.
>>> import coordinax.charts as cxc >>> import coordinax.manifolds as cxm >>> cxm.guess_manifold(cxc.Cart2D) Rn(2)
- coordinax.manifolds.guess_manifold(_: type[Cart3D | Cylindrical3D | AbstractSpherical3D | ProlateSpheroidal3D], /) EuclideanManifold
- Parameters:
- Return type:
Infer manifold from a chart class.
>>> import coordinax.charts as cxc >>> import coordinax.manifolds as cxm >>> cxm.guess_manifold(cxc.Cart3D) Rn(3)
- coordinax.manifolds.guess_manifold(obj: dict[str, Any], /) AbstractManifold
- Parameters:
- Return type:
Infer manifold from a mapping.
Redispatches based on the inferred chart.
>>> import coordinax.manifolds as cxm >>> cxm.guess_manifold({"x": 1, "y": 2, "z": 3}) Rn(3)
- Parameters:
- Return type:
- coordinax.manifolds.pt_embed(p_pos: dict[str, Any], embedded: object, /, *, usys: AbstractUnitSystem | None = None)#
Embed intrinsic point coordinates into ambient coordinates.
This function maps point coordinates from an intrinsic chart chart on an embedded manifold to the corresponding ambient space coordinates. It is the fundamental operation for working with embedded manifolds such as spheres, cylinders, or other submanifolds of Euclidean space.
Mathematical Definition:
Given an embedding $iota: M to mathbb{R}^n$ of a manifold $M$ into ambient space $mathbb{R}^n$, and intrinsic coordinates $q = (q^1, ldots, q^k)$ on a chart $U subset M$, this function computes the ambient coordinates:
$$ x = iota(q) = (x^1(q), ldots, x^n(q)) $$
For example, embedding the 2-sphere $S^2$ into $mathbb{R}^3$ using spherical coordinates $(theta, phi)$:
$$ x &= R sintheta cosphi \ y &= R sintheta sinphi \ z &= R costheta $$
where $R$ is the radius parameter stored in the embedding object.
- Parameters:
embedded (
object) –The embedded manifold chart, typically an
coordinax.manifolds.EmbeddedChartinstance. This encapsulates:intrinsic: The intrinsic chart (e.g.,SphericalTwoSphere)embedding: AnAbstractEmbeddingthat owns the ambient chart and any parameters (e.g.,TwoSphereIn3Dwithradius)
p_pos (
dict[str,Any]) – Dictionary of intrinsic position coordinates. Keys must matchembedded.components(e.g.,"theta"and"phi"forSphericalTwoSphere). Values must have appropriate dimensions (e.g., angles for angular coordinates).usys (
AbstractUnitSystem|None) – Unit system for the transformation. This is sometimes required for transformations that depend on physical constants (e.g., speed of light orDeltain {class}`~coordinax.charts.ProlateSpheroidal3D`) but p is raw values without units.
- Returns:
Dictionary of ambient position coordinates. Keys match
embedded.ambient.components(e.g.,"x","y","z"forCart3D). Values have dimensions appropriate for the ambient space (e.g., length for Cartesian coordinates).- Return type:
- Raises:
NotImplementedError – If no embedding rule is registered for the specific combination of intrinsic chart and embedding type.
ValueError – If required parameters are missing from the embedding or have incorrect dimensions.
Notes
This is a point-only transformation. It does not handle velocities or other time derivatives. Use
embed_tangentfor differential quantities.Embedding parameters (like
radiusforTwoSphereIn3D) are stored on the embedding object and must have appropriate physical dimensions.The embedding is purely geometric and does not encode physical components or metric information. Physical components require additional metric data.
Singularities of the intrinsic chart (e.g., poles on the sphere at $theta = 0, pi$) are inherited by the embedding but may not be problematic in the ambient space.
See also
pt_projectInverse operation projecting ambient to intrinsic coordinates
embed_tangentEmbedding for tangent vector components
coordinax.manifolds.EmbeddedChartContainer for embedded manifold charts
Examples
Embedding a point on the 2-sphere into 3D Cartesian coordinates:
>>> import quaxed.numpy as jnp >>> import coordinax.charts as cxc >>> import coordinax.manifolds as cxm >>> import unxt as u >>> import wadler_lindig as wl
Create an embedded manifold for a sphere of radius 5 km:
>>> chart = cxm.EmbeddedChart(cxm.TwoSphereIn3D(radius=u.Q(5, "km")))
Embed a point at the equator:
>>> p_intrinsic = { ... "theta": u.Angle(jnp.pi / 2, "rad"), # equator ... "phi": u.Angle(0.0, "rad"), # along x-axis ... } >>> p_ambient = cxm.pt_embed(p_intrinsic, chart) >>> wl.pprint(p_ambient, short_arrays='compact', named_unit=False) {'r': Quantity(5, 'km'), 'theta': Angle(1.57079633, 'rad'), 'phi': Angle(0., 'rad')}
Verify the point lies on the sphere:
>>> p_ambient_cart = cxm.pt_map(p_ambient, chart.ambient, cxc.cart3d) >>> r2 = jnp.linalg.norm(jnp.array(list(p_ambient_cart.values()))) >>> jnp.allclose(r2, u.Q(25.0, "km"), atol=u.Q(1e-10, "km")) Array(False, dtype=bool)
Embedding a point at the north pole:
>>> p_pole = { ... "theta": u.Angle(0.0, "rad"), # north pole ... "phi": u.Angle(0.0, "rad"), # phi is arbitrary at poles ... } >>> p_ambient_pole = cxm.pt_embed(p_pole, chart) >>> wl.pprint(p_ambient_pole, short_arrays='compact', named_unit=False) {'r': Quantity(5, 'km'), 'theta': Angle(0., 'rad'), 'phi': Angle(0., 'rad')}
- coordinax.manifolds.pt_embed(p_intrinsic: dict[str, Any], M: EmbeddedManifold, /, *, usys: AbstractUnitSystem | None = None) dict[str, Any]
Embed intrinsic point coordinates into ambient coordinates (manifold).
- coordinax.manifolds.pt_embed(p_intrinsic: dict[str, Any], from_intrinsic_chart: AbstractChart, to_ambient_chart: AbstractChart, M: EmbeddedManifold, /, *, usys: AbstractUnitSystem | None = None) dict[str, Any]
Embed intrinsic point coordinates into ambient coordinates (manifold).
- coordinax.manifolds.pt_embed(p_intrinsic: dict[str, Any], from_intrinsic_chart: AbstractChart, to_ambient_chart: AbstractChart, embed_map: AbstractEmbeddingMap, /, *, usys: AbstractUnitSystem | None = None) dict[str, Any]
Embed intrinsic point coordinates into ambient coordinates.
- coordinax.manifolds.pt_embed(p_intrinsic: dict[str, Any], embedding: EmbeddedChart, /, *, usys: AbstractUnitSystem | None = None) dict[str, Any]
Embed intrinsic point coordinates into ambient coordinates.
>>> import coordinax.manifolds as cxm >>> import unxt as u >>> chart = cxm.EmbeddedChart(cxm.TwoSphereIn3D(radius=u.Q(2.0, "km"))) >>> p_intrinsic = {"theta": u.Q(45, "deg"), "phi": u.Q(30, "deg")} >>> cxm.pt_embed(p_intrinsic, chart) {'r': Q(2., 'km'), 'theta': Q(45, 'deg'), 'phi': Q(30, 'deg')}
- coordinax.manifolds.pt_project(*args: object, usys: AbstractUnitSystem | None = None)#
Project ambient space coordinates onto intrinsic chart coordinates.
This function performs the inverse operation of
pt_embed, taking coordinates from the ambient (embedding) space and projecting them back onto the intrinsic manifold chart. It’s essential for converting between extrinsic and intrinsic charts of points on embedded submanifolds.Mathematical Definition:
For an embedding $iota: M to mathbb{R}^n$ of a $k$-dimensional manifold $M$ into $n$-dimensional Euclidean space, the projection $pi: mathbb{R}^n to M$ (or more precisely, onto a chart $U subset M$) satisfies:
$$ pi circ iota = mathrm{id}_M $$
meaning that projecting after embedding returns the original point (up to numerical precision and chart domain).
For a 2-sphere $S^2 subset mathbb{R}^3$ with radius $R$, given Cartesian coordinates $(x, y, z)$:
- $$
r &= sqrt{x^2 + y^2 + z^2} \ theta &= arccos!left(frac{z}{r}right) in [0, pi] \ phi &= operatorname{atan2}(y, x) in (-pi, pi]
$$
The projection normalizes the input by $r$, so it maps any point in $mathbb{R}^3 setminus {0}$ to the sphere.
Key properties:
Local inverse: On the manifold, $pi(iota(q)) = q$ exactly
Normalization: Points near the manifold are projected onto it (e.g., points near the sphere are normalized to lie exactly on it)
Singularities: Projection may have singularities where the manifold’s chart has coordinate singularities (e.g., poles of a sphere)
Not globally defined: Projection is typically only defined for points in a neighborhood of the manifold, not all of $mathbb{R}^n$
- Parameters:
*args (
object) –Typically an
EmbeddedChartchart and a dictionary of ambient coordinates, but the function supports multiple dispatch patterns. Common signature:embeddedEmbeddedChartThe embedded manifold chart specifying the chart and ambient space
p_ambientCDictAmbient coordinates keyed by
embedded.ambient.components(e.g.,{"x": ..., "y": ..., "z": ...}for Cartesian ambient space)
usys (
AbstractUnitSystem|None) – Unit system for the transformation. This is sometimes required for transformations that depend on physical constants (e.g., speed of light orDeltain {class}`~coordinax.charts.ProlateSpheroidal3D`) but p is raw values without units.
- Returns:
Intrinsic chart coordinates keyed by the manifold chart’s components. For a sphere, these are typically
{"theta": ..., "phi": ...}.- Return type:
- Raises:
NotImplementedError – If no projection is defined for the given manifold and ambient space.
ValueError – If the ambient point cannot be projected (e.g., origin for sphere).
Notes
Normalization: Implementations often normalize inputs to handle points that are close to but not exactly on the manifold. For a sphere, any non-zero point in $mathbb{R}^3$ is normalized to radius $R$.
Coordinate singularities: At chart singularities (e.g., sphere poles where $sintheta = 0$), convention determines the value of singular coordinates (typically $phi = 0$ at poles).
Round-trip accuracy: For points on the manifold,
pt_project(pt_embed(q, embedded), embedded)should returnqup to floating-point precision.Extension to nearby points: Projection extends the manifold chart to a neighborhood in the ambient space, useful for perturbed or approximate data.
Orthogonal projection: For Riemannian manifolds with the induced metric, this is often the orthogonal projection onto the manifold (shortest distance from the ambient point to the manifold).
See also
pt_embedEmbed intrinsic chart coordinates into ambient space
project_tangentProject ambient velocity/acceleration onto tangent space
Examples
>>> import jax >>> import jax.numpy as jnp >>> import coordinax.charts as cxc >>> import coordinax.manifolds as cxm >>> import unxt as u >>> import wadler_lindig as wl
Project Cartesian coordinates onto a 2-sphere. Create an embedded 2-sphere of radius 1 km:
>>> sphere = cxm.EmbeddedChart(cxm.TwoSphereIn3D(radius=u.Q(1, "km")))
Project a point in 3D Cartesian space onto the sphere. Start with a point slightly off the sphere:
>>> p_cart = {"x": u.Q(0.5, "km"), "y": u.Q(0.5, "km"), "z": u.Q(0.707, "km")} >>> p_sphere = cxm.pt_project(p_cart, cxc.cart3d, sphere) >>> wl.pprint(p_sphere, short_arrays='compact', named_unit=False) {'theta': Quantity(0.78547367, 'rad'), 'phi': Quantity(0.78539816, 'rad')}
The projection normalizes the point to lie exactly on the sphere. Verify round-trip accuracy (project after embed returns original point):
>>> q_sphere = {"theta": u.Q(jnp.pi / 3, "rad"), ... "phi": u.Q(jnp.pi / 4, "rad")} >>> q_cart = cxm.pt_embed(q_sphere, sphere) >>> q_recovered = cxm.pt_project(q_cart, sphere) >>> all(jax.tree.map(jnp.isclose, q_sphere, q_recovered)) True
Project from an arbitrary point in space (not on sphere). The projection normalizes the radius:
>>> p_far = {"x": u.Q(2.0,"km"), "y": u.Q(2.0,"km"), "z": u.Q(2.0,"km")} >>> p_normalized = cxm.pt_project(p_far, cxc.cart3d, sphere) >>> # Direction is preserved: all coordinates equal → theta ≈ 54.7°, phi = 45° >>> wl.pprint(p_normalized, short_arrays='compact', named_unit=False) {'theta': Quantity(0.95531662, 'rad'), 'phi': Quantity(0.78539816, 'rad')}
Handle coordinate singularities at the poles. At the north pole ($theta = 0$), $phi$ is conventionally set to 0:
>>> p_north = {"x": u.Q(0.0, "km"), "y": u.Q(0.0, "km"), ... "z": u.Q(1.0, "km")} # North pole >>> cxm.pt_project(p_north, cxc.cart3d, sphere) {'theta': Q(0., 'rad'), 'phi': Q(0., 'rad')}
At the south pole ($theta = pi$), $phi$ is also set to 0:
>>> p_south = { "x": u.Q(0, "km"), "y": u.Q(0, "km"), "z": u.Q(-1, "km")} >>> cxm.pt_project(p_south, cxc.cart3d, sphere) {'theta': Q(3.14159265, 'rad'), 'phi': Q(0., 'rad')}
- coordinax.manifolds.pt_project(p_ambient: dict[str, Any], M: EmbeddedManifold, /, *, usys: AbstractUnitSystem | None = None) dict[str, Any]
- Parameters:
args (object)
usys (AbstractUnitSystem | None)
- Return type:
Project ambient coordinates onto intrinsic chart coordinates (manifold).
- coordinax.manifolds.pt_project(p_ambient: dict[str, Any], from_ambient_chart: AbstractChart, to_intrinsic_chart: AbstractChart, M: EmbeddedManifold, /, *, usys: AbstractUnitSystem | None = None) dict[str, Any]
- Parameters:
args (object)
usys (AbstractUnitSystem | None)
- Return type:
Project ambient coordinates onto intrinsic chart coordinates (manifold).
- coordinax.manifolds.pt_project(p_ambient: dict[str, Any], from_ambient_chart: AbstractChart, to_intrinsic_chart: AbstractChart, embed_map: AbstractEmbeddingMap, /, *, usys: AbstractUnitSystem | None = None) dict[str, Any]
- Parameters:
args (object)
usys (AbstractUnitSystem | None)
- Return type:
Project ambient coordinates onto intrinsic chart coordinates.
- coordinax.manifolds.pt_project(p_ambient: dict[str, Any], embedding: EmbeddedChart, /, *, usys: AbstractUnitSystem | None = None) dict[str, Any]
- Parameters:
args (object)
usys (AbstractUnitSystem | None)
- Return type:
Project ambient coordinates onto intrinsic chart coordinates.
>>> import coordinax.manifolds as cxm >>> import unxt as u >>> chart = cxm.EmbeddedChart(cxm.TwoSphereIn3D(radius=u.Q(2.0, "km"))) >>> p_amb = {"r": u.Q(2.0, "km"), "theta": u.Q(45, "deg"), "phi": u.Q(30, "deg")} >>> cxm.pt_project(p_amb, chart) {'theta': Q(45, 'deg'), 'phi': Q(30, 'deg')}
- coordinax.manifolds.pt_project(p_ambient: dict[str, Any], from_chart: AbstractChart, embedding: EmbeddedChart, /, *, usys: AbstractUnitSystem | None = None) dict[str, Any]
- Parameters:
args (object)
usys (AbstractUnitSystem | None)
- Return type:
Project ambient coordinates onto intrinsic chart coordinates.
>>> import coordinax.manifolds as cxm >>> import unxt as u >>> chart = cxm.EmbeddedChart(cxm.TwoSphereIn3D(radius=u.Q(2.0, "km"))) >>> p_amb = {"r": u.Q(2.0, "km"), "theta": u.Q(45, "deg"), "phi": u.Q(30, "deg")} >>> cxm.pt_project(p_amb, cxc.sph3d, chart) {'theta': Q(45, 'deg'), 'phi': Q(30, 'deg')}
- coordinax.manifolds.pt_project(p_ambient: object, from_ambient_chart: AbstractChart, M: HyperSphericalManifold, /, *, usys: AbstractUnitSystem | None = None) object
- Parameters:
args (object)
usys (AbstractUnitSystem | None)
- Return type:
Project a point from the 3D chart to the two-sphere intrinsic chart.
This projection 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 jax.numpy as jnp >>> import unxt as u >>> import coordinax.charts as cxc >>> import coordinax.manifolds as cxm
>>> q = {"x": u.Q(1.0, "km"), "y": u.Q(0.0, "km"), "z": u.Q(0.0, "km")} >>> cxm.pt_project(q, cxc.cart3d, cxm.S2) {'theta': Q(1.57079633, 'rad'), 'phi': Q(0., 'rad')}
- coordinax.manifolds.pt_map(*args: Any, **kwargs: Any)#
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 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 pt_map represents the coordinate expression
$$ psi_{mathrm{to}} circ F circ varphi_{mathrm{from}}^{-1}. $$
3D Spherical → Cartesian:
- $$
x &= r sintheta cosphi \ y &= r sintheta sinphi \ z &= r costheta
$$
- 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': Q(0.92729522, 'rad'), 'z': Q(5., 'm')}
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': Q(1.57079633, 'rad'), 'phi': Q(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) QMatrix([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.manifolds.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': Q(1.57079633, 'rad'), 'phi': Q(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) QMatrix([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.manifolds.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.manifolds.pt_map(p: dict[str, Any], from_M: AbstractManifold, from_chart: AbstractChart, to_M: AbstractManifold, to_chart: AbstractChart, /, *, usys: AbstractUnitSystem | None = None) dict[str, Any]
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.manifolds.pt_map(p: dict[str, Any], from_M: EuclideanManifold, from_chart: AbstractChart, to_M: EuclideanManifold, to_chart: AbstractChart, /, *, usys: AbstractUnitSystem | None = None) dict[str, Any]
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.manifolds.pt_map(p: dict[str, Any], from_M: EuclideanManifold, from_chart: Radial1D, to_M: EuclideanManifold, to_chart: Cart1D, /, *, usys: AbstractUnitSystem | None = None) dict[str, Any]
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.manifolds.pt_map(p: dict[str, Any], from_M: EuclideanManifold, from_chart: Cart1D, to_M: EuclideanManifold, to_chart: Radial1D, /, *, usys: AbstractUnitSystem | None = None) dict[str, Any]
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.manifolds.pt_map(p: dict[str, Any], from_M: EuclideanManifold, from_chart: Polar2D, to_M: EuclideanManifold, to_chart: Cart2D, /, *, usys: AbstractUnitSystem | None = None) dict[str, Any]
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.manifolds.pt_map(p: dict[str, Any], from_M: EuclideanManifold, from_chart: Cart2D, to_M: EuclideanManifold, to_chart: Polar2D, /, *, usys: AbstractUnitSystem | None = None) dict[str, Any]
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': Q(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.manifolds.pt_map(p: dict[str, Any], from_M: EuclideanManifold, from_chart: Cylindrical3D, to_M: EuclideanManifold, to_chart: Cart3D, /, *, usys: AbstractUnitSystem | None = None) dict[str, Any]
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.manifolds.pt_map(p: dict[str, Any], from_M: EuclideanManifold, from_chart: Spherical3D, to_M: EuclideanManifold, to_chart: Cart3D, /, *, usys: AbstractUnitSystem | None = None) dict[str, Any]
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.manifolds.pt_map(p: dict[str, Any], from_M: EuclideanManifold, from_chart: LonLatSpherical3D, to_M: EuclideanManifold, to_chart: Cart3D, /, *, usys: AbstractUnitSystem | None = None) dict[str, Any]
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.manifolds.pt_map(p: dict[str, Any], from_M: EuclideanManifold, from_chart: LonCosLatSpherical3D, to_M: EuclideanManifold, to_chart: Cart3D, /, *, usys: AbstractUnitSystem | None = None) dict[str, Any]
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.manifolds.pt_map(p: dict[str, Any], from_M: EuclideanManifold, from_chart: MathSpherical3D, to_M: EuclideanManifold, to_chart: Cart3D, /, *, usys: AbstractUnitSystem | None = None) dict[str, Any]
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.manifolds.pt_map(p: dict[str, Any], from_M: EuclideanManifold, from_chart: ProlateSpheroidal3D, to_M: EuclideanManifold, to_chart: Cart3D, /, *, usys: AbstractUnitSystem | None = None) dict[str, Any]
ProlateSpheroidal3D -> Cart3D.
We calculate through cylindrical coordinates first:
$rho = sqrt{(mu-Delta^2)left(1-frac{|\nu|}{Delta^2}right)}$ $z = sqrt{mu,frac{|\nu|}{Delta^2}};mathrm{sign}(nu)$ $phi = phi.$
Then convert to Cartesian:
$x=rhocosphi$, $y=rhosinphi$, $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.manifolds.pt_map(p: dict[str, Any], from_M: EuclideanManifold, from_chart: Cart3D, to_M: EuclideanManifold, to_chart: Cylindrical3D, /, *, usys: AbstractUnitSystem | None = None) dict[str, Any]
Cart3D -> Cylindrical3D.
>>> import coordinax.main 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': Q(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.manifolds.pt_map(p: dict[str, Any], from_M: EuclideanManifold, from_chart: Cart3D | Cylindrical3D, to_M: EuclideanManifold, to_chart: AbstractSpherical3D, /, *, usys: AbstractUnitSystem | None = None) dict[str, Any]
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': Q(0., 'rad'), 'lat': Q(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(1.57079633, dtype=float64), 'distance': Array(1., dtype=float64, weak_type=True)}
- coordinax.manifolds.pt_map(p: dict[str, Any], from_M: EuclideanManifold, from_chart: Cart3D, to_M: EuclideanManifold, to_chart: Spherical3D, /, *, usys: AbstractUnitSystem | None = None) dict[str, Any]
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': Q(0., 'rad'), 'phi': Q(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.manifolds.pt_map(p: dict[str, Any], from_M: EuclideanManifold, from_chart: Cylindrical3D, to_M: EuclideanManifold, to_chart: Spherical3D, /, *, usys: AbstractUnitSystem | None = None) dict[str, Any]
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': Q(0., 'rad'), 'phi': Q(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.manifolds.pt_map(p: dict[str, Any], from_M: EuclideanManifold, from_chart: Spherical3D, to_M: EuclideanManifold, to_chart: Cylindrical3D, /, *, usys: AbstractUnitSystem | None = None) dict[str, Any]
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': Q(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.manifolds.pt_map(p: dict[str, Any], from_M: EuclideanManifold, from_chart: Spherical3D, to_M: EuclideanManifold, to_chart: LonLatSpherical3D, /, *, usys: AbstractUnitSystem | None = None) dict[str, Any]
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': Q(0, 'rad'), 'lat': Q(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.manifolds.pt_map(p: dict[str, Any], from_M: EuclideanManifold, from_chart: Spherical3D, to_M: EuclideanManifold, to_chart: LonCosLatSpherical3D, /, *, usys: AbstractUnitSystem | None = None) dict[str, Any]
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': Q(45., 'deg'), 'lat': Q(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': 1.5707963267948966, 'distance': 1.0}
- coordinax.manifolds.pt_map(p: dict[str, Any], from_M: EuclideanManifold, from_chart: Spherical3D, to_M: EuclideanManifold, to_chart: MathSpherical3D, /, *, usys: AbstractUnitSystem | None = None) dict[str, Any]
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': Q(60, 'deg'), 'phi': Q(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.manifolds.pt_map(p: dict[str, Any], from_M: EuclideanManifold, from_chart: MathSpherical3D, to_M: EuclideanManifold, to_chart: Spherical3D, /, *, usys: AbstractUnitSystem | None = None) dict[str, Any]
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': Q(30, 'deg'), 'phi': Q(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.manifolds.pt_map(p: dict[str, Any], from_M: EuclideanManifold, from_chart: ProlateSpheroidal3D, to_M: EuclideanManifold, to_chart: Cylindrical3D, /, *, usys: AbstractUnitSystem | None = None) dict[str, Any]
ProlateSpheroidal3D -> Cylindrical3D.
Uses the focal length $Delta$ stored on
from_chart.Validity constraints (enforced by the representation) are:
$Delta > 0$,
$mu ge Delta^2$,
$|nu| le Delta^2$.
The conversion proceeds via
$rho = sqrt{(mu-Delta^2)left(1-frac{|\nu|}{Delta^2}right)}$, $z = sqrt{mu,frac{|\nu|}{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': Q(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.manifolds.pt_map(p: dict[str, Any], from_M: EuclideanManifold, from_chart: Cylindrical3D, to_M: EuclideanManifold, to_chart: ProlateSpheroidal3D, /, *, usys: AbstractUnitSystem | None = None) dict[str, Any]
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), $|nu| = dfrac{2Delta^2}{S + D},z^2$, and $nu = |\nu|,mathrm{sign}(z)$, with a stability fix when $Delta^2 - |nu|$ 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': Q(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': Q(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.manifolds.pt_map(p: dict[str, Any], from_M: EuclideanManifold, from_chart: ProlateSpheroidal3D, to_M: EuclideanManifold, to_chart: ProlateSpheroidal3D, /, *, usys: AbstractUnitSystem | None = None) dict[str, Any]
{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': Q(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': Q(0., 'rad')}
- coordinax.manifolds.pt_map(p: dict[str, Any], from_M: EuclideanManifold, from_chart: CartND, to_M: EuclideanManifold, to_chart: AbstractChart, /, *, usys: AbstractUnitSystem | None = None) dict[str, Any]
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': Q(1.57079633, 'rad'), 'phi': Q(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': Q(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.manifolds.pt_map(p: dict[str, Any], from_M: EuclideanManifold, from_chart: AbstractChart, to_M: EuclideanManifold, to_chart: CartND, /, *, usys: AbstractUnitSystem | None = None) dict[str, Any]
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.manifolds.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.manifolds.pt_map(p: AbstractQuantity, from_M: EuclideanManifold, from_chart: AbstractChart, to_M: EuclideanManifold, to_chart: AbstractChart, /, *, usys: AbstractUnitSystem | None = None) QMatrix
Transform a QMatrix between charts.
Converts the components of a QMatrix 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.manifolds.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.- Return type:
- Returns:
Array – Array of shape
(..., ndim)containing the transformed coordinates into_chart.>>> import coordinax.charts as cxc
>>> import unxt as u
>>> import jax.numpy as jnp
>>> 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).
>>> 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)
>>> 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].
>>> 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)
>>> 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)
.. py (function:: pt_map(p: dict[str, typing.Any], from_M: coordinax._src.product.manifold.CartesianProductManifold, from_chart: coordinax._src.product.chart.AbstractCartesianProductChart, to_M: coordinax._src.product.manifold.CartesianProductManifold, to_chart: coordinax._src.product.chart.AbstractCartesianProductChart, /, *, usys: unxt._src.unitsystems.base.AbstractUnitSystem | None = None) -> dict[str, typing.Any]) – :noindex:
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,;pright) – = 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$.
- Parameters:
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.manifolds.pt_map(p: dict[str, Any], from_M: AbstractManifold, from_chart: AbstractChart, to_M: CartesianProductManifold, to_chart: AbstractCartesianProductChart, /, *, usys: AbstractUnitSystem | None = None) dict[str, Any]
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.manifolds.pt_map(p: dict[str, Any], from_M: CartesianProductManifold, from_chart: AbstractCartesianProductChart, to_M: AbstractManifold, to_chart: AbstractChart, /, *, usys: AbstractUnitSystem | None = None) dict[str, Any]
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.
- coordinax.manifolds.pt_map(p: dict[str, Any], from_chart: EmbeddedChart, to_chart: EmbeddedChart, /, *, usys: AbstractUnitSystem | None = None) dict[str, Any]
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': Q(45, 'deg'), 'phi': Q(0, 'deg')}
The angular coordinates are preserved (both spheres share the same angular parameterization via projection through the shared ambient space).
- coordinax.manifolds.pt_map(p: dict[str, Any], from_chart: AbstractChart, to_chart: EmbeddedChart, /, *, usys: AbstractUnitSystem | None = None) dict[str, Any]
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': Q(1.57079633, 'rad'), 'phi': Q(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.manifolds.pt_map(p: dict[str, Any], from_chart: EmbeddedChart, to_chart: AbstractChart, /, *, usys: AbstractUnitSystem | None = None) dict[str, Any]
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': Q(1., 'rad'), 'phi': Q(0.5, 'rad')}
- coordinax.manifolds.pt_map(p: dict[str, Any], M: EmbeddedManifold, from_chart: AbstractChart, to_chart: AbstractChart, /, *, usys: AbstractUnitSystem | None = None) dict[str, Any]
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'))) >>> 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': Q(0.66164791, 'rad'), 'lat': Q(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.manifolds.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': Q(1.57079633, 'rad'), 'phi': Q(0., 'rad')}
- coordinax.manifolds.pt_map(p: dict[str, Any], from_M: HyperSphericalManifold, from_chart: AbstractChart, to_M: HyperSphericalManifold, to_chart: AbstractChart, /, *, usys: AbstractUnitSystem | None = None) dict[str, Any]
Identity conversion for matching charts.
>>> import coordinax.manifolds as cxm >>> import coordinax.charts as cxc >>> import unxt as u
>>> q = {"theta": u.Q(30, "deg"), "phi": u.Q(60, "deg")} >>> cxc.pt_map(q, cxc.sph2, cxc.sph2) is q True
>>> q = {"lon": u.Q(45, "deg"), "lat": u.Q(10, "deg")} >>> cxc.pt_map(q, cxc.lonlat_sph2, cxc.lonlat_sph2) is q True
>>> q = {"lon_coslat": u.Q(30, "deg"), "lat": u.Q(20, "deg")} >>> cxc.pt_map(q, cxc.loncoslat_sph2, cxc.loncoslat_sph2) is q True
>>> q = {"theta": u.Q(60, "deg"), "phi": u.Q(30, "deg")} >>> cxc.pt_map(q, cxc.math_sph2, cxc.math_sph2) is q True
- coordinax.manifolds.pt_map(p: dict[str, Any], from_M: HyperSphericalManifold, from_chart: SphericalTwoSphere, to_M: HyperSphericalManifold, to_chart: LonLatSphericalTwoSphere, /, *, usys: AbstractUnitSystem | None = None) dict[str, Any]
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': Q(0, 'rad'), 'lat': Q(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': Q(45, 'deg'), 'lat': Q(0, 'deg')}
- coordinax.manifolds.pt_map(p: dict[str, Any], from_M: HyperSphericalManifold, from_chart: LonLatSphericalTwoSphere, to_M: HyperSphericalManifold, to_chart: SphericalTwoSphere, /, *, usys: AbstractUnitSystem | None = None) dict[str, Any]
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': Q(90, 'deg'), 'phi': Q(45, 'deg')}
- coordinax.manifolds.pt_map(p: dict[str, Any], from_M: HyperSphericalManifold, from_chart: SphericalTwoSphere, to_M: HyperSphericalManifold, to_chart: LonCosLatSphericalTwoSphere, /, *, usys: AbstractUnitSystem | None = None) dict[str, Any]
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': Q(45., 'deg'), 'lat': Q(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.manifolds.pt_map(p: dict[str, Any], from_M: HyperSphericalManifold, from_chart: LonCosLatSphericalTwoSphere, to_M: HyperSphericalManifold, to_chart: SphericalTwoSphere, /, *, usys: AbstractUnitSystem | None = None) dict[str, Any]
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': Q(90., 'deg'), 'phi': Q(45., 'deg')}
- coordinax.manifolds.pt_map(p: dict[str, Any], from_M: HyperSphericalManifold, from_chart: SphericalTwoSphere, to_M: HyperSphericalManifold, to_chart: MathSphericalTwoSphere, /, *, usys: AbstractUnitSystem | None = None) dict[str, Any]
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': Q(60, 'deg'), 'phi': Q(30, 'deg')}
- coordinax.manifolds.pt_map(p: dict[str, Any], from_M: HyperSphericalManifold, from_chart: MathSphericalTwoSphere, to_M: HyperSphericalManifold, to_chart: SphericalTwoSphere, /, *, usys: AbstractUnitSystem | None = None) dict[str, Any]
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': Q(30, 'deg'), 'phi': Q(60, 'deg')}
- coordinax.manifolds.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': Q(1.57079633, 'rad'), 'phi': Q(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': Q(0, 'deg'), 'lat': Q(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.manifolds.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.manifolds.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.manifolds.pt_map(from_vec: coordinax.vectors._src.point.Point, to_chart: AbstractChart, /, *, usys: AbstractUnitSystem | None = None) coordinax.vectors._src.point.Point
Convert a point from one chart to another.
>>> import unxt as u >>> import coordinax.main 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.manifolds.pt_map(from_vec: coordinax.vectors._src.point.Point, from_chart: AbstractChart, to_chart: AbstractChart, /, *, usys: AbstractUnitSystem | None = None) coordinax.vectors._src.point.Point
Convert a vector from one chart to another.
>>> import unxt as u >>> import coordinax.main 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.manifolds.scale_factors(chart: Any, /, *args: Any, **kwargs: Any)#
Return the diagonal entries of the metric matrix.
Dispatches on the first argument (metric or manifold) and the chart.
- coordinax.manifolds.scale_factors(metric: FlatMetric, chart: AbstractChart, /, *, at: dict[str, Any], usys: AbstractUnitSystem | None = None) QMatrix
Compute only the Euclidean metric diagonal instead of forming
J.T @ J.>>> import jax.numpy as jnp >>> import unxt as u >>> import coordinax.charts as cxc >>> import coordinax.manifolds as cxm
>>> metric = cxm.FlatMetric(3) >>> at = { ... "r": u.Q(jnp.array(2.0), "km"), ... "theta": u.Angle(jnp.pi / 2, "rad"), ... "phi": u.Angle(jnp.array(0.0), "rad"), ... } >>> cxm.scale_factors(metric, cxc.sph3d, at=at) QMatrix([1., 4., 4.], '(, km2 / rad2, km2 / rad2)')
- coordinax.manifolds.scale_factors(metric: MinkowskiMetric, chart: MinkowskiCT, /, *, at: dict[str, Any], usys: AbstractUnitSystem | None = None) QMatrix
Return the Minkowski metric diagonal $eta = operatorname{diag}(-1,1,1,1)$.
In the canonical coordinax.charts.MinkowskiCT chart the metric is constant, so
atis ignored and the result does not depend on the base point.>>> import jax.numpy as jnp >>> import coordinax.charts as cxc >>> import coordinax.manifolds as cxm
>>> metric = cxm.MinkowskiMetric() >>> at = {"ct": jnp.array(0.0), "x": jnp.array(1.0), ... "y": jnp.array(0.0), "z": jnp.array(0.0)} >>> cxm.scale_factors(metric, cxc.minkowskict, at=at) QMatrix([-1., 1., 1., 1.], '(, , , )')
- coordinax.manifolds.scale_factors(metric: RoundMetric, chart: AbstractChart, /, *, at: dict[str, Any], usys: AbstractUnitSystem | None = None) QMatrix
Return round-metric diagonal directly without forming the nxn matrix.
Computes the cumulative-sine diagonal $g_{kk} = prod_{j<k} sin^2theta_j$ as a 1-D vector, avoiding the O(n^2) cost of
metric_matrix().>>> import jax.numpy as jnp >>> import unxt as u >>> import coordinax.charts as cxc >>> import coordinax.manifolds as cxm
Bare angles (no units) → dimensionless QMatrix:
>>> metric = cxm.RoundMetric(2) >>> at = {"theta": jnp.array(jnp.pi / 2), "phi": jnp.array(0.0)} >>> cxm.scale_factors(metric, cxc.sph2, at=at) QMatrix([1., 1.], '(, )')
Quantity angles → dimensionless QMatrix:
>>> at = {"theta": u.Angle(jnp.pi / 2, "rad"), "phi": u.Angle(0.0, "rad")} >>> cxm.scale_factors(metric, cxc.sph2, at=at) QMatrix([1., 1.], '(, )')
- coordinax.manifolds.scale_factors(chart: AbstractChart, /, *, at: dict[str, Any], usys: AbstractUnitSystem | None = None) QMatrix
Manifold-level dispatch: delegate to the attached metric.
>>> import jax.numpy as jnp >>> import unxt as u >>> import coordinax.charts as cxc >>> import coordinax.manifolds as cxm
>>> at = { ... "r": u.Q(jnp.array(2.0), "km"), ... "theta": u.Angle(jnp.pi / 2, "rad"), ... "phi": u.Angle(jnp.array(0.0), "rad"), ... } >>> cxm.scale_factors(cxc.sph3d, at=at) QMatrix([1., 4., 4.], '(, km2 / rad2, km2 / rad2)')
- coordinax.manifolds.scale_factors(metric: AbstractMetricField, chart: AbstractChart, /, *, at: dict[str, Any], usys: AbstractUnitSystem | None = None) QMatrix
Return the diagonal entries of the metric at
atinchart.Uses the
metric_matrixdispatch API to compute the metric, then extracts the diagonal entries.>>> import jax.numpy as jnp >>> import coordinax.charts as cxc >>> import coordinax.manifolds as cxm
>>> metric = cxm.RoundMetric(2) >>> at = {"theta": jnp.array(jnp.pi / 2), "phi": jnp.array(0.0)} >>> cxm.scale_factors(metric, cxc.sph2, at=at) QMatrix([1., 1.], '(, )')
- coordinax.manifolds.scale_factors(metric: PullbackMetric, chart: AbstractChart, /, *, at: dict[str, Any], usys: AbstractUnitSystem | None = None) QMatrix
Return scale factors for a pullback (induced) metric via Jacobian pullback.
Computes the Jacobian of the composed embedding
intrinsic → Cartesian ambientto obtain a unit-consistent Jacobian where every entry has the same unit (ambient_cart_unit / intrinsic_unit). The squared column norms then give the scale factors with correct units.>>> 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")), ... ) >>> at = {"theta": u.Angle(jnp.pi / 2, "rad"), "phi": u.Angle(0.0, "rad")} >>> cxm.scale_factors(M.metric, cxc.sph2, at=at) QMatrix([4., 4.], '(m2 / rad2, m2 / rad2)')
- coordinax.manifolds.angle_between(chart: Any, uvec: Any, vvec: Any, /, *args: Any, **kwargs: Any)#
Return the metric angle between two nonzero tangent vectors.
The inputs
uvecandvvecare component dictionaries representing tangent-vector components in the coordinate basis ofchart. The metric is evaluated at a base point supplied viaat=....Examples
>>> import jax.numpy as jnp >>> import unxt as u >>> import coordinax.charts as cxc >>> import coordinax.manifolds as cxm
>>> at = {"x": u.Q(0.0, "m"), "y": u.Q(0.0, "m")} >>> uvec = {"x": u.Q(1.0, "m"), "y": u.Q(0.0, "m")} >>> vvec = {"x": u.Q(0.0, "m"), "y": u.Q(1.0, "m")} >>> cxm.angle_between(cxc.cart2d, uvec, vvec, at=at) Angle(1.57079633, 'rad')
>>> at_sph = { ... "r": u.Q(2.0, "m"), ... "theta": u.Angle(jnp.pi / 2, "rad"), ... "phi": u.Angle(0.0, "rad"), ... } >>> u_tan = {"r": u.Q(0.0, "m"), "theta": u.Angle(1.0, "rad"), "phi": u.Angle(0.0, "rad")} >>> v_tan = {"r": u.Q(0.0, "m"), "theta": u.Angle(0.0, "rad"), "phi": u.Angle(1.0, "rad")} >>> cxm.angle_between(cxc.sph3d, u_tan, v_tan, at=at_sph) Angle(1.57079633, 'rad')
- coordinax.manifolds.angle_between(chart: AbstractChart, uvec: dict[str, Any], vvec: dict[str, Any], /, *, at: dict[str, Any], usys: AbstractUnitSystem | None = None) AbstractAngle
Manifold-level dispatch: delegate to the attached metric.
>>> import jax.numpy as jnp >>> import unxt as u >>> import coordinax.charts as cxc >>> import coordinax.manifolds as cxm
>>> at = {"x": u.Q(0.0, "m"), "y": u.Q(0.0, "m")} >>> uvec = {"x": u.Q(1.0, "m"), "y": u.Q(0.0, "m")} >>> vvec = {"x": u.Q(0.0, "m"), "y": u.Q(1.0, "m")} >>> cxm.angle_between(cxc.cart2d, uvec, vvec, at=at) Angle(1.57079633, 'rad')
- coordinax.manifolds.angle_between(metric: AbstractMetricField, chart: AbstractChart, uvec: dict[str, Any], vvec: dict[str, Any], /, *, at: dict[str, Any], usys: AbstractUnitSystem | None = None) AbstractAngle
Return the metric angle between two tangent vectors.
The input component dictionaries are interpreted as tangent-vector components in the coordinate basis of
chart. The metric is evaluated at the base pointat.>>> import jax.numpy as jnp >>> import unxt as u >>> import coordinax.charts as cxc >>> import coordinax.manifolds as cxm
>>> metric = cxm.FlatMetric(3) >>> at = { ... "r": u.Q(2.0, "m"), ... "theta": u.Angle(jnp.pi / 2, "rad"), ... "phi": u.Angle(0.0, "rad"), ... } >>> uvec = {"r": u.Q(0.0, "m"), "theta": u.Angle(1.0, "rad"), ... "phi": u.Angle(0.0, "rad")} >>> vvec = {"r": u.Q(0.0, "m"), "theta": u.Angle(0.0, "rad"), ... "phi": u.Angle(1.0, "rad")} >>> cxm.angle_between(metric, cxc.sph3d, uvec, vvec, at=at) Angle(1.57079633, 'rad')
- coordinax.manifolds.metric_matrix(M: Any, point: Any, chart: Any, /)#
Compute the coordinate metric matrix at
pointinchart.Dispatches on the triple
(type(M), type(point), type(chart)). Concrete implementations for each(M, chart)pair are registered in the correspondingregister_metric.pymodules viaplum.dispatch().- Parameters:
- Returns:
The metric matrix at
point. The concrete type (~coordinax._src.metric.matrix.DiagonalMetric or ~coordinax._src.metric.matrix.DenseMetric) depends on the(M, chart)pair and is declared bymetric_representation().- Return type:
- Raises:
NotImplementedError – When no specific dispatch rule is registered for the given types.
Examples
>>> import jax.numpy as jnp >>> import coordinax.api.manifolds as cxmapi >>> import coordinax.charts as cxc >>> import coordinax.manifolds as cxm
>>> at = {"x": jnp.array(1.0), "y": jnp.array(2.0), "z": jnp.array(3.0)} >>> cxmapi.metric_matrix(cxm.R3, at, cxc.cart3d) DiagonalMetric(diagonal=f64[3])
- coordinax.manifolds.metric_matrix(M: AbstractManifold, point: dict, chart: AbstractChart, /) AbstractMetricMatrix
Fallback — raise with a helpful message for unregistered pairs.
Concrete
(manifold, chart)pairs register their own dispatch rules in the relevantregister_metric.pymodules (loaded as part of Phase 2).- Parameters:
M (EmbeddedManifold) – The manifold carrying the metric field.
point (dict) – A component dictionary giving the coordinates in
chart.chart (AbstractChart) – The coordinate chart in which to express the metric.
M – An embedded submanifold; carries
intrinsic,ambient, andembed_mapfields.point – A coordinate dictionary in the intrinsic chart coordinates.
chart – The intrinsic chart (passed through for API consistency).
- Raises:
NotImplementedError – When no specific dispatch rule is registered for the given types.
.. py:function: – metric_matrix(M: coordinax._src.euclidean.manifold.EuclideanManifold, point: dict, chart: coordinax._src.charts.d1.Cart1D | coordinax._src.charts.d2.Cart2D | coordinax._src.charts.d3.Cart3D, /) -> coordinax._src.metric.matrix.DiagonalMetric: :noindex:
Euclidean metric in a Cartesian chart –
g = I_n.:The metric matrix is the identity in any Cartesian chart, represented –
compactly as a coordinax._src.metric.matrix.DiagonalMetric with all-one –
diagonal. –
>>> import jax.numpy as jnp –
>>> import coordinax.charts as cxc –
>>> import coordinax.manifolds as cxm –
>>> from coordinax.api.manifolds import metric_matrix –
>>> from coordinax._src.metric.matrix import DiagonalMetric –
Cart1D: –
>>> at = {"x" – jnp.array(3.0)}:
>>> g = metric_matrix(cxm.R1, at, cxc.cart1d) –
>>> isinstance(g, DiagonalMetric) –
True –
>>> g.diagonal –
Array([1.], dtype=float64) –
Cart2D: –
>>> at = {"x" – jnp.array(1.0), “y”: jnp.array(2.0)}:
>>> metric_matrix(cxm.R2, at, cxc.cart2d).diagonal –
Array([1., 1.], dtype=float64) –
Cart3D: –
>>> at = {"x" – jnp.array(1.0), “y”: jnp.array(2.0), “z”: jnp.array(3.0)}:
>>> metric_matrix(cxm.R3, at, cxc.cart3d).diagonal –
Array([1., 1., 1.], dtype=float64) –
.. py:function: – metric_matrix(M: coordinax._src.euclidean.manifold.EuclideanManifold, point: dict, chart: coordinax._src.charts.dn.CartND, /) -> coordinax._src.metric.matrix.DiagonalMetric: :noindex:
Euclidean metric in CartND –
g = I_Nwhere N is inferred from the point.:>>> import jax.numpy as jnp –
>>> import coordinax.charts as cxc –
>>> import coordinax.manifolds as cxm –
>>> from coordinax.api.manifolds import metric_matrix –
>>> at = {"q" – jnp.array([1.0, 2.0, 3.0])}:
>>> metric_matrix(cxm.R3, at, cxc.cartnd).diagonal –
Array([1., 1., 1.], dtype=float64) –
.. py:function: – metric_matrix(M: coordinax._src.euclidean.manifold.EuclideanManifold, point: dict, chart: coordinax._src.charts.d1.Radial1D, /) -> coordinax._src.metric.matrix.DiagonalMetric: :noindex:
Euclidean metric in Radial1D –
g = diag(1).:The only component is g_rr = 1 (the radial direction is an –
isometry of Euclidean distance in 1-D). –
>>> import jax.numpy as jnp –
>>> import unxt as u –
>>> import coordinax.charts as cxc –
>>> import coordinax.manifolds as cxm –
>>> from coordinax.api.manifolds import metric_matrix –
>>> from coordinax._src.metric.matrix import DiagonalMetric –
Dimensionless: –
>>> at = {"r" – jnp.array(2.0)}:
>>> g = metric_matrix(cxm.R1, at, cxc.radial1d) –
>>> isinstance(g, DiagonalMetric) –
True –
With length units: –
>>> at = {"r" – u.Q(2.0, “m”)}:
>>> g = metric_matrix(cxm.R1, at, cxc.radial1d) –
>>> g.diagonal –
QMatrix([1.], '(,)') –
.. py:function: – metric_matrix(M: coordinax._src.euclidean.manifold.EuclideanManifold, point: dict, chart: coordinax._src.charts.d2.Polar2D, /) -> coordinax._src.metric.matrix.DiagonalMetric: :noindex:
Euclidean metric in Polar2D –
g = diag(1, r²).:point must contain keys "r" (length) and "theta" (angle). –
>>> import jax.numpy as jnp –
>>> import unxt as u –
>>> import coordinax.charts as cxc –
>>> import coordinax.manifolds as cxm –
>>> from coordinax.api.manifolds import metric_matrix –
>>> from coordinax._src.metric.matrix import DiagonalMetric –
Dimensionless r: –
>>> at = {"r" – jnp.array(3.0), “theta”: jnp.array(0.5)}:
>>> g = metric_matrix(cxm.R2, at, cxc.polar2d) –
>>> g.diagonal –
QMatrix([1., 9.], '(, )') –
Length-valued r and angle-valued theta: –
>>> at = {"r" – u.Q(3.0, “m”), “theta”: u.Angle(0.5, “rad”)}:
>>> g = metric_matrix(cxm.R2, at, cxc.polar2d) –
>>> g.diagonal –
QMatrix([1., 9.], '(, m2 / rad2)') –
.. py:function: – metric_matrix(M: coordinax._src.euclidean.manifold.EuclideanManifold, point: dict, chart: coordinax._src.charts.d3.Cylindrical3D, /) -> coordinax._src.metric.matrix.DiagonalMetric: :noindex:
Euclidean metric in Cylindrical3D –
g = diag(1, ρ², 1).:point` must contain keys "rho" (length), "phi" (angle) –
and "z" (length). –
>>> import jax.numpy as jnp –
>>> import unxt as u –
>>> import coordinax.charts as cxc –
>>> import coordinax.manifolds as cxm –
>>> from coordinax.api.manifolds import metric_matrix –
>>> from coordinax._src.metric.matrix import DiagonalMetric –
>>> at = {"rho" – u.Q(3.0, “m”), “phi”: u.Angle(0.0, “rad”), “z”: u.Q(1.0, “m”)}:
>>> g = metric_matrix(cxm.R3, at, cxc.cyl3d) –
>>> g.diagonal –
QMatrix([1., 9., 1.], '(, m2 / rad2, )') –
.. py:function: – metric_matrix(M: coordinax._src.euclidean.manifold.EuclideanManifold, point: dict, chart: coordinax._src.charts.d3.Spherical3D, /) -> coordinax._src.metric.matrix.DiagonalMetric: :noindex:
Euclidean metric in Spherical3D –
g = diag(1, r², r²sin²θ).:Physics convention –
θis the polar (colatitude) angle measured from:the +z axis, φ is the azimuthal angle. point must contain –
keys "r" (length), "theta" (polar angle), and "phi" –
(azimuthal angle). –
>>> import jax.numpy as jnp –
>>> import unxt as u –
>>> import coordinax.charts as cxc –
>>> import coordinax.manifolds as cxm –
>>> from coordinax.api.manifolds import metric_matrix –
>>> from coordinax._src.metric.matrix import DiagonalMetric –
>>> at = { –
... "r" – u.Q(2.0, “m”),:
... "theta" – u.Angle(jnp.pi / 2, “rad”),:
... "phi" – u.Angle(0.0, “rad”),:
... } –
>>> g = metric_matrix(cxm.R3, at, cxc.sph3d) –
>>> isinstance(g, DiagonalMetric) –
True –
>>> g.diagonal –
QMatrix([1., 4., 4.], '(, m2 / rad2, m2 / rad2)') –
.. py:function: – metric_matrix(M: coordinax._src.euclidean.manifold.EuclideanManifold, point: dict, chart: coordinax._src.charts.d3.MathSpherical3D, /) -> coordinax._src.metric.matrix.DiagonalMetric: :noindex:
Euclidean metric in MathSpherical3D –
g = diag(1, r²sin²φ, r²).:Math convention –
φis the polar angle from the+zaxis:(colatitude), θ is the azimuthal angle. point must contain –
keys "r" (length), "theta" (azimuthal angle), and "phi" –
(polar / colatitude angle). –
>>> import jax.numpy as jnp –
>>> import unxt as u –
>>> import coordinax.charts as cxc –
>>> import coordinax.manifolds as cxm –
>>> from coordinax.api.manifolds import metric_matrix –
>>> from coordinax._src.metric.matrix import DiagonalMetric –
>>> at = { –
... "r" – u.Q(2.0, “m”),:
... "theta" – u.Angle(0.0, “rad”),:
... "phi" – u.Angle(jnp.pi / 2, “rad”),:
... } –
>>> g = metric_matrix(cxm.R3, at, cxc.math_sph3d) –
>>> isinstance(g, DiagonalMetric) –
True –
>>> g.diagonal –
QMatrix([1., 4., 4.], '(, m2 / rad2, m2 / rad2)') –
.. py:function: – metric_matrix(M: coordinax._src.euclidean.manifold.EuclideanManifold, point: dict, chart: coordinax._src.charts.d3.LonLatSpherical3D, /) -> coordinax._src.metric.matrix.DiagonalMetric: :noindex:
Euclidean metric in LonLatSpherical3D. –
The metric is g = diag(distance²cos²lat, distance², 1) (components –
ordered as (lon, lat, distance)). point must contain keys –
"lon"`, "lat", and "distance" (length) –
>>> import jax.numpy as jnp –
>>> import unxt as u –
>>> import coordinax.charts as cxc –
>>> import coordinax.manifolds as cxm –
>>> from coordinax.api.manifolds import metric_matrix –
>>> from coordinax._src.metric.matrix import DiagonalMetric –
>>> at = { –
... "lon" – u.Angle(0.0, “rad”),:
... "lat" – u.Angle(0.0, “rad”),:
... "distance" – u.Q(2.0, “m”),:
... } –
>>> g = metric_matrix(cxm.R3, at, cxc.lonlat_sph3d) –
>>> isinstance(g, DiagonalMetric) –
True –
>>> g.diagonal –
QMatrix([4., 4., 1.], '(m2 / rad2, m2 / rad2, )') –
.. py:function: – metric_matrix(M: coordinax._src.euclidean.manifold.EuclideanManifold, point: dict, chart: coordinax._src.base.charts.AbstractChart, /) -> coordinax._src.metric.matrix.DenseMetric: :noindex:
Euclidean metric in a general chart via Jacobian pullback g = J^T J. –
>>> import jax.numpy as jnp –
>>> import unxt as u –
>>> import coordinax.charts as cxc –
>>> import coordinax.manifolds as cxm –
>>> from coordinax.api.manifolds import metric_matrix –
>>> from coordinax._src.metric.matrix import DenseMetric –
>>> from coordinax._src.charts.d3 import LonCosLatSpherical3D –
Non-orthogonal chart (fallback, returns DenseMetric): –
>>> M = cxm.R3 –
>>> chart = LonCosLatSpherical3D() –
>>> at = { –
... "lon_coslat" – u.Angle(0.0, “rad”),:
... "lat" – u.Angle(0.0, “rad”),:
... "distance" – u.Q(2.0, “m”),:
... } –
>>> g = metric_matrix(M, at, chart) –
>>> isinstance(g, DenseMetric) –
True –
.. py:function: – metric_matrix(M: coordinax._src.minkowski.manifold.MinkowskiManifold, point: dict, chart: coordinax._src.minkowski.charts.MinkowskiCT, /) -> coordinax._src.metric.matrix.DiagonalMetric: :noindex:
Minkowski metric $eta = operatorname{diag}(-1, 1, 1, 1)$ in CT chart. –
All four components (ct, x, y, z) carry dimension "length", so the –
entries are dimensionless. –
>>> import jax.numpy as jnp –
>>> import coordinax.charts as cxc –
>>> import coordinax.manifolds as cxm –
>>> from coordinax.api.manifolds import metric_matrix –
>>> from coordinax._src.metric.matrix import DiagonalMetric –
>>> M = cxm.MinkowskiManifold() –
>>> at = {"ct" – jnp.array(0.0), “x”: jnp.array(1.0),:
... "y" – jnp.array(0.0), “z”: jnp.array(0.0)}:
>>> g = metric_matrix(M, at, cxc.minkowskict) –
>>> isinstance(g, DiagonalMetric) –
True –
>>> g.diagonal –
Array([-1., 1., 1., 1.], dtype=float64) –
.. py:function: – metric_matrix(M: coordinax._src.minkowski.manifold.MinkowskiManifold, point: dict, chart: coordinax._src.base.charts.AbstractChart, /) -> coordinax._src.metric.matrix.DenseMetric: :noindex:
Minkowski metric in a general chart via Jacobian pullback $g = J^T eta J$. –
>>> import jax.numpy as jnp –
>>> import coordinax.charts as cxc –
>>> import coordinax.manifolds as cxm –
>>> from coordinax.api.manifolds import metric_matrix –
>>> from coordinax._src.metric.matrix import DiagonalMetric –
Canonical chart uses the specific dispatch above, not this fallback: –
>>> M = cxm.MinkowskiManifold() –
>>> at = {"ct" – jnp.array(0.0), “x”: jnp.array(1.0),:
... "y" – jnp.array(0.0), “z”: jnp.array(0.0)}:
>>> g = metric_matrix(M, at, cxc.minkowskict) –
>>> isinstance(g, DiagonalMetric) –
True –
.. py:function: – metric_matrix(M: coordinax._src.product.manifold.CartesianProductManifold, point: dict, chart: coordinax._src.product.chart.AbstractCartesianProductChart, /) -> coordinax._src.metric.matrix.DenseMetric: :noindex:
Product metric (block-diagonal) in a product chart. –
Assembles the block-diagonal matrix from factor metrics by recursively –
calling the standalone metric_matrix dispatch API. –
>>> import jax.numpy as jnp –
>>> import coordinax.manifolds as cxm –
>>> from coordinax.api.manifolds import metric_matrix –
>>> from coordinax._src.metric.matrix import DenseMetric –
Two-factor Euclidean product (R² x R¹): –
>>> M = cxm.CartesianProductManifold( –
... factors=(cxm.R2, cxm.R1), factor_names=("xy", "z") –
... ) –
>>> chart = M.default_chart() –
>>> at = {k – jnp.array(0.0) for k in chart.components}:
>>> g = metric_matrix(M, at, chart) –
>>> isinstance(g, DenseMetric) –
True –
>>> g.ndim –
3 –
.. py:function: – metric_matrix(M: coordinax._src.embedded.manifold.EmbeddedManifold, point: dict, chart: coordinax._src.base.charts.AbstractChart, /) -> coordinax._src.metric.matrix.DenseMetric: :noindex:
Induced metric on an embedded submanifold via Jacobian pullback. –
Computes $g_{ij} = sum_k J^k_i J^k_j$ where $J$ is the Jacobian of the –
composition intrinsic → Cartesian ambient. Routing through Cartesian –
ambient coordinates ensures all entries of $J$ share the same unit –
(cart_unit / intrinsic_unit), so the matrix product $J^T J$ is –
unit-compatible and the result carries physically correct units. –
- Returns:
Induced metric matrix at
point, backed by aQMatrixwith unitscart_unit^2 / (intrinsic_unit_i * intrinsic_unit_j).- Return type:
Examples
>>> import jax.numpy as jnp >>> import unxt as u >>> import coordinax.manifolds as cxm >>> import coordinax.charts as cxc >>> from coordinax.api.manifolds import metric_matrix >>> from coordinax._src.metric.matrix import DenseMetric
Unit sphere — values should be the identity:
>>> M = cxm.EmbeddedManifold( ... intrinsic=cxm.S2, ambient=cxm.R3, ... embed_map=cxm.TwoSphereIn3D(radius=1.0), ... ) >>> p = {"theta": u.Angle(jnp.pi / 2, "rad"), "phi": u.Angle(0.0, "rad")} >>> g = metric_matrix(M, p, cxc.sph2) >>> isinstance(g, DenseMetric) True >>> g.matrix.value Array([[1., 0.], [0., 1.]], dtype=float64, weak_type=True)
Radius-2 sphere — metric scaled by R²:
>>> M2 = cxm.EmbeddedManifold( ... intrinsic=cxm.S2, ambient=cxm.R3, ... embed_map=cxm.TwoSphereIn3D(radius=u.Q(2.0, "m")), ... ) >>> g2 = metric_matrix(M2, p, cxc.sph2) >>> g2.matrix.value Array([[4., 0.], [0., 4.]], dtype=float64, weak_type=True) >>> g2.matrix.unit[0, 0] Unit("m2 / rad2")
- coordinax.manifolds.metric_matrix(M: HyperSphericalManifold, point: dict[str, Any], chart: AbstractSphericalHyperSphere, /) DiagonalMetric
Round metric on the unit $n$-sphere in a standard angular chart.
Computes diagonal entries directly via
_sine_product_diagonal:$$g_{kk} = prod_{j=0}^{k-1} sin^2(theta_j)$$
>>> import jax.numpy as jnp >>> import coordinax.charts as cxc >>> import coordinax.manifolds as cxm >>> from coordinax.api.manifolds import metric_matrix >>> from coordinax._src.metric.matrix import DiagonalMetric
$S^2$ at the equator $theta = pi/2$:
>>> M = cxm.HyperSphericalManifold(2) >>> at = {"theta": jnp.array(jnp.pi / 2), "phi": jnp.array(0.0)} >>> g = metric_matrix(M, at, cxc.sph2) >>> isinstance(g, DiagonalMetric) True >>> g.diagonal Array([1., 1.], dtype=float64)
$S^2$ at $theta = pi/6$:
>>> at = {"theta": jnp.array(jnp.pi / 6), "phi": jnp.array(0.0)} >>> g = metric_matrix(M, at, cxc.sph2) >>> round(float(g.diagonal[1]), 10) # sin\u00b2(\u03c0/6) \u2248 0.25 0.25
- coordinax.manifolds.metric_representation(M: Any, chart: Any, /)#
Return the AbstractMetricMatrix subtype for
(manifold, chart).A lightweight, allocation-free query that reports which concrete ~coordinax._src.metric.matrix.AbstractMetricMatrix subclass
metric_matrix()will return for a given(manifold, chart)pair, without actually computing the metric values.Dispatches on
(type(manifold), type(chart)). Concrete rules are registered in the relevantregister_metric.pymodules. The default fallback returns ~coordinax._src.metric.matrix.DenseMetric.- Parameters:
- Returns:
The metric matrix type guaranteed for this
(manifold, chart)pair.- Return type:
Examples
>>> import coordinax.api.manifolds as cxmapi >>> import coordinax.charts as cxc >>> import coordinax.manifolds as cxm
>>> cxmapi.metric_representation(cxm.R3, cxc.cart3d) is cxm.DiagonalMetric True
- coordinax.manifolds.metric_representation(M: AbstractManifold, chart: AbstractChart, /) type[AbstractMetricMatrix]
Return DenseMetric as the default fallback.
More specific rules (e.g. for Cartesian charts) override this and return DiagonalMetric.
>>> import coordinax.manifolds as cxm >>> import coordinax.charts as cxc >>> from coordinax.api.manifolds import metric_representation >>> from coordinax._src.metric.matrix import DenseMetric >>> from coordinax._src.charts.d3 import LonCosLatSpherical3D
Non-orthogonal chart falls back to
DenseMetric:>>> metric_representation(cxm.R3, LonCosLatSpherical3D()) is DenseMetric True
- coordinax.manifolds.metric_representation(M: EuclideanManifold, chart: AbstractChart, /) type[DenseMetric]
Euclidean manifold in a general (non-Cartesian) chart → DenseMetric.
>>> import coordinax.charts as cxc >>> import coordinax.manifolds as cxm >>> from coordinax.api.manifolds import metric_representation >>> from coordinax._src.metric.matrix import DenseMetric
>>> from coordinax._src.charts.d3 import LonCosLatSpherical3D >>> chart = LonCosLatSpherical3D() >>> metric_representation(cxm.R3, chart) <class 'coordinax._src.metric.matrix.DenseMetric'>
- coordinax.manifolds.metric_representation(M: EuclideanManifold, chart: Cart1D | Cart2D | Cart3D | CartND | Radial1D | Polar2D | Cylindrical3D | Spherical3D | MathSpherical3D | LonLatSpherical3D, /) type[DiagonalMetric]
Euclidean manifold in a Cartesian or orthogonal curvilinear chart.
Returns
DiagonalMetric.>>> import coordinax.charts as cxc >>> import coordinax.manifolds as cxm >>> from coordinax.api.manifolds import metric_representation >>> from coordinax._src.metric.matrix import DiagonalMetric
>>> metric_representation(cxm.R3, cxc.cart3d) <class 'coordinax._src.metric.matrix.DiagonalMetric'>
>>> metric_representation(cxm.R2, cxc.polar2d) <class 'coordinax._src.metric.matrix.DiagonalMetric'>
>>> metric_representation(cxm.R3, cxc.sph3d) <class 'coordinax._src.metric.matrix.DiagonalMetric'>
- coordinax.manifolds.metric_representation(M: MinkowskiManifold, chart: AbstractChart, /) type[DenseMetric]
Minkowski manifold in a general chart →
DenseMetric.>>> import coordinax.charts as cxc >>> import coordinax.manifolds as cxm >>> from coordinax.api.manifolds import metric_representation >>> from coordinax._src.metric.matrix import DenseMetric
>>> M = cxm.MinkowskiManifold() >>> metric_representation(M, cxc.minkowskict) <class 'coordinax._src.metric.matrix.DenseMetric'>
- coordinax.manifolds.metric_representation(M: MinkowskiManifold, chart: MinkowskiCT, /) type[DiagonalMetric]
Minkowski manifold in the canonical CT chart →
DiagonalMetric.>>> import coordinax.charts as cxc >>> import coordinax.manifolds as cxm >>> from coordinax.api.manifolds import metric_representation >>> from coordinax._src.metric.matrix import DiagonalMetric
>>> M = cxm.MinkowskiManifold() >>> metric_representation(M, cxc.minkowskict) <class 'coordinax._src.metric.matrix.DiagonalMetric'>
- coordinax.manifolds.metric_representation(M: CartesianProductManifold, chart: AbstractCartesianProductChart, /) type[DenseMetric]
Product manifold in a product chart →
DenseMetric.The product metric is block-diagonal in general (not necessarily diagonal even if each factor metric is diagonal), so
DenseMetricis the conservative declaration.>>> import coordinax.manifolds as cxm >>> from coordinax.api.manifolds import metric_representation >>> from coordinax._src.metric.matrix import DenseMetric
>>> M = cxm.CartesianProductManifold( ... factors=(cxm.R2, cxm.R1), factor_names=("xy", "z") ... ) >>> chart = M.default_chart() >>> metric_representation(M, chart) <class 'coordinax._src.metric.matrix.DenseMetric'>
- coordinax.manifolds.metric_representation(M: EmbeddedManifold, chart: AbstractChart, /) type[DenseMetric]
Embedded manifold in any intrinsic chart → DenseMetric.
>>> import unxt as u >>> import coordinax.manifolds as cxm >>> import coordinax.charts as cxc >>> from coordinax.api.manifolds import metric_representation
>>> M = cxm.EmbeddedManifold( ... intrinsic=cxm.S2, ambient=cxm.R3, ... embed_map=cxm.TwoSphereIn3D(radius=u.Q(1.0, "km")), ... ) >>> metric_representation(M, cxc.sph2) <class 'coordinax._src.metric.matrix.DenseMetric'>
- coordinax.manifolds.metric_representation(M: HyperSphericalManifold, chart: AbstractSphericalHyperSphere, /) type[DiagonalMetric]
Return DiagonalMetric for a unit $n$-sphere in a standard angular chart.
>>> import coordinax.charts as cxc >>> import coordinax.manifolds as cxm
>>> cxm.metric_representation(cxm.S2, cxc.sph2) <class 'coordinax._src.metric.matrix.DiagonalMetric'>
- class coordinax.manifolds.AbstractAtlas#
Bases:
objectAtlas protocol for manifolds.
An atlas defines the set of charts that may be used to represent coordinates on a manifold. In differential geometry, a smooth manifold is defined by a pair $(M, \mathcal{A})$ where $M$ is a topological space and $\mathcal{A}$ is a maximal smooth atlas — a collection of compatible charts whose domains cover the $M$.
Responsibilities of an atlas include:
declaring the dimension of the manifold it covers,
determining whether a chart is compatible with the manifold,
providing a default chart used when one is not explicitly specified.
The atlas does not perform coordinate transformations itself. Those are implemented by chart-level transition maps and higher-level transformation machinery (e.g. {func}`pt_map`).
Notes
Atlas objects are structural descriptors, not numerical objects.
Multiple manifolds may share the same atlas type if their smooth structures coincide.
Charts belonging to the same atlas are assumed to have compatible transition maps.
Some atlas implementations allow charts to register themselves as compatible coordinate systems. For example, Euclidean charts register with coordinax.manifolds.EuclideanAtlas so they can be recognized automatically.
Examples
Constructing a Euclidean atlas
In the Euclidean case the atlas consists of common coordinate systems on $mathbb{R}^n$.
>>> import coordinax.manifolds as cxm >>> atlas = cxm.EuclideanAtlas(3)
The atlas records the dimension of the manifold:
>>> atlas.ndim 3
It can provide a canonical chart:
>>> atlas.default_chart() Cart3D(M=Rn(3))
The atlas determines whether a chart belongs to the manifold.
>>> import coordinax.charts as cxc >>> cxc.cart3d in atlas True
>>> cxc.cyl3d in atlas True
Charts with the wrong dimensionality are rejected:
>>> cxc.cart2d in atlas False
Atlas-manifold interaction
A manifold object typically owns an atlas describing its smooth structure.
>>> from coordinax.manifolds import EuclideanManifold >>> M = EuclideanManifold(3)
>>> M.atlas.ndim 3
The manifold uses the atlas to verify chart compatibility:
>>> M.has_chart(cxc.cart3d) True
>>> M.has_chart(cxc.cart2d) False
- abstractmethod default_chart()#
Return a default chart from the atlas.
- Return type:
AbstractChart[Any,Any,Any]
>>> import coordinax.manifolds as cxm >>> atlas = cxm.EuclideanAtlas(2) >>> atlas.default_chart() Cart2D(M=Rn(2))
- default_chart_for(M: AbstractManifold, /)#
Return a default chart from the atlas for the given manifold.
This is a thin convenience wrapper over
self.default_chart()that checks that the manifold’s atlas matches this atlas.>>> import coordinax.manifolds as cxm >>> M = cxm.R2 >>> M.atlas.default_chart_for(M) Cart2D(M=Rn(2))
>>> try: M.atlas.default_chart_for(cxm.R3) ... except ValueError as e: print(e) Atlas EuclideanAtlas(ndim=2) does not match manifold atlas EuclideanAtlas(ndim=3).
- Parameters:
M (
AbstractManifold)- Return type:
AbstractChart[Any,Any,Any]
- abstractmethod has_chart(chart: AbstractChart[Any, Any, Any], /)#
Return whether the atlas supports the given chart.
>>> import coordinax.manifolds as cxm >>> atlas = cxm.EuclideanAtlas(2) >>> atlas.has_chart(cxc.cart2d) True
>>> atlas.has_chart(cxc.cart3d) False
- Parameters:
chart (
AbstractChart[Any,Any,Any])- Return type:
- class coordinax.manifolds.AbstractMetricField#
Bases:
objectAbstract base class for metrics of manifolds.
The metric defines a bilinear form on the tangent space of a chart.
$$ g_p: T_p M times T_p M to mathbb{R} $$
The metric can be represented as a matrix in the coordinate basis of a chart. Let $(U,varphi)$ be a chart with coordinates $q = (q^1, dots, q^n)$.
The coordinate basis of the tangent space is
$left{ frac{partial}{partial q^i} right}_{i=1}^n$.
The metric matrix is defined by evaluating the metric on these basis vectors:
$$ g_{ij}(q) = g_p left( frac{partial}{partial q^i}, frac{partial}{partial q^j} right). $$
This gives an n times n matrix
$$ g(q) = begin{pmatrix}
g_{11}(q) & cdots & g_{1n}(q) \ vdots & ddots & vdots \ g_{n1}(q) & cdots & g_{nn}(q) end{pmatrix}.
$$
The metric matrix is computed via the standalone dispatch function
coordinax.manifolds.metric_matrix().Examples
>>> import coordinax.manifolds as cxm >>> cxm.FlatMetric(3).ndim 3
- class coordinax.manifolds.AbstractManifold#
Bases:
objectAbstract interface for smooth manifolds.
A smooth manifold of dimension $n$ is a topological space $M$ equipped with a maximal smooth atlas $mathcal{A}$ — a collection of compatible charts $(U_alpha, varphi_alpha)$ whose domains cover $M$. Each chart $varphi_alpha : U_alpha subset M to mathbb{R}^n$ assigns local coordinates to points in an open neighbourhood $U_alpha$.
AbstractManifoldis the base class for all manifold objects in coordinax. It couples a manifold to its atlas and exposes a small set of coordinate-level operations:chart introspection — querying which charts belong to the manifold,
point transition maps — converting point coordinates between two charts in the same atlas, and
Cartesian realization — converting point coordinates into (or out of) a canonical ambient Cartesian chart when the manifold admits one.
All geometric and numerical work is delegated to the charts and the atlas; the manifold itself is a lightweight descriptor.
- atlas#
The atlas that defines the smooth structure of this manifold. The atlas records the intrinsic dimension and determines which charts are compatible.
- Type:
- ndim#
Intrinsic dimension $n$ of the manifold, forwarded from {attr}`atlas.ndim <AbstractAtlas.ndim>`.
- Type:
- default_chart#
A canonical chart chosen by the atlas, forwarded from {meth}`atlas.default_chart() <AbstractAtlas.default_chart>`.
- Type:
- Return type:
AbstractChart[Any, Any, Any]
Notes
Manifold objects are structural descriptors, not numerical arrays. They carry no point data.
Subclasses are typically frozen dataclasses registered with JAX as static pytree nodes so that they can appear as static metadata inside JIT-compiled functions.
The two-sphere $S^2$ is not a product manifold: its atlas requires at least two charts with non-trivial overlaps, and its transition maps do not factor. Contrast with the Euclidean case where a single global chart covers all of $mathbb{R}^n$.
Examples
AbstractManifoldcannot be instantiated directly; use a concrete subclass such as {class}`~coordinax.manifolds.EuclideanManifold` or {class}`~coordinax.manifolds.HyperSphericalManifold`.Basic construction and introspection
The Euclidean 3-manifold $mathbb{R}^3$ with its standard atlas:
>>> import coordinax.manifolds as cxm >>> M = cxm.Rn(3) >>> M Rn(3)
The intrinsic dimension is read from the atlas:
>>> M.ndim 3
The atlas itself is accessible and carries the same dimension:
>>> M.atlas EuclideanAtlas(ndim=3)
The default chart is the canonical Cartesian chart for that dimension:
>>> M.default_chart() Cart3D(M=Rn(3))
Chart membership
{meth}`has_chart` returns
Truewhen a chart instance belongs to the 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 {exc}`ValueError` for unsupported charts, which makes it convenient as an assertion inside other methods:
>>> try: ... M.check_chart(cxc.cart2d) ... except ValueError as e: ... print(e) Chart Cart2D(M=Rn(2)) is not supported by this manifold atlas.
Non-Euclidean manifolds
The two-sphere $S^2$ is a 2-dimensional manifold that is not a subspace of any Euclidean atlas. Its atlas admits only angular charts:
>>> S2 = cxm.HyperSphericalManifold(2) >>> S2.ndim 2
>>> S2.has_chart(cxc.sph2) True
>>> S2.has_chart(cxc.cart2d) False
>>> S2.default_chart() SphericalTwoSphere(M=Sn(2))
- atlas: AbstractAtlas#
Charts compatible with this manifold. This defines the smooth structure.
- metric: AbstractMetricField#
The manifold’s metric. This defines the geometric structure.
- 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
- 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:
- 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:
- angle_between(chart: AbstractChart[Any, Any, Any], uvec: dict[str, Any], vvec: dict[str, Any], /, *, at: dict[str, Any], 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).
- class coordinax.manifolds.AbstractDiagonalMetricField#
Bases:
AbstractMetricFieldAbstract base class for metrics whose matrix is diagonal.
A metric is diagonal (equivalently, the coordinate chart is an orthogonal coordinate system) when all off-diagonal entries of the metric matrix vanish at every base point in every chart in the metric’s diagonal domain:
$$g_{ij}(p) = 0 quad text{for } i neq j.$$
The coordinate basis vectors $partial/partial q^i$ are then mutually orthogonal. The diagonal entries $g_{ii}(p)$ give the squared scale factors
$$h_i(p)^2 = g_{ii}(p),$$
and the infinitesimal line element simplifies to
$$ds^2 = sum_i g_{ii}(q),(dq^i)^2.$$
Role: structural marker, not behavioral interface.
This class adds no new abstract methods beyond those of AbstractMetricField. Its purpose is to declare that the
metric_matrixdispatch must return aDiagonalMetricat every valid base point for charts where this metric is used as diagonal (typically orthogonal charts).In particular, manifold/atlas chart membership (for example,
has_chart) is a broader structural notion and does not, by itself, imply orthogonality or diagonality.See also
FlatMetricflat Riemannian metric on $mathbb{R}^n$; in Cartesian charts $g = I_n$; in orthogonal curvilinear charts computed by Jacobian pullback $g = J^top J$.
RoundMetricround metric on $S^{n-1}$ in the intrinsic hyperspherical chart; diagonal entries follow the cumulative-sine rule $g_{kk} = prod_{j < k} sin^2!theta_j$.
MinkowskiMetricLorentzian pseudo-Riemannian metric $eta = operatorname{diag}(-1, 1, 1, 1)$ on Minkowski spacetime; diagonal in the canonical Cartesian spacetime chart.
Examples
>>> import coordinax.manifolds as cxm
FlatMetricis anAbstractDiagonalMetricField:>>> isinstance(cxm.FlatMetric(3), AbstractDiagonalMetricField) True
MinkowskiMetricis also anAbstractDiagonalMetricField:>>> isinstance(cxm.MinkowskiMetric(), AbstractDiagonalMetricField) True
General (non-diagonal) metrics such as
PullbackMetricare not:>>> import unxt as u >>> isinstance( ... cxm.PullbackMetric( ... cxm.TwoSphereIn3D(radius=u.Q(1.0, "m")), ... cxm.FlatMetric(3), ... ), ... AbstractDiagonalMetricField, ... ) False
- class coordinax.manifolds.AbstractMetricMatrix#
Bases:
ModuleAbstract base class for typed metric matrix representations.
Concrete subclasses encode the sparsity structure of a metric matrix (diagonal vs. dense) and provide matrix-level operations consistent with that structure.
- abstractmethod to_dense()#
Return an equivalent
DenseMetric.For diagonal metrics, off-diagonal entries are zero. For dense metrics, returns
self.- Return type:
- final class coordinax.manifolds.DiagonalMetric(diagonal: QMatrix | Array)#
Bases:
AbstractMetricMatrixDiagonal metric matrix stored as a 1-D array or QMatrix.
Encodes a metric whose coordinate matrix is diagonal — i.e. orthogonal coordinate charts. Storing only the diagonal avoids materialising the full $n times n$ matrix and makes operations like matrix-vector products and inversion run in $O(n)$.
Examples
>>> import jax.numpy as jnp >>> from coordinax._src.metric.matrix import DiagonalMetric
>>> d = DiagonalMetric(jnp.array([1.0, 4.0, 9.0])) >>> d.ndim 3 >>> d.determinant Array(36., dtype=float64) >>> d.inverse.diagonal Array([1. , 0.25 , 0.11111111], dtype=float64)
- to_dense()#
Convert to a full $n times n$ matrix with zeros off the diagonal.
When the diagonal is a
QMatrix, the off-diagonal entry(i, j)is assigned the geometric-mean unitsqrt(diag_unit[i] * diag_unit[j]). This choice ensures thatg[i, j] * v[j]is unit-compatible withg[i, i] * v[i]during matrix-vector contraction, which is required for theQMatrix()dot-product to succeed even when the coordinate components have different physical dimensions (e.g. metres and radians in spherical coordinates).- Return type:
Examples
>>> import jax.numpy as jnp >>> from coordinax._src.metric.matrix import DiagonalMetric
Plain array — off-diagonal entries are zero:
>>> d = DiagonalMetric(jnp.array([1.0, 4.0])) >>> d.to_dense().matrix Array([[1., 0.], [0., 4.]], dtype=float64)
QMatrix diagonal — diagonal units are preserved and off-diagonal entries get the geometric-mean unit:
>>> from coordinax.internal import QMatrix >>> d = DiagonalMetric(QMatrix(jnp.array([1.0, 4.0]), unit=("m2", "s2"))) >>> d.to_dense().matrix.unit[0, 0] Unit("m2") >>> d.to_dense().matrix.unit[1, 1] Unit("s2") >>> d.to_dense().matrix.unit[0, 1] # geometric mean: sqrt(m2 * s2) Unit("m s")
- property inverse: DiagonalMetric#
Inverse diagonal metric — reciprocal of each diagonal entry.
Examples
>>> import jax.numpy as jnp >>> from coordinax._src.metric.matrix import DiagonalMetric
>>> d = DiagonalMetric(jnp.array([2.0, 4.0])) >>> d.inverse.diagonal Array([0.5 , 0.25], dtype=float64)
- property determinant: Array | AbstractQuantity#
Product of the diagonal entries.
Returns a
AbstractQuantitywhen the diagonal is aQMatrix.Examples
>>> import jax.numpy as jnp >>> from coordinax._src.metric.matrix import DiagonalMetric
Bare array — returns a plain
Array:>>> DiagonalMetric(jnp.array([2.0, 3.0])).determinant Array(6., dtype=float64)
QMatrix diagonal — returns a
Quantity:>>> import unxt as u >>> from coordinax.internal import QMatrix >>> d = DiagonalMetric(QMatrix(jnp.array([2.0, 3.0]), unit=("m2", "s2"))) >>> d.determinant Q(6., 'm2 s2')
- final class coordinax.manifolds.DenseMetric(matrix: QMatrix | Array)#
Bases:
AbstractMetricMatrixDense symmetric metric matrix.
Stores the full $n \times n$ metric matrix. Used for non-orthogonal charts or metrics that cannot be expressed diagonally.
Examples
>>> import jax.numpy as jnp >>> from coordinax._src.metric.matrix import DenseMetric
>>> g = DenseMetric(jnp.eye(3)) >>> g.ndim 3 >>> g.determinant Array(1., dtype=float64)
- to_dense()#
Return
self— already in dense form.- Return type:
Examples
>>> import jax.numpy as jnp >>> from coordinax._src.metric.matrix import DenseMetric
>>> g = DenseMetric(jnp.eye(2)) >>> g.to_dense() is g True
- property inverse: DenseMetric#
Inverse via
jax.numpy.linalg.inv()(positive-definite assumption).Returns a
QMatrix-backedDenseMetricwith units1 / ref_unitwhen the matrix carries units. Assumes all entries share the same unit (physically well-formed metrics from the Cartesian-Jacobian pullback always satisfy this).Examples
>>> import jax.numpy as jnp >>> from coordinax._src.metric.matrix import DenseMetric
Bare array:
>>> g = DenseMetric(jnp.array([[2.0, 0.0], [0.0, 4.0]])) >>> g.inverse.matrix Array([[0.5 , 0. ], [0. , 0.25]], dtype=float64)
QMatrix — inverse carries reciprocal units:
>>> import unxt as u >>> from coordinax.internal import QMatrix, UnitsMatrix >>> g = DenseMetric( ... QMatrix( ... jnp.array([[4.0, 0.0], [0.0, 1.0]]), ... unit=UnitsMatrix(( ... ("m2 / rad2", "m2 / rad2"), ... ("m2 / rad2", "m2 / rad2"), ... )), ... ) ... ) >>> g.inverse.matrix.unit[0, 0] Unit("rad2 / m2") >>> g.inverse.matrix.value Array([[0.25, 0. ], [0. , 1. ]], dtype=float64)
- property determinant: Array | AbstractQuantity#
Determinant via the custom
det_pJAX primitive.Routes through Quax, so a
QMatrixmatrix returns aAbstractQuantitywhile a plain array returns a bareArray. The unit is the product of the main-diagonal units — valid for diagonal and uniform-unit matrices.Examples
>>> import jax.numpy as jnp >>> from coordinax._src.metric.matrix import DenseMetric
Bare array — returns a plain
Array:>>> DenseMetric(jnp.eye(3)).determinant Array(1., dtype=float64)
QMatrix — returns a
Quantity:>>> import unxt as u >>> from coordinax.internal import QMatrix >>> g = DenseMetric(QMatrix(jnp.eye(2), unit=(("m2", ""), ("", "s2")))) >>> g.determinant Q(1., 'm2 s2')
- class coordinax.manifolds.NoManifold#
Bases:
AbstractManifoldA degenerate placeholder manifold with no charts and no geometry.
NoManifoldis a sentinel value used when a manifold object is required by the API but none has been specified by the user.ndim == Falsesignals “no manifold specified”.has_chart(chart)always returnsFalse.
Examples
>>> import coordinax.manifolds as cxm >>> import coordinax.charts as cxc >>> M = cxm.NoManifold() >>> M.ndim 0 >>> M.has_chart(cxc.cart2d) False
- angle_between(chart: AbstractChart[Any, Any, Any], uvec: dict[str, Any], vvec: dict[str, Any], /, *, at: dict[str, Any], 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).
- 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))
- final class coordinax.manifolds.NoMetric#
Bases:
AbstractMetricFieldA degenerate placeholder metric with no geometry.
NoMetricis a sentinel value used when a metric object is required by the API but none has been specified by the user.ndim == Falsesignals “no metric specified”.
- class coordinax.manifolds.NoAtlas#
Bases:
AbstractAtlasTrivial atlas that supports no charts.
- default_chart()#
Return a default chart from the atlas.
- Return type:
>>> import coordinax.manifolds as cxm >>> atlas = cxm.EuclideanAtlas(2) >>> atlas.default_chart() Cart2D(M=Rn(2))
- has_chart(_: Any, /)#
Return whether the atlas supports the given chart.
>>> import coordinax.manifolds as cxm >>> atlas = cxm.EuclideanAtlas(2) >>> atlas.has_chart(cxc.cart2d) True
>>> atlas.has_chart(cxc.cart3d) False
- default_chart_for(M: AbstractManifold, /)#
Return a default chart from the atlas for the given manifold.
This is a thin convenience wrapper over
self.default_chart()that checks that the manifold’s atlas matches this atlas.>>> import coordinax.manifolds as cxm >>> M = cxm.R2 >>> M.atlas.default_chart_for(M) Cart2D(M=Rn(2))
>>> try: M.atlas.default_chart_for(cxm.R3) ... except ValueError as e: print(e) Atlas EuclideanAtlas(ndim=2) does not match manifold atlas EuclideanAtlas(ndim=3).
- Parameters:
M (
AbstractManifold)- Return type:
AbstractChart[Any,Any,Any]
- final class coordinax.manifolds.EuclideanAtlas(ndim: int)#
Bases:
AbstractAtlasAtlas of coordinate charts for the Euclidean manifold $mathbb{R}^n$.
An atlas $mathcal{A}$ is the collection of compatible charts that together cover a smooth manifold. For the Euclidean manifold $mathbb{R}^n$, the atlas $mathcal{A}_{mathbb{R}^n}$ admits a chart $C = (U, varphi)$ if and only if both of the following hold:
The chart dimensionality matches $n$ (i.e. $varphi$ maps into $mathbb{R}^n$).
The chart class is explicitly registered with {class}`~coordinax.manifolds.EuclideanAtlas` via {meth}`register`, or the chart has a compatible transition map to the default Cartesian chart (detected via
chart.cartesian).
Built-in charts. The following chart classes are registered automatically:
0-D: Cart0D
1-D: Cart1D, Radial1D
2-D: Cart2D, Polar2D
3-D: Cart3D, Cylindrical3D, Spherical3D, LonLatSpherical3D, LonCosLatSpherical3D, MathSpherical3D, ProlateSpheroidal3D
N-D: CartND
Default chart. The atlas provides a canonical Cartesian chart for each dimension: Cart0D through Cart3D for $n leq 3$, and CartND for $n > 3$.
- Parameters:
ndim (
int) – Dimension $n geq 0$ of the Euclidean manifold that this atlas covers.
Examples
Construction
>>> import coordinax.manifolds as cxmd >>> atlas = cxmd.EuclideanAtlas(3) >>> atlas EuclideanAtlas(ndim=3)
>>> atlas.ndim 3
Default chart
The atlas provides a canonical Cartesian chart for each dimension:
>>> atlas.default_chart() Cart3D(M=Rn(3))
>>> cxmd.EuclideanAtlas(2).default_chart() Cart2D(M=Rn(2))
>>> cxmd.EuclideanAtlas(1).default_chart() Cart1D(M=Rn(1))
For $n > 3$ the fallback is CartND:
>>> cxmd.EuclideanAtlas(10).default_chart() CartND(M=Rn(True))
Chart membership
Use the
inoperator (via {meth}`~AbstractAtlas.__contains__`) to test whether a chart instance belongs to this atlas:>>> import coordinax.charts as cxc
>>> cxc.cart3d in atlas True
>>> cxc.sph3d in atlas True
Charts with the wrong dimensionality are rejected:
>>> cxc.cart2d in atlas False
- default_chart()#
Return the default chart for this atlas.
- Return type:
AbstractChart[Any,Any,Any]
Examples
>>> import coordinax.charts as cxc >>> import coordinax.manifolds as cxmd
>>> cxmd.EuclideanAtlas(0).default_chart() Cart0D(M=Rn(0))
>>> cxmd.EuclideanAtlas(1).default_chart() Cart1D(M=Rn(1))
>>> cxmd.EuclideanAtlas(2).default_chart() Cart2D(M=Rn(2))
>>> cxmd.EuclideanAtlas(3).default_chart() Cart3D(M=Rn(3))
For higher dimensions, the default is CartND:
>>> cxmd.EuclideanAtlas(100).default_chart() CartND(M=Rn(True))
- has_chart(chart: AbstractChart[Any, Any, Any], /)#
Return whether the atlas supports the given chart.
This checks if the chart has the same dimension as the atlas and is either explicitly registered or has a common point transition map to the default Cartesian chart.
Examples
>>> import coordinax.charts as cxc >>> import coordinax.manifolds as cxmd
>>> atlas = cxmd.EuclideanAtlas(2)
>>> atlas.has_chart(cxc.cart2d) True
>>> atlas.has_chart(cxc.polar2d) True
>>> atlas.has_chart(cxc.cart3d) False
- Parameters:
chart (
AbstractChart[Any,Any,Any])- Return type:
- default_chart_for(M: AbstractManifold, /)#
Return a default chart from the atlas for the given manifold.
This is a thin convenience wrapper over
self.default_chart()that checks that the manifold’s atlas matches this atlas.>>> import coordinax.manifolds as cxm >>> M = cxm.R2 >>> M.atlas.default_chart_for(M) Cart2D(M=Rn(2))
>>> try: M.atlas.default_chart_for(cxm.R3) ... except ValueError as e: print(e) Atlas EuclideanAtlas(ndim=2) does not match manifold atlas EuclideanAtlas(ndim=3).
- Parameters:
M (
AbstractManifold)- Return type:
AbstractChart[Any,Any,Any]
- classmethod register(registrant: CT, /)#
Register a class for Euclidean Atlas eligibility.
This does not guarantee that a particular atlas will support the chart, since support also depends on dimensionality and transition maps. It simply makes the chart class eligible for support if those other conditions are met.
- final class coordinax.manifolds.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 coordinax.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)
- final class coordinax.manifolds.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
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.
- atlas#
The atlas of coordinate charts compatible with this manifold. Its {attr}`~EuclideanAtlas.ndim` equals
ndim.- Type:
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)
- angle_between(chart: AbstractChart[Any, Any, Any], uvec: dict[str, Any], vvec: dict[str, Any], /, *, at: dict[str, Any], 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).
- 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:
- atlas: AbstractAtlas#
Charts compatible with this manifold. This defines the smooth structure.
- metric: AbstractMetricField#
The manifold’s metric. This defines the geometric structure.
- coordinax.manifolds.Rn#
alias of
EuclideanManifold
- final class coordinax.manifolds.HyperSphericalAtlas(ndim: int = 2)#
Bases:
AbstractAtlasAtlas for spherical manifolds (e.g. the circle or 2-sphere).
E.g. the 2-sphere contains charts:
~coordinax.charts.SphericalTwoSphere,
~coordinax.charts.LonLatSphericalTwoSphere,
~coordinax.charts.LonCosLatSphericalTwoSphere, and
~coordinax.charts.MathSphericalTwoSphere.
Examples
>>> import coordinax.manifolds as cxm >>> import coordinax.charts as cxc
>>> atlas = cxm.HyperSphericalAtlas() >>> atlas.ndim 2
>>> cxc.sph2 in atlas True
>>> cxc.lonlat_sph2 in atlas True
>>> cxc.cart2d in atlas False
>>> atlas.default_chart() SphericalTwoSphere(M=Sn(2))
- Parameters:
ndim (
int)
- default_chart()#
Return the default chart (SphericalTwoSphere) for this atlas.
- Return type:
AbstractChart[Any,Any,Any]
- has_chart(chart: AbstractChart[Any, Any, Any], /)#
Return whether the atlas supports the given chart.
Examples
>>> import coordinax.manifolds as cxm >>> import coordinax.charts as cxc
>>> atlas = cxm.HyperSphericalAtlas() >>> atlas.has_chart(cxc.sph2) True
>>> atlas.has_chart(cxc.cart3d) False
- Parameters:
chart (
AbstractChart[Any,Any,Any])- Return type:
- default_chart_for(M: AbstractManifold, /)#
Return a default chart from the atlas for the given manifold.
This is a thin convenience wrapper over
self.default_chart()that checks that the manifold’s atlas matches this atlas.>>> import coordinax.manifolds as cxm >>> M = cxm.R2 >>> M.atlas.default_chart_for(M) Cart2D(M=Rn(2))
>>> try: M.atlas.default_chart_for(cxm.R3) ... except ValueError as e: print(e) Atlas EuclideanAtlas(ndim=2) does not match manifold atlas EuclideanAtlas(ndim=3).
- Parameters:
M (
AbstractManifold)- Return type:
AbstractChart[Any,Any,Any]
- classmethod register(registrant: CT, /)#
Register a chart class for HyperSphericalAtlas eligibility.
Examples
>>> import coordinax.manifolds as cxm >>> import coordinax.charts as cxc
>>> cxc.SphericalTwoSphere in SPHERICAL_ATLAS_ELIGIBLE_CHARTS True
- final class coordinax.manifolds.RoundMetric(ndim: int = <property object>)#
Bases:
AbstractDiagonalMetricFieldRound metric on the unit $n$-sphere $S^{n-1}$ in standard spherical coordinates.
The round metric on $S^2$ in the $(theta, phi)$ spherical chart is
$$g = begin{pmatrix} 1 & 0 \ 0 & sin^2theta end{pmatrix}.$$
- Parameters:
ndim (
int) – Intrinsic dimension of the sphere (e.g.ndim=2for $S^2$).
Examples
>>> import jax.numpy as jnp >>> import coordinax.api.manifolds as cxmapi >>> import coordinax.charts as cxc >>> import coordinax.manifolds as cxm
>>> m = cxm.RoundMetric(2) >>> m.signature (1, 1) >>> m.ndim 2
The metric matrix is obtained via the dispatch API on the associated manifold:
>>> at = {"theta": jnp.array(jnp.pi / 2), "phi": jnp.array(0.0)} >>> cxmapi.metric_matrix(cxm.S2, at, cxc.sph2).diagonal Array([1., 1.], dtype=float64)
- final class coordinax.manifolds.HyperSphericalManifold(ndim: int = 2, /)#
Bases:
AbstractManifoldThe unit two-sphere $S^2$ as a smooth manifold.
Examples
>>> import coordinax.manifolds as cxm >>> import coordinax.charts as cxc
>>> S2 = cxm.HyperSphericalManifold(2) >>> S2.ndim 2
>>> S2.has_chart(cxc.sph2) True
>>> S2.default_chart() SphericalTwoSphere(M=Sn(2))
- Parameters:
ndim (
int)
- angle_between(chart: AbstractChart[Any, Any, Any], uvec: dict[str, Any], vvec: dict[str, Any], /, *, at: dict[str, Any], 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).
- 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:
- atlas: AbstractAtlas#
Charts compatible with this manifold. This defines the smooth structure.
- metric: AbstractMetricField#
The manifold’s metric. This defines the geometric structure.
- coordinax.manifolds.Sn#
alias of
HyperSphericalManifold
- final class coordinax.manifolds.MinkowskiAtlas(ndim: int = 4)#
Bases:
AbstractAtlasAtlas of coordinate charts for Minkowski spacetime $mathbb{R}^{1,3}$.
An atlas $mathcal{A}$ is the collection of compatible charts that together cover a smooth manifold. For Minkowski spacetime $mathbb{R}^{1,3}$, the atlas $mathcal{A}$ admits a chart $C$ if and only if:
The chart dimensionality is 4.
The chart class is {class}`~coordinax.charts.MinkowskiCT` (or a subclass explicitly registered via {meth}`register`).
Built-in charts:
{class}`~coordinax.charts.MinkowskiCT` — canonical $(ct, x, y, z)$ chart.
- Parameters:
ndim (
int) – Intrinsic dimension of the manifold. Always 4 for Minkowski spacetime.
Examples
>>> import coordinax.manifolds as cxm >>> import coordinax.charts as cxc
>>> atlas = cxm.MinkowskiAtlas() >>> atlas.ndim 4
>>> cxc.minkowskict in atlas True
>>> cxc.cart3d in atlas False
>>> atlas.default_chart() MinkowskiCT(M=MinkowskiManifold(ndim=4))
- default_chart()#
Return the default chart (canonical
MinkowskiCT).- Return type:
AbstractChart[Any,Any,Any]
Examples
>>> import coordinax.manifolds as cxm >>> cxm.MinkowskiAtlas().default_chart() MinkowskiCT(M=MinkowskiManifold(ndim=4))
- has_chart(chart: AbstractChart[Any, Any, Any], /)#
Return whether the chart belongs to this atlas.
A chart belongs when its dimensionality is 4 and its class is {class}`~coordinax.charts.MinkowskiCT` (or another class registered via {meth}`register`).
Examples
>>> import coordinax.manifolds as cxm >>> import coordinax.charts as cxc
>>> atlas = cxm.MinkowskiAtlas()
>>> atlas.has_chart(cxc.minkowskict) True
>>> atlas.has_chart(cxc.cart3d) False
- Parameters:
chart (
AbstractChart[Any,Any,Any])- Return type:
- default_chart_for(M: AbstractManifold, /)#
Return a default chart from the atlas for the given manifold.
This is a thin convenience wrapper over
self.default_chart()that checks that the manifold’s atlas matches this atlas.>>> import coordinax.manifolds as cxm >>> M = cxm.R2 >>> M.atlas.default_chart_for(M) Cart2D(M=Rn(2))
>>> try: M.atlas.default_chart_for(cxm.R3) ... except ValueError as e: print(e) Atlas EuclideanAtlas(ndim=2) does not match manifold atlas EuclideanAtlas(ndim=3).
- Parameters:
M (
AbstractManifold)- Return type:
AbstractChart[Any,Any,Any]
- classmethod register(registrant: CT, /)#
Register a chart class for {class}`MinkowskiAtlas` eligibility.
Examples
>>> import coordinax.charts as cxc >>> import coordinax.manifolds as cxm
>>> cxc.MinkowskiCT in cxm.MinkowskiAtlas._ELIGIBLE_CHARTS True
- final class coordinax.manifolds.MinkowskiMetric#
Bases:
AbstractDiagonalMetricFieldPseudo-Riemannian (Lorentzian) metric on Minkowski spacetime.
In the canonical {class}`~coordinax.charts.MinkowskiCT` chart $(ct, x, y, z)$, the Minkowski metric is
$$eta = operatorname{diag}(-1, 1, 1, 1),$$
where the time coordinate $ct = c,t$ absorbs the speed of light so that all four components carry the same unit (length). The line element is
$$ds^2 = -(d(ct))^2 + dx^2 + dy^2 + dz^2.$$
Signature. The metric is pseudo-Riemannian with Lorentzian signature $(-1, 1, 1, 1)$ meaning one negative and three positive eigenvalues (convention: “mostly plus”).
Examples
>>> import jax.numpy as jnp >>> import coordinax.charts as cxc >>> import coordinax.manifolds as cxm >>> from coordinax.api.manifolds import metric_matrix
Canonical Cartesian spacetime chart:
>>> m = cxm.MinkowskiMetric() >>> M = cxm.MinkowskiManifold() >>> at = {"ct": jnp.array(0.0), "x": jnp.array(0.0), ... "y": jnp.array(0.0), "z": jnp.array(0.0)} >>> metric_matrix(M, at, cxc.minkowskict).diagonal Array([-1., 1., 1., 1.], dtype=float64)
The signature is Lorentzian (pseudo-Riemannian):
>>> m.signature (-1, 1, 1, 1)
>>> m.ndim 4
- final class coordinax.manifolds.MinkowskiManifold(ndim: int = 4, /)#
Bases:
AbstractManifoldMinkowski spacetime $mathbb{R}^{1,3}$.
Minkowski spacetime is the 4-dimensional pseudo-Riemannian manifold $(mathbb{R}^{1,3}, eta)$ equipped with the metric $eta = operatorname{diag}(-1, 1, 1, 1)$ in the canonical $(ct, x, y, z)$ chart. It is the geometric arena of special relativity.
Charts. The manifold admits all charts registered with coordinax.manifolds.MinkowskiAtlas. The built-in chart is
MinkowskiCT— the canonical $(ct, x, y, z)$ Cartesian spacetime chart.Metric. The manifold carries the flat Minkowski metric $eta = operatorname{diag}(-1, 1, 1, 1)$.
Pre-built instance. The module exports
minkowski4das a ready-to-use instance.- Parameters:
ndim (
int) – Intrinsic dimension. Always 4 for Minkowski spacetime.
Examples
>>> import coordinax.manifolds as cxm >>> import coordinax.charts as cxc
>>> M = cxm.MinkowskiManifold() >>> M MinkowskiManifold(ndim=4)
>>> M.ndim 4
>>> M.atlas MinkowskiAtlas(ndim=4)
>>> M.default_chart() MinkowskiCT(M=MinkowskiManifold(ndim=4))
>>> M.has_chart(cxc.minkowskict) True
>>> M.has_chart(cxc.cart3d) False
- angle_between(chart: AbstractChart[Any, Any, Any], uvec: dict[str, Any], vvec: dict[str, Any], /, *, at: dict[str, Any], 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).
- 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:
- atlas: AbstractAtlas#
Charts compatible with this manifold. This defines the smooth structure.
- metric: AbstractMetricField#
The manifold’s metric. This defines the geometric structure.
- final class coordinax.manifolds.CartesianProductAtlas(factors: tuple[AbstractAtlas, ...], factor_names: tuple[str, ...])#
Bases:
AbstractAtlasAtlas for a product manifold.
The atlas consists of Cartesian product charts formed from the atlases of the factor manifolds.
Consider the product manifold $S^2 times \mathbb{R}$, where
$S^2$ is the 2-sphere with spherical coordinates $(theta, \phi)$ and atlas of charts including SphericalTwoSphere.
$mathbb{R}$ is the real line with Cartesian coordinate $x$ and atlas of charts including Cartesian1D.
>>> import coordinax.manifolds as cxm >>> import coordinax.charts as cxc
>>> atlas = cxm.CartesianProductAtlas( ... factors=(cxm.HyperSphericalAtlas(), cxm.EuclideanAtlas(1)), ... factor_names=("S2", "R1")) >>> atlas CartesianProductAtlas(factors=(HyperSphericalAtlas(ndim=2), EuclideanAtlas(ndim=1)), factor_names=('S2', 'R1'))
>>> atlas.ndim 3
>>> chart = cxc.CartesianProductChart( ... factors=(cxc.sph2, cxc.cart1d), factor_names=("S2", "R1")) >>> chart in atlas True
>>> cxc.sph2 in atlas False
>>> atlas["R1"] # Access factor atlas by name EuclideanAtlas(ndim=1)
- factors: tuple[AbstractAtlas, ...]#
Factor atlases that define the product atlas.
- default_chart()#
Return a default chart for the product atlas.
- Return type:
Examples
>>> import coordinax.manifolds as cxm >>> atlas = cxm.CartesianProductAtlas( ... factors=(cxm.HyperSphericalAtlas(), cxm.EuclideanAtlas(1)), ... factor_names=("S2", "R1")) >>> chart = atlas.default_chart() >>> chart CartesianProductChart( factors=(SphericalTwoSphere(M=Sn(2)), Cart1D(M=Rn(1))), factor_names=('S2', 'R1') )
>>> chart["S2"] == cxm.HyperSphericalAtlas().default_chart() True
>>> chart["R1"] == cxm.EuclideanAtlas(ndim=1).default_chart() True
- default_chart_for(M: AbstractManifold, /)#
Return a default chart from the atlas for the given manifold.
This is a thin convenience wrapper over
self.default_chart()that checks that the manifold’s atlas matches this atlas.>>> import coordinax.manifolds as cxm >>> M = cxm.R2 >>> M.atlas.default_chart_for(M) Cart2D(M=Rn(2))
>>> try: M.atlas.default_chart_for(cxm.R3) ... except ValueError as e: print(e) Atlas EuclideanAtlas(ndim=2) does not match manifold atlas EuclideanAtlas(ndim=3).
- Parameters:
M (
AbstractManifold)- Return type:
AbstractChart[Any,Any,Any]
- has_chart(chart: AbstractChart)#
Check if the atlas supports the given chart.
>>> import coordinax.manifolds as cxm >>> import coordinax.charts as cxc >>> atlas = cxm.CartesianProductAtlas( ... factors=(cxm.HyperSphericalAtlas(), cxm.EuclideanAtlas(1)), ... factor_names=("S2", "R1")) >>> chart = cxc.CartesianProductChart( ... factors=(cxc.sph2, cxc.cart1d), factor_names=("S2", "R1")) >>> atlas.has_chart(chart) True
- Parameters:
chart (
AbstractChart)- Return type:
- final class coordinax.manifolds.ProductMetric(factors: tuple[AbstractMetricField, ...])#
Bases:
AbstractMetricFieldCanonical product metric on a Cartesian product manifold.
For factor manifolds $(M_i, g_i)$, the product metric on $M = M_1 times cdots times M_k$ is
$$ g_{(p_1,ldots,p_k)}big((v_1,ldots,v_k),(w_1,ldots,w_k)big) = sum_{i=1}^k g_i(v_i, w_i), $$
which is block diagonal in a product chart.
- Parameters:
factors (
tuple[AbstractMetricField,...])
- factors: tuple[AbstractMetricField, ...]#
Metrics for each factor manifold, in product order.
- final class coordinax.manifolds.CartesianProductManifold(factors: tuple[AbstractManifold, ...], factor_names: tuple[str, ...])#
Bases:
AbstractManifoldManifold defined as a Cartesian product of other manifolds.
Given smooth manifolds $M_1, M_2, ldots, M_k$ of intrinsic dimensions $n_1, n_2, ldots, n_k$, the Cartesian product manifold is
$$M = M_1 times M_2 times cdots times M_k,$$
whose points are $k$-tuples $(p_1, p_2, ldots, p_k)$ with $p_i in M_i$. The product is itself a smooth manifold of dimension
$$dim(M) = n_1 + n_2 + cdots + n_k.$$
Smooth structure. The atlas $mathcal{A}_M$ of the product manifold consists precisely of Cartesian product charts
$$C_1 times C_2 times cdots times C_k, quad C_i in mathcal{A}_{M_i},$$
encoded as {class}`~coordinax.charts.CartesianProductChart` instances. Transition maps on $M$ factor component-wise: if $tau_i : C_i^alpha to C_i^beta$ is the transition map on $M_i$, then the product transition map is
- $$tau(p_1, ldots, p_k) mapsto
bigl(tau_1(p_1), ldots, tau_k(p_k)bigr).$$
Naming and indexing. Each factor is assigned a string name via
factor_names. Names must be unique and are used to retrieve individual factor manifolds or factor atlases from the product atlas via string indexing (e.g.manifold.atlas["S2"]).- Parameters:
- atlas#
The product atlas formed from the factor atlases.
- Type:
- metric#
The canonical product metric formed from the factor metrics.
- Type:
- default_chart#
Product of the default charts from each factor atlas.
- Type:
- Return type:
AbstractChart[Any, Any, Any]
Examples
Basic construction
The most common example is the phase-space-like manifold $S^2 times mathbb{R}$, which pairs the 2-sphere (2 angular degrees of freedom) with the real line (1 radial degree of freedom):
>>> import coordinax.manifolds as cxm >>> import wadler_lindig as wl
>>> M = cxm.CartesianProductManifold( ... factors=(cxm.S2, cxm.R1), factor_names=("S2", "R1") ... ) >>> wl.pprint(M, width=60) CartesianProductManifold( factors=(Sn(2), Rn(1)), factor_names=('S2', 'R1') )
Dimension
The dimension equals the sum of the factor dimensions, $dim(S^2) + dim(mathbb{R}) = 2 + 1 = 3$:
>>> M.ndim 3
Atlas and default chart
The atlas is a {class}`CartesianProductAtlas` whose default chart is the Cartesian product of the default charts of each factor:
>>> M.atlas CartesianProductAtlas(factors=(HyperSphericalAtlas(ndim=2), EuclideanAtlas(ndim=1)), factor_names=('S2', 'R1'))
>>> M.default_chart() CartesianProductChart( factors=(SphericalTwoSphere(M=Sn(2)), Cart1D(M=Rn(1))), factor_names=('S2', 'R1') )
Factor atlases can be retrieved by name:
>>> M.atlas["S2"] HyperSphericalAtlas(ndim=2)
>>> M.atlas["R1"] EuclideanAtlas(ndim=1)
Chart membership
A {class}`~coordinax.charts.CartesianProductChart` belongs to the product atlas when its factor charts belong to the corresponding factor atlases:
>>> import coordinax.charts as cxc
>>> product_chart = cxc.CartesianProductChart( ... factors=(cxc.sph2, cxc.cart1d), factor_names=("S2", "R1") ... ) >>> M.has_chart(product_chart) True
Non-product charts and wrong-factor charts are rejected:
>>> M.has_chart(cxc.sph2) False
Higher-dimensional products
Any number of factors may be combined. The 4-dimensional manifold $S^2 times mathbb{R}^2$ has $dim = 2 + 2 = 4$:
>>> M4 = cxm.CartesianProductManifold( ... factors=(cxm.S2, cxm.R2), factor_names=("S2", "R2") ... ) >>> M4.ndim 4
Euclidean-Euclidean products
Factor manifolds need not be non-Euclidean. The product $mathbb{R}^2 times mathbb{R}$ reproduces 3-dimensional Euclidean space (though as a product structure rather than a single EuclideanManifold):
>>> Mprod = cxm.CartesianProductManifold( ... factors=(cxm.R2, cxm.R1), factor_names=("xy", "z") ... ) >>> Mprod.ndim 3
>>> Mprod.default_chart() CartesianProductChart( factors=(Cart2D(M=Rn(2)), Cart1D(M=Rn(1))), factor_names=('xy', 'z') )
- factors: tuple[AbstractManifold, ...]#
- property atlas: CartesianProductAtlas#
Return the product atlas for the manifold.
>>> import coordinax.manifolds as cxm >>> import wadler_lindig as wl
>>> M = cxm.CartesianProductManifold( ... factors=(cxm.S2, cxm.R1), factor_names=("S2", "R1")) >>> wl.pprint(M.atlas, width=60) CartesianProductAtlas( factors=(HyperSphericalAtlas(), EuclideanAtlas(ndim=1)), factor_names=('S2', 'R1') )
- property metric: ProductMetric#
Return the canonical product metric from the factor metrics.
- angle_between(chart: AbstractChart[Any, Any, Any], uvec: dict[str, Any], vvec: dict[str, Any], /, *, at: dict[str, Any], 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).
- 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:
- class coordinax.manifolds.AbstractEmbeddingMap#
Bases:
Generic[IntrinsicT,AmbientT]Abstract base class representing a smooth embedding.
An embedding represents a smooth injective map $$ iota : M hookrightarrow N $$ of an intrinsic manifold (with charts in coordinax.charts) into an ambient manifold.
Conceptually, an embedding provides:
A smooth map from intrinsic coordinates
q^ito ambient coordinatesx^a = x^a(q)via embed.A (possibly local) inverse or projection map from ambient coordinates back to intrinsic coordinates via project.
Examples
A concrete example is the embedding of
SphericalTwoSphereintoSpherical3D: the intrinsic coordinates may be(θ, φ)on the unit 2-sphere, while the ambient coordinates are(r, θ, φ)with fixed radiusr = R. A concrete subclass can therefore:Map
(θ, φ) ↦ (R, θ, φ)inSpherical3Dvia embed.Drop the radial component via project.
Realize to Cartesian coordinates by first embedding into
Spherical3Dand then delegating to its Cartesian realization.
Subclasses are responsible for implementing the coordinate-level maps; higher-level metric machinery (e.g. induced metrics) can be built on top of this interface.
- intrinsic: IntrinsicT#
- ambient: AmbientT#
- abstractmethod embed(point: dict[str, Any], /, *, usys: AbstractUnitSystem | None = None)#
Embed intrinsic coordinates into ambient coordinates.
- final class coordinax.manifolds.CustomEmbeddingMap(intrinsic: IntrinsicT, ambient: AmbientT, embed_fn: EPCallable, project_fn: EPCallable)#
Bases:
AbstractEmbeddingMap[IntrinsicT,AmbientT]A concrete embedding map defined by user-provided functions.
This class allows users to define an embedding by providing custom embed and project functions, without needing to create a new subclass.
- Parameters:
intrinsic (
TypeVar(IntrinsicT, bound=AbstractChart[Any,Any,Any])) – The intrinsic chart.ambient (
TypeVar(AmbientT, bound=AbstractChart[Any,Any,Any])) – The ambient chart.embed_fn (
EPCallable) – A function that takes a point in intrinsic coordinates and returns the corresponding point in ambient coordinates.project_fn (
EPCallable) – A function that takes a point in ambient coordinates and returns the corresponding point in intrinsic coordinates.
- intrinsic: IntrinsicT#
- ambient: AmbientT#
- embed_fn: EPCallable#
- project_fn: EPCallable#
- embed(point: dict[str, Any], /, *, usys: AbstractUnitSystem | None = None)#
Embed intrinsic coordinates into ambient coordinates.
- final class coordinax.manifolds.TwoSphereIn3D(radius: AbstractQuantity | float | int)#
Bases:
AbstractEmbeddingMap[IntrinsicT,AmbientT]Embedding of
cxc.SphericalTwoSphereas a 2-sphere in a 3D ambient chart.This embedding models a 2-sphere of fixed radius $R$ as the hypersurface $r = R$ in 3D spherical coordinates $(r, theta, phi)$. The intrinsic chart is therefore expected to have components $(theta, phi)$.
The key design choice is that all coordinate-level embedding and projection operations are defined via an intermediate 3D spherical chart ({class}`~coordinax.charts.Spherical3D`), regardless of which ambient chart is selected. In particular:
If
ambientis {class}`~coordinax.charts.Spherical3D, then {meth}`embed` returns spherical coordinates(r, theta, phi)and {meth}`project` expects the same.If
ambientis {class}`~coordinax.charts.Cart3D, then {meth}`embed` performsSphericalTwoSphere -> Spherical3D -> Cart3Dand returns Cartesian coordinates(x, y, z); {meth}`project` performsCart3D -> Spherical3D -> SphericalTwoSphere.
- Parameters:
radius (
AbstractQuantity|float|int) – Sphere radiusR.ambient – Ambient chart. Defaults to {class}`~coordinax.charts.Spherical3D.
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)
Embed/project through an ambient {class}`~coordinax.charts.Cart3D chart (routing via {class}`~coordinax.charts.Spherical3D internally):
>>> chart = cxm.EmbeddedChart(cxm.TwoSphereIn3D(radius=u.Q(2.0, "km"))) >>> xyz = cxm.pt_embed(p, chart) >>> p3 = cxm.pt_project(xyz, chart) >>> p3
{‘theta’: Angle(1.57079633, ‘rad’), ‘phi’: Angle(0., ‘rad’)}
>>> bool(jnp.allclose(u.ustrip("rad", p3["phi"]), u.ustrip("rad", p["phi"]))) True
- radius: AbstractQuantity | float | int#
- property intrinsic: AbstractChart[Any, Any, Any]#
The intrinsic chart is always coordinax.charts.SphericalTwoSphere.
- property ambient: AbstractChart[Any, Any, Any]#
The ambient chart is always coordinax.charts.Spherical3D.
- coordinax.manifolds.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):
>>> manifold = cxm.embedded_twosphere(radius=u.Q(2.0, "km")) >>> manifold
- EmbeddedManifold(intrinsic=HyperSphericalManifold(…),
ambient=Rn(3), embed_map=TwoSphereIn3D(radius=Q(2., ‘km’)))
>>> p = {"theta": u.Angle(jnp.pi / 2, "rad"), "phi": u.Angle(0.0, "rad")} >>> sph = cxm.pt_embed(p, manifold) >>> sph {'r': Q(2., 'km'), 'theta': Angle(1.57079633, 'rad'), 'phi': Angle(0., 'rad')}
With Cartesian ambient:
>>> manifold = cxm.embedded_twosphere(
… radius=u.Q(2.0, “km”), ambient=cxc.cart3d, … ) >>> xyz = cxm.pt_embed(p, manifold) >>> p3 = cxm.pt_project(xyz, manifold) >>> p3 {‘theta’: Angle(1.57079633, ‘rad’), ‘phi’: Angle(0., ‘rad’)}
- final class coordinax.manifolds.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[str, Any], from_intrinsic_chart: AbstractChart[Any, Any, Any], to_ambient_chart: AbstractChart[Any, Any, Any], /, *, usys: AbstractUnitSystem | None = None)#
- project(ambient_point: dict[str, Any], from_ambient_chart: AbstractChart[Any, Any, Any], to_intrinsic_chart: AbstractChart[Any, Any, Any], /, *, usys: AbstractUnitSystem | None = None)#
- property metric: PullbackMetric#
Induced (pullback) Riemannian metric from the ambient manifold.
- property atlas: AbstractAtlas#
- angle_between(chart: AbstractChart[Any, Any, Any], uvec: dict[str, Any], vvec: dict[str, Any], /, *, at: dict[str, Any], 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).
- 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:
- final class coordinax.manifolds.EmbeddedChart(embed_map: AbstractEmbeddingMap[IntrinsicT, AmbientT])#
Bases:
AbstractChart[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'))
- 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')))
- 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:
data (
TypeVar(CDictT, bound=dict[str,Any])) – The data to check.keys (
bool) – Whether to check that the keys of data match chart.components. If False, this check is skipped. Default is True.values (
bool) – Whether to check that the dimensions of the values in data match chart.coord_dimensions. If False, this check is skipped. Default is False.
- Return type:
- final class coordinax.manifolds.PullbackMetric(embed_map: AbstractEmbeddingMap, ambient_metric: AbstractMetricField)#
Bases:
AbstractMetricFieldPullback metric induced by an embedding map.
Given an embedding $iota : N hookrightarrow M$, the metric $g_N$ on the submanifold is the pullback of the ambient metric $g_M$:
- $$g_N = iota^* g_M, quad text{or component-wise}quad
(g_N)_{ij} = (J^T G J)_{ij},$$
where $J = partial iota / partial q$ is the Jacobian of the embedding map and $G = g_M$ is the ambient metric evaluated at $iota(p)$.
- Parameters:
embed_map (
AbstractEmbeddingMap) – The embedding map from the submanifold into the ambient space.ambient_metric (
AbstractMetricField) – The Riemannian metric on the ambient manifold.
Examples
>>> import jax.numpy as jnp >>> import unxt as u >>> import coordinax.api.manifolds as cxmapi >>> import coordinax.charts as cxc >>> import coordinax.manifolds as cxm
>>> embed_map = cxm.TwoSphereIn3D(radius=u.Q(1.0, "km")) >>> ambient_metric = cxm.FlatMetric(3) >>> M = cxm.PullbackMetric(embed_map, ambient_metric) >>> M.signature (1, 1) >>> M.ndim 2
The metric matrix is obtained via the dispatch API on an
EmbeddedManifold:>>> M_emb = cxm.EmbeddedManifold( ... intrinsic=cxm.S2, ambient=cxm.R3, ... embed_map=cxm.TwoSphereIn3D(radius=u.Q(1.0, "km")), ... ) >>> at = {"theta": u.Q(jnp.pi / 2, "rad"), "phi": u.Q(0.0, "rad")} >>> g = cxmapi.metric_matrix(M_emb, at, cxc.sph2) >>> g.matrix.value Array([[1., 0.], [0., 1.]], dtype=float64, weak_type=True) >>> g.matrix.unit[0, 0] Unit("km2 / rad2")
- embed_map: AbstractEmbeddingMap#
- ambient_metric: AbstractMetricField#
- final class coordinax.manifolds.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]
- default_chart_for(M: AbstractManifold, /)#
Return a default chart from the atlas for the given manifold.
This is a thin convenience wrapper over
self.default_chart()that checks that the manifold’s atlas matches this atlas.>>> import coordinax.manifolds as cxm >>> M = cxm.R2 >>> M.atlas.default_chart_for(M) Cart2D(M=Rn(2))
>>> try: M.atlas.default_chart_for(cxm.R3) ... except ValueError as e: print(e) Atlas EuclideanAtlas(ndim=2) does not match manifold atlas EuclideanAtlas(ndim=3).
- Parameters:
M (
AbstractManifold)- Return type:
AbstractChart[Any,Any,Any]
- 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.manifolds.CustomMetric(metric_matrix: ~collections.abc.Callable[[...], ~typing.Any], signature: tuple[int, ...] = <property object>)#
Bases:
AbstractMetricFieldMetric for a {class}`CustomManifold`, defined by user-provided callables.
CustomMetricallows users to supply their own metric without subclassingAbstractMetricField. Both the metric-matrix callable and the signature must be provided at construction time.- Parameters:
metric_matrix (
Callable[...,Any]) – Callable(chart, /, *, at) -> Arrayreturning the $(n times n)$ metric matrix at the given base point.signature (
tuple[int,...]) – Metric signature as a length-$n$ tuple of+1(positive eigenvalue) and-1(negative eigenvalue) entries. Use(1,) * nfor a Riemannian metric of dimension $n$.
Examples
>>> import jax.numpy as jnp >>> import coordinax.charts as cxc >>> import coordinax.manifolds as cxm
>>> def flat_3d(chart, /, *, at): ... return jnp.eye(3)
>>> atlas = cxm.CustomAtlas( ... charts=(cxc.Cart3D,), ... chart_default=cxc.cart3d, ... ) >>> metric = cxm.CustomMetric(metric_matrix=flat_3d, signature=(1, 1, 1)) >>> metric.signature (1, 1, 1) >>> metric.ndim 3
- final class coordinax.manifolds.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.
- metric: AbstractMetricField#
Riemannian metric for this manifold, used for norm and distance computations.
- angle_between(chart: AbstractChart[Any, Any, Any], uvec: dict[str, Any], vvec: dict[str, Any], /, *, at: dict[str, Any], 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).
- 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: