Ejemplo n.º 1
0
def run_hashpy(catalog, config, outfile):
    """
    Wrapper on hashpy for calculating HASH focal mechanisms
    :param catalog: :class: obspy.core.event.Catalog
    :param config: Configuration dict for hashpy
    :return:
    """
    new_cat = Catalog()
    for ev in catalog:
        eid = str(ev.resource_id).split('/')[-1]
        # Set up hashpy object
        hp = HashPype(**config)
        hp.input(ev, format="OBSPY")
        hp.load_velocity_models()
        hp.generate_trial_data()
        try:
            hp.calculate_takeoff_angles()
        except:
            print('Error in toa calc for eid: {}'.format(eid))
            continue
        pass1 = hp.check_minimum_polarity()
        pass2 = hp.check_maximum_gap()
        if pass1 and pass2:
            try:
                hp.calculate_hash_focalmech()
                hp.calculate_quality()
            except:
                print('Error in fm calc for eid: {}'.format(eid))
                continue
        else:
            print("Minimum polarity and/or maximum gap check failed")
            continue
        new_cat += hp.output(format="OBSPY")
    new_cat.write(outfile, format="QUAKEML")
    return
Ejemplo n.º 2
0
    def write(self, filename, compress=True, catalog_format="QUAKEML"):
        """
        Write the tribe to a file using tar archive formatting.

        :type filename: str
        :param filename:
            Filename to write to, if it exists it will be appended to.
        :type compress: bool
        :param compress:
            Whether to compress the tar archive or not, if False then will
            just be files in a folder.
        :type catalog_format: str
        :param catalog_format:
            What format to write the detection-catalog with. Only Nordic,
            SC3ML, QUAKEML are supported. Note that not all information is
            written for all formats (QUAKEML is the most complete, but is
            slow for IO).

        .. rubric:: Example

        >>> tribe = Tribe(templates=[Template(name='c', st=read())])
        >>> tribe.write('test_tribe')
        Tribe of 1 templates
        """
        from eqcorrscan.core.match_filter import CAT_EXT_MAP

        if catalog_format not in CAT_EXT_MAP.keys():
            raise TypeError("{0} is not supported".format(catalog_format))
        dirname, ext = os.path.splitext(filename)
        if not os.path.isdir(dirname):
            os.makedirs(dirname)
        self._par_write(dirname)
        tribe_cat = Catalog()
        for t in self.templates:
            if t.event is not None:
                # Check that the name in the comment matches the template name
                for comment in t.event.comments:
                    if comment.text and comment.text.startswith(
                            "eqcorrscan_template_"):
                        comment.text = "eqcorrscan_template_{0}".format(t.name)
                tribe_cat.append(t.event)
        if len(tribe_cat) > 0:
            tribe_cat.write(
                os.path.join(dirname, 'tribe_cat.{0}'.format(
                    CAT_EXT_MAP[catalog_format])), format=catalog_format)
        for template in self.templates:
            template.st.write(
                os.path.join(dirname, '{0}.ms'.format(template.name)),
                format='MSEED')
        if compress:
            if not filename.endswith(".tgz"):
                Logger.info("Appending '.tgz' to filename.")
                filename += ".tgz"
            with tarfile.open(filename, "w:gz") as tar:
                tar.add(dirname, arcname=os.path.basename(dirname))
            shutil.rmtree(dirname)
        return self
Ejemplo n.º 3
0
def consolidate_qmls(directory, outfile=False):
    """
    Take directory of single-event qmls from above function and consolidate
    into one, year-long Catalog.write() qml file.
    :param directory: Directory of qml files
    :param outfile: Defaults to False, else is path to new outfile
    :return: obspy.core.Catalog
    """
    qmls = glob(directory)
    cat = Catalog()
    for qml in qmls:
        cat += read_events(qml)
    if outfile:
        cat.write(outfile)
    return cat
Ejemplo n.º 4
0
 def test_more_than_three_mags(self):
     cat = Catalog()
     cat += full_test_event()
     cat[0].magnitudes.append(Magnitude(
         mag=0.9, magnitude_type='MS', creation_info=CreationInfo('TES'),
         origin_id=cat[0].origins[0].resource_id))
     with NamedTemporaryFile(suffix='.out') as tf:
         # raises UserWarning: mb is not convertible
         with warnings.catch_warnings():
             warnings.simplefilter('ignore', UserWarning)
             cat.write(tf.name, format='nordic')
         # raises "UserWarning: AIN in header, currently unsupported"
         with warnings.catch_warnings():
             warnings.simplefilter('ignore', UserWarning)
             cat_back = read_events(tf.name)
         for event_1, event_2 in zip(cat, cat_back):
             self.assertTrue(
                 len(event_1.magnitudes) == len(event_2.magnitudes))
             _assert_similarity(event_1, event_2)
Ejemplo n.º 5
0
 def test_more_than_three_mags(self):
     cat = Catalog()
     cat += full_test_event()
     cat[0].magnitudes.append(
         Magnitude(mag=0.9,
                   magnitude_type='MS',
                   creation_info=CreationInfo('TES'),
                   origin_id=cat[0].origins[0].resource_id))
     with NamedTemporaryFile(suffix='.out') as tf:
         # raises UserWarning: mb is not convertible
         with warnings.catch_warnings():
             warnings.simplefilter('ignore', UserWarning)
             cat.write(tf.name, format='nordic')
         # raises "UserWarning: AIN in header, currently unsupported"
         with warnings.catch_warnings():
             warnings.simplefilter('ignore', UserWarning)
             cat_back = read_events(tf.name)
         for event_1, event_2 in zip(cat, cat_back):
             self.assertTrue(
                 len(event_1.magnitudes) == len(event_2.magnitudes))
             _assert_similarity(event_1, event_2)
Ejemplo n.º 6
0
        def write_quakeml(list_of_networks):
            events = []
            catalog = Catalog()
            for network in list_of_networks:
                for shot_line in network.shot_lines:
                    for shot in shot_line.shots:
                        origins = []
                        magnitudes = []
                        iris_custom_ns = "http://www.fdsn.org/xml/event/1/iris"
                        origin = obspy.core.event.origin.Origin()
                        origin.time = shot.start_time
                        origin.latitude = shot.lat
                        origin.longitude = shot.lon
                        origin.extra = {
                            'Elevation': {
                                'value': str(shot.elev),
                                'namespace': iris_custom_ns
                            }
                        }
                        if shot.depth != 0:
                            origin.depth = shot.depth
                        origins.append(origin)
                        magnitudes.append(
                            obspy.core.event.magnitude.Magnitude(
                                mag=shot.mag, magnitude_type=shot.mag_units))

                        identifier = obspy.core.event.base.ResourceIdentifier(
                            id=str(network.code) + "." +
                            str(shot_line.name[-3:]) + "." + str(shot.shot_id))
                        event = (obspy.core.event.Event(
                            resource_id=identifier,
                            event_type="Controlled Explosion",
                            origins=origins,
                            magnitudes=magnitudes))
                        event.extra = {
                            'Network': {
                                'value': str(network.code),
                                'type': 'attribute',
                                'namespace': iris_custom_ns
                            },
                            'ReportNum': {
                                'value': str(network.reportnum),
                                'type': 'attribute',
                                'namespace': iris_custom_ns
                            },
                            'ShotLine': {
                                'value': str(shot_line.name[-3:]),
                                'type': 'attribute',
                                'namespace': iris_custom_ns
                            },
                            'Shot_id': {
                                'value': str(shot.shot_id),
                                'type': 'attribute',
                                'namespace': iris_custom_ns
                            }
                        }
                        events.append(event)

                catalog.events = events

            if catalog.events:
                if outfile:
                    target = outfile
                else:
                    target = sys.stdout

                catalog.write(target,
                              "QUAKEML",
                              nsmap={"iris": iris_custom_ns})
            else:
                raise NoDataError("Request resulted in no data being returned")
Ejemplo n.º 7
0
        cat = read_events(save_fid)

    print(f"Catalog has {len(cat)} events")

    # Not all events return focal mechanisms, kick those out
    events_with_fm = []
    for event in cat[:]:
        if event.focal_mechanisms and \
                   event.preferred_focal_mechanism().moment_tensor:
            if exclude_ak and ("ak" in parse_event_id(event)):
                continue
            events_with_fm.append(event)

    # Make the slimmed down catalog that only contains earthquakes with foc mecs
    cat = Catalog(events=events_with_fm)
    cat.write(filename=fm_save_fid, format="QUAKEML")
else:
    print(f"reading fm catalog from {fm_save_fid}")
    cat = read_events(fm_save_fid)
print(f"Catalog w/ focal mechanisms has {len(cat)} events")

# Plot moment tensors, colored by depth, scaled by magnitude
if plot:
    print("plotting crude beachball map")

    # Grab catalog information for relative colors and scaling
    xvals, yvals, depths, mags, event_ids = [], [], [], [], []
    for event in cat:
        xvals.append(event.preferred_origin().longitude)
        yvals.append(event.preferred_origin().latitude)
        depths.append(event.preferred_origin().depth * 1E-3)  # units km
Ejemplo n.º 8
0
# Extract just the
for group in groups:
    if len(group) > 7:
        big_group_ids.append(list(zip(*group)[1]))
        big_group_streams.append(list(zip(*group)[0]))
for i, group_ids in enumerate(big_group_ids):
    file_names = '/home/chet/data/mrp_data/catalogs/2015/final/thresh_' +\
    str(corr_thresh) + '_group_' + str(i)
    temp_cat = Catalog()
    with open(file_names + '.csv', 'wb') as f:
        csvwriter = csv.writer(f, delimiter=',')
        for event in cat:
            ev_name = str(event.resource_id).split('/')[-1:][0]
            if ev_name in group_ids:
                x = str(event.preferred_origin().longitude)
                y = str(event.preferred_origin().latitude)
                z = str(event.preferred_origin().depth)
                csvwriter.writerow([x, y, z])
                temp_cat.append(event)
    temp_cat.write(file_names + '.shp', format="SHAPEFILE")

# Below we'll plot picks over templates for given indices
ev_id = '2015sora495962'
res_id = ResourceIdentifier('smi:org.gfz-potsdam.de/geofon/2015sora495962')
for event in cat:
    if event.resource_id == res_id:
        test_ev = event
for i, group_id in enumerate(big_group_ids):
    if group_id == ev_id:
        pretty_template_plot(big_group_streams[i], picks=test_ev.picks)
Ejemplo n.º 9
0
                                corr_thresh=0.30,
                                allow_shift=True,
                                shift_len=25,
                                save_corrmat=True,
                                cores=cores,
                                debug=2)
    for i, grp in enumerate(groups):
        corrgrp_cat = Catalog()
        f_name_root = '/media/chet/hdd/seismic/NZ/catalogs/'
        f_name = 'spacegrp_%s_corrgrp_%03d' % (grp_num, i)
        for e in cat:
            for temp_st in grp:
                if e.resource_id == temp_st[1]:
                    corrgrp_cat.append(e)
        corrgrp_cat.write(f_name_root + 'qml/corr_groups/1_sec_temps/' +
                          f_name + '.xml',
                          format="QUAKEML")
        corrgrp_cat.write(f_name_root + 'shp/corr_groups/1_sec_temps/' +
                          f_name + '.shp',
                          format="SHAPEFILE")

# Also trying correlation cluster for whole catalog
cat = read_events(
    '/media/chet/hdd/seismic/NZ/catalogs/qml/2015_nlloc_final_run02_group_refined.xml'
)
template_list = [(template_dict[ev.resource_id], ev.resource_id) for ev in cat]
plt_name = '/media/chet/hdd/seismic/NZ/catalogs/corr_figs/4_sec_temps/' +\
           'entire_cat_cluster_dend_shift25.png'
corr_mat = '/media/chet/hdd/seismic/NZ/catalogs/corr_figs/1_sec_temps/' +\
           'entire_cat_mat_shift25.npy'
groups = clustering.cluster(template_list,
Ejemplo n.º 10
0
new_cat = Catalog()
for catalog in cat_list:
    #Read in catalog
    cat = read_events(catalog)
    for event in cat:
        lat = event.preferred_origin().latitude
        lon = event.preferred_origin().longitude
        if lat > -38.661 and lat < -38.483 and lon > 176.094 and lon < 176.296:
            new_cat.append(event)
        else:
            print('Event outside bounding box...')

#Write catalog to various formats
#VELEST format
new_cat.write(
    '/home/chet/data/mrp_data/catalogs/2015/final/cnv/rotnga2015.cnv',
    format="CNV")
#Shapefile
new_cat.write('/home/chet/data/mrp_data/catalogs/2015/final/shp/rotnga2015',
              format="SHAPEFILE")
#Loop to write single event NLLOC files
for event in new_cat:
    ev_name = str(event.resource_id).split('/')[2]
    event.write('/home/chet/data/mrp_data/catalogs/2015/final/nlloc/' +
                ev_name + '.nll',
                format="NLLOC_OBS")
#Loop to write to x, y, z text
import csv
with open(
        '/home/chet/data/mrp_data/catalogs/2015/final/xyz/' +
        'rotnga_2015_wgs84.csv', 'wb') as f:
Ejemplo n.º 11
0
def origins_pruning(xml_name,
                    output_fn='origenes_preferidos.xml',
                    check_db=False,
                    quadrant="None"):
    """Delete all origins that are not the prefered origin
    in a seiscomp event xml file. Returns a xml with origins only

    Parameters
    ----------
    xml_name : str
        Name of events type SeisComP3 xml file.
    output_fn : str
        Name of output SeisComP3 xml file.
    """

    change_xml_version(xml_name)

    print(
        '\n\nRemoving origins that are not the prefered one in the xml %s\n' %
        xml_name)
    try:
        cat = obs.read_events(xml_name, id_prefix='', format='SC3ML')
    except FileNotFoundError:
        print('\n\t No existe el archivo %s, se salta este proceso\n' %
              xml_name)
        sys.exit(1)

    cat2 = Catalog()
    # para acada evento en el xml de eventos
    for i, ev in enumerate(cat):
        magnitude = ev.preferred_magnitude().mag
        pref_orig = cat[i].preferred_origin()

        if not pass_origin_quality(pref_orig, magnitude):
            # imprime en rojo que el evento no pasó el filtro de calidad
            print(
                f'\033[91m Evento {pref_orig.time} no pasó el filtro de calidad \033[0m'
            )
            continue

        # Si check_db es True se verifica si el evento ya esta en la base de datos
        # en caso de que si devuelve True, se elimina el evento del xml
        if check_db or quadrant != "None":
            watcher = Watcher(pref_orig)
            region = ev.event_descriptions[0].text.encode('utf-8')
            if check_db and watcher.exist_in_db():
                print(
                    f'\n\n\t El evento\033[91m {pref_orig.time} - {region}\033[0m ya existe en la base de datos, se elimina del xml\n\n'
                )
                continue
            if quadrant != "None" and not watcher.check_in_region(quadrant):
                print(f'region {region}')
                print(
                    f'\n\n\t El evento\033[91m {pref_orig.time} : {pref_orig.latitude}, {pref_orig.longitude} : {region}\033[0m fuera del cuadrante {quadrant}, se elimina del xml\n\n'
                )
                continue

        del cat[i].origins[:-1]
        cat2.append(cat[i])

    # se escribe xml con solo los orígenes preferidos
    cat2.write(output_fn,
               format='SC3ML',
               validate=True,
               event_removal=True,
               verbose=True)

    remove_id_prefix(output_fn)

    print(
        '\n\tArchivo con origenes preferidos para migrar a SeisComP3:\n\n\t  %s\n'
        % output_fn)
Ejemplo n.º 12
0
    def write(self,
              filename,
              format='tar',
              write_detection_catalog=True,
              catalog_format="QUAKEML",
              overwrite=False):
        """
        Write Family out, select output format.

        :type format: str
        :param format:
            One of either 'tar', 'csv', or any obspy supported
            catalog output. See note below on formats
        :type filename: str
        :param filename: Path to write file to.
        :type write_detection_catalog: bool
        :param write_detection_catalog:
            Whether to write the detection catalog object or not - writing
            large catalog files can be slow, and catalogs can be reconstructed
            from the Tribe.
        :type catalog_format: str
        :param catalog_format:
            What format to write the detection-catalog with. Only Nordic,
            SC3ML, QUAKEML are supported. Note that not all information is
            written for all formats (QUAKEML is the most complete, but is
            slow for IO).
        :type overwrite: bool
        :param overwrite:
            Specifies whether detection-files are overwritten if they exist
            already. By default, no files are overwritten.

        .. NOTE::
            csv format will write out detection objects, all other
            outputs will write the catalog.  These cannot be rebuilt into
            a Family object.  The only format that can be read back into
            Family objects is the 'tar' type.

        .. NOTE::
            We recommend writing to the 'tar' format, which will write out
            all the template information (wavefiles as miniseed and metadata)
            alongside the detections and store these in a tar archive. This
            is readable by other programs and maintains all information
            required for further study.

        .. rubric:: Example

        >>> party = Party().read()
        >>> party.write('test_tar_write', format='tar')
        Party of 4 Families.
        >>> party.write('test_csv_write.csv', format='csv')
        Party of 4 Families.
        >>> party.write('test_quakeml.xml', format='quakeml')
        Party of 4 Families.
        """
        from eqcorrscan.core.match_filter.tribe import Tribe
        from eqcorrscan.core.match_filter import CAT_EXT_MAP

        if catalog_format not in CAT_EXT_MAP.keys():
            raise TypeError("{0} is not supported".format(catalog_format))
        if format.lower() == 'csv':
            if os.path.isfile(filename) and not overwrite:
                raise MatchFilterError('Will not overwrite existing file: %s' %
                                       filename)
            if os.path.isfile(filename) and overwrite:
                os.remove(filename)
            for family in self.families:
                write_detections(fname=filename,
                                 detections=family.detections,
                                 mode="a")
        elif format.lower() == 'tar':
            if not filename.endswith('.tgz'):
                filename = filename + ".tgz"
            if os.path.exists(filename) and not overwrite:
                raise IOError('Will not overwrite existing file: %s' %
                              filename)
            # os.makedirs(filename)
            with temporary_directory() as temp_dir:
                Tribe([f.template for f in self.families
                       ]).write(filename=temp_dir,
                                compress=False,
                                catalog_format=catalog_format)
                if write_detection_catalog:
                    all_cat = Catalog()
                    for family in self.families:
                        all_cat += family.catalog
                    if not len(all_cat) == 0:
                        all_cat.write(join(
                            temp_dir,
                            'catalog.{0}'.format(CAT_EXT_MAP[catalog_format])),
                                      format=catalog_format)
                for i, family in enumerate(self.families):
                    Logger.debug('Writing family %i' % i)
                    name = family.template.name + '_detections.csv'
                    name_to_write = join(temp_dir, name)
                    _write_family(family=family, filename=name_to_write)
                with tarfile.open(filename, "w:gz") as tar:
                    tar.add(temp_dir, arcname=os.path.basename(filename))
        else:
            Logger.warning('Writing only the catalog component, metadata '
                           'will not be preserved')
            self.get_catalog().write(filename=filename, format=format)
        return self
    # Make picks for detections from template picks
    det_picks = []
    for p in template_event.picks:
        delay_template = p.time - min_template_starttime
        det_pick_time = detect_time + delay_template
        pick = Pick(time=det_pick_time,
                    phase_hint=p.phase_hint,
                    waveform_id=p.waveform_id)
        det_picks.append(pick)

    # figure out origin time for detection
    pick1_temp = template_event.picks[0]
    origin_det = template_event.origins[0].copy()
    pick1_det = [
        p for p in det_picks if p.waveform_id == pick1_temp.waveform_id
    ][0]
    origin_det.time = pick1_det.time - (pick1_temp.time -
                                        template_event.origins[0].time)

    # Create and save event for detection
    event = Event(picks=det_picks, origins=[origin_det])
    event.preferred_origin_id = event.origins[0].resource_id
    catalog.append(event)

catalog_dir = os.path.join(os.getcwd(), "families_events")
catalog_fname = "catalog_" + family_name.split(".")[0] + ".xml"
catalog_file = os.path.join(catalog_dir, catalog_fname)
Logger.info("Now writing catalogue to file %s" % catalog_file)
catalog.write(catalog_file, format="QUAKEML")
Ejemplo n.º 14
0
def cluster_cat(catalog,
                corr_thresh,
                corr_params=None,
                raw_wav_dir=None,
                dist_mat=False,
                out_cat=None,
                show=False,
                method='average'):
    """
    Cross correlate all templates in a tribe and return separate tribes for
    each cluster
    :param tribe: Tribe to cluster
    :param corr_thresh: Correlation threshold for clustering
    :param corr_params: Dictionary of filter parameters. Must include keys:
        lowcut, highcut, samp_rate, filt_order, pre_pick, length, shift_len,
        cores
    :param raw_wav_dir: Directory of waveforms to take from
    :param dist_mat: If there's a precomputed distance matrix, use this
        instead of doing all the correlations
    :param out_cat: Output catalog corresponding to the events
    :param show: Show the dendrogram? Careful as this can exceed max recursion
    :param wavs: Should we even bother with processing waveforms? Otherwise
        will just populate the tribe with an empty Stream
    :return:

    .. Note: Functionality here is pilaged from align design as we don't
        want the multiplexed portion of that function.
    """

    if corr_params and raw_wav_dir:
        shift_len = corr_params['shift_len']
        lowcut = corr_params['lowcut']
        highcut = corr_params['highcut']
        samp_rate = corr_params['samp_rate']
        filt_order = corr_params['filt_order']
        pre_pick = corr_params['pre_pick']
        length = corr_params['length']
        cores = corr_params['cores']
        raw_wav_files = glob('%s/*' % raw_wav_dir)
        raw_wav_files.sort()
        all_wavs = [wav.split('/')[-1].split('_')[-3] for wav in raw_wav_files]
        print(all_wavs[0])
        names = [
            ev.resource_id.id.split('/')[-1] for ev in catalog
            if ev.resource_id.id.split('/')[-1] in all_wavs
        ]
        print(names[0])
        wavs = [
            wav for wav in raw_wav_files
            if wav.split('/')[-1].split('_')[-3] in names
        ]
        print(wavs[0])
        new_cat = Catalog(events=[
            ev for ev in catalog if ev.resource_id.id.split('/')[-1] in names
        ])
        print('Processing temps')
        temp_list = [(shortproc(read('{}/*'.format(tmp)),
                                lowcut=lowcut,
                                highcut=highcut,
                                samp_rate=samp_rate,
                                filt_order=filt_order,
                                parallel=True,
                                num_cores=cores),
                      ev.resource_id.id.split('/')[-1])
                     for tmp, ev in zip(wavs, new_cat)]
        print('Clipping traces')
        rm_temps = []
        for i, temp in enumerate(temp_list):
            print('Clipping template %s' % new_cat[i].resource_id.id)
            rm_ts = []  # Make a list of traces with no pick to remove
            rm_ev = []
            for tr in temp[0]:
                pk = [
                    pk for pk in new_cat[i].picks
                    if pk.waveform_id.station_code == tr.stats.station
                    and pk.waveform_id.channel_code == tr.stats.channel
                ]
                if len(pk) == 0:
                    rm_ts.append(tr)
                else:
                    tr.trim(starttime=pk[0].time - shift_len - pre_pick,
                            endtime=pk[0].time - pre_pick + length + shift_len)
            # Remove pickless traces
            for rm in rm_ts:
                temp[0].traces.remove(rm)
            # If trace lengths are internally inconsistent, remove template
            if len(list(set([len(tr) for tr in temp[0]]))) > 1:
                rm_temps.append(temp)
            # If template is now length 0, remove it and associated event
            if len(temp[0]) == 0:
                rm_temps.append(temp)
                rm_ev.append(new_cat[i])
        for t in rm_temps:
            temp_list.remove(t)
        # Remove the corresponding events as well so catalog and distmat
        # are the same shape
        for rme in rm_ev:
            new_cat.events.remove(rme)
    print(new_cat)
    new_cat.write(out_cat, format="QUAKEML")
    print('Clustering')
    if isinstance(dist_mat, np.ndarray):
        print('Assuming the tribe provided is the same shape as dist_mat')
        # Dummy streams
        temp_list = [(Stream(), ev) for ev in catalog]
        groups = cluster_from_dist_mat(dist_mat=dist_mat,
                                       temp_list=temp_list,
                                       show=show,
                                       corr_thresh=corr_thresh,
                                       method=method)
    else:
        groups = clustering.cluster(temp_list,
                                    show=show,
                                    corr_thresh=corr_thresh,
                                    shift_len=shift_len * 2,
                                    save_corrmat=True,
                                    cores=cores)
    group_tribes = []
    group_cats = []
    if corr_params:
        for group in groups:
            group_tribes.append(
                Tribe(templates=[
                    Template(st=tmp[0],
                             name=tmp[1].resource_id.id.split('/')[-1],
                             event=tmp[1],
                             highcut=highcut,
                             lowcut=lowcut,
                             samp_rate=samp_rate,
                             filt_order=filt_order,
                             prepick=pre_pick) for tmp in group
                ]))
            group_cats.append(Catalog(events=[tmp[1] for tmp in group]))
    else:
        for group in groups:
            group_tribes.append(
                Tribe(templates=[
                    Template(st=tmp[0],
                             name=tmp[1].resource_id.id.split('/')[-1],
                             event=tmp[1].event,
                             highcut=None,
                             lowcut=None,
                             samp_rate=None,
                             filt_order=None,
                             prepick=None) for tmp in group
                ]))
            group_cats.append(Catalog(events=[tmp[1] for tmp in group]))
    return group_tribes, group_cats
Ejemplo n.º 15
0
class Request(object):
    """"Initialises the FDSN request for
    the waveforms, the preprocessing of the waveforms, and the creation of
    time domain receiver functions."""
    def __init__(self,
                 phase,
                 rot,
                 evtloc,
                 statloc,
                 rawloc,
                 preproloc,
                 rfloc,
                 deconmeth,
                 starttime,
                 endtime,
                 wavdownload=True,
                 pol: str = 'v',
                 minmag: float or int = 5.5,
                 event_coords=None,
                 network=None,
                 station=None,
                 waveform_client=None,
                 re_client=['IRIS'],
                 evtcat=None,
                 debug=False):
        """
        Create object that is used to start the receiver function
        workflow.

        :param phase: Arrival phase that is to be used as source phase.
            "S" to create S-Sp receiver functions and "P" for P-Ps receiver
            functions, "SKS" or "ScS" are allowed as well.
        :type phase: str
        :param rot: The coordinate system in that the seismogram should be
            rotated
            prior to deconvolution. Options are "RTZ" for radial, transverse,
            vertical; "LQT" for an orthogonal coordinate system computed by
            minimising primary energy on the
            converted component, or "PSS" for a rotation along the polarisation
            directions using the Litho1.0 surface wave tomography model.
        :type rot: str
        :param evtloc: Directory, in which to store the event catalogue (xml).
        :type evtloc: str
        :param statloc: Directory, in which to store the station inventories
                        (xml).
        :type statloc: str
        :param rawloc: Directory, in which to store the raw waveform data.
        :type rawloc: str
        :param preproloc: Directory, in which to store
            the preprocessed waveform data (mseed).
        :type preproloc: str
        :param rfloc: Directory, in which to store the receiver functions in
            time domain (sac).
        :type rfloc: str
        :param deconmeth: The deconvolution method to use for the RF creation.
            Possible options are:
            'it': iterative time domain deconvolution (Ligorria & Ammon, 1999)
            'dampedf': damped frequency deconvolution
            'fqd': frequency dependent damping - not a good choice for SRF
            'waterlevel': Langston (1977)
            'multit': for multitaper (Helffrich, 2006)
            False/None: don't create RFs
        :type deconmeth: str
        :param starttime: Earliest event date to be considered.
        :type starttime: ~obspy.UTCDateTime
        :param endtime: Latest event date to be considered.
        :type endtime: ~obspy.UTCDateTime
        :param wavdownload: Do you want to start a new download (True),
            update the current database (True) or only preprocess and create
            RFs from an existing database (False). False is a lot faster as all
            CPUs can be used and the preprocessing does not have to wait for
            the download, defaults to True.
        :type wavdownload: bool, optional
        :param pol: Polarisation to use as source wavelet. Either "v" for
            vertically polarised or 'h' for horizontally polarised S-waves.
            Will be ignored if phase='S', by default 'v'.
        :type pol: str, optional
        :param minmag: Minimum magnitude, by default 5.5
        :type minmag: float, optional
        :param event_coords: In case you wish to constrain events to certain
            origns. Given in the form (minlat, maxlat, minlon, maxlon),
            by default None.
        :type event_coords: Tuple, optional
        :param network: Limit the dowloand and preprocessing to a certain
            network or several networks (if type==list).
            Wildcards are allowed, by default None., defaults to None
        :type network: str or list, optional
        :param station: Limit the download and preprocessing to a certain
            station or several stations. Use only if network!=None.
            Wildcards are allowed, by default None.
        :type station: str or list, optional
        :param waveform_client: List of FDSN compatible servers to download
            waveforms from.
            See obspy documentation for obspy.Client for allowed acronyms.
            A list of servers by region can be found at
            `<https://www.fdsn.org/webservices/datacenters/>`_. None means
            that all known servers are requested, defaults to None.
        :type waveform_client: list, optional
        :param re_client: Only relevant, when debug=True. List of servers that
            will be used if data is missing and the script will attempt a
            redownload, usually it's easier to just run a request several
            times. Same logic as for waveform_client applies,
            defaults to ['IRIS']
        :type re_client: list, optional
        :param evtcat: In case you want to use an already existing event
            catalogue
            in evtloc. If None a new catalogue will be downloaded (with the
            parameters defined before), by default None, defaults to None
        :type evtcat: str, optional
        :param debug: If True, all loggers will go to DEBUG mode and all
            warnings
            will be shown. That will result in a lot of information being
            shown! Also joblib will fall back to using only few cores,
            by default False.
        :type debug: bool, optional
        :raises NameError: For invalid phases.
        """

        # Allocate variables in self
        self.debug = debug
        self.wavdownload = wavdownload
        tmp.re_client = re_client

        # Set velocity model
        self.model = TauPyModel('iasp91')

        self.phase = phase[:-1] + phase[-1].upper()
        self.pol = pol.lower()
        self.rot = rot.upper()
        self.deconmeth = deconmeth

        # Directories
        self.logdir = os.path.join(os.path.dirname(os.path.abspath(statloc)),
                                   'logs')
        os.makedirs(self.logdir, exist_ok=True)
        self.evtloc = evtloc
        self.statloc = statloc
        self.rawloc = os.path.join(rawloc, self.phase)
        self.preproloc = os.path.join(preproloc, self.phase)
        self.rfloc = os.path.join(rfloc, self.phase)

        # minimum magnitude
        self.minmag = minmag

        # Request time window
        self.starttime = starttime
        self.endtime = endtime

        # geographical constraints
        if event_coords:
            (self.eMINLAT, self.eMAXLAT, self.eMINLON,
             self.eMAXLON) = event_coords
        else:
            (self.eMINLAT, self.eMAXLAT, self.eMINLON,
             self.eMAXLON) = None, None, None, None

        # Set event depth and min/max epicentral distances
        # according to phase (see Wilson et. al., 2006)
        # and time window before (tz) and after (ta) first arrival
        self.ta = 120
        if self.phase == 'P':
            self.maxdepth = None
            self.min_epid = 28.1
            self.max_epid = 95.8
            self.tz = 30
        elif self.phase == 'S':
            self.maxdepth = 300
            self.min_epid = 55
            self.max_epid = 80
            self.tz = 120
        # (see Yuan et al. 2006)
        elif self.phase.upper() == 'SCS':
            self.maxdepth = 300
            self.min_epid = 50
            self.max_epid = 75
            self.tz = 120
        elif self.phase.upper() == 'SKS':
            # (see Zhang et. al. (2014))
            self.maxdepth = 300
            self.min_epid = 90
            self.max_epid = 120
            self.tz = 120
        else:
            raise NameError(
                'The phase', self.phase, """is not valid or not
                            implemented yet.""")

        # network and station filters
        self.network = network
        self.station = station

        # Server settings
        # 2021/02/16 Events only from IRIS as the USGS webserice tends to be
        # unstable and mixing different services will lead to a messed db
        self.webclient = Webclient('IRIS')

        self.waveform_client = waveform_client
        self.re_client = re_client

        # Download or process available data?
        if evtcat:
            self.evtcat = read_events(os.path.join(self.evtloc, evtcat))
        else:
            self.download_eventcat()

    def download_eventcat(self):
        event_cat_done = False

        while not event_cat_done:
            try:
                # Check length of request and split if longer than 20yrs.
                a = 20 * 365.25 * 24 * 3600  # 20 years in seconds
                if self.endtime - self.starttime > a:
                    # Request is too big, break it down ito several requests

                    starttimes = [self.starttime, self.starttime + a]
                    while self.endtime - starttimes[-1] > a:
                        starttimes.append(starttimes[-1] + a)
                    endtimes = []
                    endtimes.extend(starttimes[1:])
                    endtimes.append(self.endtime)

                    # Query
                    self.evtcat = Catalog()
                    for st, et in zip(starttimes, endtimes):
                        self.evtcat.extend(
                            self.webclient.get_events(
                                starttime=st,
                                endtime=et,
                                minlatitude=self.eMINLAT,
                                maxlatitude=self.eMAXLAT,
                                minlongitude=self.eMINLON,
                                maxlongitude=self.eMAXLON,
                                minmagnitude=self.minmag,
                                maxmagnitude=10,
                                maxdepth=self.maxdepth))
                    event_cat_done = True

                else:
                    self.evtcat = self.webclient.get_events(
                        starttime=self.starttime,
                        endtime=self.endtime,
                        minlatitude=self.eMINLAT,
                        maxlatitude=self.eMAXLAT,
                        minlongitude=self.eMINLON,
                        maxlongitude=self.eMAXLON,
                        minmagnitude=self.minmag,
                        maxmagnitude=10,
                        maxdepth=self.maxdepth)

                    event_cat_done = True

            except IncompleteRead:
                # Server interrupted connection, just try again
                msg = "Server interrupted connection, restarting download..."
                warn(msg, UserWarning)
                print(msg)
                continue

        os.makedirs(self.evtloc, exist_ok=True)
        # check if there is a better format for event catalog
        self.evtcat.write(os.path.join(
            self.evtloc,
            datetime.now().strftime("%Y%m%dT%H%M%S")),
                          format="QUAKEML")

    def download_waveforms(self, verbose: bool = False):
        """
        Start the download of waveforms and response files.

        Parameters
        ----------
        verbose : Bool, optional
            Set True if you wish to log the output of the obspy MassDownloader.
        """
        downloadwav(self.phase,
                    self.min_epid,
                    self.max_epid,
                    self.model,
                    self.evtcat,
                    self.tz,
                    self.ta,
                    self.statloc,
                    self.rawloc,
                    self.waveform_client,
                    network=self.network,
                    station=self.station,
                    logdir=self.logdir,
                    debug=self.debug,
                    verbose=verbose,
                    saveasdf=False)

    def preprocess(self, hc_filt: float or int or None = None):
        """
        Preprocess an existing database. With parameters defined in self.

        Parameters
        ----------
        hc_filt : float or int or None, optional
            Highcut frequency to filter with right before deconvolution.
            Recommended if time domain deconvolution is used. For spectral
            division, filtering can still be done after deconvolution (i.e.
            set in :func:`~pyglimer.ccp.ccp.CCPStack.compute_stack()`).
            Value for PRFs should usually be lower than 2 Hz and for SRFs lower
            than .4 Hz, by default None.
        """
        preprocess(self.phase,
                   self.rot,
                   self.pol,
                   0.05,
                   self.evtcat,
                   self.model,
                   'hann',
                   self.tz,
                   self.ta,
                   self.statloc,
                   self.rawloc,
                   self.preproloc,
                   self.rfloc,
                   self.deconmeth,
                   hc_filt,
                   netrestr=self.network,
                   statrestr=self.station,
                   logdir=self.logdir,
                   debug=self.debug)