예제 #1
0
    def calculate_compliance(self):
        """
        Method to calculate compliance and coherence functions from the
        averaged (daily or station-averaged) noise spectra.

        Attributes
        ----------
        complyfunc : Dict
            Container Dictionary for all possible compliance and
            coherence functions

        Examples
        --------

        Calculate compliance and coherence functions for a DayNoise object

        >>> from obstools.atacr import DayNoise
        >>> from obstools.comply import Comply
        >>> daynoise = DayNoise('demo')
        Uploading demo data - March 04, 2012, station 7D.M08A
        >>> daynoise.QC_daily_spectra()
        >>> daynoise.average_daily_spectra()
        >>> daycomply = Comply(objnoise=daynoise, sta=sta)
        >>> daycomply.calculate_compliance()
        >>> tfnoise.complyfunc.keys()
        dict_keys(['ZP', 'ZP-21', 'ZP-H'])

        Calculate compliance and coherence functions for a StaNoise object

        >>> from obstools.atacr import StaNoise
        >>> from obstools.comply import Comply
        >>> stanoise = StaNoise('demo')
        Uploading demo data - March 01 to 04, 2012, station 7D.M08A
        >>> stanoise.QC_sta_spectra()
        >>> stanoise.average_sta_spectra()
        >>> stacomply = Comply(objnoise=stanoise, sta=sta)
        >>> stacomply.calculate_compliance()
        >>> stacomply.complyfunc.keys()
        dict_keys(['ZP', 'ZP-21'])

        """

        def wavenumber(omega, H):
            """
            Function to approximate wavenumber from dispersion relation

            H is depth below the seafloor, in meters
            omega is a vector of positive angular frequencies

            Stephen G. Mosher, 2020

            """

            import numpy.polynomial as poly

            g = 9.79329
            N = len(omega)

            # Approximations for k when k*H is very large (deep case) or
            # very small (shallow case)
            k_deep = omega**2 / g
            k_shal = omega / np.sqrt(g * H)

            """
            Alternatively, we can use a rational approximation to
            tanh(x) to solve k for any omega. This approximation gives
            a quartic equation, we take the positive real roots as the
            value of k we're interested in. The rational approximation
            being used is always better than the shallow approximation.
            However, it's only better than the deep approximation if
            k*H < 2.96. Therefore, we keep the solutions to k we find,
            using the rational approximation for k*H < 2.96 and use the
            deep water approximation to solve for k otherwise. The
            average error is just under 1% and the maximum error is
            2.5%.
            """

            k = np.zeros(len(omega))

            for i, om in enumerate(omega):

                if i == 0:
                    k[i] = 0.
                else:

                    a0 = -27 * om**2 / g                   # constant terms
                    a1 = 0.                 # no linear terms
                    a2 = 27 * H - (9 * om**2 * H**2)/g     # quadratic terms
                    a3 = 0.                 # no cubic terms
                    a4 = H**3           # quartic terms

                    p = poly.Polynomial([a0, a1, a2, a3, a4])
                    solu = poly.Polynomial.roots(p)
                    positive_roots = solu[solu > 0]
                    real_positive_root = \
                        positive_roots[positive_roots.imag == 0].real[0]
                    k[i] = real_positive_root

            # For k*H >= 2.96, prefer the deep approximation above
            for i, wavenumber in enumerate(k_deep):
                if wavenumber * H > 2.96:
                    k[i] = k_deep[i]

            return k

        # Calculate wavenumber - careful here, elevation is negative
        k = wavenumber(2.*np.pi*self.f, -1.*self.sta.elevation*1.e3)

        # Initialize empty dictionary
        complyfunc = self.ComplyDict()

        # Cycle through all available transfer functions in the objnoise
        # object
        for key, value in self.tf_list.items():

            if key == 'ZP':
                if value:
                    admit_ZP = utils.admittance(self.cZP, self.cPP)
                    compl_ZP = k*admit_ZP
                    coh_ZP = utils.coherence(self.cZP, self.cPP, self.cZZ)
                    complyfunc.add('ZP', [compl_ZP, coh_ZP])

            elif key == 'ZP-21':
                if value:
                    lc1cZ = np.conj(self.c1Z)/self.c11
                    lc1c2 = np.conj(self.c12)/self.c11
                    lc1cP = np.conj(self.c1P)/self.c11

                    coh_12 = utils.coherence(self.c12, self.c11, self.c22)
                    coh_1P = utils.coherence(self.c1P, self.c11, self.cPP)
                    coh_1Z = utils.coherence(self.c1Z, self.c11, self.cZZ)

                    gc2c2_c1 = self.c22*(1. - coh_12)
                    gcPcP_c1 = self.cPP*(1. - coh_1P)
                    gcZcZ_c1 = self.cZZ*(1. - coh_1Z)

                    gc2cZ_c1 = np.conj(self.c2Z) - np.conj(lc1c2*self.c1Z)
                    gcPcZ_c1 = self.cZP - np.conj(lc1cP*self.c1Z)

                    gc2cP_c1 = np.conj(self.c2P) - np.conj(lc1c2*self.c1P)

                    lc2cP_c1 = gc2cP_c1/gc2c2_c1
                    lc2cZ_c1 = gc2cZ_c1/gc2c2_c1

                    coh_c2cP_c1 = utils.coherence(gc2cP_c1, gc2c2_c1,
                                                  gcPcP_c1)
                    coh_c2cZ_c1 = utils.coherence(gc2cZ_c1, gc2c2_c1,
                                                  gcZcZ_c1)

                    gcPcP_c1c2 = gcPcP_c1*(1. - coh_c2cP_c1)
                    gcPcZ_c1c2 = gcPcZ_c1 - np.conj(lc2cP_c1)*gc2cZ_c1
                    gcZcZ_c1c2 = gcZcZ_c1*(1. - coh_c2cZ_c1)

                    admit_ZP_21 = utils.admittance(
                        gcPcZ_c1c2, gcPcP_c1c2)
                    compl_ZP_21 = k*admit_ZP_21
                    coh_ZP_21 = utils.coherence(
                        gcPcZ_c1c2, gcPcP_c1c2, gcZcZ_c1c2)

                    complyfunc.add('ZP-21', [compl_ZP_21, coh_ZP_21])

            elif key == 'ZP-H':
                if value:
                    lcHcP = np.conj(self.cHP)/self.cHH

                    coh_HP = utils.coherence(self.cHP, self.cHH, self.cPP)
                    coh_HZ = utils.coherence(self.cHZ, self.cHH, self.cZZ)

                    gcPcP_cH = self.cPP*(1. - coh_HP)
                    gcZcZ_cH = self.cZZ*(1. - coh_HZ)
                    gcPcZ_cH = self.cZP - np.conj(lcHcP*self.cHZ)

                    admit_ZP_H = utils.admittance(gcPcZ_cH, gcPcP_cH)
                    compl_ZP_H = k*admit_ZP_H
                    coh_ZP_H = utils.coherence(gcPcZ_cH, gcPcP_cH, gcZcZ_cH)

                    complyfunc.add('ZP-H', [compl_ZP_H, coh_ZP_H])

            self.complyfunc = complyfunc
예제 #2
0
def main(args=None):

    if args is None:
        # Run Input Parser
        args = get_cleanspec_arguments()

    # Load Database
    # stdb>0.1.3
    try:
        db, stkeys = stdb.io.load_db(fname=args.indb, keys=args.stkeys)

    # stdb=0.1.3
    except:
        db = stdb.io.load_db(fname=args.indb)

        # Construct station key loop
        allkeys = db.keys()
        sorted(allkeys)

        # Extract key subset
        if len(args.stkeys) > 0:
            stkeys = []
            for skey in args.stkeys:
                stkeys.extend([s for s in allkeys if skey in s])
        else:
            stkeys = db.keys()
            sorted(stkeys)

    # Loop over station keys
    for stkey in list(stkeys):

        # Extract station information from dictionary
        sta = db[stkey]

        # Path where spectra are located
        specpath = Path('SPECTRA') / stkey
        if not specpath.is_dir():
            raise(Exception("Path to "+str(specpath)+" doesn`t exist - aborting"))

        # Path where average spectra will be saved
        avstpath = Path('AVG_STA') / stkey
        if not avstpath.is_dir():
            print("Path to "+str(avstpath)+" doesn`t exist - creating it")
            avstpath.mkdir(parents=True)

        # Path where plots will be saved
        if args.saveplot:
            plotpath = avstpath / 'PLOTS'
            if not plotpath.is_dir():
                plotpath.mkdir(parents=True)
        else:
            plotpath = False

        # Get catalogue search start time
        if args.startT is None:
            tstart = sta.startdate
        else:
            tstart = args.startT

        # Get catalogue search end time
        if args.endT is None:
            tend = sta.enddate
        else:
            tend = args.endT

        if tstart > sta.enddate or tend < sta.startdate:
            continue

        # Temporary print locations
        tlocs = sta.location
        if len(tlocs) == 0:
            tlocs = ['']
        for il in range(0, len(tlocs)):
            if len(tlocs[il]) == 0:
                tlocs[il] = "--"
        sta.location = tlocs

        # Update Display
        print()
        print("|===============================================|")
        print("|===============================================|")
        print("|                   {0:>8s}                    |".format(
            sta.station))
        print("|===============================================|")
        print("|===============================================|")
        print("|  Station: {0:>2s}.{1:5s}                            |".format(
            sta.network, sta.station))
        print("|      Channel: {0:2s}; Locations: {1:15s}  |".format(
            sta.channel, ",".join(tlocs)))
        print("|      Lon: {0:7.2f}; Lat: {1:6.2f}                |".format(
            sta.longitude, sta.latitude))
        print("|      Start time: {0:19s}          |".format(
            sta.startdate.strftime("%Y-%m-%d %H:%M:%S")))
        print("|      End time:   {0:19s}          |".format(
            sta.enddate.strftime("%Y-%m-%d %H:%M:%S")))
        print("|-----------------------------------------------|")

        # Filename for output average spectra
        dstart = str(tstart.year).zfill(4)+'.'+str(tstart.julday).zfill(3)+'-'
        dend = str(tend.year).zfill(4)+'.'+str(tend.julday).zfill(3)+'.'
        fileavst = avstpath / (dstart+dend+'avg_sta.pkl')

        if fileavst.exists():
            if not args.ovr:
                print("*   -> file "+str(fileavst)+" exists - continuing")
                continue

        # Containers for power and cross spectra
        coh_all = []
        ph_all = []
        coh_12_all = []
        coh_1Z_all = []
        coh_1P_all = []
        coh_2Z_all = []
        coh_2P_all = []
        coh_ZP_all = []
        ph_12_all = []
        ph_1Z_all = []
        ph_1P_all = []
        ph_2Z_all = []
        ph_2P_all = []
        ph_ZP_all = []
        ad_12_all = []
        ad_1Z_all = []
        ad_1P_all = []
        ad_2Z_all = []
        ad_2P_all = []
        ad_ZP_all = []
        nwins = []

        t1 = tstart

        # Initialize StaNoise object
        stanoise = StaNoise()

        # Loop through each day withing time range
        while t1 < tend:

            year = str(t1.year).zfill(4)
            jday = str(t1.julday).zfill(3)

            tstamp = year+'.'+jday+'.'
            filespec = specpath / (tstamp + 'spectra.pkl')

            # Load file if it exists
            if filespec.exists():
                print()
                print("*"*60)
                print('* Calculating noise spectra for key ' +
                      stkey+' and day '+year+'.'+jday)
                print("*   -> file "+str(filespec)+" found - loading")
                file = open(filespec, 'rb')
                daynoise = pickle.load(file)
                file.close()
                stanoise += daynoise
            else:
                t1 += 3600.*24.
                continue

            coh_all.append(daynoise.rotation.coh)
            ph_all.append(daynoise.rotation.ph)

            # Coherence
            coh_12_all.append(
                utils.smooth(
                    utils.coherence(
                        daynoise.cross.c12,
                        daynoise.power.c11,
                        daynoise.power.c22), 50))
            coh_1Z_all.append(
                utils.smooth(
                    utils.coherence(
                        daynoise.cross.c1Z,
                        daynoise.power.c11,
                        daynoise.power.cZZ), 50))
            coh_1P_all.append(
                utils.smooth(
                    utils.coherence(
                        daynoise.cross.c1P,
                        daynoise.power.c11,
                        daynoise.power.cPP), 50))
            coh_2Z_all.append(
                utils.smooth(
                    utils.coherence(
                        daynoise.cross.c2Z,
                        daynoise.power.c22,
                        daynoise.power.cZZ), 50))
            coh_2P_all.append(
                utils.smooth(
                    utils.coherence(
                        daynoise.cross.c2P,
                        daynoise.power.c22,
                        daynoise.power.cPP), 50))
            coh_ZP_all.append(
                utils.smooth(
                    utils.coherence(
                        daynoise.cross.cZP,
                        daynoise.power.cZZ,
                        daynoise.power.cPP), 50))

            # Phase
            try:
                ph_12_all.append(
                    180./np.pi*utils.phase(daynoise.cross.c12))
            except:
                ph_12_all.append(None)
            try:
                ph_1Z_all.append(
                    180./np.pi*utils.phase(daynoise.cross.c1Z))
            except:
                ph_1Z_all.append(None)
            try:
                ph_1P_all.append(
                    180./np.pi*utils.phase(daynoise.cross.c1P))
            except:
                ph_1P_all.append(None)
            try:
                ph_2Z_all.append(
                    180./np.pi*utils.phase(daynoise.cross.c2Z))
            except:
                ph_2Z_all.append(None)
            try:
                ph_2P_all.append(
                    180./np.pi*utils.phase(daynoise.cross.c2P))
            except:
                ph_2P_all.append(None)
            try:
                ph_ZP_all.append(
                    180./np.pi*utils.phase(daynoise.cross.cZP))
            except:
                ph_ZP_all.append(None)

            # Admittance
            ad_12_all.append(utils.smooth(utils.admittance(
                daynoise.cross.c12, daynoise.power.c11), 50))
            ad_1Z_all.append(utils.smooth(utils.admittance(
                daynoise.cross.c1Z, daynoise.power.c11), 50))
            ad_1P_all.append(utils.smooth(utils.admittance(
                daynoise.cross.c1P, daynoise.power.c11), 50))
            ad_2Z_all.append(utils.smooth(utils.admittance(
                daynoise.cross.c2Z, daynoise.power.c22), 50))
            ad_2P_all.append(utils.smooth(utils.admittance(
                daynoise.cross.c2P, daynoise.power.c22), 50))
            ad_ZP_all.append(utils.smooth(utils.admittance(
                daynoise.cross.cZP, daynoise.power.cZZ), 50))

            t1 += 3600.*24.

        # Convert to numpy arrays
        coh_all = np.array(coh_all)
        ph_all = np.array(ph_all)
        coh_12_all = np.array(coh_12_all)
        coh_1Z_all = np.array(coh_1Z_all)
        coh_1P_all = np.array(coh_1P_all)
        coh_2Z_all = np.array(coh_2Z_all)
        coh_2P_all = np.array(coh_2P_all)
        coh_ZP_all = np.array(coh_ZP_all)
        ph_12_all = np.array(ph_12_all)
        ph_1Z_all = np.array(ph_1Z_all)
        ph_1P_all = np.array(ph_1P_all)
        ph_2Z_all = np.array(ph_2Z_all)
        ph_2P_all = np.array(ph_2P_all)
        ph_ZP_all = np.array(ph_ZP_all)
        ad_12_all = np.array(ad_12_all)
        ad_1Z_all = np.array(ad_1Z_all)
        ad_1P_all = np.array(ad_1P_all)
        ad_2Z_all = np.array(ad_2Z_all)
        ad_2P_all = np.array(ad_2P_all)
        ad_ZP_all = np.array(ad_ZP_all)

        # Store transfer functions as objects for plotting
        coh = Cross(coh_12_all, coh_1Z_all, coh_1P_all,
                    coh_2Z_all, coh_2P_all, coh_ZP_all)
        ph = Cross(ph_12_all, ph_1Z_all, ph_1P_all,
                   ph_2Z_all, ph_2P_all, ph_ZP_all)
        ad = Cross(ad_12_all, ad_1Z_all, ad_1P_all,
                   ad_2Z_all, ad_2P_all, ad_ZP_all)

        # Quality control to identify outliers
        stanoise.QC_sta_spectra(pd=args.pd, tol=args.tol, alpha=args.alpha,
                                fig_QC=args.fig_QC, debug=args.debug,
                                save=plotpath, form=args.form)

        # Average spectra for good days
        stanoise.average_sta_spectra(
            fig_average=args.fig_average,
            save=plotpath, form=args.form)

        if args.fig_av_cross:
            fname = stkey + '.' + 'av_coherence'
            plot = plotting.fig_av_cross(stanoise.f, coh, stanoise.gooddays,
                              'Coherence', stanoise.ncomp, key=stkey, lw=0.5)
            # if plotpath.is_dir():
            if plotpath:
                plot.savefig(str(plotpath / (fname + '.' + args.form)),
                            dpi=300, bbox_inches='tight', format=args.form)
            else:
                plot.show()

            fname = stkey + '.' + 'av_admittance'
            plot = plotting.fig_av_cross(stanoise.f, ad, stanoise.gooddays,
                              'Admittance', stanoise.ncomp, key=stkey, lw=0.5)

            if plotpath:
                plot.savefig(str(plotpath / (fname + '.' + args.form)),
                            dpi=300, bbox_inches='tight', format=args.form)
            else:
                plot.show()

            fname = stkey + '.' + 'av_phase'
            plot = plotting.fig_av_cross(stanoise.f, ph, stanoise.gooddays,
                              'Phase', stanoise.ncomp, key=stkey, marker=',', lw=0)

            if plotpath:
                plot.savefig(str(plotpath / (fname + '.' + args.form)),
                            dpi=300, bbox_inches='tight', format=args.form)
            else:
                plot.show()

        if args.fig_coh_ph and stanoise.direc is not None:
            fname = stkey + '.' + 'coh_ph'
            plot = plotting.fig_coh_ph(coh_all, ph_all, stanoise.direc)
            if plotpath:
                plot.savefig(str(plotpath / (fname + '.' + args.form)),
                            dpi=300, bbox_inches='tight', format=args.form)
            else:
                plot.show()

        # Save to file
        stanoise.save(fileavst)
예제 #3
0
def test_StaNoise():

    args = test_args.test_get_dailyspec_arguments()
    stanoise = test_classes.test_stanoise_demo()

    # Containers for power and cross spectra
    coh_all = []
    ph_all = []
    coh_12_all = []
    coh_1Z_all = []
    coh_1P_all = []
    coh_2Z_all = []
    coh_2P_all = []
    coh_ZP_all = []
    ph_12_all = []
    ph_1Z_all = []
    ph_1P_all = []
    ph_2Z_all = []
    ph_2P_all = []
    ph_ZP_all = []
    ad_12_all = []
    ad_1Z_all = []
    ad_1P_all = []
    ad_2Z_all = []
    ad_2P_all = []
    ad_ZP_all = []
    nwins = []

    for dn in stanoise.daylist:
        dn.QC_daily_spectra()
        dn.average_daily_spectra()
        coh_all.append(dn.rotation.coh)
        ph_all.append(dn.rotation.ph)

        # Coherence
        coh_12_all.append(
            utils.smooth(
                utils.coherence(dn.cross.c12, dn.power.c11, dn.power.c22), 50))
        coh_1Z_all.append(
            utils.smooth(
                utils.coherence(dn.cross.c1Z, dn.power.c11, dn.power.cZZ), 50))
        coh_1P_all.append(
            utils.smooth(
                utils.coherence(dn.cross.c1P, dn.power.c11, dn.power.cPP), 50))
        coh_2Z_all.append(
            utils.smooth(
                utils.coherence(dn.cross.c2Z, dn.power.c22, dn.power.cZZ), 50))
        coh_2P_all.append(
            utils.smooth(
                utils.coherence(dn.cross.c2P, dn.power.c22, dn.power.cPP), 50))
        coh_ZP_all.append(
            utils.smooth(
                utils.coherence(dn.cross.cZP, dn.power.cZZ, dn.power.cPP), 50))

        # Phase
        try:
            ph_12_all.append(180. / np.pi * utils.phase(dn.cross.c12))
        except:
            ph_12_all.append(None)
        try:
            ph_1Z_all.append(180. / np.pi * utils.phase(dn.cross.c1Z))
        except:
            ph_1Z_all.append(None)
        try:
            ph_1P_all.append(180. / np.pi * utils.phase(dn.cross.c1P))
        except:
            ph_1P_all.append(None)
        try:
            ph_2Z_all.append(180. / np.pi * utils.phase(dn.cross.c2Z))
        except:
            ph_2Z_all.append(None)
        try:
            ph_2P_all.append(180. / np.pi * utils.phase(dn.cross.c2P))
        except:
            ph_2P_all.append(None)
        try:
            ph_ZP_all.append(180. / np.pi * utils.phase(dn.cross.cZP))
        except:
            ph_ZP_all.append(None)

        # Admittance
        ad_12_all.append(
            utils.smooth(utils.admittance(dn.cross.c12, dn.power.c11), 50))
        ad_1Z_all.append(
            utils.smooth(utils.admittance(dn.cross.c1Z, dn.power.c11), 50))
        ad_1P_all.append(
            utils.smooth(utils.admittance(dn.cross.c1P, dn.power.c11), 50))
        ad_2Z_all.append(
            utils.smooth(utils.admittance(dn.cross.c2Z, dn.power.c22), 50))
        ad_2P_all.append(
            utils.smooth(utils.admittance(dn.cross.c2P, dn.power.c22), 50))
        ad_ZP_all.append(
            utils.smooth(utils.admittance(dn.cross.cZP, dn.power.cZZ), 50))

    # Convert to numpy arrays
    coh_all = np.array(coh_all)
    ph_all = np.array(ph_all)
    coh_12_all = np.array(coh_12_all)
    coh_1Z_all = np.array(coh_1Z_all)
    coh_1P_all = np.array(coh_1P_all)
    coh_2Z_all = np.array(coh_2Z_all)
    coh_2P_all = np.array(coh_2P_all)
    coh_ZP_all = np.array(coh_ZP_all)
    ph_12_all = np.array(ph_12_all)
    ph_1Z_all = np.array(ph_1Z_all)
    ph_1P_all = np.array(ph_1P_all)
    ph_2Z_all = np.array(ph_2Z_all)
    ph_2P_all = np.array(ph_2P_all)
    ph_ZP_all = np.array(ph_ZP_all)
    ad_12_all = np.array(ad_12_all)
    ad_1Z_all = np.array(ad_1Z_all)
    ad_1P_all = np.array(ad_1P_all)
    ad_2Z_all = np.array(ad_2Z_all)
    ad_2P_all = np.array(ad_2P_all)
    ad_ZP_all = np.array(ad_ZP_all)

    # Store transfer functions as objects for plotting
    coh = Cross(coh_12_all, coh_1Z_all, coh_1P_all, coh_2Z_all, coh_2P_all,
                coh_ZP_all)
    ph = Cross(ph_12_all, ph_1Z_all, ph_1P_all, ph_2Z_all, ph_2P_all,
               ph_ZP_all)
    ad = Cross(ad_12_all, ad_1Z_all, ad_1P_all, ad_2Z_all, ad_2P_all,
               ad_ZP_all)

    stanoise.QC_sta_spectra(pd=args.pd,
                            tol=args.tol,
                            alpha=args.alpha,
                            fig_QC=True,
                            debug=False,
                            save='tmp',
                            form='png')

    stanoise.average_sta_spectra(fig_average=True, save='tmp', form='png')

    plot = plotting.fig_av_cross(stanoise.f,
                                 coh,
                                 stanoise.gooddays,
                                 'Coherence',
                                 stanoise.ncomp,
                                 key='7D.M08A',
                                 lw=0.5)
    plot.close()

    plot = plotting.fig_coh_ph(coh_all, ph_all, stanoise.direc)
    plot.close()

    return stanoise