diff --git a/bilby/gw/detector.py b/bilby/gw/detector.py index 9b01722dc445c8c0c1cea58b12ead3a864045cde..a8e44849b4d2e7f03b52b4387a6d34c076888aac 100644 --- a/bilby/gw/detector.py +++ b/bilby/gw/detector.py @@ -438,7 +438,9 @@ class InterferometerStrainData(object): strain = strain.filter(bp, filtfilt=True) self._time_domain_strain = strain.value - def create_power_spectral_density(self, fft_length, name='unknown', outdir=None): + def create_power_spectral_density( + self, fft_length, overlap=0, name='unknown', outdir=None, + analysis_segment_start_time=None): """ Use the time domain strain to generate a power spectral density This create a Tukey-windowed power spectral density and writes it to a @@ -448,12 +450,17 @@ class InterferometerStrainData(object): ---------- fft_length: float Duration of the analysis segment. + overlap: float + Number of seconds of overlap between FFTs. name: str The name of the detector, used in storing the PSD. Defaults to "unknown". outdir: str The output directory to write the PSD file too. If not given, the PSD will not be written to file. + analysis_segment_start_time: float + The start time of the analysis segment, if given, this data will + be removed before creating the PSD. Returns ------- @@ -461,11 +468,24 @@ class InterferometerStrainData(object): The frequencies and power spectral density array """ - strain = gwpy.timeseries.TimeSeries(self.time_domain_strain, sample_rate=self.sampling_frequency) + + if analysis_segment_start_time is not None: + logger.info("Removing analysis segment data from the PSD data") + analysis_segment_end_time = analysis_segment_start_time + fft_length + idxs = ( + (self.time_array > analysis_segment_start_time) & + (self.time_array < analysis_segment_end_time)) + data = self.time_domain_strain[idxs] + else: + data = self.time_domain_strain + + strain = gwpy.timeseries.TimeSeries(data=data, sample_rate=self.sampling_frequency) psd_alpha = 2 * self.roll_off / fft_length - logger.info("Creating PSD with non-overlapping tukey window, alpha={}, roll off={}".format( - psd_alpha, self.roll_off)) - psd = strain.psd(fftlength=fft_length, overlap=0, window=('tukey', psd_alpha)) + logger.info( + "Tukey window PSD data with alpha={}, roll off={}".format( + psd_alpha, self.roll_off)) + psd = strain.psd( + fftlength=fft_length, overlap=overlap, window=('tukey', psd_alpha)) if outdir: psd_file = '{}/{}_PSD_{}_{}.txt'.format(outdir, name, self.start_time, self.duration) @@ -1801,7 +1821,8 @@ class PowerSpectralDensity(object): @staticmethod def from_frame_file(frame_file, psd_start_time, psd_duration, fft_length=4, sampling_frequency=4096, roll_off=0.2, - channel=None): + overlap=0, channel=None, name=None, outdir=None, + analysis_segment_start_time=None): """ Generate power spectral density from a frame file Parameters @@ -1819,15 +1840,25 @@ class PowerSpectralDensity(object): This is twice the maximum frequency. roll_off: float, optional Rise time in seconds of tukey window. + overlap: float, + Number of seconds of overlap between FFTs. channel: str, optional Name of channel to use to generate PSD. + name, outdir: str, optional + Name (and outdir) of the detector for which a PSD is to be + generated. + analysis_segment_start_time: float, optional + The start time of the analysis segment, if given, this data will + be removed before creating the PSD. """ strain = InterferometerStrainData(roll_off=roll_off) strain.set_from_frame_file( frame_file, start_time=psd_start_time, duration=psd_duration, channel=channel, sampling_frequency=sampling_frequency) - frequency_array, psd_array = strain.create_power_spectral_density(fft_length=fft_length) + frequency_array, psd_array = strain.create_power_spectral_density( + fft_length=fft_length, name=name, outdir=outdir, overlap=overlap, + analysis_segment_start_time=analysis_segment_start_time) return PowerSpectralDensity(frequency_array=frequency_array, psd_array=psd_array) @staticmethod @@ -2256,23 +2287,30 @@ def get_event_data( def load_data_from_cache_file( - cache_file, start_time, segment_duration, psd_duration, - channel_name=None, sampling_frequency=4096): + cache_file, start_time, segment_duration, psd_duration, psd_start_time, + channel_name=None, sampling_frequency=4096, roll_off=0.2, + overlap=0, outdir=None): """ Helper routine to generate an interferometer from a cache file Parameters ---------- cache_file: str Path to the location of the cache file - start_time: float - GPS start time of the segment + start_time, psd_start_time: float + GPS start time of the segment and data stretch used for the PSD segment_duration, psd_duration: float Segment duration and duration of data to use to generate the PSD (in seconds). + roll_off: float, optional + Rise time in seconds of tukey window. + overlap: float, + Number of seconds of overlap between FFTs. channel_name: str Channel name sampling_frequency: int Sampling frequency + outdir: str, optional + The output directory in which the data is saved Returns ------- @@ -2283,7 +2321,6 @@ def load_data_from_cache_file( data_set = False psd_set = False - psd_start_time = start_time - psd_duration with open(cache_file, 'r') as ff: for line in ff: @@ -2307,10 +2344,17 @@ def load_data_from_cache_file( if not psd_set & psd_in_cache: ifo.power_spectral_density = \ PowerSpectralDensity.from_frame_file( - cache.path, psd_start_time=psd_start_time, + cache.path, + psd_start_time=psd_start_time, psd_duration=psd_duration, + fft_length=segment_duration, + sampling_frequency=sampling_frequency, + roll_off=roll_off, + overlap=overlap, channel=channel_name, - sampling_frequency=sampling_frequency) + name=cache.observatory, + outdir=outdir, + analysis_segment_start_time=start_time) psd_set = True if data_set and psd_set: return ifo