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 __call__(self, tseries): """ Transform the real-valued time series stored in tseries into a complex-valued time series. The return value is a newly-allocated complex time series. The input time series is stored in the real part of the output time series, and the complex part stores the quadrature phase. """ # # transform to frequency series # lal.REAL8TimeFreqFFT(self.in_fseries, tseries, self.fwdplan) # # transform to complex time series # tseries = lal.CreateCOMPLEX16TimeSeries(length=self.n) lal.COMPLEX16FreqTimeFFT( tseries, self.add_quadrature_phase(self.in_fseries, self.n), self.revplan) # # done # return tseries
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 __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
name = "n1", epoch = h_sg.epoch, f0 = 0, deltaF = deltaF, sampleUnits = lal.lalDimensionlessUnit, length = h_sg.data.length/2 + 1 ) fwdlen = needed_samps wnb_fwdplan = lal.CreateForwardREAL8FFTPlan( h_wnb.data.length, fwdlen ) sg_fwdplan = lal.CreateForwardREAL8FFTPlan( h_sg.data.length, fwdlen ) #perform FFTs print 'performing FFTs... ' lal.REAL8TimeFreqFFT( hf_wnb, h_wnb, wnb_fwdplan ) lal.REAL8TimeFreqFFT( hf_sg, h_sg, sg_fwdplan ) # now can compute hrss. normally would account for psd in power but since it's set to 1 this is trivial # FIX ME check factor of 2, might actually be factor of 4 wnb_hrss = hf_wnb.data.data.conj() * hf_wnb.data.data wnb_power = 2 * np.real(sum(wnb_hrss)) * hf_wnb.deltaF sg_hrss = hf_sg.data.data.conj() * hf_sg.data.data sg_power = 2 * np.real(sum(sg_hrss)) * hf_sg.deltaF # ## Generate Plots #
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