Skip to content

Commit ad2c941

Browse files
committed
FEATURE: Relative Binning in bilby
See merge request lscsoft/bilby!1105
2 parents 5c05802 + 7677976 commit ad2c941

File tree

12 files changed

+1103
-47
lines changed

12 files changed

+1103
-47
lines changed

AUTHORS.md

+2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ Aditya Vijaykumar
99
Andrew Kim
1010
Andrew Miller
1111
Antoni Ramos-Buades
12+
Apratim Ganguly
1213
Avi Vajpeyi
1314
Bruce Edelman
1415
Carl-Johan Haster
@@ -40,6 +41,7 @@ Karl Wette
4041
Katerina Chatziioannou
4142
Kaylee de Soto
4243
Khun Sang Phukon
44+
Kruthi Krishna
4345
Kshipraa Athar
4446
Leslie Wade
4547
Liting Xiao

bilby/gw/conversion.py

+2-4
Original file line numberDiff line numberDiff line change
@@ -1407,11 +1407,9 @@ def compute_snrs(sample, likelihood, npool=1):
14071407
if likelihood is not None:
14081408
if isinstance(sample, dict):
14091409
likelihood.parameters.update(sample)
1410-
signal_polarizations =\
1411-
likelihood.waveform_generator.frequency_domain_strain(sample)
1410+
signal_polarizations = likelihood.waveform_generator.frequency_domain_strain(sample)
14121411
for ifo in likelihood.interferometers:
1413-
per_detector_snr = likelihood.calculate_snrs(
1414-
signal_polarizations, ifo)
1412+
per_detector_snr = likelihood.calculate_snrs(signal_polarizations, ifo)
14151413
sample['{}_matched_filter_snr'.format(ifo.name)] =\
14161414
per_detector_snr.complex_matched_filter_snr
14171415
sample['{}_optimal_snr'.format(ifo.name)] = \

bilby/gw/detector/interferometer.py

+17-10
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,7 @@ def antenna_response(self, ra, dec, time, psi, mode):
285285
else:
286286
return 0
287287

288-
def get_detector_response(self, waveform_polarizations, parameters):
288+
def get_detector_response(self, waveform_polarizations, parameters, frequencies=None):
289289
""" Get the detector response for a particular waveform
290290
291291
Parameters
@@ -294,11 +294,21 @@ def get_detector_response(self, waveform_polarizations, parameters):
294294
polarizations of the waveform
295295
parameters: dict
296296
parameters describing position and time of arrival of the signal
297-
297+
frequencies: array-like, optional
298+
The frequency values to evaluate the response at. If
299+
not provided, the response is computed using
300+
:code:`self.frequency_array`. If the frequencies are
301+
specified, no frequency masking is performed.
298302
Returns
299303
=======
300304
array_like: A 3x3 array representation of the detector response (signal observed in the interferometer)
301305
"""
306+
if frequencies is None:
307+
frequencies = self.frequency_array[self.frequency_mask]
308+
mask = self.frequency_mask
309+
else:
310+
mask = np.ones(len(frequencies), dtype=bool)
311+
302312
signal = {}
303313
for mode in waveform_polarizations.keys():
304314
det_response = self.antenna_response(
@@ -308,9 +318,7 @@ def get_detector_response(self, waveform_polarizations, parameters):
308318
parameters['psi'], mode)
309319

310320
signal[mode] = waveform_polarizations[mode] * det_response
311-
signal_ifo = sum(signal.values())
312-
313-
signal_ifo *= self.strain_data.frequency_mask
321+
signal_ifo = sum(signal.values()) * mask
314322

315323
time_shift = self.time_delay_from_geocenter(
316324
parameters['ra'], parameters['dec'], parameters['geocent_time'])
@@ -320,12 +328,11 @@ def get_detector_response(self, waveform_polarizations, parameters):
320328
dt_geocent = parameters['geocent_time'] - self.strain_data.start_time
321329
dt = dt_geocent + time_shift
322330

323-
signal_ifo[self.strain_data.frequency_mask] = signal_ifo[self.strain_data.frequency_mask] * np.exp(
324-
-1j * 2 * np.pi * dt * self.strain_data.frequency_array[self.strain_data.frequency_mask])
331+
signal_ifo[mask] = signal_ifo[mask] * np.exp(-1j * 2 * np.pi * dt * frequencies)
325332

326-
signal_ifo[self.strain_data.frequency_mask] *= self.calibration_model.get_calibration_factor(
327-
self.strain_data.frequency_array[self.strain_data.frequency_mask],
328-
prefix='recalib_{}_'.format(self.name), **parameters)
333+
signal_ifo[mask] *= self.calibration_model.get_calibration_factor(
334+
frequencies, prefix='recalib_{}_'.format(self.name), **parameters
335+
)
329336

330337
return signal_ifo
331338

bilby/gw/likelihood/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from .basic import BasicGravitationalWaveTransient
33
from .roq import BilbyROQParamsRangeError, ROQGravitationalWaveTransient
44
from .multiband import MBGravitationalWaveTransient
5+
from .relative import RelativeBinningGravitationalWaveTransient
56

67
from ..source import lal_binary_black_hole
78
from ..waveform_generator import WaveformGenerator

bilby/gw/likelihood/base.py

+25-4
Original file line numberDiff line numberDiff line change
@@ -265,8 +265,10 @@ def calculate_snrs(self, waveform_polarizations, interferometer):
265265
The bilby interferometer object
266266
267267
"""
268-
signal = interferometer.get_detector_response(
269-
waveform_polarizations, self.parameters)
268+
signal = self._compute_full_waveform(
269+
signal_polarizations=waveform_polarizations,
270+
interferometer=interferometer,
271+
)
270272
_mask = interferometer.frequency_mask
271273

272274
if 'recalib_index' in self.parameters:
@@ -614,8 +616,10 @@ def generate_time_sample_from_marginalized_likelihood(
614616
for ifo in self.interferometers:
615617
ifo_length = len(ifo.frequency_domain_strain)
616618
mask = ifo.frequency_mask
617-
signal = ifo.get_detector_response(
618-
signal_polarizations, self.parameters)
619+
signal = self._compute_full_waveform(
620+
signal_polarizations=signal_polarizations,
621+
interferometer=ifo,
622+
)
619623
signal_long[:ifo_length] = signal
620624
data[:ifo_length] = np.conj(ifo.frequency_domain_strain)
621625
psd[:ifo_length][mask] = ifo.power_spectral_density_array[mask]
@@ -703,6 +707,23 @@ def _calculate_inner_products(self, signal_polarizations):
703707
h_inner_h += per_detector_snr.optimal_snr_squared
704708
return d_inner_h, h_inner_h
705709

710+
def _compute_full_waveform(self, signal_polarizations, interferometer):
711+
"""
712+
Project the waveform polarizations against the interferometer
713+
response. This is useful for likelihood classes that don't
714+
use the full frequency array, e.g., the relative binning
715+
likelihood.
716+
717+
Parameters
718+
==========
719+
signal_polarizations: dict
720+
Dictionary containing the waveform evaluated at
721+
:code:`interferometer.frequency_array`.
722+
interferometer: bilby.gw.detector.Interferometer
723+
Interferometer to compute the response with respect to.
724+
"""
725+
return interferometer.get_detector_response(signal_polarizations, self.parameters)
726+
706727
def generate_phase_sample_from_marginalized_likelihood(
707728
self, signal_polarizations=None):
708729
r"""

0 commit comments

Comments
 (0)