def test_varying_orbital_phase(self): #"""Check that the waveform is consistent under phase changes #""" if self.p.approximant in td_approximants(): sample_attr = 'sample_times' else: sample_attr = 'sample_frequencies' f = pylab.figure() pylab.axes([.1, .2, 0.8, 0.70]) hp_ref, hc_ref = get_waveform(self.p, coa_phase=0) pylab.plot(getattr(hp_ref, sample_attr), hp_ref.real(), label="phiref") hp, hc = get_waveform(self.p, coa_phase=lal.PI / 4) m, i = match(hp_ref, hp) self.assertAlmostEqual(1, m, places=2) o = overlap(hp_ref, hp) pylab.plot(getattr(hp, sample_attr), hp.real(), label="$phiref \pi/4$") hp, hc = get_waveform(self.p, coa_phase=lal.PI / 2) m, i = match(hp_ref, hp) o = overlap(hp_ref, hp) self.assertAlmostEqual(1, m, places=7) self.assertAlmostEqual(-1, o, places=7) pylab.plot(getattr(hp, sample_attr), hp.real(), label="$phiref \pi/2$") hp, hc = get_waveform(self.p, coa_phase=lal.PI) m, i = match(hp_ref, hp) o = overlap(hp_ref, hp) self.assertAlmostEqual(1, m, places=7) self.assertAlmostEqual(1, o, places=7) pylab.plot(getattr(hp, sample_attr), hp.real(), label="$phiref \pi$") pylab.xlim(min(getattr(hp, sample_attr)), max(getattr(hp, sample_attr))) pylab.title("Vary %s oribital phiref, h+" % self.p.approximant) if self.p.approximant in td_approximants(): pylab.xlabel("Time to coalescence (s)") else: pylab.xlabel("GW Frequency (Hz)") pylab.ylabel("GW Strain (real part)") pylab.legend(loc="upper left") info = self.version_txt pylab.figtext(0.05, 0.05, info) if self.save_plots: pname = self.plot_dir + "/%s-vary-phase.png" % self.p.approximant pylab.savefig(pname) if self.show_plots: pylab.show() else: pylab.close(f)
def test_varying_orbital_phase(self): #"""Check that the waveform is consistent under phase changes #""" if self.p.approximant in td_approximants(): sample_attr = 'sample_times' else: sample_attr = 'sample_frequencies' f = pylab.figure() pylab.axes([.1, .2, 0.8, 0.70]) hp_ref, hc_ref = get_waveform(self.p, coa_phase=0) pylab.plot(getattr(hp_ref, sample_attr), hp_ref.real(), label="phiref") hp, hc = get_waveform(self.p, coa_phase=lal.PI/4) m, i = match(hp_ref, hp) self.assertAlmostEqual(1, m, places=2) o = overlap(hp_ref, hp) pylab.plot(getattr(hp, sample_attr), hp.real(), label="$phiref \pi/4$") hp, hc = get_waveform(self.p, coa_phase=lal.PI/2) m, i = match(hp_ref, hp) o = overlap(hp_ref, hp) self.assertAlmostEqual(1, m, places=7) self.assertAlmostEqual(-1, o, places=7) pylab.plot(getattr(hp, sample_attr), hp.real(), label="$phiref \pi/2$") hp, hc = get_waveform(self.p, coa_phase=lal.PI) m, i = match(hp_ref, hp) o = overlap(hp_ref, hp) self.assertAlmostEqual(1, m, places=7) self.assertAlmostEqual(1, o, places=7) pylab.plot(getattr(hp, sample_attr), hp.real(), label="$phiref \pi$") pylab.xlim(min(getattr(hp, sample_attr)), max(getattr(hp, sample_attr))) pylab.title("Vary %s oribital phiref, h+" % self.p.approximant) if self.p.approximant in td_approximants(): pylab.xlabel("Time to coalescence (s)") else: pylab.xlabel("GW Frequency (Hz)") pylab.ylabel("GW Strain (real part)") pylab.legend(loc="upper left") info = self.version_txt pylab.figtext(0.05, 0.05, info) if self.save_plots: pname = self.plot_dir + "/%s-vary-phase.png" % self.p.approximant pylab.savefig(pname) if self.show_plots: pylab.show() else: pylab.close(f)
def test_swapping_constituents(self): #""" Test that waveform remains unchanged under swapping both objects #""" hp, hc = get_waveform(self.p) hpswap, hcswap = get_waveform(self.p, mass1=self.p.mass2, mass2=self.p.mass1, spin1x=self.p.spin2x, spin1y=self.p.spin2y, spin1z=self.p.spin2z, spin2x=self.p.spin1x, spin2y=self.p.spin1y, spin2z=self.p.spin1z, lambda1=self.p.lambda2, lambda2=self.p.lambda1) op = overlap(hp, hpswap) self.assertAlmostEqual(1, op, places=7) oc = overlap(hc, hcswap) self.assertAlmostEqual(1, oc, places=7)
def test_change_rate(self): #""" Test that waveform remains unchanged under changing rate #""" hp, hc = get_waveform(self.p) hp2dec, hc2dec = get_waveform(self.p, delta_t=self.p.delta_t * 2.) hpdec = numpy.zeros(len(hp2dec.data)) hcdec = numpy.zeros(len(hp2dec.data)) for idx in range(min(len(hp2dec.data), int(len(hp.data) / 2))): hpdec[idx] = hp.data[2 * idx] hcdec[idx] = hc.data[2 * idx] hpTS = TimeSeries(hpdec, delta_t=self.p.delta_t * 2., epoch=hp.start_time) hcTS = TimeSeries(hcdec, delta_t=self.p.delta_t * 2., epoch=hc.start_time) f = pylab.figure() pylab.plot(hp.sample_times, hp.data, label="rate %s Hz" % "{:.0f}".format(1. / self.p.delta_t)) pylab.plot(hp2dec.sample_times, hp2dec.data, label="rate %s Hz" % "{:.0f}".format(1. / (self.p.delta_t * 2.))) pylab.title("Halving %s rate, $\\tilde{h}$+" % self.p.approximant) pylab.xlabel("time (sec)") pylab.ylabel("amplitude") pylab.legend() info = self.version_txt pylab.figtext(0.05, 0.05, info) if self.save_plots: pname = self.plot_dir + "/%s-vary-rate.png" % self.p.approximant pylab.savefig(pname) if self.show_plots: pylab.show() else: pylab.close(f) op = overlap(hpTS, hp2dec) self.assertAlmostEqual(1., op, places=2) oc = overlap(hcTS, hc2dec) self.assertAlmostEqual(1., oc, places=2)
def test_nearby_waveform_agreement(self): #""" Check that the overlaps are consistent for nearby waveforms #""" def nearby(params): tol = 1e-7 from numpy.random import uniform nearby_params = copy.copy(params) nearby_params.mass1 *= uniform(low=1-tol, high=1+tol) nearby_params.mass2 *= uniform(low=1-tol, high=1+tol) nearby_params.spin1x *= uniform(low=1-tol, high=1+tol) nearby_params.spin1y *= uniform(low=1-tol, high=1+tol) nearby_params.spin1z *= uniform(low=1-tol, high=1+tol) nearby_params.spin2x *= uniform(low=1-tol, high=1+tol) nearby_params.spin2y *= uniform(low=1-tol, high=1+tol) nearby_params.spin2z *= uniform(low=1-tol, high=1+tol) nearby_params.inclination *= uniform(low=1-tol, high=1+tol) nearby_params.coa_phase *= uniform(low=1-tol, high=1+tol) return nearby_params hp, hc = get_waveform(self.p) for i in range(10): p_near = nearby(self.p) hpn, hcn = get_waveform(p_near) maxlen = max(len(hpn), len(hp)) hp.resize(maxlen) hpn.resize(maxlen) o = overlap(hp, hpn) self.assertAlmostEqual(1, o, places=5)
def test_nearby_waveform_agreement(self): #""" Check that the overlaps are consistent for nearby waveforms #""" def nearby(params): tol = 1e-7 from numpy.random import uniform nearby_params = copy.copy(params) nearby_params.mass1 *= uniform(low=1 - tol, high=1 + tol) nearby_params.mass2 *= uniform(low=1 - tol, high=1 + tol) nearby_params.spin1x *= uniform(low=1 - tol, high=1 + tol) nearby_params.spin1y *= uniform(low=1 - tol, high=1 + tol) nearby_params.spin1z *= uniform(low=1 - tol, high=1 + tol) nearby_params.spin2x *= uniform(low=1 - tol, high=1 + tol) nearby_params.spin2y *= uniform(low=1 - tol, high=1 + tol) nearby_params.spin2z *= uniform(low=1 - tol, high=1 + tol) nearby_params.inclination *= uniform(low=1 - tol, high=1 + tol) nearby_params.coa_phase *= uniform(low=1 - tol, high=1 + tol) return nearby_params hp, hc = get_waveform(self.p) for i in range(10): p_near = nearby(self.p) hpn, hcn = get_waveform(p_near) maxlen = max(len(hpn), len(hp)) hp.resize(maxlen) hpn.resize(maxlen) o = overlap(hp, hpn) self.assertAlmostEqual(1, o, places=5)
def test_spatmplt(self): fl = 25 delta_f = 1.0 / 256 for m1 in [1, 1.4, 20]: for m2 in [1.4, 20]: for s1 in [-2, -1, -0.5, 0, 0.5, 1, 2]: for s2 in [-2, -1, -0.5, 0, 0.5, 1, 2]: # Generate TaylorF2 from lalsimulation, restricting to the capabilities of spatmplt hpr,_ = get_fd_waveform( mass1=m1, mass2=m2, spin1z=s1, spin2z=s2, delta_f=delta_f, f_lower=fl, approximant="TaylorF2", amplitude_order=0, spin_order=-1, phase_order=-1) hpr=hpr.astype(complex64) with self.context: # Generate the spatmplt waveform out = zeros(len(hpr), dtype=complex64) hp = get_waveform_filter(out, mass1=m1, mass2=m2, spin1z=s1, spin2z=s2, delta_f=delta_f, f_lower=fl, approximant="SPAtmplt", amplitude_order=0, spin_order=-1, phase_order=-1) # Check the diff is sane mag = abs(hpr).sum() diff = abs(hp - hpr).sum() / mag self.assertTrue(diff < 0.01) # Point to point overlap (no phase or time maximization) o = overlap(hp, hpr) self.assertAlmostEqual(1.0, o, places=4) print("checked m1: %s m2:: %s s1z: %s s2z: %s] overlap = %s, diff = %s" % (m1, m2, s1, s2, o, diff))
def test_change_rate(self): #""" Test that waveform remains unchanged under changing rate #""" hp, hc = get_waveform(self.p) hp2dec, hc2dec = get_waveform(self.p, delta_t=self.p.delta_t*2.) hpdec=numpy.zeros(len(hp2dec.data)) hcdec=numpy.zeros(len(hp2dec.data)) for idx in range(min(len(hp2dec.data),int(len(hp.data)/2))): hpdec[idx]=hp.data[2*idx] hcdec[idx]=hc.data[2*idx] hpTS=TimeSeries(hpdec, delta_t=self.p.delta_t*2.,epoch=hp.start_time) hcTS=TimeSeries(hcdec, delta_t=self.p.delta_t*2.,epoch=hc.start_time) f = pylab.figure() pylab.plot(hp.sample_times, hp.data,label="rate %s Hz" %"{:.0f}".format(1./self.p.delta_t)) pylab.plot(hp2dec.sample_times, hp2dec.data, label="rate %s Hz" %"{:.0f}".format(1./(self.p.delta_t*2.))) pylab.title("Halving %s rate, $\\tilde{h}$+" % self.p.approximant) pylab.xlabel("time (sec)") pylab.ylabel("amplitude") pylab.legend() info = self.version_txt pylab.figtext(0.05, 0.05, info) if self.save_plots: pname = self.plot_dir + "/%s-vary-rate.png" % self.p.approximant pylab.savefig(pname) if self.show_plots: pylab.show() else: pylab.close(f) op=overlap(hpTS,hp2dec) self.assertAlmostEqual(1., op, places=2) oc=overlap(hcTS,hc2dec) self.assertAlmostEqual(1., oc, places=2)
def compute_overlap(**options): psd_choice = 'HPZD' # TODO: Include this in global dictionary? psd = psd_cache.load_psd(psd_choice, options['F_MAX'], options['DEL_F']) nsamples = int(options['F_MAX'] / options['DEL_F']) + 1 SIDEBAND = [None, 2, 1, 0, -1, -2] H = [] for i, band in enumerate(SIDEBAND): options['BAND'] = band H.append(wave.generate_template(**options)) #TODO: Change these to add any overlaps of your choice. OLVP = np.zeros(9) #SNR OLVP[0] = norm(H[0], psd, options['F_MIN'], options['F_MAX']) OLVP[1] = norm(H[1], psd, options['F_MIN'], options['F_MAX']) OLVP[2] = norm(H[3], psd, options['F_MIN'], options['F_MAX']) #Overlaps OLVP[3] = overlap(H[0], H[1], psd, options['F_MIN'], options['F_MAX']) OLVP[4] = overlap(H[0], H[2], psd, options['F_MIN'], options['F_MAX']) OLVP[5] = overlap(H[0], H[3], psd, options['F_MIN'], options['F_MAX']) OLVP[6] = overlap(H[0], H[4], psd, options['F_MIN'], options['F_MAX']) OLVP[7] = overlap(H[0], H[5], psd, options['F_MIN'], options['F_MAX']) OLVP[8] = overlap(H[0], H[1] + H[3], psd, options['F_MIN'], options['F_MAX']) return OLVP
def test_spatmplt(self): fl = 25 delta_f = 1.0 / 256 for m1 in [1, 1.4, 20]: for m2 in [1.4, 20]: for s1 in [-2, -1, -0.5, 0, 0.5, 1, 2]: for s2 in [-2, -1, -0.5, 0, 0.5, 1, 2]: # Generate TaylorF2 from lalsimulation, restricting to the capabilities of spatmplt hpr, _ = get_fd_waveform(mass1=m1, mass2=m2, spin1z=s1, spin2z=s2, delta_f=delta_f, f_lower=fl, approximant="TaylorF2", amplitude_order=0, spin_order=-1, phase_order=-1) hpr = hpr.astype(complex64) with self.context: # Generate the spatmplt waveform out = zeros(len(hpr), dtype=complex64) hp = get_waveform_filter(out, mass1=m1, mass2=m2, spin1z=s1, spin2z=s2, delta_f=delta_f, f_lower=fl, approximant="SPAtmplt", amplitude_order=0, spin_order=-1, phase_order=-1) # Check the diff is sane mag = abs(hpr).sum() diff = abs(hp - hpr).sum() / mag self.assertTrue(diff < 0.01) # Point to point overlap (no phase or time maximization) o = overlap(hp, hpr) self.assertAlmostEqual(1.0, o, places=4) print( "checked m1: %s m2:: %s s1z: %s s2z: %s] overlap = %s, diff = %s" % (m1, m2, s1, s2, o, diff))
def fmoment(n, htilde, psd, frange): """ Compute the n-th moment of frequency """ # Get frequency series of waveform f = htilde.get_sample_frequencies() fhtilde = types.FrequencySeries(initial_array=htilde.data * f.data**n, delta_f=htilde.delta_f) snrsq = sigmasq(htilde=htilde, psd=psd, low_frequency_cutoff=frange[0], high_frequency_cutoff=frange[1]) return (1.0/snrsq) * overlap(fhtilde, htilde, psd=psd, low_frequency_cutoff=frange[0], high_frequency_cutoff=frange[1], normalized=False)
hp, hc = get_td_waveform(approximant="EOBNRv2", mass1=10, mass2=10, f_lower=f_low, delta_t=1.0 / 4096) print("waveform is %s seconds long" % hp.duration) print("Generating waveform 2") sp, sc = get_td_waveform(approximant="TaylorT4", mass1=10, mass2=10, f_lower=f_low, delta_t=1.0 / 4096) print("waveform is %s seconds long" % sp.duration) # Ensure that the waveforms are resized to the same length sp.resize(tlen) hp.resize(tlen) print("Calculating analytic PSD") psd = aLIGOZeroDetHighPower(flen, delta_f, f_low) print("Calculating match and overlap") # Note: This takes a while the first time as an FFT plan is generated # subsequent calls within the same program will be faster m, i = match(hp, sp, psd=psd, low_frequency_cutoff=f_low) o = overlap(hp, sp, psd=psd, low_frequency_cutoff=f_low) print("Overlap %s" % o) print("Maximized Overlap %s" % m)
def test_overlap(self): # Overlap with self should be one self.assertAlmostEqual(self.FS.overlap(self.FS, fmin=0.5), 1) # Overlap with -self should be -one self.assertAlmostEqual(self.FS.overlap(-self.FS, fmin=0.5), -1) # Test with PyCBC num_times = 2000 times = np.linspace(0, 20 * 2 * np.pi, num_times) values1 = np.sin(40 * times) values2 = np.sin(60 * times) f1_series = ts.TimeSeries(times, values1).to_FrequencySeries() f2_series = ts.TimeSeries(times, values2).to_FrequencySeries() try: # Test with PyCBC fmin = 5 fmax = 15 # PyCBC raises some benign warnings. We ignore them. with warnings.catch_warnings(): warnings.simplefilter("ignore") from pycbc.filter import overlap from pycbc.types import frequencyseries as pycbcfs from pycbc.types import timeseries as pycbcts ts1_pcbc = pycbcts.TimeSeries(values1, delta_t=times[1] - times[0]) ts2_pcbc = pycbcts.TimeSeries(values2, delta_t=times[1] - times[0]) ov = overlap( ts1_pcbc, ts2_pcbc, psd=None, low_frequency_cutoff=fmin, high_frequency_cutoff=fmax, ) self.assertAlmostEqual( f1_series.overlap(f2_series, fmin=fmin, fmax=fmax, noises=None), ov, places=4, ) # Test with non trivial noise # PyCBC requires the noise to be defined on the same frequencies as the # data df_noise = ts1_pcbc.to_frequencyseries().delta_f f_noise = np.array( [i * df_noise for i in range(num_times // 2 + 1)]) # Funky looking noise psd_noise = np.abs(np.sin(50 * f_noise) + 0.1) noise_pycbc = pycbcfs.FrequencySeries(psd_noise, delta_f=df_noise) noise2 = fs.FrequencySeries(f_noise, psd_noise) ov_noise = overlap( ts1_pcbc, ts2_pcbc, psd=noise_pycbc, low_frequency_cutoff=fmin, high_frequency_cutoff=fmax, ) self.assertAlmostEqual( f1_series.overlap(f2_series, fmin=fmin, fmax=fmax, noises=noise2), ov_noise, places=5, ) except ImportError: # pragma: no cover pass
def test_mismatch(self): fmin = 5 fmax = 15 # Invalid noise with self.assertRaises(TypeError): gwm.network_mismatch(self.ts1, self.ts2, 8, -70, "2015-09-14 09:50:45", noises=1) # No noise, three detectors antennas = gwu.antenna_responses_from_sky_localization( 8, -70, "2015-09-14 09:50:45") self.assertAlmostEqual( gwm.mismatch_from_strains( self.ts1, self.ts2, fmin=fmin, fmax=fmax, noises=None, antenna_patterns=list(antennas), num_polarization_shifts=30, num_time_shifts=30, time_shift_start=-70, time_shift_end=70, )[0], gwm.network_mismatch( self.ts1, self.ts2, 8, -70, "2015-09-14 09:50:45", fmin=fmin, fmax=fmax, noises=None, num_polarization_shifts=30, num_time_shifts=30, time_shift_start=-70, time_shift_end=70, )[0], ) # Only one active detector only_virgo = gwu.Detectors(hanford=-1, livingston=-1, virgo=None) self.assertAlmostEqual( gwm.mismatch_from_strains( self.ts1, self.ts2, fmin=fmin, fmax=fmax, noises=None, antenna_patterns=[antennas.virgo], num_polarization_shifts=30, num_time_shifts=30, time_shift_start=-70, time_shift_end=70, )[0], gwm.network_mismatch( self.ts1, self.ts2, 8, -70, "2015-09-14 09:50:45", fmin=fmin, fmax=fmax, noises=only_virgo, num_polarization_shifts=30, num_time_shifts=30, time_shift_start=-70, time_shift_end=70, )[0], ) # Test with a "gw-looking" singal from PyCBC # # First, we test the overlap by giving num_polarizations, # num_time_shifts=1 try: with warnings.catch_warnings(): warnings.simplefilter("ignore") from pycbc.filter import match, overlap from pycbc.types import timeseries as pycbcts from pycbc.waveform import get_td_waveform fmin_gw = 50 fmax_gw = 100 delta_t = 1 / 4096 hp1, hc1 = get_td_waveform( approximant="IMRPhenomPv2", mass1=10, mass2=10, spin1z=0.9, delta_t=delta_t, f_lower=40, ) hp2, hc2 = get_td_waveform( approximant="IMRPhenomPv2", mass1=10, mass2=25, spin1z=-0.5, delta_t=delta_t, f_lower=40, ) # PyCBC does not work well with series with different length. So, we # crop the longer one to the length of the shorter one. For the choice # of paramters, it is 2 that is shorter than 1. 1 starts earlier in the # past. However, they have the same frequencies, so we can simply crop # away the part we are not interested in. time_offset = 2 # Manually computed looking at the times hp1 = hp1.crop(time_offset, 0) hc1 = hc1.crop(time_offset, 0) # We apply the "antenna pattern" h1_pycbc = pycbcts.TimeSeries(0.33 * hp1 + 0.66 * hc1, delta_t=hp1.delta_t) h2_pycbc = pycbcts.TimeSeries(0.33 * hp2 + 0.66 * hc2, delta_t=hp2.delta_t) overlap_m = overlap( h1_pycbc, h2_pycbc, psd=None, low_frequency_cutoff=fmin_gw, high_frequency_cutoff=fmax_gw, ) h1_postcac = ts.TimeSeries(h1_pycbc.sample_times, hp1 - 1j * hc1) h2_postcac = ts.TimeSeries(h2_pycbc.sample_times, hp2 - 1j * hc2) o = gwm.mismatch_from_strains( h1_postcac, h2_postcac, fmin=fmin_gw, fmax=fmax_gw, noises=None, antenna_patterns=[(0.66, 0.33)], num_polarization_shifts=1, num_time_shifts=1, time_shift_start=0, time_shift_end=0, force_numba=False, ) self.assertAlmostEqual(1 - o[0], overlap_m, places=2) # Now we can test the mismatch pycbc_m, _ = match( h1_pycbc, h2_pycbc, psd=None, low_frequency_cutoff=fmin_gw, high_frequency_cutoff=fmax_gw, ) pycbc_m = 1 - pycbc_m mat = gwm.mismatch_from_strains( h1_postcac, h2_postcac, fmin=fmin_gw, fmax=fmax_gw, noises=None, antenna_patterns=[(0.66, 0.33)], num_polarization_shifts=100, num_time_shifts=800, time_shift_start=-0.3, time_shift_end=0.3, force_numba=False, ) self.assertAlmostEqual(mat[0], pycbc_m, places=2) except ImportError: # pragma: no cover pass
mass1=10, mass2=10, f_lower=f_low, delta_t=1.0/4096) print "waveform is %s seconds long" % hp.duration print "Generating waveform 2" sp, sc = get_td_waveform(approximant="TaylorT4", mass1=10, mass2=10, f_lower=f_low, delta_t=1.0/4096) print "waveform is %s seconds long" % sp.duration # Ensure that the waveforms are resized to the same length sp.resize(tlen) hp.resize(tlen) print "Calculating analytic PSD" psd = aLIGOZeroDetHighPower(flen, delta_f, f_low) print "Calculating match and overlap" # Note: This takes a while the first time as an FFT plan is generated # subsequent calls within the same program will be faster m, i = match(hp, sp, psd=psd, low_frequency_cutoff=f_low) o = overlap(hp, sp, psd=psd, low_frequency_cutoff=f_low) print "Overlap %s" % o print "Maximized Overlap %s" % m
def compress_waveform(htilde, sample_points, tolerance, interpolation, precision, decomp_scratch=None, psd=None): """Retrieves the amplitude and phase at the desired sample points, and adds frequency points in order to ensure that the interpolated waveform has a mismatch with the full waveform that is <= the desired tolerance. The mismatch is computed by finding 1-overlap between `htilde` and the decompressed waveform; no maximimization over phase/time is done, a PSD may be used. .. note:: The decompressed waveform is only garaunteed to have a true mismatch <= the tolerance for the given `interpolation` and for no PSD. However, since no maximization over time/phase is performed when adding points, the actual mismatch between the decompressed waveform and `htilde` is better than the tolerance, using no PSD. Using a PSD does increase the mismatch, and can lead to mismatches > than the desired tolerance, but typically by only a factor of a few worse. Parameters ---------- htilde : FrequencySeries The waveform to compress. sample_points : array The frequencies at which to store the amplitude and phase. More points may be added to this, depending on the desired tolerance. tolerance : float The maximum mismatch to allow between a decompressed waveform and `htilde`. interpolation : str The interpolation to use for decompressing the waveform when computing overlaps. precision : str The precision being used to generate and store the compressed waveform points. decomp_scratch : {None, FrequencySeries} Optionally provide scratch space for decompressing the waveform. The provided frequency series must have the same `delta_f` and length as `htilde`. psd : {None, FrequencySeries} The psd to use for calculating the overlap between the decompressed waveform and the original full waveform. Returns ------- CompressedWaveform The compressed waveform data; see `CompressedWaveform` for details. """ fmin = sample_points.min() df = htilde.delta_f sample_index = (sample_points / df).astype(int) amp = utils.amplitude_from_frequencyseries(htilde) phase = utils.phase_from_frequencyseries(htilde) comp_amp = amp.take(sample_index) comp_phase = phase.take(sample_index) if decomp_scratch is None: outdf = df else: outdf = None hdecomp = fd_decompress(comp_amp, comp_phase, sample_points, out=decomp_scratch, df=outdf, f_lower=fmin, interpolation=interpolation) kmax = min(len(htilde), len(hdecomp)) htilde = htilde[:kmax] hdecomp = hdecomp[:kmax] mismatch = 1. - filter.overlap( hdecomp, htilde, psd=psd, low_frequency_cutoff=fmin) if mismatch > tolerance: # we'll need the difference in the waveforms as a function of frequency vecdiffs = vecdiff(htilde, hdecomp, sample_points, psd=psd) # We will find where in the frequency series the interpolated waveform # has the smallest overlap with the full waveform, add a sample point # there, and re-interpolate. We repeat this until the overall mismatch # is > than the desired tolerance added_points = [] while mismatch > tolerance: minpt = vecdiffs.argmax() # add a point at the frequency halfway between minpt and minpt+1 add_freq = sample_points[[minpt, minpt + 1]].mean() addidx = int(round(add_freq / df)) # ensure that only new points are added if addidx in sample_index: diffidx = vecdiffs.argsort() addpt = -1 while addidx in sample_index: addpt -= 1 try: minpt = diffidx[addpt] except IndexError: raise ValueError("unable to compress to desired tolerance") add_freq = sample_points[[minpt, minpt + 1]].mean() addidx = int(round(add_freq / df)) new_index = numpy.zeros(sample_index.size + 1, dtype=int) new_index[:minpt + 1] = sample_index[:minpt + 1] new_index[minpt + 1] = addidx new_index[minpt + 2:] = sample_index[minpt + 1:] sample_index = new_index sample_points = (sample_index * df).astype( real_same_precision_as(htilde)) # get the new compressed points comp_amp = amp.take(sample_index) comp_phase = phase.take(sample_index) # update the vecdiffs and mismatch hdecomp = fd_decompress(comp_amp, comp_phase, sample_points, out=decomp_scratch, df=outdf, f_lower=fmin, interpolation=interpolation) hdecomp = hdecomp[:kmax] new_vecdiffs = numpy.zeros(vecdiffs.size + 1) new_vecdiffs[:minpt] = vecdiffs[:minpt] new_vecdiffs[minpt + 2:] = vecdiffs[minpt + 1:] new_vecdiffs[minpt:minpt + 2] = vecdiff(htilde, hdecomp, sample_points[minpt:minpt + 2], psd=psd) vecdiffs = new_vecdiffs mismatch = 1. - filter.overlap( hdecomp, htilde, psd=psd, low_frequency_cutoff=fmin) added_points.append(addidx) logging.info("mismatch: %f, N points: %i (%i added)" % (mismatch, len(comp_amp), len(added_points))) return CompressedWaveform(sample_points, comp_amp, comp_phase, interpolation=interpolation, tolerance=tolerance, mismatch=mismatch, precision=precision)
def compress_waveform(htilde, sample_points, tolerance, interpolation, decomp_scratch=None): """Retrieves the amplitude and phase at the desired sample points, and adds frequency points in order to ensure that the interpolated waveform has a mismatch with the full waveform that is <= the desired tolerance. The mismatch is computed by finding 1-overlap between `htilde` and the decompressed waveform; no maximimization over phase/time is done, nor is any PSD used. .. note:: The decompressed waveform is only garaunteed to have a true mismatch <= the tolerance for the given `interpolation` and for no PSD. However, since no maximization over time/phase is performed when adding points, the actual mismatch between the decompressed waveform and `htilde` is better than the tolerance, using no PSD. Using a PSD does increase the mismatch, and can lead to mismatches > than the desired tolerance, but typically by only a factor of a few worse. Parameters ---------- htilde : FrequencySeries The waveform to compress. sample_points : array The frequencies at which to store the amplitude and phase. More points may be added to this, depending on the desired tolerance. tolerance : float The maximum mismatch to allow between a decompressed waveform and `htilde`. interpolation : str The interpolation to use for decompressing the waveform when computing overlaps. decomp_scratch : {None, FrequencySeries} Optionally provide scratch space for decompressing the waveform. The provided frequency series must have the same `delta_f` and length as `htilde`. Returns ------- CompressedWaveform The compressed waveform data; see `CompressedWaveform` for details. """ fmin = sample_points.min() df = htilde.delta_f sample_index = (sample_points / df).astype(int) amp = utils.amplitude_from_frequencyseries(htilde) phase = utils.phase_from_frequencyseries(htilde) comp_amp = amp.take(sample_index) comp_phase = phase.take(sample_index) if decomp_scratch is None: outdf = df else: outdf = None out = decomp_scratch hdecomp = fd_decompress(comp_amp, comp_phase, sample_points, out=decomp_scratch, df=outdf, f_lower=fmin, interpolation=interpolation) mismatch = 1. - filter.overlap(hdecomp, htilde, low_frequency_cutoff=fmin) if mismatch > tolerance: # we'll need the difference in the waveforms as a function of frequency vecdiffs = vecdiff(htilde, hdecomp, sample_points) # We will find where in the frequency series the interpolated waveform # has the smallest overlap with the full waveform, add a sample point # there, and re-interpolate. We repeat this until the overall mismatch # is > than the desired tolerance added_points = [] while mismatch > tolerance: minpt = vecdiffs.argmax() # add a point at the frequency halfway between minpt and minpt+1 add_freq = sample_points[[minpt, minpt+1]].mean() addidx = int(add_freq/df) new_index = numpy.zeros(sample_index.size+1, dtype=int) new_index[:minpt+1] = sample_index[:minpt+1] new_index[minpt+1] = addidx new_index[minpt+2:] = sample_index[minpt+1:] sample_index = new_index sample_points = (sample_index * df).astype( real_same_precision_as(htilde)) # get the new compressed points comp_amp = amp.take(sample_index) comp_phase = phase.take(sample_index) # update the vecdiffs and mismatch hdecomp = fd_decompress(comp_amp, comp_phase, sample_points, out=decomp_scratch, df=outdf, f_lower=fmin, interpolation=interpolation) new_vecdiffs = numpy.zeros(vecdiffs.size+1) new_vecdiffs[:minpt] = vecdiffs[:minpt] new_vecdiffs[minpt+2:] = vecdiffs[minpt+1:] new_vecdiffs[minpt:minpt+2] = vecdiff(htilde, hdecomp, sample_points[minpt:minpt+2]) vecdiffs = new_vecdiffs mismatch = 1. - filter.overlap(hdecomp, htilde, low_frequency_cutoff=fmin) added_points.append(addidx) logging.info("mismatch: %f, N points: %i (%i added)" %(mismatch, len(comp_amp), len(added_points))) return CompressedWaveform(sample_points, comp_amp, comp_phase, interpolation=interpolation, tolerance=tolerance, mismatch=mismatch)