About empyrean

empyrean is a Python toolkit for high-precision Solar System dynamics — trajectory propagation, ephemeris generation, orbit determination, and event analysis (close approaches, occultations, eclipses, sphere-of-influence crossings) on real Solar System bodies. It pairs a friendly, columnar Python surface (PyArrow / quivr tables, dataclass configs, type-hinted everything) with a Rust engine that delivers production-grade numerics at millisecond-class single-orbit performance.

The full pipeline runs in one process: query JPL SBDB or MPC for an orbit, propagate it forward or backward across decades, predict apparent positions for any observatory in the MPC catalog, fit an orbit from astrometric observations, and detect close approaches, occultations, and eclipses along the trajectory — all without leaving Python and without round-tripping to a server.

How it’s built

empyrean composes three specialized Rust libraries into one Python package, each owning one layer of the stack:

Library

What it owns

nolan

Forward-mode automatic differentiation. The Jet1 / Jet2 number types that ride through every position, velocity, and force computation — so state-transition matrices (STMs) and state-transition tensors (STTs) come out as a by-product of integration, no finite differences. This is what makes uncertainty-first propagation possible at production speed.

villeneuve

Numerical propagation. Force models from approximate (point-mass planets only) through standard (planets, sixteen asteroid perturbers, EIH general relativity, Earth zonal harmonics, Marsden non-gravitationals). High-order adaptive integration. Ephemeris generation against the MPC observatory catalog. Event detection — close approaches, occultations, eclipses, sphere-of-influence crossings, atmospheric entry / exit.

scott

Orbit determination. Initial-orbit determination (Herget) and differential correction with adaptive multi-layer outlier rejection, per-station bias fitting, and catalog astrometric debiasing (EFCC2020). Returns the fitted orbit + covariance + per-observation residuals + an acceptability verdict you can gate on.

Each library ships as a proprietary compiled binary; empyrean is the Python integration layer that exposes their behaviour through a single coherent API.

The same engine is the source of truth for the C and CLI distributions of empyrean — every numerical result is bit-identical across empyrean (Python), the C ABI (libempyrean.dylib / libempyrean.so), and the standalone empyrean command-line binary. The Python wheel is just the most ergonomic of the three for notebook-driven and script-driven work.

See it in action

Each scenario below is a real interactive page on empyrean-dynamics.com running the same propagation, OD, and impact-probability machinery this Python package exposes. Open one to scrub through the trajectory, inspect the covariance, and see the close-approach or impact geometry in 3D.

🪨 Apophis 2029

A textbook nonlinear-dynamics encounter — Earth’s gravity field bending the trajectory and amplifying initial-condition uncertainty across a 15-year propagation.

https://empyrean-dynamics.com/scenarios/apophis-2029
🌠 2024 YR4

A recently-discovered NEO with a broad a-priori covariance — sigma-point propagation showing how a virtual-asteroid sample disperses across a multi-year forecast.

https://empyrean-dynamics.com/scenarios/2024-yr4
🔭 2008 TC3

Recovering an orbit from a 19-hour discovery arc — the original October 2008 observations, fit and propagated to atmospheric entry over Sudan.

https://empyrean-dynamics.com/scenarios/2008-tc3
🌑 2024 PT5 (mini-moon)

A temporary Earth co-orbital captured into a near-Earth lasso in 2024 — chaotic dynamics where small initial-condition differences amplify dramatically.

https://empyrean-dynamics.com/scenarios/2024-pt5
🛰️ 67P / Churyumov-Gerasimenko

Rosetta’s target — a Jupiter-family comet with non-zero outgassing forces and a measurable Marsden time-of-perihelion delay.

https://empyrean-dynamics.com/scenarios/67p
🌟 1I / ‘Oumuamua

The first interstellar object — hyperbolic orbit with custom non-gravitational acceleration model.

https://empyrean-dynamics.com/scenarios/1i-oumuamua

The same engine powers each scenario; what changes is the orbit, the force model, and the time horizon.

How the pieces fit together

A typical end-to-end workflow exercises every layer:

import empyrean

empyrean.initialize()

# 1. Pull an orbit from JPL SBDB → CometaryOrbits with covariance.
orbits = empyrean.query_sbdb(["99942"])             # nolan + villeneuve I/O

# 2. Pull historical observations from MPC → ADESObservations.
obs = empyrean.query_observations(["99942"])

# 3. Re-fit the orbit from the observations → ODResult.
fit = empyrean.refine(orbits, obs)                  # scott + villeneuve

# 4. Propagate the fitted orbit forward 10 years → states + events.
result = empyrean.propagate(fit.orbit,
                            fit.orbit.epoch + 10*365.25)
result.events.close_approaches      # one row per CA per body
result.events.occultations          # background-star occultation events
result.events.eclipses              # umbral / penumbral entries

# 5. Predict apparent positions at Mauna Kea → Ephemeris.
eph = empyrean.generate_ephemeris(fit.orbit,
                                  result.states,
                                  observers="568")

For close-encounter analysis (impact probability, B-plane geometry, keyhole structure), see the Close-approach cookbook.

You see one Python API, but each call dispatches into the right combination of nolan (autodiff), villeneuve (numerics), and scott (estimation) underneath. From your perspective they’re a single coherent toolkit; from the engine’s perspective they’re three specialized libraries doing what each does best.