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.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 nullcal 0.2.0
File Size Uploaded
nullcal-0.2.0.tar.gz 7.5 MB Details

Built distribution (wheel)

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

Total release size: 7.5 MB

Release files / nullcal-0.2.0.tar.gz

Download URL nullcal-0.2.0.tar.gz
Size 7.5 MB
Tags Source
SHA-256 checksum
How to use checksums
495f8264a174a3b3222733e792f61216e7545ec798bf089487f07452262e6faa
BLAKE2b-256 checksum
How to use checksums
5fcf6cfbdbb2392d358a864efad82d7351dfc6c434874b940b083b66edaa2f2c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.17 {"installer":{"name":"uv","version":"0.12.17","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.0-py3-none-any.whl

Download URL nullcal-0.2.0-py3-none-any.whl
Size 45.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
9c853b0c2cc8e87053f5f1f49295a10c48a9eccd699a4bf94a9bbf9372f9e9b0
BLAKE2b-256 checksum
How to use checksums
bb62d5173ac1eb6d93f3c6593efd046b87a741ef2aa3af182a337988c0e1250f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.17 {"installer":{"name":"uv","version":"0.12.17","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

0.2.1

2 release files

This release

0.2.0 This release

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