예제 #1
0
파일: remove_ir.py 프로젝트: woxin5295/SAES
def remove_ir(self, st, baz, evid, output):
    '''
    Handles instrument response and free-surface effects corrections

    Inputs:
    --------
    st: input waveform
    baz: event-station back_azimuth
    evid: event ID
    output: output waveform type (Disp, Vel or Accl)

    Returns:
    ----------
    st: instrument response corrected waveform

    '''

    trn2 = Stream()
    respf = self.stationxml
    try:
        trn2 = st.select(component='N')
    except:
        pass
    try:
        trn2 += st.select(component='E')
    except:
        pass
    pre_filt = self.stationlist[st[0].stats.station.strip()]['pre_filt']
    if pre_filt:
        for tr in st:
            tr.remove_response(respf, pre_filt=pre_filt, output=output)
    else:
        for tr in st:
            tr.remove_response(respf, output=output)
    if len(trn2) == 2:
        trn2.rotate('NE->RT', back_azimuth=baz)
        for tr in st:
            if tr.stats.channel.strip()[-1] == 'N':
                tr = trn2[0]
            elif tr.stats.channel.strip()[-1] == 'E':
                tr = trn2[1]
        if self.freesurface_cor is True:
            st = free_surface_correction(self, st, evid)
        trn2.rotate('RT->NE', back_azimuth=baz)
    return st
예제 #2
0
class RFData(object):
    """
    A RFData object contains Class attributes that associate
    station information with a single event (i.e., earthquake) 
    metadata, corresponding raw and rotated seismograms and 
    receiver functions.

    Note
    ----
    The object is initialized with the ``sta`` field only, and
    other attributes are added to the object as the analysis proceeds.

    Parameters
    ----------
    sta : object
        Object containing station information - from :mod:`~stdb` database.
    meta : :class:`~rfpy.rfdata.Meta`
        Object of metadata information for single event (initially set to None)
    data : :class:`~obspy.core.Stream`
        Stream object containing the three-component seismograms (either
        un-rotated or rotated by the method :func:`~rfpy.rfdata.rotate`)

    """
    def __init__(self, sta):

        # Load example data if initializing empty object
        if sta == 'demo' or sta == 'Demo':
            print("Uploading demo data - station NY.MMPY")
            import os
            import pickle
            sta = pickle.load(
                open(
                    os.path.join(os.path.dirname(__file__), "examples/data",
                                 "MMPY.pkl"), 'rb'))['NY.MMPY']

        # Attributes from parameters
        self.sta = sta

        # Initialize meta and data objects as None
        self.meta = None
        self.data = None

    def add_event(self, event, gacmin=30., gacmax=90., returned=False):
        """
        Adds event metadata to RFData object, including travel time info 
        of P wave. 

        Parameters
        ----------
        event : :class:`~obspy.core.event`
            Event XML object
        returned : bool
            Whether or not to return the ``accept`` attribute

        Returns
        -------
        accept : bool
            Whether or not the object is accepted for further analysis

        """
        from obspy.geodetics.base import gps2dist_azimuth as epi
        from obspy.geodetics import kilometer2degrees as k2d
        from obspy.taup import TauPyModel
        from obspy.core.event.event import Event

        if event == 'demo' or event == 'Demo':
            from obspy.clients.fdsn import Client
            from obspy.core import UTCDateTime
            client = Client()
            # Get catalogue using deployment start and end
            event = client.get_events(
                starttime=UTCDateTime('2015-07-03T06:00:00'),
                endtime=UTCDateTime('2015-07-03T07:00:00'),
                minmagnitude=6.0,
                maxmagnitude=6.5)[0]
            print(event.short_str())

        if not isinstance(event, Event):
            raise (Exception("Event has incorrect type"))

        # Store as object attributes
        self.meta = Meta(sta=self.sta,
                         event=event,
                         gacmin=gacmin,
                         gacmax=gacmax)

        if returned:
            return self.meta.accept

    def add_data(self, stream, returned=False):
        """
        Adds stream as object attribute

        Parameters
        ----------
        stream : :class:`~obspy.core.Stream`
            Stream container for NEZ seismograms
        returned : bool
            Whether or not to return the ``accept`` attribute

        Attributes
        ----------
        zne_data : :class:`~obspy.core.Stream`
            Stream container for NEZ seismograms

        Returns
        -------
        accept : bool
            Whether or not the object is accepted for further analysis

        """

        if not self.meta:
            raise (Exception("No meta data available - aborting"))

        if not self.meta.accept:
            return

        # Load demo data
        if stream == 'demo' or stream == 'Demo':
            import os
            import pickle
            file = open(
                os.path.join(os.path.dirname(__file__), "examples/data",
                             "ZNE_Data.pkl"), "rb")
            stream = pickle.load(file)
            print(stream)

        if not isinstance(stream, Stream):
            raise (Exception("Event has incorrect type"))

        try:
            trE = stream.select(component='E')[0]
            trN = stream.select(component='N')[0]
            trZ = stream.select(component='Z')[0]
            self.data = stream
        except:
            print("Error: Not all channels are available")
            self.meta.accept = False

        if returned:
            return self.meta.accept

    def download_data(self,
                      client,
                      ndval=np.nan,
                      new_sr=5.,
                      dts=120.,
                      returned=False):
        """
        Downloads seismograms based on event origin time and
        P phase arrival.

        Parameters
        ----------
        client : :class:`~obspy.client.fdsn.Client`
            Client object
        ndval : float
            Fill in value for missing data
        new_sr : float
            New sampling rate (Hz)
        dts : float
            Time duration (sec)
        returned : bool
            Whether or not to return the ``accept`` attribute

        Returns
        -------
        accept : bool
            Whether or not the object is accepted for further analysis

        Attributes
        ----------
        data : :class:`~obspy.core.Stream`
            Stream containing :class:`~obspy.core.Trace` objects

        """

        if self.meta is None:
            raise (Exception("Requires event data as attribute - aborting"))

        if not self.meta.accept:
            return

        # Define start and end times for requests
        tstart = self.meta.time + self.meta.ttime - dts
        tend = self.meta.time + self.meta.ttime + dts

        # Get waveforms
        print("* Requesting Waveforms: ")
        print("*    Startime: " + tstart.strftime("%Y-%m-%d %H:%M:%S"))
        print("*    Endtime:  " + tend.strftime("%Y-%m-%d %H:%M:%S"))

        err, stream = options.download_data(client=client,
                                            sta=self.sta,
                                            start=tstart,
                                            end=tend,
                                            stdata=self.sta.station,
                                            ndval=ndval,
                                            new_sr=new_sr)

        # Store as attributes with traces in dictionay
        try:
            trE = stream.select(component='E')[0]
            trN = stream.select(component='N')[0]
            trZ = stream.select(component='Z')[0]
            self.data = Stream(traces=[trZ, trN, trE])
        except:
            self.meta.accept = False

        if returned:
            return self.meta.accept

    def rotate(self, vp=None, vs=None, align=None):
        """
        Rotates 3-component seismograms from vertical (Z),
        east (E) and north (N) to longitudinal (L), 
        radial (Q) and tangential (T) components of motion.
        Note that the method 'rotate' from ``obspy.core.stream.Stream``
        is used for the rotation ``'ZNE->ZRT'`` and ``'ZNE->LQT'``.
        Rotation ``'ZNE->PVH'`` is implemented separately here 
        due to different conventions.

        Parameters
        ----------
        vp : float
            P-wave velocity at surface (km/s)
        vs : float
            S-wave velocity at surface (km/s)
        align : str
            Alignment of coordinate system for rotation
            ('ZRT', 'LQT', or 'PVH')

        Returns
        -------
        rotated : bool
            Whether or not the object has been rotated

        """

        if not self.meta.accept:
            return

        if self.meta.rotated:
            print("Data have been rotated already - continuing")
            return

        # Use default values from meta data if arguments are not specified
        if not align:
            align = self.meta.align

        if align == 'ZRT':
            self.data.rotate('NE->RT', back_azimuth=self.meta.baz)
            self.meta.align = align
            self.meta.rotated = True

        elif align == 'LQT':
            self.data.rotate('ZNE->LQT',
                             back_azimuth=self.meta.baz,
                             inclination=self.meta.inc)
            for tr in self.data:
                if tr.stats.channel.endswith('Q'):
                    tr.data = -tr.data
            self.meta.align = align
            self.meta.rotated = True

        elif align == 'PVH':

            # First rotate to ZRT
            self.data.rotate('NE->RT', back_azimuth=self.meta.baz)

            # Copy traces
            trP = self.data.select(component='Z')[0].copy()
            trV = self.data.select(component='R')[0].copy()
            trH = self.data.select(component='T')[0].copy()

            slow = self.meta.slow
            if not vp:
                vp = self.meta.vp
            if not vs:
                vs = self.meta.vs

            # Vertical slownesses
            # P vertical slowness
            qp = np.sqrt(1. / vp / vp - slow * slow)
            # S vertical slowness
            qs = np.sqrt(1. / vs / vs - slow * slow)

            # Elements of rotation matrix
            m11 = slow * vs * vs / vp
            m12 = -(1. - 2. * vs * vs * slow * slow) / (2. * vp * qp)
            m21 = (1. - 2. * vs * vs * slow * slow) / (2. * vs * qs)
            m22 = slow * vs

            # Rotation matrix
            rot = np.array([[-m11, m12], [-m21, m22]])

            # Vector of Radial and Vertical
            r_z = np.array([trV.data, trP.data])

            # Rotation
            vec = np.dot(rot, r_z)

            # Extract P and SV, SH components
            trP.data = vec[0, :]
            trV.data = vec[1, :]
            trH.data = -trH.data / 2.

            # Update stats of streams
            trP.stats.channel = trP.stats.channel[:-1] + 'P'
            trV.stats.channel = trV.stats.channel[:-1] + 'V'
            trH.stats.channel = trH.stats.channel[:-1] + 'H'

            # Over-write data attribute
            self.data = Stream(traces=[trP, trV, trH])
            self.meta.align = align
            self.meta.rotated = True

        else:
            raise (Exception("incorrect 'align' argument"))

    def calc_snr(self, dt=30., fmin=0.1, fmax=1.):
        """
        Calculates signal-to-noise ratio on either Z, L or P component

        Parameters
        ----------
        dt : float
            Duration (sec)
        fmin : float
            Minimum frequency corner for SNR filter (Hz)
        fmax : float
            Maximum frequency corner for SNR filter (Hz)

        Attributes
        ----------
        snr : float
            Signal-to-noise ratio  (dB)

        """

        if not self.meta.accept:
            return

        if self.meta.snr:
            print("SNR already calculated - continuing")
            return

        t1 = self.meta.time + self.meta.ttime - 5.

        comp = self.meta.align[0]

        # Copy trace to signal and noise traces
        trSig = self.data.select(component=comp)[0].copy()
        trNze = self.data.select(component=comp)[0].copy()

        # Filter between 0.1 and 1.0 (dominant P wave frequencies)
        trSig.filter('bandpass',
                     freqmin=fmin,
                     freqmax=fmax,
                     corners=2,
                     zerophase=True)
        trNze.filter('bandpass',
                     freqmin=fmin,
                     freqmax=fmax,
                     corners=2,
                     zerophase=True)

        # Trim 'twin' seconds around P-wave arrival
        trSig.trim(t1, t1 + dt)
        trNze.trim(t1 - dt, t1)

        # Calculate root mean square (RMS)
        srms = np.sqrt(np.mean(np.square(trSig.data)))
        nrms = np.sqrt(np.mean(np.square(trNze.data)))

        # Calculate signal/noise ratio in dB
        self.meta.snr = 10 * np.log10(srms * srms / nrms / nrms)

    def deconvolve(self, twin=30., vp=None, vs=None, align=None):
        """
        Deconvolves three-compoent data using one component as the source wavelet.
        The source component is always taken as the dominant compressional 
        component, which can be either 'Z', 'L', or 'P'. 

        Parameters
        ----------
        twin : float
            Estimated duration of source wavelet (sec).
        vp : float
            P-wave velocity at surface (km/s)
        vs : float
            S-wave velocity at surface (km/s)
        align : str
            Alignment of coordinate system for rotation
            ('ZRT', 'LQT', or 'PVH')

        Attributes
        ----------
        rf : :class:`~obspy.core.Stream`
            Stream containing the receiver function traces

        """

        if not self.meta.accept:
            return

        def _taper(nt, ns):
            tap = np.ones(nt)
            win = np.hanning(2 * ns)
            tap[0:ns] = win[0:ns]
            tap[nt - ns:nt] = win[ns:2 * ns]
            return tap

        if not self.meta.rotated:
            print("Warning: Data have not been rotated yet - rotating now")
            self.rotate(vp=vp, vs=vs, align=align)

        if not self.meta.snr:
            print("Warning: SNR has not been calculated - " +
                  "calculating now using default")
            self.calc_snr()

        if hasattr(self, 'rf'):
            print("Warning: Data have been deconvolved already - passing")
            return

        cL = self.meta.align[0]
        cQ = self.meta.align[1]
        cT = self.meta.align[2]

        # Define source and noise
        trL = self.data.select(component=cL)[0].copy()
        trQ = self.data.select(component=cQ)[0].copy()
        trT = self.data.select(component=cT)[0].copy()
        trS = self.data.select(component=cL)[0].copy()  # Source
        trNl = self.data.select(component=cL)[0].copy()  # Noise on L
        trNq = self.data.select(component=cQ)[0].copy()  # Noise on Q

        # trim traces 115 sec in each direction
        trL.trim(self.meta.time + self.meta.ttime - 5.,
                 self.meta.time + self.meta.ttime + 110.)
        trQ.trim(self.meta.time + self.meta.ttime - 5.,
                 self.meta.time + self.meta.ttime + 110.)
        trT.trim(self.meta.time + self.meta.ttime - 5.,
                 self.meta.time + self.meta.ttime + 110.)
        trS.trim(self.meta.time + self.meta.ttime - 5.,
                 self.meta.time + self.meta.ttime + 110.)
        trNl.trim(self.meta.time + self.meta.ttime - 120.,
                  self.meta.time + self.meta.ttime - 5.)
        trNq.trim(self.meta.time + self.meta.ttime - 120.,
                  self.meta.time + self.meta.ttime - 5.)

        # Taper trS
        window = np.zeros(len(trS.data))
        tap = _taper(int(twin / trS.stats.delta), int(2. / trS.stats.delta))
        window[0:int(twin / trS.stats.delta)] = tap
        trS.data *= window

        # Taper other traces
        window = np.zeros(len(trL.data))
        tap = _taper(len(trL.data), int(2. / trL.stats.delta))
        window[0:len(trL.data)] = tap

        # Some checks
        lwin = len(window)
        if not (lwin == len(trL.data) and lwin == len(trQ.data)
                and lwin == len(trT.data) and lwin == len(trNl.data)
                and lwin == len(trNq.data)):
            print('problem with lwin')
            self.rf = Stream(traces=[Trace(), Trace(), Trace()])

        # Apply taper
        trL.data *= window
        trQ.data *= window
        trT.data *= window
        trNl.data *= window
        trNq.data *= window

        # Fourier transform
        Fl = np.fft.fft(trL.data)
        Fq = np.fft.fft(trQ.data)
        Ft = np.fft.fft(trT.data)
        Fs = np.fft.fft(trS.data)
        Fnl = np.fft.fft(trNl.data)
        Fnq = np.fft.fft(trNq.data)

        # Auto and cross spectra
        Sl = Fl * np.conjugate(Fs)
        Sq = Fq * np.conjugate(Fs)
        St = Ft * np.conjugate(Fs)
        Ss = Fs * np.conjugate(Fs)
        Snl = Fnl * np.conjugate(Fnl)
        Snq = Fnq * np.conjugate(Fnq)
        Snlq = Fnq * np.conjugate(Fnl)

        # Denominator
        Sdenom = 0.25 * (Snl + Snq) + 0.5 * Snlq

        # Copy traces
        rfL = trL.copy()
        rfQ = trQ.copy()
        rfT = trT.copy()

        # Spectral division and inverse transform
        rfL.data = np.real(np.fft.ifft(Sl / (Ss + Sdenom)))
        rfQ.data = np.real(np.fft.ifft(Sq / (Ss + Sdenom)) / np.amax(rfL.data))
        rfT.data = np.real(np.fft.ifft(St / (Ss + Sdenom)) / np.amax(rfL.data))

        # Update stats of streams
        rfL.stats.channel = 'RF' + self.meta.align[0]
        rfQ.stats.channel = 'RF' + self.meta.align[1]
        rfT.stats.channel = 'RF' + self.meta.align[2]

        self.rf = Stream(traces=[rfL, rfQ, rfT])

    def to_stream(self):
        """
        Method to switch from RFData object to Stream object.
        This allows easier manipulation of the receiver functions
        for post-processing.

        """

        if not self.meta.accept:
            return

        def _add_rfstats(trace):
            trace.stats.snr = self.meta.snr
            trace.stats.slow = self.meta.slow
            trace.stats.baz = self.meta.baz
            trace.stats.stlo = self.sta.longitude
            trace.stats.stla = self.sta.latitude
            trace.stats.vp = self.meta.vp
            trace.stats.vs = self.meta.vs
            trace.stats.is_rf = True
            return trace

        if not hasattr(self, 'rf'):
            raise (Exception("Warning: Receiver functions are not available"))

        stream = self.rf
        for tr in stream:
            tr = _add_rfstats(tr)

        return stream

    def save(self, file):
        """
        Saves RFData object to file

        Parameters
        ----------
        file : str
            File name for RFData object

        """

        import pickle
        output = open(file, 'wb')
        pickle.dump(self, output)
        output.close()
예제 #3
0
    if pcorr:
        st += read(string + '30_LDO*.seed')

st.trim(starttime=stime2, endtime=stime2 + hours * 60. * 60.)
st.merge()
st.detrend('linear')
st.detrend('constant')

print(st)

st.merge()
print(st)

#st.decimate(10)
#st.decimate(10)
st.rotate(method="->ZNE", inventory=inventory)
for tr in st:
    if tr.stats.channel == 'LHN':
        tr.stats.channel = "LH1"
    if tr.stats.channel == "LHE":
        tr.stats.channel = "LH2"

# decimate to 1 sample per 20 s
st.decimate(5)
st.decimate(2)
st.decimate(2)

for tr in st:
    win = signal.get_window(('kaiser', 2. * np.pi), tr.stats.npts)
    tr.data *= win
예제 #4
0
    if tr.stats.channel == 'LH1':
        tr.stats.channel = 'LHN'
    if tr.stats.channel == 'LH2':
        tr.stats.channel = 'LHE'
st2.detrend()
st2.merge(fill_value=0.)
st2.sort(reverse=True)

# Data is in ZNE format
if debug:
    print(st)
    print(st2)

# Wrap-around issues near 0 degree. Hack and rotate test station 180

st2 = st2.rotate('NE->RT', 180.)
st2 = st2.rotate('RT->NE', 0.)

# Setup the variable arrays
angF = []
coh = []
Pres = []

syear = []
smonth = []
sday = []
shour = []
sminute = []
ssecond = []

fs = st2[0].stats.sampling_rate