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 |
|---|---|
Forward-mode automatic differentiation. The |
|
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. |
|
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.
A textbook nonlinear-dynamics encounter — Earth’s gravity field bending the trajectory and amplifying initial-condition uncertainty across a 15-year propagation.
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.
Recovering an orbit from a 19-hour discovery arc — the original October 2008 observations, fit and propagated to atmospheric entry over Sudan.
A temporary Earth co-orbital captured into a near-Earth lasso in 2024 — chaotic dynamics where small initial-condition differences amplify dramatically.
Rosetta’s target — a Jupiter-family comet with non-zero outgassing forces and a measurable Marsden time-of-perihelion delay.
The first interstellar object — hyperbolic orbit with custom non-gravitational acceleration model.
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.