def fourier_segments(self): """ Return a list of the FFT'd segments. Return the list of FrequencySeries. Additional properties are added that describe the strain segment. The property 'analyze' is a slice corresponding to the portion of the time domain equivelant of the segment to analyze for triggers. The value 'cumulative_index' indexes from the beginning of the original strain series. """ if not self._fourier_segments: self._fourier_segments = [] for seg_slice, ana in zip(self.segment_slices, self.analyze_slices): if seg_slice.start >= 0 and seg_slice.stop <= len(self.strain): freq_seg = make_frequency_series(self.strain[seg_slice]) # Assume that we cannot have a case where we both zero-pad on # both sides elif seg_slice.start < 0: strain_chunk = self.strain[:seg_slice.stop] strain_chunk.prepend_zeros(-seg_slice.start) freq_seg = make_frequency_series(strain_chunk) elif seg_slice.stop > len(self.strain): strain_chunk = self.strain[seg_slice.start:] strain_chunk.append_zeros(seg_slice.stop - len(self.strain)) freq_seg = make_frequency_series(strain_chunk) freq_seg.analyze = ana freq_seg.cumulative_index = seg_slice.start + ana.start freq_seg.seg_slice = seg_slice self._fourier_segments.append(freq_seg) return self._fourier_segments
def inner(vec1, vec2, psd=None, low_frequency_cutoff=None, high_frequency_cutoff=None, v1_norm=None, v2_norm=None): htilde = pf.make_frequency_series(vec1) stilde = pf.make_frequency_series(vec2) N = (len(htilde) - 1) * 2 global _snr _snr = None if _snr is None or _snr.dtype != htilde.dtype or len(_snr) != N: _snr = pt.zeros(N, dtype=pt.complex_same_precision_as(vec1)) snr, corr, snr_norm = pf.matched_filter_core(htilde, stilde, psd, low_frequency_cutoff, high_frequency_cutoff, v1_norm, out=_snr) if v2_norm is None: v2_norm = pf.sigmasq(stilde, psd, low_frequency_cutoff, high_frequency_cutoff) snr.data = snr.data * snr_norm / np.sqrt(v2_norm) return snr
def test_distance_scaling(self): #""" Check that the waveform is consistent under distance changes #""" distance = 1e6 tolerance = 1e-5 fac = 10 hpc, hcc = get_waveform(self.p, distance=distance) hpm, hcm = get_waveform(self.p, distance=distance * fac) hpf, hcf = get_waveform(self.p, distance=distance * fac * fac) hpn, hcn = get_waveform(self.p, distance=distance / fac) f = pylab.figure() pylab.axes([.1, .2, 0.8, 0.70]) htilde = make_frequency_series(hpc) pylab.loglog(htilde.sample_frequencies, abs(htilde), label="D") htilde = make_frequency_series(hpm) pylab.loglog(htilde.sample_frequencies, abs(htilde), label="D * %s" % fac) htilde = make_frequency_series(hpf) pylab.loglog(htilde.sample_frequencies, abs(htilde), label="D * %s" % (fac * fac)) htilde = make_frequency_series(hpn) pylab.loglog(htilde.sample_frequencies, abs(htilde), label="D / %s" % fac) pylab.title("Vary %s distance, $\\tilde{h}$+" % self.p.approximant) pylab.xlabel("GW Frequency (Hz)") pylab.ylabel("GW Strain") pylab.legend() pylab.xlim(xmin=self.p.f_lower) info = self.version_txt pylab.figtext(0.05, .05, info) if self.save_plots: pname = self.plot_dir + "/%s-distance-scaling.png" % self.p.approximant pylab.savefig(pname) if self.show_plots: pylab.show() else: pylab.close(f) self.assertTrue( hpc.almost_equal_elem(hpm * fac, tolerance, relative=True)) self.assertTrue( hpc.almost_equal_elem(hpf * fac * fac, tolerance, relative=True)) self.assertTrue( hpc.almost_equal_elem(hpn / fac, tolerance, relative=True))
def power_chisq(template, data, num_bins, psd, low_frequency_cutoff=None, high_frequency_cutoff=None, return_bins=False): """Calculate the chisq timeseries Parameters ---------- template: FrequencySeries or TimeSeries A time or frequency series that contains the filter template. data: FrequencySeries or TimeSeries A time or frequency series that contains the data to filter. The length must be commensurate with the template. (EXPLAINME - does this mean 'the same as' or something else?) num_bins: int The number of frequency bins used for chisq. The number of statistical degrees of freedom ('dof') is 2*num_bins-2. psd: FrequencySeries The psd of the data. low_frequency_cutoff: {None, float}, optional The low frequency cutoff for the filter high_frequency_cutoff: {None, float}, optional The high frequency cutoff for the filter return_bins: {boolean, False}, optional Return a list of the individual chisq bins Returns ------- chisq: TimeSeries TimeSeries containing the chisq values for all times. """ htilde = make_frequency_series(template) stilde = make_frequency_series(data) bins = power_chisq_bins(htilde, num_bins, psd, low_frequency_cutoff, high_frequency_cutoff) corra = zeros((len(htilde) - 1) * 2, dtype=htilde.dtype) total_snr, corr, tnorm = matched_filter_core(htilde, stilde, psd, low_frequency_cutoff, high_frequency_cutoff, corr_out=corra) return power_chisq_from_precomputed(corr, total_snr, tnorm, bins, return_bins=return_bins)
def test_distance_scaling(self): #""" Check that the waveform is consistent under distance changes #""" distance = 1e6 tolerance = 1e-5 fac = 10 hpc, hcc = get_waveform(self.p, distance=distance) hpm, hcm = get_waveform(self.p, distance=distance*fac) hpf, hcf = get_waveform(self.p, distance=distance*fac*fac) hpn, hcn = get_waveform(self.p, distance=distance/fac) f = pylab.figure() pylab.axes([.1, .2, 0.8, 0.70]) htilde = make_frequency_series(hpc) pylab.loglog(htilde.sample_frequencies, abs(htilde), label="D") htilde = make_frequency_series(hpm) pylab.loglog(htilde.sample_frequencies, abs(htilde), label="D * %s" %fac) htilde = make_frequency_series(hpf) pylab.loglog(htilde.sample_frequencies, abs(htilde), label="D * %s" %(fac*fac)) htilde = make_frequency_series(hpn) pylab.loglog(htilde.sample_frequencies, abs(htilde), label="D / %s" %fac) pylab.title("Vary %s distance, $\\tilde{h}$+" % self.p.approximant) pylab.xlabel("GW Frequency (Hz)") pylab.ylabel("GW Strain") pylab.legend() pylab.xlim(xmin=self.p.f_lower) info = self.version_txt pylab.figtext(0.05, .05, info) if self.save_plots: pname = self.plot_dir + "/%s-distance-scaling.png" % self.p.approximant pylab.savefig(pname) if self.show_plots: pylab.show() else: pylab.close(f) self.assertTrue(hpc.almost_equal_elem(hpm * fac, tolerance, relative=True)) self.assertTrue(hpc.almost_equal_elem(hpf * fac * fac, tolerance, relative=True)) self.assertTrue(hpc.almost_equal_elem(hpn / fac, tolerance, relative=True))
def power_chisq(template, data, num_bins, psd, low_frequency_cutoff=None, high_frequency_cutoff=None, return_bins=False): """Calculate the chisq timeseries Parameters ---------- template: FrequencySeries or TimeSeries A time or frequency series that contains the filter template. data: FrequencySeries or TimeSeries A time or frequency series that contains the data to filter. The length must be commensurate with the template. (EXPLAINME - does this mean 'the same as' or something else?) num_bins: int The number of bins in the chisq. Note that the dof goes as 2*num_bins-2. psd: FrequencySeries The psd of the data. low_frequency_cutoff: {None, float}, optional The low frequency cutoff for the filter high_frequency_cutoff: {None, float}, optional The high frequency cutoff for the filter return_bins: {boolean, False}, optional Return a list of the individual chisq bins Returns ------- chisq: TimeSeries TimeSeries containing the chisq values for all times. """ htilde = make_frequency_series(template) stilde = make_frequency_series(data) bins = power_chisq_bins(htilde, num_bins, psd, low_frequency_cutoff, high_frequency_cutoff) corra = zeros((len(htilde)-1)*2, dtype=htilde.dtype) total_snr, corr, tnorm = matched_filter_core(htilde, stilde, psd, low_frequency_cutoff, high_frequency_cutoff, corr_out=corra) return power_chisq_from_precomputed(corr, total_snr, tnorm, bins, return_bins=return_bins)
def fourier_segments(self): """ Return a list of the FFT'd segments. Return the list of FrequencySeries. Additional properties are added that describe the strain segment. The property 'analyze' is a slice corresponding to the portion of the time domain equivelant of the segment to analyze for triggers. The value 'cumulative_index' indexes from the beginning of the original strain series. """ if not self._fourier_segments: self._fourier_segments = [] for seg_slice, ana in zip(self.segment_slices, self.analyze_slices): freq_seg = make_frequency_series(self.strain[seg_slice]) freq_seg.analyze = ana freq_seg.cumulative_index = seg_slice.start + ana.start self._fourier_segments.append(freq_seg) return self._fourier_segments
def fourier_segments(self): """ Return a list of the FFT'd segments. Return the list of FrequencySeries. Additional properties are added that describe the strain segment. The property 'analyze' is a slice corresponding to the portion of the time domain equivelant of the segment to analyze for triggers. The value 'cumulative_index' indexes from the beginning of the original strain series. """ if not self._fourier_segments: self._fourier_segments = [] for seg_slice, ana in zip(self.segment_slices, self.analyze_slices): freq_seg = make_frequency_series(self.strain[seg_slice]) freq_seg.analyze = ana freq_seg.cumulative_index = seg_slice.start + ana.start freq_seg.seg_slice = seg_slice self._fourier_segments.append(freq_seg) return self._fourier_segments
def get_waveform(approximant, phase_order, amplitude_order, spin_order, template_params, start_frequency, sample_rate, length, datafile=None, verbose=False): #{{{ print("IN hERE") delta_t = 1. / sample_rate delta_f = 1. / length filter_N = int(length) filter_n = filter_N / 2 + 1 if approximant in fd_approximants() and 'Eccentric' not in approximant: print("NORMAL FD WAVEFORM for", approximant) delta_f = sample_rate / length hplus, hcross = get_fd_waveform(template_params, approximant=approximant, spin_order=spin_order, phase_order=phase_order, delta_f=delta_f, f_lower=start_frequency, amplitude_order=amplitude_order) elif approximant in td_approximants() and 'Eccentric' not in approximant: print("NORMAL TD WAVEFORM for", approximant) hplus, hcross = get_td_waveform(template_params, approximant=approximant, spin_order=spin_order, phase_order=phase_order, delta_t=1.0 / sample_rate, f_lower=start_frequency, amplitude_order=amplitude_order) elif 'EccentricIMR' in approximant: #{{{ # Legacy support import sys sys.path.append('/home/kuma/grav/kuma/src/Eccentric_IMR/Codes/Python/') import EccentricIMR as Ecc try: mass1 = getattr(template_params, 'mass1') mass2 = getattr(template_params, 'mass2') except: raise RuntimeError("template_params does not have mass1 or mass2!") try: ecc = getattr(template_params, 'alpha1') if 'E0' in approximant: ecc = 0 anom = getattr(template_params, 'alpha2') inc = getattr(template_params, 'inclination') rtrans = getattr(template_params, 'alpha') beta = 0 except: raise RuntimeError(\ "template_params does not have alpha{,1,2} or inclination") tol = 1.e-16 fmin = start_frequency sample_rate = sample_rate # print(" Using phase order: %d" % phase_order, file=sys.stdout) sys.stdout.flush() hplus, hcross = Ecc.generate_eccentric_waveform(mass1, mass2,\ ecc, anom, inc, beta,\ tol,\ r_transition=rtrans,\ phase_order=phase_order,\ fmin=fmin,\ sample_rate=sample_rate,\ inspiral_only=False) #}}} elif 'EccentricInspiral' in approximant: #{{{ # Legacy support import sys sys.path.append('/home/kuma/grav/kuma/src/Eccentric_IMR/Codes/Python/') import EccentricIMR as Ecc try: mass1 = getattr(template_params, 'mass1') mass2 = getattr(template_params, 'mass2') except: raise RuntimeError("template_params does not have mass1 or mass2!") try: ecc = getattr(template_params, 'alpha1') if 'E0' in approximant: ecc = 0 anom = getattr(template_params, 'alpha2') inc = getattr(template_params, 'inclination') beta = getattr(template_params, 'alpha') except: raise RuntimeError(\ "template_params does not have alpha{,1,2} or inclination") tol = 1.e-16 fmin = start_frequency sample_rate = sample_rate # hplus, hcross = Ecc.generate_eccentric_waveform(mass1, mass2,\ ecc, anom, inc, beta,\ tol,\ phase_order=phase_order,\ fmin=fmin,\ sample_rate=sample_rate,\ inspiral_only=True) #}}} elif 'EccentricFD' in approximant: #{{{ # Legacy support import lalsimulation as ls import lal delta_f = sample_rate / length try: mass1 = getattr(template_params, 'mass1') mass2 = getattr(template_params, 'mass2') except: raise RuntimeError("template_params does not have mass1 or mass2!") try: ecc = getattr(template_params, 'alpha1') if 'E0' in approximant: ecc = 0 anom = getattr(template_params, 'alpha2') inc = getattr(template_params, 'inclination') except: raise RuntimeError(\ "template_params does not have alpha{1,2} or inclination") eccPar = ls.SimInspiralCreateTestGRParam("inclination_azimuth", inc) ls.SimInspiralAddTestGRParam(eccPar, "e_min", ecc) fmin = start_frequency fmax = sample_rate / 2 # thp, thc = ls.SimInspiralChooseFDWaveform(0, delta_f,\ mass1*lal.MSUN_SI, mass2*lal.MSUN_SI,\ 0,0,0,0,0,0,\ fmin, fmax, 0, 1.e6 * lal.PC_SI,\ inc, 0, 0, None, eccPar, -1, 7, ls.EccentricFD) hplus = FrequencySeries(thp.data.data[:], delta_f=thp.deltaF, epoch=thp.epoch) hcross = FrequencySeries(thc.data.data[:], delta_f=thc.deltaF, epoch=thc.epoch) #}}} elif 'FromDataFile' in approximant: #{{{ # Legacy support if not os.path.exists(datafile): raise IOError("File %s not found!" % datafile) if verbose: print("Reading from data file %s" % datafile) # Figure out waveform parameters from filename #q_value, M_value, w_value, _, _ = EA.get_q_m_e_pn_o_from_filename(datafile) q_value, M_value, w_value = EA.get_q_m_e_from_filename(datafile) # Read data, down-sample (assume data file is more finely sampled than # needed, i.e. interpolation is NOT supported, nor will be) data = np.loadtxt(datafile) dt = data[1, 0] - data[0, 0] delta_t = 1. / sample_rate downsample_ratio = delta_t / dt if not approx_equal(downsample_ratio, np.int(downsample_ratio)): raise RuntimeError( "Cannot handling resampling at a fractional factor = %e" % downsample_ratio) elif verbose: print("Downsampling by a factor of %d" % int(downsample_ratio)) h_real = TimeSeries(data[::int(downsample_ratio), 1] / DYN_RANGE_FAC, delta_t=delta_t) h_imag = TimeSeries(data[::int(downsample_ratio), 2] / DYN_RANGE_FAC, delta_t=delta_t) if verbose: print("max, min,len of h_real = ", max(h_real.data), min(h_real.data), len(h_real.data)) # Compute Strain tmplt_pars = template_params wav = generate_detector_strain(tmplt_pars, h_real, h_imag) wav = extend_waveform_TimeSeries(wav, filter_N) # Return TimeSeries with (m1, m2, w_value) m1, m2 = mtotal_eta_to_mass1_mass2(M_value, q_value / (1. + q_value)**2) htilde = make_frequency_series(wav) htilde = extend_waveform_FrequencySeries(htilde, filter_n) if verbose: print("ISNAN(htilde from file) = ", np.any(np.isnan(htilde.data))) return htilde, [m1, m2, w_value, dt] #}}} else: raise IOError(".. APPROXIMANT %s not found.." % approximant) ## hvec = hplus htilde = make_frequency_series(hvec) htilde = extend_waveform_FrequencySeries(htilde, filter_n) # print("type of hplus, hcross = ", type(hplus.data), type(hcross.data)) if any(isnan(hplus.data)) or any(isnan(hcross.data)): print("..### %s hplus or hcross have NANS!!" % approximant) # if any(isinf(hplus.data)) or any(isinf(hcross.data)): print("..### %s hplus or hcross have INFS!!" % approximant) # if any(isnan(htilde.data)): print("..### %s Fourier transform htilde has NANS!!" % approximant) # if any(isinf(htilde.data)): print("..### %s Fourier transform htilde has INFS!!" % approximant) # return htilde
def calculate_faithfulness(m1, m2, s1x=0, s1y=0, s1z=0, s2x=0, s2y=0, s2z=0, tc=0, phic=0, ra=0, dec=0, polarization=0, signal_approx='IMRPhenomD', signal_file=None, tmplt_approx='IMRPhenomC', tmplt_file=None, aligned_spin_tmplt_only=True, non_spin_tmplt_only=False, f_lower=15.0, sample_rate=4096, signal_duration=256, psd_string='aLIGOZeroDetHighPower', verbose=True, debug=False): """ Calculates the match for a signal of given physical parameters, as modelled by a given signal approximant, against templates of another approximant. This function allows turning off x,y components of spin for templates. IN PROGRESS: Adding facility to use "FromDataFile" waveforms """ # {{{ # 0) OPTION CHECKING if aligned_spin_tmplt_only: print( "WARNING: Spin components parallel to L allowed, others set to 0 in templates.") # 1) GENERATE FILTERING META-PARAMETERS filter_N = signal_duration * sample_rate filter_n = filter_N / 2 + 1 delta_t = 1./sample_rate delta_f = 1./signal_duration # LIGO Noise PSD psd = from_string(psd_string, filter_n, delta_f, f_lower) # 2) GENERATE THE TARGET SIGNAL # Get the signal waveform first if signal_approx in pywf.fd_approximants(): generator = pywfg.FDomainDetFrameGenerator(pywfg.FDomainCBCGenerator, 0, variable_args=['mass1', 'mass2', 'spin1x', 'spin1y', 'spin1z', 'spin2x', 'spin2y', 'spin2z', 'coa_phase', 'tc', 'ra', 'dec', 'polarization'], detectors=['H1'], delta_f=delta_f, f_lower=f_lower, approximant=signal_approx) elif signal_approx in pywf.td_approximants(): generator = pywfg.TDomainDetFrameGenerator(pywfg.TDomainCBCGenerator, 0, variable_args=['mass1', 'mass2', 'spin1x', 'spin1y', 'spin1z', 'spin2x', 'spin2y', 'spin2z', 'coa_phase', 'tc', 'ra', 'dec', 'polarization'], detectors=['H1'], delta_t=delta_t, f_lower=f_lower, approximant=signal_approx) elif 'FromDataFile' in signal_approx: if os.path.getsize(signal_file) == 0: raise RuntimeError( " ERROR:...OOPS. Waveform file %s empty!!" % signal_file) try: _ = np.loadtxt(signal_file) except: raise RuntimeError( " WARNING: FAILURE READING DATA FROM %s.." % signal_file) waveform_params = lsctables.SimInspiral() waveform_params.latitude = 0 waveform_params.longitude = 0 waveform_params.polarization = 0 waveform_params.spin1x = 0 waveform_params.spin1y = 0 waveform_params.spin1z = 0 waveform_params.spin2x = 0 waveform_params.spin2y = 0 waveform_params.spin2z = 0 # try: if True: if verbose: print(".. generating signal waveform ") signal_htilde, _params = get_waveform(signal_approx, -1, -1, -1, waveform_params, f_lower, sample_rate, filter_N, datafile=signal_file) print(".. generated signal waveform ") m1, m2, w_value, _ = _params waveform_params.mass1 = m1 waveform_params.mass2 = m2 signal_h = make_frequency_series(signal_htilde) signal_h = extend_waveform_FrequencySeries(signal_h, filter_n) # except: raise IOError("Approximant %s not found.." % signal_approx) else: raise IOError("Signal Approximant %s not found.." % signal_approx) if verbose: print("..Generating signal with masses = %3f, %.3f, spin1 = (%.3f, %.3f, %.3f), and spin2 = (%.3f, %.3f, %.3f)" % (m1, m2, s1x, s1y, s1z, s2x, s2y, s2z)) sys.stdout.flush() if signal_approx in pywf.fd_approximants(): signal = generator.generate_from_args(m1, m2, s1x, s1y, s1z, s2x, s2y, s2z, phic, tc, ra, dec, polarization) # NOTE: SEOBNRv4 has extra high frequency content, it seems.. if 'SEOBNRv4_ROM' in signal_approx or 'SEOBNRv2_ROM' in signal_approx: signal_h = extend_waveform_FrequencySeries( signal['H1'], filter_n, force_fit=True) else: signal_h = extend_waveform_FrequencySeries(signal['H1'], filter_n) elif signal_approx in pywf.td_approximants(): signal = generator.generate_from_args(m1, m2, s1x, s1y, s1z, s2x, s2y, s2z, phic, tc, ra, dec, polarization) signal_h = make_frequency_series(signal['H1']) signal_h = extend_waveform_FrequencySeries(signal_h, filter_n) elif 'FromDataFile' in signal_approx: pass else: raise IOError("Signal Approximant %s not found.." % signal_approx) # 3) GENERATE THE TARGET TEMPLATE # Get the signal waveform first if tmplt_approx in pywf.fd_approximants(): generator = pywfg.FDomainDetFrameGenerator(pywfg.FDomainCBCGenerator, 0, variable_args=['mass1', 'mass2', 'spin1x', 'spin1y', 'spin1z', 'spin2x', 'spin2y', 'spin2z', 'coa_phase', 'tc', 'ra', 'dec', 'polarization'], detectors=['H1'], delta_f=delta_f, f_lower=f_lower, approximant=tmplt_approx) elif tmplt_approx in pywf.td_approximants(): generator = pywfg.TDomainDetFrameGenerator(pywfg.TDomainCBCGenerator, 0, variable_args=['mass1', 'mass2', 'spin1x', 'spin1y', 'spin1z', 'spin2x', 'spin2y', 'spin2z', 'coa_phase', 'tc', 'ra', 'dec', 'polarization'], detectors=['H1'], delta_t=delta_t, f_lower=f_lower, approximant=tmplt_approx) elif 'FromDataFile' in tmplt_approx: if os.path.getsize(tmplt_file) == 0: raise RuntimeError( " ERROR:...OOPS. Waveform file %s empty!!" % tmplt_file) try: _ = np.loadtxt(tmplt_file) except: raise RuntimeError( " WARNING: FAILURE READING DATA FROM %s.." % tmplt_file) waveform_params = lsctables.SimInspiral() waveform_params.latitude = 0 waveform_params.longitude = 0 waveform_params.polarization = 0 waveform_params.spin1x = 0 waveform_params.spin1y = 0 waveform_params.spin1z = 0 waveform_params.spin2x = 0 waveform_params.spin2y = 0 waveform_params.spin2z = 0 # try: if True: if verbose: print(".. generating signal waveform ") tmplt_htilde, _params = get_waveform(tmplt_approx, -1, -1, -1, waveform_params, f_lower, 1./delta_t, filter_N, datafile=tmplt_file) print(".. generated signal waveform ") m1, m2, w_value, _ = _params waveform_params.mass1 = m1 waveform_params.mass2 = m2 tmplt_h = make_frequency_series(tmplt_htilde) tmplt_h = extend_waveform_FrequencySeries(tmplt_h, filter_n) # except: raise IOError("Approximant %s not found.." % tmplt_approx) else: raise IOError("Template Approximant %s not found.." % tmplt_approx) # if aligned_spin_tmplt_only: _m1, _m2, _s1x, _s1y, _s1z, _s2x, _s2y, _s2z = m1, m2, 0, 0, s1z, 0, 0, s2z elif non_spin_tmplt_only: _m1, _m2, _s1x, _s1y, _s1z, _s2x, _s2y, _s2z = m1, m2, 0, 0, 0, 0, 0, 0 else: _m1, _m2, _s1x, _s1y, _s1z, _s2x, _s2y, _s2z = m1, m2, s1x, s1y, s1z, s2x, s2y, s2z # # template = generator.generate_from_args(_m1, _m2, _s1x, _s1y, _s1z, _s2x, _s2y, _s2z,\ # phic, tc, ra, dec, polarization) # if verbose: print( "..Generating template with masses = %3f, %.3f, spin1 = (%.3f, %.3f, %.3f), and spin2 = (%.3f, %.3f, %.3f)" % (_m1, _m2, _s1x, _s1y, _s1z, _s2x, _s2y, _s2z)) sys.stdout.flush() if tmplt_approx in pywf.fd_approximants(): try: template = generator.generate_from_args(_m1, _m2, _s1x, _s1y, _s1z, _s2x, _s2y, _s2z, phic, tc, ra, dec, polarization) except RuntimeError as rerr: print("""FAILED TO GENERATE %s waveform for masses = %.3f, %.3f spins = (%.3f, %.3f, %.3f), (%.3f, %.3f, %.3f) phic, tc, ra, dec, pol = (%.3f, %.3f, %.3f, %.3f, %.3f)""" % (tmplt_approx, _m1, _m2, _s1x, _s1y, _s1z, _s2x, _s2y, _s2z, phic, tc, ra, dec, polarization)) raise RuntimeError(rerr) # NOTE: SEOBNRv4 has extra high frequency content, it seems.. if 'SEOBNRv4_ROM' in tmplt_approx or 'SEOBNRv2_ROM' in tmplt_approx: template_h = extend_waveform_FrequencySeries( template['H1'], filter_n, force_fit=True) else: template_h = extend_waveform_FrequencySeries( template['H1'], filter_n) elif tmplt_approx in pywf.td_approximants(): try: template = generator.generate_from_args(_m1, _m2, _s1x, _s1y, _s1z, _s2x, _s2y, _s2z, phic, tc, ra, dec, polarization) except RuntimeError as rerr: print("""FAILED TO GENERATE %s waveform for masses = %.3f, %.3f spins = (%.3f, %.3f, %.3f), (%.3f, %.3f, %.3f) phic, tc, ra, dec, pol = (%.3f, %.3f, %.3f, %.3f, %.3f)""" % (tmplt_approx, _m1, _m2, _s1x, _s1y, _s1z, _s2x, _s2y, _s2z, phic, tc, ra, dec, polarization)) raise RuntimeError(rerr) template_h = make_frequency_series(template['H1']) template_h = extend_waveform_FrequencySeries(template_h, filter_n) elif 'FromDataFile' in tmplt_approx: pass else: raise IOError("Template Approximant %s not found.." % tmplt_approx) # 4) COMPUTE MATCH m, idx = match(signal_h, template_h, psd=psd, low_frequency_cutoff=f_lower) if debug: print( "MATCH IS %.6f for parameters" % m, m1, m2, _s1x, _s1y, _s1z, _s2x, _s2y, _s2z) sys.stderr.flush() # # 5) RETURN OPTIMIZED MATCH return m, idx
def calculate_fitting_factor(m1, m2, s1x=0, s1y=0, s1z=0, s2x=0, s2y=0, s2z=0, tc=0, phic=0, ra=0, dec=0, polarization=0, signal_approx='IMRPhenomD', signal_file=None, tmplt_approx='IMRPhenomC', vary_masses_only=True, vary_masses_and_aligned_spin_only=False, chirp_mass_window=0.1, effective_spin_window=0.5, num_retries=4, f_lower=15.0, sample_rate=4096, signal_duration=256, psd_string='aLIGOZeroDetHighPower', pso_swarm_size=500, pso_omega=0.5, pso_phip=0.5, pso_phig=0.25, pso_minfunc=1e-8, verbose=True, debug=False): """ Calculates the fitting factor for a signal of given physical parameters, as modelled by a given signal approximant, against templates of another approximant. This function uses a particle swarm optimization to maximize the overlaps between signal and templates. Algorithm parameters are tunable, depending on how many dimensions we are optimizing over. IN PROGRESS: Adding facility to use "FromDataFile" waveforms """ # {{{ # 0) OPTION CHECKING if vary_masses_only: print("WARNING: Only component masses are allowed to be varied in templates. Setting the rest to signal values.") if vary_masses_and_aligned_spin_only: print("WARNING: Only component masses and spin components parallel to L allowed to be varied in templates. Setting the rest to signal values.") if vary_masses_only and vary_masses_and_aligned_spin_only: raise IOError( "Inconsistent options: vary_masses_only and vary_masses_and_aligned_spin_only") if (not vary_masses_only) and (not vary_masses_and_aligned_spin_only): print("WARNING: All mass and spin components varied in templates. Sky parameters still fixed to signal values.") # 1) GENERATE FILTERING META-PARAMETERS signal_duration = int(signal_duration) sample_rate = int(sample_rate) filter_N = signal_duration * sample_rate filter_n = filter_N / 2 + 1 delta_t = 1./sample_rate delta_f = 1./signal_duration if verbose: print("signal_duration = %d, sample_rate = %d, filter_N = %d, filter_n = %d" % ( signal_duration, sample_rate, filter_N, filter_n)) print("deltaT = %f, deltaF = %f" % (delta_t, delta_f)) # LIGO Noise PSD psd = from_string(psd_string, filter_n, delta_f, f_lower) # 2) GENERATE THE TARGET SIGNAL # PREPARATORY: Get the signal generator if signal_approx in pywf.fd_approximants(): generator = pywfg.FDomainDetFrameGenerator(pywfg.FDomainCBCGenerator, 0, variable_args=['mass1', 'mass2', 'spin1x', 'spin1y', 'spin1z', 'spin2x', 'spin2y', 'spin2z', 'coa_phase', 'tc', 'ra', 'dec', 'polarization'], detectors=['H1'], delta_f=delta_f, f_lower=f_lower, approximant=signal_approx) elif signal_approx in pywf.td_approximants(): generator = pywfg.TDomainDetFrameGenerator(pywfg.TDomainCBCGenerator, 0, variable_args=['mass1', 'mass2', 'spin1x', 'spin1y', 'spin1z', 'spin2x', 'spin2y', 'spin2z', 'coa_phase', 'tc', 'ra', 'dec', 'polarization'], detectors=['H1'], delta_t=delta_t, f_lower=f_lower, approximant=signal_approx) elif 'FromDataFile' in signal_approx: if os.path.getsize(signal_file) == 0: raise RuntimeError( " ERROR:...OOPS. Waveform file %s empty!!" % signal_file) try: _ = np.loadtxt(signal_file) except: raise RuntimeError( " WARNING: FAILURE READING DATA FROM %s.." % signal_file) waveform_params = lsctables.SimInspiral() waveform_params.latitude = 0 waveform_params.longitude = 0 waveform_params.polarization = 0 waveform_params.spin1x = 0 waveform_params.spin1y = 0 waveform_params.spin1z = 0 waveform_params.spin2x = 0 waveform_params.spin2y = 0 waveform_params.spin2z = 0 # try: if True: if verbose: print(".. generating signal waveform ") signal_htilde, _params = get_waveform(signal_approx, -1, -1, -1, waveform_params, f_lower, sample_rate, filter_N, datafile=signal_file) print(".. generated signal waveform ") m1, m2, w_value, _ = _params waveform_params.mass1 = m1 waveform_params.mass2 = m2 signal_h = make_frequency_series(signal_htilde) signal_h = extend_waveform_FrequencySeries(signal_h, filter_n) # except: raise IOError("Approximant %s not found.." % signal_approx) else: raise IOError("Approximant %s not found.." % signal_approx) if verbose: print( "\nGenerating signal with masses = %3f, %.3f, spin1 = (%.3f, %.3f, %.3f), and spin2 = (%.3f, %.3f, %.3f)" % (m1, m2, s1x, s1y, s1z, s2x, s2y, s2z)) sys.stdout.flush() # Actually GENERATE THE SIGNAL if signal_approx in pywf.fd_approximants(): signal = generator.generate_from_args(m1, m2, s1x, s1y, s1z, s2x, s2y, s2z, phic, tc, ra, dec, polarization) signal_h = extend_waveform_FrequencySeries(signal['H1'], filter_n) elif signal_approx in pywf.td_approximants(): signal = generator.generate_from_args(m1, m2, s1x, s1y, s1z, s2x, s2y, s2z, phic, tc, ra, dec, polarization) signal_h = make_frequency_series(signal['H1']) signal_h = extend_waveform_FrequencySeries(signal_h, filter_n) elif 'FromDataFile' in signal_approx: pass else: raise IOError("Approximant %s not found.." % signal_approx) ### # NOW : Set up PSO calculation of the optimal overlap parameter set, i.e. \theta(FF) ### # 3) INITIALIZE THE WAVEFORM GENERATOR FOR TEMPLATES # We allow all intrinsic parameters to vary, and fix them to the signal # values, in case only masses or only mass+aligned-spin components are # requested to be varied. This fixing is done inside the objective function. if tmplt_approx in pywf.fd_approximants(): generator_tmplt = pywfg.FDomainDetFrameGenerator(pywfg.FDomainCBCGenerator, 0, variable_args=['mass1', 'mass2', 'spin1x', 'spin1y', 'spin1z', 'spin2x', 'spin2y', 'spin2z' ], detectors=['H1'], coa_phase=phic, tc=tc, ra=ra, dec=dec, polarization=polarization, delta_f=delta_f, f_lower=f_lower, approximant=tmplt_approx) elif tmplt_approx in pywf.td_approximants(): raise IOError( "Time-domain templates not supported yet (TDomainDetFrameGenerator doesn't exist)") generator_tmplt = pywfg.TDomainDetFrameGenerator(pywfg.TDomainCBCGenerator, 0, variable_args=['mass1', 'mass2', 'spin1x', 'spin1y', 'spin1z', 'spin2x', 'spin2y', 'spin2z' ], detectors=['H1'], coa_phase=phic, tc=tc, ra=ra, dec=dec, polarization=polarization, delta_t=delta_t, f_lower=f_lower, approximant=tmplt_approx) elif 'FromDataFile' in tmplt_approx: raise RuntimeError( "Using **templates** from data files is not implemented yet") else: raise IOError("Approximant %s not found.." % tmplt_approx) # 4) DEFINE AN OBJECTIVE FUNCTION FOR PSO TO MINIMIZE def objective_function_fitting_factor(x, *args): """ This function is to be minimized if the fitting factor is to be found """ objective_function_fitting_factor.counter += 1 # 1) OBTAIN THE TEMPLATE PARAMETERS FROM X. ASSUME THAT ONLY # THOSE ARE PASSED THAT ARE NEEDED BY THE GENERATOR if len(x) == 2: m1, m2 = x if vary_masses_only: _s1x = _s1y = _s1z = _s2x = _s2y = _s2z = 0 else: _s1x, _s1y, _s1z = s1x, s1y, s1z _s2x, _s2y, _s2z = s2x, s2y, s2z elif len(x) == 4: m1, m2, _s1z, _s2z = x if vary_masses_and_aligned_spin_only: _s1x = _s1y = _s2x = _s2y = 0 else: _s1x, _s1y = s1x, s1y _s2x, _s2y = s2x, s2y elif len(x) == 8: m1, m2, _s1x, _s1y, _s1z, _s2x, _s2y, _s2z = x else: raise IOError( "No of vars %d not supported (should be 2 or 4 or 8)" % len(x)) # 2) CHECK FOR CONSISTENCY if (_s1x**2 + _s1y**2 + _s1z**2) > s_max or (_s2x**2 + _s2y**2 + _s2z**2) > s_max: return 1e99 # 2) ASSUME THAT signal_h, tmplt_generator = args tmplt = tmplt_generator.generate_from_args( m1, m2, _s1x, _s1y, _s1z, _s2x, _s2y, _s2z) tmplt_h = make_frequency_series(tmplt['H1']) if debug: print("IN FF Objective-> for parameters:", m1, m2, _s1x, _s1y, _s1z, _s2x, _s2y, _s2z) if debug: print("IN FF Objective-> Length(tmplt) = %d, making it %d" % (len(tmplt['H1']), filter_n)) # NOTE: SEOBNRv4 has extra high frequency content, it seems.. if 'SEOBNRv4_ROM' in tmplt_approx or 'SEOBNRv2_ROM' in tmplt_approx: tmplt_h = extend_waveform_FrequencySeries( tmplt_h, filter_n, force_fit=True) else: tmplt_h = extend_waveform_FrequencySeries(tmplt_h, filter_n) # 3) COMPUTE MATCH m, _ = match(signal_h, tmplt_h, psd=psd, low_frequency_cutoff=f_lower) if debug: print("MATCH IS %.6f for parameters:" % m, m1, m2, _s1x, _s1y, _s1z, _s2x, _s2y, _s2z) retval = np.log10(1. - m) # We do not want PSO to go berserk, so we stop when FF = 0.999999 if retval <= -6.0: retval = -6.0 return retval objective_function_fitting_factor.counter = 0 # 5) DEFINE A CONSTRAINT FUNCTION FOR PSO TO RESPECT def constraint_function_fitting_factor(x, *args): """ This function implements constraints on the optimization of fitting factors: 1) spin magnitudes on both holes should be <= 1 """ if len(x) == 2: m1, m2 = x s1x = s1y = s1z = s2x = s2y = s2z = 0 elif len(x) == 4: m1, m2, s1z, s2z = x s1x = s1y = s2x = s2y = 0 elif len(x) == 8: m1, m2, s1x, s1y, s1z, s2x, s2y, s2z = x # 1) Constraint on spin magnitudes s1_mag = (s1x**2 + s1y**2 + s1z**2)**0.5 s2_mag = (s2x**2 + s2y**2 + s2z**2)**0.5 ## if (s1_mag > s_max) or (s2_mag > s_max): return -1 # 2) Constraint on effective spin s_eff = (s1z * m1 + s2z * m2) / (m1 + m2) ## if (s_eff > s_eff_max) or (s_eff < s_eff_min): return -1 # FINALLY) DEFAULT return 1 # 6) FINALLY, CALL THE PSO TO COMPUTE THE FITTING FACTOR # 6a) FIRST CONSTRUCT THE FIXED ARGUMENTS FOR THE PSO's OBJECTIVE FUNCTION pso_args = (signal_h, generator_tmplt) # 6b) NOW SET THE RANGE OF PARAMETERS TO BE PROBED mt = m1 + m2 * 1.0 et = m1 * m2 / mt / mt mc = mt * et**0.6 mc_min = mc * (1.0 - chirp_mass_window) mc_max = mc * (1.0 + chirp_mass_window) et_max = 0.25 et_min = 10. / 121. # Lets say we trust waveform models up to q = 10 m1_max, _ = pnutils.mchirp_eta_to_mass1_mass2(mc_max, et_min) m1_min, _ = pnutils.mchirp_eta_to_mass1_mass2(mc_min, et_max) _, m2_max = pnutils.mchirp_eta_to_mass1_mass2(mc_max, et_max) _, m2_min = pnutils.mchirp_eta_to_mass1_mass2(mc_min, et_min) s_min = -0.99 s_max = +0.99 s_eff = (s1z * m1 + s2z * m2) / (m1 + m2) s_eff_min = s_eff - effective_spin_window s_eff_max = s_eff + effective_spin_window if verbose: print(m1, m2, mt, et, mc, mc_min, mc_max, et_min, et_max, m1_min, m1_max, m2_min, m2_max) if vary_masses_only: low_lim = [m1_min, m2_min] high_lim = [m1_max, m2_max] elif vary_masses_and_aligned_spin_only: low_lim = [m1_min, m2_min, s_min, s_min] high_lim = [m1_max, m2_max, s_max, s_max] else: low_lim = [m1_min, m2_min, s_min, s_min, s_min, s_min, s_min, s_min] high_lim = [m1_max, m2_max, s_max, s_max, s_max, s_max, s_max, s_max] # if verbose: print("\nSearching within limits:\n", low_lim, " and \n", high_lim) print("\nCalculating overlap now..") sys.stdout.flush() olap, idx = calculate_faithfulness(m1, m2, s1x, s1y, s1z, s2x, s2y, s2z, tc=tc, phic=phic, ra=ra, dec=dec, polarization=polarization, signal_approx=signal_approx, signal_file=signal_file, tmplt_approx=tmplt_approx, tmplt_file=None, aligned_spin_tmplt_only=vary_masses_and_aligned_spin_only, non_spin_tmplt_only=vary_masses_only, f_lower=f_lower, sample_rate=sample_rate, signal_duration=signal_duration, verbose=verbose, debug=debug) # if verbose: print("Overlap with aligned_spin_tmplt_only = ", vary_masses_and_aligned_spin_only, " and non_spin_tmplt_only = ", vary_masses_only, ": ", olap, np.log10( 1. - olap)) sys.stdout.flush() # idx = 1 ff = 0.0 while ff < olap: if idx and idx % 2 == 0: pso_minfunc *= 0.1 pso_phig *= 1.1 if idx > num_retries: print( "WARNING: Failed to improve on overlap in %d iterations. Set ff = olap now" % num_retries) ff = olap break if verbose: print("\nTry %d to compute fitting factor" % idx) sys.stdout.flush() params, ff = pso(objective_function_fitting_factor, low_lim, high_lim, f_ieqcons=constraint_function_fitting_factor, args=pso_args, swarmsize=pso_swarm_size, omega=pso_omega, phip=pso_phip, phig=pso_phig, minfunc=pso_minfunc, maxiter=500, debug=verbose) # Restore fitting factor from 1-ff ff = 1.0 - 10**ff if verbose: print("\nLoop will continue till %.12f < %.12f" % (ff, olap)) sys.stdout.flush() idx += 1 if verbose: print("optimization took %d objective func evals" % objective_function_fitting_factor.counter) sys.stdout.flush() # # 7) RETURN OPTIMIZED PARAMETERS return [params, olap, ff]
def align_waveforms_suboptimally(hplus1, hcross1, hplus2, hcross2, psd='aLIGOZeroDetHighPower', low_frequency_cutoff=None, high_frequency_cutoff=None, tsign=1, phsign=1, verify=True, trim_leading=False, trim_trailing=False, verbose=False): # Cast into time-series h_plus1 = TimeSeries(hplus1, epoch=hplus1._epoch, delta_t=hplus1.delta_t, dtype=hplus1.dtype) h_cross1 = TimeSeries(hcross1, epoch=hplus1._epoch, delta_t=hplus1.delta_t, dtype=hplus1.dtype) h_plus2 = TimeSeries(hplus2, epoch=hplus2._epoch, delta_t=hplus2.delta_t, dtype=hplus2.dtype) h_cross2 = TimeSeries(hcross2, epoch=hplus2._epoch, delta_t=hplus2.delta_t, dtype=hplus2.dtype) # # Ensure both input hplus vectors are equal in length if len(hplus2) > len(hplus1): h_plus1.append_zeros(len(hplus2) - len(hplus1)) h_cross1.append_zeros(len(hplus2) - len(hplus1)) elif len(hplus2) < len(hplus1): h_plus2.append_zeros(len(hplus1) - len(hplus2)) h_cross2.append_zeros(len(hplus1) - len(hplus2)) # htilde = make_frequency_series(h_plus1) stilde = make_frequency_series(h_plus2) # if high_frequency_cutoff == None: high_frequency_cutoff = 1. / h_plus1.delta_t / 2. # if psd == None: raise IOError("Need compatible psd [or name] as input!") elif type(psd) == str: psd_name = psd psd = from_string(psd_name, len(htilde), htilde.delta_f, low_frequency_cutoff) # # Determine the phase and time shifts for optimal match snr, corr, snr_norm = matched_filter_core( htilde, stilde, # h_plus1, h_plus2, psd, low_frequency_cutoff, high_frequency_cutoff, None) max_snr, max_id = snr.abs_max_loc() if max_id != 0: t_shift = snr.delta_t * (len(snr) - max_id) else: t_shift = snr.delta_t * max_id ph_shift = np.angle(snr[max_id]) - 0.24850315030 - 0.0465881735639 # if verbose: print(("max_id = %d, id_shift = %d" % (max_id, int(t_shift / snr.delta_t)))) print(("t_shift = %f,\n ph_shift = %f" % (t_shift, ph_shift))) # # print(OVERLAPS if verbose: print(("Overlap BEFORE ALIGNMENT:", overlap_cplx(h_plus1, h_plus2, psd=psd, low_frequency_cutoff=low_frequency_cutoff, high_frequency_cutoff=high_frequency_cutoff, normalized=True))) print(("Match BEFORE ALIGNMENT:", match(h_plus1, h_plus2, psd=psd, low_frequency_cutoff=low_frequency_cutoff, high_frequency_cutoff=high_frequency_cutoff))) # Shift whichever needs to be shifted to future time. # Shifting back in time is tricky. if t_shift >= 0: hp2, hc2 = shift_waveform_phase_time(h_plus2, h_cross2, tsign * t_shift, phsign * ph_shift, verbose=verbose) else: hp2, hc2 = shift_waveform_phase_time(h_plus2, h_cross2, tsign * t_shift, phsign * ph_shift, verbose=verbose) # # Ensure both input hplus vectors are equal in length if len(h_plus1) > len(hp2): hp2.append_zeros(len(h_plus1) - len(hp2)) elif len(h_plus1) < len(hp2): h_plus1.append_zeros(len(hp2) - len(h_plus1)) if verbose: htilde = make_frequency_series(h_plus1) psd = from_string(psd_name, len(htilde), htilde.delta_f, low_frequency_cutoff) print(("Overlap AFTER ALIGNMENT:", overlap_cplx(h_plus1, hp2, psd=psd, low_frequency_cutoff=low_frequency_cutoff, high_frequency_cutoff=high_frequency_cutoff, normalized=True))) print(("Match AFTER ALIGNMENT:", match(h_plus1, hp2, psd=psd, low_frequency_cutoff=low_frequency_cutoff, high_frequency_cutoff=high_frequency_cutoff))) if verify: # print("Verifying time alignment...") # Determine the phase and time shifts for optimal match snr, corr, snr_norm = matched_filter_core( # htilde, stilde, h_plus1, hp2, psd, low_frequency_cutoff, high_frequency_cutoff, None) max_snr, max_id = snr.abs_max_loc() print(("Post-Alignment Index of MAX SNR (should be 0 or 1 or %d): %d" % (len(snr) - 1, max_id))) print(("Length of whole SNR time-series: ", len(snr))) if max_id != 0 and max_id != 1 and max_id != ( len(snr) - 1) and max_id != (len(snr) - 2): # raise RuntimeError( "Warning: ALIGNMENT NOT CORRECT (see above)" ) print("Warning: ALIGNMENT NOT CORRECT (see above)") else: print("Alignment in time correct..") # print("Verifying phase alignment...") ph_shift = np.angle(snr[max_id]) if ph_shift != 0: print("Warning: Phasing alignment possibly incorrect.") print(("dphi, dphi+pi, dphi-pi: ", ph_shift, ph_shift + np.pi, ph_shift - np.pi)) print(("dphi/pi, dphi*pi: ", ph_shift / np.pi, ph_shift * np.pi)) # # if trim_trailing: hp1 = trim_trailing_zeros(hp1) hc1 = trim_trailing_zeros(hc1) hp2 = trim_trailing_zeros(hp2) hc2 = trim_trailing_zeros(hc2) if trim_leading: hp1 = trim_leading_zeros(hp1) hc1 = trim_leading_zeros(hc1) hp2 = trim_leading_zeros(hp2) hc2 = trim_leading_zeros(hc2) # return hplus1, hcross1, hp2, hc2
def align_waveforms_optimally(hplus1, hcross1, hplus2, hcross2, psd='aLIGOZeroDetHighPower', low_frequency_cutoff=None, high_frequency_cutoff=None, tsign=1, phsign=-1, verify=True, phase_tolerance=1e-3, overlap_tolerance=1e-3, trim_leading=False, trim_trailing=False, verbose=False): """ Align waveforms such that their inner product (noise weighted) is optimal without requiring any phase or time shift. The appropriate time and phase shifts are determined iteratively and applied to the second set of (hplus, hcross) vectors. """ ############################################################################# # First copy over data into local memory, ensure lengths of time and # frequency domain vectors are consistent, and compute the maximized overlap # # 1) Cast into time-series h_plus1 = TimeSeries(hplus1, epoch=hplus1._epoch, delta_t=hplus1.delta_t, dtype=hplus1.dtype, copy=True) h_cross1 = TimeSeries(hcross1, epoch=hplus1._epoch, delta_t=hplus1.delta_t, dtype=hplus1.dtype, copy=True) h_plus2 = TimeSeries(hplus2, epoch=hplus2._epoch, delta_t=hplus2.delta_t, dtype=hplus2.dtype, copy=True) h_cross2 = TimeSeries(hcross2, epoch=hplus2._epoch, delta_t=hplus2.delta_t, dtype=hplus2.dtype, copy=True) # # 2) Ensure both input hplus vectors are equal in length if len(hplus2) > len(hplus1): h_plus1.append_zeros(len(hplus2) - len(hplus1)) h_cross1.append_zeros(len(hplus2) - len(hplus1)) elif len(hplus2) < len(hplus1): h_plus2.append_zeros(len(hplus1) - len(hplus2)) h_cross2.append_zeros(len(hplus1) - len(hplus2)) # # 3) Set the upper frequency cutoff to Nyquist if not set by User if high_frequency_cutoff == None: high_frequency_cutoff = 1. / h_plus1.delta_t / 2. # # 4) Compute LIGO noise psd if psd == None: raise IOError("Need compatible psd [or name] as input!") elif type(psd) == str: htilde = make_frequency_series(h_plus1) psd_name = psd psd = from_string(psd_name, len(htilde), htilde.delta_f, low_frequency_cutoff) ## # 5) Calculate Overlap (maximized) before alignment m = match(h_plus1, h_plus2, psd=psd, low_frequency_cutoff=low_frequency_cutoff, high_frequency_cutoff=high_frequency_cutoff) optimal_overlap = m[0] # FIXME if verbose: print(("Overlap BEFORE ALIGNMENT:", overlap_cplx(h_plus1, h_plus2, psd=psd, low_frequency_cutoff=low_frequency_cutoff, high_frequency_cutoff=high_frequency_cutoff, normalized=True))) print(("Match BEFORE ALIGNMENT:", m)) ############################################################################# # Iterate to obtain the correct phase and time shifts, using which we # align the two waveforms such that their unmaximized and maximized overlaps # agree. # # 1) Initialize phase/time offset counters t_shift_counter = 0 ph_shift_counter = 0 # # 2) Initialize initial garbage values to enter the while loop idx = 0 ph_shift = t_shift = 1e9 olap = 0 + 0j # # 3) Iteration begins # >>>>>> while np.abs(ph_shift) > phase_tolerance or \ np.abs(t_shift) > h_plus1.delta_t or \ np.abs(np.abs(olap.real) - optimal_overlap) > overlap_tolerance: if idx == 0: hp2, hc2 = h_plus2, h_cross2 # # 1) Determine the phase and time shifts for optimal match # by comparing hplus1/hcross1 with hp2/hc2 which is phase/time shifted # in previous iteration snr, corr, snr_norm = matched_filter_core(h_plus1, hp2, psd, low_frequency_cutoff, high_frequency_cutoff, None) max_snr, max_id = snr.abs_max_loc() if max_id != 0: t_shift = snr.delta_t * (len(snr) - max_id) else: t_shift = snr.delta_t * max_id ph_shift = np.angle(snr[max_id]) # # 2) Add them to running time/phase offset counter t_shift_counter += t_shift ph_shift_counter += ph_shift # if verbose: print((" >> Iteration %d\n" % (idx + 1))) print(("max_id = %d, id_shift = %d" % (max_id, int(t_shift / snr.delta_t)))) print(("t_shift = %f,\n ph_shift = %f" % (t_shift, ph_shift))) # #### # 3) Shift the second hp/hc pair (ORIGINAL) by cumulative phase/time offset hp2, hc2 = shift_waveform_phase_time(h_plus2, h_cross2, tsign * t_shift_counter, phsign * ph_shift_counter, verbose=verbose) # ### # 4) As time shifting can change array lengths, equalize again, compute psd ## if len(h_plus1) > len(hp2): hp2.append_zeros(len(h_plus1) - len(hp2)) htilde = make_frequency_series(h_plus1) psd = from_string(psd_name, len(htilde), htilde.delta_f, low_frequency_cutoff) elif len(h_plus1) < len(hp2): h_plus1.append_zeros(len(hp2) - len(h_plus1)) htilde = make_frequency_series(h_plus1) psd = from_string(psd_name, len(htilde), htilde.delta_f, low_frequency_cutoff) # # 5) Compute UNMAXIMIZED overlap. olap = overlap_cplx(h_plus1, hp2, psd=psd, low_frequency_cutoff=low_frequency_cutoff, high_frequency_cutoff=high_frequency_cutoff, normalized=True) if verbose: print(("Overlap AFTER ALIGNMENT = ", olap)) print(("Optimal Overlap = ", optimal_overlap)) # idx += 1 if verbose: print("\n") # >>>>>> # 3) Iteration ended. ############################################################################# # Verify the alignment ### if verify: # print("Verifying time alignment...") # # 1) Determine the phase and time shifts for optimal match snr, corr, snr_norm = matched_filter_core(h_plus1, hp2, psd, low_frequency_cutoff, high_frequency_cutoff, None) max_snr, max_id = snr.abs_max_loc() if verbose: print( ("Post-Alignment Index of MAX SNR (should be 0 or 1 or %d): %d" % (len(snr) - 1, max_id))) print(("Length of whole SNR time-series: ", len(snr))) # # 2) Test if current time shift is within tolerance if max_id != 0 and max_id != 1 and \ max_id != (len(snr)-1) and max_id != (len(snr)-2): raise RuntimeError("Warning: ALIGNMENT NOT CORRECT (see above)") else: print("Alignment in time correct..") # # 3) Test if current phase shift is within tolerance print("Verifying phase alignment...") ph_shift = np.angle(snr[max_id]) if np.abs(ph_shift) > phase_tolerance: if verbose: print(("dphi, dphi+pi, dphi-pi: ", ph_shift, ph_shift + np.pi, ph_shift - np.pi)) print( ("dphi/pi, dphi*pi: ", ph_shift / np.pi, ph_shift * np.pi)) raise RuntimeError( "Warning: Phasing alignment possibly incorrect.") else: if verbose: print(("Post-Alignmend Phase shift (should be < %.2e): %.2e" % (phase_tolerance, np.abs(ph_shift)))) print(("Alignment in phasing correct.. (within tol %.2e)" % phase_tolerance)) # ############################################################################# # TRIM the output arrays and return if trim_trailing: hp2 = trim_trailing_zeros(hp2) hc2 = trim_trailing_zeros(hc2) if trim_leading: hp2 = trim_leading_zeros(hp2) hc2 = trim_leading_zeros(hc2) # return hplus1, hcross1, hp2, hc2
def values(self, sn, indices, template, psd, norm, stilde=None, low_frequency_cutoff=None, high_frequency_cutoff=None): """ Calculate the auto-chisq at the specified indices. Parameters ----------- sn : Array[complex] SNR time series of the template for which auto-chisq is being computed. Provided unnormalized. indices : Array[int] List of points at which to calculate auto-chisq template : Pycbc template object The template for which we are calculating auto-chisq psd : Pycbc psd object The PSD of the data being analysed norm : float The normalization factor to apply to sn stilde : Pycbc data object, needed if using reverse-template The data being analysed. Only needed if using reverse-template, otherwise ignored low_frequency_cutoff : float The lower frequency to consider in matched-filters high_frequency_cutoff : float The upper frequency to consider in matched-filters """ if self.do and (len(indices) > 0): htilde = make_frequency_series(template) # Check if we need to recompute the autocorrelation key = (id(template), id(psd)) if key != self._autocor_id: logging.info("Calculating autocorrelation") if not self.reverse_template: Pt, _Ptilde, P_norm = matched_filter_core(htilde, htilde, psd=psd, low_frequency_cutoff=low_frequency_cutoff, high_frequency_cutoff=high_frequency_cutoff) Pt = Pt * (1./ Pt[0]) self._autocor = Array(Pt, copy=True) else: Pt, _Ptilde, P_norm = matched_filter_core(htilde, htilde.conj(), psd=psd, low_frequency_cutoff=low_frequency_cutoff, high_frequency_cutoff=high_frequency_cutoff) # T-reversed template has same norm as forward template # so we can normalize using that # FIXME: Here sigmasq has to be cast to a float or the # code is really slow ... why?? norm_fac = P_norm / float(((template.sigmasq(psd))**0.5)) Pt *= norm_fac self._autocor = Array(Pt, copy=True) self._autocor_id = key logging.info("...Calculating autochisquare") sn = sn*norm if self.reverse_template: assert(stilde is not None) asn, acor, ahnrm = matched_filter_core(htilde.conj(), stilde, low_frequency_cutoff=low_frequency_cutoff, high_frequency_cutoff=high_frequency_cutoff, h_norm=template.sigmasq(psd)) correlation_snr = asn * ahnrm else: correlation_snr = sn achi_list = np.array([]) index_list = np.array(indices) dof, achi_list, _ = autochisq_from_precomputed(sn, correlation_snr, self._autocor, index_list, stride=self.stride, num_points=self.num_points, oneside=self.one_sided, twophase=self.two_phase, maxvalued=self.take_maximum_value) self.dof = dof return achi_list
def values(self, sn, indices, template, psd, norm, stilde=None, low_frequency_cutoff=None, high_frequency_cutoff=None): """ Calculate the auto-chisq at the specified indices. Parameters ----------- sn : Array[complex] SNR time series of the template for which auto-chisq is being computed. Provided unnormalized. indices : Array[int] List of points at which to calculate auto-chisq template : Pycbc template object The template for which we are calculating auto-chisq psd : Pycbc psd object The PSD of the data being analysed norm : float The normalization factor to apply to sn stilde : Pycbc data object, needed if using reverse-template The data being analysed. Only needed if using reverse-template, otherwise ignored low_frequency_cutoff : float The lower frequency to consider in matched-filters high_frequency_cutoff : float The upper frequency to consider in matched-filters """ if self.do and (len(indices) > 0): htilde = make_frequency_series(template) # Check if we need to recompute the autocorrelation key = (id(template), id(psd)) if key != self._autocor_id: logging.info("Calculating autocorrelation") if not self.reverse_template: Pt, _Ptilde, P_norm = matched_filter_core(htilde, htilde, psd=psd, low_frequency_cutoff=low_frequency_cutoff, high_frequency_cutoff=high_frequency_cutoff) Pt = Pt * (1./ Pt[0]) self._autocor = Array(Pt, copy=True) else: Pt, _Ptilde, P_norm = matched_filter_core(htilde.conj(), htilde, psd=psd, low_frequency_cutoff=low_frequency_cutoff, high_frequency_cutoff=high_frequency_cutoff) # T-reversed template has same norm as forward template # so we can normalize using that # FIXME: Here sigmasq has to be cast to a float or the # code is really slow ... why?? norm_fac = P_norm / float(((template.sigmasq(psd))**0.5)) Pt *= norm_fac self._autocor = Array(Pt, copy=True) self._autocor_id = key logging.info("...Calculating autochisquare") sn = sn*norm if self.reverse_template: assert(stilde is not None) asn, acor, ahnrm = matched_filter_core(htilde.conj(), stilde, low_frequency_cutoff=low_frequency_cutoff, high_frequency_cutoff=high_frequency_cutoff, h_norm=template.sigmasq(psd)) correlation_snr = asn * ahnrm else: correlation_snr = sn achi_list = np.array([]) index_list = np.array(indices) dof, achi_list, _ = autochisq_from_precomputed(sn, correlation_snr, self._autocor, index_list, stride=self.stride, num_points=self.num_points, oneside=self.one_sided, twophase=self.two_phase, maxvalued=self.take_maximum_value) self.dof = dof return achi_list
def objective_function_fitting_factor(x, *args): """ This function is to be minimized if the fitting factor is to be found """ objective_function_fitting_factor.counter += 1 # 1) OBTAIN THE TEMPLATE PARAMETERS FROM X. ASSUME THAT ONLY # THOSE ARE PASSED THAT ARE NEEDED BY THE GENERATOR if len(x) == 2: m1, m2 = x if vary_masses_only: _s1x = _s1y = _s1z = _s2x = _s2y = _s2z = 0 else: _s1x, _s1y, _s1z = s1x, s1y, s1z _s2x, _s2y, _s2z = s2x, s2y, s2z elif len(x) == 4: m1, m2, _s1z, _s2z = x if vary_masses_and_aligned_spin_only: _s1x = _s1y = _s2x = _s2y = 0 else: _s1x, _s1y = s1x, s1y _s2x, _s2y = s2x, s2y elif len(x) == 8: m1, m2, _s1x, _s1y, _s1z, _s2x, _s2y, _s2z = x else: raise IOError( "No of vars %d not supported (should be 2 or 4 or 8)" % len(x)) # 2) CHECK FOR CONSISTENCY if (_s1x**2 + _s1y**2 + _s1z**2) > s_max or (_s2x**2 + _s2y**2 + _s2z**2) > s_max: return 1e99 # 2) ASSUME THAT signal_h, tmplt_generator = args tmplt = tmplt_generator.generate_from_args( m1, m2, _s1x, _s1y, _s1z, _s2x, _s2y, _s2z) tmplt_h = make_frequency_series(tmplt['H1']) if debug: print("IN FF Objective-> for parameters:", m1, m2, _s1x, _s1y, _s1z, _s2x, _s2y, _s2z) if debug: print("IN FF Objective-> Length(tmplt) = %d, making it %d" % (len(tmplt['H1']), filter_n)) # NOTE: SEOBNRv4 has extra high frequency content, it seems.. if 'SEOBNRv4_ROM' in tmplt_approx or 'SEOBNRv2_ROM' in tmplt_approx: tmplt_h = extend_waveform_FrequencySeries( tmplt_h, filter_n, force_fit=True) else: tmplt_h = extend_waveform_FrequencySeries(tmplt_h, filter_n) # 3) COMPUTE MATCH m, _ = match(signal_h, tmplt_h, psd=psd, low_frequency_cutoff=f_lower) if debug: print("MATCH IS %.6f for parameters:" % m, m1, m2, _s1x, _s1y, _s1z, _s2x, _s2y, _s2z) retval = np.log10(1. - m) # We do not want PSO to go berserk, so we stop when FF = 0.999999 if retval <= -6.0: retval = -6.0 return retval
def get_waveform(wav, approximant, f_min, dt, N): """This function will generate the waveform corresponding to the point taken as input""" #{{{ m1 = wav.mass1 m2 = wav.mass2 s1x = wav.spin1x s1y = wav.spin1y s1z = wav.spin1z s2x = wav.spin2x s2y = wav.spin2y s2z = wav.spin2z ecc = wav.alpha mean_per_ano = wav.alpha1 long_asc_nodes = wav.alpha2 coa_phase = wav.coa_phase inc = wav.inclination dist = wav.distance df = 1. / (dt * N) f_max = min(1. / (2. * dt), 0.15 / ((m1 + m2) * lal.MTSUN_SI)) if approximant in fd_approximants(): try: hptild, hctild = get_fd_waveform(approximant=approximant, mass1=m1, mass2=m2, spin1x=s1x, spin1y=s1y, spin1z=s1z, spin2x=s2x, spin2y=s2y, spin2z=s2z, eccentricity=ecc, mean_per_ano=mean_per_ano, long_asc_nodes=long_asc_nodes, coa_phase=coa_phase, inclination=inc, distance=dist, f_lower=f_min, f_final=f_max, delta_f=df) except RuntimeError as re: for c in dir(wav): if "__" not in c and "get" not in c and "set" not in c and hasattr( wav, c): print(c, getattr(wav, c)) raise RuntimeError(re) hptilde = FrequencySeries(hptild, delta_f=df, dtype=np.complex128, copy=True) hpref_padded = FrequencySeries(zeros(N / 2 + 1), delta_f=df, dtype=np.complex128, copy=True) hpref_padded[0:len(hptilde)] = hptilde hctilde = FrequencySeries(hctild, delta_f=df, dtype=np.complex128, copy=True) hcref_padded = FrequencySeries(zeros(N / 2 + 1), delta_f=df, dtype=np.complex128, copy=True) hcref_padded[0:len(hctilde)] = hctilde href_padded = generate_detector_strain(wav, hpref_padded, hcref_padded) elif approximant in td_approximants(): #raise IOError("Time domain approximants not supported at the moment..") try: hp, hc = get_td_waveform(approximant=approximant, mass1=m1, mass2=m2, spin1x=s1x, spin1y=s1y, spin1z=s1z, spin2x=s2x, spin2y=s2y, spin2z=s2z, eccentricity=ecc, mean_per_ano=mean_per_ano, long_asc_nodes=long_asc_nodes, coa_phase=coa_phase, inclination=inc, distance=dist, f_lower=f_min, delta_t=dt) except RuntimeError as re: for c in dir(wav): if "__" not in c and "get" not in c and "set" not in c and hasattr( wav, c): print(c, getattr(wav, c)) raise RuntimeError(re) hpref_padded = TimeSeries(zeros(N), delta_t=dt, dtype=hp.dtype, copy=True) hpref_padded[:len(hp)] = hp hcref_padded = TimeSeries(zeros(N), delta_t=dt, dtype=hc.dtype, copy=True) hcref_padded[:len(hc)] = hc href_padded_td = generate_detector_strain(wav, hpref_padded, hcref_padded) href_padded = make_frequency_series(href_padded_td) return href_padded