diff --git a/bilby/gw/source.py b/bilby/gw/source.py index bd2cd1b6..08b7e5f2 100644 --- a/bilby/gw/source.py +++ b/bilby/gw/source.py @@ -16,8 +16,22 @@ """ -def gwsignal_binary_black_hole(frequency_array, mass_1, mass_2, luminosity_distance, a_1, tilt_1, - phi_12, a_2, tilt_2, phi_jl, theta_jn, phase, **kwargs): +def _base_gwsignal_binary_black_hole( + frequency_array, + mass_1, + mass_2, + luminosity_distance, + a_1, + tilt_1, + phi_12, + a_2, + tilt_2, + phi_jl, + theta_jn, + phase, + eccentricity, + mean_per_ano, + **kwargs): """ A binary black hole waveform model using GWsignal @@ -48,6 +62,10 @@ def gwsignal_binary_black_hole(frequency_array, mass_1, mass_2, luminosity_dista Angle between the total binary angular momentum and the line of sight phase: float The phase at coalescence + eccentricity: float + Orbital eccentricity + mean_per_ano: float + Mean anomaly kwargs: dict Optional keyword arguments Supported arguments: @@ -83,8 +101,8 @@ def gwsignal_binary_black_hole(frequency_array, mass_1, mass_2, luminosity_dista ===== This function is a temporary wrapper to the interface that will likely be significantly changed or removed in a future release. - This version is only intended to be used with `SEOBNRv5HM` and `SEOBNRv5PHM` and - does not have full functionality for other waveform models. + This version is only intended to be used with ``SEOBNRv5HM``, ``SEOBNRv5EHM`` + and ``SEOBNRv5PHM`` and does not have full functionality for other waveform models. """ from lalsimulation.gwsignal import GenerateFDWaveform @@ -103,7 +121,7 @@ def gwsignal_binary_black_hole(frequency_array, mass_1, mass_2, luminosity_dista waveform_kwargs.update(kwargs) waveform_approximant = waveform_kwargs['waveform_approximant'] - if waveform_approximant not in ["SEOBNRv5HM", "SEOBNRv5PHM"]: + if waveform_approximant not in ["SEOBNRv5HM", "SEOBNRv5EHM", "SEOBNRv5PHM"]: if waveform_approximant == "IMRPhenomXPHM": logger.warning("The new waveform interface is unreviewed for this model" + "and it is only intended for testing.") @@ -141,9 +159,7 @@ def gwsignal_binary_black_hole(frequency_array, mass_1, mass_2, luminosity_dista phi_12=phi_12, a_1=a_1, a_2=a_2, mass_1=mass_1 * utils.solar_mass, mass_2=mass_2 * utils.solar_mass, reference_frequency=reference_frequency, phase=phase) - eccentricity = 0.0 longitude_ascending_nodes = 0.0 - mean_per_ano = 0.0 # Check if conditioning is needed condition = 0 @@ -252,6 +268,62 @@ def gwsignal_binary_black_hole(frequency_array, mass_1, mass_2, luminosity_dista return dict(plus=h_plus, cross=h_cross) +def gwsignal_binary_black_hole(frequency_array, mass_1, mass_2, luminosity_distance, a_1, tilt_1, + phi_12, a_2, tilt_2, phi_jl, theta_jn, phase, **kwargs): + + return _base_gwsignal_binary_black_hole( + frequency_array=frequency_array, + mass_1=mass_1, + mass_2=mass_2, + luminosity_distance=luminosity_distance, + a_1=a_1, + tilt_1=tilt_1, + phi_12=phi_12, + a_2=a_2, + tilt_2=tilt_2, + phi_jl=phi_jl, + theta_jn=theta_jn, + phase=phase, + eccentricity=0, + mean_per_ano=0, + **kwargs) + + +def gwsignal_eccentric_binary_black_hole( + frequency_array, + mass_1, + mass_2, + luminosity_distance, + a_1, + tilt_1, + phi_12, + a_2, + tilt_2, + phi_jl, + theta_jn, + phase, + eccentricity, + mean_per_ano, + **kwargs): + + return _base_gwsignal_binary_black_hole( + frequency_array=frequency_array, + mass_1=mass_1, + mass_2=mass_2, + luminosity_distance=luminosity_distance, + a_1=a_1, + tilt_1=tilt_1, + phi_12=phi_12, + a_2=a_2, + tilt_2=tilt_2, + phi_jl=phi_jl, + theta_jn=theta_jn, + phase=phase, + eccentricity=eccentricity, + mean_per_ano=mean_per_ano, + **kwargs) + + def lal_binary_black_hole( frequency_array, mass_1, mass_2, luminosity_distance, a_1, tilt_1, phi_12, a_2, tilt_2, phi_jl, theta_jn, phase, **kwargs): diff --git a/test/gw/source_test.py b/test/gw/source_test.py index be9072fb..19108238 100644 --- a/test/gw/source_test.py +++ b/test/gw/source_test.py @@ -1,13 +1,15 @@ -import unittest import logging -import pytest +import random +import unittest +from copy import copy +from unittest.mock import patch +import astropy.units as u import bilby import lal import lalsimulation - import numpy as np -from copy import copy +import pytest class TestLalBBH(unittest.TestCase): @@ -170,6 +172,7 @@ def test_waveform_error_raising(self): bilby.gw.source.gwsignal_binary_black_hole( self.frequency_array, **raise_error_parameters ) + # def test_gwsignal_bbh_works_without_waveform_parameters(self): # self.assertIsInstance( # bilby.gw.source.gwsignal_binary_black_hole( @@ -193,6 +196,79 @@ def test_gwsignal_lal_bbh_consistency(self): np.allclose(hpc_gwsignal["cross"], hpc_lal["cross"], atol=0, rtol=1e-7) ) + def test_argument_passed_to_generate_waveform(self): + # here we test the behaviour of the function "gwsignal_binary_black_hole" + # until the execution of the "generate_fd_waveform" in gwsignal. In particular + # the actual generate_fd_waveform is not called and only the parameters passed to + # the function are checked. + # The test does not require gwsignal to support any of the approximants or parameters. + from lalsimulation.gwsignal.models.pyseobnr_model import SEOBNRv5PHM as wf_gen + + class MyException(Exception): + pass + + with patch.object( + lalsimulation.gwsignal.models, + "gwsignal_get_waveform_generator", + autospec=True, + ) as mock_gwsignal_get_waveform_generator: + + with patch.object( + wf_gen, "generate_fd_waveform", autospec=True + ) as mock_wgen_gen_fd: + mock_wgen_gen_fd.side_effect = MyException( + "__not_the_string_input_domain_error__" + ) + mock_gwsignal_get_waveform_generator.return_value = wf_gen() + + for current_param, gwsignal_target_param in ( + "eccentricity", + "eccentricity", + ), ("mean_per_ano", "meanPerAno"): + mock_wgen_gen_fd.reset_mock() + mock_gwsignal_get_waveform_generator.reset_mock() + + parameters = self.parameters.copy() + parameters["waveform_approximant"] = "SEOBNRv5PHM" + parameters.update(self.waveform_kwargs | {"eccentricity": 0, "mean_per_ano": 0}) + parameters[current_param] = random.uniform(0, 0.3) + + with self.assertRaises(MyException): + bilby.gw.source.gwsignal_eccentric_binary_black_hole( + self.frequency_array, **parameters + ) + + # check we are calling the mock generator + mock_gwsignal_get_waveform_generator.assert_called_once_with( + parameters["waveform_approximant"] + ) + + # checks generated_fd_waveform is called + mock_wgen_gen_fd.assert_called_once() + + # checks parameters are passed: this is done inside the + self.assertIsInstance( + mock_wgen_gen_fd.call_args_list[0].args[0], wf_gen + ) + + # check if the currently modified parameter is dA22, dtau32 etc + self.assertIn( + gwsignal_target_param, mock_wgen_gen_fd.call_args_list[0].kwargs + ) + converted_param = mock_wgen_gen_fd.call_args_list[0].kwargs[ + gwsignal_target_param + ] + + if converted_param.unit == u.rad: + self.assertEqual( + converted_param.value, parameters[current_param] + ) + + elif converted_param.unit == u.dimensionless_unscaled: + self.assertEqual( + float(converted_param), parameters[current_param] + ) + class TestLalBNS(unittest.TestCase): def setUp(self):