Skip to content

Data¤

This guide covers the different ways to get gravitational-wave data into Jim.

All detector data in Jim lives inside Detector objects. Jim ships with convenience constructors for common detectors — LIGO Hanford (get_H1), LIGO Livingston (get_L1), Virgo (get_V1), Einstein Telescope (get_ET), and Cosmic Explorer (get_CE):

from jimgw.core.single_event.detector import get_H1, get_L1, get_V1

H1 = get_H1()
L1 = get_L1()
V1 = get_V1()

You can also retrieve all presets at once as a dictionary with get_detector_preset():

from jimgw.core.single_event.detector import get_detector_preset

detectors = get_detector_preset()  # {"H1": ..., "L1": ..., "V1": ..., "ET": ..., "CE": ...}
H1 = detectors["H1"]

Note that get_ET() returns a list of three GroundBased2G objects (one for each of ET's triangular arms), while all others return a single detector.

Once you have a detector, you need to attach strain data and a PSD to it.

Loading Data¤

Fetch from GWOSC¤

Use Data.from_gwosc() to download public strain data from the Gravitational Wave Open Science Center:

from jimgw.core.single_event.data import Data

gps_start = 1126259446
gps_end = 1126259478

data = Data.from_gwosc("H1", gps_start, gps_end)
H1.set_data(data)

Load from File¤

Data.from_file() detects the file format from the extension and dispatches accordingly.

NumPy archive (.npz)¤

The file must contain the keys td (time-domain strain), dt (time step in seconds), and start_time (GPS start time):

data = Data.from_file("path/to/data.npz")
H1.set_data(data)

GWF / LAL frame file (.gwf)¤

Pass a channel name via the channel argument. If omitted, a set of common LIGO/Virgo channel names is tried automatically:

# Explicit channel (recommended)
data = Data.from_file("path/to/data.gwf", channel="H1:GDS-CALIB_STRAIN")
H1.set_data(data)

# Auto-detect channel from common presets
data = Data.from_file("path/to/data.gwf")
H1.set_data(data)

Optional start_time and end_time (GPS seconds) let you trim the segment read from the frame file.

HDF5 file (.hdf5 / .h5)¤

data = Data.from_file("path/to/data.hdf5", channel="H1:GDS-CALIB_STRAIN")
H1.set_data(data)

CSV file (.csv)¤

data = Data.from_file("path/to/data.csv")
H1.set_data(data)

Construct from Frequency-Domain Arrays¤

Use Data.from_fd() when you already have frequency-domain strain (e.g. from your own pipeline). The frequency array must form a valid rfft grid:

import jax.numpy as jnp

duration = 4.0
sampling_frequency = 2048.0
n = int(duration * sampling_frequency)
frequencies = jnp.fft.rfftfreq(n, 1.0 / sampling_frequency)

fd_strain = jnp.zeros(len(frequencies), dtype=jnp.complex128)  # replace with your data
data = Data.from_fd(fd_strain, frequencies, start_time=0.0)
H1.set_data(data)

Warning

The frequency array must come from jnp.fft.rfftfreq. Internally, from_fd reconstructs the full rfft grid and asserts it matches yours exactly. jnp.linspace produces floating-point values via a different arithmetic path, so the equality check will fail even for a nominally identical grid.

PSD¤

Before you can evaluate a likelihood, each detector also needs a power spectral density (PSD).

Default GWTC-2 ASD¤

The simplest approach downloads and sets the default GWTC-2 ASD for the detector:

H1.load_and_set_psd()

Load from a file¤

There are two ways to load a PSD from a local file, depending on how much control you need.

One-liner: load_and_set_psd¤

load_and_set_psd is a convenience method that loads and sets the PSD in a single call. It supports all the same formats as PowerSpectrum.from_file (.npz, .txt, .dat, .csv):

# PSD file — any supported format (values in Hz^{-1})
H1.load_and_set_psd(psd_file="path/to/psd.txt")

# ASD file — any supported format (values in Hz^{-1/2}, squared internally)
H1.load_and_set_psd(asd_file="path/to/asd.npz")

Explicit: PowerSpectrum.from_file + set_psd¤

For full control, or when your file is not a plain text file, use PowerSpectrum.from_file. Supported formats:

Format Extensions Notes
NumPy archive .npz Must contain values (Hz⁻¹) and frequencies arrays
Text / dat .txt, .dat Two-column whitespace-separated: (frequency, value)
CSV .csv Two-column comma-separated: (frequency, value)
from jimgw.core.single_event.data import PowerSpectrum

# From an .npz archive
H1.set_psd(PowerSpectrum.from_file("path/to/psd.npz"))

# From a PSD text file (Hz^{-1})
H1.set_psd(PowerSpectrum.from_file("path/to/psd.txt"))

# From an ASD text file (Hz^{-1/2}) — pass is_asd=True to square the values
H1.set_psd(PowerSpectrum.from_file("path/to/asd.txt", is_asd=True))

# From a CSV file
H1.set_psd(PowerSpectrum.from_file("path/to/psd.csv"))

Construct from arrays¤

Use set_psd with a PowerSpectrum object when you already have PSD values and frequencies as arrays:

import jax.numpy as jnp
from jimgw.core.single_event.data import PowerSpectrum

psd_values = jnp.array(...)      # PSD in Hz^{-1}
frequencies = jnp.array(...)     # Frequencies in Hz

H1.set_psd(PowerSpectrum(psd_values, frequencies))

Injecting a Simulated Signal¤

For testing and validation, you can inject a waveform directly into a detector. Set the PSD and frequency bounds first, then call inject_signal.

import jax
from jimgw.core.single_event.waveform import RippleIMRPhenomD

gps_time = 1126259462.0

H1.load_and_set_psd()

waveform = RippleIMRPhenomD(f_ref=20.0)
injection_params = {
    "M_c": 28.0, "eta": 0.24,
    "s1_z": 0.0, "s2_z": 0.0,
    "d_L": 440.0, "t_c": 0.0,
    "phase_c": 0.0, "iota": 0.0,
    "psi": 0.3, "ra": 1.5, "dec": 0.5,
}

H1.inject_signal(
    duration=4.0,
    sampling_frequency=2048.0,
    trigger_time=gps_time,
    waveform_model=waveform,
    parameters=injection_params,
    f_min=20.0,
    f_max=1024.0,
    rng_key=jax.random.key(0),
)

Set zero_noise=True to get a noiseless injection.