def test_removing_channels(self):
     chans = {tr.id for template in self.tribe for tr in template.st}
     removed_chan = chans.pop()
     checked_tribe = check_tribe_quality(self.tribe, seed_ids=chans)
     for template in checked_tribe:
         template_ids = {tr.id for tr in template.st}
         self.assertNotIn(removed_chan, template_ids)
 def test_too_short_chans(self):
     tribe = self.tribe.copy()
     tribe[0].st[0].data = tribe[0].st[0].data[0:-10]
     checked_tribe = check_tribe_quality(tribe)
     for template in checked_tribe:
         for tr in template.st:
             self.assertEqual(tr.stats.npts, 150)
 def test_removing_short_templates(self):
     checked_tribe = check_tribe_quality(self.tribe, min_stations=5)
     self.assertLess(len(checked_tribe), len(self.tribe))
     for template in checked_tribe:
         stas = {tr.stats.station for tr in template.st}
         self.assertGreaterEqual(len(stas), 5)
 def test_good_tribe_quality(self):
     checked_tribe = check_tribe_quality(self.tribe)
     for template in checked_tribe:
         for tr in template.st:
             self.assertEqual(tr.stats.npts, 150)
Example #5
0
def run(working_dir: str, cores: int = 1, log_to_screen: bool = False):
    os.chdir(working_dir)
    Logger.debug("Reading config")
    config = read_config('rt_eqcorrscan_config.yml')
    config.setup_logging(
        screen=log_to_screen, file=True,
        filename="{0}/rt_eqcorrscan_{1}.log".format(
            working_dir,
            os.path.split(working_dir)[-1]))
    # Enforce a local-wave-bank for the spun-up real-time instance
    config.rt_match_filter.local_wave_bank = "streaming_wavebank"

    triggering_event = read_events('triggering_event.xml')[0]
    Logger.debug(f"Triggered by {triggering_event}")
    min_stations = config.rt_match_filter.get("min_stations", None)
    Logger.info("Reading the Tribe")
    tribe = Tribe().read("tribe.tgz")
    # Remove file to avoid re-reading it
    os.remove("tribe.tgz")

    Logger.info("Read in {0} templates".format(len(tribe)))
    if len(tribe) == 0:
        Logger.warning("No appropriate templates found")
        return
    Logger.info("Checking tribe quality: removing templates with "
                "fewer than {0} stations".format(min_stations))
    tribe = check_tribe_quality(
        tribe, min_stations=min_stations, **config.template)
    Logger.info("Tribe now contains {0} templates".format(len(tribe)))
    if len(tribe) == 0:
        return None, None

    client = config.rt_match_filter.get_client()
    rt_client = config.streaming.get_streaming_client()

    inventory = get_inventory(
        client, tribe, triggering_event=triggering_event,
        max_distance=config.rt_match_filter.max_distance,
        n_stations=config.rt_match_filter.n_stations)
    if len(inventory) == 0:
        Logger.critical(
            f"No inventory within {config.rt_match_filter.max_distance}"
            f"km of the trigger matching your templates, not running")
        return None, None
    detect_interval = config.rt_match_filter.get(
        "detect_interval", 60)
    plot = config.rt_match_filter.get("plot", False)
    real_time_tribe = RealTimeTribe(
        tribe=tribe, inventory=inventory, rt_client=rt_client,
        detect_interval=detect_interval, plot=plot,
        plot_options=config.plot,
        name=triggering_event.resource_id.id.split('/')[-1],
        wavebank=config.rt_match_filter.local_wave_bank)
    if real_time_tribe.expected_seed_ids is None:
        Logger.error("No matching channels in inventory and templates")
        return

    # Disable parallel processing for subprocess
    real_time_tribe._parallel_processing = False
    # Set the maximum correlation core-count
    if config.rt_match_filter.get("max_correlation_cores", None):
        cores = min(cores, config.rt_match_filter.max_correlation_cores)
    real_time_tribe.max_correlation_cores = cores

    Logger.info("Created real-time tribe with inventory:\n{0}".format(
        inventory))

    # TODO: How will this work? Currently notifiers are not implemented
    # real_time_tribe.notifier = None

    backfill_to = event_time(triggering_event) - 180
    backfill_client = config.rt_match_filter.get_waveform_client()

    if backfill_client and real_time_tribe.wavebank:
        # Download the required data and write it to disk.
        endtime = UTCDateTime.now()
        Logger.info(
            f"Backfilling between {backfill_to} and {endtime}")
        st = Stream()
        for network in inventory:
            for station in network:
                for channel in station:
                    Logger.info(
                        f"Downloading for {network.code}.{station.code}."
                        f"{channel.location_code}.{channel.code}")
                    try:
                        st += backfill_client.get_waveforms(
                            network=network.code, station=station.code,
                            location=channel.location_code,
                            channel=channel.code,
                            starttime=backfill_to,
                            endtime=endtime)
                    except Exception as e:
                        Logger.error(e)
                        continue
        st = st.merge()
        Logger.info(f"Downloaded {len(st)} for backfill")
        if len(st) == 0:
            Logger.warning("No backfill available, skipping")
        else:
            st = st.split()  # Cannot write masked data
            real_time_tribe.wavebank.put_waveforms(st)
        backfill_stations = {tr.stats.station for tr in st}
        backfill_templates = [
            t for t in real_time_tribe.templates
            if len({tr.stats.station for tr in t.st}.intersection(
                backfill_stations)) >= min_stations]
        Logger.info("Computing backfill detections")
        real_time_tribe.backfill(
            templates=backfill_templates,
            threshold=config.rt_match_filter.threshold,
            threshold_type=config.rt_match_filter.threshold_type,
            trig_int=config.rt_match_filter.trig_int,
            hypocentral_separation=config.rt_match_filter.hypocentral_separation,
            keep_detections=86400,
            detect_directory="{name}/detections",
            plot_detections=config.rt_match_filter.plot_detections)

    Logger.info("Starting real-time run")
    
    real_time_tribe.run(
        threshold=config.rt_match_filter.threshold,
        threshold_type=config.rt_match_filter.threshold_type,
        trig_int=config.rt_match_filter.trig_int,
        hypocentral_separation=config.rt_match_filter.hypocentral_separation,
        min_stations=min_stations,
        keep_detections=86400,
        detect_directory="{name}/detections",
        plot_detections=config.rt_match_filter.plot_detections,
        save_waveforms=config.rt_match_filter.save_waveforms,
        max_run_length=config.rt_match_filter.max_run_length,
        minimum_rate=config.rt_match_filter.minimum_rate,
        backfill_to=backfill_to)
Example #6
0
    def spin_up(
        self,
        triggering_event: Event,
        run: bool = True,
    ) -> Union[Party, tuple]:
        """
        Run the reactors response function.

        Parameters
        ----------
        triggering_event
            Event that triggered this run - needs to have at-least an origin.
        run
            Whether to run the real-time tribe immediately (True),
            or return it (False).
        """
        min_stations = self.listener_kwargs.get("min_stations", None)
        region = estimate_region(triggering_event)
        if region is None:
            return None, None
        region.update(self.template_lookup_kwargs)
        Logger.info("Getting templates within {0}".format(region))
        tribe = self.template_database.get_templates(**region)
        Logger.info("Read in {0} templates".format(len(tribe)))
        tribe.templates = [
            t for t in tribe if t.name not in self.running_template_ids
        ]
        if len(tribe) == 0:
            Logger.warning(
                "No appropriate templates for event: {0}".format(region))
            return None, None
        Logger.info("Checking tribe quality: removing templates with "
                    "fewer than {0} stations".format(min_stations))
        tribe = check_tribe_quality(tribe,
                                    min_stations=min_stations,
                                    **self.listener_kwargs["template_kwargs"])
        Logger.info("Tribe now contains {0} templates".format(len(tribe)))
        inventory = get_inventory(self.client,
                                  tribe,
                                  triggering_event=triggering_event,
                                  max_distance=self.max_station_distance,
                                  n_stations=self.n_stations)
        detect_interval = self.real_time_tribe_kwargs.get(
            "detect_interval", 60)
        plot = self.real_time_tribe_kwargs.get("plot", False)
        if plot and self._plotting is not None:
            Logger.warning(
                "Cannot plot for more than one real-time-tribe at once.")
            plot = False
        elif plot:
            self._plotting = triggering_event.resource_id
        real_time_tribe = RealTimeTribe(
            tribe=tribe,
            inventory=inventory,
            rt_client=self.rt_client.copy(),
            detect_interval=detect_interval,
            plot=plot,
            plot_options=self.plot_kwargs,
            name=triggering_event.resource_id.id.split('/')[-1])
        Logger.info(
            "Created real-time tribe with inventory:\n{0}".format(inventory))
        real_time_tribe.notifier = self.notifier

        real_time_tribe_kwargs = {
            "backfill_to": event_time(triggering_event),
            "backfill_client": self.listener.waveform_client,
            "cores": self.available_cores
        }
        real_time_tribe_kwargs.update(self.real_time_tribe_kwargs)
        self.running_tribes.update({
            triggering_event.resource_id.id: {
                "tribe": real_time_tribe,
                "region": region
            }
        })
        self.running_template_ids.update({t.name for t in real_time_tribe})
        if run:
            return real_time_tribe.run(**real_time_tribe_kwargs)
        else:
            return real_time_tribe, real_time_tribe_kwargs
Example #7
0
    def process_new_events(self, new_events: Catalog) -> None:
        """
        Process any new events in the system.

        Check if new events should be in one of the already running
        tribes and add them. Check all other events for possible triggers and
        spin-up a detector instance for triggers.

        Parameters
        ----------
        new_events
            Catalog of new-events to be assessed.
        """
        for triggering_event_id, tribe_region in self._running_regions.items():
            try:
                add_events = get_events(new_events, **tribe_region)
            except Exception:
                # This only occurs when there are no events in the region
                # and is fixed by PR #177 on Obsplus.
                add_events = Catalog()
            # Don't trigger on events now running in another tribe.
            new_events.events = [e for e in new_events if e not in add_events]
            # TODO: Implement region growth based on new events added.
            added_ids = {e.resource_id.id
                         for e in add_events
                         }.difference(self.running_template_ids)
            if added_ids:
                tribe = self.template_database.get_templates(eventid=added_ids)
                tribe = check_tribe_quality(
                    tribe,
                    min_stations=self.config.rt_match_filter.min_stations,
                    **self.config.template)
                if len(tribe) > 0:
                    Logger.info(
                        f"Adding {len(tribe)} events to {triggering_event_id}")
                    template_dir = os.path.join(
                        _get_triggered_working_dir(triggering_event_id),
                        "new_templates")
                    if not os.path.isdir(template_dir):
                        os.makedirs(template_dir)
                    for template in tribe:
                        template.write(
                            filename=os.path.join(template_dir, template.name))
                    Logger.info(f"Written new templates to {template_dir}")
                    self._running_templates[triggering_event_id].update(
                        added_ids)
        trigger_events = self.trigger_func(new_events)
        # Sanitize trigger-events - make sure that multiple events that would otherwise
        # run together do not all trigger - sort by magnitude
        for trigger_event in trigger_events:
            # Make sure they all have a magnitude
            if len(trigger_event.magnitudes
                   ) == 0 or trigger_event.magnitudes[0] is None:
                trigger_event.magnitudes = [Magnitude(mag=-999)]
        trigger_events.events.sort(
            key=lambda e: (e.preferred_magnitude() or e.magnitudes[0]).mag,
            reverse=True)
        for trigger_event in trigger_events:
            if trigger_event in self._triggered_events:
                continue
            if trigger_event.resource_id.id in self.running_template_ids:
                Logger.info(
                    f"Not spinning up {trigger_event}: it is already running")
                continue
            Logger.warning(
                "Listener triggered by event {0}".format(trigger_event))
            if len(self._running_regions) >= self.available_cores:
                Logger.error("No more available processors")
                continue
            self._triggered_events.append(trigger_event)
            self.spin_up(trigger_event)