Waveforms and backends¶
Overview¶
This page explains how time-domain gravitational-wave polarizations \(h_{+}\)
and \(h_{\times}\) are produced and how they surface as
GWpy
TimeSeries
values (dict keys "plus" and "cross"). Typical uses include injection
studies, matched-filter sanity checks, and mock data challenges.
The helpers are stateless at call time for a single generation: one call
returns one waveform pair. WaveformFactory adds a
registry: every built-in name from the active waveform backend is
pre-registered, and you may add more with WaveformFactory.register_model.
API reference
Exact signatures, types, and exceptions live under Waveform API; the sections below are narrative and copy-paste examples.
Backend architecture¶
Waveform generation is split into three layers:
-
WaveformBackend(abstract interface; see Waveform API) Implementations exposeavailable_approximants()andgenerate_td_waveform(approximant, tc, sampling_frequency, minimum_frequency, **params). -
Concrete backends
-
LALSimulationBackend(default) — uses LALSimulation (SimInspiralChooseTDWaveform). Shipped with the core dependencylalsuite; no optional extra is required. Parameter handling is strict: only documented mass, spin, distance, inclination, and coalescence-phase aliases are accepted; unknown keys raiseValueError. -
PyCBCBackend(optional) — delegates to PyCBC’s time-domain waveform path via the internalpycbc_waveform_wrapper. Requires installinggwmock-signal[pycbc](see Installation). Extra parameters are forwarded to PyCBC like a directget_td_waveformcall.
-
-
WaveformFactory— on construction, takesbackend: WaveformBackend | None(defaultLALSimulationBackend()), registers one callable per name returned bybackend.available_approximants(), and dispatchesgenerate(waveform_model, parameters, **extra)to the right callable.
CBCSimulator and inject_cbc_signal
accept an optional waveform_backend and build a WaveformFactory(backend=…)
internally. The CLI inject cbc exposes --backend lal (default) or
--backend pycbc.
Supported waveform model names¶
Built-in model names are not hard-coded in this documentation: they depend
on your installed lalsuite / PyCBC versions.
| Backend | Where names come from | Install |
|---|---|---|
| LAL (default) | Every approximant for which LAL marks a time-domain implementation (SimInspiralImplementedTDApproximants) |
Core (lalsuite) |
| PyCBC | pycbc.waveform.td_approximants() |
Optional extra [pycbc] |
List every name in your environment
python -c "from gwmock_signal.waveform import WaveformFactory; print('\n'.join(WaveformFactory().list_models()))"
With PyCBC installed:
python -c "from gwmock_signal.waveform import WaveformFactory, PyCBCBackend; print('\n'.join(WaveformFactory(backend=PyCBCBackend()).list_models()))"
The two lists overlap in spirit (many IMR models exist in both stacks) but are not identical string-for-string. If a name fails, use the listing above or check upstream tables (LALSimulation approximant documentation; PyCBC waveforms).
Illustrative examples (always confirm with list_models() on your machine):
IMRPhenomD, IMRPhenomPv2, SEOBNRv4, SEOBNRv4PHM, TaylorF2,
SpinTaylorT4, …
Examples¶
Example 1 — WaveformFactory with default LAL backend (BBH, IMRPhenomD)¶
from gwmock_signal.waveform import WaveformFactory
factory = WaveformFactory()
pol = factory.generate(
"IMRPhenomD",
{
"tc": 1_400_000_000.0,
"detector_frame_mass_1": 36.0,
"detector_frame_mass_2": 29.0,
"spin_1z": 0.0,
"spin_2z": 0.0,
"distance": 410.0,
"inclination": 0.0,
"coa_phase": 0.0,
},
sampling_frequency=4096.0,
minimum_frequency=20.0,
)
hp, hx = pol["plus"], pol["cross"]
print(hp.t0, hp.duration)
Example 2 — Explicit PyCBC backend (requires optional install)¶
from gwmock_signal.waveform import PyCBCBackend, WaveformFactory
factory = WaveformFactory(backend=PyCBCBackend())
params = {
"tc": 1_400_000_000.0,
"mass1": 40.0,
"mass2": 30.0,
"spin1z": 0.5,
"spin2z": -0.3,
"distance": 400.0,
"inclination": 0.0,
"coa_phase": 0.0,
}
pol = factory.generate(
"IMRPhenomD",
params,
sampling_frequency=4096.0,
minimum_frequency=20.0,
)
Example 3 — Direct PyCBC wrapper (lazy import)¶
pycbc_waveform_wrapper is still available for direct PyCBC calls without
going through WaveformFactory. Importing it triggers a PyCBC import; install
gwmock-signal[pycbc] first.
from gwmock_signal.waveform import pycbc_waveform_wrapper
pol = pycbc_waveform_wrapper(
tc=1_400_000_000.0,
sampling_frequency=4096.0,
minimum_frequency=20.0,
waveform_model="IMRPhenomD",
mass1=36.0,
mass2=29.0,
spin1z=0.0,
spin2z=0.0,
distance=410.0,
inclination=0.0,
coa_phase=0.0,
)
hp, hx = pol["plus"], pol["cross"]
Example 4 — Register your own waveform model¶
Use this when you have a custom generator (e.g. numerical relativity or a
ROM) and want the same {"plus", "cross"} GWpy contract as the built-in
backends. The callable does not have to call PyCBC or LAL.
import numpy as np
from astropy import units as u
from gwpy.timeseries import TimeSeries
from gwmock_signal.waveform import WaveformFactory
def toy_gaussian_sine_burst(
*,
waveform_model: str,
tc: float,
sampling_frequency: float,
minimum_frequency: float,
**kwargs,
) -> dict[str, TimeSeries]:
width_s = float(kwargs.get("width", 0.1))
f_hz = float(kwargs.get("f0", 150.0))
dt = 1.0 / sampling_frequency
n_half = max(8, int(width_s * sampling_frequency))
t = (np.arange(-n_half, n_half + 1) * dt) + tc
env = np.exp(-0.5 * ((t - tc) / (width_s / 3)) ** 2)
phase = 2 * np.pi * f_hz * (t - tc)
hp = env * np.cos(phase)
hc = env * np.sin(phase)
return {
"plus": TimeSeries(hp, t0=float(t[0]), dt=dt, unit=u.dimensionless_unscaled),
"cross": TimeSeries(hc, t0=float(t[0]), dt=dt, unit=u.dimensionless_unscaled),
}
factory = WaveformFactory()
factory.register_model("toy_burst", toy_gaussian_sine_burst)
out = factory.generate(
"toy_burst",
{"tc": 1_400_000_000.0, "f0": 120.0, "width": 0.05},
sampling_frequency=4096.0,
minimum_frequency=20.0,
)
Example 5 — Inspect registered names¶
from gwmock_signal.waveform import WaveformFactory
factory = WaveformFactory()
names = factory.list_models()
print(len(names), names[:5])
You can look up a single registered generator with get_model():
generator = factory.get_model("IMRPhenomD")
# generator is the wrapped backend callable
Example 6 — Register a model via import string¶
The register_model method also accepts a string path to a callable, so you can
register waveform generators without importing them at registration time:
factory = WaveformFactory()
# Colon syntax: "module.path:callable"
factory.register_model("my_nr_model", "mypackage.waveforms:my_nr_generator")
# Dot syntax: "package.module.callable"
factory.register_model("another_model", "mypackage.waveforms.a_func")
The string is resolved via importlib; the resolved object must be callable.
LAL backend — accepted parameters¶
When using LALSimulationBackend (the default), only the following parameter
keys are accepted. Extra keys raise ValueError.
| Canonical name | Aliases | Default | Description |
|---|---|---|---|
detector_frame_mass_1 |
mass1 |
required | Primary mass (solar masses) |
detector_frame_mass_2 |
mass2 |
required | Secondary mass (solar masses) |
luminosity_distance |
distance |
required | Luminosity distance (Mpc) |
spin_1x |
spin1x |
0.0 |
Primary spin x-component |
spin_1y |
spin1y |
0.0 |
Primary spin y-component |
spin_1z |
spin1z |
0.0 |
Primary spin z-component |
spin_2x |
spin2x |
0.0 |
Secondary spin x-component |
spin_2y |
spin2y |
0.0 |
Secondary spin y-component |
spin_2z |
spin2z |
0.0 |
Secondary spin z-component |
inclination |
— | 0.0 |
Inclination angle (radians) |
coa_phase |
— | 0.0 |
Coalescence phase (radians) |
The PyCBC backend forwards all extra parameters to get_td_waveform instead
of rejecting unknown keys.
!!! note "sampling_frequency type" The Python API (WaveformFactory.generate,
WaveformBackend.generate_td_waveform) accepts sampling_frequency as float.
The CLI --sample-rate flag accepts an int. Both represent Hertz.
Tips and pitfalls¶
- Unknown model names raise
ValueError; uselist_models()after install. - LAL surfaces invalid physics or unsupported parameter combinations as
LAL/LALSimulation errors; PyCBC behaves like PyCBC’s
get_td_waveform. - Series are offset by
tcin the sense documented for each backend; they are not cropped to a science segment — windowing is downstream. - Reuse one
WaveformFactoryin hot loops: construction callsavailable_approximants()once and can be slow on cold import.
Scientific notes¶
- Default LAL path follows
SimInspiralChooseTDWaveformconventions; see LIGO Algorithm Library / LALSimulation documentation for parameter meanings. - PyCBC wrapper path follows PyCBC’s
get_td_waveformkeyword conventions. - GWpy
TimeSeriesoutputs interoperate with typical GWpy, frame, and Bilby workflows.
See also¶
- User guide overview
- Installation — core vs
[pycbc]optional extra - Command-line interface —
inject cbc --backend/--approximant - Detector projection
- Waveform API reference
- Documentation home