From 90a0a745e2f4b94fc6f2d1adc81c39be4727e64c Mon Sep 17 00:00:00 2001 From: ConWea Date: Fri, 14 Jul 2023 17:37:31 +0100 Subject: [PATCH 01/36] Initial testing to include higher order modes --- BBHX_PhenomD.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/BBHX_PhenomD.py b/BBHX_PhenomD.py index 2aeee61..6a6125e 100644 --- a/BBHX_PhenomD.py +++ b/BBHX_PhenomD.py @@ -160,8 +160,13 @@ def bbhx_fd(ifos=None, run_phenomd=True, else: freqs = sample_points - modes = [(2,2)] # More modes if not phenomd - direct = False # See the BBHX documentation + compress=True + modes = [(2,2)] + direct = False + if not run_phenomd: + modes = params['modes'] # More modes if not phenomd + compress = False # If True, combine harmonics into single channel waveforms. (Default: True) + direct = True # If True, directly compute the waveform without interpolation. (Default: False) fill = True # See the BBHX documentation squeeze = True # See the BBHX documentation length = 1024 # An internal generation parameter, not an output parameter @@ -173,8 +178,9 @@ def bbhx_fd(ifos=None, run_phenomd=True, beta, psi, t_ref, freqs=freqs, modes=modes, direct=direct, fill=fill, squeeze=squeeze, length=length, t_obs_start=t_obs_start/YRSID_SI, - t_obs_end=t_obs_end, - shift_t_limits=shift_t_limits)[0] + t_obs_end=t_obs_end, compress=compress, + shift_t_limits=shift_t_limits) # Remeber that there was a [0] previously! + wanted = {} From da8d9051f8ad0748226d84f7874ec3cca540c525 Mon Sep 17 00:00:00 2001 From: ConWea Date: Wed, 19 Jul 2023 13:22:03 +0100 Subject: [PATCH 02/36] Edit to include option to use gpus --- BBHX_PhenomD.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/BBHX_PhenomD.py b/BBHX_PhenomD.py index 6a6125e..5006fe8 100644 --- a/BBHX_PhenomD.py +++ b/BBHX_PhenomD.py @@ -8,11 +8,13 @@ @functools.lru_cache(maxsize=128) -def get_waveform_genner(log_mf_min, run_phenomd=True): +def get_waveform_genner(log_mf_min, run_phenomd=True, use_gpu=False): # See below where this function is called for description of how we handle # log_mf_min. mf_min = math.exp(log_mf_min/25.) - wave_gen = BBHWaveformFD(amp_phase_kwargs=dict(run_phenomd=run_phenomd, mf_min=mf_min)) + wave_gen = BBHWaveformFD(amp_phase_kwargs=dict(run_phenomd=run_phenomd, + mf_min=mf_min), + use_gpu=use_gpu) return wave_gen @functools.lru_cache(maxsize=10) @@ -45,7 +47,7 @@ def imr_duration(**params): return time_length * 1.1 def interpolated_tf(m1, m2): - # Using findchirp_chirptime in PyCBC to calculate + # Using findchirp_chirptime in PyCBC to calculate # the time-frequency track of dominant mode to get # the corresponding `f_min` for `t_obs_start`. freq_array = np.logspace(-4, 0, num=10) @@ -55,7 +57,7 @@ def interpolated_tf(m1, m2): tf_track = interp1d(t_array, freq_array) return tf_track -def bbhx_fd(ifos=None, run_phenomd=True, +def bbhx_fd(ifos=None, run_phenomd=True, use_gpu=False, ref_frame='LISA', sample_points=None, **params): if ifos is None: @@ -140,7 +142,8 @@ def bbhx_fd(ifos=None, run_phenomd=True, # frequency. The factor of 25 ensures reasonable spacing while doing this. # So we round down to the nearest 1/25 of the logarithm of the frequency log_mf_min = int(math.log(f_min*MTSUN_SI*(m1+m2)) * 25) - wave_gen = get_waveform_genner(log_mf_min, run_phenomd=run_phenomd) + wave_gen = get_waveform_genner(log_mf_min, run_phenomd=run_phenomd, + use_gpu=use_gpu) if sample_points is None: if 'delta_f' in params and params['delta_f'] > 0: From 010b343d96fa18b9bd9e01f2e9a22f1873e8a57e Mon Sep 17 00:00:00 2001 From: ConWea Date: Wed, 19 Jul 2023 17:57:44 +0100 Subject: [PATCH 03/36] Changes to requested mode returns --- BBHX_PhenomD.py | 36 +++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/BBHX_PhenomD.py b/BBHX_PhenomD.py index 5006fe8..287e93e 100644 --- a/BBHX_PhenomD.py +++ b/BBHX_PhenomD.py @@ -163,26 +163,32 @@ def bbhx_fd(ifos=None, run_phenomd=True, use_gpu=False, else: freqs = sample_points - compress=True - modes = [(2,2)] - direct = False - if not run_phenomd: - modes = params['modes'] # More modes if not phenomd - compress = False # If True, combine harmonics into single channel waveforms. (Default: True) - direct = True # If True, directly compute the waveform without interpolation. (Default: False) + compress=True # If True, combine harmonics into single channel waveforms. (Default: True) + direct = False # If True, directly compute the waveform without interpolation. (Default: False) fill = True # See the BBHX documentation squeeze = True # See the BBHX documentation length = 1024 # An internal generation parameter, not an output parameter shift_t_limits = False # Times are relative to merger t_obs_end = 0.0 # Generates ringdown as well! - - wave = wave_gen(m1, m2, a1, a2, - dist, phi_ref, f_ref, inc, lam, - beta, psi, t_ref, freqs=freqs, - modes=modes, direct=direct, fill=fill, squeeze=squeeze, - length=length, t_obs_start=t_obs_start/YRSID_SI, - t_obs_end=t_obs_end, compress=compress, - shift_t_limits=shift_t_limits) # Remeber that there was a [0] previously! + if not run_phenomd: + modes = params['modes'] # More modes if not phenomd + compress = False + direct = True + wave = wave_gen(m1, m2, a1, a2, + dist, phi_ref, f_ref, inc, lam, + beta, psi, t_ref, freqs=freqs, + modes=modes, direct=direct, fill=fill, squeeze=squeeze, + length=length, t_obs_start=t_obs_start/YRSID_SI, + t_obs_end=t_obs_end, compress=compress, + shift_t_limits=shift_t_limits) # Remeber that there was a [0] previously! + else: + wave = wave_gen(m1, m2, a1, a2, + dist, phi_ref, f_ref, inc, lam, + beta, psi, t_ref, freqs=freqs, direct=direct, + fill=fill, squeeze=squeeze, length=length, + t_obs_start=t_obs_start/YRSID_SI, + t_obs_end=t_obs_end, compress=compress, + shift_t_limits=shift_t_limits)[0] wanted = {} From 8208a330e7f1e4d26b738194d972336ab1ee7649 Mon Sep 17 00:00:00 2001 From: Connor Date: Fri, 28 Jul 2023 16:25:40 +0100 Subject: [PATCH 04/36] Restructure to check for modes --- BBHX_PhenomD.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/BBHX_PhenomD.py b/BBHX_PhenomD.py index 287e93e..38e7a96 100644 --- a/BBHX_PhenomD.py +++ b/BBHX_PhenomD.py @@ -170,7 +170,15 @@ def bbhx_fd(ifos=None, run_phenomd=True, use_gpu=False, length = 1024 # An internal generation parameter, not an output parameter shift_t_limits = False # Times are relative to merger t_obs_end = 0.0 # Generates ringdown as well! - if not run_phenomd: + if run_phenomd: + wave = wave_gen(m1, m2, a1, a2, + dist, phi_ref, f_ref, inc, lam, + beta, psi, t_ref, freqs=freqs, direct=direct, + fill=fill, squeeze=squeeze, length=length, + t_obs_start=t_obs_start/YRSID_SI, + t_obs_end=t_obs_end, compress=compress, + shift_t_limits=shift_t_limits)[0] + else: modes = params['modes'] # More modes if not phenomd compress = False direct = True @@ -181,14 +189,6 @@ def bbhx_fd(ifos=None, run_phenomd=True, use_gpu=False, length=length, t_obs_start=t_obs_start/YRSID_SI, t_obs_end=t_obs_end, compress=compress, shift_t_limits=shift_t_limits) # Remeber that there was a [0] previously! - else: - wave = wave_gen(m1, m2, a1, a2, - dist, phi_ref, f_ref, inc, lam, - beta, psi, t_ref, freqs=freqs, direct=direct, - fill=fill, squeeze=squeeze, length=length, - t_obs_start=t_obs_start/YRSID_SI, - t_obs_end=t_obs_end, compress=compress, - shift_t_limits=shift_t_limits)[0] wanted = {} From c532ceb3b73fc80c995510274eed9b782fed8597 Mon Sep 17 00:00:00 2001 From: Connor Date: Tue, 3 Oct 2023 16:52:42 +0100 Subject: [PATCH 05/36] Testing with higher order modes --- BBHX_PhenomD.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/BBHX_PhenomD.py b/BBHX_PhenomD.py index 38e7a96..a5affde 100644 --- a/BBHX_PhenomD.py +++ b/BBHX_PhenomD.py @@ -163,14 +163,20 @@ def bbhx_fd(ifos=None, run_phenomd=True, use_gpu=False, else: freqs = sample_points - compress=True # If True, combine harmonics into single channel waveforms. (Default: True) + # If creating injection of many modes, or just single, compress = True + # will do the same thing. + compress = True # If True, combine harmonics into single channel waveforms. (Default: True) + # Need to give length if direct = False. direct = False # If True, directly compute the waveform without interpolation. (Default: False) fill = True # See the BBHX documentation squeeze = True # See the BBHX documentation length = 1024 # An internal generation parameter, not an output parameter shift_t_limits = False # Times are relative to merger t_obs_end = 0.0 # Generates ringdown as well! + modes = params['modes'] # More modes if not phenomd + if run_phenomd: + # Of run_phenomd, modes is automatically set to (2,2). wave = wave_gen(m1, m2, a1, a2, dist, phi_ref, f_ref, inc, lam, beta, psi, t_ref, freqs=freqs, direct=direct, @@ -179,17 +185,19 @@ def bbhx_fd(ifos=None, run_phenomd=True, use_gpu=False, t_obs_end=t_obs_end, compress=compress, shift_t_limits=shift_t_limits)[0] else: + # This should work with both generating entire injections with + # multiple modes and when computing single modes for inference. + # This will NOT work when wanting the mode information seperately. + # If you want all modes seperated out, the final [0] needs to be + # removed as that is the A TDI stream. modes = params['modes'] # More modes if not phenomd - compress = False - direct = True wave = wave_gen(m1, m2, a1, a2, dist, phi_ref, f_ref, inc, lam, beta, psi, t_ref, freqs=freqs, modes=modes, direct=direct, fill=fill, squeeze=squeeze, length=length, t_obs_start=t_obs_start/YRSID_SI, t_obs_end=t_obs_end, compress=compress, - shift_t_limits=shift_t_limits) # Remeber that there was a [0] previously! - + shift_t_limits=shift_t_limits)[0] wanted = {} From fe51407c47006eac155115ed28a364aba5fbc335 Mon Sep 17 00:00:00 2001 From: Connor Date: Wed, 4 Oct 2023 18:18:26 +0100 Subject: [PATCH 06/36] BBHx testing with higher modes edits. --- BBHX_PhenomD.py | 37 ++++++++++++++----------------------- 1 file changed, 14 insertions(+), 23 deletions(-) diff --git a/BBHX_PhenomD.py b/BBHX_PhenomD.py index a5affde..4247995 100644 --- a/BBHX_PhenomD.py +++ b/BBHX_PhenomD.py @@ -63,6 +63,10 @@ def bbhx_fd(ifos=None, run_phenomd=True, use_gpu=False, if ifos is None: raise Exception("Must define data streams to compute") + # If asking for anything but the (2,2) mode, run PhenomHM + if params['modes'] != [(2, 2)]: + run_phenomd = False + from pycbc.types import FrequencySeries, Array from pycbc import pnutils @@ -175,29 +179,16 @@ def bbhx_fd(ifos=None, run_phenomd=True, use_gpu=False, t_obs_end = 0.0 # Generates ringdown as well! modes = params['modes'] # More modes if not phenomd - if run_phenomd: - # Of run_phenomd, modes is automatically set to (2,2). - wave = wave_gen(m1, m2, a1, a2, - dist, phi_ref, f_ref, inc, lam, - beta, psi, t_ref, freqs=freqs, direct=direct, - fill=fill, squeeze=squeeze, length=length, - t_obs_start=t_obs_start/YRSID_SI, - t_obs_end=t_obs_end, compress=compress, - shift_t_limits=shift_t_limits)[0] - else: - # This should work with both generating entire injections with - # multiple modes and when computing single modes for inference. - # This will NOT work when wanting the mode information seperately. - # If you want all modes seperated out, the final [0] needs to be - # removed as that is the A TDI stream. - modes = params['modes'] # More modes if not phenomd - wave = wave_gen(m1, m2, a1, a2, - dist, phi_ref, f_ref, inc, lam, - beta, psi, t_ref, freqs=freqs, - modes=modes, direct=direct, fill=fill, squeeze=squeeze, - length=length, t_obs_start=t_obs_start/YRSID_SI, - t_obs_end=t_obs_end, compress=compress, - shift_t_limits=shift_t_limits)[0] + + # NOTE: This does not allow for the seperation of multiple modes into + # their own streams. + wave = wave_gen(m1, m2, a1, a2, + dist, phi_ref, f_ref, inc, lam, + beta, psi, t_ref, freqs=freqs, + modes=modes, direct=direct, fill=fill, squeeze=squeeze, + length=length, t_obs_start=t_obs_start/YRSID_SI, + t_obs_end=t_obs_end, compress=compress, + shift_t_limits=shift_t_limits)[0] wanted = {} From 2ac1041cb6fa87be18fb8a888264ef4e616adebf Mon Sep 17 00:00:00 2001 From: Connor Date: Fri, 13 Oct 2023 16:48:25 +0100 Subject: [PATCH 07/36] More modes stuff --- BBHX_PhenomD.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/BBHX_PhenomD.py b/BBHX_PhenomD.py index 4247995..e1a08e4 100644 --- a/BBHX_PhenomD.py +++ b/BBHX_PhenomD.py @@ -57,15 +57,18 @@ def interpolated_tf(m1, m2): tf_track = interp1d(t_array, freq_array) return tf_track -def bbhx_fd(ifos=None, run_phenomd=True, use_gpu=False, + + +# For now, always run phenom=False +def bbhx_fd(ifos=None, run_phenomd=False, use_gpu=False, ref_frame='LISA', sample_points=None, **params): if ifos is None: raise Exception("Must define data streams to compute") # If asking for anything but the (2,2) mode, run PhenomHM - if params['modes'] != [(2, 2)]: - run_phenomd = False + # if params['modes'] != [(2, 2)]: + # run_phenomd = False from pycbc.types import FrequencySeries, Array from pycbc import pnutils @@ -167,6 +170,11 @@ def bbhx_fd(ifos=None, run_phenomd=True, use_gpu=False, else: freqs = sample_points + if params['modes'] == 22.0: + params['modes'] = [(2,2)] + elif params['modes'] == 33.0: + params['modes'] = [(3,3)] + # If creating injection of many modes, or just single, compress = True # will do the same thing. compress = True # If True, combine harmonics into single channel waveforms. (Default: True) From 2335c722eafbe0dbe68ea7353c38c2c0fde06206 Mon Sep 17 00:00:00 2001 From: Connor Date: Tue, 17 Oct 2023 08:55:32 +0100 Subject: [PATCH 08/36] More likelihood tests --- BBHX_PhenomD.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/BBHX_PhenomD.py b/BBHX_PhenomD.py index e1a08e4..c098089 100644 --- a/BBHX_PhenomD.py +++ b/BBHX_PhenomD.py @@ -170,6 +170,8 @@ def bbhx_fd(ifos=None, run_phenomd=False, use_gpu=False, else: freqs = sample_points + # freqs = np.load('/home/connor/main/higher_modes_dev/likelihood_testing/freq.npy') + if params['modes'] == 22.0: params['modes'] = [(2,2)] elif params['modes'] == 33.0: From e9d92773ca58187d40cdf5926569f20fd5027bbb Mon Sep 17 00:00:00 2001 From: Connor Date: Mon, 23 Oct 2023 18:10:08 +0100 Subject: [PATCH 09/36] Hard coded changes to separate modes --- BBHX_PhenomD.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/BBHX_PhenomD.py b/BBHX_PhenomD.py index c098089..4470ed9 100644 --- a/BBHX_PhenomD.py +++ b/BBHX_PhenomD.py @@ -66,10 +66,6 @@ def bbhx_fd(ifos=None, run_phenomd=False, use_gpu=False, if ifos is None: raise Exception("Must define data streams to compute") - # If asking for anything but the (2,2) mode, run PhenomHM - # if params['modes'] != [(2, 2)]: - # run_phenomd = False - from pycbc.types import FrequencySeries, Array from pycbc import pnutils @@ -170,7 +166,8 @@ def bbhx_fd(ifos=None, run_phenomd=False, use_gpu=False, else: freqs = sample_points - # freqs = np.load('/home/connor/main/higher_modes_dev/likelihood_testing/freq.npy') + #freqs = np.load('/home/connor/main/higher_modes_dev/likelihood_testing/freq.npy') + print(freqs) if params['modes'] == 22.0: params['modes'] = [(2,2)] From 8042edbf9c2a8c874ce24c4c69afcd63a85de7d7 Mon Sep 17 00:00:00 2001 From: Connor Date: Tue, 7 Nov 2023 15:52:16 +0000 Subject: [PATCH 10/36] Testing --- BBHX_PhenomD.py | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/BBHX_PhenomD.py b/BBHX_PhenomD.py index 4470ed9..bfeb05a 100644 --- a/BBHX_PhenomD.py +++ b/BBHX_PhenomD.py @@ -166,14 +166,6 @@ def bbhx_fd(ifos=None, run_phenomd=False, use_gpu=False, else: freqs = sample_points - #freqs = np.load('/home/connor/main/higher_modes_dev/likelihood_testing/freq.npy') - print(freqs) - - if params['modes'] == 22.0: - params['modes'] = [(2,2)] - elif params['modes'] == 33.0: - params['modes'] = [(3,3)] - # If creating injection of many modes, or just single, compress = True # will do the same thing. compress = True # If True, combine harmonics into single channel waveforms. (Default: True) @@ -184,8 +176,20 @@ def bbhx_fd(ifos=None, run_phenomd=False, use_gpu=False, length = 1024 # An internal generation parameter, not an output parameter shift_t_limits = False # Times are relative to merger t_obs_end = 0.0 # Generates ringdown as well! - modes = params['modes'] # More modes if not phenomd + # modes = params['modes'] # More modes if not phenomd + params['modes'] = float(params['modes']) + + if params['modes'] == 22.0: + modes = [(2,2)] + elif params['modes'] == 33.0: + modes = [(3,3)] + elif params['modes'] == 44.0: + modes = [(4,4)] + elif params['modes'] == 2233.0: + modes = [(2,2),(3,3)] + elif params['modes'] == 223344.0: + modes = [(2,2),(3,3),(4,4)] # NOTE: This does not allow for the seperation of multiple modes into # their own streams. From 2cd0b744044a3f5cfd6d84fd5c987f298170a54d Mon Sep 17 00:00:00 2001 From: ConWea Date: Fri, 24 Nov 2023 16:17:11 +0000 Subject: [PATCH 11/36] Working higher modes --- BBHX_PhenomD.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/BBHX_PhenomD.py b/BBHX_PhenomD.py index bfeb05a..779ec86 100644 --- a/BBHX_PhenomD.py +++ b/BBHX_PhenomD.py @@ -176,20 +176,29 @@ def bbhx_fd(ifos=None, run_phenomd=False, use_gpu=False, length = 1024 # An internal generation parameter, not an output parameter shift_t_limits = False # Times are relative to merger t_obs_end = 0.0 # Generates ringdown as well! - # modes = params['modes'] # More modes if not phenomd params['modes'] = float(params['modes']) - if params['modes'] == 22.0: modes = [(2,2)] + elif params['modes'] == 21.0: + modes = [(2,1)] elif params['modes'] == 33.0: modes = [(3,3)] + elif params['modes'] == 32.0: + modes = [(3,2)] elif params['modes'] == 44.0: modes = [(4,4)] + elif params['modes'] == 43.0: + modes = [(4,3)] elif params['modes'] == 2233.0: modes = [(2,2),(3,3)] elif params['modes'] == 223344.0: modes = [(2,2),(3,3),(4,4)] + elif params['modes'] == 22334443.0: + modes = [(2,2),(3,3),(4,4),(4,3)] + elif params['modes'] == 223344213243.0: + modes = [(2,2),(3,3),(4,4),(2,1),(3,2),(4,3)] + # NOTE: This does not allow for the seperation of multiple modes into # their own streams. From a36406c7a8910a7bed7a51fcf2de48396c3add0b Mon Sep 17 00:00:00 2001 From: ConWea Date: Wed, 6 Dec 2023 22:33:37 +0000 Subject: [PATCH 12/36] Edits to include higher order modes --- BBHX_PhenomD.py => BBHX_Phenom.py | 49 ++++++++++--------------------- setup.py | 8 ++--- 2 files changed, 19 insertions(+), 38 deletions(-) rename BBHX_PhenomD.py => BBHX_Phenom.py (87%) diff --git a/BBHX_PhenomD.py b/BBHX_Phenom.py similarity index 87% rename from BBHX_PhenomD.py rename to BBHX_Phenom.py index 779ec86..2ed002d 100644 --- a/BBHX_PhenomD.py +++ b/BBHX_Phenom.py @@ -8,13 +8,12 @@ @functools.lru_cache(maxsize=128) -def get_waveform_genner(log_mf_min, run_phenomd=True, use_gpu=False): +def get_waveform_genner(log_mf_min, run_phenomd=True): # See below where this function is called for description of how we handle # log_mf_min. mf_min = math.exp(log_mf_min/25.) wave_gen = BBHWaveformFD(amp_phase_kwargs=dict(run_phenomd=run_phenomd, - mf_min=mf_min), - use_gpu=use_gpu) + mf_min=mf_min)) return wave_gen @functools.lru_cache(maxsize=10) @@ -57,11 +56,15 @@ def interpolated_tf(m1, m2): tf_track = interp1d(t_array, freq_array) return tf_track +def waveform_setup(**kwargs): + if kwargs['approximant'] == "BBHX_PhenomD": + kwargs['mode_array'] = [(2, 2)] + return _bbhx_fd(**kwargs) + elif kwargs['approximant'] == "BBHX_PhenomHM": + return _bbhx_fd(run_phenomd=False, **kwargs) - -# For now, always run phenom=False -def bbhx_fd(ifos=None, run_phenomd=False, use_gpu=False, - ref_frame='LISA', sample_points=None, **params): +def _bbhx_fd(ifos=None, run_phenomd=True, ref_frame='LISA', + sample_points=None, **params): if ifos is None: raise Exception("Must define data streams to compute") @@ -145,8 +148,7 @@ def bbhx_fd(ifos=None, run_phenomd=False, use_gpu=False, # frequency. The factor of 25 ensures reasonable spacing while doing this. # So we round down to the nearest 1/25 of the logarithm of the frequency log_mf_min = int(math.log(f_min*MTSUN_SI*(m1+m2)) * 25) - wave_gen = get_waveform_genner(log_mf_min, run_phenomd=run_phenomd, - use_gpu=use_gpu) + wave_gen = get_waveform_genner(log_mf_min, run_phenomd=run_phenomd) if sample_points is None: if 'delta_f' in params and params['delta_f'] > 0: @@ -177,36 +179,15 @@ def bbhx_fd(ifos=None, run_phenomd=False, use_gpu=False, shift_t_limits = False # Times are relative to merger t_obs_end = 0.0 # Generates ringdown as well! - params['modes'] = float(params['modes']) - if params['modes'] == 22.0: - modes = [(2,2)] - elif params['modes'] == 21.0: - modes = [(2,1)] - elif params['modes'] == 33.0: - modes = [(3,3)] - elif params['modes'] == 32.0: - modes = [(3,2)] - elif params['modes'] == 44.0: - modes = [(4,4)] - elif params['modes'] == 43.0: - modes = [(4,3)] - elif params['modes'] == 2233.0: - modes = [(2,2),(3,3)] - elif params['modes'] == 223344.0: - modes = [(2,2),(3,3),(4,4)] - elif params['modes'] == 22334443.0: - modes = [(2,2),(3,3),(4,4),(4,3)] - elif params['modes'] == 223344213243.0: - modes = [(2,2),(3,3),(4,4),(2,1),(3,2),(4,3)] - # NOTE: This does not allow for the seperation of multiple modes into - # their own streams. + # their own streams. All modes requested are combined into one stream. wave = wave_gen(m1, m2, a1, a2, dist, phi_ref, f_ref, inc, lam, beta, psi, t_ref, freqs=freqs, - modes=modes, direct=direct, fill=fill, squeeze=squeeze, - length=length, t_obs_start=t_obs_start/YRSID_SI, + modes=params['mode_array'], direct=direct, fill=fill, + squeeze=squeeze, length=length, + t_obs_start=t_obs_start/YRSID_SI, t_obs_end=t_obs_end, compress=compress, shift_t_limits=shift_t_limits)[0] diff --git a/setup.py b/setup.py index 91f7932..dfd7596 100644 --- a/setup.py +++ b/setup.py @@ -18,10 +18,10 @@ download_url = 'https://github.com/gwastro/BBHX-waveform-model/v%s' % VERSION, keywords = ['pycbc', 'signal processing', 'gravitational waves', 'lisa'], install_requires = ['pycbc'], - py_modules = ['BBHX_PhenomD'], - entry_points = {"pycbc.waveform.fd_det":"BBHX_PhenomD=BBHX_PhenomD:bbhx_fd", - "pycbc.waveform.fd_det_sequence":"BBHX_PhenomD=BBHX_PhenomD:bbhx_fd", - "pycbc.waveform.length":"BBHX_PhenomD=BBHX_PhenomD:imr_duration"}, + py_modules = ['BBHX_Phenom'], + entry_points = {"pycbc.waveform.fd_det":["BBHX_PhenomD=BBHX_Phenom:waveform_setup", "BBHX_PhenomHM=BBHX_Phenom:waveform_setup"], + "pycbc.waveform.fd_det_sequence":["BBHX_PhenomD=BBHX_Phenom:waveform_setup", "BBHX_PhenomHM=BBHX_Phenom:waveform_setup"], + "pycbc.waveform.length":"BBHX_PhenomD=BBHX_Phenom:imr_duration"}, classifiers=[ 'Programming Language :: Python', From 5264e7e436a84fdb8cd5a4078c3f69878ec5a786 Mon Sep 17 00:00:00 2001 From: ConWea Date: Fri, 15 Dec 2023 10:43:50 +0000 Subject: [PATCH 13/36] Added the appropriate function for approximant in the imr_duration function --- BBHX_Phenom.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/BBHX_Phenom.py b/BBHX_Phenom.py index 2ed002d..b404a9f 100644 --- a/BBHX_Phenom.py +++ b/BBHX_Phenom.py @@ -31,12 +31,18 @@ def imr_duration(**params): # including merge, ringdown, and aligned spin effects. # This is used in the time-domain signal injection in PyCBC. import warnings - from pycbc.waveform.waveform import imrphenomd_length_in_time nparams = {'mass1':params['mass1'], 'mass2':params['mass2'], 'spin1z':params['spin1z'], 'spin2z':params['spin2z'], 'f_lower':params['f_lower']} - time_length = np.float64(imrphenomd_length_in_time(**nparams)) + + if params['approximant'] == 'BBHX_IMRPhenomD': + from pycbc.waveform.waveform import imrphenomd_length_in_time + time_length = np.float64(imrphenomd_length_in_time(**nparams)) + elif params['approximant'] == 'BBHX_IMRPhenomHM': + from pycbc.waveform.waveform import imrphenomhm_length_in_time + time_length = np.float64(imrphenomhm_length_in_time(**nparams)) + if time_length < 2678400: warnings.warn("Waveform duration is too short! Setting it to 1 month (2678400 s).") time_length = 2678400 From 807a327c68a0df9a235eceaf697467e1f8c24ddc Mon Sep 17 00:00:00 2001 From: ConWea Date: Fri, 15 Dec 2023 10:58:17 +0000 Subject: [PATCH 14/36] Moved error message so prints correctly --- BBHX_Phenom.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/BBHX_Phenom.py b/BBHX_Phenom.py index b404a9f..12764d4 100644 --- a/BBHX_Phenom.py +++ b/BBHX_Phenom.py @@ -93,8 +93,8 @@ def _bbhx_fd(ifos=None, run_phenomd=True, ref_frame='LISA', t_offset = np.float64(params['t_offset']) # in seconds else: raise Exception("Must set `t_offset`, if you don't have a preferred value, \ - please set it to be the default value %f, which will put LISA behind \ - the Earth by ~20 degrees." % TIME_OFFSET_20_DEGREES) +please set it to be the default value %f, which will put LISA behind \ +the Earth by ~20 degrees." % TIME_OFFSET_20_DEGREES) t_obs_start = np.float64(params['t_obs_start']) # in seconds if ref_frame == 'LISA': From 74c5f7cb1166d6d94e4677a172427daba62bfe04 Mon Sep 17 00:00:00 2001 From: ConWea Date: Fri, 15 Dec 2023 10:29:30 +0000 Subject: [PATCH 15/36] Length changes --- BBHX_Phenom.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/BBHX_Phenom.py b/BBHX_Phenom.py index 12764d4..5566188 100644 --- a/BBHX_Phenom.py +++ b/BBHX_Phenom.py @@ -93,8 +93,8 @@ def _bbhx_fd(ifos=None, run_phenomd=True, ref_frame='LISA', t_offset = np.float64(params['t_offset']) # in seconds else: raise Exception("Must set `t_offset`, if you don't have a preferred value, \ -please set it to be the default value %f, which will put LISA behind \ -the Earth by ~20 degrees." % TIME_OFFSET_20_DEGREES) + please set it to be the default value %f, which will put LISA behind \ + the Earth by ~20 degrees." % TIME_OFFSET_20_DEGREES) t_obs_start = np.float64(params['t_obs_start']) # in seconds if ref_frame == 'LISA': @@ -181,7 +181,7 @@ def _bbhx_fd(ifos=None, run_phenomd=True, ref_frame='LISA', direct = False # If True, directly compute the waveform without interpolation. (Default: False) fill = True # See the BBHX documentation squeeze = True # See the BBHX documentation - length = 1024 # An internal generation parameter, not an output parameter + length = 1024 * 8 # An internal generation parameter, not an output parameter shift_t_limits = False # Times are relative to merger t_obs_end = 0.0 # Generates ringdown as well! From ba8b5413e78f87db67b4b1aa1f4f3d86a9be4e67 Mon Sep 17 00:00:00 2001 From: ConWea Date: Fri, 15 Dec 2023 17:21:27 +0000 Subject: [PATCH 16/36] Update error message --- BBHX_Phenom.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/BBHX_Phenom.py b/BBHX_Phenom.py index 5566188..e2aff31 100644 --- a/BBHX_Phenom.py +++ b/BBHX_Phenom.py @@ -93,8 +93,8 @@ def _bbhx_fd(ifos=None, run_phenomd=True, ref_frame='LISA', t_offset = np.float64(params['t_offset']) # in seconds else: raise Exception("Must set `t_offset`, if you don't have a preferred value, \ - please set it to be the default value %f, which will put LISA behind \ - the Earth by ~20 degrees." % TIME_OFFSET_20_DEGREES) +please set it to be the default value %f, which will put LISA behind \ +the Earth by ~20 degrees." % TIME_OFFSET_20_DEGREES) t_obs_start = np.float64(params['t_obs_start']) # in seconds if ref_frame == 'LISA': From 261f0f2c0cc6ddd7f749dc6d0a31c416ab9f246b Mon Sep 17 00:00:00 2001 From: ConWea Date: Mon, 18 Dec 2023 12:21:21 +0000 Subject: [PATCH 17/36] Injection changes --- BBHX_Phenom.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/BBHX_Phenom.py b/BBHX_Phenom.py index e2aff31..2024335 100644 --- a/BBHX_Phenom.py +++ b/BBHX_Phenom.py @@ -178,10 +178,10 @@ def _bbhx_fd(ifos=None, run_phenomd=True, ref_frame='LISA', # will do the same thing. compress = True # If True, combine harmonics into single channel waveforms. (Default: True) # Need to give length if direct = False. - direct = False # If True, directly compute the waveform without interpolation. (Default: False) + direct = True # If True, directly compute the waveform without interpolation. (Default: False) fill = True # See the BBHX documentation squeeze = True # See the BBHX documentation - length = 1024 * 8 # An internal generation parameter, not an output parameter + # length = 1024 * 8 # An internal generation parameter, not an output parameter shift_t_limits = False # Times are relative to merger t_obs_end = 0.0 # Generates ringdown as well! @@ -192,10 +192,10 @@ def _bbhx_fd(ifos=None, run_phenomd=True, ref_frame='LISA', dist, phi_ref, f_ref, inc, lam, beta, psi, t_ref, freqs=freqs, modes=params['mode_array'], direct=direct, fill=fill, - squeeze=squeeze, length=length, + squeeze=squeeze, # length=length, t_obs_start=t_obs_start/YRSID_SI, t_obs_end=t_obs_end, compress=compress, - shift_t_limits=shift_t_limits)[0] + shift_t_limits=shift_t_limits) wanted = {} From e2318a9f2a560953e3d3215b060102e63ac28225 Mon Sep 17 00:00:00 2001 From: ConWea Date: Tue, 6 Feb 2024 10:21:22 +0000 Subject: [PATCH 18/36] Added more customization to the BBHx waveform varaibles --- BBHX_Phenom.py | 94 +++++++++++++++++++++++++++++--------------------- 1 file changed, 54 insertions(+), 40 deletions(-) diff --git a/BBHX_Phenom.py b/BBHX_Phenom.py index 2024335..b6ee017 100644 --- a/BBHX_Phenom.py +++ b/BBHX_Phenom.py @@ -11,9 +11,12 @@ def get_waveform_genner(log_mf_min, run_phenomd=True): # See below where this function is called for description of how we handle # log_mf_min. - mf_min = math.exp(log_mf_min/25.) - wave_gen = BBHWaveformFD(amp_phase_kwargs=dict(run_phenomd=run_phenomd, - mf_min=mf_min)) + if log_mf_min is None: + wave_gen = BBHWaveformFD(amp_phase_kwargs=dict(run_phenomd=run_phenomd)) + else: + mf_min = math.exp(log_mf_min/25.) + wave_gen = BBHWaveformFD(amp_phase_kwargs=dict(run_phenomd=run_phenomd, + mf_min=mf_min)) return wave_gen @functools.lru_cache(maxsize=10) @@ -70,7 +73,8 @@ def waveform_setup(**kwargs): return _bbhx_fd(run_phenomd=False, **kwargs) def _bbhx_fd(ifos=None, run_phenomd=True, ref_frame='LISA', - sample_points=None, **params): + sample_points=None, length=1024, direct=False, + min_f=False, **params): if ifos is None: raise Exception("Must define data streams to compute") @@ -128,32 +132,36 @@ def _bbhx_fd(ifos=None, run_phenomd=True, ref_frame='LISA', err_msg = f"Don't recognise reference frame {ref_frame}. " err_msg = f"Known frames are 'LISA' and 'SSB'." - if ('f_lower' not in params) or (params['f_lower'] < 0): - # the default value of 'f_lower' in PyCBC is -1. - tf_track = interpolated_tf(m1, m2) - t_max = chirptime(m1=m1, m2=m2, f_lower=1e-4) - if t_obs_start > t_max: - # Avoid "above the interpolation range" issue. - f_min = 1e-4 + if min_f: + if ('f_lower' not in params) or (params['f_lower'] < 0): + # the default value of 'f_lower' in PyCBC is -1. + tf_track = interpolated_tf(m1, m2) + t_max = chirptime(m1=m1, m2=m2, f_lower=1e-4) + if t_obs_start > t_max: + # Avoid "above the interpolation range" issue. + f_min = 1e-4 + else: + f_min = tf_track(t_obs_start) # in Hz else: - f_min = tf_track(t_obs_start) # in Hz + f_min = np.float64(params['f_lower']) # in Hz + tf_track = interpolated_tf(m1, m2) + t_max = chirptime(m1=m1, m2=m2, f_lower=1e-4) + if t_obs_start > t_max: + f_min_tobs = 1e-4 + else: + f_min_tobs = tf_track(t_obs_start) # in Hz + if f_min < f_min_tobs: + err_msg = f"Input 'f_lower' is lower than the value calculated from 't_obs_start'." + + # We want to cache the waveform generator, but as it takes a mass dependent + # start frequency as input this is hard. + # To solve this we *round* the *logarithm* of this mass-dependent start + # frequency. The factor of 25 ensures reasonable spacing while doing this. + # So we round down to the nearest 1/25 of the logarithm of the frequency + log_mf_min = int(math.log(f_min*MTSUN_SI*(m1+m2)) * 25) else: - f_min = np.float64(params['f_lower']) # in Hz - tf_track = interpolated_tf(m1, m2) - t_max = chirptime(m1=m1, m2=m2, f_lower=1e-4) - if t_obs_start > t_max: - f_min_tobs = 1e-4 - else: - f_min_tobs = tf_track(t_obs_start) # in Hz - if f_min < f_min_tobs: - err_msg = f"Input 'f_lower' is lower than the value calculated from 't_obs_start'." - - # We want to cache the waveform generator, but as it takes a mass dependent - # start frequency as input this is hard. - # To solve this we *round* the *logarithm* of this mass-dependent start - # frequency. The factor of 25 ensures reasonable spacing while doing this. - # So we round down to the nearest 1/25 of the logarithm of the frequency - log_mf_min = int(math.log(f_min*MTSUN_SI*(m1+m2)) * 25) + log_mf_min=None + wave_gen = get_waveform_genner(log_mf_min, run_phenomd=run_phenomd) if sample_points is None: @@ -177,25 +185,31 @@ def _bbhx_fd(ifos=None, run_phenomd=True, ref_frame='LISA', # If creating injection of many modes, or just single, compress = True # will do the same thing. compress = True # If True, combine harmonics into single channel waveforms. (Default: True) - # Need to give length if direct = False. - direct = True # If True, directly compute the waveform without interpolation. (Default: False) fill = True # See the BBHX documentation squeeze = True # See the BBHX documentation - # length = 1024 * 8 # An internal generation parameter, not an output parameter shift_t_limits = False # Times are relative to merger t_obs_end = 0.0 # Generates ringdown as well! - # NOTE: This does not allow for the seperation of multiple modes into # their own streams. All modes requested are combined into one stream. - wave = wave_gen(m1, m2, a1, a2, - dist, phi_ref, f_ref, inc, lam, - beta, psi, t_ref, freqs=freqs, - modes=params['mode_array'], direct=direct, fill=fill, - squeeze=squeeze, # length=length, - t_obs_start=t_obs_start/YRSID_SI, - t_obs_end=t_obs_end, compress=compress, - shift_t_limits=shift_t_limits) + if direct == 'True' or direct == True: + wave = wave_gen(m1, m2, a1, a2, + dist, phi_ref, f_ref, inc, lam, + beta, psi, t_ref, freqs=freqs, + modes=params['mode_array'], direct=True, fill=fill, + squeeze=squeeze, t_obs_start=t_obs_start/YRSID_SI, + t_obs_end=t_obs_end, compress=compress, + shift_t_limits=shift_t_limits) + else: + # Need to give length if direct = False. + wave = wave_gen(m1, m2, a1, a2, + dist, phi_ref, f_ref, inc, lam, + beta, psi, t_ref, freqs=freqs, + modes=params['mode_array'], direct=direct, fill=fill, + squeeze=squeeze, length=int(length), + t_obs_start=t_obs_start/YRSID_SI, + t_obs_end=t_obs_end, compress=compress, + shift_t_limits=shift_t_limits)[0] wanted = {} From 9a6475963c5c80d7a5d79acfdaca9dd202819416 Mon Sep 17 00:00:00 2001 From: ConWea Date: Tue, 6 Feb 2024 11:59:51 +0000 Subject: [PATCH 19/36] Raise exception if mode_array is empty for PhenomHM. --- BBHX_Phenom.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/BBHX_Phenom.py b/BBHX_Phenom.py index b6ee017..302c406 100644 --- a/BBHX_Phenom.py +++ b/BBHX_Phenom.py @@ -70,6 +70,8 @@ def waveform_setup(**kwargs): kwargs['mode_array'] = [(2, 2)] return _bbhx_fd(**kwargs) elif kwargs['approximant'] == "BBHX_PhenomHM": + if 'mode_array' not in kwargs: + raise Exception("Must define modes in mode_array") return _bbhx_fd(run_phenomd=False, **kwargs) def _bbhx_fd(ifos=None, run_phenomd=True, ref_frame='LISA', From 8f147464bcf22e1ba394b783835180a0ca6d5e8b Mon Sep 17 00:00:00 2001 From: ConWea Date: Fri, 16 Feb 2024 13:21:53 +0000 Subject: [PATCH 20/36] Set default mode_array to all available modes in PhenomHM if not specified by user --- BBHX_Phenom.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BBHX_Phenom.py b/BBHX_Phenom.py index 302c406..65d508e 100644 --- a/BBHX_Phenom.py +++ b/BBHX_Phenom.py @@ -71,7 +71,7 @@ def waveform_setup(**kwargs): return _bbhx_fd(**kwargs) elif kwargs['approximant'] == "BBHX_PhenomHM": if 'mode_array' not in kwargs: - raise Exception("Must define modes in mode_array") + kwargs['mode_array'] = [(2, 2), (2, 1), (3, 3), (3, 2), (4, 4), (4, 3)] return _bbhx_fd(run_phenomd=False, **kwargs) def _bbhx_fd(ifos=None, run_phenomd=True, ref_frame='LISA', From 0e4264bf40511001725ed2fe5b0f892d20c26e8a Mon Sep 17 00:00:00 2001 From: mj-will Date: Tue, 9 Apr 2024 13:21:08 +0100 Subject: [PATCH 21/36] add mode options to `chirptime` and `interpolated_tf` --- BBHX_Phenom.py | 103 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 67 insertions(+), 36 deletions(-) diff --git a/BBHX_Phenom.py b/BBHX_Phenom.py index 65d508e..c787622 100644 --- a/BBHX_Phenom.py +++ b/BBHX_Phenom.py @@ -5,6 +5,7 @@ from bbhx.utils.constants import MTSUN_SI, YRSID_SI from bbhx.waveformbuild import BBHWaveformFD from pycbc.coordinates import TIME_OFFSET_20_DEGREES, lisa_to_ssb, ssb_to_lisa +from warnings import warn @functools.lru_cache(maxsize=128) @@ -19,16 +20,29 @@ def get_waveform_genner(log_mf_min, run_phenomd=True): mf_min=mf_min)) return wave_gen + @functools.lru_cache(maxsize=10) def cached_arange(start, stop, spacing): return np.arange(start, stop, spacing) -def chirptime(m1, m2, f_lower): + +def chirptime(m1, m2, f_lower, mode=None): + """Compute the chirptime. + + Defaults for the (2,2) mode. + """ from pycbc.waveform.spa_tmplt import findchirp_chirptime + # Find the (2,2) mode duration duration = findchirp_chirptime(m1=m1, m2=m2, fLower=f_lower, porder=7) + # If a mode is specified, convert to that mode + if mode is not None: + # See https://arxiv.org/abs/2005.08830, eqs. 2-3 + factor = (2 / (mode[1])) ** (-8 / 3) + duration *= factor return duration + def imr_duration(**params): # More accurate duration (within LISA frequency band) of the waveform, # including merge, ringdown, and aligned spin effects. @@ -54,17 +68,24 @@ def imr_duration(**params): time_length = params['t_obs_start'] return time_length * 1.1 -def interpolated_tf(m1, m2): + +def interpolated_tf(m1, m2, mode=None): + """Interpolate the time frequency-track. + + Defaults to the dominant (2,2) mode and uses :code:`chirptime` to compute + the track. + """ # Using findchirp_chirptime in PyCBC to calculate # the time-frequency track of dominant mode to get # the corresponding `f_min` for `t_obs_start`. freq_array = np.logspace(-4, 0, num=10) t_array = np.zeros(len(freq_array)) for i in range(len(freq_array)): - t_array[i] = chirptime(m1=m1, m2=m2, f_lower=freq_array[i]) + t_array[i] = chirptime(m1=m1, m2=m2, f_lower=freq_array[i], mode=mode) tf_track = interp1d(t_array, freq_array) return tf_track + def waveform_setup(**kwargs): if kwargs['approximant'] == "BBHX_PhenomD": kwargs['mode_array'] = [(2, 2)] @@ -74,9 +95,16 @@ def waveform_setup(**kwargs): kwargs['mode_array'] = [(2, 2), (2, 1), (3, 3), (3, 2), (4, 4), (4, 3)] return _bbhx_fd(run_phenomd=False, **kwargs) -def _bbhx_fd(ifos=None, run_phenomd=True, ref_frame='LISA', - sample_points=None, length=1024, direct=False, - min_f=False, **params): + +def _bbhx_fd( + ifos=None, + run_phenomd=True, + ref_frame='LISA', + sample_points=None, + length=1024, + direct=False, + **params +): if ifos is None: raise Exception("Must define data streams to compute") @@ -102,6 +130,7 @@ def _bbhx_fd(ifos=None, run_phenomd=True, ref_frame='LISA', please set it to be the default value %f, which will put LISA behind \ the Earth by ~20 degrees." % TIME_OFFSET_20_DEGREES) t_obs_start = np.float64(params['t_obs_start']) # in seconds + mode_array = np.array(params["mode_array"]) if ref_frame == 'LISA': t_ref_lisa = np.float64(params['tc']) + t_offset @@ -134,35 +163,37 @@ def _bbhx_fd(ifos=None, run_phenomd=True, ref_frame='LISA', err_msg = f"Don't recognise reference frame {ref_frame}. " err_msg = f"Known frames are 'LISA' and 'SSB'." - if min_f: - if ('f_lower' not in params) or (params['f_lower'] < 0): - # the default value of 'f_lower' in PyCBC is -1. - tf_track = interpolated_tf(m1, m2) - t_max = chirptime(m1=m1, m2=m2, f_lower=1e-4) - if t_obs_start > t_max: - # Avoid "above the interpolation range" issue. - f_min = 1e-4 - else: - f_min = tf_track(t_obs_start) # in Hz + # The mode with the largest m will have the highest frequency + # since approximately F_lm = m/2 * F_22 and will therefore reach this + # frequency at the earliest time + max_m_mode = mode_array[:, 1].max() + if ('f_lower' not in params) or (params['f_lower'] < 0): + # the default value of 'f_lower' in PyCBC is -1. + tf_track = interpolated_tf(m1, m2, mode=max_m_mode) + t_max = chirptime(m1=m1, m2=m2, f_lower=1e-4, mode=max_m_mode) + if t_obs_start > t_max: + # Avoid "above the interpolation range" issue. + f_min = 1e-4 else: - f_min = np.float64(params['f_lower']) # in Hz - tf_track = interpolated_tf(m1, m2) - t_max = chirptime(m1=m1, m2=m2, f_lower=1e-4) - if t_obs_start > t_max: - f_min_tobs = 1e-4 - else: - f_min_tobs = tf_track(t_obs_start) # in Hz - if f_min < f_min_tobs: - err_msg = f"Input 'f_lower' is lower than the value calculated from 't_obs_start'." - - # We want to cache the waveform generator, but as it takes a mass dependent - # start frequency as input this is hard. - # To solve this we *round* the *logarithm* of this mass-dependent start - # frequency. The factor of 25 ensures reasonable spacing while doing this. - # So we round down to the nearest 1/25 of the logarithm of the frequency - log_mf_min = int(math.log(f_min*MTSUN_SI*(m1+m2)) * 25) + f_min = tf_track(t_obs_start) # in Hz else: - log_mf_min=None + f_min = np.float64(params['f_lower']) # in Hz + tf_track = interpolated_tf(m1, m2, mode=max_m_mode) + t_max = chirptime(m1=m1, m2=m2, f_lower=1e-4, mode=max_m_mode) + if t_obs_start > t_max: + f_min_tobs = 1e-4 + else: + f_min_tobs = tf_track(t_obs_start) # in Hz + if f_min < f_min_tobs: + err_msg = f"Input 'f_lower' is lower than the value calculated from 't_obs_start'." + warn(err_msg, RuntimeWarning) + + # We want to cache the waveform generator, but as it takes a mass dependent + # start frequency as input this is hard. + # To solve this we *round* the *logarithm* of this mass-dependent start + # frequency. The factor of 25 ensures reasonable spacing while doing this. + # So we round down to the nearest 1/25 of the logarithm of the frequency + log_mf_min = int(math.log(f_min*MTSUN_SI*(m1+m2)) * 25) wave_gen = get_waveform_genner(log_mf_min, run_phenomd=run_phenomd) @@ -194,11 +225,11 @@ def _bbhx_fd(ifos=None, run_phenomd=True, ref_frame='LISA', # NOTE: This does not allow for the seperation of multiple modes into # their own streams. All modes requested are combined into one stream. - if direct == 'True' or direct == True: + if direct: wave = wave_gen(m1, m2, a1, a2, dist, phi_ref, f_ref, inc, lam, beta, psi, t_ref, freqs=freqs, - modes=params['mode_array'], direct=True, fill=fill, + modes=mode_array, direct=True, fill=fill, squeeze=squeeze, t_obs_start=t_obs_start/YRSID_SI, t_obs_end=t_obs_end, compress=compress, shift_t_limits=shift_t_limits) @@ -207,7 +238,7 @@ def _bbhx_fd(ifos=None, run_phenomd=True, ref_frame='LISA', wave = wave_gen(m1, m2, a1, a2, dist, phi_ref, f_ref, inc, lam, beta, psi, t_ref, freqs=freqs, - modes=params['mode_array'], direct=direct, fill=fill, + modes=mode_array, direct=direct, fill=fill, squeeze=squeeze, length=int(length), t_obs_start=t_obs_start/YRSID_SI, t_obs_end=t_obs_end, compress=compress, From 909b7f3a09bcb388e5e6a0cb20d79f3d6bf770ea Mon Sep 17 00:00:00 2001 From: mj-will Date: Thu, 11 Apr 2024 15:38:44 +0100 Subject: [PATCH 22/36] add value error for approximant name --- BBHX_Phenom.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/BBHX_Phenom.py b/BBHX_Phenom.py index c787622..9795cfa 100644 --- a/BBHX_Phenom.py +++ b/BBHX_Phenom.py @@ -94,6 +94,8 @@ def waveform_setup(**kwargs): if 'mode_array' not in kwargs: kwargs['mode_array'] = [(2, 2), (2, 1), (3, 3), (3, 2), (4, 4), (4, 3)] return _bbhx_fd(run_phenomd=False, **kwargs) + else: + raise ValueError(f"Invalid approximant: {kwargs['approximant']}") def _bbhx_fd( From 5eec1ebc6d1805bbfc0628ed11f8079e94e17ac2 Mon Sep 17 00:00:00 2001 From: mj-will Date: Tue, 16 Apr 2024 11:47:21 +0100 Subject: [PATCH 23/36] ensure mode_array is a list and fix mode argument in functions --- BBHX_Phenom.py | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/BBHX_Phenom.py b/BBHX_Phenom.py index 9795cfa..14824a2 100644 --- a/BBHX_Phenom.py +++ b/BBHX_Phenom.py @@ -26,7 +26,7 @@ def cached_arange(start, stop, spacing): return np.arange(start, stop, spacing) -def chirptime(m1, m2, f_lower, mode=None): +def chirptime(m1, m2, f_lower, m_mode=None): """Compute the chirptime. Defaults for the (2,2) mode. @@ -36,9 +36,9 @@ def chirptime(m1, m2, f_lower, mode=None): # Find the (2,2) mode duration duration = findchirp_chirptime(m1=m1, m2=m2, fLower=f_lower, porder=7) # If a mode is specified, convert to that mode - if mode is not None: + if m_mode is not None: # See https://arxiv.org/abs/2005.08830, eqs. 2-3 - factor = (2 / (mode[1])) ** (-8 / 3) + factor = (2 / (m_mode)) ** (-8 / 3) duration *= factor return duration @@ -69,7 +69,7 @@ def imr_duration(**params): return time_length * 1.1 -def interpolated_tf(m1, m2, mode=None): +def interpolated_tf(m1, m2, m_mode=None): """Interpolate the time frequency-track. Defaults to the dominant (2,2) mode and uses :code:`chirptime` to compute @@ -81,15 +81,17 @@ def interpolated_tf(m1, m2, mode=None): freq_array = np.logspace(-4, 0, num=10) t_array = np.zeros(len(freq_array)) for i in range(len(freq_array)): - t_array[i] = chirptime(m1=m1, m2=m2, f_lower=freq_array[i], mode=mode) + t_array[i] = chirptime(m1=m1, m2=m2, f_lower=freq_array[i], m_mode=m_mode) tf_track = interp1d(t_array, freq_array) return tf_track def waveform_setup(**kwargs): if kwargs['approximant'] == "BBHX_PhenomD": + if len(kwargs['mode_array']) != 1: + raise RuntimeError("BBHX_PhenomD only supports the (2,2) mode!") kwargs['mode_array'] = [(2, 2)] - return _bbhx_fd(**kwargs) + return _bbhx_fd(run_phenomd=True, **kwargs) elif kwargs['approximant'] == "BBHX_PhenomHM": if 'mode_array' not in kwargs: kwargs['mode_array'] = [(2, 2), (2, 1), (3, 3), (3, 2), (4, 4), (4, 3)] @@ -132,7 +134,7 @@ def _bbhx_fd( please set it to be the default value %f, which will put LISA behind \ the Earth by ~20 degrees." % TIME_OFFSET_20_DEGREES) t_obs_start = np.float64(params['t_obs_start']) # in seconds - mode_array = np.array(params["mode_array"]) + mode_array = list(params["mode_array"]) if ref_frame == 'LISA': t_ref_lisa = np.float64(params['tc']) + t_offset @@ -168,11 +170,11 @@ def _bbhx_fd( # The mode with the largest m will have the highest frequency # since approximately F_lm = m/2 * F_22 and will therefore reach this # frequency at the earliest time - max_m_mode = mode_array[:, 1].max() + max_m_mode = max([mode[1] for mode in mode_array]) if ('f_lower' not in params) or (params['f_lower'] < 0): # the default value of 'f_lower' in PyCBC is -1. - tf_track = interpolated_tf(m1, m2, mode=max_m_mode) - t_max = chirptime(m1=m1, m2=m2, f_lower=1e-4, mode=max_m_mode) + tf_track = interpolated_tf(m1, m2, m_mode=max_m_mode) + t_max = chirptime(m1=m1, m2=m2, f_lower=1e-4, m_mode=max_m_mode) if t_obs_start > t_max: # Avoid "above the interpolation range" issue. f_min = 1e-4 @@ -180,8 +182,8 @@ def _bbhx_fd( f_min = tf_track(t_obs_start) # in Hz else: f_min = np.float64(params['f_lower']) # in Hz - tf_track = interpolated_tf(m1, m2, mode=max_m_mode) - t_max = chirptime(m1=m1, m2=m2, f_lower=1e-4, mode=max_m_mode) + tf_track = interpolated_tf(m1, m2, m_mode=max_m_mode) + t_max = chirptime(m1=m1, m2=m2, f_lower=1e-4, m_mode=max_m_mode) if t_obs_start > t_max: f_min_tobs = 1e-4 else: From ef4856d2f90ee13010595b2caebf79a8b1f57bb2 Mon Sep 17 00:00:00 2001 From: mj-will Date: Tue, 7 May 2024 10:47:30 +0100 Subject: [PATCH 24/36] add `imr_duration` for PhenomHM Addresses https://github.com/gwastro/BBHX-waveform-model/pull/2#discussion_r1510381020 --- setup.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index dfd7596..b293492 100644 --- a/setup.py +++ b/setup.py @@ -19,9 +19,20 @@ keywords = ['pycbc', 'signal processing', 'gravitational waves', 'lisa'], install_requires = ['pycbc'], py_modules = ['BBHX_Phenom'], - entry_points = {"pycbc.waveform.fd_det":["BBHX_PhenomD=BBHX_Phenom:waveform_setup", "BBHX_PhenomHM=BBHX_Phenom:waveform_setup"], - "pycbc.waveform.fd_det_sequence":["BBHX_PhenomD=BBHX_Phenom:waveform_setup", "BBHX_PhenomHM=BBHX_Phenom:waveform_setup"], - "pycbc.waveform.length":"BBHX_PhenomD=BBHX_Phenom:imr_duration"}, + entry_points = { + "pycbc.waveform.fd_det":[ + "BBHX_PhenomD=BBHX_Phenom:waveform_setup", + "BBHX_PhenomHM=BBHX_Phenom:waveform_setup", + ], + "pycbc.waveform.fd_det_sequence": [ + "BBHX_PhenomD=BBHX_Phenom:waveform_setup", + "BBHX_PhenomHM=BBHX_Phenom:waveform_setup", + ], + "pycbc.waveform.length": [ + "BBHX_PhenomD=BBHX_Phenom:imr_duration", + "BBHX_PhenomHM=BBHX_Phenom:imr_duration", + ] + }, classifiers=[ 'Programming Language :: Python', From 6e1a49bdd120011ab15eed31bc306696c906c805 Mon Sep 17 00:00:00 2001 From: mj-will Date: Tue, 7 May 2024 11:08:50 +0100 Subject: [PATCH 25/36] handle case where mode array is None --- BBHX_Phenom.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BBHX_Phenom.py b/BBHX_Phenom.py index 14824a2..2aaea09 100644 --- a/BBHX_Phenom.py +++ b/BBHX_Phenom.py @@ -88,7 +88,7 @@ def interpolated_tf(m1, m2, m_mode=None): def waveform_setup(**kwargs): if kwargs['approximant'] == "BBHX_PhenomD": - if len(kwargs['mode_array']) != 1: + if kwargs.get('mode_array') is not None and len(kwargs['mode_array']) != 1: raise RuntimeError("BBHX_PhenomD only supports the (2,2) mode!") kwargs['mode_array'] = [(2, 2)] return _bbhx_fd(run_phenomd=True, **kwargs) From c0e50f663d1d6b14f8d29a8a755044b7df6d871c Mon Sep 17 00:00:00 2001 From: mj-will Date: Tue, 7 May 2024 13:15:41 +0100 Subject: [PATCH 26/36] remove redundant loop --- BBHX_Phenom.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/BBHX_Phenom.py b/BBHX_Phenom.py index 2aaea09..bfb7ed6 100644 --- a/BBHX_Phenom.py +++ b/BBHX_Phenom.py @@ -80,8 +80,7 @@ def interpolated_tf(m1, m2, m_mode=None): # the corresponding `f_min` for `t_obs_start`. freq_array = np.logspace(-4, 0, num=10) t_array = np.zeros(len(freq_array)) - for i in range(len(freq_array)): - t_array[i] = chirptime(m1=m1, m2=m2, f_lower=freq_array[i], m_mode=m_mode) + t_array = chirptime(m1=m1, m2=m2, f_lower=freq_array, m_mode=m_mode) tf_track = interp1d(t_array, freq_array) return tf_track From 3ead9115d6528f8283d1d109d5200d5d4ec6b015 Mon Sep 17 00:00:00 2001 From: mj-will Date: Tue, 7 May 2024 13:15:56 +0100 Subject: [PATCH 27/36] change logic for m mode --- BBHX_Phenom.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/BBHX_Phenom.py b/BBHX_Phenom.py index bfb7ed6..03e3bdd 100644 --- a/BBHX_Phenom.py +++ b/BBHX_Phenom.py @@ -166,14 +166,13 @@ def _bbhx_fd( err_msg = f"Don't recognise reference frame {ref_frame}. " err_msg = f"Known frames are 'LISA' and 'SSB'." - # The mode with the largest m will have the highest frequency - # since approximately F_lm = m/2 * F_22 and will therefore reach this - # frequency at the earliest time - max_m_mode = max([mode[1] for mode in mode_array]) + # The lowest m mode will have the lowest frequency at a given start time + # so we use this to compute the track + min_m_mode = max([mode[1] for mode in mode_array]) if ('f_lower' not in params) or (params['f_lower'] < 0): # the default value of 'f_lower' in PyCBC is -1. - tf_track = interpolated_tf(m1, m2, m_mode=max_m_mode) - t_max = chirptime(m1=m1, m2=m2, f_lower=1e-4, m_mode=max_m_mode) + tf_track = interpolated_tf(m1, m2, m_mode=min_m_mode) + t_max = chirptime(m1=m1, m2=m2, f_lower=1e-4, m_mode=min_m_mode) if t_obs_start > t_max: # Avoid "above the interpolation range" issue. f_min = 1e-4 @@ -181,8 +180,8 @@ def _bbhx_fd( f_min = tf_track(t_obs_start) # in Hz else: f_min = np.float64(params['f_lower']) # in Hz - tf_track = interpolated_tf(m1, m2, m_mode=max_m_mode) - t_max = chirptime(m1=m1, m2=m2, f_lower=1e-4, m_mode=max_m_mode) + tf_track = interpolated_tf(m1, m2, m_mode=min_m_mode) + t_max = chirptime(m1=m1, m2=m2, f_lower=1e-4, m_mode=min_m_mode) if t_obs_start > t_max: f_min_tobs = 1e-4 else: From d17f45c2725fcf6f38f9729022554c88e5412a04 Mon Sep 17 00:00:00 2001 From: mj-will Date: Tue, 7 May 2024 13:30:59 +0100 Subject: [PATCH 28/36] make number of interpolation points configurable --- BBHX_Phenom.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/BBHX_Phenom.py b/BBHX_Phenom.py index 03e3bdd..bf98824 100644 --- a/BBHX_Phenom.py +++ b/BBHX_Phenom.py @@ -69,7 +69,7 @@ def imr_duration(**params): return time_length * 1.1 -def interpolated_tf(m1, m2, m_mode=None): +def interpolated_tf(m1, m2, m_mode=None, num_interp=100): """Interpolate the time frequency-track. Defaults to the dominant (2,2) mode and uses :code:`chirptime` to compute @@ -78,7 +78,7 @@ def interpolated_tf(m1, m2, m_mode=None): # Using findchirp_chirptime in PyCBC to calculate # the time-frequency track of dominant mode to get # the corresponding `f_min` for `t_obs_start`. - freq_array = np.logspace(-4, 0, num=10) + freq_array = np.logspace(-4, 0, num=num_interp) t_array = np.zeros(len(freq_array)) t_array = chirptime(m1=m1, m2=m2, f_lower=freq_array, m_mode=m_mode) tf_track = interp1d(t_array, freq_array) @@ -106,6 +106,7 @@ def _bbhx_fd( sample_points=None, length=1024, direct=False, + num_interp=100, **params ): @@ -171,7 +172,9 @@ def _bbhx_fd( min_m_mode = max([mode[1] for mode in mode_array]) if ('f_lower' not in params) or (params['f_lower'] < 0): # the default value of 'f_lower' in PyCBC is -1. - tf_track = interpolated_tf(m1, m2, m_mode=min_m_mode) + tf_track = interpolated_tf( + m1, m2, m_mode=min_m_mode, num_interp=num_interp + ) t_max = chirptime(m1=m1, m2=m2, f_lower=1e-4, m_mode=min_m_mode) if t_obs_start > t_max: # Avoid "above the interpolation range" issue. @@ -180,7 +183,9 @@ def _bbhx_fd( f_min = tf_track(t_obs_start) # in Hz else: f_min = np.float64(params['f_lower']) # in Hz - tf_track = interpolated_tf(m1, m2, m_mode=min_m_mode) + tf_track = interpolated_tf( + m1, m2, m_mode=min_m_mode, num_interp=num_interp + ) t_max = chirptime(m1=m1, m2=m2, f_lower=1e-4, m_mode=min_m_mode) if t_obs_start > t_max: f_min_tobs = 1e-4 From 5f3124e7da6f47ed57e953f27d2ed492574feea6 Mon Sep 17 00:00:00 2001 From: mj-will Date: Tue, 7 May 2024 13:59:41 +0100 Subject: [PATCH 29/36] avoid duplicate code --- BBHX_Phenom.py | 41 ++++++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/BBHX_Phenom.py b/BBHX_Phenom.py index bf98824..dbcbca4 100644 --- a/BBHX_Phenom.py +++ b/BBHX_Phenom.py @@ -135,6 +135,8 @@ def _bbhx_fd( the Earth by ~20 degrees." % TIME_OFFSET_20_DEGREES) t_obs_start = np.float64(params['t_obs_start']) # in seconds mode_array = list(params["mode_array"]) + num_interp = int(num_interp) + length = int(length) if length is not None else None if ref_frame == 'LISA': t_ref_lisa = np.float64(params['tc']) + t_offset @@ -230,26 +232,27 @@ def _bbhx_fd( shift_t_limits = False # Times are relative to merger t_obs_end = 0.0 # Generates ringdown as well! - # NOTE: This does not allow for the seperation of multiple modes into + # NOTE: This does not allow for the separation of multiple modes into # their own streams. All modes requested are combined into one stream. - if direct: - wave = wave_gen(m1, m2, a1, a2, - dist, phi_ref, f_ref, inc, lam, - beta, psi, t_ref, freqs=freqs, - modes=mode_array, direct=True, fill=fill, - squeeze=squeeze, t_obs_start=t_obs_start/YRSID_SI, - t_obs_end=t_obs_end, compress=compress, - shift_t_limits=shift_t_limits) - else: - # Need to give length if direct = False. - wave = wave_gen(m1, m2, a1, a2, - dist, phi_ref, f_ref, inc, lam, - beta, psi, t_ref, freqs=freqs, - modes=mode_array, direct=direct, fill=fill, - squeeze=squeeze, length=int(length), - t_obs_start=t_obs_start/YRSID_SI, - t_obs_end=t_obs_end, compress=compress, - shift_t_limits=shift_t_limits)[0] + wave = wave_gen( + m1, m2, a1, a2, + dist, phi_ref, f_ref, inc, lam, + beta, psi, t_ref, + freqs=freqs, + modes=mode_array, + direct=direct, + fill=fill, + squeeze=squeeze, + t_obs_start=t_obs_start / YRSID_SI, + t_obs_end=t_obs_end, + compress=compress, + length=length, + shift_t_limits=shift_t_limits, + ) + # For some reason, the shape is different depending on if direct is True + # or False. + if not direct: + wave = wave[0] wanted = {} From 6fb2604fbafe165d551bf513e6f2a4cd17611e67 Mon Sep 17 00:00:00 2001 From: mj-will Date: Wed, 8 May 2024 12:00:37 +0100 Subject: [PATCH 30/36] use max m mode & only compute track when needed --- BBHX_Phenom.py | 42 ++++++++++++++++++++++++++++++------------ 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/BBHX_Phenom.py b/BBHX_Phenom.py index dbcbca4..1374763 100644 --- a/BBHX_Phenom.py +++ b/BBHX_Phenom.py @@ -26,6 +26,11 @@ def cached_arange(start, stop, spacing): return np.arange(start, stop, spacing) +@functools.lru_cache(maxsize=128) +def cached_freq_logspace(f_lower, num_interp): + return np.logspace(math.log10(f_lower), 0, num=num_interp) + + def chirptime(m1, m2, f_lower, m_mode=None): """Compute the chirptime. @@ -69,7 +74,7 @@ def imr_duration(**params): return time_length * 1.1 -def interpolated_tf(m1, m2, m_mode=None, num_interp=100): +def interpolated_tf(m1, m2, m_mode=None, num_interp=100, f_lower=1e-4): """Interpolate the time frequency-track. Defaults to the dominant (2,2) mode and uses :code:`chirptime` to compute @@ -78,8 +83,7 @@ def interpolated_tf(m1, m2, m_mode=None, num_interp=100): # Using findchirp_chirptime in PyCBC to calculate # the time-frequency track of dominant mode to get # the corresponding `f_min` for `t_obs_start`. - freq_array = np.logspace(-4, 0, num=num_interp) - t_array = np.zeros(len(freq_array)) + freq_array = cached_freq_logspace(f_lower, num_interp) t_array = chirptime(m1=m1, m2=m2, f_lower=freq_array, m_mode=m_mode) tf_track = interp1d(t_array, freq_array) return tf_track @@ -107,6 +111,7 @@ def _bbhx_fd( length=1024, direct=False, num_interp=100, + interp_f_lower=1e-4, **params ): @@ -137,6 +142,7 @@ def _bbhx_fd( mode_array = list(params["mode_array"]) num_interp = int(num_interp) length = int(length) if length is not None else None + interp_f_lower = float(interp_f_lower) if ref_frame == 'LISA': t_ref_lisa = np.float64(params['tc']) + t_offset @@ -171,27 +177,39 @@ def _bbhx_fd( # The lowest m mode will have the lowest frequency at a given start time # so we use this to compute the track - min_m_mode = max([mode[1] for mode in mode_array]) + max_m_mode = max([mode[1] for mode in mode_array]) if ('f_lower' not in params) or (params['f_lower'] < 0): # the default value of 'f_lower' in PyCBC is -1. - tf_track = interpolated_tf( - m1, m2, m_mode=min_m_mode, num_interp=num_interp + t_max = chirptime( + m1=m1, m2=m2, f_lower=interp_f_lower, m_mode=max_m_mode ) - t_max = chirptime(m1=m1, m2=m2, f_lower=1e-4, m_mode=min_m_mode) if t_obs_start > t_max: # Avoid "above the interpolation range" issue. - f_min = 1e-4 + f_min = interp_f_lower else: + tf_track = interpolated_tf( + m1, + m2, + m_mode=max_m_mode, + num_interp=num_interp, + f_lower=interp_f_lower, + ) f_min = tf_track(t_obs_start) # in Hz else: f_min = np.float64(params['f_lower']) # in Hz - tf_track = interpolated_tf( - m1, m2, m_mode=min_m_mode, num_interp=num_interp + t_max = chirptime( + m1=m1, m2=m2, f_lower=interp_f_lower, m_mode=max_m_mode ) - t_max = chirptime(m1=m1, m2=m2, f_lower=1e-4, m_mode=min_m_mode) if t_obs_start > t_max: - f_min_tobs = 1e-4 + f_min_tobs = interp_f_lower else: + tf_track = interpolated_tf( + m1, + m2, + m_mode=max_m_mode, + num_interp=num_interp, + f_lower=interp_f_lower, + ) f_min_tobs = tf_track(t_obs_start) # in Hz if f_min < f_min_tobs: err_msg = f"Input 'f_lower' is lower than the value calculated from 't_obs_start'." From c7c5878eb646f2c6f9a042c7f1443d33ae2269df Mon Sep 17 00:00:00 2001 From: mj-will Date: Wed, 8 May 2024 12:00:50 +0100 Subject: [PATCH 31/36] add doc-string to explain new arguments --- BBHX_Phenom.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/BBHX_Phenom.py b/BBHX_Phenom.py index 1374763..67aa790 100644 --- a/BBHX_Phenom.py +++ b/BBHX_Phenom.py @@ -114,6 +114,36 @@ def _bbhx_fd( interp_f_lower=1e-4, **params ): + + """Function to generate frequency-domain waveforms using BBHx. + + Parameters + ---------- + ifos : list + List of interferometers + run_phenomd : bool + Flag passed to :code:`bbhx.waveformbuild.BBHWaveformFD` that determines + if PhenomD or PhenomHM is used. + ref_frame : {'LISA', 'SSB'} + Reference frame. + samples_points : numpy.ndarray, optional + Array of frequencies for computing the waveform + length : int + Length parameter passed to BBHx. Must be specified if + :code:`direct=False`. See BBHx documentation for more details. + direct : bool + See BBHx documentation. + num_interp : int + Number of interpolation points used for computing chirp time. + interp_f_lower : float + Lower frequency cutoff used for interpolation when computing the + chirp time. + + Returns + ------- + dict + A dictionary containing the the waveforms for each interferometer. + """ if ifos is None: raise Exception("Must define data streams to compute") From 65bd32b02d792270759bb0dbda56a9b16ddc814d Mon Sep 17 00:00:00 2001 From: mj-will Date: Wed, 8 May 2024 14:50:19 +0100 Subject: [PATCH 32/36] update comment --- BBHX_Phenom.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/BBHX_Phenom.py b/BBHX_Phenom.py index 67aa790..587c9db 100644 --- a/BBHX_Phenom.py +++ b/BBHX_Phenom.py @@ -205,8 +205,8 @@ def _bbhx_fd( err_msg = f"Don't recognise reference frame {ref_frame}. " err_msg = f"Known frames are 'LISA' and 'SSB'." - # The lowest m mode will have the lowest frequency at a given start time - # so we use this to compute the track + # We follow the convention used in LAL and set the frequency based on the + # highest m mode. This means that lower m modes will start at later times. max_m_mode = max([mode[1] for mode in mode_array]) if ('f_lower' not in params) or (params['f_lower'] < 0): # the default value of 'f_lower' in PyCBC is -1. From bd097e0022009c8cdf429a5b2b38e7aee37aee5d Mon Sep 17 00:00:00 2001 From: mj-will Date: Mon, 13 May 2024 09:17:09 +0100 Subject: [PATCH 33/36] remove option for `log_mf_min=None` in `get_waveform_genner` --- BBHX_Phenom.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/BBHX_Phenom.py b/BBHX_Phenom.py index 587c9db..3bd7e73 100644 --- a/BBHX_Phenom.py +++ b/BBHX_Phenom.py @@ -12,12 +12,10 @@ def get_waveform_genner(log_mf_min, run_phenomd=True): # See below where this function is called for description of how we handle # log_mf_min. - if log_mf_min is None: - wave_gen = BBHWaveformFD(amp_phase_kwargs=dict(run_phenomd=run_phenomd)) - else: - mf_min = math.exp(log_mf_min/25.) - wave_gen = BBHWaveformFD(amp_phase_kwargs=dict(run_phenomd=run_phenomd, - mf_min=mf_min)) + mf_min = math.exp(log_mf_min/25.) + wave_gen = BBHWaveformFD( + amp_phase_kwargs=dict(run_phenomd=run_phenomd, mf_min=mf_min), + ) return wave_gen From a2f4dbd605a482217ac889408b9b04932909b123 Mon Sep 17 00:00:00 2001 From: mj-will Date: Mon, 13 May 2024 10:01:32 +0100 Subject: [PATCH 34/36] handle mode_array=None for HM --- BBHX_Phenom.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BBHX_Phenom.py b/BBHX_Phenom.py index 3bd7e73..7e5dc5e 100644 --- a/BBHX_Phenom.py +++ b/BBHX_Phenom.py @@ -94,7 +94,7 @@ def waveform_setup(**kwargs): kwargs['mode_array'] = [(2, 2)] return _bbhx_fd(run_phenomd=True, **kwargs) elif kwargs['approximant'] == "BBHX_PhenomHM": - if 'mode_array' not in kwargs: + if kwargs.get('mode_array') is None: kwargs['mode_array'] = [(2, 2), (2, 1), (3, 3), (3, 2), (4, 4), (4, 3)] return _bbhx_fd(run_phenomd=False, **kwargs) else: From 15ef208eb30df077ab8008af3464da401f9773ab Mon Sep 17 00:00:00 2001 From: mj-will Date: Mon, 13 May 2024 15:13:59 +0100 Subject: [PATCH 35/36] add tests for HoM --- tests.py | 46 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 40 insertions(+), 6 deletions(-) diff --git a/tests.py b/tests.py index 9f4f623..aba3087 100644 --- a/tests.py +++ b/tests.py @@ -1,13 +1,24 @@ import numpy as np -from pycbc.waveform import get_fd_det_waveform +from pycbc.waveform import get_fd_det_waveform, get_fd_det_waveform_sequence import pytest -@pytest.mark.parametrize("ref_frame", ["SSB", "LISA"]) -def test_get_fd_det_waveform(ref_frame): +@pytest.fixture(params=["BBHX_PhenomD", "BBHX_PhenomHM"]) +def approximant(request): + return request.param + + +@pytest.fixture(params=["LISA", "SSB"]) +def ref_frame(request): + return request.param + + +@pytest.fixture() +def params(): params = {} - params["ref_frame"] = ref_frame - params["approximant"] = "BBHX_PhenomD" + params["approximant"] = "BBHX_PhenomHM" + params["ref_frame"] = "LISA" + params["ifos"] = ["LISA_A", "LISA_E", "LISA_T"] params["coa_phase"] = 0.0 params["mass1"] = 1e6 params["mass2"] = 8e5 @@ -26,7 +37,30 @@ def test_get_fd_det_waveform(ref_frame): params["eclipticlongitude"] = 0.5 params["eclipticlatitude"] = 0.23 params["polarization"] = 0.1 - wf = get_fd_det_waveform(ifos=["LISA_A", "LISA_E", "LISA_T"], **params) + return params + +def test_get_fd_det_waveform(params, ref_frame, approximant): + params["ref_frame"] = ref_frame + params["approximant"] = approximant + wf = get_fd_det_waveform(**params) # Check all 3 ifos are returned assert len(wf) == 3 + + +def test_get_fd_det_waveform_sequence(params, approximant): + params["ifos"] = "LISA_A" + params["approximant"] = approximant + freqs = np.array([1-4, 1e-3, 1e-2]) + wf = get_fd_det_waveform_sequence(sample_points=freqs, **params) + # Check all 3 ifos are returned + assert len(wf) == 1 + assert len(wf["LISA_A"]) == len(freqs) + + +@pytest.mark.parametrize("mode_array", [None, [(3, 3)], [(2, 2), (3, 3)]]) +def test_phenomhm_mode_array(params, mode_array): + params["approximant"] = "BBHX_PhenomHM" + params["mode_array"] = mode_array + wf = get_fd_det_waveform(**params) + assert len(wf) == 3 From c35104c910c29c2ec8ce425d2b52f4d8b9322fe0 Mon Sep 17 00:00:00 2001 From: mj-will Date: Mon, 13 May 2024 15:23:21 +0100 Subject: [PATCH 36/36] add TDI version to sequence test --- tests.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tests.py b/tests.py index 6268184..138c6cc 100644 --- a/tests.py +++ b/tests.py @@ -13,6 +13,11 @@ def ref_frame(request): return request.param +@pytest.fixture(params=["1.5", "2.0"]) +def tdi(request): + return request.param + + @pytest.fixture() def params(): params = {} @@ -41,7 +46,6 @@ def params(): return params -@pytest.mark.parametrize("tdi", ["1.5", "2.0"]) def test_get_fd_det_waveform(params, ref_frame, approximant, tdi): params["tdi"] = tdi params["ref_frame"] = ref_frame @@ -51,8 +55,9 @@ def test_get_fd_det_waveform(params, ref_frame, approximant, tdi): assert len(wf) == 3 -def test_get_fd_det_waveform_sequence(params, approximant): +def test_get_fd_det_waveform_sequence(params, approximant, tdi): params["ifos"] = "LISA_A" + params["tdi"] = tdi params["approximant"] = approximant freqs = np.array([1-4, 1e-3, 1e-2]) wf = get_fd_det_waveform_sequence(sample_points=freqs, **params)