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.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

jittermap-0.1.0.tar.gz (418.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

jittermap-0.1.0-py3-none-any.whl (435.7 kB view details)

Uploaded Python 3

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

Hashes for jittermap-0.1.0.tar.gz
Algorithm Hash digest
SHA256 0333bd238f4d667ed56a78eb9dac49f265ea605ff4c3d80e855d4094369e8b13
MD5 1a237f635c2300804c25ced91d3fa2f5
BLAKE2b-256 1fbb91338b51acfbc5b69fc4e91893eca75df3e2dfaf7c52ad204adcbd38a88b

See more details on using hashes here.

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

Hashes for jittermap-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 77ce31e8eb5802a2fac8113eab1faaf189a3457b5b7dfaeecfd91adb10410475
MD5 8dbc5757f845b071cec0f9c5278f85c4
BLAKE2b-256 3569527f12aeee504b721fdf67febbad3dcd268d50b652628cd4c52850e0c753

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 files

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