Ejemplo n.º 1
0
def smPSA(data, samp_rate):
    """
    ShakeMap pseudo-spectral parameters

    Compute 5% damped PSA at 0.3, 1.0, and 3.0 seconds.

    Data must be an acceleration Trace

    :type data: :class:`obspy.trace`
    :param data: Data in acceleration to convolve with pendulum at freq.
    :type delta: float
    :param delta: sample rate (samples per sec)
    :rtype: (float, float, float)
    :return: PSA03, PSA10, PSA30
    """

    D = 0.05  # 5% damping

    out = []
    periods = [0.3, 1.0, 3.0]
    for T in periods:
        freq = 1.0 / T
        omega = (2 * 3.14159 * freq)**2

        paz_sa = cornFreq2Paz(freq, damp=D)
        paz_sa['sensitivity'] = omega
        paz_sa['zeros'] = []
        dd = seisSim(data.data,
                     samp_rate,
                     paz_remove=None,
                     paz_simulate=paz_sa,
                     taper=True,
                     simulate_sensitivity=True,
                     taper_fraction=0.05)

        if abs(max(dd)) >= abs(min(dd)):
            psa = abs(max(dd))
        else:
            psa = abs(min(dd))
        out.append(psa)

    return out
Ejemplo n.º 2
0
def smPSA(data, samp_rate):
    """
    ShakeMap pseudo-spectral parameters

    Compute 5% damped PSA at 0.3, 1.0, and 3.0 seconds.

    Data must be an acceleration Trace

    :type data: :class:`obspy.trace`
    :param data: Data in acceleration to convolve with pendulum at freq.
    :type delta: float
    :param delta: sample rate (samples per sec)
    :rtype: (float, float, float)
    :return: PSA03, PSA10, PSA30
    """

    D = 0.05	# 5% damping

    out = []
    periods = [0.3, 1.0, 3.0]
    for T in periods:
        freq = 1.0 / T
        omega = (2 * 3.14159 * freq) ** 2

        paz_sa = cornFreq2Paz(freq, damp=D)
        paz_sa['sensitivity'] = omega
        paz_sa['zeros'] = []
        dd = seisSim(data.data, samp_rate, paz_remove=None, paz_simulate=paz_sa,
                     taper=True, simulate_sensitivity=True, taper_fraction=0.05)

        if abs(max(dd)) >= abs(min(dd)):
            psa = abs(max(dd))
        else:
            psa = abs(min(dd))
        out.append(psa)

    return out
Ejemplo n.º 3
0
def pgm(data, delta, freq, damp=0.1):
    """
    Peak ground motion parameters

    Compute the maximal displacement, velocity, acceleration and the peak
    ground acceleration at a certain frequency (standard frequencies for
    ShakeMaps are 0.3/1.0/3.0 Hz).

    Data must be displacement

    :type data: :class:`~numpy.ndarray`
    :param data: Data in displacement to convolve with pendulum at freq.
    :type delta: float
    :param delta: Sampling interval
    :type freq: float
    :param freq: Frequency in Hz.
    :type damp: float
    :param damp: damping factor. Default is set to 0.1
    :rtype: (float, float, float, float)
    :return: Peak Ground Acceleration, maximal displacement, velocity,
        acceleration
    """
    data = data.copy()

    # Displacement
    if abs(max(data)) >= abs(min(data)):
        m_dis = abs(max(data))
    else:
        m_dis = abs(min(data))

    # Velocity
    data = np.gradient(data, delta)
    if abs(max(data)) >= abs(min(data)):
        m_vel = abs(max(data))
    else:
        m_vel = abs(min(data))

    # Acceleration
    data = np.gradient(data, delta)
    if abs(max(data)) >= abs(min(data)):
        m_acc = abs(max(data))
    else:
        m_acc = abs(min(data))

    samp_rate = 1.0 / delta
    T = freq * 1.0
    D = damp
    omega = (2 * 3.14159 * T) ** 2

    paz_sa = cornFreq2Paz(T, damp=D)
    paz_sa["sensitivity"] = omega
    paz_sa["zeros"] = []
    data = seisSim(
        data,
        samp_rate,
        paz_remove=None,
        paz_simulate=paz_sa,
        taper=True,
        simulate_sensitivity=True,
        taper_fraction=0.05,
    )

    if abs(max(data)) >= abs(min(data)):
        pga = abs(max(data))
    else:
        pga = abs(min(data))

    return (pga, m_dis, m_vel, m_acc)
Ejemplo n.º 4
0
def pgm(data, delta, freq, damp=0.1):
    """
    Peak ground motion parameters

    Compute the maximal displacement, velocity, acceleration and the peak
    ground acceleration at a certain frequency (standard frequencies for
    ShakeMaps are 0.3/1.0/3.0 Hz).

    Data must be displacement

    :type data: :class:`~numpy.ndarray`
    :param data: Data in dispalcement to convolve with pendulum at freq.
    :type delta: float
    :param delta: Sampling interval
    :type freq: float
    :param freq: Frequency in Hz.
    :type damp: float
    :param damp: damping factor. Default is set to 0.1
    :rtype: (float, float, float, float)
    :return: Peak Ground Acceleration, maximal displacement, velocity,
        acceleration
    """
    data = data.copy()

    # Displacement
    if abs(max(data)) >= abs(min(data)):
        m_dis = abs(max(data))
    else:
        m_dis = abs(min(data))

    # Velocity
    data = np.gradient(data, delta)
    if abs(max(data)) >= abs(min(data)):
        m_vel = abs(max(data))
    else:
        m_vel = abs(min(data))

    # Acceleration
    data = np.gradient(data, delta)
    if abs(max(data)) >= abs(min(data)):
        m_acc = abs(max(data))
    else:
        m_acc = abs(min(data))

    samp_rate = 1.0 / delta
    T = freq * 1.0
    D = damp
    omega = (2 * 3.14159 * T) ** 2

    paz_sa = cornFreq2Paz(T, damp=D)
    paz_sa['sensitivity'] = omega
    paz_sa['zeros'] = []
    data = seisSim(data, samp_rate, paz_remove=None, paz_simulate=paz_sa,
                   taper=True, simulate_sensitivity=True, taper_fraction=0.05)

    if abs(max(data)) >= abs(min(data)):
        pga = abs(max(data))
    else:
        pga = abs(min(data))

    return (pga, m_dis, m_vel, m_acc)
Ejemplo n.º 5
0
def find_pole_zero(freq_msu,amp_msu,seismometer,msu_freep,msu_damp,nsearch,coarse_search,fine_search,nloops,ngrids,lmult,hmult):

# ngrids is the number of grid points to be searched for each parameter, per each "percentage" loop
                        # At nsearch = 3, grid search on amplitude, damping ratio, and free period.
    nfreqs = ngrids
    if ( nsearch < 3 ): # Set flags. Free period is constrained at nsearch = 2
        nfreqs = 1
    ndamps = ngrids
    if ( nsearch < 2 ): # Set flags. Free period & Damping ratio is constrained at nsearch = 1
        ndamps = 1
    nscales = ngrids
    if ( nsearch < 1 ): # set flags. Free period, Damping Ratio, and amplitude are all constrained.
        nscales = 1

# search the grids nloops times, first between coarse_search percent smaller than starting params 
#   and coarse_search percent larger than starting params, then, eventually search only within fine_search percent
#   of the best params found on each previous loop search

    search_range = np.linspace(coarse_search, fine_search, nloops)

# freq_msu is array holding frequency values where MSU made amp measurements
# amp_msu is array holding amplitude values measured by MSU
# note that msu_freep is the MSU estimated freee period in seconds

# now find average amplitude from MSU measurements where freq > 1 Hz
#
# edit: Change the average amplitude calculation to use frequencies greater than 
# 3x the free period frequency but less than 8x the free-period frequency.
# This program calculates only two poles & zeros: Response should be flat within
# this passband and avoids any weird issues at higher frequencies and keeps 
# calculation off the knee of the response curve in cases of high or low damping
# ratios
# - drb 08/28/2014

    count = 0
    amp_average = 0.
#    lmult = lmult/msu_freep # make it a multiplier of freeperiod in Hz (lowest)
#    hmult = hmult/msu_freep                                         # (Highest)
    
    for i, freq in enumerate(freq_msu):
        if ( freq > lmult) and (freq < hmult):  
                                      # Set frequency discriminator to msu_freep * 2 to get off the 
                                      # knee of the curve for SP instruments - drb
           amp_average = amp_average + amp_msu[i] 
           count += 1
    amp_average = amp_average / float(count)

                                      # set preliminary "best parameters"

    best_freep = msu_freep 
    best_damp = msu_damp
    best_scale = amp_average

                                      # best_fit is the RMS misfit between MSU observed 
                                      # and model amplitudes in log10 space,
                                      #    an outrageously large value, initially

    best_fit = 1000000.

                                      # an index counter to keep track of the total number of searches/misfits
    j = 0
                                      # for use with later plotting
                                      # prepare array to hold total number of interation results

    misfits = np.zeros(nscales * ndamps * nfreqs * nloops)

                                      # prepare array to store each iteration number

    misfit_count = np.zeros(nscales * ndamps * nfreqs * nloops)

                                      # start of loops 

    for range in search_range:

                                      # build a list of "corner frequencies" to test
                                      # I think these "corner frequencies" - in ObsPy Speak means 1/free period of seismometer

        freep_adjust = best_freep * range
        fp_long = best_freep + freep_adjust
        fp_short = best_freep - freep_adjust

                                      # the case where free period is held constant at the MSU-supplied value

        if ( nsearch < 3 ):
            fp_long = best_freep
            fp_short = best_freep

                                      # np.linspace will create an array from low_freq to high_freq over nfreqs of even spacing

        corners = np.linspace(1./fp_long, 1./fp_short, nfreqs)
        print ( "\nsearching free periods between %f and %f seconds" % ( fp_long, fp_short ) )

                                      # build a list of damping factors to test

        damp_adjust = best_damp * range
        low_damp = best_damp - damp_adjust 
        high_damp = best_damp + damp_adjust
        if ( high_damp >= 1.0 ):
            print ( "\nwarning - damping factor is %f - must be below 1.0 - setting damping to 0.9999" % ( high_damp ) )
            high_damp = 0.9999
        if ( nsearch < 2 ):
            low_damp = best_damp
            high_damp = best_damp

        damps = np.linspace(low_damp, high_damp, ndamps)
        print ( "searching damping factors between %f and %f" % ( low_damp, high_damp ) )

                                      # build a list of scale factors to test

        scale_adjust = best_scale * range
        low_scale = best_scale - scale_adjust 
        high_scale = best_scale + scale_adjust
        if ( nsearch < 1 ):
            low_scale = best_scale
            high_scale = best_scale
            nscales = 1
        scales = np.linspace(low_scale, high_scale, nscales)
        print ( "searching scale factors between %f and %f" % ( low_scale, high_scale ) )

                                      # here are the grid search loops, over corners, dampings, and scales

        for corner in corners:   
            for damp in damps:
                for scale in scales:

                                      # the key obspy function to find inst resp based on "corner frequency" and damping constant 
                                      # cornFreq2Paz takes an instrument's corner freq and damping factor to produce
                                      #   an Obspy-style paz file

                    resp = sim.cornFreq2Paz(corner, damp) 
                    resp['gain'] = scale
                    amp_predicted = np.zeros_like(freq_msu)
                    for i, freq in enumerate(freq_msu):
                        amp_predicted[i] = sim.paz2AmpValueOfFreqResp(resp, freq) 
    
                    misfit = np.linalg.norm(np.log10(amp_msu) - np.log10(amp_predicted))
                    misfits[j] = misfit
                    misfit_count[j] = j + 1
       
                    if ( misfit < best_fit ):
                       best_fit = misfit
                       best_corner = corner
                       best_damp = damp
                       best_scale = scale
                       best_index = j

                    j = j + 1

                                      # find the best free period, which is 1/best_corner
                                      # this happens at the end of a particlar grid search loop

        best_freep  = 1./best_corner 

                                      # end of all loops

                                      # find poles and zeros using best corner freq, damp and scale 

    resp = sim.cornFreq2Paz(best_corner, best_damp) 
    resp['gain'] = best_scale
    return(resp, best_freep, best_damp, best_scale, amp_average, misfits, misfit_count, best_index) 
Ejemplo n.º 6
0
def correct_response(st, removeResp=False, removePAZ=False, simPAZ=False, pre_filt=None, cornFreq=0.0083):
    """
    Correct the seismometer response.
    
    Seismometer response is given in either a dictionary ``removeResp''
    or a dictionary ``removePAZ''. ``removeResp has precedence. The
    dictionaries have the following structure

    removeResp: dictionary with Response information to be removed
        has the following keys:
        respfile: (str) filename of evalresp response file.
        units: (str) Units to return response in. Can be either DIS, VEL or ACC
        start_stage: (int) integer stage numbers of start stage (<0 causes
            default evalresp bahaviour).
        stop_stage: (int) integer stage numbers of stop stage
    removePAZ: dictionary with poles and zeros to be removed has the following
        keys:
            poles: (list of complex numbers) location of poles
            zeros: (list of complex numbers) location of zeros
            gain: (float) gain
            sensitivity: (float) sensitivity
        It can easily be retrieved with obspy.arclink.client.Client.getPAZ

    if ``removeResp'' is given the response of each trace must be present in
    the respfile. If ``removePAZ'' is used the response is assumed to be the
    same for all traces in the stream.
    A filter specified in pre_filt can be applied in to avoid amplification of
    noise.
    The instrument to be simulated is either described in the dictionary simPAZ
    or if simPAZ is False by the corner frequency ``cornFreq''. Response
    correction is done in place and original data is overwritten.
    
    The input stream ``st'' should be demeaned and tapered.
    
    :type st: obspy.core.stream.Stream
    :param st: data stream to be corrected
    :type removeResp: dict
    :param removeResp: Response information to be removed
    :type removePAZ: dict
    :param removePAZ: Response information to be removed
    :type simPAZ: dict
    :param simPAZ: Response information to be simulated
    :type cornFreq: float
    :param cornFreq: corner frequency of instrument to be simulated
    :type pre_filt: list
    :param pre_filt: 4 corners of the filter
    """
    
    for tr in st:
        starttime = tr.stats['starttime']
        endtime = tr.stats['endtime']
        network = tr.stats['network']
        station = tr.stats['station']
        channel = tr.stats['channel']
        location = tr.stats['location']
        length = tr.stats['npts']
        sampling_rate = tr.stats['sampling_rate']
        np2l = nextpow2(2.*length)
        
        if not simPAZ:
            simPAZ = cornFreq2Paz(cornFreq, damp=0.70716)
        simresp, freqs = np.conj(pazToFreqResp(simPAZ['poles'], simPAZ['zeros'],
                          scale_fac=simPAZ['gain']*simPAZ['sensitivity'],
                          t_samp=1./sampling_rate,
                          nfft=np2l, freq=True)) #see Doc of pazToFreqResp for reason of conj()
        
        if removeResp:
            freqresp, freqs = evalresp(1./sampling_rate,np2l,removeResp['respfile'],
                                starttime, network=network, station=station,
                                channel=channel, locid=location,
                                start_stage=removeResp['start_stage'],
                                stop_stage=removeResp['stop_stage'],
                                units=removeResp['units'], freq=True)
        else:
            freqresp, freqs = np.conj(pazToFreqResp(removePAZ['poles'], removePAZ['zeros'],
                          scale_fac=removePAZ['gain']*removePAZ['sensitivity'],
                          t_samp=1./sampling_rate,
                          nfft=np2l, freq=True)) #see Doc of pazToFreqResp for reason of conj()
        
        ftr = np.fft.rfft(tr.data,n=np2l)
        ftr /= freqresp
        ftr[0] = 0.j  # correct the NaN in the DC component
        ftr *= simresp
        
        if pre_filt:
            ftr *= c_sac_taper(freqs, flimit=pre_filt)

        tr.data = np.fft.irfft(ftr)
        tr.trim(starttime,endtime)
    
    return   
Ejemplo n.º 7
0
def correct_response(st,
                     removeResp=False,
                     removePAZ=False,
                     simPAZ=False,
                     pre_filt=None,
                     cornFreq=0.0083):
    """
    Correct the seismometer response.
    
    Seismometer response is given in either a dictionary ``removeResp''
    or a dictionary ``removePAZ''. ``removeResp has precedence. The
    dictionaries have the following structure

    removeResp: dictionary with Response information to be removed
        has the following keys:
        respfile: (str) filename of evalresp response file.
        units: (str) Units to return response in. Can be either DIS, VEL or ACC
        start_stage: (int) integer stage numbers of start stage (<0 causes
            default evalresp bahaviour).
        stop_stage: (int) integer stage numbers of stop stage
    removePAZ: dictionary with poles and zeros to be removed has the following
        keys:
            poles: (list of complex numbers) location of poles
            zeros: (list of complex numbers) location of zeros
            gain: (float) gain
            sensitivity: (float) sensitivity
        It can easily be retrieved with obspy.arclink.client.Client.getPAZ

    if ``removeResp'' is given the response of each trace must be present in
    the respfile. If ``removePAZ'' is used the response is assumed to be the
    same for all traces in the stream.
    A filter specified in pre_filt can be applied in to avoid amplification of
    noise.
    The instrument to be simulated is either described in the dictionary simPAZ
    or if simPAZ is False by the corner frequency ``cornFreq''. Response
    correction is done in place and original data is overwritten.
    
    The input stream ``st'' should be demeaned and tapered.
    
    :type st: obspy.core.stream.Stream
    :param st: data stream to be corrected
    :type removeResp: dict
    :param removeResp: Response information to be removed
    :type removePAZ: dict
    :param removePAZ: Response information to be removed
    :type simPAZ: dict
    :param simPAZ: Response information to be simulated
    :type cornFreq: float
    :param cornFreq: corner frequency of instrument to be simulated
    :type pre_filt: list
    :param pre_filt: 4 corners of the filter
    """

    for tr in st:
        starttime = tr.stats['starttime']
        endtime = tr.stats['endtime']
        network = tr.stats['network']
        station = tr.stats['station']
        channel = tr.stats['channel']
        location = tr.stats['location']
        length = tr.stats['npts']
        sampling_rate = tr.stats['sampling_rate']
        np2l = nextpow2(2. * length)

        if not simPAZ:
            simPAZ = cornFreq2Paz(cornFreq, damp=0.70716)
        simresp, freqs = np.conj(
            pazToFreqResp(
                simPAZ['poles'],
                simPAZ['zeros'],
                scale_fac=simPAZ['gain'] * simPAZ['sensitivity'],
                t_samp=1. / sampling_rate,
                nfft=np2l,
                freq=True))  #see Doc of pazToFreqResp for reason of conj()

        if removeResp:
            freqresp, freqs = evalresp(1. / sampling_rate,
                                       np2l,
                                       removeResp['respfile'],
                                       starttime,
                                       network=network,
                                       station=station,
                                       channel=channel,
                                       locid=location,
                                       start_stage=removeResp['start_stage'],
                                       stop_stage=removeResp['stop_stage'],
                                       units=removeResp['units'],
                                       freq=True)
        else:
            freqresp, freqs = np.conj(
                pazToFreqResp(
                    removePAZ['poles'],
                    removePAZ['zeros'],
                    scale_fac=removePAZ['gain'] * removePAZ['sensitivity'],
                    t_samp=1. / sampling_rate,
                    nfft=np2l,
                    freq=True))  #see Doc of pazToFreqResp for reason of conj()

        ftr = np.fft.rfft(tr.data, n=np2l)
        ftr /= freqresp
        ftr[0] = 0.j  # correct the NaN in the DC component
        ftr *= simresp

        if pre_filt:
            ftr *= c_sac_taper(freqs, flimit=pre_filt)

        tr.data = np.fft.irfft(ftr)
        tr.trim(starttime, endtime)

    return