Skip to main content

Wenuray

PyPI Python build Docs DOI License: GPL v3+

Self-calibration for radio interferometers, written once and run anywhere.

Wenuray lets you describe a self-calibration procedure as a short recipe (a dozen lines of TOML or a few Python objects) and runs it on any measurement set, from any array. It images with CASA tclean, Pyralysis or your own imager, solves for phases and then amplitudes round after round, and keeps a round only if it really improves the image. Every run records what it did, so you or a referee can repeat it exactly.

Why self-calibration?

An interferometer measures visibilities, and every antenna i multiplies what it sees by its own complex gain gᵢ(t, ν):

V_ij(observed) = g_i · g_j* · V_ij(true) + noise

Calibrating against a calibrator source removes most of those gains, but not all of them. The atmosphere and the ionosphere change faster than the calibrator cycle, and the calibrator is in another part of the sky. What's left over shows up as stripes, sidelobes and a noise floor far above the thermal limit.

Self-calibration uses the target itself. An array of N antennas has N(N−1)/2 baselines but only N unknown gains, so even an imperfect model of the sky constrains the gains well. The loop is:

  1. Image the data and keep the model.
  2. Solve for the antenna gains that best match the data to that model (gaincal).
  3. Apply them and image again. If the image is better, keep the solution and go on with a shorter interval or with amplitudes; if not, roll it back.

Why Wenuray?

In practice, most astronomers write a new self-calibration script for each dataset: a new loop, the same choices made again (solution intervals, reference antennas, which tables to pre-apply with which spwmap, when to stop), and the same bookkeeping mistakes waiting to happen. These scripts are hard to share, hard to review and hard to reproduce. They are also usually tied to CASA, and so to tclean, the de facto standard imaging algorithm, even when another reconstruction method would suit the data better.

Wenuray continues snow, and replaces those scripts with a framework:

  • Describe the procedure, not the loop. A self-calibration run is a short recipe. Wenuray owns the loop and checks the recipe against self-calibration theory before it touches your data. For example, it refuses an amplitude round with no phase solution before it, and it warns when a solution interval is too short to reach your minsnr.
  • Use any imager, not only tclean. The imager is a plug-in: tclean, Pyralysis (optimization-based reconstruction) or your own algorithm, all in the same loop.
  • Extend it without forking it. Imagers, acceptance rules and observers are classes you can subclass; imagers and acceptance rules of your own can be named in a recipe, so a new idea doesn't need a new script.

Installation

pip install wenuray

You need Python 3.11, 3.12 or 3.13 on Linux or macOS, the platforms CASA ships wheels for. Conda, Docker, source installs and the optional extras (Pyralysis, plots) are in the installation guide.

Debian, Ubuntu or Arch? The casatools wheel pip picks by default fails to import there (OPENSSL_3.0.1 not found). From a clone, pip install -r requirements.txt . pins a wheel that works.

Quick start

Your data should already be calibrated, and the target split out so that the calibrated visibilities sit in DATA:

split(vis="calibrated.ms", outputvis="target.ms", field="my_target", datacolumn="corrected")

Your first recipe

Phase, then amplitude-phase self-calibration of an ALMA dataset. The values (cell size, threshold, reference antenna) belong to this dataset; adapt them to yours.

ms = "target.ms"           # paths are relative to the recipe
output = "target_selfcal"

[solve]
refant = "DA51"
minblperant = 6

[imager]
type = "tclean"
niter = 100
threshold = "0.1mJy"
deconvolver = "hogbom"
usemask = "auto-multithresh"
geometry = { imsize = 1024, cell = "0.005arcsec" }
weighting = { weighting = "briggs", robust = 0.5 }

[[steps]]
type = "phase"             # Phasecal; "amp" is Ampcal, "ampphase" AmpPhasecal
solint = "inf"
solve = { combine = "spw" }

[[steps]]
type = "phase"
solint = "600s"
solve = { combine = "spw" }

[[steps]]
type = "ampphase"
solint = "inf"
imaging = { threshold = "0.025mJy" }

Three rounds: a phase solution over the whole scan, combining spectral windows for S/N; a shorter one, now that the model is better; and finally amplitudes and phases, cleaned deeper. Check it, then run it:

wenuray validate recipe.toml    # builds the run and checks it, without touching the data
wenuray run recipe.toml         # images, solves, applies and decides, round by round

wenuray init > recipe.toml writes a generic recipe to start from, and wenuray run recipe.toml --ms other.ms runs the same procedure on another dataset.

The same run in Python

Each table in a recipe is a constructor and its keys are that class's arguments, so a recipe and a script build equal objects. This is examples/alma/selfcal.py; a test keeps the two identical.

"""
Phase, then amplitude-phase self-calibration of an ALMA dataset imaged with tclean.

Usage: python selfcal.py <visfile> <output_directory> [--plot]
"""
import argparse
import logging

from casatasks import split

from wenuray.config import ImageGeometry, SolveOptions, Weighting
from wenuray.imaging import Tclean
from wenuray.selfcalibration import AmpPhasecal, GainPlotObserver, LoggingObserver, Phasecal, Selfcal


def parse_arguments():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("visfile", help="Measurement set with the calibrated data in DATA")
    parser.add_argument("output", help="Directory for the images, tables and the result record")
    parser.add_argument("--plot", action="store_true", help="Plot the gain solutions")
    return parser.parse_args()


if __name__ == '__main__':
    args = parse_arguments()
    # Wenuray logs each self-calibration round's metrics at INFO
    logging.basicConfig(level=logging.INFO)

    # tclean's own parameter names. Automasking values for long baselines:
    # https://casaguides.nrao.edu/index.php/Automasking_Guide
    imager = Tclean(
        ImageGeometry(imsize=1024, cell="0.005arcsec"),
        Weighting("briggs", robust=0.5),
        niter=100,
        threshold="0.1mJy",
        specmode="mfs",
        deconvolver="hogbom",
        gridder="standard",
        usemask="auto-multithresh",
        sidelobethreshold=3.0,
        noisethreshold=5.0,
        minbeamfrac=0.3,
        lownoisethreshold=1.5,
        negativethreshold=0.0,
        interactive=False  # True to edit the masks by hand
    )

    # Phases combined over spectral windows, then amplitudes and phases per spectral window,
    # cleaned deeper. Each spwmap is derived from combine.
    steps = [
        Phasecal("inf", solve={"combine": "spw"}),
        Phasecal("600s", solve={"combine": "spw"}),
        AmpPhasecal("inf", imaging={"threshold": "0.025mJy"}),
    ]
    selfcal = Selfcal(
        args.visfile,
        imager,
        steps,
        solve=SolveOptions(refant="DA51", minblperant=6, minsnr=3.0, gaintype="T"),
        output=args.output,
    )
    observers = [LoggingObserver()] + ([GainPlotObserver()] if args.plot else [])
    result = selfcal.run(observers)

    # The self-calibrated data, for final imaging
    split(vis=result.ms, outputvis=args.output + "/selfcal.ms", datacolumn="corrected")

Selfcal.from_toml("recipe.toml") gives you the same Selfcal from the recipe, to inspect, change or run; selfcal.to_toml(...) writes it back.

What you get

  • A decision for every round. By default a round is kept only if the peak S/N of the image rises, every solution interval keeps enough antennas, and amplitude rounds don't lose model flux: PSNR alone can rise while the model goes wrong. A rejected round is rolled back and the run stops. You can combine your own acceptance rules with AllOf/AnyOf.
  • Checks before any I/O. An invalid schedule raises ScheduleError while the run is being built. Before each gaincal, Wenuray predicts the solution's S/N from the data and tells you the shortest solint that would reach minsnr.
  • Bookkeeping you don't have to do. Every accepted table stays applied in later solves, each with its own spwmap and interp, and one Selection is used by the solve, the apply and the imager.
  • Reproducibility. Each output directory holds the resolved recipe.toml, with every default written out, and a selfcal.toml with each round's metrics, the accepted tables and the provenance: a fingerprint of the input measurement set and the versions of Python, CASA, Wenuray and the imager.
  • Any imager. CASA tclean, Pyralysis (optimization-based reconstruction), or your own: subclass Imager or wrap a function. Each one images CORRECTED_DATA and writes MODEL_DATA, and Wenuray checks that it does.
  • Any array. The defaults come from theory and from the data, not from an instrument: there are examples for ALMA, the VLA and e-MERLIN.

Learn more

  • How a run works: preparing the data, schedules, acceptance, recipes and presets, resuming, and an API overview.
  • Imagers: the imager contract, the Nyquist-cell check, tclean, Pyralysis and writing your own.
  • Examples: ALMA, VLA and e-MERLIN, each as a recipe and the script that builds the same run.
  • Migrating from snow: code written for snow, the name before Wenuray 1.0, raises an error that shows the new call.
  • API reference on Read the Docs.

About the name

Wenuray (also written Huenuray) is a Mapuche woman's name. In Mapudungun it means "flower of the sky": wenu, the sky, and rayen, flower. The radio sky is what Wenuray brings into focus, and its roots are in Chile, home of many of the world's radio telescopes.

Wenuray is dedicated to the women of radio astronomy. Each major release carries one of their names, beginning with v1.0 "Payne-Scott", after Ruby Payne-Scott (1912–1981), the first woman radio astronomer and a pioneer of radio interferometry.

Wenuray was called snow (caSa pythoN self-calibratiOn frameWork) before version 1.0.

Citing Wenuray

If Wenuray helps your research, please cite it. GitHub's Cite this repository button (from CITATION.cff) gives APA and BibTeX, or use the BibTeX below. The DOI, 10.5281/zenodo.23775089, always resolves to the latest release; Zenodo gives each version its own DOI too.

@software{wenuray,
  author = {Cárcamo, Miguel and Casassus, Simon},
  title = {Wenuray: radio-interferometric self-calibration},
  url = {https://github.com/miguelcarcamov/wenuray},
  doi = {10.5281/zenodo.23775089},
  version = {<version>},
  year = {<year>}
}

Contributing and support

Bug reports, questions and pull requests are welcome on GitHub; see CONTRIBUTING.md. For CASA itself, the CASA documentation is the place to look. You can also write to miguel.carcamo@usach.cl.

Wenuray is free software under the GNU General Public License v3 or later. It is built on CASA (Common Astronomy Software Applications), developed by the National Radio Astronomy Observatory (NRAO).

Metadata

Release files for wenuray 1.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for wenuray 1.1.0
File Size Uploaded Provenance Actions
wenuray-1.1.0.tar.gz 180.3 kB With provenance Details for wenuray-1.1.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for wenuray 1.1.0
File Interpreter ABI Platform Actions
wenuray-1.1.0-py3-none-any.whl Python 3 none any Details for wenuray-1.1.0-py3-none-any.whl

Total release size: 266.7 kB

Release files / wenuray-1.1.0.tar.gz

Download URL wenuray-1.1.0.tar.gz
Size 180.3 kB
Tags Source
SHA-256 checksum
How to use checksums
d92c3d62ed56f7e6405b1f6f5180e5ac87c3b57698da87e553d09134728bb8ae
BLAKE2b-256 checksum
How to use checksums
c5e9e87951df4bf03205be8ab4c45ee3a15a19c5dc2d921d52b0e2763f783d09
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 29, 2026.

Transparency log

Release files / wenuray-1.1.0-py3-none-any.whl

Download URL wenuray-1.1.0-py3-none-any.whl
Size 86.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
f668cf923e3a6531868478019d826c58e03d88fcf19dd4fb0ff00380b9f1094f
BLAKE2b-256 checksum
How to use checksums
3ba33a0668fb586be18ba7152319e997cc4d8a05cccca307ead89e7b998622e7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 29, 2026.

Transparency log

Security for wenuray 1.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for wenuray-1.1.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 29, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page