Skip to content

Strain injection examples

After producing detector strain (e.g. with Waveforms and Detector projection), you often need to embed that strain into a longer segment aligned to a science run—typically starting from zeros or from a noise realization (noise generation can live in a separate package). This step is central to software injections, end-to-end mock challenges, and pipeline validation.

This page is examples only. Signatures, defaults, and raised exceptions for inject_strain / inject_strains_sequential are documented only under API → Injection.

API reference

Use API → Injection for the authoritative behavior description; examples below illustrate common patterns.

Example 1 — Inject one CBC into a zero-filled segment

import numpy as np
from gwpy.timeseries import TimeSeries

from gwmock_signal.waveform import WaveformFactory
from gwmock_signal.projection import project_polarizations_to_network
from gwmock_signal.injection import inject_strain

fs = 4096.0
duration = 8.0
t0 = 1_400_000_000.0
n = int(duration * fs)

target = TimeSeries(np.zeros(n), t0=t0, sample_rate=fs)

factory = WaveformFactory()
pol = factory.generate(
    "IMRPhenomD",
    {
        "tc": t0 + 2.0,
        "detector_frame_mass_1": 30.0,
        "detector_frame_mass_2": 25.0,
        "spin_1z": 0.0,
        "spin_2z": 0.0,
        "distance": 400.0,
        "inclination": 0.0,
        "coa_phase": 0.0,
    },
    sampling_frequency=fs,
    minimum_frequency=20.0,
)
strains = project_polarizations_to_network(
    pol,
    ["H1"],
    right_ascension=1.0,
    declination=0.5,
    polarization_angle=0.2,
    earth_rotation=False,
)
h1 = strains["H1"]

out = inject_strain(target, h1)
assert out is not target  # immutability: new object (recommended contract)

Example 2 — Multiple injections in time order

from gwmock_signal.injection import inject_strains_sequential

# target: long segment; inj1, inj2: non-overlapping or overlapping GWpy series
out = inject_strains_sequential(target, [inj1, inj2], interpolate_if_offset=True)

Example 3 — Disable interpolation (strict grid)

If you know injections are exactly aligned to the target grid and want to avoid cubic resampling at boundaries:

out = inject_strain(target, h1, interpolate_if_offset=False)

When interpolate_if_offset=False and the injection start is not aligned to a target-sample boundary, the function returns target.copy() — i.e., the background unchanged, with a debug-level log message. Use this when you need strict grid alignment guarantees.

Example 4 — One-call CBC injection with inject_cbc_signal

The convenience function inject_cbc_signal orchestrates waveform generation, projection, and injection in one call:

import numpy as np
from gwpy.timeseries import TimeSeries
from gwmock_signal.pipeline import inject_cbc_signal

fs = 4096.0
duration = 8.0
n = int(duration * fs)
t0 = 1_400_000_000.0

params = {
    "detector_frame_mass_1": 36.0,
    "detector_frame_mass_2": 29.0,
    "coa_time": t0 + duration / 2,
    "distance": 410.0,
    "inclination": 0.0,
    "right_ascension": 1.375,
    "declination": -1.211,
    "polarization_angle": 0.0,
}

background = {
    name: TimeSeries(np.zeros(n), t0=t0, sample_rate=fs)
    for name in ["H1", "L1"]
}

result = inject_cbc_signal(
    "IMRPhenomD", params, ["H1", "L1"], background,
    sampling_frequency=fs,
    minimum_frequency=20.0,
    earth_rotation=False,
)

for name in result.detector_names:
    rms = float(np.sqrt(np.mean(result[name].value**2)))
    print(f"{name}: rms={rms:.4e}")

Example 5 — Simulate and save to disk with CBCSimulator.write

The highest-level convenience is CBCSimulator.write, which runs the full pipeline and writes both the strain data and a JSON sidecar with the injection parameters:

from gwmock_signal import CBCSimulator
from gwpy.timeseries import TimeSeries
import numpy as np

sim = CBCSimulator("IMRPhenomD")

# The waveform_model property exposes the approximant name:
print(sim.waveform_model)  # IMRPhenomD

params = {
    "detector_frame_mass_1": 36.0,
    "detector_frame_mass_2": 29.0,
    "coa_time": 1_126_259_462.4,
    "distance": 410.0,
    "inclination": 0.0,
    "right_ascension": 1.375,
    "declination": -1.211,
    "polarization_angle": 0.0,
}
fs = 4096.0
n = 8192
background = {
    name: TimeSeries(np.zeros(n), t0=params["coa_time"] - 1, sample_rate=fs)
    for name in ["H1", "L1"]
}

result = sim.write(
    "my_injection.h5",
    params,
    ["H1", "L1"],
    background,
    sampling_frequency=fs,
    minimum_frequency=20.0,
    format="hdf5",
)
# Also creates my_injection_params.json with the injection parameters

Supported write formats: "hdf5" (default), "gwf", "npy", "txt".

Pitfalls

  • Units: Target and injection should use compatible strain units (typically dimensionless); mixed units should raise a clear error.
  • No overlap: If the injection lies entirely outside the target span, the API returns a copy of the target (same samples, new object).
  • Interpolation: Cubic interpolation can ring at edges; prefer aligned waveforms from the same sampling_frequency and GPS grid when possible.
  • Performance: Large segments are memory-bound; avoid unnecessary copies if the API documents mutability (default recommendation: return a new TimeSeries).

Scientific notes

  • Conventions follow common GWpy / LIGO injection practice: coherent addition of strain in the time domain on a fixed grid.
  • For colored noise backgrounds, build target as noise first, then inject; stationary Gaussian noise can be added in a separate noise-focused package.

See also