def lal(self): """ Returns a LAL Object that contains this data """ lal_data = None if type(self._data) is not _numpy.ndarray: raise TypeError("Cannot return lal type from the GPU") elif self._data.dtype == _numpy.float32: lal_data = _lal.CreateREAL4FrequencySeries("", self._epoch, 0, self.delta_f, _lal.lalSecondUnit, len(self)) elif self._data.dtype == _numpy.float64: lal_data = _lal.CreateREAL8FrequencySeries("", self._epoch, 0, self.delta_f, _lal.lalSecondUnit, len(self)) elif self._data.dtype == _numpy.complex64: lal_data = _lal.CreateCOMPLEX8FrequencySeries( "", self._epoch, 0, self.delta_f, _lal.lalSecondUnit, len(self)) elif self._data.dtype == _numpy.complex128: lal_data = _lal.CreateCOMPLEX16FrequencySeries( "", self._epoch, 0, self.delta_f, _lal.lalSecondUnit, len(self)) lal_data.data.data[:] = self._data return lal_data
def lal(self): """Produces a LAL frequency series object equivalent to self. Returns ------- lal_data : {lal.*FrequencySeries} LAL frequency series object containing the same data as self. The actual type depends on the sample's dtype. If the epoch of self was 'None', the epoch of the returned LAL object will be LIGOTimeGPS(0,0); otherwise, the same as that of self. Raises ------ TypeError If frequency series is stored in GPU memory. """ lal_data = None if self._epoch is None: ep = _lal.LIGOTimeGPS(0,0) else: ep = self._epoch if self._data.dtype == _numpy.float32: lal_data = _lal.CreateREAL4FrequencySeries("",ep,0,self.delta_f,_lal.SecondUnit,len(self)) elif self._data.dtype == _numpy.float64: lal_data = _lal.CreateREAL8FrequencySeries("",ep,0,self.delta_f,_lal.SecondUnit,len(self)) elif self._data.dtype == _numpy.complex64: lal_data = _lal.CreateCOMPLEX8FrequencySeries("",ep,0,self.delta_f,_lal.SecondUnit,len(self)) elif self._data.dtype == _numpy.complex128: lal_data = _lal.CreateCOMPLEX16FrequencySeries("",ep,0,self.delta_f,_lal.SecondUnit,len(self)) lal_data.data.data[:] = self.numpy() return lal_data
def non_herm_hoff(self): """ Returns the 2-sided h(f) associated with the real-valued h(t) seen in a real instrument. Translates epoch as needed. Based on 'non_herm_hoff' in lalsimutils.py """ htR = self.real_hoft( ) # Generate real-valued TD waveform, including detector response if self.P.deltaF == None: # h(t) was not zero-padded, so do it now TDlen = nextPow2(htR.data.length) htR = lal.ResizeREAL8TimeSeries(htR, 0, TDlen) else: # Check zero-padding was done to expected length TDlen = int(1. / self.P.deltaF * 1. / self.P.deltaT) assert TDlen == htR.data.length fwdplan = lal.CreateForwardCOMPLEX16FFTPlan(htR.data.length, 0) htC = lal.CreateCOMPLEX16TimeSeries("hoft", htR.epoch, htR.f0, htR.deltaT, htR.sampleUnits, htR.data.length) # copy h(t) into a COMPLEX16 array which happens to be purely real htC.data.data[:htR.data.length] = htR.data.data # for i in range(htR.data.length): # htC.data.data[i] = htR.data.data[i] hf = lal.CreateCOMPLEX16FrequencySeries( "Template h(f)", htR.epoch, htR.f0, 1. / htR.deltaT / htR.data.length, lalsimutils.lsu_HertzUnit, htR.data.length) lal.COMPLEX16TimeFreqFFT(hf, htC, fwdplan) return hf
def _compute_waveform(self, df, f_final): """ Since EOBNRv2 is a time domain waveform, we have to generate it, then FFT to frequency domain. """ # need to compute dt from df, duration sample_rate = 2**np.ceil(np.log2(2 * f_final)) dt = 1. / sample_rate # get hplus hplus, hcross = lalsim.SimIMREOBNRv2DominantMode( 0., dt, self.m1 * MSUN_SI, self.m2 * MSUN_SI, self.bank.flow, 1e6 * PC_SI, 0.) # zero-pad up to 1/df N = int(sample_rate / df) hplus = lal.ResizeREAL8TimeSeries(hplus, 0, N) # taper lalsim.SimInspiralREAL8WaveTaper(hplus.data, lalsim.SIM_INSPIRAL_TAPER_START) # create vector to hold output and plan htilde = lal.CreateCOMPLEX16FrequencySeries("h(f)", hplus.epoch, hplus.f0, df, lal.HertzUnit, int(N / 2 + 1)) fftplan = lal.CreateForwardREAL8FFTPlan(N, 0) # do the fft lal.REAL8TimeFreqFFT(htilde, hplus, fftplan) return htilde
def matched_filter_real_fd(template, psd): fdfilter = lal.CreateCOMPLEX16FrequencySeries(template.name, template.epoch, template.f0, template.deltaF, template.sampleUnits, len(template.data.data)) fdfilter.data.data = template.data.data fdfilter = lal.WhitenCOMPLEX16FrequencySeries(fdfilter, psd) fdfilter.data.data /= np.sqrt(np.sum(np.abs(fdfilter.data.data)**2)) fdfilter = lal.WhitenCOMPLEX16FrequencySeries(fdfilter, psd) return fdfilter
def add_quadrature_phase(rseries): rseries_len = len(rseries.data.data) cseries = lal.CreateCOMPLEX16FrequencySeries(rseries.name, rseries.epoch, rseries.f0 - rseries.deltaF * (rseries_len - 1), rseries.deltaF, rseries.sampleUnits, 2 * (rseries_len - 1)) cseries.data.data[:rseries_len] = 0 cseries.data.data[rseries_len:] = 2 * rseries.data.data[1:-1] return cseries
def generate_template(mass1, mass2, S, f_low, sample_rate, template_duration, approximant, amplitude_order, phase_order): template_length = sample_rate * template_duration if approximant == lalsimulation.TaylorF2: zf, _ = lalsimulation.SimInspiralChooseFDWaveform( 0, 1 / template_duration, mass1 * lal.LAL_MSUN_SI, mass2 * lal.LAL_MSUN_SI, 0, 0, 0, 0, 0, 0, f_low, 0, 1e6 * lal.LAL_PC_SI, 0, 0, 0, None, None, amplitude_order, phase_order, approximant) lal.ResizeCOMPLEX16FrequencySeries(zf, 0, template_length // 2 + 1) # Generate over-whitened template psd = lal.CreateREAL8FrequencySeries(None, zf.epoch, zf.f0, zf.deltaF, lal.lalDimensionlessUnit, len(zf.data.data)) psd.data.data = S(abscissa(psd)) zW = matched_filter_spa(zf, psd) elif approximant == lalsimulation.TaylorT4: hplus, hcross = lalsimulation.SimInspiralChooseTDWaveform( 0, 1 / sample_rate, mass1 * lal.LAL_MSUN_SI, mass2 * lal.LAL_MSUN_SI, 0, 0, 0, 0, 0, 0, f_low, f_low, 1e6 * lal.LAL_PC_SI, 0, 0, 0, None, None, amplitude_order, phase_order, approximant) ht = lal.CreateREAL8TimeSeries(None, lal.LIGOTimeGPS(-template_duration), hplus.f0, hplus.deltaT, hplus.sampleUnits, template_length) hf = lal.CreateCOMPLEX16FrequencySeries(None, lal.LIGOTimeGPS(0), 0, 0, lal.lalDimensionlessUnit, template_length // 2 + 1) plan = CreateForwardREAL8FFTPlan(template_length, 0) ht.data.data[:-len(hplus.data.data)] = 0 ht.data.data[-len(hplus.data.data):] = hplus.data.data lal.REAL8TimeFreqFFT(hf, ht, plan) psd = lal.CreateREAL8FrequencySeries(None, hf.epoch, hf.f0, hf.deltaF, lal.lalDimensionlessUnit, len(hf.data.data)) psd.data.data = S(abscissa(psd)) zWreal = matched_filter_real(hf, psd) ht.data.data[:-len(hcross.data.data)] = 0 ht.data.data[-len(hcross.data.data):] = hcross.data.data lal.REAL8TimeFreqFFT(hf, ht, plan) zWimag = matched_filter_real(hf, psd) zW = lal.CreateCOMPLEX16TimeSeries(None, zWreal.epoch, zWreal.f0, zWreal.deltaT, zWreal.sampleUnits, len(zWreal.data.data)) zW.data.data = zWreal.data.data + zWimag.data.data * 1j else: raise ValueError("unrecognized approximant") return zW.data.data[::-1].conj() * np.sqrt( 2) * template_duration / sample_rate / 2
def create_FIR_whitener_kernel(length, duration, sample_rate, psd): assert psd # # Add another COMPLEX16TimeSeries and COMPLEX16FrequencySeries for kernel's FFT (Leo) # # Add another FFT plan for kernel FFT (Leo) fwdplan_kernel = lal.CreateForwardCOMPLEX16FFTPlan(length, 1) kernel_tseries = lal.CreateCOMPLEX16TimeSeries( name="timeseries of whitening kernel", epoch=LIGOTimeGPS(0.), f0=0., deltaT=1.0 / sample_rate, length=length, sampleUnits=lal.Unit("strain")) kernel_fseries = lal.CreateCOMPLEX16FrequencySeries( name="freqseries of whitening kernel", epoch=LIGOTimeGPS(0), f0=0.0, deltaF=1.0 / duration, length=length, sampleUnits=lal.Unit("strain s")) # # Obtain a kernel of zero-latency whitening filter and # adjust its length (Leo) # psd_fir_kernel = reference_psd.PSDFirKernel() (kernel, latency, fir_rate) = psd_fir_kernel.psd_to_linear_phase_whitening_fir_kernel( psd, nyquist=sample_rate / 2.0) ( kernel, theta ) = psd_fir_kernel.linear_phase_fir_kernel_to_minimum_phase_whitening_fir_kernel( kernel, fir_rate) kernel = kernel[-1::-1] # FIXME this is off by one sample, but shouldn't be. Look at the miminum phase function # assert len(kernel) == length if len(kernel) < length: kernel = numpy.append(kernel, numpy.zeros(length - len(kernel))) else: kernel = kernel[:length] kernel_tseries.data.data = kernel # # FFT of the kernel # lal.COMPLEX16TimeFreqFFT(kernel_fseries, kernel_tseries, fwdplan_kernel) #FIXME return kernel_fseries
def complex_hoff(self, force_T=False): htC = self.complex_hoft(force_T=force_T, deltaT=self.P.deltaT) TDlen = int(1. / self.P.deltaF * 1. / self.P.deltaT) assert TDlen == htC.data.length hf = lal.CreateCOMPLEX16FrequencySeries( "Template h(f)", htC.epoch, htC.f0, 1. / htC.deltaT / htC.data.length, lalsimutils.lsu_HertzUnit, htC.data.length) fwdplan = lal.CreateForwardCOMPLEX16FFTPlan(htC.data.length, 0) lal.COMPLEX16TimeFreqFFT(hf, htC, fwdplan) return hf
def DataFourier( ht ): # Complex fft wrapper (COMPLEX16Time ->COMPLEX16Freq. No error checking or padding! TDlen = ht.data.length fwdplan = lal.CreateForwardCOMPLEX16FFTPlan(TDlen, 0) hf = lal.CreateCOMPLEX16FrequencySeries("Template h(f)", ht.epoch, ht.f0, 1. / ht.deltaT / TDlen, lal.lalHertzUnit, TDlen) lal.COMPLEX16TimeFreqFFT(hf, ht, fwdplan) # assume memory freed by swig python return hf
def init_ifoData(ifoData): ifoData.modelDomain = lalsimulation.LAL_SIM_DOMAIN_FREQUENCY id = ifoData while id is not None: # Set the model domain to the frequency domain id.modelDomain = lalsimulation.LAL_SIM_DOMAIN_FREQUENCY # Create some vectors to store the waveforms in freqData = id.freqData id.freqModelhPlus = lal.CreateCOMPLEX16FrequencySeries( "freqModelhPlus", freqData.epoch, freqData.f0, freqData.deltaF, freqData.sampleUnits, freqData.data.length) id.freqModelhCross = lal.CreateCOMPLEX16FrequencySeries( "freqModelhCross", freqData.epoch, freqData.f0, freqData.deltaF, freqData.sampleUnits, freqData.data.length) #Initialise the lalinference variables on the ifoData id.modelParams = li.Variables() id = id.next
def to_lal_frequencyseries(self): """ Output the frequency series strain data as a LAL FrequencySeries object. """ laldata = lal.CreateCOMPLEX16FrequencySeries( "", lal.LIGOTimeGPS(self.start_time), self.frequency_array[0], (self.frequency_array[1] - self.frequency_array[0]), lal.SecondUnit, len(self.frequency_domain_strain)) laldata.data.data[:] = self.frequency_domain_strain return laldata
def prepare_fseries_for_complex16tseries(series): """ Construct a COMPLEX16FrequencySeries object suitable for storing the Fourier transform of a COMPLEX16TimeSeries object. """ n = len(series.data.data) return lal.CreateCOMPLEX16FrequencySeries( name=series.name, epoch=series.epoch, f0=series.f0, # note: non-zero f0 not supported by LAL deltaF=1.0 / (n * series.deltaT), sampleUnits=series.sampleUnits * lal.SecondUnit, length=n)
def __init__(self, mass1, mass2, S, f_low, approximant, amplitude_order, phase_order): """Create a TaylorF2 signal model with the given masses, PSD function S(f), PN amplitude order, and low-frequency cutoff.""" if approximant == lalsimulation.TaylorF2: # Frequency-domain post-Newtonian inspiral waveform. h, _ = lalsimulation.SimInspiralChooseFDWaveform( 0, 1, mass1 * lal.LAL_MSUN_SI, mass2 * lal.LAL_MSUN_SI, 0, 0, 0, 0, 0, 0, f_low, 0, 1e6 * lal.LAL_PC_SI, 0, 0, 0, None, None, amplitude_order, 0, approximant) # Find indices of first and last nonzero samples. nonzero = np.nonzero(h.data.data)[0] first_nonzero = nonzero[0] last_nonzero = nonzero[-1] elif approximant == lalsimulation.TaylorT4: # Time-domain post-Newtonian inspiral waveform. hplus, hcross = lalsimulation.SimInspiralChooseTDWaveform( 0, 1 / 4096, mass1 * lal.LAL_MSUN_SI, mass2 * lal.LAL_MSUN_SI, 0, 0, 0, 0, 0, 0, f_low, f_low, 1e6 * lal.LAL_PC_SI, 0, 0, 0, None, None, amplitude_order, phase_order, approximant) hplus.data.data += hcross.data.data hplus.data.data /= np.sqrt(2) h = lal.CreateCOMPLEX16FrequencySeries( None, lal.LIGOTimeGPS(0), 0, 0, lal.lalDimensionlessUnit, len(hplus.data.data) // 2 + 1) plan = CreateForwardREAL8FFTPlan(len(hplus.data.data), 0) lal.REAL8TimeFreqFFT(h, hplus, plan) f = h.f0 + len(h.data.data) * h.deltaF first_nonzero = long(np.floor((f_low - h.f0) / h.deltaF)) last_nonzero = long(np.ceil((2048 - h.f0) / h.deltaF)) last_nonzero = min(last_nonzero, len(h.data.data) - 1) else: raise ValueError("unrecognized approximant") # Frequency sample points self.dw = 2 * np.pi * h.deltaF f = h.f0 + h.deltaF * np.arange(first_nonzero, last_nonzero + 1) self.w = 2 * np.pi * f # Throw away leading and trailing zeros. h = h.data.data[first_nonzero:last_nonzero + 1] self.denom_integrand = 4 / (2 * np.pi) * (np.square(h.real) + np.square(h.imag)) / S(f) self.den = np.trapz(self.denom_integrand, dx=self.dw)
def _compute_waveform(self, df, f_final): # Time domain, so compute then FFT # need to compute dt from df, duration sample_rate = 2**np.ceil(np.log2(2 * f_final)) dt = 1. / sample_rate # Generate waveform in time domain hplus, hcross = lalsim.SimInspiralSpinTaylorT5( 0, # GW phase at reference freq (rad) dt, # sampling interval (s) self.m1 * lal.MSUN_SI, # mass of companion 1 (kg) self.m2 * lal.MSUN_SI, # mass of companion 2 (kg) self.bank.flow, # start GW frequency (Hz) 1e6 * PC_SI, # distance of source (m) self.s1x, # initial value of S1x self.s1y, # initial value of S1y self.s1z, # initial value of S1z self.s2x, # initial value of S2x self.s2y, # initial value of S2y self.s2z, # initial value of S2z self. inclination, # inclination angle - careful with definition (line of sight to total vs orbital angular momentum) 7, # twice PN phase order 0) # project onto detector hoft = project_hplus_hcross(hplus, hcross, self.theta, self.phi, self.psi) # zero-pad up to 1/df N = int(sample_rate / df) hoft = lal.ResizeREAL8TimeSeries(hoft, 0, N) # taper lalsim.SimInspiralREAL8WaveTaper(hoft.data, lalsim.SIM_INSPIRAL_TAPER_STARTEND) # create vector to hold output and plan htilde = lal.CreateCOMPLEX16FrequencySeries("h(f)", hoft.epoch, hoft.f0, df, lal.HertzUnit, int(N / 2 + 1)) fftplan = lal.CreateForwardREAL8FFTPlan(N, 0) # do the fft lal.REAL8TimeFreqFFT(htilde, hoft, fftplan) return htilde
def frequency_series(self, deltaF, detector=None, fwdplan=None): """ Generate a frequency series at a given frequency bin spacing deltaF (Hz) for the waveform using a call to lalsimulation. The function time_series is called first to generate the waveform in the time domain. If detector is None (default), return only hf with no antenna patterns applied. Otherwise, apply the proper detector anntenna patterns and return the resultant series hf = FFT[F+h+ + Fxhx]. """ # FIXME: This is overkill for BTLWNB, and won't work entirely for # cosmic strings sampling_rate = 4 * (self.frequency + (self.bandwidth or 0)) sampling_rate = 2**(int(math.log(sampling_rate, 2) + 1)) self.__get_fwdplan(sampling_rate, deltaF) if detector is not None: h = self.time_series(sampling_rate, detector) else: hp, hx = self.time_series(sampling_rate, detector) h = lal.CreateREAL8TimeSeries(name="TD signal", epoch=hp.epoch, f0=0, deltaT=hp.deltaT, sampleUnits=lal.DimensionlessUnit, length=hp.data.length) h.data.data = hp.data.data + hx.data.data # zero pad needed_samps = int(sampling_rate / deltaF) prevlen = h.data.length if h.data.length < needed_samps: h = lal.ResizeREAL8TimeSeries(h, 0, needed_samps) elif h.data.length > needed_samps: h = lal.ResizeREAL8TimeSeries(h, h.data.length - needed_samps, needed_samps) # adjust heterodyne frequency to match flow hf = lal.CreateCOMPLEX16FrequencySeries( name="FD signal", epoch=h.epoch, f0=0, deltaF=deltaF, sampleUnits=lal.DimensionlessUnit, length=int(h.data.length / 2 + 1)) # Forward FFT lal.REAL8TimeFreqFFT(hf, h, self._fwdplan) return hf
def add_quadrature_phase(fseries, n): """ From the Fourier transform of a real-valued function of time, compute and return the Fourier transform of the complex-valued function of time whose real component is the original time series and whose imaginary component is the quadrature phase of the real part. fseries is a LAL COMPLEX16FrequencySeries and n is the number of samples in the original time series. """ # # positive frequencies include Nyquist if n is even # have_nyquist = not (n % 2) # # shuffle frequency bins # positive_frequencies = numpy.array(fseries.data.data) # work with copy positive_frequencies[0] = 0 # set DC to zero zeros = numpy.zeros((len(positive_frequencies), ), dtype="cdouble") if have_nyquist: # complex transform never includes positive Nyquist positive_frequencies = positive_frequencies[:-1] # # prepare output frequency series # out_fseries = lal.CreateCOMPLEX16FrequencySeries( name=fseries.name, epoch=fseries.epoch, f0=fseries.f0, # caution: only 0 is supported deltaF=fseries.deltaF, sampleUnits=fseries.sampleUnits, length=len(zeros) + len(positive_frequencies) - 1) out_fseries.data.data = numpy.concatenate( (zeros, 2 * positive_frequencies[1:])) return out_fseries
def normalized_autocorrelation(fseries, revplan): data = fseries.data.data fseries = lal.CreateCOMPLEX16FrequencySeries( name=fseries.name, epoch=fseries.epoch, f0=fseries.f0, deltaF=fseries.deltaF, sampleUnits=fseries.sampleUnits, length=len(data)) fseries.data.data = data * numpy.conj(data) tseries = lal.CreateCOMPLEX16TimeSeries(name="timeseries", epoch=fseries.epoch, f0=fseries.f0, deltaT=1. / (len(data) * fseries.deltaF), length=len(data), sampleUnits=lal.DimensionlessUnit) tseries.data.data = numpy.empty((len(data), ), dtype="cdouble") lal.COMPLEX16FreqTimeFFT(tseries, fseries, revplan) data = tseries.data.data tseries.data.data = data / data[0] return tseries
def colored_noise(epoch, duration, sample_rate, psd): """Generate a REAL8TimeSeries containing duration seconds of colored Gaussian noise at the given sample rate, with the start time given by epoch. psd should be an instance of REAL8FrequencySeries containing a discretely sample power spectrum with f0=0, deltaF=1/duration, and a length of ((duration * sample_rate) // 2 + 1) samples. """ data_length = duration * sample_rate plan = CreateReverseREAL8FFTPlan(data_length, 0) x = lal.CreateREAL8TimeSeries( None, lal.LIGOTimeGPS(0), 0, 0, lal.DimensionlessUnit, data_length) xf = lal.CreateCOMPLEX16FrequencySeries( None, epoch, 0, 1 / duration, lal.DimensionlessUnit, data_length // 2 + 1) white_noise = (np.random.randn(len(xf.data.data)) + np.random.randn(len(xf.data.data)) * 1j) # On line 1288 of lal's AverageSpectrum.c, in the code comments for # XLALWhitenCOMPLEX8FrequencySeries, it says that according to the LAL # conventions a whitened frequency series should consist of bins whose # real and imaginary parts each have a variance of 1/2. white_noise /= np.sqrt(2) # The factor of sqrt(2 * psd.deltaF) comes from the value of 'norm' on # line 1362 of AverageSpectrum.c. xf.data.data = white_noise * np.sqrt(psd.data.data / (2 * psd.deltaF)) # Detrend the data: no DC component. xf.data.data[0] = 0 # Return to time domain. lal.REAL8FreqTimeFFT(x, xf, plan) # Copy over metadata. x.epoch = epoch x.sampleUnits = lal.StrainUnit # Done. return x
def simulate_snr(ra, dec, psi, inc, distance, epoch, gmst, H, S, response, location, measurement_error='zero-noise'): from scipy.interpolate import interp1d from ..bayestar import filter from ..bayestar.interpolation import interpolate_max duration = 0.1 # Calculate whitened template autocorrelation sequence. HS = filter.signal_psd_series(H, S) n = len(HS.data.data) acor, sample_rate = filter.autocorrelation(HS, duration) # Calculate time, amplitude, and phase. u = np.cos(inc) u2 = np.square(u) signal_model = filter.SignalModel(HS) horizon = signal_model.get_horizon_distance() Fplus, Fcross = lal.ComputeDetAMResponse(response, ra, dec, psi, gmst) toa = lal.TimeDelayFromEarthCenter(location, ra, dec, epoch) z = (0.5 * (1 + u2) * Fplus + 1j * u * Fcross) * horizon / distance # Calculate complex autocorrelation sequence. snr_series = z * np.concatenate((acor[:0:-1].conj(), acor)) # If requested, add noise. if measurement_error == 'gaussian-noise': sigmasq = 4 * np.sum(HS.deltaF * np.abs(HS.data.data)) amp = 4 * n * HS.deltaF**0.5 * np.sqrt(HS.data.data / sigmasq) N = lal.CreateCOMPLEX16FrequencySeries('', HS.epoch, HS.f0, HS.deltaF, HS.sampleUnits, n) N.data.data = amp * (np.random.randn(n) + 1j * np.random.randn(n)) noise_term, sample_rate_2 = filter.autocorrelation(N, 2 * duration - 1 / sample_rate, normalize=False) assert sample_rate == sample_rate_2 snr_series += noise_term # Shift SNR series to the nearest sample. int_samples, frac_samples = divmod( (1e-9 * epoch.gpsNanoSeconds + toa) * sample_rate, 1) if frac_samples > 0.5: int_samples += 1 frac_samples -= 1 epoch = lal.LIGOTimeGPS(epoch.gpsSeconds, 0) n = len(acor) - 1 mprime = np.arange(-n, n + 1) m = mprime + frac_samples re, im = (interp1d(m, x, kind='cubic', bounds_error=False, fill_value=0)(mprime) for x in (snr_series.real, snr_series.imag)) snr_series = re + 1j * im # Find the trigger values. i_nearest = np.argmax(np.abs( snr_series[n - n // 2:n + n // 2 + 1])) + n - n // 2 i_interp, z_interp = interpolate_max(i_nearest, snr_series, n // 2, method='lanczos') toa = epoch + (int_samples + i_interp - n) / sample_rate snr = np.abs(z_interp) phase = np.angle(z_interp) # Shift and truncate the SNR time series. epoch += (int_samples + i_nearest - n - n // 2) / sample_rate snr_series = snr_series[(i_nearest - n // 2):(i_nearest + n // 2 + 1)] tseries = lal.CreateCOMPLEX8TimeSeries('snr', epoch, 0, 1 / sample_rate, lal.DimensionlessUnit, len(snr_series)) tseries.data.data = snr_series return horizon, snr, phase, toa, tseries
def _compute_waveform(self, df, f_final): # Time domain, so compute then FFT # need to compute dt from df, duration sample_rate = 2**np.ceil(np.log2(2 * f_final)) dt = 1. / sample_rate # Generate waveform in time domain hplus, hcross = lalsim.SimInspiralSpinTaylorT4( 0, # GW phase at reference freq (rad) 1, # tail gauge term (default = 1) dt, # sampling interval (s) self.m1 * lal.MSUN_SI, # mass of companion 1 (kg) self.m2 * lal.MSUN_SI, # mass of companion 2 (kg) self.bank.flow, # start GW frequency (Hz) self.bank. flow, # reference GW frequency at which phase is set (Hz) 1e6 * PC_SI, # distance of source (m) self.s1x, # initial value of S1x self.s1y, # initial value of S1y self.s1z, # initial value of S1z self.s2x, # initial value of S2x self.s2y, # initial value of S2y self.s2z, # initial value of S2z np.sin(self.inclination), # initial value of LNhatx 0, # initial value of LNhaty np.cos(self.inclination), # initial value of LNhatz np.cos(self.inclination), # initial value of E1x 0, # initial value of E1y -np.sin(self.inclination), # initial value of E1z 0, # tidal deformability of mass 1 0, # tidal deformability of mass 2 1, # phenom. parameter describing induced quad. moment of body 1 (=1 for BHs, ~2-12 for NSs) 1, # phenom. parameter describing induced quad. moment of body 2 (=1 for BHs, ~2-12 for NSs) 7, # twice PN spin order 0, # twice PN tidal order 7, # twice PN phase order 0 # twice PN amplitude order ) # project onto detector hoft = project_hplus_hcross(hplus, hcross, self.theta, self.phi, self.psi) # zero-pad up to 1/df N = ceil_pow_2(int(sample_rate / df)) hoft = lal.ResizeREAL8TimeSeries(hoft, 0, N) # taper lalsim.SimInspiralREAL8WaveTaper(hoft.data, lalsim.SIM_INSPIRAL_TAPER_STARTEND) # create vector to hold output and plan htilde = lal.CreateCOMPLEX16FrequencySeries("h(f)", hoft.epoch, hoft.f0, df, lal.HertzUnit, int(N / 2 + 1)) fftplan = lal.CreateForwardREAL8FFTPlan(N, 0) # do the fft lal.REAL8TimeFreqFFT(htilde, hoft, fftplan) return htilde
def generate_template(template_bank_row, approximant, sample_rate, duration, f_low, f_high, amporder=0, order=7, fwdplan=None, fworkspace=None): """ Generate a single frequency-domain template, which 1. is band-limited between f_low and f_high, 2. has an IFFT which is duration seconds long and 3. has an IFFT which is sampled at sample_rate Hz """ if approximant not in templates.gstlal_approximants: raise ValueError("Unsupported approximant given %s" % approximant) assert f_high <= sample_rate // 2 # FIXME use hcross somday? # We don't here because it is not guaranteed to be orthogonal # and we add orthogonal phase later parameters = {} parameters['m1'] = lal.MSUN_SI * template_bank_row.mass1 parameters['m2'] = lal.MSUN_SI * template_bank_row.mass2 parameters['S1x'] = template_bank_row.spin1x parameters['S1y'] = template_bank_row.spin1y parameters['S1z'] = template_bank_row.spin1z parameters['S2x'] = template_bank_row.spin2x parameters['S2y'] = template_bank_row.spin2y parameters['S2z'] = template_bank_row.spin2z parameters['distance'] = 1.e6 * lal.PC_SI parameters['inclination'] = 0. parameters['phiRef'] = 0. parameters['longAscNodes'] = 0. parameters['eccentricity'] = 0. parameters['meanPerAno'] = 0. parameters['deltaF'] = 1.0 / duration parameters['f_min'] = f_low parameters['f_max'] = f_high parameters['f_ref'] = 0. parameters['LALparams'] = None parameters['approximant'] = lalsim.GetApproximantFromString( str(approximant)) hplus, hcross = lalsim.SimInspiralFD(**parameters) assert len(hplus.data.data) == int(round(f_high * duration)) + 1 # pad the output vector if the sample rate was higher than the # requested final frequency if f_high < sample_rate / 2: fseries = lal.CreateCOMPLEX16FrequencySeries( name=hplus.name, epoch=hplus.epoch, f0=hplus.f0, deltaF=hplus.deltaF, length=int(round(sample_rate * duration)) // 2 + 1, sampleUnits=hplus.sampleUnits) fseries.data.data = numpy.zeros(fseries.data.length) fseries.data.data[:hplus.data.length] = hplus.data.data[:] hplus = fseries return hplus
def __init__(self, template_table, approximant, psd, f_low, time_slices, autocorrelation_length=None, fhigh=None): self.template_table = template_table self.approximant = approximant self.f_low = f_low self.time_slices = time_slices self.autocorrelation_length = autocorrelation_length self.fhigh = fhigh self.sample_rate_max = max(time_slices["rate"]) self.duration = max(time_slices["end"]) self.length_max = int(round(self.duration * self.sample_rate_max)) if self.fhigh is None: self.fhigh = self.sample_rate_max / 2. # Some input checking to avoid incomprehensible error messages if not self.template_table: raise ValueError("template list is empty") if self.f_low < 0.: raise ValueError("f_low must be >= 0. %s" % repr(self.f_low)) # working f_low to actually use for generating the waveform. pick # template with lowest chirp mass, compute its duration starting # from f_low; the extra time is 10% of this plus 3 cycles (3 / # f_low); invert to obtain f_low corresponding to desired padding. # NOTE: because SimInspiralChirpStartFrequencyBound() does not # account for spin, we set the spins to 0 in the call to # SimInspiralChirpTimeBound() regardless of the component's spins. template = min(self.template_table, key=lambda row: row.mchirp) tchirp = lalsim.SimInspiralChirpTimeBound(self.f_low, template.mass1 * lal.MSUN_SI, template.mass2 * lal.MSUN_SI, 0., 0.) self.working_f_low = lalsim.SimInspiralChirpStartFrequencyBound( 1.1 * tchirp + 3. / self.f_low, template.mass1 * lal.MSUN_SI, template.mass2 * lal.MSUN_SI) # Add duration of PSD to template length for PSD ringing, round up to power of 2 count of samples self.working_length = templates.ceil_pow_2(self.length_max + round(1. / psd.deltaF * self.sample_rate_max)) self.working_duration = float( self.working_length) / self.sample_rate_max if psd is not None: # Smooth the PSD and interpolate to required resolution self.psd = condition_psd(psd, 1.0 / self.working_duration, minfs=(self.working_f_low, self.f_low), maxfs=(self.sample_rate_max / 2.0 * 0.90, self.sample_rate_max / 2.0)) if FIR_WHITENER: # Compute a frequency response of the time-domain whitening kernel and effectively taper the psd by zero-ing some elements for a FIR kernel self.kernel_fseries = taperzero_fseries(create_FIR_whitener_kernel(self.working_length, self.working_duration, self.sample_rate_max, self.psd),\ minfs = (self.working_f_low, self.f_low),\ maxfs = (self.sample_rate_max / 2.0 * 0.90, self.sample_rate_max / 2.0)\ ) self.revplan = lal.CreateReverseCOMPLEX16FFTPlan( self.working_length, 1) self.fwdplan = lal.CreateForwardREAL8FFTPlan(self.working_length, 1) self.tseries = lal.CreateCOMPLEX16TimeSeries( name="timeseries", epoch=LIGOTimeGPS(0.), f0=0., deltaT=1.0 / self.sample_rate_max, length=self.working_length, sampleUnits=lal.Unit("strain")) self.fworkspace = lal.CreateCOMPLEX16FrequencySeries( name="template", epoch=LIGOTimeGPS(0), f0=0.0, deltaF=1.0 / self.working_duration, length=self.working_length // 2 + 1, sampleUnits=lal.Unit("strain s")) # Calculate the maximum ring down time or maximum shift time if approximant in templates.gstlal_IMR_approximants: self.max_ringtime = max([ chirptime.ringtime( row.mass1 * lal.MSUN_SI + row.mass2 * lal.MSUN_SI, chirptime.overestimate_j_from_chi( max(row.spin1z, row.spin2z))) for row in self.template_table ]) else: if self.sample_rate_max > 2. * self.fhigh: # Calculate the maximum time we need to shift the early warning # waveforms forward by, calculated by the 3.5 approximation from # fhigh to ISCO. self.max_shift_time = max([ spawaveform.chirptime( row.mass1, row.mass2, 7, fhigh, 0., spawaveform.computechi(row.mass1, row.mass2, row.spin1z, row.spin2z)) for row in self.template_table ])
# resize if necessary for FFT if h_wnb.data.length < needed_samps: h_wnb = lal.ResizeREAL8TimeSeries( h_wnb, 0, needed_samps ) elif h_wnb.data.length > needed_samps: h_wnb = lal.ResizeREAL8TimeSeries( h_wnb, h_wnb.data.length-needed_samps, needed_samps ) if h_sg.data.length < needed_samps: h_sg = lal.ResizeREAL8TimeSeries( h_sg, 0, needed_samps ) elif h_sg.data.length > needed_samps: h_sg = lal.ResizeREAL8TimeSeries( h_sg, h_sg.data.length-needed_samps, needed_samps ) hf_wnb = lal.CreateCOMPLEX16FrequencySeries( name = "m1", epoch = h_wnb.epoch, f0 = 0, deltaF = deltaF, sampleUnits = lal.lalDimensionlessUnit, length = h_wnb.data.length/2 + 1 ) hf_sg = lal.CreateCOMPLEX16FrequencySeries( name = "n1", epoch = h_sg.epoch, f0 = 0, deltaF = deltaF, sampleUnits = lal.lalDimensionlessUnit, length = h_sg.data.length/2 + 1 ) fwdlen = needed_samps
def analyze_event(P_list, indx_event, data_dict, psd_dict, fmax, opts, inv_spec_trunc_Q=inv_spec_trunc_Q, T_spec=T_spec): nEvals = 0 P = P_list[indx_event] # Precompute t_window = 0.15 rholms_intp, cross_terms, cross_terms_V, rholms, rest = factored_likelihood.PrecomputeLikelihoodTerms( fiducial_epoch, t_window, P, data_dict, psd_dict, opts.l_max, fmax, False, inv_spec_trunc_Q, T_spec, NR_group=NR_template_group, NR_param=NR_template_param, use_external_EOB=opts.use_external_EOB, nr_lookup=opts.nr_lookup, nr_lookup_valid_groups=opts.nr_lookup_group, perturbative_extraction=opts.nr_perturbative_extraction, use_provided_strain=opts.nr_use_provided_strain, hybrid_use=opts.nr_hybrid_use, hybrid_method=opts.nr_hybrid_method, ROM_group=opts.rom_group, ROM_param=opts.rom_param, ROM_use_basis=opts.rom_use_basis, verbose=opts.verbose, quiet=not opts.verbose, ROM_limit_basis_size=opts.rom_limit_basis_size_to, no_memory=opts.no_memory, skip_interpolation=opts.vectorized) # Only allocate the FFT plans ONCE ifo0 = psd_dict.keys()[0] npts = data_dict[ifo0].data.length print(" Allocating FFT forward, reverse plans ", npts) fwdplan = lal.CreateForwardREAL8FFTPlan(npts, 0) revplan = lal.CreateReverseREAL8FFTPlan(npts, 0) for ifo in psd_dict: # Plot PSDs plt.figure(99) # PSD figure) fvals = psd_dict[ifo].f0 + np.arange( psd_dict[ifo].data.length) * psd_dict[ifo].deltaF plt.plot(fvals, np.log10(np.sqrt(psd_dict[ifo].data.data)), label=ifo) plt.figure(88) plt.plot(fvals, fvals**(-14. / 6.) / psd_dict[ifo].data.data) plt.figure(1) # Plot inverse spectrum filters # - see lalsimutils code for inv_spec_trunc_Q , copied verbatim plt.figure(98) IP_here = lalsimutils.ComplexOverlap(P.fmin, fmax, 1. / 2 / P.deltaT, P.deltaF, psd_dict[ifo], False, inv_spec_trunc_Q, T_spec) WFD = lal.CreateCOMPLEX16FrequencySeries('FD root inv. spec.', lal.LIGOTimeGPS(0.), 0., IP_here.deltaF, lal.DimensionlessUnit, IP_here.len1side) WTD = lal.CreateREAL8TimeSeries('TD root inv. spec.', lal.LIGOTimeGPS(0.), 0., IP_here.deltaT, lal.DimensionlessUnit, IP_here.len2side) print(ifo, IP_here.len2side) # if not(fwdplan is None): WFD.data.data[:] = np.sqrt(IP_here.weights) # W_FD is 1/sqrt(S_n(f)) WFD.data.data[0] = WFD.data.data[-1] = 0. # zero 0, f_Nyq bins lal.REAL8FreqTimeFFT(WTD, WFD, revplan) # IFFT to TD tvals = IP_here.deltaT * np.arange(IP_here.len2side) plt.plot(tvals, np.log10(np.abs(WTD.data.data)), label=ifo) # Plot Q's plt.figure(1) plt.clf() for mode in rholms[ifo]: Q_here = rholms[ifo][mode] tvals = lalsimutils.evaluate_tvals(Q_here) - P.tref plt.plot(tvals, np.abs(Q_here.data.data), label=ifo + str(mode[0]) + "," + str(mode[1])) plt.legend() plt.xlabel("t (s)") plt.xlim(-0.1, 0.1) plt.xlabel("$Q_{lm}$") plt.title(ifo) plt.savefig(ifo + "_Q.png") plt.figure(99) plt.legend() plt.xlabel("f(Hz)") plt.ylabel(r'$\sqrt{S_h}$') plt.ylim(-24, -21) plt.savefig("PSD_plots.png") plt.xlim(5, 800) plt.savefig("PSD_plots_detail.png") plt.xlim(10, 400) plt.ylim(-23.5, -22) plt.savefig("PSD_plots_detail_fine.png") plt.figure(98) plt.legend() plt.xlabel("t (s)") plt.savefig("PSD_inv_plots.png") plt.figure(88) plt.legend() plt.xlabel("f (Hz)") plt.ylabel(r'$(S_h f^{15/6})^{-1}$') plt.savefig("PSD_weight_f.png")