Skip to main content

nullcal

Python CI Documentation Status codecov PyPI Version Python Versions License: MIT Ruff DOI

A Python package for constraining calibration errors of a closed-geometry network of gravitational-wave detectors. It provides the null stream formalism for noise-only data combinations and Bayesian recalibration likelihoods to quantify detector miscalibration.

Features

  • Null stream construction for closed-geometry detector networks
  • Calibration error constraint via Bayesian recalibration likelihood
  • Time-frequency transforms (Short-Time Fourier Transform, wavelet transforms)
  • Clustering algorithms for time-frequency map analysis
  • Modular architecture with clean separation between null stream, likelihood, calibration, immutable data, result, and utility layers

Installation

We recommend using uv to manage virtual environments for installing nullcal.

If you don't have uv installed, you can install it with pip. See the project pages for more details:

  • Install via pip: pip install --upgrade pip && pip install uv
  • Project pages: uv on PyPI | uv on GitHub
  • Full documentation and usage guide: uv docs

Note: The package requires Python 3.12 or later and is built and tested against Python 3.12–3.14. When creating a virtual environment with uv, specify the Python version to ensure compatibility: uv venv --python 3.12 (replace 3.12 with your preferred supported version: 3.12, 3.13, or 3.14). This avoids potential issues with unsupported Python versions.

From PyPI

# Create a virtual environment (recommended with uv)
uv venv --python 3.12
source .venv/bin/activate  # On Windows: .venv\Scripts\activate
uv pip install nullcal

From Source

git clone git@github.com:Leuven-Gravity-Institute/nullcal.git
cd nullcal
# Create a virtual environment (recommended with uv)
uv venv --python 3.12
source .venv/bin/activate  # On Windows: .venv\Scripts\activate
uv sync --extra jax

Quick Start

import numpy as np
from nullcal.data import InterferometerData
from nullcal.likelihood import RecalibrationLikelihood
from nullcal.null_stream.null_stream import NullStream
from nullcal.time_frequency_transform.wavelet_transforms import WaveletTransform

# Load arrays from your strain source. The loader also accepts detector objects
# exposing these fields via InterferometerData.from_interferometers(...).
duration = 4.0
sampling_frequency = 4096.0
frequency_array = np.fft.rfftfreq(int(duration * sampling_frequency), 1 / sampling_frequency)
data = InterferometerData(
    psd=np.ones((3, frequency_array.size)),
    strain=np.zeros((3, frequency_array.size), dtype=complex),
    mask=np.broadcast_to(frequency_array >= 4.0, (3, frequency_array.size)).copy(),
    frequency_array=frequency_array,
    duration=duration,
    sampling_frequency=sampling_frequency,
    start_time=0.0,
    name=("ET1", "ET2", "ET3"),
)

# Compute the null stream
wavelet_transform = WaveletTransform(
    duration=duration, sampling_frequency=sampling_frequency, nx=4, frequency_resolution=4
)
time_frequency_filter = np.ones(wavelet_transform.shape, dtype=bool)
null_stream = NullStream(
    interferometers=data,
    time_frequency_transform=wavelet_transform,
    time_frequency_filter=time_frequency_filter,
)
null_data = null_stream.compute_calibrated_frequency_domain_null_stream(calibration_factor=np.ones_like(data.strain))

# Set up a recalibration likelihood for calibration error constraints
likelihood = RecalibrationLikelihood(
    interferometers=data,
    knot_frequencies=np.geomspace(4.0, 2048.0, 10),
    time_frequency_filter=time_frequency_filter,
    wavelet_transform_frequency_resolution=4,
    wavelet_transform_nx=4,
)

params = {
    "amplitude": np.zeros((3, 10)),
    "phase": np.zeros((3, 10)),
}
log_posterior = likelihood.logdensity_fn(params)

Development

Pre-commit hooks

Install prek (a wrapper around pre-commit):

uv run prek install

Run all hooks against all files:

uv run prek run --all-files

Documentation

Full documentation is available at https://leuven-gravity-institute.github.io/nullcal/.

Contributing

Contributions are welcome!

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes
  4. Add tests
  5. Submit a pull request

Release Schedule

Releases follow a fixed schedule: every Tuesday at 00:00 UTC, unless an emergent bugfix is required. This ensures predictable updates while allowing flexibility for critical issues. Users can view upcoming changes in the draft release on the GitHub Releases page.

Testing

Run the test suite:

uv run pytest

License

This project is licensed under the MIT License. See the LICENSE file for the full license text.

Support

For questions or issues, please open an issue on GitHub or contact the maintainers.

Metadata

Release files for nullcal 0.2.1

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

Source distribution (sdist)

Source distribution for nullcal 0.2.1
File Size Uploaded
nullcal-0.2.1.tar.gz 7.5 MB Details

Built distribution (wheel)

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

Total release size: 7.5 MB

Release files / nullcal-0.2.1.tar.gz

Download URL nullcal-0.2.1.tar.gz
Size 7.5 MB
Tags Source
SHA-256 checksum
How to use checksums
60e5d6257d75319d57bdd207b9bfa87b7e5d5fba25eda602bc695ce55e8f6655
BLAKE2b-256 checksum
How to use checksums
80c5e487d71824e0715c0f12e6c9cd6de640de0ebca994247f53e46f76c0d61c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.20 {"installer":{"name":"uv","version":"0.12.20","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / nullcal-0.2.1-py3-none-any.whl

Download URL nullcal-0.2.1-py3-none-any.whl
Size 46.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
e56797f735ed423eac9d78868cd32c832576166cc0d6d0eff1197e2bc5a9267e
BLAKE2b-256 checksum
How to use checksums
a1af2de4526b04fb18d3de7a63281a7189eede3f09eac85a5abb766e3bb7bafe
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.20 {"installer":{"name":"uv","version":"0.12.20","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

0.3.0

2 release files

This release

0.2.1 This release

2 release files

0.2.0

2 release files

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