Exemple #1
0
def create_ruptures(mfd,
                    dips,
                    sampling,
                    msr,
                    asprs,
                    float_strike,
                    float_dip,
                    r,
                    values,
                    oms,
                    tspan,
                    hdf5_filename,
                    uniform_fraction,
                    proj,
                    idl,
                    align=False,
                    inslab=False):
    """
    Create inslab ruptures using an MFD, a time span. The dictionary 'oms'
    contains lists of profiles for various values of dip. The ruptures are
    floated on each virtual fault created from a set of profiles.

    :param mfd:
        A magnitude frequency distribution
    :param dips:
        A set of dip values
    :param sampling:
        The distance in km used to
    :param msr:
        A magnitude scaling relationship instance
    :param asprs:
        A set of aspect ratios
    :param float_strike:
        Along strike floating parameter
    :param float_dip:
        Along dip floating parameter
    :param r:
        Spatial index
    :param values:
        Smothing results
    :param oms:
        A dictionary. Values of dip are used as keys while values of the
        dictionary are list of lists. Every list contains one or several
        :class:`openquake.hazardlib.geo.line.Line` instances each one
        corresponding to a 3D profile.
    :param tspan:
        Time span [in yr]
    :param hdf5_filename:
        Name of the hdf5 file where to store the ruptures
    :param uniform_fraction:
        Fraction of the overall rate for a given magnitude bin to be
        distributed uniformly to all the ruptures for the same mag bin.
    :param align:
        Profile alignment flag
    """
    #
    # hdf5 file
    fh5 = h5py.File(hdf5_filename, 'a')
    grp_inslab = fh5.create_group('inslab')
    #
    allrup = {}
    iscnt = 0
    for dip in dips:
        for mi, lines in enumerate(oms[dip]):
            #
            # filter out small surfaces i.e. surfaces defined by less than
            # three profiles
            if len(lines) < 3:
                continue
            #
            # checking initial profiles
            for l in lines:
                ps = np.array([[p.longitude, p.latitude, p.depth]
                               for p in l.points])
                assert not np.any(np.isnan(ps))
            #
            # create in-slab virtual fault - `lines` is the list of profiles
            # to be used for the construction of the virtual fault surface
            smsh = create_from_profiles(lines, sampling, sampling, idl, align)
            #
            # Create mesh
            omsh = Mesh(smsh[:, :, 0], smsh[:, :, 1], smsh[:, :, 2])
            #
            # Store data in the hdf5 file
            grp_inslab.create_dataset('{:09d}'.format(iscnt), data=smsh)
            #
            # get centroids for a given virtual fault surface
            ccc = get_centroids(smsh[:, :, 0], smsh[:, :, 1], smsh[:, :, 2])
            #
            # Get weights - this assigns to each cell centroid the weight of
            # the closest node in the values array
            weights = get_weights(ccc, r, values, proj)
            #
            # loop over magnitudes
            for mag, _ in mfd.get_annual_occurrence_rates():
                #
                # TODO this is assigns arbitrarly a rake of 90 degrees. It
                # should be a configuration parameter
                area = msr.get_median_area(mag=mag, rake=90)
                rups = []
                for aspr in asprs:
                    #
                    # IMPORTANT: the sampling here must be consistent with
                    # what we use for the construction of the mesh
                    lng, wdt = get_discrete_dimensions(area, sampling, aspr)
                    #
                    # If one of the dimensions is equal to 0 it means that
                    # this aspect ratio cannot be represented with the value of
                    # sampling
                    if (lng is None or wdt is None or lng < 1e-10
                            or wdt < 1e-10):
                        msg = 'Ruptures for magnitude {:.2f} and ar {:.2f}'
                        msg = msg.format(mag, aspr)
                        msg = '{:s} will not be defined'.format(msg)
                        logging.warning(msg)
                        continue
                    #
                    # rupture lenght and rupture width as multiples of the
                    # mesh sampling distance
                    rup_len = int(lng / sampling) + 1
                    rup_wid = int(wdt / sampling) + 1
                    #
                    # skipping small ruptures
                    if rup_len < 2 or rup_wid < 2:
                        msg = 'Found an incompatible discrete rupture size'
                        logging.warning(msg)
                        continue
                    #
                    # get_ruptures
                    counter = 0
                    for rup, rl, cl in get_ruptures(omsh,
                                                    rup_len,
                                                    rup_wid,
                                                    f_strike=float_strike,
                                                    f_dip=float_dip):
                        #
                        # getting weights from the smoothing
                        w = weights[cl:rup_len - 1, rl:rup_wid - 1]
                        i = np.isfinite(w)
                        #
                        # fix the longitudes outside the standard [-180, 180]
                        # range
                        ij = np.isfinite(rup[0])
                        iw = rup[0] > 180.
                        ik = np.logical_and(ij, iw)
                        rup[0][ik] -= 360
                        """
                        iw = np.nonzero(rup[0][ij] > 180.)
                        if len(iw):
                            print(type(rup), rup[0])
                            print(ij[iw])
                            rup[0][ij[iw]] -= 360.
                        """

                        #if np.any(rup[0][j] > 180):
                        #    rup[0][rup[0] > 180.] = rup[0][rup[0] > 180.] - 360.
                        assert np.all(rup[0][ij] <= 180)
                        assert np.all(rup[0][ij] >= -180)

                        rx = rup[0][ij].flatten()
                        ry = rup[1][ij].flatten()
                        rz = rup[2][ij].flatten()

                        #
                        # normalize the weight using the aspect ratio weight
                        wsum = sum(w[i]) / asprs[aspr]
                        #
                        # create the gridded surface. We need at least four
                        # vertexes
                        if len(rx) > 3:
                            #if rup[0].size > 3:
                            srfc = GriddedSurface(
                                Mesh.from_coords(zip(rx, ry, rz), sort=False))
                            #srfc = GriddedSurface(Mesh.from_coords(zip(rup[0],
                            #                                           rup[1],
                            #                                           rup[2]),
                            #                                       sort=False))
                            #
                            # update the list with the ruptures - the last
                            # element in the list is the container for the
                            # probability of occurrence. For the time being
                            # this is not defined
                            rups.append([srfc, wsum, dip, aspr, []])
                            counter += 1
                #
                # update the list of ruptures
                lab = '{:.2f}'.format(mag)
                if lab in allrup:
                    allrup[lab] += rups
                else:
                    allrup[lab] = rups
            #
            # update counter
            iscnt += 1
    #
    # closing the hdf5 file
    fh5.close()
    #
    # logging info
    for lab in sorted(allrup.keys()):
        tmps = 'Number of ruptures for m={:s}: {:d}'
        logging.info(tmps.format(lab, len(allrup[lab])))
    #
    # Compute the normalizing factor for every rupture. This is used only in
    # the case when smoothing is used a reference for distributing occurrence
    twei = {}
    for mag, occr in mfd.get_annual_occurrence_rates():
        smm = 0.
        lab = '{:.2f}'.format(mag)
        for _, wei, _, _, _ in allrup[lab]:
            if np.isfinite(wei):
                smm += wei
        twei[lab] = smm
        tmps = 'Total weight {:s}: {:f}'
        logging.info(tmps.format(lab, twei[lab]))
    #
    # generate and store the final set of ruptures
    fh5 = h5py.File(hdf5_filename, 'a')
    grp_rup = fh5.create_group('ruptures')
    #
    for mag, occr in mfd.get_annual_occurrence_rates():
        #
        # set label
        lab = '{:.2f}'.format(mag)
        #
        # warning
        if twei[lab] < 1e-50 and uniform_fraction < 0.99:
            tmps = 'Weight for magnitude {:s} equal to 0'
            tmps = tmps.format(lab)
            logging.warning(tmps)
        #
        #
        rups = []
        grp = grp_rup.create_group(lab)
        cnt = 0
        numrup = len(allrup[lab])
        for srfc, wei, _, _, _ in allrup[lab]:
            #
            # Adjust the weight. Every rupture will have a weight that is
            # a combination between a flat rate and a variable rate
            if twei[lab] > 1e-10:
                wei = wei / twei[lab]
                ocr = (wei * occr * (1. - uniform_fraction) +
                       occr / numrup * uniform_fraction)
            else:
                ocr = occr / numrup * uniform_fraction
            #
            # compute the probabilities
            p0 = np.exp(-ocr * tspan)
            p1 = 1. - p0
            #
            #
            rups.append([srfc, wei, dip, aspr, [p0, p1]])
            #
            #
            a = np.zeros(1,
                         dtype=[
                             ('lons', 'f4', srfc.mesh.lons.shape),
                             ('lats', 'f4', srfc.mesh.lons.shape),
                             ('deps', 'f4', srfc.mesh.lons.shape),
                             ('w', 'float32'),
                             ('dip', 'f4'),
                             ('aspr', 'f4'),
                             ('prbs', 'float32', (2)),
                         ])
            a['lons'] = srfc.mesh.lons
            a['lats'] = srfc.mesh.lats
            a['deps'] = srfc.mesh.depths
            a['w'] = wei
            a['dip'] = dip
            a['aspr'] = aspr
            a['prbs'] = np.array([p0, p1], dtype='float32')
            grp.create_dataset('{:08d}'.format(cnt), data=a)
            cnt += 1
        allrup[lab] = rups
    fh5.close()

    return allrup
Exemple #2
0
def create_ruptures(mfd,
                    dips,
                    sampling,
                    msr,
                    asprs,
                    float_strike,
                    float_dip,
                    r,
                    values,
                    oms,
                    tspan,
                    hdf5_filename,
                    uniform_fraction,
                    proj,
                    idl,
                    align=False,
                    inslab=False):
    """
    Create inslab ruptures using an MFD, a time span. The dictionary 'oms'
    contains lists of profiles for various values of dip. The ruptures are
    floated on each virtual fault created from a set of profiles.

    :param mfd:
        A magnitude frequency distribution
    :param dips:
        A set of dip values used to create the virtual faults withni the slab.
    :param sampling:
        The distance in km used to sample the profiles
    :param msr:
        A magnitude scaling relationship instance
    :param asprs:
        A dictionary of aspect ratios (key: aspect ratio, value: weight)
    :param float_strike:
        Along strike rupture floating parameter
    :param float_dip:
        Along dip rupture floating parameter
    :param r:
        Spatial index for the nodes of the grid over which we smoothed
        seismicity
    :param values:
        Smothing results
    :param oms:
        A dictionary. Values of dip are used as keys while values of the
        dictionary are list of lists. Every list contains one or several
        :class:`openquake.hazardlib.geo.line.Line` instances each one
        corresponding to a 3D profile.
    :param tspan:
        Time span [in yr]
    :param hdf5_filename:
        Name of the hdf5 file where to store the ruptures
    :param uniform_fraction:
        Fraction of the overall rate for a given magnitude bin to be
        distributed uniformly to all the ruptures for the same mag bin.
    :param align:
        Profile alignment flag
    """

    # Create the output hdf5 file
    fh5 = h5py.File(hdf5_filename, 'a')
    grp_inslab = fh5.create_group('inslab')

    # Loop over dip angles, top traces on the top the slab surface and
    # magnitudes. The traces are used to create the virtual faults and
    # float the ruptures.
    allrup = {}
    iscnt = 0
    trup = 0
    for dip in dips:
        for mi, lines in enumerate(oms[dip]):

            print('\nVirtual fault {:d} dip {:.2f}\n'.format(mi, dip))

            # Filter out small surfaces i.e. surfaces defined by less than
            # three profiles
            if len(lines) < 3:
                continue

            # Checking initial profiles
            for lne in lines:
                ps = np.array([[p.longitude, p.latitude, p.depth]
                               for p in lne.points])
                assert not np.any(np.isnan(ps))

            # Create in-slab virtual fault - `lines` is the list of profiles
            # to be used for the construction of the virtual fault surface
            smsh = create_from_profiles(lines, sampling, sampling, idl, align)

            # Create mesh
            omsh = Mesh(smsh[:, :, 0], smsh[:, :, 1], smsh[:, :, 2])

            # Store data in the hdf5 file
            grp_inslab.create_dataset('{:09d}'.format(iscnt), data=smsh)

            # Get centroids for a given virtual fault surface
            ccc = get_centroids(smsh[:, :, 0], smsh[:, :, 1], smsh[:, :, 2])

            # Get weights - this assigns to each centroid the weight of
            # the closest node in the values array
            weights = get_weights(ccc, r, values, proj)

            # Loop over magnitudes
            for mag, _ in mfd.get_annual_occurrence_rates():

                # TODO this is assigns arbitrarly a rake of 90 degrees. It
                # should be a configuration parameter
                area = msr.get_median_area(mag=mag, rake=90)
                rups = []
                for aspr in asprs:

                    # IMPORTANT: the sampling here must be consistent with
                    # what we use for the construction of the mesh
                    lng, wdt = get_discrete_dimensions(area, sampling, aspr)

                    # If one of the dimensions is equal to 0 it means that
                    # this aspect ratio cannot be represented with the value of
                    # sampling
                    if (lng is None or wdt is None or lng < 1e-10
                            or wdt < 1e-10):
                        msg = 'Ruptures for magnitude {:.2f} and ar {:.2f}'
                        msg = msg.format(mag, aspr)
                        msg = ' {:s} will not be defined'.format(msg)
                        logging.warning(msg)
                        continue

                    # Rupture lenght and rupture width as multiples of the
                    # mesh sampling distance
                    rup_len = int(lng / sampling) + 1
                    rup_wid = int(wdt / sampling) + 1

                    # Skip small ruptures
                    if rup_len < 2 or rup_wid < 2:
                        msg = 'Found a small rupture size'
                        logging.warning(msg)
                        continue

                    # Get Ruptures
                    counter = 0
                    for rup, rl, cl in get_ruptures(omsh,
                                                    rup_len,
                                                    rup_wid,
                                                    f_strike=float_strike,
                                                    f_dip=float_dip):

                        # Get weights
                        wsum = asprs[aspr]
                        wsum_smoo = np.nan
                        if uniform_fraction < 0.99:
                            w = weights[rl:rl + rup_wid - 1,
                                        cl:cl + rup_len - 1]
                            i = np.isfinite(w)
                            tmpw = sum(w[i])
                            wsum_smoo = tmpw * asprs[aspr]

                        # Fix the longitudes outside the standard [-180, 180]
                        # range
                        ij = np.isfinite(rup[0])
                        iw = rup[0] > 180.
                        ik = np.logical_and(ij, iw)
                        rup[0][ik] -= 360

                        # Get centroid
                        idx_r = np.floor(rup[0].shape[0] / 2).astype('i4')
                        idx_c = np.floor(rup[0].shape[1] / 2).astype('i4')
                        hypo = [
                            rup[0][idx_r, idx_c], rup[1][idx_r, idx_c],
                            rup[2][idx_r, idx_c]
                        ]

                        # Checking
                        assert np.all(rup[0][ij] <= 180)
                        assert np.all(rup[0][ij] >= -180)

                        # Get coordinates of the rupture surface
                        rx = rup[0][ij].flatten()
                        ry = rup[1][ij].flatten()
                        rz = rup[2][ij].flatten()

                        # Create the gridded surface. We need at least four
                        # vertexes
                        if len(rx) > 3:
                            srfc = GriddedSurface(
                                Mesh.from_coords(zip(rx, ry, rz), sort=False))
                            # Update the list with the ruptures - the last
                            # element in the list is the container for the
                            # probability of occurrence. For the time being
                            # this is not defined
                            rups.append(
                                [srfc, wsum, wsum_smoo, dip, aspr, [], hypo])
                            counter += 1
                            trup += 1

                # Update the list of ruptures
                lab = '{:.2f}'.format(mag)
                if lab in allrup:
                    allrup[lab] += rups
                else:
                    allrup[lab] = rups

            # Update counter
            iscnt += 1

    # Closing the hdf5 file
    fh5.close()

    # Logging info
    for lab in sorted(allrup.keys()):
        tmps = 'Number of ruptures for m={:s}: {:d}'
        logging.info(tmps.format(lab, len(allrup[lab])))

    # Compute the normalizing factor
    twei = {}
    tweis = {}
    for mag, occr in mfd.get_annual_occurrence_rates():
        smm = 0.
        smms = 0.
        lab = '{:.2f}'.format(mag)
        for _, wei, weis, _, _, _, _ in allrup[lab]:
            if np.isfinite(wei):
                smm += wei
            if np.isfinite(weis):
                smms += weis
        twei[lab] = smm
        tweis[lab] = smms
        tmps = 'Total weight {:s}: {:f}'
        logging.info(tmps.format(lab, twei[lab]))

    # Generate and store the final set of ruptures
    fh5 = h5py.File(hdf5_filename, 'a')
    grp_rup = fh5.create_group('ruptures')

    # Assign probability of occurrence
    for mag, occr in mfd.get_annual_occurrence_rates():

        # Create the label
        lab = '{:.2f}'.format(mag)

        # Check if weight is larger than 0
        if twei[lab] < 1e-50 and uniform_fraction < 0.99:
            tmps = 'Weight for magnitude {:s} equal to 0'
            tmps = tmps.format(lab)
            logging.warning(tmps)

        rups = []
        grp = grp_rup.create_group(lab)

        # Loop over the ruptures and compute the annual pocc
        cnt = 0
        chk = 0
        chks = 0
        for srfc, wei, weis, _, _, _, hypo in allrup[lab]:

            # Adjust the weight. Every rupture will have a weight that is
            # a combination between a flat rate and a spatially variable rate
            wei = wei / twei[lab]
            ocr = (occr * uniform_fraction) * wei
            chk += wei
            if uniform_fraction < 0.99:
                weis = weis / tweis[lab]
                ocr += (occr * (1. - uniform_fraction)) * weis
                chks += weis

            # Compute the probabilities
            p0 = np.exp(-ocr * tspan)
            p1 = 1. - p0

            # Append ruptures
            rups.append([srfc, [wei, weis], dip, aspr, [p0, p1]])

            # Preparing the data structure for storing information
            a = np.zeros(1,
                         dtype=[
                             ('lons', 'f4', srfc.mesh.lons.shape),
                             ('lats', 'f4', srfc.mesh.lons.shape),
                             ('deps', 'f4', srfc.mesh.lons.shape),
                             ('w', 'float32', (2)),
                             ('dip', 'f4'),
                             ('aspr', 'f4'),
                             ('prbs', 'float32', (2)),
                             ('hypo', 'float32', (3)),
                         ])

            a['lons'] = srfc.mesh.lons
            a['lats'] = srfc.mesh.lats
            a['deps'] = srfc.mesh.depths
            a['w'] = [wei, weis]
            a['dip'] = dip
            a['aspr'] = aspr
            a['prbs'] = np.array([p0, p1], dtype='float32')
            a['hypo'] = hypo
            grp.create_dataset('{:08d}'.format(cnt), data=a)
            cnt += 1

        allrup[lab] = rups

        if len(rups):
            if uniform_fraction < 0.99:
                fmt = 'Sum of weights for smoothing: '
                fmt = '{:.5f}. Should be close to 1'
                msg = fmt.format(chks)
                assert (1.0 - chks) < 1e-5, msg

            if uniform_fraction > 0.01:
                fmt = 'Sum of weights for uniform: '
                fmt = '{:.5f}. Should be close to 1'
                msg = fmt.format(chk)
                assert (1.0 - chk) < 1e-5, msg

    fh5.close()

    return allrup