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
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