Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,69 @@ jobs:
run: |
./.venv/bin/pytest -q --maxfail=1 --disable-warnings -ra

docs-snippets:
name: Run documentation Python snippets
runs-on: ubuntu-latest
env:
CARGO_TERM_COLOR: always
RUST_BACKTRACE: 1
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Python 3.12
uses: actions/setup-python@v5
with:
python-version: '3.12'

- name: Setup Rust (stable)
uses: dtolnay/rust-toolchain@stable
with:
toolchain: stable
components: clippy,rustfmt

- name: Cache Rust build
uses: Swatinem/rust-cache@v2
with:
workspaces: |
.
cache-on-failure: true

- name: Create virtualenv (.venv)
run: |
python -m venv .venv
./.venv/bin/python -V

- name: Upgrade pip & install build tools
run: |
./.venv/bin/python -m pip install --upgrade pip wheel setuptools
./.venv/bin/pip install maturin

- name: Build and develop-install py_outfit
run: |
./.venv/bin/maturin develop --release

- name: Show installed package info
run: |
./.venv/bin/python -c "import sys; print('Python:', sys.version)"
./.venv/bin/python -c "import py_outfit as m; print('py_outfit:', getattr(m,'__version__','unknown')); print('from:', m.__file__)"

- name: Run documentation snippets
shell: bash
run: |
set -euo pipefail
shopt -s nullglob
SNIPPETS=(docs/tutorials/tutorial_snippets/*.py)
echo "Found ${#SNIPPETS[@]} snippet(s)."
for f in "${SNIPPETS[@]}"; do
# Skip cached/compiled files just in case (pattern already excludes __pycache__)
case "$f" in
*__pycache__*) continue;;
esac
echo "=== Running $f ==="
./.venv/bin/python "$f"
done

lint:
name: Rust lint (clippy & fmt)
runs-on: ubuntu-latest
Expand Down
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,24 @@ The format is based on Keep a Changelog and this repository follows semantic ver
- `trajectories.rs` — Trajectory batch readers and IOD orchestration helpers.
- `orbit_type/keplerian.rs`, `equinoctial.rs`, `cometary.rs` — Orbital element representations and conversions.
- `lib.rs` — crate entrypoint and Python binding exports.
- Pandas integration: `DataFrame.outfit` accessor in `py_outfit.pandas_pyoutfit` with
`estimate_orbits(...)` and a `Schema` helper for column remapping. Supports both
degrees+arcseconds and radians workflows, and uses a single `Observer` for the set.
- Observatory-aware helpers and estimation methods:
- Trajectory ingestion builders accept an `Observer` (single-station workflow) and
`TrajectorySet.estimate_all_orbits(...)` performs batch IOD using that site.
- Single-object `Observations.estimate_best_orbit(env, params, seed=...)` mirrors the
batch path for a single trajectory.
- Display helpers resolving observatory names via the environment:
`show_with_env`, `table_wide_with_env`, `table_iso_with_env`.
- User documentation:
- New tutorial “IOD from trajectories” (loading from MPC/ADES, NumPy arrays, batch/single IOD).
- New tutorial “Working with orbit results” (inspect `GaussResult`, extract/convert element sets, export).
- New tutorial “Using pandas with pyOutfit” (vectorized IOD from DataFrames via the accessor).
Each tutorial’s code blocks were externalized into standalone, runnable Python snippets under
`docs/tutorials/tutorial_snippets/` and included via the snippets macro.
- CI: Added a job “Run documentation Python snippets” that builds the extension with `maturin
develop` and executes every `docs/tutorials/tutorial_snippets/*.py` to keep examples up to date.

### Python bindings
- Stub type hinting files (`.pyi`) and `py.typed` are included to provide static typing / IDE support:
Expand Down
37 changes: 0 additions & 37 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ crate-type = ["cdylib"]
[dependencies]
outfit = { version = "2.1.0", features = [
"jpl-download",
"progress",
"parallel",
] }
numpy = { version = "0.26.0", default-features = false }
Expand Down
54 changes: 54 additions & 0 deletions docs/api/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# API Reference

This section documents the public Python API exposed by pyOutfit. It describes the main classes, their responsibilities, configuration options, data representations, and how results are structured. The focus is on clarity and practical use within astrometric and orbit-determination workflows.

## What you will find here

- A guided description of the core classes that make up the user-facing API.
- The configuration surface for Initial Orbit Determination (IOD) and related numerical and physical filters.
- The data containers used to ingest, store, and iterate over observations and trajectory batches.
- The different orbital element families and how to interpret their fields and reference epochs.
- The observer model and ephemeris context needed by the solvers.
- Notes on performance, parallel execution, determinism, and error handling.

## Package layout (Python names)

- Environment and context: `PyOutfit` (ephemerides, error model, observatory registry).
- Observers: `Observer` (MPC-coded or custom definitions, geodetic parameters).
- IOD configuration: `IODParams` and its builder for numerical tolerances and execution mode.
- Observations and batches: `Observations` (per-trajectory), `TrajectorySet` (ID → observations mapping).
- IOD results: `GaussResult` (preliminary/corrected solution access, element extraction).
- Orbital elements: `KeplerianElements`, `EquinoctialElements`, `CometaryElements`.
- Pandas helpers: optional utilities for tabular ingestion and export.

## Conventions and units

- Angles are expressed in radians internally. Degree ingestion is supported and converted on input.
- Times are Modified Julian Date (MJD), typically in the TT scale consistent with the ephemerides.
- Distances follow conventions of the underlying Rust core: astronomical units for orbital scales and kilometers for observer elevation.
- All public classes are typed; the package ships with type stubs and a `py.typed` marker for static analysis.
- Errors from the Rust core surface as Python `RuntimeError` with descriptive messages; error variants are flattened for batch results.

## Parallelism and determinism

- Parallel execution is opt-in and controlled through `IODParams`; it is designed for large batches.
- Deterministic runs can be achieved by providing a seed where supported (e.g., batch estimation pathways).
- Heavy numerical work executes outside the Python GIL, minimizing interpreter overhead.

## Navigation

- Python package overview: [py_outfit](py_outfit.md)
- Observers and observatory registry: [observer](observer.md)
- IOD configuration parameters: [iod_params](iod_params.md)
- Gauss Initial Orbit Determination: [iod_gauss](iod_gauss.md)
- Observations container: [observations](observations.md)
- Trajectories and batch processing: [trajectories](trajectories.md)
- Orbital element families:
- [Keplerian](orbit_type/keplerian.md)
- [Equinoctial](orbit_type/equinoctial.md)
- [Cometary](orbit_type/cometary.md)
- Pandas integration notes: [pandas_pyoutfit](pandas_pyoutfit.md)

## Stability and status

The API is designed to be practical and predictable, with an emphasis on scientific correctness. While the project is still evolving, changes aim to preserve user-facing stability where feasible. Type information and documentation are maintained to ease integration into existing pipelines.
4 changes: 4 additions & 0 deletions docs/api/iod_gauss.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@

::: py_outfit.iod_gauss.GaussResult
options:
show_root_heading: true
24 changes: 24 additions & 0 deletions docs/api/iod_params.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# IOD Parameters


::: py_outfit.iod_params.IODParams
options:
show_root_heading: true
heading_level: 2
members_order: source
show_signature: true
show_signature_annotations: true
merge_init_into_class: true
filters:
- "!^_"


::: py_outfit.iod_params.IODParamsBuilder
options:
show_root_heading: true
heading_level: 2
members_order: source
show_signature: true
show_signature_annotations: true
filters:
- "!^_"
1 change: 1 addition & 0 deletions docs/api/observations.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
::: py_outfit.observations.Observations
1 change: 1 addition & 0 deletions docs/api/observer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
::: py_outfit.observer.Observer
2 changes: 2 additions & 0 deletions docs/api/orbit_type/cometary.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@

::: py_outfit.orbit_type.cometary.CometaryElements
3 changes: 3 additions & 0 deletions docs/api/orbit_type/equinoctial.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Equinoctial Elements

::: py_outfit.orbit_type.equinoctial.EquinoctialElements
3 changes: 3 additions & 0 deletions docs/api/orbit_type/keplerian.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Keplerian Elements

::: py_outfit.orbit_type.keplerian.KeplerianElements
3 changes: 3 additions & 0 deletions docs/api/pandas_pyoutfit.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Integration with Pandas

::: py_outfit.pandas_pyoutfit
15 changes: 15 additions & 0 deletions docs/api/py_outfit.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
::: py_outfit.PyOutfit

Physical and astronomical constants exposed by Outfit.

These values are provided in SI units or astronomical conventions

::: py_outfit
options:
show_root_toc_entry: true
show_root_heading: true
show_category_heading: true
show_source: true
members_order: source
filters:
- "!^_"
5 changes: 5 additions & 0 deletions docs/api/trajectories.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
::: py_outfit.trajectories.Key

::: py_outfit.trajectories.PathLike

::: py_outfit.trajectories.TrajectorySet
72 changes: 72 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# pyOutfit

High-performance Python bindings for the Outfit orbit-determination engine. pyOutfit provides a thin, typed interface to the Rust core to perform initial orbit determination, manipulate orbital elements, ingest astrometric observations, and process large batches efficiently.

## What this project is

pyOutfit is a Python package that exposes the Rust Outfit crate through PyO3. It brings robust, numerically stable routines for orbit determination and observation handling to Python workflows while keeping the heavy computation in Rust. The design emphasizes reliability, performance, and a clean user-facing API that integrates well with scientific Python stacks.

## Purpose and scope

The package focuses on initial orbit determination (IOD) based on the classical Gauss method, conversions between orbital element representations, ingestion of astrometric observations from multiple sources, and scalable batch processing. It is intended for researchers and engineers working on astrometry pipelines, moving-object detection, and orbit characterization.

## Core capabilities

- Initial Orbit Determination using the Gauss method, with configurable numerical tolerances and physical filters.
- Multiple orbital element families with conversions: Keplerian, Equinoctial, and Cometary.
- Observation ingestion from common astronomy formats and in-memory arrays with minimal overhead.
- Parallel batch execution for large datasets, with deterministic behavior via seed control.
- Observer management, including MPC-coded observatories and custom definitions.
- Typed Python interface with docstrings, type stubs, and consistent error mapping.

## Architecture at a glance

- Rust core (Outfit crate) implements numerical algorithms, ephemerides access, reference frame transformations, and data structures.
- Python bindings (PyO3) expose high-level classes and functions, keeping data copies and conversions to a minimum.
- Optional parallelism in the Rust layer leverages multi-core systems transparently to Python users.
- The interface is designed to be predictable and stable for integration into existing pipelines.

## Data and models

- Orbital elements: Keplerian, Equinoctial, and Cometary families with consistent units and reference epochs.
- Observations: right ascension, declination, timing, and uncertainties, with support for degrees or radians ingestion paths.
- Reference frames and corrections: precession, nutation, aberration, and observer geometry are handled in the Rust core.
- Ephemerides: planetary positions are obtained from high-accuracy JPL series (e.g., DE440) via the Outfit crate.

## Observation ingestion and batches

- Single-trajectory and multi-trajectory ingestion are both supported.
- Batch containers group observations by trajectory identifier for efficient processing.
- Readers and adapters cover traditional astronomy formats (e.g., MPC 80-column and ADES XML) and tabular data sources (e.g., Parquet), alongside direct NumPy-based ingestion.

## Performance and reliability

- Numerical kernels run in Rust without the Python GIL, minimizing overhead.
- Parallel execution is opt-in to avoid contention on small datasets and can be toggled via configuration.
- Deterministic runs are available by providing a random seed when executing batch estimations.
- Errors from the Rust core are mapped to idiomatic Python exceptions with informative messages.

## Requirements and compatibility

- Python 3.12.
- A recent Rust toolchain matching the Outfit crate minimum supported version.
- Linux (POSIX) is the primary target platform for packaged distributions.
- The package is distributed with type information (py.typed) and Python type stubs.

## Project status

The project is in active development and aims for scientific correctness, clear documentation, and practical performance. The public Python API is designed to be stable where possible; incremental improvements and extensions are expected as the Rust core evolves.

## Documentation map

- Python package overview: [PyOutfit](api/py_outfit.md)
- Observer definitions and utilities: [Observer](api/observer.md)
- IOD parameters and configuration: [IODParams](api/iod_params.md)
- Initial Orbit Determination (Gauss): [IODGauss](api/iod_gauss.md)
- Observations and containers: [Observations](api/observations.md), [Trajectories](api/trajectories.md)
- Orbital element types: [Keplerian](api/orbit_type/keplerian.md), [Equinoctial](api/orbit_type/equinoctial.md), [Cometary](api/orbit_type/cometary.md)
- Pandas integration notes: [Pandas Integration](api/pandas_pyoutfit.md)

## Heritage and licensing

pyOutfit builds on the Outfit Rust crate, which is a modern reimplementation of classical Fortran-based orbit determination approaches. The package is distributed under the CeCILL-C license. See the repository license file for details.
Loading