astroapers 0.1.2

Rust-backed aperture overlap and summation utilities
Documentation

astroapers

(Astronomy + Apertures + Rust(rs) = astroapers)

astroapers is a Rust-backed Python package for exact pixel-aperture overlap, bbox-tight aperture weights, and aperture summation in pixel coordinates. Although it was developed for astronomical image analysis, the core algorithms operate on generic two-dimensional image arrays. They can also be useful for other scientific and technical images, including microscopy, photography, and any workflow that needs reproducible measurements over pixel-defined apertures or regions.

The package exposes three public layers:

  • PixelAp objects such as CircAp, EllipAp, RectAp, PillAp, WedgeAp, and *An for readable workflows, plotting, weights, and one-shot aperture sums.
  • bboxes(), weights_exact(), and BoundingBox methods for reusing bbox-tight aperture weights.
  • import astroapers._rust as aapr for expert users who need the raw extension functions within python and are willing to supply contiguous arrays and handle raw return values. (For how-tos for aapr, inspect astroapers.kernels; it is the Python layer that calls _rust internally).

Project links:

Install

astroapers builds a native Rust extension with maturin, so source installs require a working Rust/Cargo toolchain.

# You may activate your Python environment before this, e.g.,
# source ~/.venvs/your_env/bin/activate

# General install:
uv pip install -e .

# development install:
uv pip install -e ".[dev]"

Then try tests:

uv run pytest -q

For Rust crate use:

[dependencies]
astroapers = "0.1"

Quickstart

import astroapers as aap

ap = aap.CircAp((42.3, 17.2), r=3.0)
apsum, npix = ap.apsum_exact(data, mask=bad_pixels)  # return_npix=True by default
weights = ap.weights_exact()[0]
center_weights = ap.weights_center()[0]
bbox = ap.bboxes()[0]
center_samples = ap.sampled_values(data)[0]
weighted_values = ap.weighted_values(data)[0]
fits_section = bbox.to_fits_section(data.shape)

wedge = aap.WedgeAp((42.3, 17.2), r_in=5.0, r_out=50.0, theta_in=0.0, dtheta_in=0.2)
wedge_sum, wedge_npix = wedge.apsum_exact(data)

For maximum-performance:

import astroapers._rust as aapr

x = np.ascontiguousarray(x, dtype=np.float64)
y = np.ascontiguousarray(y, dtype=np.float64)
data = np.ascontiguousarray(data, dtype=np.float64)

apsum = aapr.apsum_circ_exact_sum(data, x, y, 3.0)

Dtype caveats

astroapers performs geometry and public aperture-sum outputs in float64. The raw _rust functions do not provide validation, mask handling, dtype conversion, or return shaping. Use contiguous arrays with the dtype-specific raw function (*_f32, *_i32, *_i16) when not using float64. Coordinate inputs and scalar geometry parameters are expected to be float64-compatible.

Bbox-tight weights from PixelAp.weights_exact() are float64. When user-supplied weights are passed to BoundingBox methods, only float32 and float64 arrays are preserved. Other numeric or boolean weight arrays, including extended precision dtypes such as float128/longdouble where NumPy provides them, are converted to float64. BoundingBox.to_image(), weighted_cutout(), and weighted_values() preserve float32 when both data and weights are float32, but BoundingBox.apsum() and BoundingBox.npix() always accumulate in float64. Bad-pixel masks passed as mask= are converted to boolean, where True means excluded.

weights_center() and sampled_values(data) are related but not synonyms: weights_center() returns bbox-tight binary weights, while sampled_values(data) returns the raw image values selected by the positive center weights.

Documentation

The documentation is hosted at https://ysbach.github.io/astroapers/ and built from the Quarto site in docs/quarto. The hosted docs contain:

  • autogenerated API reference pages from Python docstrings;
  • generated Rust API reference from rustdoc;
  • tutorial .qmd files for common aperture-sum, mask, background, vector, and raw-Rust performance workflows.

Local docs build:

uv pip install -e ".[docs]"
make docs-build

Release Checks

Before publishing both distributions, verify the Python package and Rust crate from the same source tree:

cargo test --no-default-features
cargo check --features extension-module
cargo package --allow-dirty
uv run pytest -q
uv build

Publish the Cargo crate with cargo publish, then publish the Python artifacts from dist/ with uv publish after confirming the versions match in Cargo.toml and pyproject.toml.

Conventions

Coordinates follow the SEP/Photutils pixel convention: pixel (x, y) is centered at integer coordinates and covers [x - 0.5, x + 0.5].