Beispiel #1
0
    def initialize_realizations(self, rlz_callbacks=None):
        """
        Create records for the `hzrdr.lt_realization` and
        `htemp.source_progress` records.

        This function works either in random sampling mode (when lt_realization
        models get the random seed value) or in enumeration mode (when weight
        values are populated). In both cases we record the logic tree paths
        for both trees in the `lt_realization` record, as well as ordinal
        number of the realization (zero-based).

        Then we create `htemp.source_progress` records for each source
        in the source model chosen for each realization,
        see :meth:`initialize_source_progress`.

        :param rlz_callbacks:
            Optionally, you can specify a list of callbacks for each
            realization.  In the case of the classical hazard calculator, for
            example, we would include a callback function to create initial
            records for temporary hazard curve result data.

            Callbacks should accept a single argument:
            A :class:`~openquake.db.models.LtRealization` object.
        """
        logs.log_progress("initializing realizations", 2)
        if self.job.hazard_calculation.number_of_logic_tree_samples > 0:
            # random sampling of paths
            self._initialize_realizations_montecarlo(
                rlz_callbacks=rlz_callbacks)
        else:
            # full paths enumeration
            self._initialize_realizations_enumeration(
                rlz_callbacks=rlz_callbacks)
Beispiel #2
0
    def execute(self):
        """
        Calculation work is parallelized over sources, which means that each
        task will compute hazard for all sites but only with a subset of the
        seismic sources defined in the input model.

        The general workflow is as follows:

        1. Fill the queue with an initial set of tasks. The number of initial
        tasks is configurable using the `concurrent_tasks` parameter in the
        `[hazard]` section of the OpenQuake config file.

        2. Wait for tasks to signal completion (via AMQP message) and enqueue a
        new task each time another completes. Once all of the job work is
        enqueued, we just wait until all of the tasks conclude.
        """
        block_size = int(config.get('hazard', 'block_size'))
        concurrent_tasks = int(config.get('hazard', 'concurrent_tasks'))

        self.progress = dict(total=0, computed=0)
        # The following two counters are in a dict so that we can use them in
        # the closures below.
        # When `self.progress['compute']` becomes equal to
        # `self.progress['total']`, # `execute` can conclude.

        task_gen = self.task_arg_gen(block_size)

        exchange, conn_args = exchange_and_conn_args()

        routing_key = ROUTING_KEY_FMT % dict(job_id=self.job.id)
        task_signal_queue = kombu.Queue(
            'htasks.job.%s' % self.job.id, exchange=exchange,
            routing_key=routing_key, durable=False, auto_delete=True)

        with kombu.BrokerConnection(**conn_args) as conn:
            task_signal_queue(conn.channel()).declare()
            with conn.Consumer(
                task_signal_queue,
                callbacks=[self.get_task_complete_callback(task_gen)]):

                # First: Queue up the initial tasks.
                for _ in xrange(concurrent_tasks):
                    try:
                        self.core_calc_task.apply_async(task_gen.next())
                    except StopIteration:
                        # If we get a `StopIteration` here, that means we have
                        # a number of tasks < concurrent_tasks.
                        # This basically just means that we could be
                        # under-utilizing worker node resources.
                        break

                while (self.progress['computed'] < self.progress['total']):
                    # This blocks until a message is received.
                    # Once we receive a completion signal, enqueue the next
                    # piece of work (if there's anything left to be done).
                    # (The `task_complete_callback` will handle additional
                    # queuing.)
                    conn.drain_events()
        logs.log_progress("hazard calculation 100% complete", 2)
Beispiel #3
0
def _switch_to_job_phase(job, status):
    """Switch to a particular phase of execution.

    This involves setting the job's status as well as creating a
    `job_phase_stats` record.

    :param job:
        An :class:`~openquake.db.models.OqJob` instance.
    :param str status: one of the following: pre_executing, executing,
        post_executing, post_processing, export, clean_up, complete
    """
    job.status = status
    job.save()
    models.JobPhaseStats.objects.create(oq_job=job, job_status=status)
    logs.log_progress("%s" % status, 1)
Beispiel #4
0
    def initialize_sources(self):
        """
        Parse and validation logic trees (source and gsim). Then get all
        sources referenced in the the source model logic tree, create
        :class:`~openquake.db.models.Input` records for all of them, parse
        then, and save the parsed sources to the `parsed_source` table
        (see :class:`openquake.db.models.ParsedSource`).
        """
        logs.log_progress("initializing sources", 2)
        hc = self.job.hazard_calculation

        [smlt] = models.inputs4hcalc(hc.id, input_type='lt_source')
        [gsimlt] = models.inputs4hcalc(hc.id, input_type='lt_gsim')
        source_paths = logictree.read_logic_trees(
            hc.base_path, smlt.path, gsimlt.path)

        src_inputs = []
        for src_path in source_paths:
            full_path = os.path.join(hc.base_path, src_path)

            # Get or reuse the 'source' Input:
            inp = engine2.get_input(
                full_path, 'source', hc.owner, hc.force_inputs)
            src_inputs.append(inp)

            # Associate the source input to the calculation:
            models.Input2hcalc.objects.get_or_create(
                input=inp, hazard_calculation=hc)

            # Associate the source input to the source model logic tree input:
            models.Src2ltsrc.objects.get_or_create(
                hzrd_src=inp, lt_src=smlt, filename=src_path)

        # Now parse the source models and store `pared_source` records:
        for src_inp in src_inputs:
            src_content = StringIO.StringIO(src_inp.model_content.raw_content)
            sm_parser = nrml_parsers.SourceModelParser(src_content)
            src_db_writer = source.SourceDBWriter(
                src_inp, sm_parser.parse(), hc.rupture_mesh_spacing,
                hc.width_of_mfd_bin, hc.area_source_discretization)
            src_db_writer.serialize()
Beispiel #5
0
    def initialize_site_model(self):
        """
        If a site model is specified in the calculation configuration. parse
        it and load it into the `hzrdi.site_model` table. This includes a
        validation step to ensure that the area covered by the site model
        completely envelops the calculation geometry. (If this requirement is
        not satisfied, an exception will be raised. See
        :func:`openquake.calculators.hazard.general.validate_site_model`.)

        Then, take all of the points/locations of interest defined by the
        calculation geometry. For each point, do distance queries on the site
        model and get the site parameters which are closest to the point of
        interest. This aggregation of points to the closest site parameters
        is what we store in `htemp.site_data`. (Computing this once prior to
        starting the calculation is optimal, since each task will need to
        consider all sites.)
        """
        logs.log_progress("initializing site model", 2)
        hc_id = self.job.hazard_calculation.id

        site_model_inp = get_site_model(hc_id)
        if site_model_inp is not None:
            # Explicit cast to `str` here because the XML parser doesn't like
            # unicode. (More specifically, lxml doesn't like unicode.)
            site_model_content = str(site_model_inp.model_content.raw_content)

            # Store `site_model` records:
            store_site_model(
                site_model_inp, StringIO.StringIO(site_model_content))

            mesh = self.job.hazard_calculation.points_to_compute()

            # Get the site model records we stored:
            site_model_data = models.SiteModel.objects.filter(
                input=site_model_inp)

            validate_site_model(site_model_data, mesh)

            store_site_data(hc_id, site_model_inp, mesh)
Beispiel #6
0
def _switch_to_job_phase(job_ctxt, ctype, status):
    """Switch to a particular phase of execution.

    This involves creating a `job_phase_stats` record and logging the new
    status.

    :param job_ctxt:
        An :class:`~openquake.engine.JobContext` instance.
    :param str ctype: calculation type (hazard|risk)
    :param str status: one of the following: pre_executing, executing,
        post_executing, post_processing, export, clean_up, complete
    """
    job = OqJob.objects.get(id=job_ctxt.job_id)
    JobPhaseStats.objects.create(oq_job=job, ctype=ctype, job_status=status)
    logs.log_progress("%s (%s)" % (status, ctype), 1)
    if status == "executing":
        # Record the compute nodes that were available at the beginning of the
        # execute phase so we can detect failed nodes later.
        failed_nodes = monitor.count_failed_nodes(job)
        if failed_nodes == -1:
            logs.LOG.critical("No live compute nodes, aborting calculation")
            sys.exit(1)
Beispiel #7
0
def _switch_to_job_phase(job_ctxt, ctype, status):
    """Switch to a particular phase of execution.

    This involves creating a `job_phase_stats` record and logging the new
    status.

    :param job_ctxt:
        An :class:`~openquake.engine.JobContext` instance.
    :param str ctype: calculation type (hazard|risk)
    :param str status: one of the following: pre_executing, executing,
        post_executing, post_processing, export, clean_up, complete
    """
    job = OqJob.objects.get(id=job_ctxt.job_id)
    JobPhaseStats.objects.create(oq_job=job, ctype=ctype, job_status=status)
    logs.log_progress("%s (%s)" % (status, ctype), 1)
    if status == "executing":
        # Record the compute nodes that were available at the beginning of the
        # execute phase so we can detect failed nodes later.
        failed_nodes = monitor.count_failed_nodes(job)
        if failed_nodes == -1:
            logs.LOG.critical("No live compute nodes, aborting calculation")
            sys.exit(1)
Beispiel #8
0
def curves2nrml(target_dir, job):
    """Write hazard curves to NRML files.

    :param str target_dir: where should the output files go?
    :param int job_id: the database key of the job at hand.
    """
    LOG.debug("> curves2nrml")
    hc_outputs = models.Output.objects.filter(oq_job=job,
                                              output_type="hazard_curve")

    for hc_output in hc_outputs:
        export_hazard_curves(hc_output, target_dir)

    hco_count = len(hc_outputs)
    if hco_count > 1:
        logs.log_progress(
            "%s hazard curves exported to %s" % (hco_count, target_dir), 2)
    elif hco_count == 1:
        logs.log_progress("One hazard curve exported to %s" % target_dir, 2)
    else:
        logs.log_progress("No hazard curves found for export", 2)

    LOG.debug("< curves2nrml")
Beispiel #9
0
    def execute(self):
        """
        Calculation work is parallelized over sources, which means that each
        task will compute hazard for all sites but only with a subset of the
        seismic sources defined in the input model.

        The general workflow is as follows:

        1. Fill the queue with an initial set of tasks. The number of initial
        tasks is configurable using the `concurrent_tasks` parameter in the
        `[hazard]` section of the OpenQuake config file.

        2. Wait for tasks to signal completion (via AMQP message) and enqueue a
        new task each time another completes. Once all of the job work is
        enqueued, we just wait until all of the tasks conclude.
        """
        job = self.job
        hc = job.hazard_calculation
        sources_per_task = int(config.get('hazard', 'block_size'))
        concurrent_tasks = int(config.get('hazard', 'concurrent_tasks'))

        progress = dict(total=0, computed=0)
        # The following two counters are in a dict so that we can use them in
        # the closures below.
        # When `progress['compute']` becomes equal to `progress['total']`,
        # `execute` can conclude.

        task_gen = self.task_arg_gen(hc, job, sources_per_task, progress)

        def task_complete_callback(body, message):
            """
            :param dict body:
                ``body`` is the message sent by the task. The dict should
                contain 2 keys: `job_id` and `num_sources` (to indicate the
                number of sources computed).

                Both values are `int`.
            :param message:
                A :class:`kombu.transport.pyamqplib.Message`, which contains
                metadata about the message (including content type, channel,
                etc.). See kombu docs for more details.
            """
            job_id = body['job_id']
            num_sources = body['num_sources']

            assert job_id == job.id
            progress['computed'] += num_sources

            logs.log_percent_complete(job_id, "hazard")

            # Once we receive a completion signal, enqueue the next
            # piece of work (if there's anything left to be done).
            try:
                self.core_calc_task.apply_async(task_gen.next())
            except StopIteration:
                # There are no more tasks to dispatch; now we just need
                # to wait until all tasks signal completion.
                pass

            message.ack()

        exchange, conn_args = exchange_and_conn_args()

        routing_key = ROUTING_KEY_FMT % dict(job_id=job.id)
        task_signal_queue = kombu.Queue(
            'htasks.job.%s' % job.id, exchange=exchange,
            routing_key=routing_key, durable=False, auto_delete=True)

        with kombu.BrokerConnection(**conn_args) as conn:
            task_signal_queue(conn.channel()).declare()
            with conn.Consumer(task_signal_queue,
                               callbacks=[task_complete_callback]):
                # First: Queue up the initial tasks.
                for _ in xrange(concurrent_tasks):
                    try:
                        self.core_calc_task.apply_async(task_gen.next())
                    except StopIteration:
                        # If we get a `StopIteration` here, that means we have
                        # a number of tasks < concurrent_tasks.
                        # This basically just means that we could be
                        # under-utilizing worker node resources.
                        break

                while (progress['computed'] < progress['total']):
                    # This blocks until a message is received.
                    # Once we receive a completion signal, enqueue the next
                    # piece of work (if there's anything left to be done).
                    # (The `task_complete_callback` will handle additional
                    # queuing.)
                    conn.drain_events()
        logs.log_progress("hazard calculation 100% complete", 2)