Beispiel #1
0
def normal(start, end, seed=0):
    """ Generate data with a white Gaussian (normal) distribution

    Parameters
    ----------
    start_time : int
        Start time in GPS seconds to generate noise
    end_time : int
        End time in GPS seconds to generate nosie
    seed : {None, int}
        The seed to generate the noise.

    Returns
    --------
    noise : TimeSeries
        A TimeSeries containing gaussian noise
    """
    # This is reproduceable because we used fixed seeds from known values
    s = int(start / BLOCK_SIZE)
    e = int(end / BLOCK_SIZE)

    # The data evenly divides so the last block would be superfluous
    if end % BLOCK_SIZE == 0:
        e -= 1

    sv = RandomState(seed).randint(-2**50, 2**50)
    data = numpy.concatenate([block(i + sv) for i in numpy.arange(s, e + 1, 1)])
    ts = TimeSeries(data, delta_t=1.0 / SAMPLE_RATE, epoch=start)
    return ts.time_slice(start, end)
Beispiel #2
0
def line_model(freq, data, tref, amp=1, phi=0):
    """ Simple time-domain model for a frequency line.

    Parameters
    ----------
    freq: float
        Frequency of the line.
    data: pycbc.types.TimeSeries
        Reference data, to get delta_t, start_time, duration and sample_times.
    tref: float
        Reference time for the line model.
    amp: {1., float}, optional
        Amplitude of the frequency line.
    phi: {0. float}, optional
        Phase of the frequency line (radians).

    Returns
    -------
    freq_line: pycbc.types.TimeSeries
        A timeseries of the line model with frequency 'freq'. The returned
        data are complex to allow measuring the amplitude and phase of the
        corresponding frequency line in the strain data. For extraction, use
        only the real part of the data.
    """
    freq_line = TimeSeries(zeros(len(data)), delta_t=data.delta_t,
                           epoch=data.start_time)

    times = data.sample_times - float(tref)
    alpha = 2 * numpy.pi * freq * times + phi
    freq_line.data = amp * numpy.exp(1.j * alpha)

    return freq_line
Beispiel #3
0
def noise_from_psd(length, delta_t, psd, seed=None):
    """ Create noise with a given psd.

    Return noise with a given psd. Note that if unique noise is desired
    a unique seed should be provided.

    Parameters
    ----------
    length : int
        The length of noise to generate in samples.
    delta_t : float
        The time step of the noise.
    psd : FrequencySeries
        The noise weighting to color the noise.
    seed : {0, int}
        The seed to generate the noise.

    Returns
    --------
    noise : TimeSeries
        A TimeSeries containing gaussian noise colored by the given psd.
    """
    noise_ts = TimeSeries(zeros(length), delta_t=delta_t)

    if seed is None:
        seed = numpy.random.randint(2**32)

    randomness = lal.gsl_rng("ranlux", seed)

    N = int (1.0 / delta_t / psd.delta_f)
    n = N/2+1
    stride = N/2

    if n > len(psd):
        raise ValueError("PSD not compatible with requested delta_t")

    psd = (psd[0:n]).lal()
    psd.data.data[n-1] = 0

    segment = TimeSeries(zeros(N), delta_t=delta_t).lal()
    length_generated = 0

    SimNoise(segment, 0, psd, randomness)
    while (length_generated < length):
        if (length_generated + stride) < length:
            noise_ts.data[length_generated:length_generated+stride] = segment.data.data[0:stride]
        else:
            noise_ts.data[length_generated:length] = segment.data.data[0:length-length_generated]

        length_generated += stride
        SimNoise(segment, stride, psd, randomness)

    return noise_ts
Beispiel #4
0
 def test_injection_presence(self):
     """Verify presence of signals at expected times"""
     injections = InjectionSet(self.inj_file.name)
     for det in self.detectors:
         for inj in self.injections:
             ts = TimeSeries(numpy.zeros(10 * self.sample_rate),
                             delta_t=1/self.sample_rate,
                             epoch=lal.LIGOTimeGPS(inj.end_time - 5),
                             dtype=numpy.float64)
             injections.apply(ts, det.name)
             max_amp, max_loc = ts.abs_max_loc()
             # FIXME could test amplitude and time more precisely
             self.assertTrue(max_amp > 0 and max_amp < 1e-10)
             time_error = ts.sample_times.numpy()[max_loc] - inj.end_time
             self.assertTrue(abs(time_error) < 1.5 * self.earth_time)
Beispiel #5
0
 def test_injection_absence(self):
     """Verify absence of signals outside known injection times"""
     clear_times = [
         self.injections[0].end_time - 86400,
         self.injections[-1].end_time + 86400
     ]
     injections = InjectionSet(self.inj_file.name)
     for det in self.detectors:
         for epoch in clear_times:
             ts = TimeSeries(numpy.zeros(10 * self.sample_rate),
                             delta_t=1/self.sample_rate,
                             epoch=lal.LIGOTimeGPS(epoch),
                             dtype=numpy.float64)
             injections.apply(ts, det.name)
             max_amp, max_loc = ts.abs_max_loc()
             self.assertEqual(max_amp, 0)
Beispiel #6
0
    def __init__(self, frame_src, 
                       channel_name,
                       start_time,
                       max_buffer=2048, 
                       force_update_cache=True,
                       increment_update_cache=None):
        """ Create a rolling buffer of frame data

        Parameters
        ---------
        frame_src: str of list of strings
            Strings that indicate where to read from files from. This can be a
        list of frame files, a glob, etc.
        channel_name: str
            Name of the channel to read from the frame files
        start_time: 
            Time to start reading from.
        max_buffer: {int, 2048}, Optional
            Length of the buffer in seconds
        """
        self.frame_src = frame_src
        self.channel_name = channel_name
        self.read_pos = start_time
        self.force_update_cache = force_update_cache
        self.increment_update_cache = increment_update_cache

        self.update_cache()
        self.channel_type, self.raw_sample_rate = self._retrieve_metadata(self.stream, self.channel_name)

        raw_size = self.raw_sample_rate * max_buffer
        self.raw_buffer = TimeSeries(zeros(raw_size, dtype=numpy.float64),
                                     copy=False,
                                     epoch=start_time - max_buffer,
                                     delta_t=1.0/self.raw_sample_rate)
Beispiel #7
0
def interpolate_complex_frequency(series, delta_f, zeros_offset=0, side='right'):
    """Interpolate complex frequency series to desired delta_f.

    Return a new complex frequency series that has been interpolated to the
    desired delta_f.

    Parameters
    ----------
    series : FrequencySeries
        Frequency series to be interpolated.
    delta_f : float
        The desired delta_f of the output
    zeros_offset : optional, {0, int}
        Number of sample to delay the start of the zero padding
    side : optional, {'right', str}
        The side of the vector to zero pad
        
    Returns
    -------
    interpolated series : FrequencySeries
        A new FrequencySeries that has been interpolated.
    """
    new_n = int( (len(series)-1) * series.delta_f / delta_f + 1)
    samples = numpy.arange(0, new_n) * delta_f
    old_N = int( (len(series)-1) * 2 )
    new_N = int( (new_n - 1) * 2 )
    time_series = TimeSeries(zeros(old_N), delta_t =1.0/(series.delta_f*old_N),
                             dtype=real_same_precision_as(series))
                             
    ifft(series, time_series)

    time_series.roll(-zeros_offset)
    time_series.resize(new_N)
    
    if side == 'left':
        time_series.roll(zeros_offset + new_N - old_N)
    elif side == 'right':
        time_series.roll(zeros_offset)

    out_series = FrequencySeries(zeros(new_n), epoch=series.epoch,
                           delta_f=delta_f, dtype=series.dtype)
    fft(time_series, out_series)

    return out_series
Beispiel #8
0
def phase_from_polarizations(h_plus, h_cross, remove_start_phase=True):
    """Return gravitational wave phase

    Return the gravitation-wave phase from the h_plus and h_cross
    polarizations of the waveform. The returned phase is always
    positive and increasing with an initial phase of 0.

    Parameters
    ----------
    h_plus : TimeSeries
        An PyCBC TmeSeries vector that contains the plus polarization of the
        gravitational waveform.
    h_cross : TimeSeries
        A PyCBC TmeSeries vector that contains the cross polarization of the
        gravitational waveform.

    Returns
    -------
    GWPhase : TimeSeries
        A TimeSeries containing the gravitational wave phase.

    Examples
    --------s
    >>> from pycbc.waveform import get_td_waveform, phase_from_polarizations
    >>> hp, hc = get_td_waveform(approximant="TaylorT4", mass1=10, mass2=10,
                         f_lower=30, delta_t=1.0/4096)
    >>> phase = phase_from_polarizations(hp, hc)

    """
    p = numpy.unwrap(numpy.arctan2(h_cross.data, h_plus.data)).astype(
        real_same_precision_as(h_plus))
    if remove_start_phase:
        p += -p[0]
    return TimeSeries(p,
                      delta_t=h_plus.delta_t,
                      epoch=h_plus.start_time,
                      copy=False)
Beispiel #9
0
    def test_chirp(self):
        ### use a chirp as a signal


        sigt = TimeSeries(self.sig1, self.del_t)
        sig_tilde = make_frequency_series(sigt)

        del_f = sig_tilde.get_delta_f()
        psd = FrequencySeries(self.Psd, del_f)
        flow = self.low_frequency_cutoff

        with _context:
            hautocor, hacorfr, hnrm = matched_filter_core(self.htilde, self.htilde, psd=psd, \
                        low_frequency_cutoff=flow, high_frequency_cutoff=self.fmax)
            hautocor = hautocor * float(np.real(1./hautocor[0]))

            snr, cor, nrm = matched_filter_core(self.htilde, sig_tilde, psd=psd, \
                        low_frequency_cutoff=flow, high_frequency_cutoff=self.fmax)


        hacor = Array(hautocor, copy=True)
        indx = np.array([352250, 352256, 352260])

        snr = snr*nrm


        with _context:
           dof, achisq, indices= \
               autochisq_from_precomputed(snr, snr, hacor, indx, stride=3,
                                          num_points=20)

        obt_snr = abs(snr[indices[1]])
        obt_ach = achisq[1]
        self.assertTrue(obt_snr > 10.0 and obt_snr < 12.0)
        self.assertTrue(obt_ach < 3.e-3)
        self.assertTrue(achisq[0] > 20.0)
        self.assertTrue(achisq[2] > 20.0)
Beispiel #10
0
def frequency_from_polarizations(h_plus, h_cross):
    """Return gravitational wave frequency

    Return the gravitation-wave frequency as a function of time
    from the h_plus and h_cross polarizations of the waveform.
    It is 1 bin shorter than the input vectors and the sample times
    are advanced half a bin.

    Parameters
    ----------
    h_plus : TimeSeries
        A PyCBC TimeSeries vector that contains the plus polarization of the
        gravitational waveform.
    h_cross : TimeSeries
        A PyCBC TimeSeries vector that contains the cross polarization of the
        gravitational waveform.

    Returns
    -------
    GWFrequency : TimeSeries
        A TimeSeries containing the gravitational wave frequency as a function
        of time.

    Examples
    --------
    >>> from pycbc.waveform import get_td_waveform, phase_from_polarizations
    >>> hp, hc = get_td_waveform(approximant="TaylorT4", mass1=10, mass2=10,
                         f_lower=30, delta_t=1.0/4096)
    >>> freq = frequency_from_polarizations(hp, hc)

    """
    phase = phase_from_polarizations(h_plus, h_cross)
    freq = numpy.diff(phase) / (2 * lal.PI * phase.delta_t)
    start_time = phase.start_time + phase.delta_t / 2
    return TimeSeries(freq.astype(real_same_precision_as(h_plus)),
                      delta_t=phase.delta_t,
                      epoch=start_time)
Beispiel #11
0
def taper_timeseries(tsdata, tapermethod=None, return_lal=False):
    """
    Taper either or both ends of a time series using wrapped
    LALSimulation functions

    Parameters
    ----------
    tsdata : TimeSeries
        Series to be tapered, dtype must be either float32 or float64
    tapermethod : string
        Should be one of ('TAPER_NONE', 'TAPER_START', 'TAPER_END',
        'TAPER_STARTEND', 'start', 'end', 'startend') - NB 'TAPER_NONE' will
        not change the series!
    return_lal : Boolean
        If True, return a wrapped LAL time series object, else return a
        PyCBC time series.
    """
    if tapermethod is None:
        raise ValueError("Must specify a tapering method (function was called"
                         "with tapermethod=None)")
    if tapermethod not in taper_map.keys():
        raise ValueError("Unknown tapering method %s, valid methods are %s" % \
                         (tapermethod, ", ".join(taper_map.keys())))
    if tsdata.dtype not in (float32, float64):
        raise TypeError("Strain dtype must be float32 or float64, not " +
                        str(tsdata.dtype))
    taper_func = taper_func_map[tsdata.dtype]
    # make a LAL TimeSeries to pass to the LALSim function
    ts_lal = tsdata.astype(tsdata.dtype).lal()
    if taper_map[tapermethod] is not None:
        taper_func(ts_lal.data, taper_map[tapermethod])
    if return_lal:
        return ts_lal
    else:
        return TimeSeries(ts_lal.data.data[:],
                          delta_t=ts_lal.deltaT,
                          epoch=ts_lal.epoch)
Beispiel #12
0
def highpass_fir(timeseries, frequency, order, beta=5.0):
    """ Highpass filter the time series using an FIR filtered generated from 
    the ideal response passed through a kaiser window (beta = 5.0)

    Parameters
    ----------
    Time Series: TimeSeries
        The time series to be high-passed.
    frequency: float
        The frequency below which is suppressed. 
    order: int
        Number of corrupted samples on each side of the time series
    beta: float
        Beta parameter of the kaiser window that sets the side lobe attenuation.
    """
    k = frequency / float((int(1.0 / timeseries.delta_t) / 2))
    coeff = scipy.signal.firwin(order * 2 + 1,
                                k,
                                window=('kaiser', beta),
                                pass_zero=False)
    data = fir_zero_filter(coeff, timeseries)
    return TimeSeries(data,
                      epoch=timeseries.start_time,
                      delta_t=timeseries.delta_t)
Beispiel #13
0
class DataBuffer(object):

    """A linear buffer that acts as a FILO for reading in frame data
    """

    def __init__(self, frame_src, 
                 channel_name,
                 start_time,
                 max_buffer=2048,
                 force_update_cache=True,
                 increment_update_cache=None,
                 dtype=numpy.float64):
        """ Create a rolling buffer of frame data

        Parameters
        ---------
        frame_src: str of list of strings
            Strings that indicate where to read from files from. This can be a
        list of frame files, a glob, etc.
        channel_name: str
            Name of the channel to read from the frame files
        start_time: 
            Time to start reading from.
        max_buffer: {int, 2048}, Optional
            Length of the buffer in seconds
        dtype: {dtype, numpy.float32}, Optional
            Data type to use for the interal buffer
        """
        self.frame_src = frame_src
        self.channel_name = channel_name
        self.read_pos = start_time
        self.force_update_cache = force_update_cache
        self.increment_update_cache = increment_update_cache
        self.detector = channel_name.split(':')[0]

        self.update_cache()
        self.channel_type, self.raw_sample_rate = self._retrieve_metadata(self.stream, self.channel_name)

        raw_size = self.raw_sample_rate * max_buffer
        self.raw_buffer = TimeSeries(zeros(raw_size, dtype=dtype),
                                     copy=False,
                                     epoch=start_time - max_buffer,
                                     delta_t=1.0/self.raw_sample_rate)

    def update_cache(self):
        """Reset the lal cache. This can be used to update the cache if the 
        result may change due to more files being added to the filesystem, 
        for example.
        """
        cache = locations_to_cache(self.frame_src)
        stream = lalframe.FrStreamCacheOpen(cache)
        self.stream = stream

    @staticmethod
    def _retrieve_metadata(stream, channel_name):
        """Retrieve basic metadata by reading the first file in the cache
    
        Parameters
        ----------
        stream: lal stream object
            Stream containing a channel we want to learn about
        channel_name: str
            The name of the channel we want to know the dtype and sample rate of

        Returns
        -------
        channel_type: lal type enum
            Enum value which indicates the dtype of the channel
        sample_rate: int
            The sample rate of the data within this channel
        """
        lalframe.FrStreamGetVectorLength(channel_name, stream)
        channel_type = lalframe.FrStreamGetTimeSeriesType(channel_name, stream)
        create_series_func = _fr_type_map[channel_type][2]
        get_series_metadata_func = _fr_type_map[channel_type][3]
        series = create_series_func(channel_name, stream.epoch, 0, 0,
                            lal.ADCCountUnit, 0)
        get_series_metadata_func(series, stream)
        return channel_type, int(1.0/series.deltaT)

    def _read_frame(self, blocksize):
        """Try to read the block of data blocksize seconds long

        Parameters
        ----------
        blocksize: int
            The number of seconds to attempt to read from the channel

        Returns
        -------
        data: TimeSeries
            TimeSeries containg 'blocksize' seconds of frame data

        Raises
        ------
        RuntimeError:
            If data cannot be read for any reason
        """
        try:
            read_func = _fr_type_map[self.channel_type][0]
            dtype = _fr_type_map[self.channel_type][1]
            data = read_func(self.stream, self.channel_name,
                             self.read_pos, int(blocksize), 0)
            return TimeSeries(data.data.data, delta_t=data.deltaT,
                              epoch=self.read_pos, 
                              dtype=dtype)     
        except Exception:
            raise RuntimeError('Cannot read {0} frame data'.format(self.channel_name))

    def null_advance(self, blocksize):
        """Advance and insert zeros

        Parameters
        ----------
        blocksize: int
            The number of seconds to attempt to read from the channel
        """
        self.raw_buffer.roll(-int(blocksize * self.raw_sample_rate))
        self.read_pos += blocksize       
        self.raw_buffer.start_time += blocksize

    def advance(self, blocksize):
        """Add blocksize seconds more to the buffer, push blocksize seconds
        from the beginning.

        Parameters
        ----------
        blocksize: int
            The number of seconds to attempt to read from the channel
        """
        ts = self._read_frame(blocksize)

        self.raw_buffer.roll(-len(ts))
        self.raw_buffer[-len(ts):] = ts[:] 
        self.read_pos += blocksize
        self.raw_buffer.start_time += blocksize
        return ts
        
    def update_cache_by_increment(self, blocksize):
        """Update the internal cache by starting from the first frame
        and incrementing.

        Guess the next frame file name by incrementing from the first found
        one. This allows a pattern to be used for the GPS folder of the file,
        which is indicated by `GPSX` where x is the number of digits to use.

        Parameters
        ----------
        blocksize: int
            Number of seconds to increment the next frame file.
        """
        start = float(self.raw_buffer.end_time)
        end = float(start + blocksize)
        
        if not hasattr(self, 'dur'):       
            fname = glob.glob(self.frame_src[0])[0]
            fname = os.path.splitext(os.path.basename(fname))[0].split('-')
            
            self.beg = '-'.join([fname[0], fname[1]])
            self.ref = int(fname[2])
            self.dur = int(fname[3])
        
        fstart = int(self.ref + numpy.floor((start - self.ref) / float(self.dur)) * self.dur)
        starts = numpy.arange(fstart, end, self.dur).astype(numpy.int)
        
        keys = []
        for s in starts:
            pattern = self.increment_update_cache
            if 'GPS' in pattern:
                n = int(pattern[int(pattern.index('GPS') + 3)])
                pattern = pattern.replace('GPS%s' % n, str(s)[0:n])
                
            name = '%s/%s-%s-%s.gwf' % (pattern, self.beg, s, self.dur)
            # check that file actually exists, else abort now
            if not os.path.exists(name):
                logging.info("%s does not seem to exist yet" % name)
                raise RuntimeError

            keys.append(name)
        cache = locations_to_cache(keys)
        stream = lalframe.FrStreamCacheOpen(cache)
        self.stream = stream
        self.channel_type, self.raw_sample_rate = \
            self._retrieve_metadata(self.stream, self.channel_name)

    def attempt_advance(self, blocksize, timeout=10):
        """ Attempt to advance the frame buffer. Retry upon failure, except
        if the frame file is beyond the timeout limit.

        Parameters
        ----------
        blocksize: int
            The number of seconds to attempt to read from the channel
        timeout: {int, 10}, Optional
            Number of seconds before giving up on reading a frame

        Returns
        -------
        data: TimeSeries
            TimeSeries containg 'blocksize' seconds of frame data
        """
        if self.force_update_cache:
            self.update_cache()
        
        try:
            if self.increment_update_cache:
                self.update_cache_by_increment(blocksize)

            return DataBuffer.advance(self, blocksize)

        except RuntimeError:
            if lal.GPSTimeNow() > timeout + self.raw_buffer.end_time:
                # The frame is not there and it should be by now, so we give up
                # and treat it as zeros
                DataBuffer.null_advance(self, blocksize)
                return None
            else:
                # I am too early to give up on this frame, so we should try again
                time.sleep(1)
                return self.attempt_advance(blocksize, timeout=timeout)
Beispiel #14
0
def get_td_lm_allmodes(template=None, taper=None, **kwargs):
    """Return time domain ringdown with all the modes specified.

    Parameters
    ----------
    template: object
        An object that has attached properties. This can be used to substitute
        for keyword arguments. A common example would be a row in an xml table.
    taper: {None, float}, optional
        Tapering at the beginning of the waveform with duration taper * tau.
        This option is recommended with timescales taper=1./2 or 1. for
        time-domain ringdown-only injections.
        The abrupt turn on of the ringdown can cause issues on the waveform
        when doing the fourier transform to the frequency domain. Setting
        taper will add a rapid ringup with timescale tau/10.
        Each mode and overtone will have a different taper depending on its tau,
        the final taper being the superposition of all the tapers.
    final_mass : float
        Mass of the final black hole.
    final_spin : float
        Spin of the final black hole.
    lmns : list
        Desired lmn modes as strings (lm modes available: 22, 21, 33, 44, 55).
        The n specifies the number of overtones desired for the corresponding
        lm pair (maximum n=8).
        Example: lmns = ['223','331'] are the modes 220, 221, 222, and 330
    amp220 : float
        Amplitude of the fundamental 220 mode.
    amplmn : float
        Fraction of the amplitude of the lmn overtone relative to the 
        fundamental mode, as many as the number of subdominant modes.
    philmn : float
        Phase of the lmn overtone, as many as the number of modes.
    delta_t : {None, float}, optional
        The time step used to generate the ringdown.
        If None, it will be set to the inverse of the frequency at which the
        amplitude is 1/1000 of the peak amplitude (the minimum of all modes).
    t_final : {None, float}, optional
        The ending time of the output frequency series.
        If None, it will be set to the time at which the amplitude
        is 1/1000 of the peak amplitude (the maximum of all modes).

    Returns
    -------
    hplustilde: FrequencySeries
        The plus phase of a ringdown with the lm modes specified and
        n overtones in frequency domain.
    hcrosstilde: FrequencySeries
        The cross phase of a ringdown with the lm modes specified and
        n overtones in frequency domain.
    """

    input_params = props(template, lm_allmodes_required_args, **kwargs)

    # Get required args
    final_mass = input_params['final_mass']
    final_spin = input_params['final_spin']
    lmns = input_params['lmns']
    for lmn in lmns:
        if int(lmn[2]) == 0:
            raise ValueError('Number of overtones (nmodes) must be greater '
                             'than zero.')
    # following may not be in input_params
    delta_t = input_params.pop('delta_t', None)
    t_final = input_params.pop('t_final', None)

    if delta_t is None:
        delta_t = lm_deltat(final_mass, final_spin, lmns)
    if t_final is None:
        t_final = lm_tfinal(final_mass, final_spin, lmns)

    kmax = int(t_final / delta_t) + 1
    _, tau = get_lm_f0tau_allmodes(final_mass, final_spin, lmns)
    # Different overtones will have different tapering window-size
    # Find maximum window size to create long enough output vector
    if taper is not None:
        taper_window = int(taper * max(tau.values()) / delta_t)
        kmax += taper_window

    outplus = TimeSeries(zeros(kmax, dtype=float64), delta_t=delta_t)
    outcross = TimeSeries(zeros(kmax, dtype=float64), delta_t=delta_t)
    for lmn in lmns:
        l, m, nmodes = int(lmn[0]), int(lmn[1]), int(lmn[2])
        hplus, hcross = get_td_lm(taper=taper,
                                  l=l,
                                  m=m,
                                  nmodes=nmodes,
                                  delta_t=delta_t,
                                  t_final=t_final,
                                  **input_params)
        if taper is None:
            outplus.data += hplus.data
            outcross.data += hcross.data
        else:
            outplus = taper_shift(hplus, outplus)
            outcross = taper_shift(hcross, outcross)

    return outplus, outcross
Beispiel #15
0
def get_td_qnm(template=None, taper=None, **kwargs):
    """Return a time domain damped sinusoid.

    Parameters
    ----------
    template: object
        An object that has attached properties. This can be used to substitute
        for keyword arguments. A common example would be a row in an xml table.
    taper: {None, float}, optional
        Tapering at the beginning of the waveform with duration taper * tau.
        This option is recommended with timescales taper=1./2 or 1. for
        time-domain ringdown-only injections.
        The abrupt turn on of the ringdown can cause issues on the waveform
        when doing the fourier transform to the frequency domain. Setting
        taper will add a rapid ringup with timescale tau/10.
    f_0 : float
        The ringdown-frequency.
    tau : float
        The damping time of the sinusoid.
    phi : float
        The initial phase of the ringdown.
    amp : float
        The amplitude of the ringdown (constant for now).
    delta_t : {None, float}, optional
        The time step used to generate the ringdown.
        If None, it will be set to the inverse of the frequency at which the
        amplitude is 1/1000 of the peak amplitude.
    t_final : {None, float}, optional
        The ending time of the output time series.
        If None, it will be set to the time at which the amplitude is 
        1/1000 of the peak amplitude.

    Returns
    -------
    hplus: TimeSeries
        The plus phase of the ringdown in time domain.
    hcross: TimeSeries
        The cross phase of the ringdown in time domain.
    """

    input_params = props(template, qnm_required_args, **kwargs)

    f_0 = input_params.pop('f_0')
    tau = input_params.pop('tau')
    amp = input_params.pop('amp')
    phi = input_params.pop('phi')
    # the following may not be in input_params
    delta_t = input_params.pop('delta_t', None)
    t_final = input_params.pop('t_final', None)

    if delta_t is None:
        delta_t = 1. / qnm_freq_decay(f_0, tau, 1. / 1000)
        if delta_t < min_dt:
            delta_t = min_dt
    if t_final is None:
        t_final = qnm_time_decay(tau, 1. / 1000)
    kmax = int(t_final / delta_t) + 1

    times = numpy.arange(kmax) * delta_t

    hp = amp * numpy.exp(-times / tau) * numpy.cos(two_pi * f_0 * times + phi)
    hc = amp * numpy.exp(-times / tau) * numpy.sin(two_pi * f_0 * times + phi)

    # If size of tapering window is less than delta_t, do not apply taper.
    if taper is None or delta_t > taper * tau:
        hplus = TimeSeries(zeros(kmax), delta_t=delta_t)
        hcross = TimeSeries(zeros(kmax), delta_t=delta_t)
        hplus.data[:kmax] = hp
        hcross.data[:kmax] = hc

        return hplus, hcross

    else:
        taper_hp, taper_hc, taper_window, start = apply_taper(
            delta_t, taper, f_0, tau, amp, phi)
        hplus = TimeSeries(zeros(taper_window + kmax), delta_t=delta_t)
        hcross = TimeSeries(zeros(taper_window + kmax), delta_t=delta_t)
        hplus.data[:taper_window] = taper_hp
        hplus.data[taper_window:] = hp
        hplus._epoch = start
        hcross.data[:taper_window] = taper_hc
        hcross.data[taper_window:] = hc
        hcross._epoch = start

        return hplus, hcross
Beispiel #16
0
def get_td_lm(template=None, taper=None, **kwargs):
    """Return time domain lm mode with the given number of overtones.

    Parameters
    ----------
    template: object
        An object that has attached properties. This can be used to substitute
        for keyword arguments. A common example would be a row in an xml table.
    taper: {None, float}, optional
        Tapering at the beginning of the waveform with duration taper * tau.
        This option is recommended with timescales taper=1./2 or 1. for
        time-domain ringdown-only injections.
        The abrupt turn on of the ringdown can cause issues on the waveform
        when doing the fourier transform to the frequency domain. Setting
        taper will add a rapid ringup with timescale tau/10.
        Each overtone will have a different taper depending on its tau, the
        final taper being the superposition of all the tapers.
    freqs : dict
        {lmn:f_lmn} Dictionary of the central frequencies for each overtone,
        as many as number of modes.
    taus : dict
        {lmn:tau_lmn} Dictionary of the damping times for each overtone,
        as many as number of modes.
    l : int
        l mode (lm modes available: 22, 21, 33, 44, 55).
    m : int
        m mode (lm modes available: 22, 21, 33, 44, 55).
    nmodes: int
        Number of overtones desired (maximum n=8)
    amp220 : float
        Amplitude of the fundamental 220 mode, needed for any lm.
    amplmn : float
        Fraction of the amplitude of the lmn overtone relative to the
        fundamental mode, as many as the number of subdominant modes.
    philmn : float
        Phase of the lmn overtone, as many as the number of modes. Should also
        include the information from the azimuthal angle (phi + m*Phi).
    inclination : {None, float}, optional
        Inclination of the system in radians for the spherical harmonics.
    delta_t : {None, float}, optional
        The time step used to generate the ringdown.
        If None, it will be set to the inverse of the frequency at which the
        amplitude is 1/1000 of the peak amplitude (the minimum of all modes).
    t_final : {None, float}, optional
        The ending time of the output time series.
        If None, it will be set to the time at which the amplitude is
        1/1000 of the peak amplitude (the maximum of all modes).

    Returns
    -------
    hplus: TimeSeries
        The plus phase of a lm mode with overtones (n) in time domain.
    hcross: TimeSeries
        The cross phase of a lm mode with overtones (n) in time domain.
    """

    input_params = props(template, lm_required_args, **kwargs)

    # Get required args
    amps, phis = lm_amps_phases(**input_params)
    f_0 = input_params.pop('freqs')
    tau = input_params.pop('taus')
    inc = input_params.pop('inclination', None)
    l, m = input_params.pop('l'), input_params.pop('m')
    nmodes = input_params.pop('nmodes')
    if int(nmodes) == 0:
        raise ValueError('Number of overtones (nmodes) must be greater '
                         'than zero.')
    # The following may not be in input_params
    delta_t = input_params.pop('delta_t', None)
    t_final = input_params.pop('t_final', None)

    if not delta_t:
        delta_t = lm_deltat(f_0, tau, ['%d%d%d' %(l,m,nmodes)])
    if not t_final:
        t_final = lm_tfinal(tau, ['%d%d%d' %(l, m, nmodes)])

    kmax = int(t_final / delta_t) + 1
    # Different overtones will have different tapering window-size
    # Find maximum window size to create long enough output vector
    if taper:
        taper_window = int(taper*max(tau.values())/delta_t)
        kmax += taper_window

    outplus = TimeSeries(zeros(kmax, dtype=float64), delta_t=delta_t)
    outcross = TimeSeries(zeros(kmax, dtype=float64), delta_t=delta_t)
    if taper:
        start = - taper * max(tau.values())
        outplus._epoch, outcross._epoch = start, start

    for n in range(nmodes):
        hplus, hcross = get_td_qnm(template=None, taper=taper,
                            f_0=f_0['%d%d%d' %(l,m,n)],
                            tau=tau['%d%d%d' %(l,m,n)],
                            phi=phis['%d%d%d' %(l,m,n)],
                            amp=amps['%d%d%d' %(l,m,n)],
                            inclination=inc, l=l, m=m,
                            delta_t=delta_t, t_final=t_final)
        if not taper:
            outplus.data += hplus.data
            outcross.data += hcross.data
        else:
            outplus = taper_shift(hplus, outplus)
            outcross = taper_shift(hcross, outcross)

    return outplus, outcross
Beispiel #17
0
def calc_psd_variation(strain, psd_short_segment, psd_long_segment, overlap,
                       low_freq, high_freq):
    """Calculates time series of PSD variability

    This function first splits the segment up in to 512 second chunks. It
    then calculates the PSD over this 512 second period as well as in 4
    second chunks throughout each 512 second period. Next the function
    estimates how different the 4 second PSD is to the 512 second PSD and
    produces a timeseries of this variability.

    Parameters
    ----------
    strain : TimeSeries
        Input strain time series to estimate PSDs
    psd_short_segment : {float, 4}
        Duration of the short segments for PSD estimation in seconds.
    psd_long_segment : {float, 512}
        Duration of the long segments for PSD estimation in seconds.
    overlap : {float, 0.5}
        Duration in seconds to use for each sample of the PSD.
    low_freq : {float, 20}
        Minimum frequency to consider the comparison between PSDs.
    high_freq : {float, 512}
        Maximum frequency to consider the comparison between PSDs.

    Returns
    -------
    psd_var : TimeSeries
        Time series of the variability in the PSD estimation
    """

    # Calculate strain precision
    if strain.precision == 'single':
        fs_dtype = numpy.float32
    elif strain.precision == 'double':
        fs_dtype = numpy.float64

    # Convert start and end times immediately to floats
    start_time = numpy.float(strain.start_time)
    end_time = numpy.float(strain.end_time)

    # Find the times of the long segments
    times_long = numpy.arange(start_time, end_time, psd_long_segment)

    # Set up the empty time series for the PSD variation estimate
    psd_var = TimeSeries(zeros(
        int(numpy.ceil((end_time - start_time) / psd_short_segment))),
                         delta_t=psd_short_segment,
                         copy=False,
                         epoch=start_time)

    ind = 0
    for tlong in times_long:
        # Calculate PSD for long segment and separate the long segment in to
        # overlapping shorter segments
        if tlong + psd_long_segment <= end_time:
            psd_long = strain.time_slice(tlong,
                                         tlong + psd_long_segment).psd(overlap)
            times_short = numpy.arange(tlong, tlong + psd_long_segment,
                                       psd_short_segment)
        else:
            psd_long = strain.time_slice(end_time - psd_long_segment,
                                         end_time).psd(overlap)
            times_short = numpy.arange(tlong, end_time, psd_short_segment)
        # Calculate the PSD of the shorter segments
        psd_short = []
        for tshort in times_short:
            if tshort + psd_short_segment * 2 <= end_time:
                pshort = strain.time_slice(tshort, tshort +
                                           psd_short_segment * 2).psd(overlap)
            else:
                pshort = strain.time_slice(tshort - psd_short_segment,
                                           end_time).psd(overlap)
            psd_short.append(pshort)
        # Estimate the range of the PSD to compare
        kmin = int(low_freq / psd_long.delta_f)
        kmax = int(high_freq / psd_long.delta_f)
        # Comapre the PSD of the short segment to the long segment
        # The weight factor gives the rough response of a cbc template across
        # the defined frequency range given the expected PSD (i.e. long PSD)
        # Then integrate the weighted ratio of the actual PSD (i.e. short PSD)
        # with the expected PSD (i.e. long PSD) over the specified frequency
        # range
        freqs = FrequencySeries(psd_long.sample_frequencies,
                                delta_f=psd_long.delta_f,
                                epoch=psd_long.epoch,
                                dtype=fs_dtype)
        weight = numpy.array(freqs[kmin:kmax]**(-7. / 3.) /
                             psd_long[kmin:kmax])
        weight /= weight.sum()
        diff = numpy.array([
            (weight *
             numpy.array(p_short[kmin:kmax] / psd_long[kmin:kmax])).sum()
            for p_short in psd_short
        ])

        # Store variation value
        for i, val in enumerate(diff):
            psd_var[ind + i] = val

        ind = ind + len(diff)

    return psd_var
Beispiel #18
0
def power_chisq_from_precomputed(corr,
                                 snr,
                                 snr_norm,
                                 bins,
                                 indices=None,
                                 return_bins=False):
    """Calculate the chisq timeseries from precomputed values.

    This function calculates the chisq at all times by performing an
    inverse FFT of each bin.

    Parameters
    ----------

    corr: FrequencySeries
        The produce of the template and data in the frequency domain.
    snr: TimeSeries
        The unnormalized snr time series.
    snr_norm:
        The snr normalization factor (true snr = snr * snr_norm) EXPLAINME - define 'true snr'?
    bins: List of integers
        The edges of the chisq bins.
    indices: {Array, None}, optional
        Index values into snr that indicate where to calculate
        chisq values. If none, calculate chisq for all possible indices.
    return_bins: {boolean, False}, optional
        Return a list of the SNRs for each chisq bin.

    Returns
    -------
    chisq: TimeSeries
    """
    # Get workspace memory
    global _q_l, _qtilde_l, _chisq_l

    bin_snrs = []

    if _q_l is None or len(_q_l) != len(snr):
        q = zeros(len(snr), dtype=complex_same_precision_as(snr))
        qtilde = zeros(len(snr), dtype=complex_same_precision_as(snr))
        _q_l = q
        _qtilde_l = qtilde
    else:
        q = _q_l
        qtilde = _qtilde_l

    if indices is not None:
        snr = snr.take(indices)

    if _chisq_l is None or len(_chisq_l) < len(snr):
        chisq = zeros(len(snr), dtype=real_same_precision_as(snr))
        _chisq_l = chisq
    else:
        chisq = _chisq_l[0:len(snr)]
        chisq.clear()

    num_bins = len(bins) - 1

    for j in range(num_bins):
        k_min = int(bins[j])
        k_max = int(bins[j + 1])

        qtilde[k_min:k_max] = corr[k_min:k_max]
        pycbc.fft.ifft(qtilde, q)
        qtilde[k_min:k_max].clear()

        if return_bins:
            bin_snrs.append(
                TimeSeries(q * snr_norm * num_bins**0.5,
                           delta_t=snr.delta_t,
                           epoch=snr.start_time))

        if indices is not None:
            chisq_accum_bin(chisq, q.take(indices))
        else:
            chisq_accum_bin(chisq, q)

    chisq = (chisq * num_bins - snr.squared_norm()) * (snr_norm**2.0)

    if indices is None:
        chisq = TimeSeries(chisq,
                           delta_t=snr.delta_t,
                           epoch=snr.start_time,
                           copy=False)

    if return_bins:
        return chisq, bin_snrs
    else:
        return chisq
Beispiel #19
0
from pycbc.psd import welch, interpolate
from pycbc.types import TimeSeries
try:
    from urllib.request import urlretrieve
except ImportError:  # python < 3
    from urllib import urlretrieve

# Read data and remove low frequency content
fname = 'H-H1_LOSC_4_V2-1126259446-32.gwf'
url = "https://www.gw-openscience.org/GW150914data/" + fname
urlretrieve(url, filename=fname)
h1 = highpass_fir(read_frame(fname, 'H1:LOSC-STRAIN'), 15.0, 8)

# Calculate the noise spectrum and whiten
psd = interpolate(welch(h1), 1.0 / 32)
white_strain = (h1.to_frequencyseries() / psd ** 0.5 * psd.delta_f).to_timeseries()

# remove some of the high and low frequencies
smooth = highpass_fir(white_strain, 25, 8)
smooth = lowpass_fir(white_strain, 250, 8)

#strech out and shift the frequency upwards to aid human hearing
fdata = smooth.to_frequencyseries()
fdata.roll(int(1200 / fdata.delta_f))
smooth = TimeSeries(fdata.to_timeseries(), delta_t=1.0/1024)

#Take slice around signal
smooth = smooth[len(smooth)/2 - 1500:len(smooth)/2 + 3000]
smooth.save_to_wav('gw150914_h1_chirp.wav')

    def __init__(self, filename=None, filetype='HDF5', wavetype='Auto', \
          ex_order=3, group_name=None,\
          modeLmin=2, modeLmax=8, skipM0=True, \
          sample_rate=8192, time_length=32, rawdelta_t=-1, \
          totalmass=None, inclination=0, phi=0, distance=1.e6, \
          verbose=False, debug=False):
        """
    #### Assumptions:
    ### 1. The nr waveform file is uniformly sampled
    ### 2. wavetypes passed should be :
    ###     CCE , Extrapolated , FiniteRadius , NoGroup, Auto
    ###     Auto : from filename figure out the file type
    ### 3. filetypes passed should be : ASCII , HDF5 , DataSet

    ###### About conventions:
    ### 1. Modes themselves are not amplitude-scaled. Only their time-axis is
    ###    rescaled.
    ### 2. ...

        """
        #{{{
        ##################################################################
        #   0. Ensure inputs are correct
        ##################################################################
        if 'dataset' not in filetype:
            if filename is not None and not os.path.exists(filename):
                raise IOError("Please provide data file!")
            if verbose:
                print >>sys.stderr, "\n Reading From Filename=%s" % filename

        # Datafile details
        self.verbose = verbose
        self.debug   = debug
        self.filename = filename
        self.filetype = filetype
        self.modeLmax = modeLmax
        self.modeLmin = modeLmin
        self.skipM0   = skipM0

        # Extraction parameters
        self.ex_order   = ex_order
        self.group_name = group_name

        # Data analysis parameters
        self.sample_rate = sample_rate
        self.time_length = time_length
        self.dt = 1./self.sample_rate
        self.df = 1./self.time_length
        self.n = int(self.sample_rate * self.time_length)
        if self.verbose:
            print >>sys.stderr, "self.sample-rate & time_len = ", \
                    self.sample_rate, self.time_length
        if self.verbose: print >>sys.stderr, "self.n = ", self.n

        # Binary parameters
        self.totalmass = None
        self.inclination = inclination
        self.phi = phi
        self.distance = distance
        if self.verbose:
            print >>sys.stderr, " >> Input mass, inc, phi, dist = ", totalmass,\
                                self.inclination, self.phi, self.distance

        sys.stderr.flush()
        ##################################################################
        #   1. Figure out what the data-storage type (wavetype) is
        ##################################################################
        self.wavetype = None
        if str(wavetype) in ['CCE', 'Extrapolated', 'FiniteRadius', 'NoGroup']:
            self.wavetype = wavetype
        elif str(wavetype) == 'Auto':
            # Decide from filename the wavetype
            fname = self.filename.split('/')[-1]
            if 'rhOverM_Asymptotic_GeometricUnits' in fname:
                self.wavetype = 'Extrapolated'
            elif 'Cce' in fname: self.wavetype = 'CCE'
            elif 'FiniteRadii' in fname: self.wavetype = 'FiniteRadius'
            elif 'HDF' in self.filetype:
                ftmp = h5py.File(self.filename, 'r')
                fgrps = [str(grptmp) for grptmp in fgrpstmp]
                if 'Y_l2_m2.dat' in fgrps: self.wavetype = 'NoGroup'
        #
        if self.wavetype == None: raise IOError("Could not figure out wavetype")
        if self.verbose: print >>sys.stderr, self.wavetype

        sys.stderr.flush()
        ##################################################################
        #   2. Read the data from the file. Read all modes.
        ##################################################################
        #
        if 'HDF' in self.filetype:
            if self.verbose:
                print >>sys.stderr, " > Reading NR data in HDF5 from %s" % self.filename

            # Read data file
            self.fin = h5py.File(self.filename,'r')
            if self.wavetype != 'NoGroup':
                grp = self.get_groupname()
                print "FOUND GROUP = ", grp, type(grp)
                if self.verbose:
                    print >>sys.stderr, ("From %s out of " % grp), self.fin.keys()
                wavedata = self.fin[grp]
            else: wavedata = self.fin

            # Read modes
            self.rawtsamples, self.rawmodes_real, self.rawmodes_imag = {}, {}, {}
            for modeL in np.arange( 2, self.modeLmax+1 ):
                self.rawtsamples[modeL] = {}
                self.rawmodes_real[modeL], self.rawmodes_imag[modeL] = {}, {}
                for modeM in np.arange( modeL, -1*modeL-1, -1 ):
                    if self.skipM0 and modeM==0: continue
                    mdata = wavedata['Y_l%d_m%d.dat' % (modeL, modeM)].value
                    if self.verbose: print >> sys.stderr, "Reading %d,%d mode" % (modeL, modeM)
                    #
                    ts = mdata[:,0]
                    hp_int = InterpolatedUnivariateSpline(ts, mdata[:,1])
                    hc_int = InterpolatedUnivariateSpline(ts, mdata[:,2])

                    # Hard-coded to re-sample at initial dt
                    if rawdelta_t <= 0: self.rawdelta_t = ts[1] - ts[0]
                    else: self.rawdelta_t = rawdelta_t

                    #
                    self.rawtsamples[modeL][modeM] = TimeSeries(\
                                      np.arange(ts.min(), ts.max(), self.rawdelta_t),\
                                      delta_t=self.rawdelta_t, epoch=0)
                    self.rawmodes_real[modeL][modeM] = TimeSeries(\
                                      hp_int( self.rawtsamples[2][2] ),\
                                      delta_t=self.rawdelta_t, epoch=0)
                    self.rawmodes_imag[modeL][modeM] = TimeSeries(\
                                      hc_int( self.rawtsamples[2][2] ),\
                                      delta_t=self.rawdelta_t, epoch=0)
        #
        elif 'dataset' in self.filetype:
            raise IOError("datasets not supported yet!")
            data = self.filename
            ts = data[:,0] - data[0,0]
            hp_int = InterpolatedUnivariateSpline(ts, data[:,1])
            hc_int = InterpolatedUnivariateSpline(ts, data[:,2])
            # Hard-coded to re-sample at dt = 1M
            if rawdelta_t <= 0: self.rawdelta_t = 1.
            else: self.rawdelta_t = rawdelta_t
            self.rawtsamples = TimeSeries(arange(0, ts.max()),\
                                          delta_t=self.rawdelta_t, epoch=0)
            self.rawhp = TimeSeries(array([hp_int(t) for t in self.rawtsamples]),\
                                          delta_t=self.rawdelta_t, epoch=0)
            self.rawhc = TimeSeries(array([hc_int(t) for t in self.rawtsamples]),\
                                          delta_t=self.rawdelta_t, epoch=0)
            if self.verbose:
              print >>sys.stderr, "times go from %f to %f" % (min(ts), max(ts))
              print >>sys.stderr, "rawhp Min = %e, Max = %e" % (min(self.rawhp), max(self.rawhp))
            #
        elif 'ASCII' in self.filetype:
            raise IOError("ASCII datafile not accepted yet!")
            if self.verbose: print >>sys.stderr, "Reading NR data in ASCII from %s" % \
                                              self.filename

            # Read modes
            self.rawtsamples, self.rawmodes_real, self.rawmodes_imag = {}, {}, {}
            for modeL in np.arange( 2, self.modeLmax+1 ):
                self.rawtsamples[modeL] = {}
                self.rawmodes_real[modeL], self.rawmodes_imag[modeL] = {}, {}
                for modeM in np.arange( -1*modeL, modeL+1 ):
                    if self.skipM0 and modeM==0: continue
                    mdata = np.loadtxt(self.filename % (modeL, modeM))
                    if self.verbose: print >> sys.stderr, np.shape(mdata)
                    #
                    ts = mdata[:,0]
                    hp_int = InterpolatedUnivariateSpline(ts, mdata[:,1])
                    hc_int = InterpolatedUnivariateSpline(ts, mdata[:,2])

                    # Hard-coded to re-sample at initial dt
                    if rawdelta_t <= 0: self.rawdelta_t = ts[1] - ts[0]
                    else: self.rawdelta_t = rawdelta_t

                    #
                    self.rawtsamples[modeL][modeM] = TimeSeries(\
                                            np.arange(ts.min(), ts.max(), self.rawdelta_t),\
                                            delta_t=self.rawdelta_t, epoch=0)
                    self.rawmodes_real[modeL][modeM] = TimeSeries(\
                                            array([hp_int(t) for t in self.rawtsamples]),\
                                            delta_t=self.rawdelta_t, epoch=0)
                    self.rawmodes_imag[modeL][modeM] = TimeSeries(\
                                            array([hc_int(t) for t in self.rawtsamples]),\
                                            delta_t=self.rawdelta_t, epoch=0)
        #
        self.rescaled_hp = None
        self.rescaled_hc = None
        if totalmass:
            self.rescale_to_totalmass(totalmass)
            self.totalmass = totalmass

        try: self.fin.close()
        except: pass
Beispiel #21
0
def get_td_qnm(template=None, delta_t=None, t_lower=None, t_final=None, **kwargs):
    """Return a time domain damped sinusoid.

    Parameters
    ----------
    template: object
        An object that has attached properties. This can be used to substitute
        for keyword arguments. A common example would be a row in an xml table.
    f_0 : float
        The ringdown-frequency.
    tau : float
        The damping time of the sinusoid.
    t_0 :  {0, float}, optional
        The starting time of the ringdown.
    phi_0 : {0, float}, optional
        The initial phase of the ringdown.
    Amp : {1, float}, optional
        The amplitude of the ringdown (constant for now).
    delta_t : {None, float}, optional
        The time step used to generate the ringdown.
        If None, it will be set to the inverse of the frequency at which the
        amplitude is 1/100 of the peak amplitude.
    t_lower: {None, float}, optional
        The starting time of the output time series.
        If None, it will be set to delta_t.
    t_final : {None, float}, optional
        The ending time of the output time series.
        If None, it will be set to the time at which the amplitude is 
        1/1000 of the peak amplitude.

    Returns
    -------
    hplus: TimeSeries
        The plus phase of the ringdown in time domain.
    hcross: TimeSeries
        The cross phase of the ringdown in time domain.
    """

    input_params = props_ringdown(template,**kwargs)

    f_0 = input_params['f_0']
    tau = input_params['tau']
    t_0 = input_params['t_0']
    phi_0 = input_params['phi_0']
    Amp = input_params['Amp']
    if delta_t is None:
        delta_t = 1. / qnm_freq_decay(f_0, tau, 1./100)
    if t_lower is None:
        t_lower = delta_t
        kmin = 0
    else:
        kmin=int(t_lower / delta_t)
    if t_final is None:
        t_final = qnm_time_decay(tau, 1./1000)
    kmax = int(t_final / delta_t)
    n = int(t_final / delta_t) + 1

    two_pi = 2 * numpy.pi

    times = numpy.arange(t_lower, t_final, delta_t)

    hp = Amp * numpy.exp(-times/tau) * numpy.cos(two_pi*f_0*times + phi_0)
    hc = Amp * numpy.exp(-times/tau) * numpy.sin(two_pi*f_0*times + phi_0)

    hplus = TimeSeries(zeros(n), delta_t=delta_t)
    hcross = TimeSeries(zeros(n), delta_t=delta_t)
    hplus.data[kmin:kmax] = hp
    hcross.data[kmin:kmax] = hc

    return hplus, hcross
Beispiel #22
0
def _get_waveform_from_inspiral(**p):
    import lalmetaio

    # prefix with 'Inspiral-'
    name = p['approximant'][9:]

    if name.startswith('EOB'):
        p['phase_order'] = -8

    params = lalmetaio.SimInspiralTable()
    params.waveform = name + string_from_order[p['phase_order']]
    params.mass1= p['mass1']
    params.mass2= p['mass2']
    params.f_lower = p['f_lower']
    params.spin1x = p['spin1x']
    params.spin1y = p['spin1y']
    params.spin1z = p['spin1z']
    params.spin2x = p['spin2x']
    params.spin2y = p['spin2y']
    params.spin2z = p['spin2z']
    params.inclination = p['inclination']
    params.distance = p['distance']
    params.coa_phase = p['coa_phase']
    import lalinspiral
    guess_length = lalinspiral.FindChirpChirpTime(params.mass1, params.mass2,
                                                  params.f_lower, 7)
    guess_length = max(guess_length, 3)
    params.geocent_end_time = guess_length * 1.5
    params.taper = 'TAPER_NONE'  #FIXME - either explain or don't hardcode this
    bufferl = guess_length * 2
    dt = p['delta_t']
    df = 1.0 / bufferl
    sample_rate = int(1.0 / dt)
    epoch = lal.LIGOTimeGPS(0, 0)
    N = bufferl * sample_rate
    n = N / 2 + 1

    resp  = FrequencySeries(zeros(n), delta_f=df, epoch=epoch,
                            dtype=complex64) + 1
    out   = TimeSeries(zeros(N), delta_t=dt, epoch=epoch, dtype=float32)
    outl  = out.lal()
    outl.sampleUnits = lal.ADCCountUnit

    out2  = TimeSeries(zeros(N), delta_t=dt, epoch=epoch, dtype=float32)
    outl2 = out.lal()
    outl2.sampleUnits = lal.ADCCountUnit

    respl = resp.lal()
    respl.sampleUnits = lal.DimensionlessUnit

    lalinspiral.FindChirpInjectSignals(outl, params, respl)

    params.coa_phase -= lal.PI / 4
    lalinspiral.FindChirpInjectSignals(outl2, params, respl)
    seriesp = TimeSeries(outl.data.data, delta_t=dt,
                         epoch=epoch - params.geocent_end_time)

    seriesc = TimeSeries(outl2.data.data, delta_t=dt,
                         epoch=epoch - params.geocent_end_time)

    return seriesp, seriesc
Beispiel #23
0
from pycbc.frame import read_frame
from pycbc.filter import highpass_fir, lowpass_fir
from pycbc.psd import welch, interpolate
from pycbc.types import TimeSeries
import urllib

# Read data and remove low frequency content
fname = 'H-H1_LOSC_4_V2-1126259446-32.gwf'
url = "https://losc.ligo.org/s/events/GW150914/" + fname
urllib.urlretrieve(url, filename=fname)
h1 = highpass_fir(read_frame(fname, 'H1:LOSC-STRAIN'), 15.0, 8)

# Calculate the noise spectrum and whiten
psd = interpolate(welch(h1), 1.0 / 32)
white_strain = (h1.to_frequencyseries() / psd ** 0.5 * psd.delta_f).to_timeseries()

# remove some of the high and low frequencies
smooth = highpass_fir(white_strain, 25, 8)
smooth = lowpass_fir(white_strain, 250, 8)

#strech out and shift the frequency upwards to aid human hearing
fdata = smooth.to_frequencyseries()
fdata.roll(int(1200 / fdata.delta_f))
smooth = TimeSeries(fdata.to_timeseries(), delta_t=1.0/1024)

#Take slice around signal
smooth = smooth[len(smooth)/2 - 1500:len(smooth)/2 + 3000]
smooth.save_to_wav('gw150914_h1_chirp.wav')

Beispiel #24
0
def resample_to_delta_t(timeseries, delta_t, method='butterworth'):
    """Resmple the time_series to delta_t

    Resamples the TimeSeries instance time_series to the given time step, 
    delta_t. Only powers of two and real valued time series are supported 
    at this time. Additional restrictions may apply to particular filter
    methods.

    Parameters
    ----------
    time_series: TimeSeries
        The time series to be resampled
    delta_t: float
        The desired time step 

    Returns
    -------
    Time Series: TimeSeries
        A TimeSeries that has been resampled to delta_t.

    Raises
    ------
    TypeError: 
        time_series is not an instance of TimeSeries.
    TypeError: 
        time_series is not real valued

    Examples
    --------

    >>> h_plus_sampled = resample_to_delta_t(h_plus, 1.0/2048)
    """
    if not isinstance(timeseries,TimeSeries):
        raise TypeError("Can only resample time series")

    if timeseries.kind is not 'real':
        raise TypeError("Time series must be real")

    if timeseries.delta_t == delta_t:
        return timeseries * 1

    if method == 'butterworth':
        lal_data = timeseries.lal()
        _resample_func[timeseries.dtype](lal_data, delta_t)
        data = lal_data.data.data 
        
    elif method == 'ldas':  
        factor = int(delta_t / timeseries.delta_t)
        numtaps = factor * 20 + 1

        # The kaiser window has been testing using the LDAS implementation
        # and is in the same configuration as used in the original lalinspiral
        filter_coefficients = scipy.signal.firwin(numtaps, 1.0 / factor,
                                                  window=('kaiser', 5))             

        # apply the filter and decimate
        data = fir_zero_filter(filter_coefficients, timeseries)[::factor]
        
    else:
        raise ValueError('Invalid resampling method: %s' % method)
        
    ts = TimeSeries(data, delta_t = delta_t,
                      dtype=timeseries.dtype, 
                      epoch=timeseries._epoch)
                      
    # From the construction of the LDAS FIR filter there will be 10 corrupted samples
    # explanation here http://software.ligo.org/docs/lalsuite/lal/group___resample_time_series__c.html
    ts.corrupted_samples = 10
    return ts
Beispiel #25
0
def get_td_qnm(template=None, taper=None, **kwargs):
    """Return a time domain damped sinusoid.

    Parameters
    ----------
    template: object
        An object that has attached properties. This can be used to substitute
        for keyword arguments. A common example would be a row in an xml table.
    taper: {None, float}, optional
        Tapering at the beginning of the waveform with duration taper * tau.
        This option is recommended with timescales taper=1./2 or 1. for
        time-domain ringdown-only injections.
        The abrupt turn on of the ringdown can cause issues on the waveform
        when doing the fourier transform to the frequency domain. Setting
        taper will add a rapid ringup with timescale tau/10.
    f_0 : float
        The ringdown-frequency.
    tau : float
        The damping time of the sinusoid.
    phi : float
        The initial phase of the ringdown.
    amp : float
        The amplitude of the ringdown (constant for now).
    delta_t : {None, float}, optional
        The time step used to generate the ringdown.
        If None, it will be set to the inverse of the frequency at which the
        amplitude is 1/1000 of the peak amplitude.
    t_final : {None, float}, optional
        The ending time of the output time series.
        If None, it will be set to the time at which the amplitude is 
        1/1000 of the peak amplitude.

    Returns
    -------
    hplus: TimeSeries
        The plus phase of the ringdown in time domain.
    hcross: TimeSeries
        The cross phase of the ringdown in time domain.
    """

    input_params = props(template, qnm_required_args, **kwargs)
    
    f_0 = input_params.pop('f_0')
    tau = input_params.pop('tau')
    amp = input_params.pop('amp')
    phi = input_params.pop('phi')
    # the following may not be in input_params
    delta_t = input_params.pop('delta_t', None)
    t_final = input_params.pop('t_final', None)

    if delta_t is None:
        delta_t = 1. / qnm_freq_decay(f_0, tau, 1./1000)
        if delta_t < min_dt:
            delta_t = min_dt
    if t_final is None:
        t_final = qnm_time_decay(tau, 1./1000)
    kmax = int(t_final / delta_t) + 1

    times = numpy.arange(kmax) * delta_t

    hp = amp * numpy.exp(-times/tau) * numpy.cos(two_pi*f_0*times + phi)
    hc = amp * numpy.exp(-times/tau) * numpy.sin(two_pi*f_0*times + phi)

    # If size of tapering window is less than delta_t, do not apply taper.
    if taper is None or delta_t > taper*tau:
        hplus = TimeSeries(zeros(kmax), delta_t=delta_t)
        hcross = TimeSeries(zeros(kmax), delta_t=delta_t)
        hplus.data[:kmax] = hp
        hcross.data[:kmax] = hc

        return hplus, hcross

    else:
        taper_hp, taper_hc, taper_window, start = apply_taper(delta_t, taper,
                                                        f_0, tau, amp, phi)
        hplus = TimeSeries(zeros(taper_window+kmax), delta_t=delta_t)
        hcross = TimeSeries(zeros(taper_window+kmax), delta_t=delta_t)
        hplus.data[:taper_window] = taper_hp
        hplus.data[taper_window:] = hp
        hplus._epoch = start
        hcross.data[:taper_window] = taper_hc
        hcross.data[taper_window:] = hc
        hcross._epoch = start

        return hplus, hcross
Beispiel #26
0
def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('--start',
                        default=0,
                        type=int,
                        help='The index of the first template to generate.')
    parser.add_argument('--number-samples',
                        default=1000,
                        type=int,
                        help='The number of samples to generate.')
    parser.add_argument(
        '--workers',
        help=
        'The number of processes to spwan. (default: number processes = number available cores)'
    )
    parser.add_argument('--name',
                        required=True,
                        help='The base-name for the output file.')
    parser.add_argument(
        '--params-config-file',
        required=True,
        help='The configuration file from which the templates are generated.')
    parser.add_argument(
        '--network-config-file',
        required=True,
        help='The config-file specifying the network to use for training.')
    parser.add_argument(
        '--file-name',
        default='{name}-{start}-{stop}-{samples}.hdf',
        type=str,
        help=
        'A formatting string from which the file names are constructed. Available keys are "name", "start", "stop", "samples".'
    )
    parser.add_argument(
        '--detectors',
        nargs='+',
        default=['H1', 'L1', 'V1'],
        help=
        'The detectors for which to generate the waveforms. (Detector names as understood by PyCBC)'
    )
    parser.add_argument(
        '--verbose',
        default=False,
        type=str2bool,
        help='Whether or not to print a progress bar. (default: false)')
    parser.add_argument(
        '--psd-names',
        action=MultiDetOptionAction,
        help=
        'The names (as understood by PyCBC) to use for whitening and noise generation. (Add as det_name1:psd_name1 det_name2:psd_name2)'
    )
    parser.add_argument(
        '--time-shift-name',
        default='tc',
        help=
        'The name of the variable that gives the length of the time-shift to apply to every template'
    )
    parser.add_argument('--final-duration',
                        default=1.0,
                        type=float,
                        help='The duration of the final template in seconds.')
    parser.add_argument(
        '--translation',
        nargs='+',
        action=TranslationAction,
        default={},
        help=
        'Translate the parameter names. (Input as par1:par1new par2:par2new ...)'
    )
    parser.add_argument(
        '--training-data',
        type=str2bool,
        default=True,
        help=
        'If true it uses the directory for the training data to store the results. Otherwise the testing data directory will be used.'
    )
    parser.add_argument(
        '--output-vitaminb-params-files',
        type=str2bool,
        default=False,
        help=
        'Whether or not to generate the params, bounds and fixed-vals files that are required by VItaminB.'
    )
    parser.add_argument('--seed',
                        help='The seed to use to draw waveform-parameters.')

    args = parser.parse_args()

    stop = args.start + args.number_samples

    file_name = args.file_name.format(name=args.name,
                                      start=args.start,
                                      stop=stop,
                                      samples=args.number_samples)

    ######################
    #Convert config files#
    ######################
    #Create .ini-file for PyCBC
    config_file = 'tmp_parameter_config.ini'
    vitaminb_params_to_pycbc_params(args.params_config_file, config_file)

    #Convert .ini-files to vitaminb understandable .json files.
    params_file = 'tmp_params.json'
    bounds_file = 'tmp_bounds.json'
    fixed_vals_file = 'tmp_fixed_vals.json'
    tmp_params, tmp_bounds, tmp_fixed = params_files_from_config(
        args.params_config_file,
        args.network_config_file,
        translation=args.translation)
    if args.output_vitaminb_params_files:
        with open(params_file, 'w') as fp:
            json.dump(tmp_params, fp, indent=4)
        with open(bounds_file, 'w') as fp:
            json.dump(tmp_bounds, fp, indent=4)
        with open(fixed_vals_file, 'w') as fp:
            json.dump(tmp_fixed, fp, indent=4)

    ####################
    #Generate waveforms#
    ####################
    if args.seed is None:
        seed = int(time.time())
    else:
        seed = int(args.seed)
    param_gen = WFParamGenerator(config_file, seed=seed)

    params = param_gen.draw_multiple(args.number_samples)
    params = field_array_to_dict(params)
    try:
        params['tc'] = params.pop(args.time_shift_name)
    except:
        pass

    getter = WaveformGetter(variable_params=params,
                            static_params=param_gen.static,
                            detectors=args.detectors)

    wavs = getter.generate_mp(workers=args.workers, verbose=args.verbose)
    try:
        params[args.time_shift_name] = params.pop('tc')
    except:
        pass

    #############
    #Whiten data#
    #############

    #TODO: Make this use multiprocessing

    psd_names = {key: val for (key, val) in args.psd_names.items()}
    for key in wavs.keys():
        if key not in psd_names:
            if key == 'H1' or key == 'L1':
                psd_names[key] = 'aLIGOZeroDetHighPower'
            elif key == 'V1':
                #psd_names[key] = 'AdVDesignSensitivityP1200087'
                psd_names[key] = 'Virgo'
            elif key == 'K1':
                psd_names[key] = 'KAGRADesignSensitivityT1600593'

    flow = getter.static_params['f_lower']
    if args.verbose:
        bar = progress_tracker(len(wavs) * len(getter),
                               name='Whitening samples')
    white_wavs = {}
    psd_cache = NamedPSDCache(list(set(psd_names.values())))
    for key in wavs.keys():
        white_wavs[key] = []
        psd_name = psd_names.get(key)
        for wav in wavs[key]:
            tmp = TimeSeries(np.zeros(int(8 * wav.sample_rate + len(wav))),
                             delta_t=wav.delta_t,
                             epoch=wav.start_time - 4)
            tmp.data[int(4 *
                         wav.sample_rate):int(-4 *
                                              wav.sample_rate)] = wav.data[:]
            psd = psd_cache.get_from_timeseries(tmp,
                                                max(flow - 2, 0),
                                                psd_name=psd_names[key])
            white_wavs[key].append(
                whiten(tmp,
                       low_freq_cutoff=flow,
                       max_filter_duration=4.,
                       psd=psd))
            if args.verbose:
                bar.iterate()

    #############################
    #Crop data to correct length#
    #############################
    time_shifted = {}
    final_len = int(args.final_duration / getter.static_params['delta_t'])
    if args.verbose:
        bar = progress_tracker(len(white_wavs) * len(getter),
                               name='Applying time shifts')
    for key in white_wavs.keys():
        time_shifted[key] = []
        for i, wav in enumerate(white_wavs[key]):
            t0_idx = int((args.final_duration / 2 - float(wav.start_time)) *
                         wav.sample_rate)
            #Pad too short waveforms
            if t0_idx < 0:
                wav.prepend_zeros(-t0_idx)
                t0_idx = 0
            time_shifted[key].append(wav[t0_idx:t0_idx + final_len])
            if args.verbose:
                bar.iterate()

    ################
    #Store the data#
    ################
    #Set the output file location
    if args.training_data:
        file_loc = os.path.join(tmp_params['train_set_dir'], file_name)
    else:
        file_loc = os.path.join(tmp_params['test_set_dir'], file_name)

    #Ensure that a translation is given for every parameter
    translation = args.translation
    keys = list(getter.variable_params)
    for key in keys:
        if key not in translation:
            translation[key] = key

    #Format the data to store it properly
    store_array = np.zeros((len(time_shifted), len(getter), final_len))
    for i, key in enumerate(time_shifted.keys()):
        for j, dat in enumerate(time_shifted[key]):
            store_array[i][j][:] = np.array(dat)[:]
    store_array = store_array.transpose(1, 0, 2)

    #print(f"Store array shape: {store_array.shape}")

    x_data = np.vstack([np.array(params[key]) for key in keys])
    x_data = x_data.transpose()
    #x_data = x_data.reshape((len(getter), 1, len(keys)))

    #Calculate the optimal SNR of all signals
    if args.verbose:
        bar = progress_tracker(len(time_shifted) * len(getter),
                               name='Calculating SNRs')
    snrs = []
    for key in time_shifted.keys():
        tmp = []
        for wav in time_shifted[key]:
            tmp.append(sigma(wav, low_frequency_cutoff=flow))
        snrs.append(tmp)
    snrs = np.array(snrs)
    snrs = snrs.transpose()

    #Set some auxiliary data
    y_normscale = tmp_params['y_normscale']
    tmpkeys = [translation[key] for key in keys]

    #Store the data
    if args.training_data:
        with h5py.File(file_loc, 'w') as fp:
            fp.create_dataset('x_data', data=x_data)
            fp.create_dataset('y_data_noisefree',
                              data=store_array *
                              np.sqrt(getter.static_params['delta_t']))
            #            fp.create_dataset('y_data_noisefree', data=store_array)
            fp.create_dataset('y_data_noisy',
                              data=store_array +
                              np.random.normal(size=store_array.shape))
            #            fp.create_dataset('y_data_noisy', data=store_array+np.random.normal(scale=np.sqrt(1. / getter.static_params['delta_t']), size=store_array.shape))
            fp.create_dataset('rand_pars', data=np.array(tmpkeys, dtype='S'))
            fp.create_dataset('snrs', data=snrs)
            fp.create_dataset('y_normscale', data=y_normscale)
            for key, val in tmp_bounds.items():
                fp.create_dataset(key, data=val)
    else:
        for i in range(args.number_samples):

            file_loc = os.path.join(
                tmp_params['test_set_dir'],
                args.file_name.format(name=args.name,
                                      start=args.start + i,
                                      stop=args.start + i + 1,
                                      samples=args.number_samples))
            with h5py.File(file_loc, 'w') as fp:
                fp.create_dataset('x_data',
                                  data=np.expand_dims(x_data[i], axis=0))
                fp.create_dataset('y_data_noisefree',
                                  data=store_array[i] *
                                  np.sqrt(getter.static_params['delta_t']))
                #                fp.create_dataset('y_data_noisefree', data=store_array[i])
                fp.create_dataset('y_data_noisy',
                                  data=store_array[i] +
                                  np.random.normal(size=store_array[i].shape))
                #                fp.create_dataset('y_data_noisy', data=store_array[i]+np.random.normal(scale=np.sqrt(1. / getter.static_params['delta_t']), size=store_array[i].shape))
                fp.create_dataset('rand_pars',
                                  data=np.array(tmpkeys, dtype='S'))
                fp.create_dataset('snrs', data=snrs[i])
                fp.create_dataset('y_normscale', data=y_normscale)
                for key, val in tmp_bounds.items():
                    fp.create_dataset(key, data=val)

    if args.output_vitaminb_params_files:
        return params_file, bounds_file, fixed_vals_file
    else:
        return None
Beispiel #27
0
def resample_to_delta_t(timeseries, delta_t, method='butterworth'):
    """Resmple the time_series to delta_t

    Resamples the TimeSeries instance time_series to the given time step, 
    delta_t. Only powers of two and real valued time series are supported 
    at this time. Additional restrictions may apply to particular filter
    methods.

    Parameters
    ----------
    time_series: TimeSeries
        The time series to be resampled
    delta_t: float
        The desired time step 

    Returns
    -------
    Time Series: TimeSeries
        A TimeSeries that has been resampled to delta_t.

    Raises
    ------
    TypeError: 
        time_series is not an instance of TimeSeries.
    TypeError: 
        time_series is not real valued

    Examples
    --------

    >>> h_plus_sampled = resample_to_delta_t(h_plus, 1.0/2048)
    """

    if not isinstance(timeseries, TimeSeries):
        raise TypeError("Can only resample time series")

    if timeseries.kind is not 'real':
        raise TypeError("Time series must be real")

    if timeseries.delta_t == delta_t:
        return timeseries * 1

    if method == 'butterworth':
        lal_data = timeseries.lal()
        _resample_func[timeseries.dtype](lal_data, delta_t)
        data = lal_data.data.data

    elif method == 'ldas':
        factor = int(delta_t / timeseries.delta_t)

        if factor == 8:
            timeseries = resample_to_delta_t(timeseries,
                                             timeseries.delta_t * 4.0,
                                             method='ldas')
            factor = 2
        elif factor == 16:
            timeseries = resample_to_delta_t(timeseries,
                                             timeseries.delta_t * 4.0,
                                             method='ldas')
            factor = 4
        elif factor == 32:
            timeseries = resample_to_delta_t(timeseries,
                                             timeseries.delta_t * 8.0,
                                             method='ldas')
            factor = 4
        elif factor == 64:
            timeseries = resample_to_delta_t(timeseries,
                                             timeseries.delta_t * 16.0,
                                             method='ldas')
            factor = 4

        try:
            filter_coefficients = LDAS_FIR_LP[factor]
        except:
            raise ValueError('Unsupported resample factor, %s, given' % factor)

        # apply the filter
        series = scipy.signal.lfilter(filter_coefficients, 1.0,
                                      timeseries.numpy())

        # reverse the time shift caused by the filter
        corruption_length = len(filter_coefficients)
        data = numpy.zeros(len(timeseries))
        data[:len(data) - corruption_length / 2] = series[corruption_length /
                                                          2:]

        # zero out corrupted region
        data[0:corruption_length / 2] = 0
        data[len(data) - corruption_length / 2:] = 0

        # Decimate the time series
        data = data[::factor] * 1

    else:
        raise ValueError('Invalid resampling method: %s' % method)

    return TimeSeries(data,
                      delta_t=delta_t,
                      dtype=timeseries.dtype,
                      epoch=timeseries._epoch)
Beispiel #28
0
    def setUp(self):

        self.Msun = 4.92549095e-6
        self.sample_rate = 4096
        self.segment_length = 256
        self.low_frequency_cutoff = 30.0

        # chirp params
        self.m1 = 2.0
        self.m2 = 2.5
        self.del_t = 1.0/self.sample_rate
        self.Dl = 40.0
        self.iota = 1.0
        self.phi_c = 2.0
        self.tc_indx = 86*self.sample_rate ## offset from the beginnig of a segment
        self.fmax = 1.0/(6.**1.5 *pi *(self.m1+self.m2)*self.Msun)


        self.zeta = 1.0
        self.thetaS = 0.5
        self.phiS = 2.781

        self.Fp = 0.5*cos(2.0*self.zeta)*(1.0 + cos(self.thetaS)*cos(self.thetaS))*cos(2.0*self.phiS) - \
                sin(2.*self.zeta)*cos(self.thetaS)*sin(2.*self.phiS)

        self.Fc = 0.5*sin(2.0*self.zeta)*(1.0 + cos(self.thetaS)*cos(self.thetaS))*cos(2.0*self.phiS) + \
                 cos(2.*self.zeta)*sin(self.thetaS)*sin(2.*self.phiS)


        # params of sin-gaussian
        self.Q = 1.e-1
        self.om = 200.0*pi*2.0

        # use flat psd
        self.seg_len_idx = self.segment_length * self.sample_rate
        self.psd_len = int(self.seg_len_idx/2+1)

        self.Psd = np.ones(self.psd_len)*2.0e-46

        #  generate waveform and chirp signal

        hp, hc = get_td_waveform(approximant="SpinTaylorT5", mass1=self.m1, mass2=self.m2, \
                        delta_t=self.del_t, f_lower=self.low_frequency_cutoff, distance=self.Dl, \
                        inclination=self.iota, coa_phase=self.phi_c)

        # signal which is a noiseless data
        thp = np.zeros(self.seg_len_idx)
        thp[self.tc_indx:len(hp)+self.tc_indx] = hp
        thc = np.zeros(self.seg_len_idx)
        thc[self.tc_indx:len(hc)+self.tc_indx] = hc

        fct = 10.0/15.21377
        self.sig1 = fct*(self.Fp*thp + self.Fc*thc)

        #### template
        h = np.zeros(self.seg_len_idx)
        h[0:len(hp)] = hp
        hpt = TimeSeries(h, self.del_t)
        self.htilde = make_frequency_series(hpt)

        # generate sin-gaussian signal
        time = np.arange(0, len(hp))*self.del_t
        Nby2 = int(len(hp)/2)
        sngt = np.zeros(len(hp))
        for i in range(len(hp)):
            sngt[i] = 9.0e-21*exp(-(time[i]-time[Nby2])**2/self.Q)*sin(self.om*time[i])

        self.sig2 = np.zeros(self.seg_len_idx)
        self.sig2[self.tc_indx:len(sngt)+self.tc_indx] = sngt
Beispiel #29
0
def calc_filt_psd_variation(strain, segment, short_segment, psd_long_segment,
                            psd_duration, psd_stride, psd_avg_method, low_freq,
                            high_freq):
    """ Calculates time series of PSD variability

    This function first splits the segment up into 512 second chunks. It
    then calculates the PSD over this 512 second. The PSD is used to
    to create a filter that is the composition of three filters:
    1. Bandpass filter between f_low and f_high.
    2. Weighting filter which gives the rough response of a CBC template.
    3. Whitening filter.
    Next it makes the convolution of this filter with the stretch of data.
    This new time series is given to the "mean_square" function, which
    computes the mean square of the timeseries within an 8 seconds window,
    once per second.
    The result, which is the variance of the S/N in that stride for the
    Parseval theorem, is then stored in a timeseries.

    Parameters
    ----------
    strain : TimeSeries
        Input strain time series to estimate PSDs
    segment : {float, 8}
        Duration of the segments for the mean square estimation in seconds.
    short_segment : {float, 0.25}
        Duration of the short segments for the outliers removal.
    psd_long_segment : {float, 512}
        Duration of the long segments for PSD estimation in seconds.
    psd_duration : {float, 8}
        Duration of FFT segments for long term PSD estimation, in seconds.
    psd_stride : {float, 4}
        Separation between FFT segments for long term PSD estimation, in
        seconds.
    psd_avg_method : {string, 'median'}
        Method for averaging PSD estimation segments.
    low_freq : {float, 20}
        Minimum frequency to consider the comparison between PSDs.
    high_freq : {float, 480}
        Maximum frequency to consider the comparison between PSDs.

    Returns
    -------
    psd_var : TimeSeries
        Time series of the variability in the PSD estimation
    """
    # Calculate strain precision
    if strain.precision == 'single':
        fs_dtype = numpy.float32
    elif strain.precision == 'double':
        fs_dtype = numpy.float64

    # Convert start and end times immediately to floats
    start_time = numpy.float(strain.start_time)
    end_time = numpy.float(strain.end_time)

    # Resample the data
    strain = resample_to_delta_t(strain, 1.0 / 2048)
    srate = int(strain.sample_rate)

    # Fix the step for the PSD estimation and the time to remove at the
    # edge of the time series.
    step = 1.0
    strain_crop = 8.0

    # Find the times of the long segments
    times_long = numpy.arange(
        start_time, end_time,
        psd_long_segment - 2 * strain_crop - segment + step)

    # Set up the empty time series for the PSD variation estimate
    ts_duration = end_time - start_time - 2 * strain_crop - segment + 1
    psd_var = TimeSeries(zeros(int(numpy.floor(ts_duration / step))),
                         delta_t=step,
                         copy=False,
                         epoch=start_time + strain_crop + segment)

    # Create a bandpass filter between low_freq and high_freq
    filt = sig.firwin(4 * srate, [low_freq, high_freq],
                      pass_zero=False,
                      window='hann',
                      nyq=srate / 2)
    filt.resize(int(psd_duration * srate))
    # Fourier transform the filter and take the absolute value to get
    # rid of the phase. Save the filter as a frequency series.
    filt = abs(rfft(filt))
    my_filter = FrequencySeries(filt,
                                delta_f=1. / psd_duration,
                                dtype=fs_dtype)

    ind = 0
    for tlong in times_long:
        # Calculate PSD for long segment
        if tlong + psd_long_segment <= float(end_time):
            astrain = strain.time_slice(tlong, tlong + psd_long_segment)
            plong = pycbc.psd.welch(
                astrain,
                seg_len=int(psd_duration * strain.sample_rate),
                seg_stride=int(psd_stride * strain.sample_rate),
                avg_method=psd_avg_method)
        else:
            astrain = strain.time_slice(tlong, end_time)
            plong = pycbc.psd.welch(
                strain.time_slice(end_time - psd_long_segment, end_time),
                seg_len=int(psd_duration * strain.sample_rate),
                seg_stride=int(psd_stride * strain.sample_rate),
                avg_method=psd_avg_method)

        # Make the weighting filter - bandpass, which weight by f^-7/6,
        # and whiten. The normalization is chosen so that the variance
        # will be one if this filter is applied to white noise which
        # already has a variance of one.
        freqs = FrequencySeries(plong.sample_frequencies,
                                delta_f=plong.delta_f,
                                epoch=plong.epoch,
                                dtype=fs_dtype)
        fweight = freqs**(-7. / 6.) * my_filter / numpy.sqrt(plong)
        fweight[0] = 0.
        norm = (sum(abs(fweight)**2) / (len(fweight) - 1.))**-0.5
        fweight = norm * fweight
        fwhiten = numpy.sqrt(2. / srate) / numpy.sqrt(plong)
        fwhiten[0] = 0.
        full_filt = sig.hann(int(psd_duration * srate)) * numpy.roll(
            irfft(fwhiten * fweight),
            int(psd_duration / 2) * srate)
        # Convolve the filter with long segment of data
        wstrain = TimeSeries(sig.fftconvolve(astrain, full_filt, mode='same'),
                             delta_t=strain.delta_t,
                             epoch=astrain.start_time)
        wstrain = wstrain[int(strain_crop * srate):-int(strain_crop * srate)]
        # compute the mean square of the chunk of data
        delta_t = wstrain.end_time.gpsSeconds - wstrain.start_time.gpsSeconds
        variation = mean_square(wstrain, delta_t, short_segment, segment)

        # Store variation value
        for i, val in enumerate(variation):
            psd_var[ind + i] = val

        ind = ind + len(variation)
    return psd_var
Beispiel #30
0
def bank_chisq_from_filters(tmplt_snr,
                            tmplt_norm,
                            bank_snrs,
                            bank_norms,
                            tmplt_bank_matches,
                            indices=None):
    """ This function calculates and returns a TimeSeries object containing the
    bank veto calculated over a segment.

    Parameters
    ----------
    tmplt_snr: TimeSeries
        The SNR time series from filtering the segment against the current
        search template
    tmplt_norm: float
        The normalization factor for the search template
    bank_snrs: list of TimeSeries
        The precomputed list of SNR time series between each of the bank veto
        templates and the segment
    bank_norms: list of floats
        The normalization factors for the list of bank veto templates
        (usually this will be the same for all bank veto templates)
    tmplt_bank_matches: list of floats
        The complex overlap between the search template and each
        of the bank templates
    indices: {None, Array}, optional
        Array of indices into the snr time series. If given, the bank chisq
        will only be calculated at these values.

    Returns
    -------
    bank_chisq: TimeSeries of the bank vetos
    """
    if indices is not None:
        tmplt_snr = Array(tmplt_snr, copy=False)
        bank_snrs_tmp = []
        for bank_snr in bank_snrs:
            bank_snrs_tmp.append(bank_snr.take(indices))
        bank_snrs = bank_snrs_tmp

    # Initialise bank_chisq as 0s everywhere
    bank_chisq = zeros(len(tmplt_snr), dtype=real_same_precision_as(tmplt_snr))

    # Loop over all the bank templates
    for i in range(len(bank_snrs)):
        bank_match = tmplt_bank_matches[i]
        if (abs(bank_match) > 0.99):
            # Not much point calculating bank_chisquared if the bank template
            # is very close to the filter template. Can also hit numerical
            # error due to approximations made in this calculation.
            # The value of 2 is the expected addition to the chisq for this
            # template
            bank_chisq += 2.
            continue
        bank_norm = sqrt((1 - bank_match * bank_match.conj()).real)

        bank_SNR = bank_snrs[i] * (bank_norms[i] / bank_norm)
        tmplt_SNR = tmplt_snr * (bank_match.conj() * tmplt_norm / bank_norm)

        bank_SNR = Array(bank_SNR, copy=False)
        tmplt_SNR = Array(tmplt_SNR, copy=False)

        bank_chisq += (bank_SNR - tmplt_SNR).squared_norm()

    if indices is not None:
        return bank_chisq
    else:
        return TimeSeries(bank_chisq,
                          delta_t=tmplt_snr.delta_t,
                          epoch=tmplt_snr.start_time,
                          copy=False)
Beispiel #31
0
def matched_filter_core(template,
                        data,
                        psd=None,
                        low_frequency_cutoff=None,
                        high_frequency_cutoff=None,
                        h_norm=None,
                        out=None,
                        corr_out=None):
    """ Return the complex snr and normalization. 
    
    Return the complex snr, along with its associated normalization of the template,
    matched filtered against the data. 

    Parameters
    ----------
    template : TimeSeries or FrequencySeries 
        The template waveform
    data : TimeSeries or FrequencySeries 
        The strain data to be filtered.
    psd : {FrequencySeries}, optional
        The noise weighting of the filter.
    low_frequency_cutoff : {None, float}, optional
        The frequency to begin the filter calculation. If None, begin at the
        first frequency after DC.
    high_frequency_cutoff : {None, float}, optional
        The frequency to stop the filter calculation. If None, continue to the 
        the nyquist frequency.
    h_norm : {None, float}, optional
        The template normalization. If none, this value is calculated internally.
    out : {None, Array}, optional
        An array to use as memory for snr storage. If None, memory is allocated 
        internally.
    corr_out : {None, Array}, optional
        An array to use as memory for correlation storage. If None, memory is allocated 
        internally. If provided, management of the vector is handled externally by the
        caller. No zero'ing is done internally. 

    Returns
    -------
    snr : TimeSeries
        A time series containing the complex snr. 
    corrrelation: FrequencySeries
        A frequency series containing the correlation vector. 
    norm : float
        The normalization of the complex snr.  
    """
    if corr_out is not None:
        _qtilde = corr_out
    else:
        global _qtilde_t
        _qtilde = _qtilde_t

    htilde = make_frequency_series(template)
    stilde = make_frequency_series(data)

    if len(htilde) != len(stilde):
        raise ValueError("Length of template and data must match")

    N = (len(stilde) - 1) * 2
    kmin, kmax = get_cutoff_indices(low_frequency_cutoff,
                                    high_frequency_cutoff, stilde.delta_f, N)

    if out is None:
        _q = zeros(N, dtype=complex_same_precision_as(data))
    elif (len(out) == N) and type(out) is Array and out.kind == 'complex':
        _q = out
    else:
        raise TypeError('Invalid Output Vector: wrong length or dtype')

    if corr_out:
        pass
    elif (_qtilde is None) or (len(_qtilde) !=
                               N) or _qtilde.dtype != data.dtype:
        _qtilde_t = _qtilde = zeros(N, dtype=complex_same_precision_as(data))
    else:
        _qtilde.clear()

    correlate(htilde[kmin:kmax], stilde[kmin:kmax], _qtilde[kmin:kmax])

    if psd is not None:
        if isinstance(psd, FrequencySeries):
            if psd.delta_f == stilde.delta_f:
                _qtilde[kmin:kmax] /= psd[kmin:kmax]
            else:
                raise TypeError("PSD delta_f does not match data")
        else:
            raise TypeError("PSD must be a FrequencySeries")

    ifft(_qtilde, _q)

    if h_norm is None:
        h_norm = sigmasq(htilde, psd, low_frequency_cutoff,
                         high_frequency_cutoff)

    norm = (4.0 * stilde.delta_f) / sqrt(h_norm)
    delta_t = 1.0 / (N * stilde.delta_f)

    return (TimeSeries(_q, epoch=stilde._epoch, delta_t=delta_t, copy=False),
            FrequencySeries(_qtilde,
                            epoch=stilde._epoch,
                            delta_f=htilde.delta_f,
                            copy=False), norm)
Beispiel #32
0
    def do_test(self, n_ifos, n_ifos_followup):
        # choose a random selection of interferometers
        # n_ifos will be used to generate the simulated trigger
        # n_ifos_followup will be used as followup-only
        all_ifos = random.sample(self.possible_ifos, n_ifos + n_ifos_followup)
        trig_ifos = all_ifos[0:n_ifos]

        results = {
            'foreground/stat': np.random.uniform(4, 20),
            'foreground/ifar': np.random.uniform(0.01, 1000)
        }
        followup_data = {}
        for ifo in all_ifos:
            offset = 10000 + np.random.uniform(-0.02, 0.02)
            amplitude = np.random.uniform(4, 20)

            # generate a mock SNR time series with a peak
            n = 201
            dt = 1. / 2048.
            t = np.arange(n) * dt
            t_peak = dt * n / 2
            snr = np.exp(-(t - t_peak)**2 * 3e-3**-2) * amplitude
            snr_series = TimeSeries((snr + 1j * 0).astype(np.complex64),
                                    delta_t=dt,
                                    epoch=offset)

            # generate a mock PSD
            psd_samples = np.random.exponential(size=1024)
            psd = FrequencySeries(psd_samples, delta_f=1.)

            # fill in the various fields
            if ifo in trig_ifos:
                base = 'foreground/' + ifo + '/'
                results[base + 'end_time'] = t_peak + offset
                results[base + 'snr'] = amplitude
                results[base + 'sigmasq'] = np.random.uniform(1e6, 2e6)
            followup_data[ifo] = {'snr_series': snr_series, 'psd': psd}

        for ifo, k in itertools.product(trig_ifos, self.template):
            results['foreground/' + ifo + '/' + k] = self.template[k]

        kwargs = {
            'psds': {ifo: followup_data[ifo]['psd']
                     for ifo in all_ifos},
            'low_frequency_cutoff': 20.,
            'followup_data': followup_data
        }
        coinc = SingleCoincForGraceDB(trig_ifos, results, **kwargs)

        tempdir = tempfile.mkdtemp()

        coinc_file_name = os.path.join(tempdir, 'coinc.xml.gz')

        if GraceDb is not None:
            # pretend to upload the event to GraceDB.
            # The upload will fail, but it should not raise an exception
            # and it should still leave the event file around
            coinc.upload(coinc_file_name,
                         gracedb_server='localhost',
                         testing=True)
        else:
            # no GraceDb module, so just save the coinc file
            coinc.save(coinc_file_name)

        # read back and check the coinc document
        read_coinc = ligolw_utils.load_filename(coinc_file_name,
                                                verbose=False,
                                                contenthandler=ContentHandler)
        single_table = table.get_table(read_coinc,
                                       lsctables.SnglInspiralTable.tableName)
        self.assertEqual(len(single_table), len(all_ifos))
        coinc_table = table.get_table(read_coinc,
                                      lsctables.CoincInspiralTable.tableName)
        self.assertEqual(len(coinc_table), 1)

        # make sure lalseries can read the PSDs
        psd_doc = ligolw_utils.load_filename(
            coinc_file_name,
            verbose=False,
            contenthandler=lalseries.PSDContentHandler)
        psd_dict = lalseries.read_psd_xmldoc(psd_doc)
        self.assertEqual(set(psd_dict.keys()), set(all_ifos))

        shutil.rmtree(tempdir)
Beispiel #33
0
    def apply(self, strain, detector_name, f_lower=None, distance_scale=1):
        """Add injections (as seen by a particular detector) to a time series.

        Parameters
        ----------
        strain : TimeSeries
            Time series to inject signals into, of type float32 or float64.
        detector_name : string
            Name of the detector used for projecting injections.
        f_lower : {None, float}, optional
            Low-frequency cutoff for injected signals. If None, use value
            provided by each injection.
        distance_scale: {1, foat}, optional
            Factor to scale the distance of an injection with. The default is
            no scaling.

        Returns
        -------
        None

        Raises
        ------
        TypeError
            For invalid types of `strain`.
        """

        if not strain.dtype in (float32, float64):
            raise TypeError("Strain dtype must be float32 or float64, not " \
                    + str(strain.dtype))

        lalstrain = strain.lal()
        detector = Detector(detector_name)
        earth_travel_time = lal.REARTH_SI / lal.C_SI
        t0 = float(strain.start_time) - earth_travel_time
        t1 = float(strain.end_time) + earth_travel_time

        # pick lalsimulation tapering function
        taper = taper_func_map[strain.dtype]

        # pick lalsimulation injection function
        add_injection = injection_func_map[strain.dtype]

        for inj in self.table:
            # roughly estimate if the injection may overlap with the segment
            end_time = inj.get_time_geocent()
            #CHECK: This is a hack (10.0s); replace with an accurate estimate
            inj_length = 10.0
            eccentricity = 0.0
            polarization = 0.0
            start_time = end_time - 2 * inj_length
            if end_time < t0 or start_time > t1:
               continue

            # compute the waveform time series
            hp, hc = sim.SimBurstSineGaussian(float(inj.q),
                float(inj.frequency),float(inj.hrss),float(eccentricity),
                float(polarization),float(strain.delta_t))
            hp = TimeSeries(hp.data.data[:], delta_t=hp.deltaT, epoch=hp.epoch)
            hc = TimeSeries(hc.data.data[:], delta_t=hc.deltaT, epoch=hc.epoch)
            hp._epoch += float(end_time)
            hc._epoch += float(end_time)
            if float(hp.start_time) > t1:
               continue

            # compute the detector response, taper it if requested
            # and add it to the strain
            #signal = detector.project_wave(
            #        hp, hc, inj.longitude, inj.latitude, inj.polarization)
            signal_lal = hp.astype(strain.dtype).lal()
            if taper_map['TAPER_NONE'] is not None:
                taper(signal_lal.data, taper_map['TAPER_NONE'])
            add_injection(lalstrain, signal_lal, None)

        strain.data[:] = lalstrain.data.data[:]
Beispiel #34
0
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
Beispiel #35
0
def resample_to_delta_t(timeseries, delta_t, method='butterworth'):
    """Resmple the time_series to delta_t

    Resamples the TimeSeries instance time_series to the given time step,
    delta_t. Only powers of two and real valued time series are supported
    at this time. Additional restrictions may apply to particular filter
    methods.

    Parameters
    ----------
    time_series: TimeSeries
        The time series to be resampled
    delta_t: float
        The desired time step

    Returns
    -------
    Time Series: TimeSeries
        A TimeSeries that has been resampled to delta_t.

    Raises
    ------
    TypeError:
        time_series is not an instance of TimeSeries.
    TypeError:
        time_series is not real valued

    Examples
    --------

    >>> h_plus_sampled = resample_to_delta_t(h_plus, 1.0/2048)
    """
    if not isinstance(timeseries,TimeSeries):
        raise TypeError("Can only resample time series")

    if timeseries.kind is not 'real':
        raise TypeError("Time series must be real")

    if timeseries.delta_t == delta_t:
        return timeseries * 1

    if method == 'butterworth':
        lal_data = timeseries.lal()
        _resample_func[timeseries.dtype](lal_data, delta_t)
        data = lal_data.data.data

    elif method == 'ldas':
        factor = int(delta_t / timeseries.delta_t)
        numtaps = factor * 20 + 1

        # The kaiser window has been testing using the LDAS implementation
        # and is in the same configuration as used in the original lalinspiral
        filter_coefficients = scipy.signal.firwin(numtaps, 1.0 / factor,
                                                  window=('kaiser', 5))

        # apply the filter and decimate
        data = fir_zero_filter(filter_coefficients, timeseries)[::factor]

    else:
        raise ValueError('Invalid resampling method: %s' % method)

    ts = TimeSeries(data, delta_t = delta_t,
                      dtype=timeseries.dtype,
                      epoch=timeseries._epoch)

    # From the construction of the LDAS FIR filter there will be 10 corrupted samples
    # explanation here http://software.ligo.org/docs/lalsuite/lal/group___resample_time_series__c.html
    ts.corrupted_samples = 10
    return ts
Beispiel #36
0
def blend(hin, mm, sample, time, t_opt, WinID=-1):
    # Only dealing with real part, don't do hc calculations
    # t_opt is length-5 array describing multiples of mm
    # Returns length-5 array of TimeSeries (1 per blending)
    #{{{
    hp0, hc0 = hin.rescale_to_totalmass(mm)
    hp0._epoch = hc0._epoch = 0
    amp = TimeSeries(np.sqrt(hp0**2 + hc0**2), copy=True, delta_t=hp0.delta_t)
    max_a, max_a_index = amp.abs_max_loc()
    print("\n\n In blend:\nTotal Mass = %f, len(hp0,hc0) = %d, %d = %f s" %\
          (mm, len(hp0), len(hc0), hp0.sample_times[-1]-hp0.sample_times[0]))
    print("Waveform max = %e, located at %d" % (max_a, max_a_index))
    #amp_after_peak = amp
    #amp_after_peak[:max_a_index] = 0
    mtsun = lal.MTSUN_SI
    amp_after_peak = amp[max_a_index:]
    iA, vA = min(enumerate(amp_after_peak),
                 key=lambda x: abs(x[1] - 0.01 * max_a))
    iA += max_a_index
    #iA, vA = min(enumerate(amp_after_peak),key=lambda x:abs(x[1]-0.01*max_a))
    iB, vB = min(enumerate(amp_after_peak),
                 key=lambda x: abs(x[1] - 0.1 * max_a))
    iB += max_a_index
    if iA <= max_a_index:
        print("iA = %d, iB = %d, vA = %e, vB = %e" % (iA, iB, vA, vB),
              file=sys.stdout)
        sys.stdout.flush()
        raise RuntimeError("Couldnt find amplitude threshold time iA")
        # do something
        #fout = open('hpdump.dat','w+')
        #for i in range( len(amp) ):
        #  if i > max_a_index and amp[i] == 0: break
        #  fout.write('%e\t%e\n' % (amp.sample_times[i],amp[i]))
        #fout.close()
        # Find the point the hard way
        target_amp = max_a * 0.01
        tmp_data = amp.data
        for idx in range(max_a_index, len(amp)):
            if tmp_data[idx] < target_amp: break
        iA = idx
        print("Newfound iA = %d" % iA)
        # Yet another way
        amp_after_peak = amp[max_a_index:]
        iA, vA = min(enumerate(amp_after_peak),
                     key=lambda x: abs(x[1] - 0.01 * max_a))
        iA += max_a_index
        print("Newfound iA another way = %d" % iA)
        raise RuntimeError("Had to find amplitude threshold the hard way")
    if iB <= max_a_index:
        raise RuntimeError("Couldnt find amplitude threshold time iB")
        # this doesn't happen yet
        pass
    print("NEW: iA = %d, iB = %d, vA = %e, vB = %e" % (iA, iB, vA, vB))
    t = [
        [
            t_opt[0] * mm, 500 * mm, hp0.sample_times.data[iA] / mtsun,
            hp0.sample_times.data[iA] / mtsun + t_opt[3] * mm
        ],  # Prayush's E
        [
            t_opt[0] * mm, t_opt[1] * mm, hp0.sample_times.data[iA] / mtsun,
            hp0.sample_times.data[iA] / mtsun + t_opt[3] * mm
        ],
        [
            t_opt[0] * mm, t_opt[1] * mm, hp0.sample_times.data[iB] / mtsun,
            hp0.sample_times.data[iB] / mtsun + t_opt[4] * mm
        ],
        [
            t_opt[0] * mm, t_opt[2] * mm, hp0.sample_times.data[iA] / mtsun,
            hp0.sample_times.data[iA] / mtsun + t_opt[3] * mm
        ],
        [
            t_opt[0] * mm, t_opt[2] * mm, hp0.sample_times.data[iB] / mtsun,
            hp0.sample_times.data[iB] / mtsun + t_opt[4] * mm
        ]
    ]
    hphc = []
    hphc.append(hp0)
    for i in range(len(t)):
        if (WinID >= 0 and WinID < len(t)) and i != WinID: continue
        print("Testing window with t = ", t[i])
        hphc.append(
            hin.blending_function(hp0=hp0,
                                  t=t[i],
                                  sample_rate=sample,
                                  time_length=time))
    print("No of blending windows being tested = %d" % (len(hphc) - 1))
    return hphc
Beispiel #37
0
def filter_zpk(timeseries, z, p, k):
    """Return a new timeseries that was filtered with a zero-pole-gain filter.
    The transfer function in the s-domain looks like:
    .. math::
    \\frac{H(s) = (s - s_1) * (s - s_3) * ... * (s - s_n)}{(s - s_2) * (s - s_4) * ... * (s - s_m)}, m >= n

    The zeroes, and poles entered in Hz are converted to angular frequency,
    along the imaginary axis in the s-domain s=i*omega.  Then the zeroes, and
    poles are bilinearly transformed via:
    .. math::
    z(s) = \\frac{(1 + s*T/2)}{(1 - s*T/2)}

    Where z is the z-domain value, s is the s-domain value, and T is the
    sampling period.  After the poles and zeroes have been bilinearly
    transformed, then the second-order sections are found and filter the data
    using scipy.

    Parameters
    ----------
    timeseries: TimeSeries
        The TimeSeries instance to be filtered.
    z: array
        Array of zeros to include in zero-pole-gain filter design.
        In units of Hz.
    p: array
        Array of poles to include in zero-pole-gain filter design.
        In units of Hz.
    k: float
        Gain to include in zero-pole-gain filter design. This gain is a
        constant multiplied to the transfer function.

    Returns
    -------
    Time Series: TimeSeries
        A  new TimeSeries that has been filtered.

    Examples
    --------
    To apply a 5 zeroes at 100Hz, 5 poles at 1Hz, and a gain of 1e-10 filter
    to a TimeSeries instance, do:
    >>> filtered_data = zpk_filter(timeseries, [100]*5, [1]*5, 1e-10)
    """

    # sanity check type
    if not isinstance(timeseries, TimeSeries):
        raise TypeError("Can only filter TimeSeries instances.")

    # sanity check casual filter
    degree = len(p) - len(z)
    if degree < 0:
        raise TypeError("May not have more zeroes than poles. \
                         Filter is not casual.")

    # cast zeroes and poles as arrays and gain as a float
    z = np.array(z)
    p = np.array(p)
    k = float(k)

    # put zeroes and poles in the s-domain
    # convert from frequency to angular frequency
    z *= -2 * np.pi
    p *= -2 * np.pi

    # get denominator of bilinear transform
    fs = 2.0 * timeseries.sample_rate

    # zeroes in the z-domain
    z_zd = (1 + z / fs) / (1 - z / fs)

    # any zeros that were at infinity are moved to the Nyquist frequency
    z_zd = z_zd[np.isfinite(z_zd)]
    z_zd = np.append(z_zd, -np.ones(degree))

    # poles in the z-domain
    p_zd = (1 + p / fs) / (1 - p / fs)

    # gain change in z-domain
    k_zd = k * np.prod(fs - z) / np.prod(fs - p)

    # get second-order sections
    sos = zpk2sos(z_zd, p_zd, k_zd)

    # filter
    filtered_data = sosfilt(sos, timeseries.numpy())

    return TimeSeries(filtered_data,
                      delta_t=timeseries.delta_t,
                      dtype=timeseries.dtype,
                      epoch=timeseries._epoch)
Beispiel #38
0
def get_td_qnm(template=None, **kwargs):
    """Return a time domain damped sinusoid.

    Parameters
    ----------
    template: object
        An object that has attached properties. This can be used to substitute
        for keyword arguments. A common example would be a row in an xml table.
    f_0 : float
        The ringdown-frequency.
    tau : float
        The damping time of the sinusoid.
    phi_0 : {0, float}, optional
        The initial phase of the ringdown.
    amp : {1, float}, optional
        The amplitude of the ringdown (constant for now).
    delta_t : {None, float}, optional
        The time step used to generate the ringdown.
        If None, it will be set to the inverse of the frequency at which the
        amplitude is 1/1000 of the peak amplitude.
    t_final : {None, float}, optional
        The ending time of the output time series.
        If None, it will be set to the time at which the amplitude is 
        1/1000 of the peak amplitude.

    Returns
    -------
    hplus: TimeSeries
        The plus phase of the ringdown in time domain.
    hcross: TimeSeries
        The cross phase of the ringdown in time domain.
    """

    input_params = props_ringdown(template,**kwargs)

    # get required args
    try:
        f_0 = input_params['f_0']
    except KeyError:
        raise ValueError('f_0 is required')
    try:
        tau = input_params['tau']
    except KeyError:
        raise ValueError('tau is required')
    # get optional args
    # the following have defaults, and so will be populated
    phi_0 = input_params.pop('phi_0')
    amp = input_params.pop('amp')
    # the following may not be in input_params
    delta_t = input_params.pop('delta_t', None)
    t_final = input_params.pop('t_final', None)

    if delta_t is None:
        delta_t = 1. / qnm_freq_decay(f_0, tau, 1./1000)
    if t_final is None:
        t_final = qnm_time_decay(tau, 1./1000)
    kmax = int(t_final / delta_t) + 1

    two_pi = 2 * numpy.pi

    times = numpy.arange(kmax)*delta_t

    hp = amp * numpy.exp(-times/tau) * numpy.cos(two_pi*f_0*times + phi_0)
    hc = amp * numpy.exp(-times/tau) * numpy.sin(two_pi*f_0*times + phi_0)

    hplus = TimeSeries(zeros(kmax), delta_t=delta_t)
    hcross = TimeSeries(zeros(kmax), delta_t=delta_t)
    hplus.data[:kmax] = hp
    hcross.data[:kmax] = hc

    return hplus, hcross
Beispiel #39
0
def get_td_qnm(template=None, taper=None, **kwargs):
    """Return a time domain damped sinusoid.

    Parameters
    ----------
    template: object
        An object that has attached properties. This can be used to substitute
        for keyword arguments. A common example would be a row in an xml table.
    taper: {None, float}, optional
        Tapering at the beginning of the waveform with duration taper * tau.
        This option is recommended with timescales taper=1./2 or 1. for
        time-domain ringdown-only injections.
        The abrupt turn on of the ringdown can cause issues on the waveform
        when doing the fourier transform to the frequency domain. Setting
        taper will add a rapid ringup with timescale tau/10.
    f_0 : float
        The ringdown-frequency.
    tau : float
        The damping time of the sinusoid.
    amp : float
        The amplitude of the ringdown (constant for now).
    phi : float
        The initial phase of the ringdown. Should also include the information
        from the azimuthal angle (phi_0 + m*Phi)
    inclination : {None, float}, optional
        Inclination of the system in radians for the spherical harmonics.
    l : {2, int}, optional
        l mode for the spherical harmonics. Default is l=2.
    m : {2, int}, optional
        m mode for the spherical harmonics. Default is m=2.
    delta_t : {None, float}, optional
        The time step used to generate the ringdown.
        If None, it will be set to the inverse of the frequency at which the
        amplitude is 1/1000 of the peak amplitude.
    t_final : {None, float}, optional
        The ending time of the output time series.
        If None, it will be set to the time at which the amplitude is
        1/1000 of the peak amplitude.

    Returns
    -------
    hplus: TimeSeries
        The plus phase of the ringdown in time domain.
    hcross: TimeSeries
        The cross phase of the ringdown in time domain.
    """

    input_params = props(template, qnm_required_args, **kwargs)

    f_0 = input_params.pop('f_0')
    tau = input_params.pop('tau')
    amp = input_params.pop('amp')
    phi = input_params.pop('phi')
    # the following may not be in input_params
    inc = input_params.pop('inclination', None)
    l = input_params.pop('l', 2)
    m = input_params.pop('m', 2)
    delta_t = input_params.pop('delta_t', None)
    t_final = input_params.pop('t_final', None)

    if not delta_t:
        delta_t = 1. / qnm_freq_decay(f_0, tau, 1./1000)
        if delta_t < min_dt:
            delta_t = min_dt
    if not t_final:
        t_final = qnm_time_decay(tau, 1./1000)

    kmax = int(t_final / delta_t) + 1
    times = numpy.arange(kmax) * delta_t
    if inc is not None:
        Y_plus, Y_cross = spher_harms(l, m, inc)
    else:
        Y_plus, Y_cross = 1, 1

    hplus = amp * Y_plus * numpy.exp(-times/tau) * \
                                numpy.cos(two_pi*f_0*times + phi)
    hcross = amp * Y_cross * numpy.exp(-times/tau) * \
                                numpy.sin(two_pi*f_0*times + phi)

    if taper and delta_t < taper*tau:
        taper_window = int(taper*tau/delta_t)
        kmax += taper_window

    outplus = TimeSeries(zeros(kmax), delta_t=delta_t)
    outcross = TimeSeries(zeros(kmax), delta_t=delta_t)

    # If size of tapering window is less than delta_t, do not apply taper.
    if not taper or delta_t > taper*tau:
        outplus.data[:kmax] = hplus
        outcross.data[:kmax] = hcross

        return outplus, outcross

    else:
        taper_hp, taper_hc = apply_taper(delta_t, taper, f_0, tau, amp, phi,
                                                                    l, m, inc)
        start = - taper * tau
        outplus.data[:taper_window] = taper_hp
        outplus.data[taper_window:] = hplus
        outcross.data[:taper_window] = taper_hc
        outcross.data[taper_window:] = hcross
        outplus._epoch, outcross._epoch = start, start

        return outplus, outcross
Beispiel #40
0
    def apply(self, strain, detector_name, f_lower=None, distance_scale=1,
              simulation_ids=None):
        """Add injections (as seen by a particular detector) to a time series.

        Parameters
        ----------
        strain : TimeSeries
            Time series to inject signals into, of type float32 or float64.
        detector_name : string
            Name of the detector used for projecting injections.
        f_lower : {None, float}, optional
            Low-frequency cutoff for injected signals. If None, use value
            provided by each injection.
        distance_scale: {1, float}, optional
            Factor to scale the distance of an injection with. The default is 
            no scaling. 
        simulation_ids: iterable, optional
            If given, only inject signals with the given simulation IDs.

        Returns
        -------
        None

        Raises
        ------
        TypeError
            For invalid types of `strain`.
        """

        if not strain.dtype in (float32, float64):
            raise TypeError("Strain dtype must be float32 or float64, not " \
                    + str(strain.dtype))

        lalstrain = strain.lal()    
        detector = Detector(detector_name)
        earth_travel_time = lal.REARTH_SI / lal.C_SI
        t0 = float(strain.start_time) - earth_travel_time
        t1 = float(strain.end_time) + earth_travel_time

        # pick lalsimulation injection function
        add_injection = injection_func_map[strain.dtype]

        injections = self.table
        if simulation_ids:
            injections = [inj for inj in injections \
                          if inj.simulation_id in simulation_ids]

        for inj in injections:
            if f_lower is None:
                f_l = inj.f_lower
            else:
                f_l = f_lower

            if inj.numrel_data != None and inj.numrel_data != "":
                # performing NR waveform injection
                # reading Hp and Hc from the frame files
                swigrow = self.getswigrow(inj)
                import lalinspiral
                Hp, Hc = lalinspiral.NRInjectionFromSimInspiral(swigrow,
                                                                strain.delta_t)
                # converting to pycbc timeseries
                hp = TimeSeries(Hp.data.data[:], delta_t=Hp.deltaT,
                                epoch=Hp.epoch)
                hc = TimeSeries(Hc.data.data[:], delta_t=Hc.deltaT,
                                epoch=Hc.epoch)
                hp /= distance_scale
                hc /= distance_scale
                end_time = float(hp.get_end_time())
                start_time = float(hp.get_start_time())
                if end_time < t0 or start_time > t1:
                    continue
            else:
                # roughly estimate if the injection may overlap with the segment
                end_time = inj.get_time_geocent()
                inj_length = sim.SimInspiralTaylorLength(
                    strain.delta_t, inj.mass1 * lal.MSUN_SI,
                    inj.mass2 * lal.MSUN_SI, f_l, 0)
                start_time = end_time - 2 * inj_length
                if end_time < t0 or start_time > t1:
                   continue
                   
                name, phase_order = legacy_approximant_name(inj.waveform)

                # compute the waveform time series
                hp, hc = get_td_waveform(
                    inj, approximant=name, delta_t=strain.delta_t,
                    phase_order=phase_order,
                    f_lower=f_l, distance=inj.distance * distance_scale,
                    **self.extra_args)

                hp._epoch += float(end_time)
                hc._epoch += float(end_time)
                if float(hp.start_time) > t1:
                   continue

            # compute the detector response, taper it if requested
            # and add it to the strain
            signal = detector.project_wave(
                    hp, hc, inj.longitude, inj.latitude, inj.polarization)
            # the taper_timeseries function converts to a LAL TimeSeries
            signal = signal.astype(strain.dtype)
            signal_lal = wfutils.taper_timeseries(signal, inj.taper, return_lal=True)
            add_injection(lalstrain, signal_lal, None)

        strain.data[:] = lalstrain.data.data[:]
Beispiel #41
0
def get_td_from_freqtau(template=None, taper=None, **kwargs):
    """Return time domain ringdown with all the modes specified.

    Parameters
    ----------
    template: object
        An object that has attached properties. This can be used to substitute
        for keyword arguments. A common example would be a row in an xml table.
    taper: {None, float}, optional
        Tapering at the beginning of the waveform with duration taper * tau.
        This option is recommended with timescales taper=1./2 or 1. for
        time-domain ringdown-only injections.
        The abrupt turn on of the ringdown can cause issues on the waveform
        when doing the fourier transform to the frequency domain. Setting
        taper will add a rapid ringup with timescale tau/10.
        Each mode and overtone will have a different taper depending on its tau,
        the final taper being the superposition of all the tapers.
    lmns : list
        Desired lmn modes as strings (lm modes available: 22, 21, 33, 44, 55).
        The n specifies the number of overtones desired for the corresponding
        lm pair (maximum n=8).
        Example: lmns = ['223','331'] are the modes 220, 221, 222, and 330
    f_lmn: float
        Central frequency of the lmn overtone, as many as number of modes.
    tau_lmn: float
        Damping time of the lmn overtone, as many as number of modes.
    amp220 : float
        Amplitude of the fundamental 220 mode.
    amplmn : float
        Fraction of the amplitude of the lmn overtone relative to the
        fundamental mode, as many as the number of subdominant modes.
    philmn : float
        Phase of the lmn overtone, as many as the number of modes. Should also
        include the information from the azimuthal angle (phi + m*Phi).
    inclination : {None, float}, optional
        Inclination of the system in radians. If None, the spherical harmonics
        will be set to 1.
    delta_t : {None, float}, optional
        The time step used to generate the ringdown.
        If None, it will be set to the inverse of the frequency at which the
        amplitude is 1/1000 of the peak amplitude (the minimum of all modes).
    t_final : {None, float}, optional
        The ending time of the output frequency series.
        If None, it will be set to the time at which the amplitude
        is 1/1000 of the peak amplitude (the maximum of all modes).

    Returns
    -------
    hplustilde: FrequencySeries
        The plus phase of a ringdown with the lm modes specified and
        n overtones in frequency domain.
    hcrosstilde: FrequencySeries
        The cross phase of a ringdown with the lm modes specified and
        n overtones in frequency domain.
    """

    input_params = props(template, freqtau_required_args, **kwargs)

    # Get required args
    f_0, tau = lm_freqs_taus(**input_params)
    lmns = input_params['lmns']
    for lmn in lmns:
        if int(lmn[2]) == 0:
            raise ValueError('Number of overtones (nmodes) must be greater '
                             'than zero.')
    # following may not be in input_params
    inc = input_params.pop('inclination', None)
    delta_t = input_params.pop('delta_t', None)
    t_final = input_params.pop('t_final', None)

    if not delta_t:
        delta_t = lm_deltat(f_0, tau, lmns)
    if not t_final:
        t_final = lm_tfinal(tau, lmns)

    kmax = int(t_final / delta_t) + 1
    # Different overtones will have different tapering window-size
    # Find maximum window size to create long enough output vector
    if taper:
        taper_window = int(taper*max(tau.values())/delta_t)
        kmax += taper_window

    outplus = TimeSeries(zeros(kmax, dtype=float64), delta_t=delta_t)
    outcross = TimeSeries(zeros(kmax, dtype=float64), delta_t=delta_t)
    if taper:
        start = - taper * max(tau.values())
        outplus._epoch, outcross._epoch = start, start

    for lmn in lmns:
        l, m, nmodes = int(lmn[0]), int(lmn[1]), int(lmn[2])
        hplus, hcross = get_td_lm(freqs=f_0, taus=tau, l=l, m=m, nmodes=nmodes,
                             taper=taper, inclination=inc, delta_t=delta_t,
                             t_final=t_final, **input_params)
        if not taper:
            outplus.data += hplus.data
            outcross.data += hcross.data
        else:
            outplus = taper_shift(hplus, outplus)
            outcross = taper_shift(hcross, outcross)

    return outplus, outcross
Beispiel #42
0
    def project_wave(self, hp, hc, ra, dec, polarization,
                     method='lal',
                     reference_time=None):
        """Return the strain of a waveform as measured by the detector.
        Apply the time shift for the given detector relative to the assumed
        geocentric frame and apply the antenna patterns to the plus and cross
        polarizations.

        Parameters
        ----------
        hp: pycbc.types.TimeSeries
            Plus polarization of the GW
        hc: pycbc.types.TimeSeries
            Cross polarization of the GW
        ra: float
            Right ascension of source location
        dec: float
            Declination of source location
        polarization: float
            Polarization angle of the source
        method: {'lal', 'constant', 'vary_polarization'}
            The method to use for projecting the polarizations into the
            detector frame. Default is 'lal'.
        reference_time: float, Optional
            The time to use as, a reference for some methods of projection.
            Used by 'constant' and 'vary_polarization' methods. Uses average
            time if not provided.
        """
        # The robust and most fefature rich method which includes
        # time changing antenna patterns and doppler shifts due to the
        # earth rotation and orbit
        if method == 'lal':
            import lalsimulation
            h_lal = lalsimulation.SimDetectorStrainREAL8TimeSeries(
                    hp.astype(np.float64).lal(), hc.astype(np.float64).lal(),
                    ra, dec, polarization, self.lal())
            ts = TimeSeries(
                    h_lal.data.data, delta_t=h_lal.deltaT, epoch=h_lal.epoch,
                    dtype=np.float64, copy=False)

        # 'constant' assume fixed orientation relative to source over the
        # duration of the signal, accurate for short duration signals
        # 'fixed_polarization' applies only time changing orientation
        # but no doppler corrections
        elif method in ['constant', 'vary_polarization']:
            if reference_time is not None:
                rtime = reference_time
            else:
                # In many cases, one should set the reference time if using
                # this method as we don't know where the signal is within
                # the given time series. If not provided, we'll choose
                # the midpoint time.
                rtime = (float(hp.end_time) + float(hp.start_time)) / 2.0

            if method == 'constant':
                time = rtime
            elif method == 'vary_polarization':
                if (not isinstance(hp, TimeSeries) or
                   not isinstance(hc, TimeSeries)):
                    raise TypeError('Waveform polarizations must be given'
                                    ' as time series for this method')

                # this is more granular than needed, may be optimized later
                # assume earth rotation in ~30 ms needed for earth ceneter
                # to detector is completely negligible.
                time = hp.sample_times.numpy()

            fp, fc = self.antenna_pattern(ra, dec, polarization, time)
            dt = self.time_delay_from_earth_center(ra, dec, rtime)
            ts = fp * hp + fc * hc
            ts.start_time = float(ts.start_time) + dt

        # add in only the correction for the time variance in the polarization
        # due to the earth's rotation, no doppler correction applied
        else:
            raise ValueError("Unkown projection method {}".format(method))
        return ts
Beispiel #43
0
def get_td_lm(template=None, taper=None, **kwargs):
    """Return frequency domain lm mode with the given number of overtones.

    Parameters
    ----------
    template: object
        An object that has attached properties. This can be used to substitute
        for keyword arguments. A common example would be a row in an xml table.
    taper: {None, float}, optional
        Tapering at the beginning of the waveform with duration taper * tau.
        This option is recommended with timescales taper=1./2 or 1. for
        time-domain ringdown-only injections.
        The abrupt turn on of the ringdown can cause issues on the waveform
        when doing the fourier transform to the frequency domain. Setting
        taper will add a rapid ringup with timescale tau/10.
        Each overtone will have a different taper depending on its tau, the
        final taper being the superposition of all the tapers.
    final_mass : float
        Mass of the final black hole.
    final_spin : float
        Spin of the final black hole.
    l : int
        l mode (lm modes available: 22, 21, 33, 44, 55).
    m : int
        m mode (lm modes available: 22, 21, 33, 44, 55).
    nmodes: int
        Number of overtones desired (maximum n=8)
    amp220 : float
        Amplitude of the fundamental 220 mode, needed for any lm.
    amplmn : float
        Fraction of the amplitude of the lmn overtone relative to the 
        fundamental mode, as many as the number of subdominant modes.
    philmn : float
        Phase of the lmn overtone, as many as the number of nmodes.
    delta_t : {None, float}, optional
        The time step used to generate the ringdown.
        If None, it will be set to the inverse of the frequency at which the
        amplitude is 1/1000 of the peak amplitude (the minimum of all modes).
    t_final : {None, float}, optional
        The ending time of the output time series.
        If None, it will be set to the time at which the amplitude is 
        1/1000 of the peak amplitude (the maximum of all modes).

    Returns
    -------
    hplustilde: FrequencySeries
        The plus phase of a lm mode with overtones (n) in frequency domain.
    hcrosstilde: FrequencySeries
        The cross phase of a lm mode with overtones (n) in frequency domain.
    """

    input_params = props(template, lm_required_args, **kwargs)

    # Get required args
    amps, phis = lm_amps_phases(**input_params)
    final_mass = input_params.pop('final_mass')
    final_spin = input_params.pop('final_spin')
    l, m = input_params.pop('l'), input_params.pop('m')
    nmodes = input_params.pop('nmodes')
    if int(nmodes) == 0:
        raise ValueError('Number of overtones (nmodes) must be greater '
                         'than zero.')
    # The following may not be in input_params
    delta_t = input_params.pop('delta_t', None)
    t_final = input_params.pop('t_final', None)

    if delta_t is None:
        delta_t = lm_deltat(final_mass, final_spin,
                            ['%d%d%d' % (l, m, nmodes)])
    if t_final is None:
        t_final = lm_tfinal(final_mass, final_spin,
                            ['%d%d%d' % (l, m, nmodes)])

    f_0, tau = get_lm_f0tau(final_mass, final_spin, l, m, nmodes)

    kmax = int(t_final / delta_t) + 1
    # Different overtones will have different tapering window-size
    # Find maximum window size to create long enough output vector
    if taper is not None:
        taper_window = int(taper * max(tau) / delta_t)
        kmax += taper_window

    outplus = TimeSeries(zeros(kmax, dtype=float64), delta_t=delta_t)
    outcross = TimeSeries(zeros(kmax, dtype=float64), delta_t=delta_t)

    for n in range(nmodes):
        hplus, hcross = get_td_qnm(template=None,
                                   taper=taper,
                                   f_0=f_0[n],
                                   tau=tau[n],
                                   phi=phis['%d%d%d' % (l, m, n)],
                                   amp=amps['%d%d%d' % (l, m, n)],
                                   delta_t=delta_t,
                                   t_final=t_final)
        if taper is None:
            outplus.data += hplus.data
            outcross.data += hcross.data
        else:
            outplus = taper_shift(hplus, outplus)
            outcross = taper_shift(hcross, outcross)

    return outplus, outcross
    def rescale_wave(self, M=None, inclination=None, phi=None, distance=None):
        """ Rescale modes and polarizations to given mass, angles, distance.
            Note that this function re-sets the stored values of binary parameters
            and so all future calculations will assume new values unless otherwise
            specified.
        """
        #{{{
        #
        # If MASS has changed, rescale modes
        #
        if self.totalmass != M and M is not None:
            # Rescale the time-axis for all modes
            self.rescaledmodes_real, self.rescaledmodes_imag = {}, {}
            for modeL in np.arange( 2, self.modeLmax+1 ):
                self.rescaledmodes_real[modeL], self.rescaledmodes_imag[modeL] = {}, {}
                for modeM in np.arange( -1*modeL, modeL+1 ):
                    if self.skipM0 and modeM==0: continue
                    self.rescaledmodes_real[modeL][modeM], \
                    self.rescaledmodes_imag[modeL][modeM] = \
                              self.rescale_mode(M, modeL=modeL, modeM=modeM)
            self.totalmass = M
        elif self.totalmass == None and M == None:
            raise IOError("Please provide a total-mass value to rescale")

        #
        # Now rescale with distance and ANGLES
        #
        if inclination is not None: self.inclination = inclination
        if phi is not None: self.phi = phi
        if distance is not None: self.distance = distance

        # Mass / distance scaling pre-factor
        scalefac = self.totalmass * lal.MRSUN_SI / self.distance / lal.PC_SI

        # Orbital phase at the time of merger (time of amplitude peak)
        amp22 = self.get_mode_amplitude(totalmass=self.totalmass,
                                        modeL=2, modeM=2, dimensionless=False)
        iPeak, aPeak = self.get_peak_amplitude(amp=amp22)
        #phase22 = self.get_mode_phase(totalmass=self.totalmass, dimensionless=False)
        #phiOrbMerger = phase22[iPeak] / 2.

        # Combine all modes
        hp, hc = np.zeros(self.n, dtype=float), np.zeros(self.n, dtype=float)

        for modeL in np.arange( 2, self.modeLmax+1 ):
            for modeM in np.arange( -1*modeL, modeL+1 ):
                if self.skipM0 and modeM==0: continue
                # h+ - \ii hx = \Sum Ylm * hlm
                ArcTan2Merger = np.arctan2(self.rescaledmodes_imag[modeL][modeM].data[iPeak],\
                    self.rescaledmodes_real[modeL][modeM].data[iPeak])

                if self.verbose:
                  print "arctan2 at merger after: ", ArcTan2Merger

                #curr_ylm = np.exp(1 * modeM * phiOrbMerger * 1j)
                curr_ylm = np.exp(-1 * ArcTan2Merger * 1j)
                if self.debug and curr_ylm: print curr_ylm / np.abs(curr_ylm)
                curr_ylm *= lal.SpinWeightedSphericalHarmonic(\
                                self.inclination, self.phi, -2, modeL, modeM)
                if self.debug and curr_ylm: print curr_ylm / np.abs(curr_ylm)
                #
                if self.debug:
                  print ( np.arctan2(curr_ylm.imag, curr_ylm.real) + (modeM*phiOrbMerger) ) / np.pi,\
                        ( np.arctan2(curr_ylm.imag, curr_ylm.real) + (ArcTan2Merger) ) / np.pi
                  print ( np.arctan2(curr_ylm.imag, curr_ylm.real) - (modeM*phiOrbMerger) ) / np.pi,\
                        ( np.arctan2(curr_ylm.imag, curr_ylm.real) - (ArcTan2Merger) ) / np.pi
                ##
                hp += self.rescaledmodes_real[modeL][modeM].data * curr_ylm.real - \
                      self.rescaledmodes_imag[modeL][modeM].data * curr_ylm.imag
                hc -= self.rescaledmodes_real[modeL][modeM].data * curr_ylm.imag + \
                      self.rescaledmodes_imag[modeL][modeM].data * curr_ylm.real
        if self.debug: print "END\n\n"

        # Scale amplitude by mass and distance factors
        self.rescaled_hp = TimeSeries(scalefac * hp, delta_t=self.dt, epoch=0)
        self.rescaled_hc = TimeSeries(scalefac * hc, delta_t=self.dt, epoch=0)

        return [self.rescaled_hp, self.rescaled_hc]
Beispiel #45
0
    def apply(self, strain, detector_name, f_lower=None, distance_scale=1):
        """Add injections (as seen by a particular detector) to a time series.

        Parameters
        ----------
        strain : TimeSeries
            Time series to inject signals into, of type float32 or float64.
        detector_name : string
            Name of the detector used for projecting injections.
        f_lower : {None, float}, optional
            Low-frequency cutoff for injected signals. If None, use value
            provided by each injection.
        distance_scale: {1, foat}, optional
            Factor to scale the distance of an injection with. The default is
            no scaling.

        Returns
        -------
        None

        Raises
        ------
        TypeError
            For invalid types of `strain`.
        """
        if strain.dtype not in (float32, float64):
            raise TypeError("Strain dtype must be float32 or float64, not " \
                    + str(strain.dtype))

        lalstrain = strain.lal()
        detector = Detector(detector_name)
        earth_travel_time = lal.REARTH_SI / lal.C_SI
        t0 = float(strain.start_time) - earth_travel_time
        t1 = float(strain.end_time) + earth_travel_time

        # pick lalsimulation injection function
        add_injection = injection_func_map[strain.dtype]

        for inj in self.table:
            # roughly estimate if the injection may overlap with the segment
            end_time = inj.get_time_geocent()
            #CHECK: This is a hack (10.0s); replace with an accurate estimate
            inj_length = 10.0
            eccentricity = 0.0
            polarization = 0.0
            start_time = end_time - 2 * inj_length
            if end_time < t0 or start_time > t1:
                continue

            # compute the waveform time series
            hp, hc = sim.SimBurstSineGaussian(float(inj.q),
                                              float(inj.frequency),
                                              float(inj.hrss),
                                              float(eccentricity),
                                              float(polarization),
                                              float(strain.delta_t))
            hp = TimeSeries(hp.data.data[:], delta_t=hp.deltaT, epoch=hp.epoch)
            hc = TimeSeries(hc.data.data[:], delta_t=hc.deltaT, epoch=hc.epoch)
            hp._epoch += float(end_time)
            hc._epoch += float(end_time)
            if float(hp.start_time) > t1:
                continue

            # compute the detector response, taper it if requested
            # and add it to the strain
            strain = wfutils.taper_timeseries(strain, inj.taper)
            signal_lal = hp.astype(strain.dtype).lal()
            add_injection(lalstrain, signal_lal, None)

        strain.data[:] = lalstrain.data.data[:]
def convert_lalREAL8TimeSeries_to_TimeSeries( h ):
    return TimeSeries(h.data.data, delta_t=h.deltaT, \
                      copy=True, epoch=h.epoch, dtype=h.data.data.dtype)
Beispiel #47
0
class DataBuffer(object):

    """A linear buffer that acts as a FILO for reading in frame data
    """

    def __init__(self, frame_src, 
                       channel_name,
                       start_time,
                       max_buffer=2048, 
                       force_update_cache=True,
                       increment_update_cache=None):
        """ Create a rolling buffer of frame data

        Parameters
        ---------
        frame_src: str of list of strings
            Strings that indicate where to read from files from. This can be a
        list of frame files, a glob, etc.
        channel_name: str
            Name of the channel to read from the frame files
        start_time: 
            Time to start reading from.
        max_buffer: {int, 2048}, Optional
            Length of the buffer in seconds
        """
        self.frame_src = frame_src
        self.channel_name = channel_name
        self.read_pos = start_time
        self.force_update_cache = force_update_cache
        self.increment_update_cache = increment_update_cache

        self.update_cache()
        self.channel_type, self.raw_sample_rate = self._retrieve_metadata(self.stream, self.channel_name)

        raw_size = self.raw_sample_rate * max_buffer
        self.raw_buffer = TimeSeries(zeros(raw_size, dtype=numpy.float64),
                                     copy=False,
                                     epoch=start_time - max_buffer,
                                     delta_t=1.0/self.raw_sample_rate)

    def update_cache(self):
        """Reset the lal cache. This can be used to update the cache if the 
        result may change due to more files being added to the filesystem, 
        for example.
        """
        cache = locations_to_cache(self.frame_src)
        stream = lalframe.FrStreamCacheOpen(cache)
        self.stream = stream

    def _retrieve_metadata(self, stream, channel_name):
        """Retrieve basic metadata by reading the first file in the cache
    
        Parameters
        ----------
        stream: lal stream object
            Stream containing a channel we want to learn about
        channel_name: str
            The name of the channel we want to know the dtype and sample rate of

        Returns
        -------
        channel_type: lal type enum
            Enum value which indicates the dtype of the channel
        sample_rate: int
            The sample rate of the data within this channel
        """
        data_length = lalframe.FrStreamGetVectorLength(channel_name, stream)
        channel_type = lalframe.FrStreamGetTimeSeriesType(channel_name, stream)
        create_series_func = _fr_type_map[channel_type][2]
        get_series_metadata_func = _fr_type_map[channel_type][3]
        series = create_series_func(channel_name, stream.epoch, 0, 0,
                            lal.ADCCountUnit, 0)
        get_series_metadata_func(series, stream)
        return channel_type, int(1.0/series.deltaT)

    def _read_frame(self, blocksize):
        """Try to read the block of data blocksize seconds long

        Parameters
        ----------
        blocksize: int
            The number of seconds to attempt to read from the channel

        Returns
        -------
        data: TimeSeries
            TimeSeries containg 'blocksize' seconds of frame data

        Raises
        ------
        RuntimeError:
            If data cannot be read for any reason
        """
        try:
            read_func = _fr_type_map[self.channel_type][0]
            dtype = _fr_type_map[self.channel_type][1]
            data = read_func(self.stream, self.channel_name,
                             self.read_pos, int(blocksize), 0)
            return TimeSeries(data.data.data, delta_t=data.deltaT,
                              epoch=self.read_pos, 
                              dtype=dtype)     
        except Exception as e:
            raise RuntimeError('Cannot read requested frame data') 

    def null_advance(self, blocksize):
        """Advance and insert zeros

        Parameters
        ----------
        blocksize: int
            The number of seconds to attempt to read from the channel
        """
        self.raw_buffer.roll(-int(blocksize * self.raw_sample_rate))
        self.read_pos += blocksize       
        self.raw_buffer.start_time += blocksize

    def advance(self, blocksize):
        """Add blocksize seconds more to the buffer, push blocksize seconds
        from the beginning.

        Parameters
        ----------
        blocksize: int
            The number of seconds to attempt to read from the channel
        """
        ts = self._read_frame(blocksize)

        self.raw_buffer.roll(-len(ts))
        self.raw_buffer[-len(ts):] = ts[:] 
        self.read_pos += blocksize
        self.raw_buffer.start_time += blocksize
        return ts
        
    def update_cache_by_increment(self, blocksize):
        """Update the internal cache by starting from the first frame
        and incrementing.

        Guess the next frame file name by incrementing from the first found
        one. This allows a pattern to be used for the GPS folder of the file,
        which is indicated by `GPSX` where x is the number of digits to use.

        Parameters
        ----------
        blocksize: int
            Number of seconds to increment the next frame file.
        """
        start = float(self.raw_buffer.end_time)
        end = float(start + blocksize)
        
        if not hasattr(self, 'dur'):       
            fname = glob.glob(self.frame_src[0])[0]
            fname = os.path.splitext(os.path.basename(fname))[0].split('-')
            
            self.beg = '-'.join([fname[0], fname[1]])
            self.ref = int(fname[2])
            self.dur = int(fname[3])
        
        fstart = int(self.ref + numpy.floor((start - self.ref) / float(self.dur)) * self.dur)
        starts = numpy.arange(fstart, end, self.dur).astype(numpy.int)
        
        keys = []
        for s in starts:
            pattern = self.increment_update_cache
            if 'GPS' in pattern:
                n = int(pattern[int(pattern.index('GPS') + 3)])
                pattern = pattern.replace('GPS%s' % n, str(s)[0:n])
                
            name = '%s/%s-%s-%s.gwf' % (pattern, self.beg, s, self.dur)
            # check that file actually exists, else abort now
            if not os.path.exists(name):
                logging.info("%s does not seem to exist yet" % name)
                raise RuntimeError

            keys.append(name)
        cache = locations_to_cache(keys)
        stream = lalframe.FrStreamCacheOpen(cache)
        self.stream = stream
        self.channel_type, self.raw_sample_rate = self._retrieve_metadata(self.stream, self.channel_name)

    def attempt_advance(self, blocksize, timeout=10):
        """ Attempt to advance the frame buffer. Retry upon failure, except
        if the frame file is beyond the timeout limit.

        Parameters
        ----------
        blocksize: int
            The number of seconds to attempt to read from the channel
        timeout: {int, 10}, Optional
            Number of seconds before giving up on reading a frame

        Returns
        -------
        data: TimeSeries
            TimeSeries containg 'blocksize' seconds of frame data
        """
        if self.force_update_cache:
            self.update_cache()
        
        try:
            if self.increment_update_cache:
                self.update_cache_by_increment(blocksize)

            return DataBuffer.advance(self, blocksize)

        except RuntimeError:
            if lal.GPSTimeNow() > timeout + self.raw_buffer.end_time:
                # The frame is not there and it should be by now, so we give up
                # and treat it as zeros
                logging.info('Frame missing, giving up...')
                DataBuffer.null_advance(self, blocksize)
                return None
            else:
                # I am too early to give up on this frame, so we should try again
                logging.info('Frame missing, waiting a bit more...')
                time.sleep(1)
                return self.attempt_advance(blocksize, timeout=timeout)
    def taper_filter_waveform( self, hpsamp=None, hcsamp=None, tapermethod='planck', \
              ttaper1=100, ttaper2=1000, ftaper3=0.1, ttaper4=100., \
              npad=00, f_filter=-1., \
              verbose=False):
        """Tapers using a Plank-taper window and high-pass filters.
        **IMPORTANT** The domain of the window is passed as ttaper{1,2,3,4} values.
        ttaper1 : time (in total-mass units) from the start where the window starts
        ttaper2 : width of start window
        ftaper3 : fraction by which amplitude should fall after its peak,
                    marking where the rolldown window starts
        ttaper4 : width of the rolldown window

        Currently supported tapermethods: 'planck' [default], 'cosine'
        """
        #{{{
        if hpsamp and hcsamp: hp0, hc0 = [hpsamp, hcsamp]
        elif self.rescaled_hp is not None and self.rescaled_hc is not None:
            totalmass = self.totalmass
            hp0, hc0 = [self.rescaled_hp, self.rescaled_hc]
        else: raise IOError("Please provide either the total mass (and strain)")
        # Check windowing extents
        if ttaper1 > ttaper2 or \
            (ttaper1+ttaper2+ttaper4) > (len(hp0)*hp0.delta_t/self.totalmass/lal.MTSUN_SI):
            raise IOError("Invalid window configuration with [%f,%f,%f,%f]" %\
                            (ttaper1, ttaper2, ftaper3, ttaper4))
        #
        hp = TimeSeries( hp0.data, dtype=hp0.dtype, delta_t=hp0.delta_t, epoch=hp0._epoch )
        hc = TimeSeries( hc0.data, dtype=hc0.dtype, delta_t=hc0.delta_t, epoch=hc0._epoch )
        # Get actual waveform length
        for idx in np.arange( len(hp)-1, 0, -1 ):
            if hp[idx]==0 and hc[idx]==0: break
        N = idx #np.where( hp.data == 0 )[0][0]
        # Check npad
        if abs(len(hp) - N) < npad:
            print >>sys.stdout, "Ignoring npad..\n"
            npad = 0
        else:
            # Prepend some zeros to the waveform (assuming there are ''npad'' zeros at the end)
            hp = zero_pad_beginning( hp, steps=npad )
            hc = zero_pad_beginning( hc, steps=npad )
        #
        # ##########   Construct the taper window    #############
        #
        # Get the amplitude peak
        amp = amplitude_from_polarizations(hp, hc)
        max_a, nPeak = amp.abs_max_loc()

        # First get the starting-half indices
        ttapers = np.array([ttaper1, ttaper2], dtype=np.float128)
        ntapers = np.int64(\
              np.round(ttapers * self.totalmass * lal.MTSUN_SI * self.sample_rate))
        ntaper1, ntaper2 = ntapers
        ntaper2 += ntaper1

        if ntaper2 < ntaper1:
            raise RuntimeError("Could not configure starting taper-window")

        # Next get the next-half indices
        amp_after_peak = amp[nPeak:]
        iA, vA = min(enumerate(amp_after_peak),key=lambda x:abs(x[1] - ftaper3*max_a))
        ntaper3 = nPeak + iA

        #iB, vB = min(enumerate(amp_after_peak),key=lambda x:abs(x[1] - 0.01*max_a))
        #iB = iA + ntaper4
        ntaper4 = np.int64(\
              np.round(ttaper4 * self.totalmass * lal.MTSUN_SI * self.sample_rate))
        ntaper4 += ntaper3

        if ntaper3 <= nPeak or ntaper4 < ntaper3:
            tmp_data = amp_after_peak.data
            for idx in range( len(amp_after_peak) ):
                if tmp_data[idx] < ftaper3*max_a: break
            ntaper3 = nPeak + idx
            for idx in range( len(amp_after_peak) ):
                if tmp_data[idx] < ftaper4*max_a: break
            ntaper4 = nPeak + idx

        if ntaper3 <= nPeak or ntaper4 < ntaper3:
            raise RuntimeError("Could not configure ringdown tapering window")

        ntapers = np.array([ntaper1, ntaper2, ntaper3, ntaper4], dtype=np.int64)
        ttapers = np.float128(ntapers) / self.sample_rate
        time_array = hp.sample_times.data - np.float(hp._epoch)
        #
        # Actual windowing function
        #
        region1 = np.zeros(ntaper1)
        region2 = np.zeros(ntaper2 - ntaper1)
        region3 = np.ones(ntaper3 - ntaper2)
        region4 = np.zeros(ntaper4 - ntaper3)
        region5 = np.zeros(len(hp) - ntaper4)
        #
        if 'planck' in tapermethod:
            np.seterr(divide='raise',over='raise',under='ignore',invalid='raise')
            t1, t2, t3, t4 = ttapers
            i1, i2, i3, i4 = ntapers
            if verbose:
                print "window times = ", t1, t2, t3, t4, " idxs = ", i1, i2, i3, i4
            #
            for i in range(len(region2)):
                if i == 0:
                    region2[i] = 0
                    continue
                try:
                    region2[i] = 1./(np.exp( ((t2-t1)/(time_array[i+i1]-t1)) + \
                                      ((t2-t1)/(time_array[i+i1]-t2)) ) + 1)
                except:
                    if time_array[i+i1]>0.9*t1 and time_array[i+i1]<1.1*t1: region2[i] = 0
                    if time_array[i+i1]>0.9*t2 and time_array[i+i1]<1.1*t2: region2[i] = 1.
            #
            for i in range(len(region4)):
                if i == 0:
                    region4[i] = 1.
                    continue
                try:
                    region4[i] = 1./(np.exp( ((t3-t4)/(time_array[i+i3]-t3)) + \
                                      ((t3-t4)/(time_array[i+i3]-t4)) ) + 1)
                except:
                    if time_array[i+i3]>0.9*t3 and time_array[i+i3]<1.1*t3: region4[i] = 1.
                    if time_array[i+i3]>0.9*t4 and time_array[i+i3]<1.1*t4: region4[i] = 0
            #
            if verbose and False:
                import matplotlib.pyplot as plt
                plt.plot(np.arange(len(region1))*hp.delta_t, region1)
                offset = len(region1)
                plt.plot((offset+np.arange(len(region2)))*hp.delta_t, region2)
                offset += len(region2)
                plt.plot((offset+np.arange(len(region3)))*hp.delta_t, region3)
                offset += len(region3)
                plt.plot((offset+np.arange(len(region4)))*hp.delta_t, region4)
                plt.grid()
                plt.show()

            win = np.concatenate((region1,region2,region3,region4,region5))
        elif 'cos' in tapermethod:
            win = region1
            win12 = 0.5 + 0.5*np.array([np.cos( np.pi*(float(j-ntaper1)/float(ntaper2-\
                                  ntaper1) - 1)) for j in np.arange(ntaper1,ntaper2)])
            win = np.append(win, win12)
            win = np.append(win, region3)
            win34 = 0.5 - 0.5*np.array([np.cos( np.pi*(float(j-ntaper3)/float(ntaper4-\
                                  ntaper3) - 1)) for j in np.arange(ntaper3,ntaper4)])
            win = np.append(win, win34)
            win = np.append(win, region5)
        else: raise IOError("Please specify valid taper-method")
        #
        # ########### Taper & Filter the waveform ##############
        #
        hp.data *= win
        hc.data *= win
        #
        # High pass filter the waveform
        if f_filter > 0:
            hplal = convert_TimeSeries_to_lalREAL8TimeSeries( hp )
            hclal = convert_TimeSeries_to_lalREAL8TimeSeries( hc )
            lal.HighPassREAL8TimeSeries( hplal, f_filter, 0.9, 8 )
            lal.HighPassREAL8TimeSeries( hclal, f_filter, 0.9, 8 )
            hp = convert_lalREAL8TimeSeries_to_TimeSeries( hplal )
            hc = convert_lalREAL8TimeSeries_to_TimeSeries( hclal )

        return hp, hc
Beispiel #49
0
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
Beispiel #50
0
class DataBuffer(object):
    """ A linear buffer that acts as a FILO for reading in frame data
    """
    def __init__(self, frame_src, 
                       channel_name,
                       start_time,
                       max_buffer=2048):
        """ Create a rolling buffer of frame data

        Parameters
        ---------
        frame_src: str of list of strings
            Strings that indicate where to read from files from. This can be a
        list of frame files, a glob, etc.
        channel_name: str
            Name of the channel to read from the frame files
        start_time: 
            Time to start reading from.
        max_buffer: {int, 2048}, Optional
            Length of the buffer in seconds
        """
        self.frame_src = frame_src
        self.channel_name = channel_name
        self.read_pos = start_time

        self.update_cache()
        self.channel_type, self.sample_rate = self._retrieve_metadata(self.stream, self.channel_name)

        raw_size = self.sample_rate * max_buffer
        self.raw_buffer = TimeSeries(zeros(raw_size, dtype=numpy.float64),
                                     copy=False,
                                     epoch=start_time - max_buffer,
                                     delta_t=1.0/self.sample_rate)

    def update_cache(self):
        """ Reset the lal cache. This can be used to update the cache if the 
        result may change due to more files being added to the filesystem, 
        for example.
        """
        cache = locations_to_cache(self.frame_src)
        stream = lalframe.FrStreamCacheOpen(cache)
        self.stream = stream

    def _retrieve_metadata(self, stream, channel_name):
        """ Retrieve basic metadata by reading the first file in the cache
    
        Parameters
        ----------
        stream: lal stream object
            Stream containing a channel we want to learn about
        channel_name: str
            The name of the channel we want to know the dtype and sample rate of

        Returns
        -------
        channel_type: lal type enum
            Enum value which indicates the dtype of the channel
        sample_rate: int
            The sample rate of the data within this channel
        """
        data_length = lalframe.FrStreamGetVectorLength(channel_name, stream)
        channel_type = lalframe.FrStreamGetTimeSeriesType(channel_name, stream)
        create_series_func = _fr_type_map[channel_type][2]
        get_series_metadata_func = _fr_type_map[channel_type][3]
        series = create_series_func(channel_name, stream.epoch, 0, 0,
                            lal.ADCCountUnit, 0)
        get_series_metadata_func(series, stream)
        return channel_type, int(1.0/series.deltaT)        

    def _read_frame(self, blocksize):
        """ Try to read the block of data blocksize seconds long

        Parameters
        ----------
        blocksize: int
            The number of seconds to attempt to read from the channel

        Returns
        -------
        data: TimeSeries
            TimeSeries containg 'blocksize' seconds of frame data

        Raises
        ------
        RuntimeError:
            If data cannot be read for any reason
        """
        try:
            read_func = _fr_type_map[self.channel_type][0]
            dtype = _fr_type_map[self.channel_type][1]
            data = read_func(self.stream, self.channel_name, self.read_pos, blocksize, 0)
            return TimeSeries(data.data.data, delta_t=data.deltaT,
                              epoch=self.read_pos, 
                              dtype=dtype)     
        except:
            raise RuntimeError('Cannot read requested frame data') 

    def null_advance(self, blocksize):
        """ Advance and insert zeros

        Parameters
        ----------
        blocksize: int
            The number of seconds to attempt to read from the channel
        """
        self.raw_buffer.roll(-int(blocksize * self.sample_rate))       
        self.raw_buffer.start_time += blocksize

    def advance(self, blocksize):
        """ Add blocksize seconds more to the buffer, push blocksize seconds
        from the beginning.

        Parameters
        ----------
        blocksize: int
            The number of seconds to attempt to read from the channel
        """
        ts = self._read_frame(blocksize)

        self.raw_buffer.roll(-len(ts))
        self.raw_buffer[-len(ts):] = ts[:] 
        self.read_pos += blocksize
        self.raw_buffer.start_time += blocksize
        return ts

    def attempt_advance(self, blocksize, timeout=10):
        """ Attempt to advance the frame buffer. Retry upon failure, except
        if the frame file is beyond the timeout limit.

        Parameters
        ----------
        blocksize: int
            The number of seconds to attempt to read from the channel
        timeout: {int, 10}, Optional
            Number of seconds before giving up on reading a frame

        Returns
        -------
        data: TimeSeries
            TimeSeries containg 'blocksize' seconds of frame data
        """
        self.update_cache()
        
        try:
            return DataBuffer.advance(self, blocksize)
        except ValueError:
            if lal.GPSTimeNow() > timeout + self.raw_buffer.end_time:
                # The frame is not there and it should be by now, so we give up
                # and treat it as zeros
                self.null_advance(blocksize)
                return None
            else:
                # I am too early to give up on this frame, so we should try again
                return self.attempt_advance(self, blocksize, timeout=timeout)