Metadata-Version: 2.4
Name: pympc
Version: 2.0.0
Summary: Perform checks for the presence of moving Solar-system bodies at astronomical locations for a given epoch
Author: Joe Lyman
License: GNU General Public License v3 (GPLv3)
Project-URL: Homepage, https://github.com/lyalpha/pympc
Project-URL: Repository, https://github.com/lyalpha/pympc
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE.txt
Requires-Dist: ephem>=4.1
Requires-Dist: astropy>=4.2
Requires-Dist: numpy>=1.18
Requires-Dist: pandas>=1.0
Requires-Dist: pyerfa>=2.0.0.0
Requires-Dist: requests>=2.20
Requires-Dist: rich>=14.0.0
Requires-Dist: rich-click>=1.8.9
Requires-Dist: loguru>=0.7.0
Requires-Dist: platformdirs>=4.0.0
Requires-Dist: zstandard>=0.25.0
Dynamic: license-file

[![ascl](https://img.shields.io/badge/ascl-2501.002-blue.svg?colorB=262255)](https://ascl.net/2501.002)
[![arXiv](https://img.shields.io/badge/arXiv-2603.02330-b31b1b.svg)](https://arxiv.org/abs/2603.02330)

pympc
=====

Perform checks for the presence of moving Solar-system bodies at astronomical locations for a given epoch.

![Matches demo](docs/assets/matches_demo.svg)

## Installation

Install from PyPI:

```bash
pip install pympc
```

Or using [uv](https://docs.astral.sh/uv/):

```bash
uv pip install pympc
```

### Development Installation

Clone the repository and install in editable mode:

```bash
git clone https://github.com/lyalpha/pympc.git
cd pympc
uv sync --dev
```

## Setup

### Fetching the orbital elements catalogue

First, if we want to search for minor bodies, we need to grab the orbital elements catalogue from the Minor
Planet Center. This must be done at least once prior to any searches and can be run to overwrite the catalogues
with the latest versions. The default call signature is shown for both the Python API and command line interface:
**Python API**
```python
import pympc
xephem_cat =pympc.update_catalogue()
print(xephem_cat)
# e.g. /home/[user]/.cache/pympc/xephem/xephem_astorb.csv
```
**Command line**
```bash
pympc catalogue update
```

The base asteroid catalogue downloaded by default will be Lowell Observatory's
[`astorb.dat`](https://ftp.lowell.edu/pub/elgb/astorb.dat.gz) catalogue.

The Minor Planet Center Near Earth Asteroid and Comets catalogues will be downloaded and used to update the
base asteroid entries based on the values of the `include_nea` and `include_comets` arguments (both default to `True`).

To use the MPC orbital catalogue as the base asteroid source instead, pass
`source="mpcorb"` to `pympc.update_catalogue()`.

It will create a csv file in the [xephem database format](http://www.clearskyinstitute.com/xephem/help/xephem.html#mozTocId468501) and return the filepath to this file.
Filenames follow the pattern `xephem_{source}.csv`. By default, the file will be saved in the user's cache
directory - this can be changed by setting the `cat_dir` argument.

The catalogue should be updated periodically to ensure the most accurate positions are calculated, see
[Limitations](#Limitations) for more details.

Downloading a minor-body catalogue isn't necessary if you just want to do major body checks (i.e. planets, moons).

### Fetching observatory information

The Minor Planet Center provides a list of observatory codes and their coordinates via their [Observatory
Codes API](https://www.minorplanetcenter.net/mpcops/documentation/obscodes-api/). The first time a minor body search is performed (or `get_observatory_data()` is called), this list will be downloaded and cached locally for all future use.
The cache is stored in the OS-specific user-cache directory.

To explicitly refresh the cache with the latest MPC data, call from Python:

```python
import pympc
pympc.update_obscode_cache()
```

Or from the command line:

```bash
pympc update-obscode-cache
```

> **Note**: You only need to refresh the cache if you expect recently added or renamed observatories to be available. The cache persists between sessions and across package upgrades.

## Usage

After downloading a catalogue (see [Fetching the orbital elements catalogue](#Fetching the orbital elements catalogue)),
we can search for both major and minor bodies, and planet Hill sphere intersections at a given location.

## CLI

Installation of the package creates a `pympc` command with the following structure:

![pympc CLI demo](docs/assets/cli_demo.svg)

Use `pympc [subcommand] --help` to get more information on the usage of each subcommand.

The command `pympc catalogue update` should be run before searching, to fetch catalogues and generate xephem output.


### Interactive searching

Within an interpretor session, define a search location, epoch and radius and run the search.

```python
import astropy.units as u
import pympc
from astropy.time import Time

ra = 230.028 * u.deg
dec = -11.774 * u.deg
epoch = Time("2019-01-01T00:00")
search_radius = 5 * u.arcmin
observatory = 950  # equivalently pass as "950" or "La Palma" or (342.1176 0.87764 +0.47847)
pympc.minor_planet_check(ra, dec, epoch, search_radius, observatory=observatory)
```

Results are returned as an astropy table.

The above example uses explicit quantities, but if passed simple float arguments, and the program will assume the
units (see comments below and `pympc.minor_planet_check()` docstring for unit assumptions).
```python
import pympc
ra = 230.028  # assumed degrees
dec = -11.774  # assumed degrees
epoch = 58484.  # assumed MJD
search_radius = 30  # assumed arcseconds
observatory = 950  # equivalently pass as "950" or "La Palma" or (342.1176 0.87764 +0.47847)
pympc.minor_planet_check(ra, dec, epoch, search_radius, observatory=observatory)
```

By default, the search will use an existing xephem catalogue matching the requested `source` in the `pympc` cache directory.
If the file has been moved - or a custom `cat_dir` was passed to `pympc.update_catalogue()` - then the filepath can be specified.

```python
import pympc
pympc.minor_planet_check(
    ra=230.028,
    dec=-11.774,
    epoch=58484.,
    search_radius=30,
    xephem_filepath='/path/to/xephem_astorb_20260424.csv'
)
```

To use the latest generated MPC-based catalogue without explicitly passing a filepath, provide the source and directory:

```python
import pympc
pympc.minor_planet_check(
    ra=230.028,
    dec=-11.774,
    epoch=58484.,
    search_radius=30,
    cat_dir='/path/to/catalogues',
    source='mpcorb',
)
```

The search will by default search both major and minor bodies. These can be toggled via the boolean arguments
`include_minor_bodies` and `include_major_bodies` arguments.

### Adding logging to interactive use

The package is silent by default. Call

```python
import pympc
pympc.add_logging(level="INFO")  # or "DEBUG"/"WARNING"/"ERROR"
```

to enable logging to stdout.

### Defining an observer

By default, if the `observatory` argument is not passed, the program will return geocentric coordinates. However, for
relatively nearby objects like minor bodies, there can be signicant parallax introduced by the location of an observer
on the Earth's surface. For this reason it is crucial to pass either an
[observatory code](https://www.minorplanetcenter.net/iau/lists/ObsCodes.html), an IAU-recognised name of an observatory,
or a tuple containing the observatory information. See the documentation for `pympc.minor_planet_check()` for more details.


### Major bodies (planets, moons)

Major bodies (those with a `ephem.[body]` object in the `pyephem` package) can be included in the search by adding
the `include_major_bodies` argument to `minor_planet_check()` when doing interactive searches, or by using
`pympc check major ...` / `pympc check all ...` at the command line.

Additionally, `pympc` can perform a Hill sphere check for a position, to ensure it is not within the gravitational
sphere of influence of a planet. This is done by calling `planet_hill_sphere_check()` interactively, or adding
`pympc check hillshpere ...` at the command line.

#### Example of a minor Jovian moon

This object is not in the major body catalogue of `pyephem`, but does provide a match to Jupiter's Hill sphere.

![Minor Jovian Moon demo](docs/assets/jovian_moon_demo.svg)

## Speed and multiprocessing
The major body check takes much less than one second. The minor body check should take of order a few seconds,
depending on multiprocessing capabilities.

The private function which actually performs the calculation is `pympc.pympc._minor_planet_check()` (note leading underscore).
This can be called directly, to avoid the small overhead associated with converting input arguments in `pympc.minor_planet_check()`,
if you provide them directly as required (see `pympc.pympc._minor_planet_check()` docstring). Note that in this case a list of
tuples is returned, rather than an astropy table. Equivalently, `pympc.pympc._planet_hill_sphere_check()` exists.

By default, the program does calculate positions of bodies in the catalogue multiprocessed. To switch this off set
`chunk_size = 0`, i.e.:

```python
import pympc
pympc.minor_planet_check(ra=230.028, dec=-11.774, epoch=58484., search_radius=30, chunk_size=0)
```

## Limitations

* The orbits are propagated following [xephem](http://www.clearskyinstitute.com/xephem) (via the
[pyephem](https://rhodesmill.org/pyephem/) package), and this does not account for perturbations of the orbits. Thus,
the accuracy of the position is dependent on the time difference between the epoch of the orbit elements (oscluation
epoch) and the epoch at which the search is being performed. ASTORB improves this by providing more frequently updated
osculating elements than MPCORB, but the propagated positions are still fundamentally two-body xephem propagations.
Epoch differences of a few months or less will provide typical positional accuracies of less than a few arcsecond for the vast
majority of minor bodies. Note, additionally, that a small number of bodies (those undergoing strong perturbations and
close to Earth -- i.e. NEAs) may be quite inaccurate (arcminutes) even at modest time differences of a few weeks between the search and orbit
elements epochs. A fuller, quantitative analysis is given inside two notebooks:
[position_accuracy_asteroids.ipynb](notebooks/position_accuracy_asteroids.ipynb) and [position_accuracy_neas.ipynb](notebooks/position_accuracy_neas.ipynb).
> **Note:** For this reason, `pympc` is not suitable to historical (or far-future) searches of positions. It is intended
> for near real-time searches and relies on users to periodically update and overwrite the underlying catalogue. Weekly
> is sufficient to maintain arcsecond-level accuracy even for NEAs (excluding a few pathological cases).

* The `xephem` package, used to calculate positions, can only provide geocentric astrometric positions. `pympc` will
calculate the topocentric correction as a post-processing to the initial position. The simple topometric correction
applied is more than sufficient for the overwhelming majority of minor bodies, but for some near earth objects the
correction can be large and the relatively simple treatment by `pympc` may not be sufficient. Additionally, in order
to find matches in geocentric positions prior to applying the topocentric correction, a buffer is added to the search
radius - this should capture the vast majority of cases where the geocentric position is outside the seach radius but
the topocentric position is within it - unless the object is within ~1/3 AU of Earth. To work around this you can
artifically inflate your search radius and filter yourself afterwards.

* The filtering of matches based on magnitude via `max_mag` argument to `minor_planet_check()` is limited by the
accuracy of the magnitude information in the source catalogues so some buffer should be applied to the desired magnitude cutoff
to allow for this.

### Acknowledgements
The resources to support astorb.dat were originally provided by NASA grant NAG5-4741 (PI E. Bowell)
and the Lowell Observatory endowment, and more recently by NASA PDART grant NNX16AG52G (PI N. Moskovitz).

This package makes use of data and/or services provided by the International Astronomical Union's
[Minor Planet Center](https://www.minorplanetcenter.net).

Based from a package developed by Chris Klein and Duncan Galloway.
