def testcase01(self): config = {'time_distance_window': 'GardnerKnopoffWindow', 'fs_time_prop': 0.9} decluster(catalogue_hmtk_fname=self.catalogue, declustering_meth='GardnerKnopoffType1', declustering_params=config, output_path=self.tmp, labels=['a', 'b'], tr_fname=self.classification, subcatalogues=True, fmat='pkl') # # Read first mainshock catalogue a_fname = os.path.join(self.tmp, 'catalogue_dec__a.pkl') self.assertTrue(os.path.exists(a_fname)) cat = _load_catalogue(a_fname) self.assertTrue(len(cat.data['magnitude'] == 1)) self.assertAlmostEqual(cat.data['magnitude'][0], 6.0) # # Read second mainshock catalogue b_fname = os.path.join(self.tmp, 'catalogue_dec__b.pkl') self.assertTrue(os.path.exists(b_fname)) cat = _load_catalogue(b_fname) self.assertTrue(len(cat.data['magnitude'] == 1)) self.assertAlmostEqual(cat.data['magnitude'][0], 6.1) # # Check that the third mainshock catalogue does not exist c_fname = os.path.join(self.tmp, 'catalogue_dec__c.pkl') self.assertFalse(os.path.exists(c_fname))
def compute_mmax(fname_input_pattern: str, fname_config: str, label: str): """ This function assignes an mmax value to each source with a catalogue file as selected by the provided `fname_input_pattern`. """ if isinstance(fname_input_pattern, str): fname_list = glob(fname_input_pattern) else: fname_list = fname_input_pattern # Parsing config model = toml.load(fname_config) # Processing files for fname in sorted(fname_list): src_id = _get_src_id(fname) # Processing catalogue tcat = _load_catalogue(fname) if tcat is None or len(tcat.data['magnitude']) < 2: continue tmp = "{:.5e}".format(np.max(tcat.data['magnitude'])) model['sources'][src_id]['mmax_{:s}'.format(label)] = float(tmp) # Saving results into the config file with open(fname_config, 'w') as fou: fou.write(toml.dumps(model)) print('Updated {:s}'.format(fname_config))
def catalogue_declustering(fname: str, output_folder: str, *, subcatalogues: bool = False): """ Decluster a catalogue """ create_folder(output_folder) create_folder('./tmp') # Create a fake file with the classification. We use a fake classification # since earthquakes in this analysis are just from stable crust tr_fname = './tmp/fake.hdf5' cat = _load_catalogue(fname) label = np.ones_like(np.array(cat['magnitude'])) f = h5py.File(tr_fname, 'w') _ = f.create_dataset("undef", data=label) f.close() labels = ['undef'] # Declustering with the classical GK algorithm declustering_meth = 'GardnerKnopoffType1' declustering_params = { 'time_distance_window': 'GardnerKnopoffWindow', 'fs_time_prop': 0.9 } out = decluster(fname, declustering_meth, declustering_params, output_folder, labels=labels, tr_fname=tr_fname, subcatalogues=subcatalogues, olab='_gk', save_af=True, fix_defaults=True) declustering_meth = 'GardnerKnopoffType1' declustering_params = { 'time_distance_window': 'UhrhammerWindow', 'fs_time_prop': 0.9 } out = decluster(fname, declustering_meth, declustering_params, output_folder, labels=labels, tr_fname=tr_fname, subcatalogues=subcatalogues, olab='_uh', save_af=True, fix_defaults=True) declustering_meth = 'GardnerKnopoffType1' declustering_params = { 'time_distance_window': 'GruenthalWindow', 'fs_time_prop': 0.9 } _ = decluster(fname, declustering_meth, declustering_params, output_folder, labels=labels, tr_fname=tr_fname, subcatalogues=subcatalogues, olab='_gr', save_af=True, fix_defaults=True)
def main(fname: str, *, example_flag: bool = False): """ Compares SES against a catalogue given a .toml configuration file """ # Print an example of configuration file if example_flag: print_example() exit() # Load the .toml file containing the information required config_main = toml.load(fname) path = os.path.dirname(fname) print('Root path: {:s}'.format(path)) # Read information in the config file fname_catalogues = [] for tmp_name in config_main['main']['catalogues']: # If not absolute if not re.search('^/', tmp_name): tmp_name = os.path.join(path, tmp_name) assert os.path.exists(tmp_name) print('Catalogue: {:s}'.format(tmp_name)) fname_catalogues.append(tmp_name) calc_id = config_main['main']['calc_id'] ses_duration = config_main['main']['ses_duration'] polygon_fname = os.path.join(path, config_main['main']['polygon']) output_dir = os.path.join(path, config_main['main']['output_dir']) descr = config_main['main']['description'] binw = config_main['main'].get('bin_width', 0.2) min_magnitude = config_main['main'].get('min_magnitude', None) if ('tectonic_region' not in config_main['main'] or config_main['main']['tectonic_region'] in ['', 'none', 'None']): tectonic_region = None else: tectonic_region = int(config_main['main']['tectonic_region']) # Checking msg = 'The polygon file does not exist:\n{:s}'.format(polygon_fname) assert os.path.exists(polygon_fname), msg if not os.path.exists(output_dir): create_folder(output_dir) # Reading ruptures from the datastore dstore = read(calc_id) dfr = dstore.read_df('ruptures') dfr = gpd.GeoDataFrame(dfr, geometry=gpd.points_from_xy(dfr.hypo_0, dfr.hypo_1)) if tectonic_region is not None: dfr = dfr.loc[dfr['trt_smr'] == tectonic_region] # Reading geojson polygon and create the shapely geometry with open(polygon_fname) as json_file: data = json.load(json_file) polygon = data['features'][0]['geometry'] tmp = eval(geoj.dumps(polygon)) geom = shape(tmp) # Get region limits coo = [] for poly in geom.geoms: coo += list(zip(*poly.exterior.coords.xy)) coo = np.array(coo) minlo = np.min(coo[:, 0]) minla = np.min(coo[:, 1]) maxlo = np.max(coo[:, 0]) maxla = np.max(coo[:, 1]) region = "{:f}/{:f}/{:f}/{:f}".format(minlo, maxlo, minla, maxla) # Read catalogue for i, fname in enumerate(fname_catalogues): if i == 0: tcat = _load_catalogue(fname) else: tcat.concatenate(_load_catalogue(fname)) # Create a dataframe from the catalogue dfcat = to_df(tcat) dfcat = gpd.GeoDataFrame(dfcat, geometry=gpd.points_from_xy( dfcat.longitude, dfcat.latitude)) dfcat.head(n=1) # Select the events within the polygon and convert from df to catalogue idx = dfcat.within(geom) selcat_df = dfcat.loc[idx] selcat = from_df(selcat_df) if 'completeness_table' in config_main['main']: ctab = config_main['main']['completeness_table'] ctab = np.array(ctab) else: fname_config = os.path.join(path, config_main['main']['fname_config']) msg = 'The config file does not exist:\n{:s}'.format(fname_config) assert os.path.exists(fname_config), msg config = toml.load(fname_config) completeness_label = config_main['main']['completeness_label'] _, ctab = get_mmax_ctab(config, completeness_label) if len(selcat_df.magnitude) < 2: print('The catalogue contains less than 2 earthquakes') return selcat.data["dtime"] = selcat.get_decimal_time() cent_mag, t_per, n_obs = get_completeness_counts(selcat, ctab, binw) tmp = n_obs / t_per hiscml_cat = np.array([np.sum(tmp[i:]) for i in range(0, len(tmp))]) # Take into account possible multiple occurrences in the SES df = dfr.loc[dfr.index.repeat(dfr.n_occ)] assert len(df) == np.sum(dfr.n_occ) # SES histogram idx = dfr.within(geom) bins = np.arange(min_magnitude, 9.0, binw) hisr, _ = np.histogram(df.loc[idx].mag, bins=bins) hisr = hisr / ses_duration hiscml = np.array([np.sum(hisr[i:]) for i in range(0, len(hisr))]) # Plotting fig = plt.figure(figsize=(7, 5)) # - cumulative plt.plot(bins[:-1], hiscml, '--x', label='SES') plt.plot(cent_mag - binw / 2, hiscml_cat, '-.x', label='Catalogue') # - incremental plt.bar(cent_mag, n_obs / t_per, width=binw * 0.7, fc='none', ec='red', alpha=0.5, align='center') plt.bar(bins[1:] - binw / 2, hisr, width=binw * 0.6, fc='none', ec='blue', alpha=0.5) plt.yscale('log') _ = plt.xlabel('Magnitude') _ = plt.ylabel('Annual frequency of exceedance') plt.grid() plt.legend() plt.title(descr) # - set xlim xlim = list(fig.gca().get_xlim()) xlim[0] = min_magnitude if min_magnitude is not None else xlim[0] plt.xlim(xlim) plt.savefig(os.path.join(output_dir, 'ses.png')) # Plot map with the SES fig = pygmt.Figure() fig.basemap(region=region, projection="M15c", frame=True) fig.coast(land="#666666", water="skyblue") pygmt.makecpt(cmap="jet", series=[0, 300]) fig.plot(x=dfr.loc[idx].hypo_0, y=dfr.loc[idx].hypo_1, style="c", color=dfr.loc[idx].hypo_2, cmap=True, size=0.01 * (1.5**dfr.loc[idx].mag), pen="black") fig.show() fig.savefig(os.path.join(output_dir, 'map_ses.png')) # Plot map with catalogue fig = pygmt.Figure() fig.basemap(region=region, projection="M15c", frame=True) fig.coast(land="#666666", water="skyblue") pygmt.makecpt(cmap="jet", series=[0, 300]) fig.plot(x=selcat_df.longitude, y=selcat_df.latitude, style="c", color=selcat_df.depth, cmap=True, size=0.01 * (1.5**selcat_df.magnitude), pen="black") fig.show() fig.savefig(os.path.join(output_dir, 'map_eqks.png')) # Depth histogram deptw = 10. mmin = 5.0 dfs = df.loc[idx] bins = np.arange(0.0, 200.0, deptw) fig = plt.figure() hisr, _ = np.histogram(dfs[dfs.mag > mmin].hypo_2, bins=bins) hiscat, _ = np.histogram(selcat_df[selcat_df.magnitude > mmin].depth, bins=bins) fig = plt.Figure(figsize=(5, 8)) plt.barh(bins[:-1], hisr / sum(hisr), align='edge', height=deptw * 0.6, fc='lightgreen', ec='blue', label='ses') plt.barh(bins[:-1], hiscat / sum(hiscat), align='edge', height=deptw * 0.5, fc='white', ec='red', alpha=0.5, lw=1.5, label='catalogue') for dep, val in zip(bins[:-1], hiscat): if val > 0: plt.text(val / sum(hiscat), dep, s='{:.2f}'.format(val)) plt.gca().invert_yaxis() _ = plt.ylabel('Depth [km]') _ = plt.xlabel('Count') plt.grid() plt.legend() plt.title(descr) plt.savefig(os.path.join(output_dir, 'depth_normalized.png'))
def decluster(catalogue_hmtk_fname, declustering_meth, declustering_params, output_path, labels=None, tr_fname=None, subcatalogues=False, fmat='csv', olab='', save_af=False, out_fname_ext='', fix_defaults=False): """ :param str catalogue_hmtk_fname: Full path to the file containing the initial catalogue :param str declustering_meth: A string indicating the type of declustering :param dict declustering_params: Parameters required by the declustering algorithm :param str output_path: Folder where the output catalogue/s will be created :param list labels: It can be a string or a list of strings :param str tr_fname: An .hdf5 file containing the TR classification of the catalogue :param bool subcatalogues: When true creates subcatalogues per tectonic region :param str fmat: Can be either 'csv' or 'pkl' :param str olab: Optional label for output catalogues :param boolean save_af: Save aftershocks and foreshocks :param str out_fname_ext: String to be added to the putput filename :param str fix_defaults: Fix defaults values when missing """ # Check if the initial catalogue file exists msg = 'Catalogue {:s} is missing'.format(catalogue_hmtk_fname) assert os.path.exists(catalogue_hmtk_fname), msg # Create output filename lbl = 'all' if labels is not None and len(out_fname_ext) == 0: labels = [labels] if isinstance(labels, str) else labels if len(labels) < 2: lbl = labels[0] else: lbl = '-'.join([l for l in labels]) assert tr_fname is not None assert os.path.exists(tr_fname) ext = '_dec_{:s}_{:s}.{:s}'.format(olab, lbl, fmat) else: ext = '_dec_{:s}_{:s}.{:s}'.format(olab, out_fname_ext, fmat) # Output filename out_fname = Path(os.path.basename(catalogue_hmtk_fname)).stem+ext if output_path is not None: assert os.path.exists(output_path) else: output_path = os.path.dirname(catalogue_hmtk_fname) out_fname = os.path.abspath(os.path.join(output_path, out_fname)) # Read the catalogue and adding default values cat = _load_catalogue(catalogue_hmtk_fname) if fix_defaults: cat = _add_defaults(cat) cato = copy.deepcopy(cat) # Select earthquakes belonging to a given TR. When necessary combining # multiple TRs, use label <TR_1>,<TR_2>AND... idx = numpy.full(cat.data['magnitude'].shape, True, dtype=bool) sumchk = 0 if labels is not None and tr_fname is not None: f = h5py.File(tr_fname, 'r') idx = numpy.array([False for i in range(len(f[labels[0]]))]) for lab in labels: idx_tmp = f[lab][:] idx[numpy.where(idx_tmp.flatten())] = True print(lab, sum(idx_tmp.flatten())) sumchk += sum(idx_tmp.flatten()) f.close() idx = idx.flatten() # Filter catalogue num_eqks_sub = len(cat.data['magnitude']) if labels is not None: sel = CatalogueSelector(cat, create_copy=False) sel.select_catalogue(idx) num_eqks_sub = len(cat.data['magnitude']) assert sumchk == num_eqks_sub # Declustering vcl, flag = dec(declustering_params, declustering_meth, cat) # Save foreshocks and aftershocks catt = copy.deepcopy(cat) catt.select_catalogue_events(numpy.where(flag != 0)[0]) if save_af: ext = '_dec_af_{:s}_{:s}.{:s}'.format(olab, lbl, fmat) outfa_fname = Path(os.path.basename(catalogue_hmtk_fname)).stem+ext outfa_fname = os.path.abspath(os.path.join(output_path, outfa_fname)) # Select mainshocks cat.select_catalogue_events(numpy.where(flag == 0)[0]) tmps = 'Number of earthquakes in the original subcatalogue: {:d}' print('Total eqks : {:d}'.format(num_eqks_sub)) num_main = len(cat.data['magnitude']) num_foaf = len(catt.data['magnitude']) print('Mainshocks : {:d}'.format(num_main)) print('Fore/Aftershocks : {:d}'.format(num_foaf)) assert num_main + num_foaf == num_eqks_sub # Save output if fmat == 'csv': cat.write_catalogue(out_fname) print('Writing catalogue to file: {:s}'.format(out_fname)) if save_af: catt.write_catalogue(outfa_fname) elif fmat == 'pkl': fou = open(out_fname, 'wb') pickle.dump(cat, fou) fou.close() if save_af: fou = open(outfa_fname, 'wb') pickle.dump(catt, fou) fou.close() # Create subcatalogues icat = numpy.nonzero(idx)[0] if subcatalogues: f = h5py.File(tr_fname, 'r') for lab in labels: # # Select mainshocks in a given tectonic region jjj = numpy.where(flag == 0)[0] tmpi = numpy.full((len(idx)), False, dtype=bool) tmpi[icat[jjj.astype(int)]] = True idx_tmp = f[lab][:].flatten() kkk = numpy.logical_and(tmpi, idx_tmp) if save_af: jjj = numpy.where(flag != 0)[0] tmpi = numpy.full((len(idx)), False, dtype=bool) tmpi[icat[jjj.astype(int)]] = True idx_tmp = f[lab][:].flatten() jjj = numpy.logical_and(tmpi, idx_tmp) # # Create output catalogue tsel = CatalogueSelector(cato, create_copy=True) ooo = tsel.select_catalogue(kkk) if save_af: aaa = tsel.select_catalogue(jjj) # # Info tmps = 'Cat: {:s}\n' tmps += ' Earthquakes: {:5d} Mainshocks {:5d} {:4.1f}%' pct = sum(kkk)/sum(idx_tmp)*100. tmpr = ' mmin: {:5.2f} mmax {:5.2f}' tmpsum1 = int(sum(idx_tmp)) tmpsum2 = int(sum(kkk)) logging.info(tmps.format(lab, tmpsum1, tmpsum2, pct)) print(tmps.format(lab, tmpsum1, tmpsum2, pct)) print(tmpr.format(min(ooo.data['magnitude']), max(ooo.data['magnitude']))) # # Output filename ext = '_dec_{:s}_{:s}.{:s}'.format(olab, lab, fmat) tcat_fname = Path(os.path.basename(catalogue_hmtk_fname)).stem+ext tmps = os.path.join(output_path, tcat_fname) tcat_fname = os.path.abspath(tmps) if save_af: ext = '_dec_af_{:s}_{:s}.{:s}'.format(olab, lab, fmat) tcataf_fname = Path( os.path.basename(catalogue_hmtk_fname)).stem + ext tmps = os.path.join(output_path, tcataf_fname) tcataf_fname = os.path.abspath(tmps) # # Dumping data into the pickle file if ooo is not None: if fmat == 'csv': ooo.write_catalogue(tcat_fname) if save_af: aaa.write_catalogue(tcataf_fname) elif fmat == 'pkl': fou = open(tcat_fname, 'wb') pickle.dump(ooo, fou) fou.close() if save_af: fou = open(tcataf_fname, 'wb') pickle.dump(aaa, fou) fou.close() else: tstr = 'Catalogue for region {:s} is empty'.format(lab) logging.warning(tstr) f.close() outl = [out_fname] if save_af: outl.append(outfa_fname) return [out_fname]
def create_mtd(catalogue_fname, label, tr_fname, cumulative, store, mwid=0.1, twid=20., pmint=None, pmaxt=None, ylim=None): """ :param catalogue_fname: :param label: :param tr_fname: """ mwid = float(mwid) twid = float(twid) if pmint is not None: pmint = int(pmint) if pmaxt is not None: pmaxt = int(pmaxt) # # loading catalogue if isinstance(catalogue_fname, str): cat = _load_catalogue(catalogue_fname) elif isinstance(catalogue_fname, Catalogue): cat = catalogue_fname else: raise ValueError('Unknown instance') # Check catalogue if cat is None or len(cat.data['magnitude']) < 1: return None # Select earthquakes belonging to a given TR idx = numpy.full(cat.data['magnitude'].shape, True, dtype=bool) if label is not None and tr_fname is not None: f = h5py.File(tr_fname, 'r') idx = f[label][:] f.close() # # select catalogue sel = CatalogueSelector(cat, create_copy=False) sel.select_catalogue(idx) start = datetime.datetime(pmint, 1, 1) if pmint is not None else None stop = datetime.datetime(pmaxt, 12, 31) if pmaxt is not None else None sel.within_time_period(start, stop) # Check if the catalogue contains earthquakes if len(cat.data['magnitude']) < 1: return None # # find rounded min and max magnitude mmin, mmax = _get_extremes(cat.data['magnitude'], mwid) tmin, tmax = _get_extremes(cat.data['year'], twid) if ylim is not None: mmin = ylim[0] mmax = ylim[1] # # if pmint is None: pmint = tmin if pmaxt is None: pmaxt = tmax # # histogram bins_ma = numpy.arange(mmin, mmax + mwid * 0.01, mwid) bins_time = numpy.arange(tmin, tmax + twid * 0.01, twid) his, _, _ = numpy.histogram2d(cat.data['year'], cat.data['magnitude'], bins=(bins_time, bins_ma)) his = his.T # # complementary cumulative if cumulative: ccu = numpy.zeros_like(his) for i in range(his.shape[1]): cc = numpy.cumsum(his[::-1, i]) ccu[:, i] = cc[::-1] # # plotting fig = plt.figure() ax = fig.add_subplot(1, 1, 1) # # X, Y = numpy.meshgrid(bins_time, bins_ma) if cumulative: his = ccu pcm = ax.pcolormesh(X, Y, his, norm=colors.LogNorm(vmin=1e-1, vmax=his.max()), cmap='BuGn') # # plotting number of occurrences for it, vt in enumerate(bins_time[:-1]): for im, vm in enumerate(bins_ma[:-1]): ax.text(vt + twid / 2, vm + mwid / 2, '{:.0f}'.format(his[im, it]), fontsize=7, ha='center') # # plotting colorbar cb = fig.colorbar(pcm, ax=ax, extend='max') cb.set_label('Number of occurrences') # # finishing the plot plt.ylabel('Magnitude') plt.xlabel('Time') if label is not None: plt.title('label: {:s}'.format(label)) plt.grid(linestyle='-') return fig
def completeness_analysis(fname, idxs, years, mags, binw, ref_mag, bgrlim, src_id, folder_out_figs, rewrite=False): tcat = _load_catalogue(fname) tcat = _add_defaults(tcat) tcat.data["dtime"] = tcat.get_decimal_time() print('\nSOURCE:', src_id) print('Catalogue contains {:d} events'.format(len(tcat.data['magnitude']))) # See http://shorturl.at/adsvA fname_disp = 'dispositions.npy' perms = np.load(fname_disp) mags = np.flipud(np.load('mags.npy')) years = np.load('years.npy') wei_conf = {'magnitude_interval': binw, 'reference_magnitude': 0.0} weichert = Weichert() rate = -1e10 save = [] mags = np.array(mags) for prm in perms: idx = prm.astype(int) tmp = np.array([(y, m) for y, m in zip(years, mags[idx])]) ctab = clean_completeness(tmp) try: cent_mag, t_per, n_obs = get_completeness_counts(tcat, ctab, binw) bval, sigb, aval, siga = weichert.calculate(tcat, wei_conf, ctab) tmp_rate = 10**(-bval * ref_mag + aval) if tmp_rate > rate and bval <= bgrlim[1] and bval >= bgrlim[0]: rate = tmp_rate save = [aval, bval, rate, ctab] gwci = get_weichert_confidence_intervals lcl, ucl, ex_rates, ex_rates_scaled = gwci( cent_mag, n_obs, t_per, bval) mmax = max(tcat.data['magnitude']) wei = [ cent_mag, n_obs, binw, t_per, ex_rates_scaled, lcl, ucl, mmax, aval, bval ] except RuntimeWarning: logging.debug('Skipping', ctab) except UserWarning: logging.debug('Skipping', ctab) except: logging.debug('Skipping', ctab) if True: fmt = 'Maximum annual rate for {:.1f}: {:.4f}' print(fmt.format(ref_mag, save[2])) fmt = 'GR a and b : {:.4f} {:.4f}' print(fmt.format(save[0], save[1])) print('Completeness:\n', save[3]) _weichert_plot(wei[0], wei[1], wei[2], wei[3], wei[4], wei[5], wei[6], wei[7], wei[8], wei[9], src_id=src_id) # Saving figure if folder_out_figs is not None: ext = 'png' fmt = 'fig_mfd_{:s}.{:s}' figure_fname = os.path.join(folder_out_figs, fmt.format(src_id, ext)) plt.savefig(figure_fname, format=ext) plt.close() return save
def create_mtd(catalogue_fname, label, tr_fname, cumulative, store, mwid=0.1, twid=20., pmint=None, pmaxt=None, ylim=None): """ :param catalogue_fname: :param label: :param tr_fname: """ mwid = float(mwid) twid = float(twid) if pmint is not None: pmint = int(pmint) if pmaxt is not None: pmaxt = int(pmaxt) # # loading catalogue if isinstance(catalogue_fname, str): cat = _load_catalogue(catalogue_fname) elif isinstance(catalogue_fname, Catalogue): cat = catalogue_fname else: raise ValueError('Unknown instance') # Check catalogue if cat is None or len(cat.data['magnitude']) < 1: return None # Select earthquakes belonging to a given TR idx = numpy.full(cat.data['magnitude'].shape, True, dtype=bool) if label is not None and tr_fname is not None: f = h5py.File(tr_fname, 'r') idx = f[label][:] f.close() # # select catalogue sel = CatalogueSelector(cat, create_copy=False) sel.select_catalogue(idx) start = datetime.datetime(pmint, 1, 1) if pmint is not None else None stop = datetime.datetime(pmaxt, 12, 31) if pmaxt is not None else None sel.within_time_period(start, stop) # Check if the catalogue contains earthquakes if len(cat.data['magnitude']) < 1: return None # Get matrix bins_time, bins_ma, his = get_mtd(cat, mwid, twid, ylim, cumulative) # Plotting fig = plt.figure() ax = fig.add_subplot(1, 1, 1) X, Y = numpy.meshgrid(bins_time, bins_ma) tmp_col = colors.LogNorm(vmin=1e-1, vmax=his.max()) pcm = ax.pcolormesh(X, Y, his, norm=tmp_col, cmap='BuGn') # Plotting number of occurrences for it, vt in enumerate(bins_time[:-1]): for im, vm in enumerate(bins_ma[:-1]): ax.text(vt + twid / 2, vm + mwid / 2, '{:.0f}'.format(his[im, it]), fontsize=7, ha='center') # Plotting colorbar cb = fig.colorbar(pcm, ax=ax, extend='max') cb.set_label('Number of occurrences') # Finishing the plot plt.ylabel('Magnitude') plt.xlabel('Time') if label is not None: plt.title('label: {:s}'.format(label)) plt.grid(linestyle='-') return fig
def plot_mfd(catalogue_fname, grd, label, store, tr_fname, compl_table=None, mwid=0.1, upper_mag=11., title='', xlim=None, ylim=None, figsize=None): """ This function plots the incremental and complementary cumulative distribution of the earthquakes included in a catalogue file. :param catalogue_fname: Full path to the hmtk formatted catalogue :param label: If the user provides a tectonic regionalisation file, this label (or list of labels with the format LAB1, LAB2) defines the tectonic regions to be selected. :param tr_fname: Full path to the .hdf5 file containing the TR :param compl_table: A :class:`numpy.ndarray` instance of shape (2, n) where the first column contains years in a decreasing order and the second column contains magnitude (generally) in an increasing order :param grd: A boolean indicating the need to compute GR parameters :param upper_mag: The upper magnitude threshold used to filter the catalogue. This is useful for example in cases when it is interesting to fit only the exponential component of a magnitude-frequency distribution. :return: A tuple containing the output of the Weichert method in the following order: gr_pars """ mwid = float(mwid) # # loading catalogue cat = _load_catalogue(catalogue_fname) # # select earthquakes belonging to a given TR idx = numpy.full(cat.data['magnitude'].shape, True, dtype=bool) if label is not None and tr_fname is not None: f = h5py.File(tr_fname, 'r') idx = f[label][:] f.close() # # select catalogue sel = CatalogueSelector(cat, create_copy=False) sel.select_catalogue(idx) sel.within_magnitude_range(-1, upper_mag) tmps = 'Selecting earthquakes below magnitude {:.2f}' logging.info(tmps.format(upper_mag)) # # find rounded min and max magnitude mmin, mmax = _get_extremes(cat.data['magnitude'], mwid) tmin, tmax = _get_extremes(cat.data['year'], 10) # # compute histogram bins = numpy.arange(mmin, mmax + mwid * 0.01, mwid) his, _ = numpy.histogram(cat.data['magnitude'], bins) # # plotting if figsize is None: figsize = (8, 6) fig = plt.figure(figsize=figsize) ax = fig.add_subplot(1, 1, 1) # # add cumulative plot cs = numpy.cumsum(his[::-1]) plt.bar(bins[:-1], cs[::-1], mwid, align='edge', ec='cyan', fc='none') plt.plot(bins[:-1] + mwid / 2, cs[::-1], '-r', label='cumulative') # # add incremental plot plt.bar(bins[:-1], his, mwid, align='edge', ec='orange', fc='none') plt.plot(bins[:-1] + mwid / 2, his, '-b', label='incremental') # # if grd: if compl_table is None: compl_table = numpy.array([[tmin, mmin]]) agr, bgr, asig, bsig = _compute_mfd(cat, compl_table, mwid) # # info num = len(cat.data['magnitude']) print('Number of earthquakes in the catalogue : {:d}'.format(num)) num = max(cs) print('Maximum value in the c. cumulative distribution : {:d}'.format(num)) # # finish plot plt.legend() plt.yscale('log') plt.ylabel('Number of earthquakes') plt.xlabel('Magnitude') if label is not None: plt.title('label: {:s}'.format(label)) plt.grid(linestyle='-') if grd: plt.text(0.65, 0.70, 'bval: %.3f (sigma=%.3f)' % (bgr, bsig), horizontalalignment='left', verticalalignment='center', fontsize=8, transform=ax.transAxes) plt.text(0.65, 0.75, 'aval: %.3f (sigma=%.3f)' % (agr, asig), horizontalalignment='left', verticalalignment='center', fontsize=8, transform=ax.transAxes) # # ascaled = numpy.log10(10**agr * (tmax - tmin)) v = 10.**(-bins * bgr + ascaled) plt.plot(bins, v, '--g', lw=2) # # set limits if xlim is not None: plt.xlim(xlim) if ylim is not None: plt.ylim(ylim) # # Set title plt.title(title) # # Storing figure if store is not None: lbl = '' ext = 'png' if label is not None: lbl = label figure_fname = 'fig_mfd_{:s}.{:s}'.format(lbl, ext) plt.savefig(figure_fname, format=ext) else: plt.show() out = (bins[:-1] + mwid / 2, numpy.array([float(h) for h in his])) if grd: return out, (agr, bgr, asig, bsig) else: return out, None
def compute_a_value(fname_input_pattern: str, bval: float, fname_config: str, folder_out: str, use: str = '', folder_out_figs: str = None, plt_show=False): """ This function assignes an a-value to each source with a file selected by the provided `fname_input_pattern`. """ if len(use) > 0: use = get_list(use) # Processing input parameters bval = float(bval) if folder_out is not None: create_folder(folder_out) if folder_out_figs is not None: create_folder(folder_out_figs) if isinstance(fname_input_pattern, str): fname_list = glob(fname_input_pattern) else: fname_list = fname_input_pattern # Parsing config model = toml.load(fname_config) binw = model['bin_width'] # Processing files for fname in sorted(fname_list): # Get source ID src_id = _get_src_id(fname) if len(use) > 0 and src_id not in use: continue print(fname) mmax, ctab = get_mmax_ctab(model, src_id) # Processing catalogue tcat = _load_catalogue(fname) if tcat is None or len(tcat.data['magnitude']) < 2: continue # Completeness analysis tcat = _add_defaults(tcat) tcat.data["dtime"] = tcat.get_decimal_time() try: cent_mag, t_per, n_obs = get_completeness_counts(tcat, ctab, binw) if cent_mag is None: print(' Completeness analysis failed') continue except ValueError: print(' Completeness analysis failed') continue df = pd.DataFrame() df['mag'] = cent_mag df['deltaT'] = t_per df['nobs'] = n_obs fout = os.path.join(folder_out, 'occ_count_zone_{:s}'.format(src_id)) df.to_csv(fout, index=False) # Computing GR a if 'sources' not in model: model['sources'] = {} if src_id not in model['sources']: model['sources'][src_id] = {} exrs = get_exrs(df, bval) aval = get_agr(df.mag[0] - binw / 2, bval, exrs[0]) tmp = "{:.5e}".format(aval) model['sources'][src_id]['agr_counting'] = float(tmp) tmp = "{:.5e}".format(bval) model['sources'][src_id]['bgr_counting'] = float(tmp) gwci = get_weichert_confidence_intervals lcl, ucl, ex_rates, ex_rates_scaled = gwci(cent_mag, n_obs, t_per, bval) _ = plt.figure() ax = plt.gca() plt.plot(cent_mag, n_obs / t_per, 'o', markerfacecolor='none') plt.plot(cent_mag - binw / 2, ex_rates_scaled, 's', markerfacecolor='none', color='red') plt.plot(cent_mag - binw / 2, lcl, '--', color='black') plt.plot(cent_mag - binw / 2, ucl, '--', color='black') xmag = numpy.arange(cent_mag[0] - binw / 2, mmax - 0.01 * binw, binw / 2) exra = (10.0**(aval - bval * xmag) - 10.0**(aval - bval * mmax)) plt.plot(xmag, exra, '--', lw=3, color='green') plt.yscale('log') plt.xlabel('Magnitude') plt.ylabel('Annual rate of exceedance') plt.text(0.75, 0.95, 'Fixed b_GR = {:.2f}'.format(bval), transform=ax.transAxes) plt.grid(which='major', color='grey') plt.grid(which='minor', linestyle='--', color='lightgrey') plt.title(src_id) if plt_show: plt.show() # Saving figures if folder_out_figs is not None: ext = 'png' fmt = 'fig_mfd_{:s}.{:s}' figure_fname = os.path.join(folder_out_figs, fmt.format(src_id, ext)) plt.savefig(figure_fname, format=ext) plt.close() # Saving results into the config file with open(fname_config, 'w') as fou: fou.write(toml.dumps(model)) print('Updated {:s}'.format(fname_config))
def weichert_analysis(fname_input_pattern, fname_config, folder_out=None, folder_out_figs=None, skip=[], binw=None, plt_show=False): """ Computes GR parameters for a set of catalogues stored in a .csv file :param fname_input_pattern: It can be either a string (definining a pattern) or a list of .csv files. The file names must have the source ID at the end. The delimiter of the source ID on the left is `_` :param fname_config: The name of the .toml configuration file :param folder_out: The folder where to store the files with the counting of occurrences :param folder_out_figs: The folder where to store the figures :param skip: A list with the IDs of the sources to skip """ if folder_out is not None: create_folder(folder_out) if folder_out_figs is not None: create_folder(folder_out_figs) # Parsing config if fname_config is not None: model = toml.load(fname_config) if binw is None and fname_config is not None: binw = model['bin_width'] else: binw = 0.1 if isinstance(fname_input_pattern, str): fname_list = [f for f in glob(fname_input_pattern)] else: fname_list = fname_input_pattern # Processing files for fname in sorted(fname_list): print(fname) # Get source ID src_id = _get_src_id(fname) if src_id in skip: print(" skipping") continue if 'sources' in model: if (src_id in model['sources'] and 'mmax' in model['sources'][src_id]): mmax = model['sources'][src_id]['mmax'] else: mmax = model['default']['mmax'] if (src_id in model['sources'] and 'completeness_table' in model['sources'][src_id]): key_tmp = 'completeness_table' ctab = numpy.array(model['sources'][src_id][key_tmp]) print('Using source specific completeness') else: ctab = numpy.array(model['default']['completeness_table']) else: mmax = model['default']['mmax'] ctab = numpy.array(model['default']['completeness_table']) # Processing catalogue tcat = _load_catalogue(fname) if tcat is None or len(tcat.data['magnitude']) < 2: print(' Source {:s} has less than 2 eqks'.format(src_id)) continue tcat.data["dtime"] = tcat.get_decimal_time() cent_mag, t_per, n_obs = get_completeness_counts(tcat, ctab, binw) if folder_out is not None: df = pd.DataFrame() df['mag'] = cent_mag df['deltaT'] = t_per df['nobs'] = n_obs fmt = 'occ_count_zone_{:s}' fout = os.path.join(folder_out, fmt.format(src_id)) df.to_csv(fout, index=False) # Computing GR a and b tcat = _add_defaults(tcat) weichert_config = { 'magnitude_interval': binw, 'reference_magnitude': 0.0 } weichert = Weichert() bval_wei, sigmab, aval_wei, sigmaa = weichert.calculate( tcat, weichert_config, ctab) # Computing confidence intervals gwci = get_weichert_confidence_intervals lcl, ucl, ex_rates, ex_rates_scaled = gwci(cent_mag, n_obs, t_per, bval_wei) if 'sources' not in model: model['sources'] = {} if src_id not in model['sources']: model['sources'][src_id] = {} tmp = "{:.5e}".format(aval_wei) model['sources'][src_id]['agr_weichert'] = float(tmp) tmp = "{:.3f}".format(bval_wei) model['sources'][src_id]['bgr_weichert'] = float(tmp) _ = plt.figure() ax = plt.gca() plt.plot(cent_mag, n_obs / t_per, 'o', markerfacecolor='none') plt.plot(cent_mag - binw / 2, ex_rates_scaled, 's', markerfacecolor='none', color='red') plt.plot(cent_mag - binw / 2, lcl, '--', color='darkgrey') plt.plot(cent_mag - binw / 2, ucl, '--', color='darkgrey') xmag = numpy.arange(cent_mag[0] - binw / 2, mmax - 0.01 * binw, binw / 2) exra = (10.0**(aval_wei - bval_wei * xmag) - 10.0**(aval_wei - bval_wei * mmax)) plt.plot(xmag, exra, '--', lw=3, color='green') plt.yscale('log') plt.xlabel('Magnitude') plt.ylabel('Annual rate of exceedance') plt.text(0.75, 0.95, 'b_GR = {:.2f}'.format(bval_wei), transform=ax.transAxes) plt.grid(which='major', color='grey') plt.grid(which='minor', linestyle='--', color='lightgrey') plt.title(src_id) if plt_show: plt.show() # Saving figures if folder_out_figs is not None: ext = 'png' fmt = 'fig_mfd_{:s}.{:s}' figure_fname = os.path.join(folder_out_figs, fmt.format(src_id, ext)) plt.savefig(figure_fname, format=ext) plt.close() # Saving results into the config file if fname_config is not None: with open(fname_config, 'w') as f: f.write(toml.dumps(model)) print('Updated {:s}'.format(fname_config))