Ejemplo n.º 1
0
def get_shifts(st, s, baz):
    '''
    Calculates the shifts (as an integer number of samples in the time series) for every station in a stream of time series seismograms for a slowness vector of given magnitude and backazimuth.
    
    The shift is that which needs to be applied in order to align an arrival (arriving with slowness s and backazimuth baz) with the same arrival at the array reference point (the location of the station that makes up the first trace in the stream).

    Parameters
    ----------
    st : ObsPy Stream object
        Stream of SAC format seismograms for the seismic array, length K = no. of stations in array
    s  : float
        Magnitude of slowness vector, in s / km
    baz : float
        Backazimuth of slowness vector, (i.e. angle from North back to epicentre of event)

    Returns
    -------
    shifts : list
        List of integer delays at each station in the array, also length K
    '''
    theta = [
    ]  # Angular position of each station, measured clockwise from North
    r = []  # Distance of each station

    # First station is reference point, so has zero position vector
    theta.append(0.0)
    r.append(0.0)

    geometry = get_station_coordinates(st) / 1000.  # in km

    # For each station, get distance from array reference point (first station), and the angular displacement clockwise from north
    for station in geometry[1:]:
        r_x = station[0]  # x-component of position vector
        r_y = station[1]  # y-component of position vector

        # theta is angle c/w from North to position vector of station; need to compute diffently for each quadrant
        if r_x >= 0 and r_y >= 0:
            theta.append(np.degrees(np.arctan(r_x / r_y)))
        elif r_x > 0 and r_y < 0:
            theta.append(180 + np.degrees(np.arctan(r_x / r_y)))
        elif r_x <= 0 and r_y <= 0:
            theta.append(180 + np.degrees(np.arctan(r_x / r_y)))
        else:
            theta.append(360 + np.degrees(np.arctan(r_x / r_y)))

        r.append(np.sqrt(r_x**2 + r_y**2))

    # Find angle between station position vector and slowness vector in order to compute dot product

    # Angle between slowness and position vectors, measured clockwise
    phi = [180 - baz + th for th in theta]

    sampling_rate = st[0].stats.sampling_rate

    shifts = []

    # Shift is dot product. The minus sign is because a positive time delay needs to be corrected by a negative shift in order to stack
    for i in range(0, len(st)):

        shifts.append(
            -1 *
            int(round(r[i] * s * np.cos(np.radians(phi[i])) * sampling_rate)))

    return shifts
Ejemplo n.º 2
0
def get_shifts(st, s, baz):
    """
    Calculates the shifts (as an integer number of samples in the time series) for every station in a stream of time series seismograms for a slowness vector of given magnitude and backazimuth.
    
    The shift is that which needs to be applied in order to align an arrival (arriving with slowness s and backazimuth baz) with the same arrival at the array reference point (the location of the station that makes up the first trace in the stream).

    Parameters
    ----------
    st : ObsPy Stream object
        Stream of SAC format seismograms for the seismic array, length K = no. of stations in array
    s  : float
        Magnitude of slowness vector, in s / km
    baz : float
        Backazimuth of slowness vector, (i.e. angle from North back to epicentre of event)

    Returns
    -------
    shifts : list
        List of integer delays at each station in the array, also length K
    """
    theta = []  # Angular position of each station, measured clockwise from North
    r = []  # Distance of each station

    # First station is reference point, so has zero position vector
    theta.append(0.0)
    r.append(0.0)

    geometry = get_station_coordinates(st) / 1000.0  # in km

    # For each station, get distance from array reference point (first station), and the angular displacement clockwise from north
    for station in geometry[1:]:
        r_x = station[0]  # x-component of position vector
        r_y = station[1]  # y-component of position vector

        # theta is angle c/w from North to position vector of station; need to compute diffently for each quadrant
        if r_x >= 0 and r_y >= 0:
            theta.append(np.degrees(np.arctan(r_x / r_y)))
        elif r_x > 0 and r_y < 0:
            theta.append(180 + np.degrees(np.arctan(r_x / r_y)))
        elif r_x <= 0 and r_y <= 0:
            theta.append(180 + np.degrees(np.arctan(r_x / r_y)))
        else:
            theta.append(360 + np.degrees(np.arctan(r_x / r_y)))

        r.append(np.sqrt(r_x ** 2 + r_y ** 2))

    # Find angle between station position vector and slowness vector in order to compute dot product

    # Angle between slowness and position vectors, measured clockwise
    phi = [180 - baz + th for th in theta]

    sampling_rate = st[0].stats.sampling_rate

    shifts = []

    # Shift is dot product. The minus sign is because a positive time delay needs to be corrected by a negative shift in order to stack
    for i in range(0, len(st)):

        shifts.append(-1 * int(round(r[i] * s * np.cos(np.radians(phi[i])) * sampling_rate)))

    return shifts
Ejemplo n.º 3
0
def fk_analysis(st, smax, fmin, fmax, tmin, tmax, stat='power'):
    '''
    Performs frequency-wavenumber space (FK) analysis on a stream of time series data for a given slowness range, frequency band and time window.
    
    For an input stream of length K, the output is a K x K array with values of the chosen statistic calculated on a slowness grid in the x and y spatial dimensions. This statistic can be one of:-

    * 'power' - the power in frequency-domain stack
    * 'semblance' - the semblance calculated in the frequency domain
    * 'F' - the F-statistic calculated in the frequency domain
    
    Before the FK analysis is performed, the seismograms are cut to a time window between tmin and tmax, and the data is bandpass-filtered between frequencies fmin and fmax.


    Parameters
    ----------
    st : ObsPy Stream object
        Stream of SAC format seismograms for the seismic array, length K = no. of stations in array
    smax  : float
        Maximum magnitude of slowness, used for constructing the slowness grid in both x and y directions 
    fmin  : float
        Lower end of frequency band to perform FK analysis over
    fmax  : float
        Upper end of frequency band to perform FK analysis over
    tmin : float
        Start of time window, seismograms are cut between tmin and tmax before FK starts
    tmax : int
        End of time window, seismograms are cut between tmin and tmax before FK starts
    stat : string
        Statistic that is to be calculated over the slowness grid, either 'power', 'semblance', or 'F'
        
    Returns
    -------
    fk : NumPy array
        The chosen statistic calculated at each point in a K x K grid of slowness values in the x and y directions
    '''
    
    assert (stat == 'power' or stat == 'semblance' or stat == 'F'), "Argument 'stat' must be one of 'power', 'semblance' or 'F'"
    
    st = st.copy().trim(starttime=tmin, endtime=tmax)    
    
    # Retrieve metadata: time step and number of stations to be stacked
    delta = st[0].stats.delta
    nbeam = len(st)
    
    # Pre-process, and filter to frequency window
    st.detrend()
    st.taper(type='cosine', max_percentage=0.05)
    
    st = st.copy().filter("bandpass", freqmin=fmin, freqmax=fmax)   
    
    npts = st[0].stats.npts
    
    # Computer Fourier transforms for each trace
    fft_st = np.zeros((nbeam, (npts / 2) + 1), dtype=complex) # Length of real FFT is only half that of time series data
    for i, tr in enumerate(st):
        fft_st[i, :] = np.fft.rfft(tr.data) # Only need positive frequencies, so use rfft

    freqs = np.fft.fftfreq(npts, delta)[0:(npts / 2) + 1]
    
    # Slowness grid
    slow_x = np.linspace(-smax, smax, nbeam)
    slow_y = np.linspace(-smax, smax, nbeam)
    
    # Array geometry
    x, y = np.split(get_station_coordinates(st)[:, :2], 2, axis=1)
    # convert to km
    x /= 1000.
    y /= 1000. 
    
    # Calculate the F-K spectrum
    fk = np.zeros((nbeam, nbeam))
    for ii in range(nbeam):
        for jj in range(nbeam):
            dt = slow_x[jj] * x + slow_y[ii] * y
            beam = np.sum(np.exp(-1j * 2 * np.pi * np.outer(dt, freqs)) * fft_st / nbeam, axis=0)
            fk[ii, jj] = np.vdot(beam, beam).real
    
    if stat == 'semblance' or stat == 'F':
        tracepower = np.vdot(fft_st, fft_st).real
        
        if stat == 'semblance':
            fk_semb = nbeam * fk / tracepower
            return fk_semb
    
        elif stat == 'F':
            fk_F = (nbeam - 1)* nbeam * fk / (tracepower - nbeam * fk) 
            return fk_F
    
    else:
        return fk