Skip to content

GPU support  #2

@tclements

Description

@tclements

Hey, I saw @ChengXin was implementing GPU support. Looks fast! We might want to consider using CuPy - it's a Numpy compatible library for CUDA. One nice thing about CuPy is we can use the same code on CPU/GPU. Here's the correlate functions with CuPy:

import numpy as np
import scipy
from scipy.fftpack.helper import next_fast_len
import cupy as cp


def correlate(fft1,fft2, maxlag, Nfft=None, method='cross_correlation'):
    """This function takes ndimensional *data* array, computes the cross-correlation in the frequency domain
    and returns the cross-correlation function between [-*maxlag*:*maxlag*].

    :type fft1: :class:`numpy.ndarray' 
    :param fft1: This array contains the fft of each timeseries to be cross-correlated.
    :type maxlag: int
    :param maxlag: This number defines the number of samples (N=2*maxlag + 1) of the CCF that will be returned.

    :rtype: :class:`numpy.ndarray`
    :returns: The cross-correlation function between [-maxlag:maxlag]
    """
    # Speed up FFT by padding to optimal size for FFTPACK

    xp = cp.get_array_module(fft1)

    if fft1.ndim == 1:
        axis = 0
    elif fft1.ndim == 2:
        axis = 1

    if Nfft is None:
        Nfft = next_fast_len(int(fft1.shape[axis]))

    maxlag = np.round(maxlag)

    Nt = fft1.shape[axis]

    corr = xp.multiply(xp.conj(fft1), fft2)
    if method == 'deconv':
        corr /= noise.smooth(xp.abs(fft1),half_win=5) ** 2
    elif method == 'cohrence':
        corr /= noise.smooth(xp.abs(fft1),half_win=5)
        corr /= noise.smooth(xp.abs(fft2),half_win=5)

    corr = xp.real(xp.fft.irfft(corr,n=Nfft,axis=axis)) 
    corr = xp.fft.fftshift(corr,axis)

    tcorr = np.arange(-Nt//2 + 1, Nt//2)
    ind = np.where(np.abs(tcorr) <= maxlag)[0]
    if axis == 1:
        corr = corr[:,ind]
    else:
        corr = corr[ind]

    return corr


def whiten(data, delta, freqmin, freqmax,Nfft=None):
    """This function takes 1-dimensional *data* timeseries array,
    goes to frequency domain using fft, whitens the amplitude of the spectrum
    in frequency domain between *freqmin* and *freqmax*
    and returns the whitened fft.

    :type data: :class:`numpy.ndarray`
    :param data: Contains the 1D time series to whiten
    :type Nfft: int
    :param Nfft: The number of points to compute the FFT
    :type delta: float
    :param delta: The sampling frequency of the `data`
    :type freqmin: float
    :param freqmin: The lower frequency bound
    :type freqmax: float
    :param freqmax: The upper frequency bound

    :rtype: :class:`numpy.ndarray`
    :returns: The FFT of the input trace, whitened between the frequency bounds
    """

    xp = cp.get_array_module(data)

    # Speed up FFT by padding to optimal size for FFTPACK
    if data.ndim == 1:
        axis = 0
    elif data.ndim == 2:
        axis = 1

    if Nfft is None:
        Nfft = next_fast_len(int(data.shape[axis]))

    pad = 100
    Nfft = int(Nfft)
    freqVec = np.fft.rfftfreq(Nfft, d=delta)

    ind = np.where((freqVec >= freqmin) & (freqVec <= freqmax))[0]
    low = ind[0] - pad
    if low <= 0:
        low = 1

    left = ind[0]
    right = ind[-1]
    high = ind[-1] + pad
    if high > Nfft / 2:
        high = int(Nfft // 2)

    FFTRawSign = xp.fft.rfft(data, Nfft,axis=axis)

    # Left tapering:
    if axis == 1:
        FFTRawSign[:,0:low] *= 0
        FFTRawSign[:,low:left] = xp.cos(
            xp.linspace(xp.pi / 2., xp.pi, left - low)) ** 2 * xp.exp(
            1j * xp.angle(FFTRawSign[:,low:left]))
        # Pass band:
        FFTRawSign[:,left:right] = xp.exp(1j * xp.angle(FFTRawSign[:,left:right]))
        # Right tapering:
        FFTRawSign[:,right:high] = xp.cos(
            xp.linspace(0., xp.pi / 2., high - right)) ** 2 * xp.exp(
            1j * xp.angle(FFTRawSign[:,right:high]))
        FFTRawSign[:,high:Nfft + 1] *= 0

    else:
        FFTRawSign[0:low] *= 0
        FFTRawSign[low:left] = xp.cos(
            xp.linspace(xp.pi / 2., xp.pi, left - low)) ** 2 * xp.exp(
            1j * xp.angle(FFTRawSign[low:left]))
        # Pass band:
        FFTRawSign[left:right] = xp.exp(1j * xp.angle(FFTRawSign[left:right]))
        # Right tapering:
        FFTRawSign[right:high] = xp.cos(
            xp.linspace(0., xp.pi / 2., high - right)) ** 2 * xp.exp(
            1j * xp.angle(FFTRawSign[right:high]))
        FFTRawSign[high:Nfft + 1] *= 0

    return FFTRawSign

Metadata

Metadata

Labels

enhancementNew feature or request

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions