jittermap
Documentation: jittermap.readthedocs.io
Overview
Developed by Jamila Taaki (STScI).
jittermap is a Python library for stellar surface mapping from astrometric jitter and photometry. It is the reference implementation of the methods developed in the paper:
Taaki, J. S., Corrales, L., & Hero, A. O. III (2026), "Using Astrometry to Break Degeneracies in Stellar Surface Mapping", ApJ 1003, 226. doi:10.3847/1538-4357/ae66f7 (arXiv:2601.11737)
The forward model, the astrometric/photometric moment kernels, the Wigner rotation formulation, the identifiability results, and the reconstruction approach implemented here are all derived in that paper. If you use jittermap, or code or results derived from it, in published work, please cite the paper (see Citing below).
Astrometric jitter arises when starspots on a rotating stellar surface move in and out of view, shifting the observed photocenter; this jitter is a noise floor for detecting small exoplanets, but it also carries information about the stellar surface itself. An interactive visualizer of how spot jitter contaminates the true reflex motion of a star with an orbiting planet is available at xiaziyna.github.io/astrometry_visualizer.html.
jittermap implements a linear forward model for the astrometric jitter and photometric signals of a rotating star in a spherical-harmonic coordinate system, together with the inverse problem: reconstructing surface-brightness maps (and estimating the stellar inclination) from those time series. Astrometry and photometry probe complementary halves of the surface: photometry measures even-degree spherical harmonic modes, symmetric about the equator, while astrometry measures odd-degree modes. Their joint use breaks degeneracies inherent to either channel alone.
The forward model factors per spherical-harmonic degree l as
y_c(t) = Re[ B(t) C(beta) a_c,l s_l ], c in {x, y, photometry}
where s are the complex SH surface coefficients, a_c,l are
visible-hemisphere moment kernels, and B(t), C(beta) are Wigner-D
rotation blocks for the spin phase and inclination. Both rotation blocks are
phase-diagonal modulations of a single fixed matrix per degree,
M_l = D^l(pi/2, pi/2, pi/2), which jittermap ships as precomputed numeric
tables up to L = 40, with no symbolic algebra or compilation at runtime.
Performance
All of the heavy math is precomputed and cached. The Wigner rotation tables and the moment kernels ship with the package as exact numeric arrays (derived in 50-digit arithmetic, tabulated to L = 40), so the forward model and the inversion reduce to small dense matrix products at runtime and are exact to double precision.
Measured timings (single CPU core):
| task | time |
|---|---|
| simulate astrometry + photometry (L=10, 100 epochs) | ~20 ms |
| surface fit, known inclination | ~30 ms |
| surface + inclination fit | ~2 s |
| forward model at L=40 | ~0.5 s |
The package depends only on numpy, scipy, sympy, and matplotlib, with no
compilation step. It models the astrometric jitter channel, which
photometry-only mapping tools do not provide. On the photometric channel,
where a direct comparison exists, it assembles the L = 30 forward
operator about 7x faster than starry v1.2 (2.3 ms vs 16 ms, both warm;
see examples/benchmark_vs_starry.py). This throughput is what makes the
reconstruction galleries in the paper and the Monte Carlo studies the
model was built for practical: thousands of candidate surfaces,
inclinations, and noise realizations rather than a single fit.
Installation
pip install jittermap
or, for the development version:
pip install git+https://github.com/xiaziyna/jittermap
Dependencies: numpy, scipy, sympy, matplotlib.
Quickstart
import numpy as np
import jittermap as jm
# A two-spot surface at degree L=10 with light small-scale texture
s_true = jm.multispot_surface([(30, 315, 18.0), (-15, 30, 13.0)], l_max=10,
texture_amplitude=0.002, texture_seed=3)
# Forward model: N uniform samples over one rotation, inclination 0.6 rad
times = np.linspace(0, 2 * np.pi, 100, endpoint=False)
fm = jm.ForwardModel(times, l_max=10)
y = fm.observe(s_true, inclination=0.6, channels="xyp", stacked=False)
# Inverse problem: joint astrometry + photometry MAP reconstruction,
# with the inclination estimated by profile grid search
result = jm.reconstruct(y, times, l_max=10, channels="xyp")
print(result.inclination) # ~0.6
s_hat = result.s_hat
Reconstruction from single channels (channels="xy" for astrometry only,
"p" for photometry only) exposes the null spaces of each: photometry alone
cannot localize spots in latitude against the inclination ambiguity, while
astrometry alone accesses the odd-degree modes photometry misses.
Tutorials and documentation
Five executed notebook tutorials in notebooks/ walk through
the library and reproduce the key results of the paper:
- Surfaces: spherical-harmonic representation, analytic cap spots, GMRF textures, rendering at different inclinations.
- Forward model: astrometric jitter and photometric signals vs. inclination and spot latitude (paper Figs. 2–3), including the pole-on circularization worst case.
- Kernels: the odd/even selection rules, and a machine-precision demonstration of the photometric null space that astrometry breaks.
- Reconstruction: joint vs. single-channel MAP inversion, noise and regularization, and the identifiable subspace.
- Inclination & Fourier: the profile objective (photometric inclination ambiguity made visible) and lossless frequency-comb compression.
- Truncation: a sharp L = 40 surface and its L = 20 truncation produce the same observable signals to a fraction of a percent, so a modest fit degree captures everything the data can see.
The full documentation (theory summary with the paper's equations mapped to
the API, tutorials, examples, API reference) builds with sphinx from
docs/ and is ReadTheDocs-ready (.readthedocs.yaml).
Organization
jittermap ├── jittermap │ ├── harmonics # surface representation │ │ ├── indexing.py # SH indexing, real-surface transform │ │ ├── spots.py # analytic cap starspots (no external deps) │ │ └── surfaces.py # multi-spot maps, GMRF random textures │ ├── forward # surface -> observables │ │ ├── kernels.py # astrometric + photometric moment kernels │ │ ├── wigner.py # Wigner rotation operators (numeric M_l cache) │ │ ├── design.py # design matrices; fast Vandermonde path │ │ ├── fourier.py # frequency-comb compression of uniform sampling │ │ └── build_cache.py # cache builder (python -m jittermap.forward.build_cache) │ ├── inference # observables -> surface │ │ ├── inversion.py # GMRF-regularized MAP / ridge solvers │ │ ├── inclination.py # profile-objective inclination estimation │ │ └── reconstruct.py # high-level joint reconstruction │ ├── plotting │ │ ├── render.py # visible-hemisphere rendering │ │ ├── panels.py # truth-vs-reconstruction comparison figures │ │ └── animate.py # spin animations │ └── data # shipped caches: Wigner M_l to L=40, kernels ├── examples ├── notebooks # executed tutorial notebooks ├── tests └── docs
Precomputed caches
The Wigner tables (data/wigner, ~0.4 MB) are exact: each M_l is computed
from the Wigner sum formula at beta = pi/2 in 50-digit arithmetic and
rounded once to complex128. Kernel tables (data/kernels) cover the
astrometric and photometric moments to l = 40. Higher degrees are computed
on demand and cached per user, or in bulk via
python -m jittermap.forward.build_cache --lmax 60
python -m jittermap.forward.build_cache --photo 60
Tests
python -m pytest tests/
The suite cross-validates the kernels against independent symbolic and brute-force computations, the rotation operators against direct Wigner-D evaluation, the fast design-matrix path against a reference implementation, and end-to-end noiseless recovery on the identifiable subspace.
Citing
jittermap is based on the work in Taaki, Corrales & Hero (2026). If you use this library, any part of its code, or results produced with it in your research, please cite the paper:
Taaki, J. S., Corrales, L., & Hero, A. O. III 2026, "Using Astrometry to Break Degeneracies in Stellar Surface Mapping", The Astrophysical Journal, 1003, 226. doi:10.3847/1538-4357/ae66f7 (arXiv:2601.11737)
@article{Taaki2026jittermap,
author = {Taaki, Jamila S. and Corrales, Lia and Hero, Alfred O., III},
title = {Using Astrometry to Break Degeneracies in Stellar Surface Mapping},
journal = {The Astrophysical Journal},
year = {2026},
volume = {1003},
number = {2},
pages = {226},
doi = {10.3847/1538-4357/ae66f7},
eprint = {2601.11737},
archivePrefix = {arXiv}
}
A machine-readable citation is provided in CITATION.cff;
GitHub's "Cite this repository" button uses it directly. jittermap is GPL-3.0
licensed: derivative works must retain the copyright and license notices.
License
jittermap is released under the GNU General Public License v3.0.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file jittermap-0.1.0.tar.gz.
File metadata
- Download URL: jittermap-0.1.0.tar.gz
- Upload date:
- Size: 418.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0333bd238f4d667ed56a78eb9dac49f265ea605ff4c3d80e855d4094369e8b13
|
|
| MD5 |
1a237f635c2300804c25ced91d3fa2f5
|
|
| BLAKE2b-256 |
1fbb91338b51acfbc5b69fc4e91893eca75df3e2dfaf7c52ad204adcbd38a88b
|
File details
Details for the file jittermap-0.1.0-py3-none-any.whl.
File metadata
- Download URL: jittermap-0.1.0-py3-none-any.whl
- Upload date:
- Size: 435.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
77ce31e8eb5802a2fac8113eab1faaf189a3457b5b7dfaeecfd91adb10410475
|
|
| MD5 |
8dbc5757f845b071cec0f9c5278f85c4
|
|
| BLAKE2b-256 |
3569527f12aeee504b721fdf67febbad3dcd268d50b652628cd4c52850e0c753
|