convolve-rs

A Rust port of beamcon from RACS-tools. Smooths FITS images and spectral cubes to a common beam using UV-plane (FFT) convolution to avoid numerical issues with undersampled kernels.

Note: This is an experiment in LLM-assisted coding with Claude. Do not trust this software as far as you can throw it.

Installation

Python library

pip install convolve-rs

CLI binary

Requires Rust 1.85+.

cargo install convolve-rs

Python usage

from astropy.io import fits
import numpy as np
from convolve_rs import Beam, common_beam, smooth

hdu = fits.open("image.fits")
data = hdu[0].data.squeeze().astype(np.float32)
dx_deg = hdu[0].header["CDELT1"]   # may be negative
dy_deg = hdu[0].header["CDELT2"]

current = Beam.from_fits_header(hdu[0].header)
target = Beam(0.002, 0.002, 0.0)   # or common_beam([...]) across channels

# `bunit` selects the flux scaling: Jy/beam images are rescaled to stay in
# Jy/beam; Kelvin (brightness temperature) images are left unscaled. An
# unrecognised unit emits a UserWarning and is treated as Jy/beam.
smoothed = smooth(data, current, target, dx_deg, dy_deg, bunit=hdu[0].header.get("BUNIT"))

hdu[0].data[0, 0] = smoothed
fits.writeto("smoothed.fits", hdu[0].data, hdu[0].header, overwrite=True)

CLI usage

convolvers --help
convolvers 2d --help
convolvers 3d --help

Development

Install in editable mode:

uv pip install -e .

After changing the Python-facing Rust API in src/python.rs, rebuild with the stubgen feature (the default build omits _generate_stubs) and regenerate the type stubs:

uv run maturin develop --features stubgen
uv run --no-sync python -c "from convolve_rs._convolve_rs import _generate_stubs; _generate_stubs()"

This overwrites convolve_rs/_convolve_rs.pyi from the Rust annotations and docstrings. Commit the result alongside any API changes.

Running tests

Python tests need the compiled extension and the test extra (pytest, radio-beam, astropy). uv sync builds the maturin extension into .venv, so a plain uvx pytest won’t work — it runs in an isolated env with neither the module nor the deps:

uv sync --extra test       # builds the extension + installs test deps
uv run --no-sync pytest

Rust tests run with cargo test. One integration test compares output against MIRIAD; point MIRIAD_BIN at a MIRIAD bin directory to enable it (it is skipped when unset):

cargo test
MIRIAD_BIN=/path/to/miriad/bin cargo test   # include the MIRIAD comparison

Pre-commit hooks

Formatters and linters run via prek, a fast drop-in pre-commit reimplementation. The hooks (.pre-commit-config.yaml) are the same checks CI enforces: ruff lint + format, ty type checking, cargo fmt, and cargo clippy.

uv sync --extra dev   # installs prek + ty into the venv
uvx prek install      # install the git hook (runs on every commit)
uvx prek run --all-files   # run all hooks manually

License

convolve-rs is released under the BSD 3-Clause License.

It builds on prior work in the radio-astronomy community: the UV-plane convolution and cube handling are ported from RACS-tools, and the common-beam computation follows radio_beam (both BSD). The Gaussian beam algebra implements the standard formulae of Wild (1970), and MIRIAD (GPL) serves as a validation reference in the test suite. See NOTICE.md for full attributions.

Table of contents

Rust API

The Rust crate is documented separately on docs.rs/convolve-rs — the Python API on this site is a thin binding over it.

Indices and tables