def gen(**params): import numpy, pycbc.conversions from pycbc.types import FrequencySeries from ctypes import c_double, c_void_p from pycbc.pnutils import megaparsecs_to_meters df = params['delta_f'] fend = 1000 if 'f_final' not in params else params['f_final'] if fend == 0: fend = 1000 flow = params['f_lower'] flen = int(fend / df) + 1 m1 = params['mass1'] m2 = params['mass2'] inc = params['inclination'] ecc = params['eccentricity'] if ecc < 1e-5: ecc = 1e-5 lc = params['long_asc_nodes'] lamc = params['coa_phase'] dist = params['distance'] / 9.7156118319036E-15 mchirp = float(pycbc.conversions.mchirp_from_mass1_mass2(m1, m2)) eta = float(pycbc.conversions.eta_from_mass1_mass2(m1, m2)) hp = numpy.zeros(flen, dtype=numpy.complex128) hc = numpy.zeros(flen, dtype=numpy.complex128) f = rlib.generate f.argtypes = [ c_void_p, c_void_p, c_double, c_double, c_double, c_double, c_double, c_double, c_double, c_double, c_double ] _ = f(hp.ctypes.data, hc.ctypes.data, mchirp, eta, inc, ecc, lamc, lc, dist, fend, df) # it appears that the plus / cross data is time inverted hp = FrequencySeries(hp.conj(), delta_f=df, epoch=-int(1.0 / df)) hc = FrequencySeries(hc.conj(), delta_f=df, epoch=-int(1.0 / df)) kmin = int(flow / df) hp[:kmin].clear() hc[:kmin].clear() return hp, hc
def _get_imrphenomx_modes(return_posneg=False, **params): """Generates ``IMRPhenomX[P]HM`` waveforms mode-by-mode. Currently does not work; just raises a ``NotImplementedError``. """ # FIXME: raising not implemented error because this currently does not # work. The issue is the OneMode function adds the +/- m modes together # automatically. Remove once this is fixed in lalsimulation, and/or I # figure out a reliable way to separate the +/-m modes. raise NotImplementedError("Currently not implemented") approx = params['approximant'] if not approx.startswith('IMRPhenomX'): raise ValueError("unsupported approximant") mode_array = params.pop('mode_array', None) if mode_array is None: mode_array = default_modes(approx) if 'f_final' not in params: # setting to 0 will default to ringdown frequency params['f_final'] = 0. hlms = {} for (l, m) in mode_array: params['mode_array'] = [(l, m)] laldict = _check_lal_pars(params) hpos, hneg = lalsimulation.SimIMRPhenomXPHMOneMode( l, m, params['mass1']*lal.MSUN_SI, params['mass2']*lal.MSUN_SI, params['spin1x'], params['spin1y'], params['spin1z'], params['spin2x'], params['spin2y'], params['spin2z'], params['distance']*1e6*lal.PC_SI, params['coa_phase'], params['delta_f'], params['f_lower'], params['f_final'], params['f_ref'], laldict) hpos = FrequencySeries(hpos.data.data, delta_f=hpos.deltaF, epoch=hpos.epoch) hneg = FrequencySeries(hneg.data.data, delta_f=hneg.deltaF, epoch=hneg.epoch) if return_posneg: hlms[l, m] = (hpos, hneg) else: # convert to ulm, vlm ulm = 0.5 * (hpos + hneg.conj()) vlm = 0.5j * (hneg.conj() - hpos) hlms[l, m] = (ulm, vlm) return hlms
def welch(timeseries, seg_len=4096, seg_stride=2048, window='hann', avg_method='median', num_segments=None, require_exact_data_fit=False): """PSD estimator based on Welch's method. Parameters ---------- timeseries : TimeSeries Time series for which the PSD is to be estimated. seg_len : int Segment length in samples. seg_stride : int Separation between consecutive segments, in samples. window : {'hann'} Function used to window segments before Fourier transforming. avg_method : {'median', 'mean', 'median-mean'} Method used for averaging individual segment PSDs. Returns ------- psd : FrequencySeries Frequency series containing the estimated PSD. Raises ------ ValueError For invalid choices of `seg_len`, `seg_stride` `window` and `avg_method` and for inconsistent combinations of len(`timeseries`), `seg_len` and `seg_stride`. Notes ----- See arXiv:gr-qc/0509116 for details. """ window_map = { 'hann': numpy.hanning } # sanity checks if not window in window_map: raise ValueError('Invalid window') if not avg_method in ('mean', 'median', 'median-mean'): raise ValueError('Invalid averaging method') if type(seg_len) is not int or type(seg_stride) is not int \ or seg_len <= 0 or seg_stride <= 0: raise ValueError('Segment length and stride must be positive integers') if timeseries.precision == 'single': fs_dtype = numpy.complex64 elif timeseries.precision == 'double': fs_dtype = numpy.complex128 num_samples = len(timeseries) if num_segments is None: num_segments = int(num_samples // seg_stride) # NOTE: Is this not always true? if (num_segments - 1) * seg_stride + seg_len > num_samples: num_segments -= 1 if not require_exact_data_fit: data_len = (num_segments - 1) * seg_stride + seg_len # Get the correct amount of data if data_len < num_samples: diff = num_samples - data_len start = diff // 2 end = num_samples - diff // 2 # Want this to be integers so if diff is odd, catch it here. if diff % 2: start = start + 1 timeseries = timeseries[start:end] num_samples = len(timeseries) if data_len > num_samples: err_msg = "I was asked to estimate a PSD on %d " %(data_len) err_msg += "data samples. However the data provided only contains " err_msg += "%d data samples." %(num_samples) if num_samples != (num_segments - 1) * seg_stride + seg_len: raise ValueError('Incorrect choice of segmentation parameters') w = Array(window_map[window](seg_len).astype(timeseries.dtype)) # calculate psd of each segment delta_f = 1. / timeseries.delta_t / seg_len segment_tilde = FrequencySeries(numpy.zeros(seg_len / 2 + 1), \ delta_f=delta_f, dtype=fs_dtype) segment_psds = [] for i in xrange(num_segments): segment_start = i * seg_stride segment_end = segment_start + seg_len segment = timeseries[segment_start:segment_end] assert len(segment) == seg_len fft(segment * w, segment_tilde) seg_psd = abs(segment_tilde * segment_tilde.conj()).numpy() #halve the DC and Nyquist components to be consistent with TO10095 seg_psd[0] /= 2 seg_psd[-1] /= 2 segment_psds.append(seg_psd) segment_psds = numpy.array(segment_psds) if avg_method == 'mean': psd = numpy.mean(segment_psds, axis=0) elif avg_method == 'median': psd = numpy.median(segment_psds, axis=0) / median_bias(num_segments) elif avg_method == 'median-mean': odd_psds = segment_psds[::2] even_psds = segment_psds[1::2] odd_median = numpy.median(odd_psds, axis=0) / \ median_bias(len(odd_psds)) even_median = numpy.median(even_psds, axis=0) / \ median_bias(len(even_psds)) psd = (odd_median + even_median) / 2 psd *= 2 * delta_f * seg_len / (w*w).sum() return FrequencySeries(psd, delta_f=delta_f, dtype=timeseries.dtype, epoch=timeseries.start_time)
def inverse_spectrum_truncation(psd, max_filter_len, low_frequency_cutoff=None, trunc_method=None): """Modify a PSD such that the impulse response associated with its inverse square root is no longer than `max_filter_len` time samples. In practice this corresponds to a coarse graining or smoothing of the PSD. Parameters ---------- psd : FrequencySeries PSD whose inverse spectrum is to be truncated. max_filter_len : int Maximum length of the time-domain filter in samples. low_frequency_cutoff : {None, int} Frequencies below `low_frequency_cutoff` are zeroed in the output. trunc_method : {None, 'hann'} Function used for truncating the time-domain filter. None produces a hard truncation at `max_filter_len`. Returns ------- psd : FrequencySeries PSD whose inverse spectrum has been truncated. Raises ------ ValueError For invalid types or values of `max_filter_len` and `low_frequency_cutoff`. Notes ----- See arXiv:gr-qc/0509116 for details. """ # sanity checks if type(max_filter_len) is not int or max_filter_len <= 0: raise ValueError('max_filter_len must be a positive integer') if low_frequency_cutoff is not None and low_frequency_cutoff < 0 \ or low_frequency_cutoff > psd.sample_frequencies[-1]: raise ValueError('low_frequency_cutoff must be within the bandwidth of the PSD') N = (len(psd)-1)*2 inv_asd = FrequencySeries((1. / psd)**0.5, delta_f=psd.delta_f, \ dtype=complex_same_precision_as(psd)) inv_asd[0] = 0 inv_asd[N/2] = 0 q = TimeSeries(numpy.zeros(N), delta_t=(N / psd.delta_f), \ dtype=real_same_precision_as(psd)) if low_frequency_cutoff: kmin = int(low_frequency_cutoff / psd.delta_f) inv_asd[0:kmin] = 0 ifft(inv_asd, q) trunc_start = max_filter_len / 2 trunc_end = N - max_filter_len / 2 if trunc_method == 'hann': trunc_window = Array(numpy.hanning(max_filter_len), dtype=q.dtype) q[0:trunc_start] *= trunc_window[max_filter_len/2:max_filter_len] q[trunc_end:N] *= trunc_window[0:max_filter_len/2] q[trunc_start:trunc_end] = 0 psd_trunc = FrequencySeries(numpy.zeros(len(psd)), delta_f=psd.delta_f, \ dtype=complex_same_precision_as(psd)) fft(q, psd_trunc) psd_trunc *= psd_trunc.conj() psd_out = 1. / abs(psd_trunc) return psd_out
m1 = m2 = 1.4 mchirp = float(pycbc.conversions.mchirp_from_mass1_mass2(m1, m2)) eta = float(pycbc.conversions.eta_from_mass1_mass2(m1, m2)) hp = numpy.zeros(flen, dtype=numpy.complex128) hc = hp.copy() f = lib.generate f.argtypes = [ c_void_p, c_void_p, c_double, c_double, c_double, c_double, c_double, c_double, c_double, c_double, c_double ] _ = f(hp.ctypes.data, hc.ctypes.data, mchirp, eta, inc, ecc, lamc, lc, dist, fend, df) import pylab # it appears that the plus / cross data is time inverted hp = FrequencySeries(hp.conj(), delta_f=df, epoch=-int(1.0 / df)) hc = FrequencySeries(hc.conj(), delta_f=df, epoch=-int(1.0 / df)) kmin = int(flow / df) hp[:kmin].clear() hc[:kmin].clear() t = hp.to_timeseries() pylab.plot(t.sample_times, t) #pylab.xscale('log') pylab.show()