Repository navigation
Time-domain likelihood (with heterodyning) - #1161
neha2023sharma wants to merge 21 commits into
Conversation
ColmTalbot
left a comment
There was a problem hiding this comment.
Thanks @neha2023sharma this looks in generally pretty good shape! Given the nature of the proposed change, there are a decent number of conceptual questions here. I haven't looked in detail through everything, but I looked at the interferometer, likelihood, and source additions which seem the most important.
There are also some things in the implementation that I think are points of tension with previous implementation choices. I appreciate you not changing existing code, but I think over the review process we may need to find a few changes where we can make changes to make the new functionality feel more natural.
|
|
||
| >>> from bilby.gw.detector import PowerSpectralDensity | ||
| >>> psd = PowerSpectralDensity.from_aligo() | ||
| >>> acf = AutoCovarianceFunction.from_power_spectral_density_array( |
There was a problem hiding this comment.
This begs the question of whether the AutoCovarianceFunction should mirror the PowerSpectralDensity more closely. Or even if they should just be one class.
There was a problem hiding this comment.
I think one class here would be better. The ACF is just the inverse Fourier transform of the PSD, so it makes sense to keep them together. I can add a method to PowerSpectralDensity that computes the ACF whenever needed.
Does this sound good? If so, I can make these changes.
| frequency_array = np.asarray(frequency_array, dtype=float) | ||
| psd_array = np.asarray(psd_array, dtype=float) | ||
| delta_f = frequency_array[1] - frequency_array[0] | ||
| frequency_values_missing = int(round(frequency_array[0] / delta_f)) | ||
| if frequency_values_missing > 0: | ||
| frequency_array = np.concatenate( | ||
| [np.arange(frequency_values_missing) * delta_f, frequency_array]) | ||
| psd_array = np.concatenate([np.zeros(frequency_values_missing), psd_array]) | ||
|
|
||
| psd_sampling_frequency = 2 * frequency_array[-1] | ||
| if sampling_frequency is not None and not np.isclose( | ||
| sampling_frequency, psd_sampling_frequency, rtol=1e-9, atol=0): | ||
| logger.warning( | ||
| f"The sampling frequency given ({sampling_frequency:g} Hz) is not equal to the one " | ||
| f"computed from the PSD ({psd_sampling_frequency:g} Hz); using " | ||
| f"{psd_sampling_frequency:g} Hz. An ACF computed with {sampling_frequency:g} Hz " | ||
| "would be wrongly scaled and its lags wrongly spaced, giving wrong likelihood " | ||
| "and SNR values.") | ||
|
|
||
| band = (frequency_array >= minimum_frequency) & (frequency_array <= maximum_frequency) | ||
| if fill_value is None: | ||
| fill_value = fill_multiplier * np.max(psd_array[band]) | ||
| else: | ||
| fill_multiplier = None | ||
| psd_array = psd_array.copy() | ||
| psd_array[~band] = fill_value | ||
| acf_array = 0.5 * np.fft.irfft(psd_array) * psd_sampling_frequency |
There was a problem hiding this comment.
This feels significant and large enough to just make it a standalone function to convert between PSDs and ACFs.
There was a problem hiding this comment.
I will move this to a standalone function power_spectral_density_to_autocovariance that converts PSD to ACF. The new ACF method in PowerSpectralDensity will call this function to compute the ACF.
| start_time=times[0]) | ||
|
|
||
|
|
||
| __all__ = ["condition_strain_data"] |
There was a problem hiding this comment.
__all__ should live at the top of modules if we define it.
There was a problem hiding this comment.
Thanks! I will move __all__ to the top of the modules.
| parameters=parameters, injection_polarizations=injection_polarizations, placement=placement) | ||
| return injection_polarizations | ||
|
|
||
| def inject_signal_time_domain_from_waveform_polarizations(self, parameters, injection_polarizations, |
There was a problem hiding this comment.
Can this use the existing functionality https://github.com/bilby-dev/bilby/blob/main/bilby/gw/detector/__init__.py#L33? If not, what is missing?
| polarizations_at = signal["polarizations_at"] | ||
|
|
||
| def rescaled(times): | ||
| polarizations = polarizations_at(times) |
There was a problem hiding this comment.
Wait, this is a function?
There was a problem hiding this comment.
Skipping this file for this pass.
| result, source, utils, waveform_generator) | ||
| from .waveform_generator import WaveformGenerator, LALCBCWaveformGenerator | ||
| from .likelihood import GravitationalWaveTransient | ||
| from .likelihood import GravitationalWaveTransient, TimeDomainGravitationalWaveTransient |
There was a problem hiding this comment.
I wouldn't add this extra top level import. I'd like to reduce the number we have over time.
| return output_sample | ||
|
|
||
|
|
||
| def _signal_polarizations_for_likelihood(likelihood, parameters): |
There was a problem hiding this comment.
This looks like a workaround for the method I suggested adding to the GravitationalWaveTransient.
|
|
||
| Returns | ||
| ======= | ||
| dict: polarizations_at, a function of the times relative to the merger (in s) |
There was a problem hiding this comment.
I don't understand this use case yet, is it for cases where you want to apply detector-based time delays before evaluating the model?
Co-authored-by: Colm Talbot <talbotcolm@gmail.com>
What it does
set_analysis_windows(duration=..., analysis_segment="inspiral" or "post_inspiral", segment_cut_time=..., cut_reference_parameters=...), wheresegment_cut_timeis the cut-off time relative to the merger, andcut_reference_parametersgives the reference merger time and sky location.placement:"nearest": the waveform is generated on its own time grid and then shifted to the data sample nearest to its arrival time in each detector. It is the simplest and fastest option, but the timing can be off by up to half a sample."fd_shift": the waveform is Fourier transformed, the exact time shift is applied as a phase in the frequency domain, and it is transformed back to the time domain."model_time": the waveform is evaluated directly at the model times of each detector, shifted by the arrival time, so no shifting or interpolation is needed afterwards. This option currently works with the IMRPhenomT/TP/THM/TPHM approximants.