Skip to content

Waveform

gwmock_signal.waveform

Waveform generation (PyCBC-backed and extensible).

WaveformFactory

Registry and dispatcher for time-domain waveform generators.

On construction, every name returned by PyCBC td_approximants is registered and mapped to pycbc_waveform_wrapper. You may register additional names pointing at custom callables. See package docs for examples.

Source code in src/gwmock_signal/waveform/factory.py
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
class WaveformFactory:
    """Registry and dispatcher for time-domain waveform generators.

    On construction, every name returned by PyCBC
    [`td_approximants`](https://pycbc.org/pycbc/latest/html/waveform.html) is
    registered and mapped to ``pycbc_waveform_wrapper``.
    You may register additional names pointing at custom callables. See package docs for examples.
    """

    def __init__(self) -> None:
        """Build the registry of built-in PyCBC TD approximants.

        Note:
            Importing PyCBC and enumerating approximants can be slow; reuse one factory
            instance in tight loops instead of creating many factories.
        """
        self._models: dict[str, Callable[..., dict[str, TimeSeries]]] = dict.fromkeys(
            td_approximants(),
            pycbc_waveform_wrapper,
        )

    def register_model(self, name: str, factory_func: Callable[..., Any] | str) -> None:
        """Register or overwrite a waveform model under ``name``.

        Args:
            name: Key used with ``WaveformFactory.generate`` and ``WaveformFactory.get_model``.
            factory_func: Callable that accepts merged waveform kwargs (including
                ``waveform_model``, ``tc``, ``sampling_frequency``, ``minimum_frequency``)
                and returns a dict of GWpy ``plus``/``cross`` series, **or** an import
                string: either ``module.path:callable`` (colon before the name) or
                ``package.module.callable`` (split on the last ``.`` for attribute lookup).

        Raises:
            ImportError: If a string path does not refer to an importable module.
            AttributeError: If the imported module has no such callable attribute.
            ValueError: If factory_func string is neither 'module.path:callable' nor 'package.module.callable'.
            TypeError: Registered model is not callable.
        """
        if isinstance(factory_func, str):
            if ":" in factory_func:
                module_path, func_name = factory_func.split(":", 1)
            else:
                if "." not in factory_func:
                    raise ValueError("factory_func string must be 'module.path:callable' or 'package.module.callable'")
                module_path, func_name = factory_func.rsplit(".", 1)
            module = importlib.import_module(module_path)
            factory_func = getattr(module, func_name)

        if not callable(factory_func):
            raise TypeError(f"Registered model '{name}' is not callable")

        self._models[name] = factory_func
        logger.info("Registered waveform model: %s", name)

    def get_model(self, name: str) -> Callable[..., dict[str, TimeSeries]]:
        """Look up the generator function registered for ``name``.

        Args:
            name: Registered model name (built-in approximant or custom).

        Returns:
            The callable registered for this name.

        Raises:
            ValueError: If ``name`` is not registered.
        """
        if name in self._models:
            return self._models[name]
        raise ValueError(f"Waveform model '{name}' not found. Available: {list(self._models.keys())}.")

    def list_models(self) -> list[str]:
        """Return every registered waveform model name, in dict iteration order.

        Returns:
            List of keys (PyCBC TD approximants plus any custom registrations).
        """
        return list(self._models.keys())

    def generate(
        self,
        waveform_model: str,
        parameters: dict[str, Any],
        **extra_params: Any,
    ) -> dict[str, TimeSeries]:
        """Generate polarizations by calling the registered model with merged parameters.

        The callable is invoked with ``waveform_model``, then entries from ``parameters``,
        then ``extra_params`` (later keys override earlier ones).

        Args:
            waveform_model: Name of the registered model to run.
            parameters: Injection parameters (e.g. ``tc``, masses, spins) merged first.
            **extra_params: Additional fixed settings (e.g. ``sampling_frequency``,
                ``minimum_frequency``) merged after ``parameters``; later keys override.

        Returns:
            Dict whose keys are the strings ``plus`` and ``cross``, each mapping to a
            GWpy [`TimeSeries`](https://gwpy.github.io/docs/latest/api/gwpy.timeseries.TimeSeries/).

        Raises:
            ValueError: If ``waveform_model`` is not registered.
            TypeError: If the underlying generator is called with invalid arguments.
        """
        waveform_func = self.get_model(waveform_model)
        if "waveform_model" in parameters or "waveform_model" in extra_params:
            raise ValueError("Do not pass 'waveform_model' in parameters/extra_params.")
        all_params: dict[str, Any] = {**parameters, **extra_params, "waveform_model": waveform_model}
        return waveform_func(**all_params)

__init__()

Build the registry of built-in PyCBC TD approximants.

Note

Importing PyCBC and enumerating approximants can be slow; reuse one factory instance in tight loops instead of creating many factories.

Source code in src/gwmock_signal/waveform/factory.py
30
31
32
33
34
35
36
37
38
39
40
def __init__(self) -> None:
    """Build the registry of built-in PyCBC TD approximants.

    Note:
        Importing PyCBC and enumerating approximants can be slow; reuse one factory
        instance in tight loops instead of creating many factories.
    """
    self._models: dict[str, Callable[..., dict[str, TimeSeries]]] = dict.fromkeys(
        td_approximants(),
        pycbc_waveform_wrapper,
    )

generate(waveform_model, parameters, **extra_params)

Generate polarizations by calling the registered model with merged parameters.

The callable is invoked with waveform_model, then entries from parameters, then extra_params (later keys override earlier ones).

Parameters:

Name Type Description Default
waveform_model str

Name of the registered model to run.

required
parameters dict[str, Any]

Injection parameters (e.g. tc, masses, spins) merged first.

required
**extra_params Any

Additional fixed settings (e.g. sampling_frequency, minimum_frequency) merged after parameters; later keys override.

{}

Returns:

Type Description
dict[str, TimeSeries]

Dict whose keys are the strings plus and cross, each mapping to a

dict[str, TimeSeries]

GWpy TimeSeries.

Raises:

Type Description
ValueError

If waveform_model is not registered.

TypeError

If the underlying generator is called with invalid arguments.

Source code in src/gwmock_signal/waveform/factory.py
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
def generate(
    self,
    waveform_model: str,
    parameters: dict[str, Any],
    **extra_params: Any,
) -> dict[str, TimeSeries]:
    """Generate polarizations by calling the registered model with merged parameters.

    The callable is invoked with ``waveform_model``, then entries from ``parameters``,
    then ``extra_params`` (later keys override earlier ones).

    Args:
        waveform_model: Name of the registered model to run.
        parameters: Injection parameters (e.g. ``tc``, masses, spins) merged first.
        **extra_params: Additional fixed settings (e.g. ``sampling_frequency``,
            ``minimum_frequency``) merged after ``parameters``; later keys override.

    Returns:
        Dict whose keys are the strings ``plus`` and ``cross``, each mapping to a
        GWpy [`TimeSeries`](https://gwpy.github.io/docs/latest/api/gwpy.timeseries.TimeSeries/).

    Raises:
        ValueError: If ``waveform_model`` is not registered.
        TypeError: If the underlying generator is called with invalid arguments.
    """
    waveform_func = self.get_model(waveform_model)
    if "waveform_model" in parameters or "waveform_model" in extra_params:
        raise ValueError("Do not pass 'waveform_model' in parameters/extra_params.")
    all_params: dict[str, Any] = {**parameters, **extra_params, "waveform_model": waveform_model}
    return waveform_func(**all_params)

get_model(name)

Look up the generator function registered for name.

Parameters:

Name Type Description Default
name str

Registered model name (built-in approximant or custom).

required

Returns:

Type Description
Callable[..., dict[str, TimeSeries]]

The callable registered for this name.

Raises:

Type Description
ValueError

If name is not registered.

Source code in src/gwmock_signal/waveform/factory.py
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
def get_model(self, name: str) -> Callable[..., dict[str, TimeSeries]]:
    """Look up the generator function registered for ``name``.

    Args:
        name: Registered model name (built-in approximant or custom).

    Returns:
        The callable registered for this name.

    Raises:
        ValueError: If ``name`` is not registered.
    """
    if name in self._models:
        return self._models[name]
    raise ValueError(f"Waveform model '{name}' not found. Available: {list(self._models.keys())}.")

list_models()

Return every registered waveform model name, in dict iteration order.

Returns:

Type Description
list[str]

List of keys (PyCBC TD approximants plus any custom registrations).

Source code in src/gwmock_signal/waveform/factory.py
91
92
93
94
95
96
97
def list_models(self) -> list[str]:
    """Return every registered waveform model name, in dict iteration order.

    Returns:
        List of keys (PyCBC TD approximants plus any custom registrations).
    """
    return list(self._models.keys())

register_model(name, factory_func)

Register or overwrite a waveform model under name.

Parameters:

Name Type Description Default
name str

Key used with WaveformFactory.generate and WaveformFactory.get_model.

required
factory_func Callable[..., Any] | str

Callable that accepts merged waveform kwargs (including waveform_model, tc, sampling_frequency, minimum_frequency) and returns a dict of GWpy plus/cross series, or an import string: either module.path:callable (colon before the name) or package.module.callable (split on the last . for attribute lookup).

required

Raises:

Type Description
ImportError

If a string path does not refer to an importable module.

AttributeError

If the imported module has no such callable attribute.

ValueError

If factory_func string is neither 'module.path:callable' nor 'package.module.callable'.

TypeError

Registered model is not callable.

Source code in src/gwmock_signal/waveform/factory.py
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
def register_model(self, name: str, factory_func: Callable[..., Any] | str) -> None:
    """Register or overwrite a waveform model under ``name``.

    Args:
        name: Key used with ``WaveformFactory.generate`` and ``WaveformFactory.get_model``.
        factory_func: Callable that accepts merged waveform kwargs (including
            ``waveform_model``, ``tc``, ``sampling_frequency``, ``minimum_frequency``)
            and returns a dict of GWpy ``plus``/``cross`` series, **or** an import
            string: either ``module.path:callable`` (colon before the name) or
            ``package.module.callable`` (split on the last ``.`` for attribute lookup).

    Raises:
        ImportError: If a string path does not refer to an importable module.
        AttributeError: If the imported module has no such callable attribute.
        ValueError: If factory_func string is neither 'module.path:callable' nor 'package.module.callable'.
        TypeError: Registered model is not callable.
    """
    if isinstance(factory_func, str):
        if ":" in factory_func:
            module_path, func_name = factory_func.split(":", 1)
        else:
            if "." not in factory_func:
                raise ValueError("factory_func string must be 'module.path:callable' or 'package.module.callable'")
            module_path, func_name = factory_func.rsplit(".", 1)
        module = importlib.import_module(module_path)
        factory_func = getattr(module, func_name)

    if not callable(factory_func):
        raise TypeError(f"Registered model '{name}' is not callable")

    self._models[name] = factory_func
    logger.info("Registered waveform model: %s", name)

pycbc_waveform_wrapper(tc, sampling_frequency, minimum_frequency, waveform_model, **kwargs)

Generate plus and cross polarizations with PyCBC and return GWpy time series.

Calls pycbc.waveform.get_td_waveform with delta_t = 1/sampling_frequency, f_lower = minimum_frequency, and approximant=waveform_model, then shifts both polarizations by tc in GPS seconds and converts them to GWpy.

Parameters:

Name Type Description Default
tc float

GPS time of coalescence (seconds); added to each series' start_time.

required
sampling_frequency float

Sample rate in Hz for delta_t.

required
minimum_frequency float

Low-frequency cutoff in Hz, passed to PyCBC as f_lower.

required
waveform_model str

PyCBC time-domain approximant name (e.g. IMRPhenomD).

required
**kwargs Any

Additional arguments for get_td_waveform (masses, spins, distance, etc., depending on the approximant).

{}

Returns:

Type Description
dict[str, TimeSeries]

Mapping whose keys are the strings plus and cross; each value is a

dict[str, TimeSeries]

GWpy TimeSeries.

Raises:

Type Description
ValueError

sampling_frequency must be > 0.

Note

Invalid parameters may raise exceptions from PyCBC/LAL; see PyCBC waveform docs.

Source code in src/gwmock_signal/waveform/pycbc_wrapper.py
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
def pycbc_waveform_wrapper(
    tc: float,
    sampling_frequency: float,
    minimum_frequency: float,
    waveform_model: str,
    **kwargs: Any,
) -> dict[str, TimeSeries]:
    """Generate plus and cross polarizations with PyCBC and return GWpy time series.

    Calls [`pycbc.waveform.get_td_waveform`](https://pycbc.org/pycbc/latest/html/waveform.html)
    with ``delta_t = 1/sampling_frequency``,
    ``f_lower = minimum_frequency``, and ``approximant=waveform_model``, then shifts both
    polarizations by ``tc`` in GPS seconds and converts them to GWpy.

    Args:
        tc: GPS time of coalescence (seconds); added to each series' ``start_time``.
        sampling_frequency: Sample rate in Hz for ``delta_t``.
        minimum_frequency: Low-frequency cutoff in Hz, passed to PyCBC as ``f_lower``.
        waveform_model: PyCBC time-domain approximant name (e.g. ``IMRPhenomD``).
        **kwargs: Additional arguments for ``get_td_waveform`` (masses, spins, distance,
            etc., depending on the approximant).

    Returns:
        Mapping whose keys are the strings ``plus`` and ``cross``; each value is a
        GWpy [`TimeSeries`](https://gwpy.github.io/docs/latest/api/gwpy.timeseries.TimeSeries/).

    Raises:
        ValueError: sampling_frequency must be > 0.

    Note:
        Invalid parameters may raise exceptions from PyCBC/LAL; see PyCBC waveform docs.
    """
    if sampling_frequency <= 0:
        raise ValueError("sampling_frequency must be > 0")

    hp, hc = get_td_waveform(
        approximant=waveform_model,
        delta_t=1 / sampling_frequency,
        f_lower=minimum_frequency,
        **kwargs,
    )
    hp.start_time += tc
    hc.start_time += tc
    return {
        "plus": TimeSeries.from_pycbc(hp, copy=True),
        "cross": TimeSeries.from_pycbc(hc, copy=True),
    }

For usage examples, see the User guide — Waveform examples.