Source code for skyscapes.io._frames
"""Frame conversions for ExoVista-style barycentric -> sky-plane rotations.
Ports ``exoverses.util.misc.gen_rotate_to_sky_coords`` (convention
``"exovista"``) to JAX-friendly degrees-in-floats inputs. The math is
identical: rotate by ``-inclination`` around X, then by ``+position_angle``
around Z, then flip the Z sign so that the resulting frame has +Z toward
the observer.
"""
from __future__ import annotations
import jax.numpy as jnp
from jaxtyping import Array
[docs]
def _rot_x(angle_rad: float) -> Array:
"""3x3 rotation matrix around the X axis."""
c = jnp.cos(angle_rad)
s = jnp.sin(angle_rad)
return jnp.array(
[
[1.0, 0.0, 0.0],
[0.0, c, -s],
[0.0, s, c],
]
)
[docs]
def _rot_z(angle_rad: float) -> Array:
"""3x3 rotation matrix around the Z axis."""
c = jnp.cos(angle_rad)
s = jnp.sin(angle_rad)
return jnp.array(
[
[c, -s, 0.0],
[s, c, 0.0],
[0.0, 0.0, 1.0],
]
)
[docs]
def rotate_to_sky_coords(
vectors: Array,
inc_deg: float,
pa_deg: float,
) -> Array:
"""Rotate ``Nx3`` barycentric vectors into the sky frame.
Matches ``exoverses.util.misc.gen_rotate_to_sky_coords`` with
``convention="exovista"``:
1. Rotate by ``-inc_deg`` around the X axis.
2. Rotate by ``+pa_deg`` around the Z axis.
3. Flip the Z component sign.
Args:
vectors: ``(N, 3)`` array of barycentric position or velocity
vectors.
inc_deg: System midplane inclination in degrees.
pa_deg: System midplane position angle in degrees.
Returns:
``(N, 3)`` array of vectors expressed in the sky frame.
"""
inc_rad = jnp.deg2rad(inc_deg)
pa_rad = jnp.deg2rad(pa_deg)
# Apply rotations: vectors are row vectors (N, 3); right-multiply by R^T,
# i.e. v_out = v_in @ R^T == (R @ v_in^T)^T.
rotated = vectors @ _rot_x(-inc_rad).T
rotated = rotated @ _rot_z(pa_rad).T
# Flip the Z axis (matches exoverses' final ``vector[:, 2] = -vector[:, 2]``).
rotated = rotated.at[:, 2].multiply(-1.0)
return rotated