Uncertainty-first orbit propagation, ephemeris, orbit determination, and event detection for asteroids and comets, powered by automatic differentiation
Project description
empyrean
Uncertainty-first orbit propagation, ephemeris, orbit determination, and event detection for asteroids and comets, powered by automatic differentiation
pip install empyrean
A plain install pulls empyrean together with the B612 Foundation's
pre-packaged SPICE kernels (~640 MB). After installation, the first
call to empyrean.initialize() downloads a small remainder
(moon_pa, EGM2008, jwst_rec — about 370 MB) that isn't available
on PyPI.
What it does
- Propagation — N-body (Sun, planets, Moon, Pluto) with EIH general relativity, Sun J2 and Earth J2–J4 zonal harmonics, 16 asteroid perturbers, and the Marsden non-gravitational model — selectable across Approximate / Basic / Standard force-model tiers (Standard is the default). GR15 and DOP853 integrators.
- Uncertainty — First-order (Jet1) state transition matrices; second-order (Jet2) state transition tensors.
- Ephemeris — RA/Dec, rates, photometry (H–G, H–G₁G₂, H–G₁₂), light time, phase angle, solar elongation, local horizon.
- Orbit determination — Gauss, Herget, and systematic-ranging (admissible region + Manifold of Variations) IOD → N-body differential correction with STM caching and outlier rejection. Validated against
find_orband JPL SBDB. - Events — Close approach (start/end), periapsis, gravitational capture (start/end), shadow entry/exit, atmospheric entry/exit, impact, and possible impact.
Quick start
import empyrean
from empyrean import Epochs, TimeScale
empyrean.download_data() # SPICE kernels, first run only
empyrean.initialize()
# Query SBDB for Apophis and propagate through its 2029 Earth flyby
orbits = empyrean.query_sbdb(["Apophis"])
epochs = Epochs.from_kwargs(mjd=[65000.0], scale=TimeScale.TDB)
result = empyrean.propagate(orbits, epochs)
# Event timeline
for i in range(len(result.events.summary)):
ev = result.events.summary
print(f"{ev.event_type.to_pylist()[i]:25s} "
f"{ev.body.to_pylist()[i]:8s} "
f"MJD {ev.epoch.to_numpy()[i]:.2f}")
Orbit determination
obs, radar = empyrean.read_ades("observations.psv") # (optical, radar)
result = empyrean.determine(obs) # one fit per call
print(
f"converged={result.converged}, "
f"RMS={result.summary.rms_ra_arcsec:.2f}\" RA / "
f"{result.summary.rms_dec_arcsec:.2f}\" Dec"
)
Ephemeris
observers = empyrean.get_observer_states(["W84", "F51"], epochs)
eph = empyrean.generate_ephemeris(orbits, observers)
print(eph.ephemeris.coordinates.lon.to_numpy()) # RA (degrees)
print(eph.ephemeris.coordinates.lat.to_numpy()) # Dec (degrees)
print(eph.ephemeris.mag.to_numpy()) # apparent V magnitude
Uncertainty
from empyrean import UncertaintyMethod
# Second-order: populates STM (6x6) and STT (6x6x6)
result = empyrean.propagate(
orbits, epochs,
uncertainty_method=UncertaintyMethod.SECOND_ORDER,
)
print(result.sensitivity.stms_array().shape) # (N, 6, 6)
print(result.sensitivity.stts_array().shape) # (N, 6, 6, 6)
Impact probability and B-plane geometry
For each detected close approach, you can ask the propagator for an impact-probability assessment or a full B-plane breakdown — and run several uncertainty methods side-by-side on the same encounter:
from empyrean import UncertaintyMethod
ips = empyrean.compute_impact_probabilities(
orbits,
end_epoch=63000.0,
methods=[UncertaintyMethod.FIRST_ORDER, UncertaintyMethod.SECOND_ORDER],
)
ips.epochs.scale # "tdb"
ips.where(ips.method == "second_order").ip_second_order.to_numpy()
ips.ip_linear.to_numpy() # always populated
bps = empyrean.compute_b_planes(orbits, 63000.0, [UncertaintyMethod.SECOND_ORDER])
print(bps.b_dot_t_km.to_numpy()) # B·T (km)
print(bps.b_dot_r_km.to_numpy()) # B·R (km)
print(bps.semi_major_3sig_km.to_numpy()) # 3σ ellipse semi-major
Returns typed ImpactProbabilities and BPlanes quivr tables — one
row per (method × orbit × body) encounter, with the closest-approach
time as an embedded Epochs sub-table so .to_utc() / .to_tdb()
just works.
Data files
empyrean needs a set of SPICE kernels. Most arrive via PyPI as installation dependencies; the remainder download on first use.
From pip (installed automatically with empyrean)
| Package | File | Size |
|---|---|---|
naif-de440 |
de440.bsp |
114 MB |
jpl-small-bodies-de441-n16 |
sb441-n16.bsp |
616 MB |
naif-eop-high-prec |
earth_latest_high_prec.bpc |
5 MB |
naif-eop-historical |
earth_620120_*.bpc |
5 MB |
naif-eop-predict |
earth_*_predict.bpc |
1 MB |
mpc-obscodes |
obscodes_extended.json |
266 KB |
empyrean bundles gm_de440.tpc (12 KB) in the wheel itself. On
initialize(), empyrean stages a symlinked cache at
~/.empyrean/b612-cache/ mapping these paths into the filenames
villeneuve expects.
Downloaded on first initialize()
| File | Size | Source |
|---|---|---|
moon_pa_de440_200625.bpc |
12 MB | NAIF — Moon orientation |
jwst_rec.bsp |
121 MB | NAIF — JWST ephemeris (for JWST observations) |
Any of these can be relocated with EMPYREAN_DATA_DIR, and individual
files can be preset with VILLENEUVE_*_PATH environment variables.
Accuracy
Validated against JPL Horizons, ASSIST (reboundx), and find_orb on
43 objects across 13 dynamical populations (NEOs, MBAs, Trojans, TNOs,
comets, etc.). Sub-meter propagation accuracy on bounded timescales.
See the validation report.
No guarantee of accuracy
empyrean performs numerical computations used in planetary-science and mission-planning contexts. Outputs should not be used as the sole basis for any decision — including but not limited to impact monitoring, mission planning, collision avoidance, or navigation — without independent verification. See the LICENSE file shipped with this package for the full terms.
License
empyrean is dual-licensed:
- Wrapper / binding source code — the Rust API surface, C-ABI bindings, and Python wrapper sources in the main repository — is licensed under the BSD 3-Clause License.
- This Python wheel (and any other pre-compiled binary distribution of empyrean) is licensed under the proprietary Empyrean Binary License. The wheel is free to install and use (including commercial use) but may not be redistributed, modified, reverse-engineered, decompiled, or disassembled.
The BSD-3 grant covers only the binding / integration layers in the public repository. The propagation engine, orbit- determination engine, and automatic-differentiation library are proprietary closed-source components distributed only inside the compiled wheel — the wrapper sources call into them through stable internal APIs but do not contain their implementations. Cloning the repository will not let you build a working empyrean from source; install the published wheel.
Copyright © 2024–2026 Joachim Moeyens. All rights reserved.
Links
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distributions
Built Distributions
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 empyrean-0.7.0rc1-cp312-cp312-manylinux_2_34_x86_64.whl.
File metadata
- Download URL: empyrean-0.7.0rc1-cp312-cp312-manylinux_2_34_x86_64.whl
- Upload date:
- Size: 7.1 MB
- Tags: CPython 3.12, manylinux: glibc 2.34+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
834f45cae26da394496668bde8db8f41299763fb9811ee03d6fa302534294a41
|
|
| MD5 |
ad77f4c9ebad46daeb07beaee317e627
|
|
| BLAKE2b-256 |
c8c6558d976936649434f1e98bc33a09e610b842920efaad7dab2a0a4d1f1241
|
Provenance
The following attestation bundles were made for empyrean-0.7.0rc1-cp312-cp312-manylinux_2_34_x86_64.whl:
Publisher:
release.yml on Empyrean-Dynamics/empyrean
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
empyrean-0.7.0rc1-cp312-cp312-manylinux_2_34_x86_64.whl -
Subject digest:
834f45cae26da394496668bde8db8f41299763fb9811ee03d6fa302534294a41 - Sigstore transparency entry: 1928550518
- Sigstore integration time:
-
Permalink:
Empyrean-Dynamics/empyrean@7fcbb3700925ea79d47ba78737ff00cf14798a37 -
Branch / Tag:
refs/tags/v0.7.0-rc.1 - Owner: https://github.com/Empyrean-Dynamics
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@7fcbb3700925ea79d47ba78737ff00cf14798a37 -
Trigger Event:
push
-
Statement type:
File details
Details for the file empyrean-0.7.0rc1-cp312-cp312-macosx_11_0_arm64.whl.
File metadata
- Download URL: empyrean-0.7.0rc1-cp312-cp312-macosx_11_0_arm64.whl
- Upload date:
- Size: 6.5 MB
- Tags: CPython 3.12, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f3635b6e94912d6b742c4a482b725719db7296a366f25e15ee63b40bde01e153
|
|
| MD5 |
6829c17d81b410c1f9185909b5c8dec5
|
|
| BLAKE2b-256 |
02bda7fabce87eb0e3e146cbd8d7cfa5a889f2dbc00f065bb73b8ce228ad4890
|
Provenance
The following attestation bundles were made for empyrean-0.7.0rc1-cp312-cp312-macosx_11_0_arm64.whl:
Publisher:
release.yml on Empyrean-Dynamics/empyrean
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
empyrean-0.7.0rc1-cp312-cp312-macosx_11_0_arm64.whl -
Subject digest:
f3635b6e94912d6b742c4a482b725719db7296a366f25e15ee63b40bde01e153 - Sigstore transparency entry: 1928550372
- Sigstore integration time:
-
Permalink:
Empyrean-Dynamics/empyrean@7fcbb3700925ea79d47ba78737ff00cf14798a37 -
Branch / Tag:
refs/tags/v0.7.0-rc.1 - Owner: https://github.com/Empyrean-Dynamics
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@7fcbb3700925ea79d47ba78737ff00cf14798a37 -
Trigger Event:
push
-
Statement type: