Skip to content

Analysing GW150914 with the CLI¤

This tutorial walks through a complete parameter-estimation run on GW150914 — the first gravitational-wave detection — using the jim-run command. By the end you will have a posterior sample file and know how to inspect the results.

Prerequisites¤

  • Jim installed: pip install jimgw (or uv sync in the repository)
  • Internet access to fetch strain and PSD data from GWOSC
  • A GPU is strongly recommended; CPU runs are possible but slow

1. Generate a template config¤

jim-run --init gw150914.toml

This writes a GW150914-style template config to gw150914.toml and exits. Open it in any text editor — the sections below explain each piece.

2. Walk through the config¤

Seed¤

seed = 0

JAX random seed. Change this to run independent realisations.

[data]¤

[data]
type = "gwosc"
detectors = ["H1", "L1"]
trigger_time = 1126259462.4
duration = 4.0
post_trigger_duration = 2.0
psd_duration = 1024.0
  • type = "gwosc" — fetches strain and PSD from the public GWOSC archive automatically.
  • detectors — the detectors to include. H1 is LIGO Hanford, L1 is LIGO Livingston.
  • trigger_time — GPS time of the event (1126259462.4 s for GW150914).
  • duration — analysis segment length in seconds. The strain window runs from trigger_time - duration + post_trigger_duration to trigger_time + post_trigger_duration.
  • post_trigger_duration — seconds after the trigger kept in the analysis window (2 s is standard for stellar-mass BBH).
  • psd_duration — length of the off-source segment used to estimate the PSD (1024 s is recommended to resolve low-frequency features).

[waveform]¤

[waveform]
approximant = "IMRPhenomXAS"
f_ref = 20.0
  • approximant — which ripple waveform model to use. IMRPhenomXAS is a fast, accurate aligned-spin BBH model.
  • f_ref — reference frequency in Hz at which spin magnitudes and angles are defined (20 Hz is conventional for BBH).

[prior]¤

[prior]
M_c     = { type = "uniform",   min = 10.0,  max = 80.0  }
q       = { type = "uniform",   min = 0.125, max = 1.0   }
s1_z    = { type = "uniform",   min = -0.99, max = 0.99  }
s2_z    = { type = "uniform",   min = -0.99, max = 0.99  }
iota    = { type = "sine" }
d_L     = { type = "power_law", min = 1.0,   max = 2000.0, alpha = 2.0 }
t_c     = { type = "uniform",   min = -0.1,  max = 0.1   }
phase_c = { type = "uniform",   min = 0.0,   max = 6.283185307179586 }
psi     = { type = "uniform",   min = 0.0,   max = 3.141592653589793 }
ra      = { type = "uniform",   min = 0.0,   max = 6.283185307179586 }
dec     = { type = "cosine" }

Each line names a parameter and specifies its prior distribution. Key choices for GW150914:

Parameter Meaning Prior choice
M_c Chirp mass (\(M_\odot\)) Uniform, broad enough to encompass GW150914 (\(\sim 28\,M_\odot\))
q Mass ratio \(m_2/m_1 \leq 1\) Uniform; 0.125 = 1:8 mass ratio
s1_z, s2_z Aligned spin components Uniform on (-0.99, 0.99) — physical black holes
iota Inclination angle sine prior = uniform on the sphere
d_L Luminosity distance (Mpc) power_law with \(\alpha=2\) = uniform in volume
t_c Coalescence time offset (s) Uniform ±0.1 s around trigger_time
phase_c Coalescence phase Uniform on \([0, 2\pi)\)
psi Polarisation angle Uniform on \([0, \pi)\)
ra, dec Sky position Uniform in ra; cosine prior in dec = uniform on sphere

The CLI automatically infers all required transforms (mass-ratio conversion, sky-frame rotation) from the parameter names — you do not configure these yourself.

[likelihood]¤

[likelihood]
f_min = 20.0
f_max = 1024.0

The frequency band for the matched-filter likelihood. 20–1024 Hz is standard for stellar-mass BBH with LIGO O1 data.

[sampler]¤

[sampler]
type = "flowmc"
n_chains = 1000
n_local_steps = 100
n_global_steps = 1000
n_training_loops = 50
n_production_loops = 10
n_NFproposal_batch_size = 100
global_thinning = 100

flowmc is a normalizing-flow-enhanced MCMC sampler. For a quick test (minutes instead of hours), reduce to:

n_chains = 100
n_global_steps = 100
n_production_loops = 2

[output]¤

[output]
dir = "output/GW150914"
save_corner = false

Results are written to output/GW150914/. Set save_corner = true to automatically generate a corner plot (requires the corner package).

3. Run the analysis¤

jim-run gw150914.toml

Use -v for verbose (DEBUG-level) logging. On a modern GPU the production settings finish in roughly 30–60 minutes; the quick-test settings complete in a few minutes.

Expected console output:

INFO | jimgw.cli | Loaded config from gw150914.toml
INFO | jimgw.cli | seed: 0
INFO | jimgw.cli | data: type=gwosc, detectors=['H1', 'L1']
INFO | jimgw.cli | waveform: IMRPhenomXAS (f_ref=20.0 Hz)
INFO | jimgw.cli | prior: 11 parameter(s): ['M_c', 'q', 's1_z', ...]
INFO | jimgw.cli | sampler: type=flowmc
INFO | jimgw.cli | Sampling complete.

4. Inspect the outputs¤

After the run, output/GW150914/ contains:

output/GW150914/
├── samples.npz          # posterior samples
├── diagnostics.json     # scalar diagnostics (acceptance rates, …)
├── diagnostics.npz      # array diagnostics (log-prob traces, …)
└── config.final.toml    # the resolved config (useful for reproducing this run)

Load the posterior samples¤

import numpy as np

data    = np.load("output/GW150914/samples.npz")
params  = list(data.keys())                          # one key per parameter
samples = np.column_stack([data[p] for p in params]) # shape: (n_samples, n_params)

print(params)
# ['M_c', 'q', 's1_z', 's2_z', 'iota', 'd_L', 't_c', 'phase_c', 'psi', 'ra', 'dec']

Make a corner plot¤

import corner
import matplotlib.pyplot as plt

fig = corner.corner(samples, labels=params)
plt.savefig("corner.png", dpi=150)

Reproduce the run¤

config.final.toml records the exact resolved configuration (including any defaults that were applied). To reproduce the run, pass it directly to jim-run. Because config.final.toml retains the original output.dir, the run will abort if that directory already exists and output.overwrite is not set. Before re-running, either change output.dir to a new path or add output.overwrite = true:

# in config.final.toml — pick one:
[output]
dir = "output/GW150914_repro"   # write to a fresh directory
# or
overwrite = true                # allow overwriting output/GW150914 in-place

Then run:

jim-run output/GW150914/config.final.toml

5. Next steps¤

  • Switch to a nested sampler — change [sampler] to type = "blackjax-ns-aw" or type = "blackjax-nss" (requires uv sync --group nested-sampling) to obtain a log-evidence estimate.
  • Try an injection — change [data] to type = "injection" with injection_parameters to validate recovery on a known signal. See the CLI Config Reference for the full injection syntax.
  • Enable heterodyning — add a [likelihood.heterodyne] block for a 10–100× likelihood speedup. See the CLI Config Reference.
  • Full config referenceCLI Config Reference documents every available field and default.