Skip to main content

jittermap

PyPI License: GPL v3 Documentation Status

Documentation: jittermap.readthedocs.io

The sunspot groups of Carrington rotation 2156 mapped onto a young active Sun, rotating at three labeled inclinations

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.

Truth vs joint, astrometry-only and photometry-only reconstructions

Tutorials and documentation

Five executed notebook tutorials in notebooks/ walk through the library and reproduce the key results of the paper:

  1. Surfaces: spherical-harmonic representation, analytic cap spots, GMRF textures, rendering at different inclinations.
  2. Forward model: astrometric jitter and photometric signals vs. inclination and spot latitude (paper Figs. 2–3), including the pole-on circularization worst case.
  3. Kernels: the odd/even selection rules, and a machine-precision demonstration of the photometric null space that astrometry breaks.
  4. Reconstruction: joint vs. single-channel MAP inversion, noise and regularization, and the identifiable subspace.
  5. Inclination & Fourier: the profile objective (photometric inclination ambiguity made visible) and lossless frequency-comb compression.
  6. 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.

Metadata

Release files for jittermap 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for jittermap 0.1.0
File Size Uploaded Provenance Actions
jittermap-0.1.0.tar.gz 418.5 kB Without provenance Details for jittermap-0.1.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for jittermap 0.1.0
File Interpreter ABI Platform Actions
jittermap-0.1.0-py3-none-any.whl Python 3 none any Details for jittermap-0.1.0-py3-none-any.whl

Total release size: 854.2 kB

Release files / jittermap-0.1.0.tar.gz

Download URL jittermap-0.1.0.tar.gz
Size 418.5 kB
Tags Source
SHA-256 checksum
How to use checksums
0333bd238f4d667ed56a78eb9dac49f265ea605ff4c3d80e855d4094369e8b13
BLAKE2b-256 checksum
How to use checksums
1fbb91338b51acfbc5b69fc4e91893eca75df3e2dfaf7c52ad204adcbd38a88b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.13

Release files / jittermap-0.1.0-py3-none-any.whl

Download URL jittermap-0.1.0-py3-none-any.whl
Size 435.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
77ce31e8eb5802a2fac8113eab1faaf189a3457b5b7dfaeecfd91adb10410475
BLAKE2b-256 checksum
How to use checksums
3569527f12aeee504b721fdf67febbad3dcd268d50b652628cd4c52850e0c753
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.13

Security for jittermap 0.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page