Coverage for pesummary/gw/pycbc.py: 41.9%
43 statements
« prev ^ index » next coverage.py v7.4.4, created at 2025-11-05 13:38 +0000
« prev ^ index » next coverage.py v7.4.4, created at 2025-11-05 13:38 +0000
1# Licensed under an MIT style license -- see LICENSE.md
3from pesummary.utils.utils import logger, import_error_msg
4import numpy as np
5try:
6 import pycbc.psd
7 from pycbc.filter import sigma, overlap_cplx
8 from pycbc import pnutils
9except ImportError:
10 logger.warning(import_error_msg.format("pycbc"))
12__author__ = ["Charlie Hoy <charlie.hoy@ligo.org>"]
15def optimal_snr(
16 template, psd, low_frequency_cutoff=20., high_frequency_cutoff=1024.
17):
18 """Calculate the loudness of the waveform. See Duncan Brown’s thesis for
19 details
21 Parameters
22 ----------
23 template: pycbc.type.frequencyseries.FrequencySeries
24 waveform you wish to calculate the loudness for
25 psd: pycbc.type.frequencyseries.FrequencySeries
26 psd to use when calculating the integral
27 low_frequency_cutoff: float, optional
28 low frequency cut-off to start calculating the integral
29 """
30 return sigma(
31 template, psd, low_frequency_cutoff=low_frequency_cutoff,
32 high_frequency_cutoff=high_frequency_cutoff
33 )
36def compute_the_overlap(template, data, psd, **kwargs):
37 """Wrapper for pycbc.filter.overlap_cplx
39 Parameters
40 ----------
41 template: pycbc.types.frequencyseries.FrequencySeries
42 frequency domain template
43 data: pycbc.types.frequencyseries.FrequencySeries
44 frequency domain data
45 psd: pycbc.types.frequencyseries.FrequencySeries
46 the PSD to use when computing the overlap
47 **kwargs: dict, optional
48 all additional kwargs passed to pycbc.filter.overlap_cplx
49 """
50 return overlap_cplx(template, data, psd=psd, **kwargs)
53def pycbc_default_psd(
54 mass_1=None, mass_2=None, chi_eff=None, spin_1z=None, spin_2z=None, psd=None,
55 duration=None, df=None, detectors=["H1", "L1"], f_low=20., f_final=1024.
56):
57 """Return a dictionary of pycbc psds for a given detector network
59 Parameters
60 ----------
61 mass_1: np.ndarray, optional
62 array of primary masses to use when working out the duration and
63 consequently delta_f for the PSD
64 mass_2: np.ndarray, optional
65 array of secondary masses to use when working out the duration and
66 consequently delta_f for the PSD
67 chi_eff: np.ndarray, optional
68 array of effective spin samples to use when working out the duration and
69 consequently delta_f for the psd. Used as an approximant when spin_1z
70 and spin_2z samples are not provided
71 spin_1z: np.ndarray, optional
72 array of samples for the primary spin parallel to the orbital angular
73 momentum to use when computing the duration and consequently delta_f for
74 the psd
75 spin_2z: np.ndarray, optional
76 array of samples for the secondary spin parallel to the orbital angular
77 momentum to use when computing the duration and consequently delta_f for
78 the psd
79 psd: func, optional
80 pycbc function to use when calculating the default psd. Default is
81 aLIGOZeroDetHighPower
82 duration: np.npdarray
83 array containing the durations for a given set of waveform. The maximum
84 duration is then used to calculate the delta_f for the PSD
85 detectors: list, optional
86 the detector network you wish to calculate psds for
87 f_low: float, optional
88 the low frequency to start computing the psd from. Default 20Hz
89 f_final: float, optional
90 the highest frequency to finish computing the psd. Default 1024Hz
91 """
92 if psd is None:
93 psd = pycbc.psd.aLIGOZeroDetHighPower
94 elif isinstance(psd, str):
95 psd = getattr(pycbc.psd, psd)
97 logger.warning("No PSD provided. Using '{}' for the psd".format(psd.__name__))
98 _required = [mass_1, mass_2]
99 if df is None:
100 cond1 = all(i is not None for i in _required + [spin_1z, spin_2z])
101 cond2 = all(i is not None for i in _required + [chi_eff])
102 if cond1 and duration is None:
103 duration = pnutils.get_imr_duration(
104 mass_1, mass_2, spin_1z, spin_2z, f_low
105 )
106 elif cond2 and duration is None:
107 logger.warning(
108 "Could not find samples for spin_1z and spin_2z. We will "
109 "assume that spin_1z = spin_2z = chi_eff to estimate the "
110 "maximum IMR duration. This is used to estimate delta_f for "
111 "the PSD."
112 )
113 duration = pnutils.get_imr_duration(
114 mass_1, mass_2, chi_eff, chi_eff, f_low
115 )
116 elif duration is None:
117 raise ValueError(
118 "Please provide either 'spin_1z' and 'spin_2z' or 'chi_eff' "
119 "samples to estimate the maximum IMR duration. This is used to "
120 "estimate delta_f for the PSD."
121 )
122 duration = np.max(duration)
123 t_len = 2**np.ceil(np.log2(duration) + 1)
124 df = 1. / t_len
125 flen = int(f_final / df) + 1
126 _psd = psd(flen, df, f_low)
127 setattr(_psd, "__name__", psd.__name__)
128 return {ifo: _psd for ifo in detectors}
131def interpolate_psd(psd, low_freq_cutoff, delta_f):
132 """Interpolate PSD to a new delta_f
134 Parameters
135 ----------
136 low_freq_cutoff: float
137 Frequencies below this value are set to zero.
138 delta_f : float, optional
139 Frequency resolution of the frequency series in Hertz.
140 """
141 from pesummary.gw.file.psd import PSD
142 from pycbc.psd import estimate
143 if isinstance(psd, PSD):
144 psd = psd.to_pycbc(low_freq_cutoff)
145 return estimate.interpolate(psd, delta_f)