class SourceGenerator(LocationGenerator): nevents = Int.T(default=2) avoid_water = Bool.T( default=False, help='Avoid sources offshore under the ocean / lakes.') radius = Float.T(default=10 * km) time_min = Timestamp.T(default=util.str_to_time('2017-01-01 00:00:00')) time_max = Timestamp.T(default=util.str_to_time('2017-01-03 00:00:00')) magnitude_min = Float.T(default=4.0) magnitude_max = Float.T(default=7.0) def get_sources(self): sources = [] for ievent in range(self.nevents): src = self.get_source(ievent) src.name = 'scenario_ev%03d' % (ievent + 1) sources.append(src) return sources def dump_data(self, path): fn_sources = op.join(path, 'sources.yml') with open(fn_sources, 'w') as f: for src in self.get_sources(): f.write(src.dump()) fn_events = op.join(path, 'events.txt') with open(fn_events, 'w') as f: for isrc, src in enumerate(self.get_sources()): f.write(src.pyrocko_event().dump()) return [fn_events, fn_sources] def add_map_artists(self, automap): pass
class QSSPConfig(Object): qssp_version = String.T(default='2010beta') time_region = Tuple.T(2, gf.Timing.T(), default=(gf.Timing('-10'), gf.Timing('+890'))) frequency_max = Float.T(optional=True) slowness_max = Float.T(default=0.4) antialiasing_factor = Float.T(default=0.1) lowpass_order = Int.T(default=0, optional=True) lowpass_corner = Float.T(default=1.0, optional=True) bandpass_order = Int.T(default=0, optional=True) bandpass_corner_low = Float.T(default=1.0, optional=True) bandpass_corner_high = Float.T(default=1.0, optional=True) output_slowness_min = Float.T(default=0.0, optional=True) output_slowness_max = Float.T(optional=True) spheroidal_modes = Bool.T(default=True) toroidal_modes = Bool.T(default=True) # only available in 2010beta: cutoff_harmonic_degree_sd = Int.T(optional=True, default=0) cutoff_harmonic_degree_min = Int.T(default=0) cutoff_harmonic_degree_max = Int.T(default=25000) crit_frequency_sge = Float.T(default=0.0) crit_harmonic_degree_sge = Int.T(default=0) include_physical_dispersion = Bool.T(default=False) source_patch_radius = Float.T(default=0.0) cut = Tuple.T(2, gf.Timing.T(), optional=True) fade = Tuple.T(4, gf.Timing.T(), optional=True) relevel_with_fade_in = Bool.T(default=False) nonzero_fade_in = Bool.T(default=False) nonzero_fade_out = Bool.T(default=False) def items(self): return dict(self.T.inamevals(self))
class SamplerConfig(Object): """ Config for the sampler specific parameters. """ name = String.T(default='SMC', help='Sampler to use for sampling the solution space.' ' Metropolis/ SMC') progressbar = Bool.T(default=True, help='Display progressbar(s) during sampling.') buffer_size = Int.T(default=5000, help='number of samples after which the result ' 'buffer is written to disk') parameters = SamplerParameters.T(default=SMCConfig.D(), optional=True, help='Sampler dependend Parameters') def set_parameters(self, **kwargs): if self.name is None: logger.info('Sampler not defined, using default sampler: SMC') self.name = 'SMC' if self.name == 'SMC': self.parameters = SMCConfig(**kwargs) elif self.name != 'SMC': kwargs.pop('update_covariances', None) if self.name == 'Metropolis': self.parameters = MetropolisConfig(**kwargs) elif self.name == 'PT': self.parameters = ParallelTemperingConfig(**kwargs) else: raise TypeError('Sampler "%s" is not implemented.' % self.name)
class MetaDataDownloadConfig(Object): # Data and Metadata download download_data = Bool.T() download_metadata = Bool.T() local_metadata = List.T(default=[]) local_data = List.T(default=[]) local_waveforms_only = Bool.T(default=False) sds_structure = Bool.T(default=False) use_downmeta = Bool.T(default=True) channels_download = String.T() all_channels = Bool.T(default=False) # components = List.T() token = Dict.T(default={}) sites = List.T() # start and end time with respect to origin time # start time before origin time! dt_start = Float.T() dt_end = Float.T()
class TargetBalancingAnalyserConfig(AnalyserConfig): """Configuration parameters of the target balancing.""" niterations = Int.T( default=1000, help='Number of random forward models for mean phase amplitude ' 'estimation') use_reference_magnitude = Bool.T( default=False, help='Fix magnitude of random sources to the magnitude of the ' 'reference event.') cutoff = Float.T( optional=True, help='Remove targets where ratio m/p > cutoff, where m is the misfit ' 'between synthetics and observations and p is the misfit between ' 'synthetics and zero-traces. Magnitude should be fixed to use ' 'this.') def get_analyser(self): return TargetBalancingAnalyser( niter=self.niterations, use_reference_magnitude=self.use_reference_magnitude, cutoff=self.cutoff)
class EngineConfig(HasPaths): gf_stores_from_pyrocko_config = Bool.T( default=True, help='Load the GF stores from ~/.pyrocko/config') gf_store_superdirs = List.T( Path.T(), help='List of path hosting collection of Green\'s function stores.') gf_store_dirs = List.T( Path.T(), help='List of Green\'s function stores') def __init__(self, *args, **kwargs): HasPaths.__init__(self, *args, **kwargs) self._engine = None def get_engine(self): if self._engine is None: fp = self.expand_path self._engine = gf.LocalEngine( use_config=self.gf_stores_from_pyrocko_config, store_superdirs=fp(self.gf_store_superdirs), store_dirs=fp(self.gf_store_dirs)) return self._engine
class PalantiriDataConfig(Object): '''Configuration of data IO and data preprocessing''' quantity = String.T(default="displacement", help='velocity and displacement') tttopt = String.T(default="-ph P", help='quantity') cam = Int.T(default=2, help='Length in seconds. Not needed \ when using TFRecordData') pyrocko_download = Bool.T( default=True, optional=True, help='if download of was not done with palantiridown command, set to 0' ) export_unfiltered = Bool.T(default=False, optional=True, help='Save unfiltered waveforms.') export_filtered = Bool.T(default=False, optional=True, help='Save filtered waveforms.') export_resampled = Bool.T(default=False, optional=True, help='Save resmpled mseed.') colesseo_input = Bool.T(default=False, optional=True, help='if colosseo\ synthetics should be used, set to 1') colosseo_scenario_yml = String.T(optional=True, help='give the colosseo\ scenario.yml file') load_wdf = Bool.T(default=True, optional=True, help='Save and load xcorr files as pickle ')
class PhaseRatioTarget(gf.Location, MisfitTarget): ''' Target to compare ratios or log ratios of two seismic phases. misfit = | a_obs / (a_obs + b_obs) - a_syn / (a_syn + b_syn) | ''' codes = Tuple.T( 3, String.T(), help='network, station, location codes.') store_id = gf.StringID.T( help='ID of Green\'s function store to use for the computation.') backazimuth = Float.T(optional=True) interpolation = gf.InterpolationMethod.T() measure_a = fm.FeatureMeasure.T() measure_b = fm.FeatureMeasure.T() fit_log_ratio = Bool.T( default=True, help='if true, compare synthetic and observed log ratios') fit_log_ratio_waterlevel = Float.T( default=0.01, help='Waterlevel added to both ratios when comparing on logarithmic ' 'scale, to avoid log(0)') can_bootstrap_weights = True def __init__(self, **kwargs): gf.Location.__init__(self, **kwargs) MisfitTarget.__init__(self, **kwargs) @classmethod def get_plot_classes(cls): from . import plot plots = super(PhaseRatioTarget, cls).get_plot_classes() plots.extend(plot.get_plot_classes()) return plots def string_id(self): return '.'.join(x for x in (self.path,) + self.codes) def get_plain_targets(self, engine, source): return self.prepare_modelling(engine, source, None) def prepare_modelling(self, engine, source, targets): modelling_targets = [] for measure in [self.measure_a, self.measure_b]: modelling_targets.extend(measure.get_modelling_targets( self.codes, self.lat, self.lon, self.depth, self.store_id, self.backazimuth)) return modelling_targets def finalize_modelling( self, engine, source, modelling_targets, modelling_results): ds = self.get_dataset() try: imt = 0 amps = [] for measure in [self.measure_a, self.measure_b]: nmt_this = measure.get_nmodelling_targets() amp_obs, _ = measure.evaluate( engine, source, modelling_targets[imt:imt+nmt_this], dataset=ds) amp_syn, _ = measure.evaluate( engine, source, modelling_targets[imt:imt+nmt_this], trs=[r.trace.pyrocko_trace() for r in modelling_results[imt:imt+nmt_this]]) amps.append((amp_obs, amp_syn)) imt += nmt_this (a_obs, a_syn), (b_obs, b_syn) = amps eps = self.fit_log_ratio_waterlevel if self.fit_log_ratio: res_a = num.log(a_obs / (a_obs + b_obs) + eps) \ - num.log(a_syn / (a_syn + b_syn) + eps) else: res_a = a_obs / (a_obs + b_obs) - a_syn / (a_syn + b_syn) misfit = num.abs(res_a) norm = 1.0 result = PhaseRatioResult( misfits=num.array([[misfit, norm]], dtype=num.float), a_obs=a_obs, b_obs=b_obs, a_syn=a_syn, b_syn=b_syn) return result except dataset.NotFound as e: logger.debug(str(e)) return gf.SeismosizerError('No waveform data: %s' % str(e))
class HudsonPlot(PlotConfig): ''' Illustration of the solution distribution of decomposed moment tensor. ''' name = 'hudson' size_cm = Tuple.T(2, Float.T(), default=(17.5, 17.5*(3./4.))) beachball_type = StringChoice.T( choices=['full', 'deviatoric', 'dc'], default='dc') show_reference = Bool.T(default=True) def make(self, environ): cm = environ.get_plot_collection_manager() history = environ.get_history(subset='harvest') mpl_init(fontsize=self.font_size) cm.create_group_mpl( self, self.draw_figures(history), title=u'Hudson Plot', section='solution', feather_icon='box', description=u''' Hudson's source type plot with the ensemble of bootstrap solutions. For about 10% of the solutions (randomly chosen), the focal mechanism is depicted, others are represented as dots. The square marks the global best fitting solution. ''') def draw_figures(self, history): color = 'black' fontsize = self.font_size markersize = fontsize * 1.5 markersize_small = markersize * 0.2 beachballsize = markersize beachballsize_small = beachballsize * 0.5 beachball_type = self.beachball_type problem = history.problem best_source = history.get_best_source() mean_source = history.get_mean_source() fig = plt.figure(figsize=self.size_inch) axes = fig.add_subplot(1, 1, 1) data = [] for ix, x in enumerate(history.models): source = problem.get_source(x) mt = source.pyrocko_moment_tensor() u, v = hudson.project(mt) if random.random() < 0.1: try: beachball.plot_beachball_mpl( mt, axes, beachball_type=beachball_type, position=(u, v), size=beachballsize_small, color_t=color, alpha=0.5, zorder=1, linewidth=0.25) except beachball.BeachballError as e: logger.warn(str(e)) else: data.append((u, v)) if data: u, v = num.array(data).T axes.plot( u, v, 'o', color=color, ms=markersize_small, mec='none', mew=0, alpha=0.25, zorder=0) hudson.draw_axes(axes) mt = mean_source.pyrocko_moment_tensor() u, v = hudson.project(mt) try: beachball.plot_beachball_mpl( mt, axes, beachball_type=beachball_type, position=(u, v), size=beachballsize, color_t=color, zorder=2, linewidth=0.5) except beachball.BeachballError as e: logger.warn(str(e)) mt = best_source.pyrocko_moment_tensor() u, v = hudson.project(mt) axes.plot( u, v, 's', markersize=markersize, mew=1, mec='black', mfc='none', zorder=-2) if self.show_reference: mt = problem.base_source.pyrocko_moment_tensor() u, v = hudson.project(mt) try: beachball.plot_beachball_mpl( mt, axes, beachball_type=beachball_type, position=(u, v), size=beachballsize, color_t='red', zorder=2, linewidth=0.5) except beachball.BeachballError as e: logger.warn(str(e)) item = PlotItem( name='main') return [[item, fig]]
class MTDecompositionPlot(PlotConfig): ''' Moment tensor decomposition plot. ''' name = 'mt_decomposition' size_cm = Tuple.T(2, Float.T(), default=(15., 5.)) cluster_attribute = meta.StringID.T( optional=True, help='name of attribute to use as cluster IDs') show_reference = Bool.T(default=True) def make(self, environ): cm = environ.get_plot_collection_manager() history = environ.get_history(subset='harvest') mpl_init(fontsize=self.font_size) cm.create_group_mpl( self, self.draw_figures(history), title=u'MT Decomposition', section='solution', feather_icon='sun', description=u''' Moment tensor decomposition of the best-fitting solution into isotropic, deviatoric and best double couple components. Shown are the ensemble best, the ensemble mean%s and, if available, a reference mechanism. The symbol size indicates the relative strength of the components. The inversion result is consistent and stable if ensemble mean and ensemble best have similar symbol size and patterns. ''' % (', cluster results' if self.cluster_attribute else '')) def draw_figures(self, history): fontsize = self.font_size fig = plt.figure(figsize=self.size_inch) axes = fig.add_subplot(1, 1, 1, aspect=1.0) fig.subplots_adjust(left=0., right=1., bottom=0., top=1.) problem = history.problem models = history.models if models.size == 0: logger.warn('Empty models vector.') return [] # gms = problem.combine_misfits(history.misfits) # isort = num.argsort(gms) # iorder = num.empty_like(isort) # iorder[isort] = num.arange(iorder.size)[::-1] ref_source = problem.base_source mean_source = stats.get_mean_source( problem, history.models) best_source = history.get_best_source() nlines_max = int(round(self.size_cm[1] / 5. * 4. - 1.0)) if self.cluster_attribute: cluster_sources = history.mean_sources_by_cluster( self.cluster_attribute) else: cluster_sources = [] def get_deco(source): mt = source.pyrocko_moment_tensor() return mt.standard_decomposition() lines = [] lines.append( ('Ensemble best', get_deco(best_source), mpl_color('aluminium5'))) lines.append( ('Ensemble mean', get_deco(mean_source), mpl_color('aluminium5'))) for (icluster, perc, source) in cluster_sources: if len(lines) < nlines_max - int(self.show_reference): lines.append( (cluster_label(icluster, perc), get_deco(source), cluster_color(icluster))) else: logger.warn( 'Skipping display of cluster %i because figure height is ' 'too small. Figure height should be at least %g cm.' % ( icluster, (3 + len(cluster_sources) + int(self.show_reference)) * 5/4.)) if self.show_reference: lines.append( ('Reference', get_deco(ref_source), mpl_color('aluminium3'))) moment_full_max = max(deco[-1][0] for (_, deco, _) in lines) for xpos, label in [ (0., 'Full'), (2., 'Isotropic'), (4., 'Deviatoric'), (6., 'CLVD'), (8., 'DC')]: axes.annotate( label, xy=(1 + xpos, nlines_max), xycoords='data', xytext=(0., 0.), textcoords='offset points', ha='center', va='center', color='black', fontsize=fontsize) for i, (label, deco, color_t) in enumerate(lines): ypos = nlines_max - i - 1.0 [(moment_iso, ratio_iso, m_iso), (moment_dc, ratio_dc, m_dc), (moment_clvd, ratio_clvd, m_clvd), (moment_devi, ratio_devi, m_devi), (moment_full, ratio_full, m_full)] = deco size0 = moment_full / moment_full_max axes.annotate( label, xy=(-2., ypos), xycoords='data', xytext=(0., 0.), textcoords='offset points', ha='left', va='center', color='black', fontsize=fontsize) for xpos, mt_part, ratio, ops in [ (0., m_full, ratio_full, '-'), (2., m_iso, ratio_iso, '='), (4., m_devi, ratio_devi, '='), (6., m_clvd, ratio_clvd, '+'), (8., m_dc, ratio_dc, None)]: if ratio > 1e-4: try: beachball.plot_beachball_mpl( mt_part, axes, beachball_type='full', position=(1. + xpos, ypos), size=0.9 * size0 * math.sqrt(ratio), size_units='data', color_t=color_t, linewidth=1.0) except beachball.BeachballError as e: logger.warn(str(e)) axes.annotate( 'ERROR', xy=(1. + xpos, ypos), ha='center', va='center', color='red', fontsize=fontsize) else: axes.annotate( 'N/A', xy=(1. + xpos, ypos), ha='center', va='center', color='black', fontsize=fontsize) if ops is not None: axes.annotate( ops, xy=(2. + xpos, ypos), ha='center', va='center', color='black', fontsize=fontsize) axes.axison = False axes.set_xlim(-2.25, 9.75) axes.set_ylim(-0.5, nlines_max+0.5) item = PlotItem(name='main') return [[item, fig]]
class JointparPlot(PlotConfig): ''' Source problem parameter's tradeoff plots. ''' name = 'jointpar' size_cm = Tuple.T(2, Float.T(), default=(20., 20.)) misfit_cutoff = Float.T(optional=True) ibootstrap = Int.T(optional=True) color_parameter = String.T(default='misfit') exclude = List.T(String.T()) include = List.T(String.T()) show_ellipses = Bool.T(default=False) nsubplots = Int.T(default=6) show_ticks = Bool.T(default=False) show_reference = Bool.T(default=True) def make(self, environ): cm = environ.get_plot_collection_manager() history = environ.get_history(subset='harvest') optimiser = environ.get_optimiser() sref = 'Dark gray boxes mark reference solution.' \ if self.show_reference else '' mpl_init(fontsize=self.font_size) cm.create_group_mpl( self, self.draw_figures(history, optimiser), title=u'Jointpar Plot', section='solution', feather_icon='crosshair', description=u''' Source problem parameter's scatter plots, to evaluate the resolution of source parameters and possible trade-offs between pairs of model parameters. A subset of model solutions (from harvest) is shown in two dimensions for all possible parameter pairs as points. The point color indicates the misfit for the model solution with cold colors (blue) for high misfit models and warm colors (red) for low misfit models. The plot ranges are defined by the given parameter bounds and shows the model space of the optimsation. %s''' % sref) def draw_figures(self, history, optimiser): color_parameter = self.color_parameter exclude = self.exclude include = self.include nsubplots = self.nsubplots figsize = self.size_inch ibootstrap = 0 if self.ibootstrap is None else self.ibootstrap misfit_cutoff = self.misfit_cutoff show_ellipses = self.show_ellipses msize = 1.5 cmap = 'coolwarm' problem = history.problem if not problem: return [] models = history.models exclude = list(exclude) bounds = problem.get_combined_bounds() for ipar in range(problem.ncombined): par = problem.combined[ipar] lo, hi = bounds[ipar] if lo == hi: exclude.append(par.name) xref = problem.get_reference_model() isort = history.get_sorted_misfits_idx(chain=ibootstrap)[::-1] models = history.get_sorted_models(chain=ibootstrap)[::-1] nmodels = history.nmodels gms = history.get_sorted_misfits(chain=ibootstrap)[::-1] if misfit_cutoff is not None: ibest = gms < misfit_cutoff gms = gms[ibest] models = models[ibest] kwargs = {} if color_parameter == 'dist': mx = num.mean(models, axis=0) cov = num.cov(models.T) mdists = core.mahalanobis_distance(models, mx, cov) icolor = meta.ordersort(mdists) elif color_parameter == 'misfit': iorder = num.arange(nmodels) icolor = iorder elif color_parameter in problem.parameter_names: ind = problem.name_to_index(color_parameter) icolor = problem.extract(models, ind) elif color_parameter in history.attribute_names: icolor = history.get_attribute(color_parameter)[isort] icolor_need = num.unique(icolor) colors = [] for i in range(icolor_need[-1]+1): colors.append(mpl_graph_color(i)) cmap = mcolors.ListedColormap(colors) cmap.set_under(mpl_color('aluminium3')) kwargs.update(dict(vmin=0, vmax=icolor_need[-1])) else: raise meta.GrondError( 'Invalid color_parameter: %s' % color_parameter) smap = {} iselected = 0 for ipar in range(problem.ncombined): par = problem.combined[ipar] if exclude and par.name in exclude or \ include and par.name not in include: continue smap[iselected] = ipar iselected += 1 nselected = iselected if nselected < 2: logger.warn('Cannot draw joinpar figures with less than two ' 'parameters selected.') return [] nfig = (nselected - 2) // nsubplots + 1 figs = [] for ifig in range(nfig): figs_row = [] for jfig in range(nfig): if ifig >= jfig: item = PlotItem(name='fig_%i_%i' % (ifig, jfig)) item.attributes['parameters'] = [] figs_row.append((item, plt.figure(figsize=figsize))) else: figs_row.append(None) figs.append(figs_row) for iselected in range(nselected): ipar = smap[iselected] ypar = problem.combined[ipar] for jselected in range(iselected): jpar = smap[jselected] xpar = problem.combined[jpar] ixg = (iselected - 1) iyg = jselected ix = ixg % nsubplots iy = iyg % nsubplots ifig = ixg // nsubplots jfig = iyg // nsubplots aind = (nsubplots, nsubplots, (ix * nsubplots) + iy + 1) item, fig = figs[ifig][jfig] tlist = item.attributes['parameters'] if xpar.name not in tlist: tlist.append(xpar.name) if ypar.name not in tlist: tlist.append(ypar.name) axes = fig.add_subplot(*aind) axes.axvline(0., color=mpl_color('aluminium3'), lw=0.5) axes.axhline(0., color=mpl_color('aluminium3'), lw=0.5) for spine in axes.spines.values(): spine.set_edgecolor(mpl_color('aluminium5')) spine.set_linewidth(0.5) xmin, xmax = fixlim(*xpar.scaled(bounds[jpar])) ymin, ymax = fixlim(*ypar.scaled(bounds[ipar])) if ix == 0 or jselected + 1 == iselected: for (xpos, xoff, x) in [ (0.0, 10., xmin), (1.0, -10., xmax)]: axes.annotate( '%.3g%s' % (x, xpar.get_unit_suffix()), xy=(xpos, 1.05), xycoords='axes fraction', xytext=(xoff, 5.), textcoords='offset points', verticalalignment='bottom', horizontalalignment='left', rotation=45.) if iy == nsubplots - 1 or jselected + 1 == iselected: for (ypos, yoff, y) in [ (0., 10., ymin), (1.0, -10., ymax)]: axes.annotate( '%.3g%s' % (y, ypar.get_unit_suffix()), xy=(1.0, ypos), xycoords='axes fraction', xytext=(5., yoff), textcoords='offset points', verticalalignment='bottom', horizontalalignment='left', rotation=45.) axes.set_xlim(xmin, xmax) axes.set_ylim(ymin, ymax) if not self.show_ticks: axes.get_xaxis().set_ticks([]) axes.get_yaxis().set_ticks([]) else: axes.tick_params(length=4, which='both') axes.get_yaxis().set_ticklabels([]) axes.get_xaxis().set_ticklabels([]) if iselected == nselected - 1 or ix == nsubplots - 1: axes.annotate( xpar.get_label(with_unit=False), xy=(0.5, -0.05), xycoords='axes fraction', verticalalignment='top', horizontalalignment='right', rotation=45.) if iy == 0: axes.annotate( ypar.get_label(with_unit=False), xy=(-0.05, 0.5), xycoords='axes fraction', verticalalignment='top', horizontalalignment='right', rotation=45.) fx = problem.extract(models, jpar) fy = problem.extract(models, ipar) axes.scatter( xpar.scaled(fx), ypar.scaled(fy), c=icolor, s=msize, alpha=0.5, cmap=cmap, edgecolors='none', **kwargs) if show_ellipses: cov = num.cov((xpar.scaled(fx), ypar.scaled(fy))) evals, evecs = eigh_sorted(cov) evals = num.sqrt(evals) ell = patches.Ellipse( xy=( num.mean(xpar.scaled(fx)), num.mean(ypar.scaled(fy))), width=evals[0] * 2, height=evals[1] * 2, angle=num.rad2deg( num.arctan2(evecs[1][0], evecs[0][0]))) ell.set_facecolor('none') axes.add_artist(ell) if self.show_reference: fx = problem.extract(xref, jpar) fy = problem.extract(xref, ipar) ref_color = mpl_color('aluminium6') ref_color_light = 'none' axes.plot( xpar.scaled(fx), ypar.scaled(fy), 's', mew=1.5, ms=5, mfc=ref_color_light, mec=ref_color) figs_flat = [] for figs_row in figs: figs_flat.extend( item_fig for item_fig in figs_row if item_fig is not None) return figs_flat
class Parameter(Object): name__ = String.T() unit = Unicode.T(optional=True) scale_factor = Float.T(default=1., optional=True) scale_unit = Unicode.T(optional=True) label = Unicode.T(optional=True) optional = Bool.T(default=True, optional=True) def __init__(self, *args, **kwargs): if len(args) >= 1: kwargs['name'] = args[0] if len(args) >= 2: kwargs['unit'] = newstr(args[1]) self.groups = [None] self._name = None Object.__init__(self, **kwargs) def get_label(self, with_unit=True): lbl = [self.label or self.name] if with_unit: unit = self.get_unit_label() if unit: lbl.append('[%s]' % unit) return ' '.join(lbl) def set_groups(self, groups): if not isinstance(groups, list): raise AttributeError('Groups must be a list of strings.') self.groups = groups def _get_name(self): if None not in self.groups: return '%s.%s' % ('.'.join(self.groups), self._name) return self._name def _set_name(self, value): self._name = value name = property(_get_name, _set_name) @property def name_nogroups(self): return self._name def get_value_label(self, value, format='%(value)g%(unit)s'): value = self.scaled(value) unit = self.get_unit_suffix() return format % dict(value=value, unit=unit) def get_unit_label(self): if self.scale_unit is not None: return self.scale_unit elif self.unit: return self.unit else: return None def get_unit_suffix(self): unit = self.get_unit_label() if not unit: return '' else: return ' %s' % unit def scaled(self, x): if isinstance(x, tuple): return tuple(v / self.scale_factor for v in x) if isinstance(x, list): return list(v / self.scale_factor for v in x) else: return x / self.scale_factor def inv_scaled(self, x): if isinstance(x, tuple): return tuple(v * self.scale_factor for v in x) if isinstance(x, list): return list(v * self.scale_factor for v in x) else: return x * self.scale_factor
class WaveformMisfitTarget(gf.Target, MisfitTarget): flip_norm = Bool.T(default=False) misfit_config = WaveformMisfitConfig.T() can_bootstrap_weights = True def __init__(self, **kwargs): gf.Target.__init__(self, **kwargs) MisfitTarget.__init__(self, **kwargs) self._piggyback_subtargets = [] def string_id(self): return '.'.join(x for x in (self.path, ) + self.codes) @classmethod def get_plot_classes(cls): from . import plot plots = super(WaveformMisfitTarget, cls).get_plot_classes() plots.extend(plot.get_plot_classes()) return plots def get_combined_weight(self): if self._combined_weight is None: w = self.manual_weight for analyser in self.analyser_results.values(): w *= analyser.weight self._combined_weight = num.array([w], dtype=num.float) return self._combined_weight def get_taper_params(self, engine, source): store = engine.get_store(self.store_id) config = self.misfit_config tmin_fit = source.time + store.t(config.tmin, source, self) tmax_fit = source.time + store.t(config.tmax, source, self) if config.fmin > 0.0: tfade = 1.0 / config.fmin else: tfade = 1.0 / config.fmax if config.tfade is None: tfade_taper = tfade else: tfade_taper = config.tfade return tmin_fit, tmax_fit, tfade, tfade_taper def get_backazimuth_for_waveform(self): return backazimuth_for_waveform(self.azimuth, self.codes) def get_freqlimits(self): config = self.misfit_config return (config.fmin / config.ffactor, config.fmin, config.fmax, config.fmax * config.ffactor) def get_pick_shift(self, engine, source): config = self.misfit_config tobs = None tsyn = None ds = self.get_dataset() if config.pick_synthetic_traveltime and config.pick_phasename: store = engine.get_store(self.store_id) tsyn = source.time + store.t(config.pick_synthetic_traveltime, source, self) marker = ds.get_pick(source.name, self.codes[:3], config.pick_phasename) if marker: tobs = marker.tmin return tobs, tsyn def get_cutout_timespan(self, tmin, tmax, tfade): if self.misfit_config.fmin > 0: tinc_obs = 1.0 / self.misfit_config.fmin else: tinc_obs = 10.0 / self.misfit_config.fmax tmin_obs = (math.floor((tmin - tfade) / tinc_obs) - 1.0) * tinc_obs tmax_obs = (math.ceil((tmax + tfade) / tinc_obs) + 1.0) * tinc_obs return tmin_obs, tmax_obs def post_process(self, engine, source, tr_syn): tr_syn = tr_syn.pyrocko_trace() nslc = self.codes config = self.misfit_config tmin_fit, tmax_fit, tfade, tfade_taper = \ self.get_taper_params(engine, source) ds = self.get_dataset() tobs, tsyn = self.get_pick_shift(engine, source) if None not in (tobs, tsyn): tobs_shift = tobs - tsyn else: tobs_shift = 0.0 tr_syn.extend(tmin_fit - tfade * 2.0, tmax_fit + tfade * 2.0, fillmethod='repeat') freqlimits = self.get_freqlimits() tr_syn = tr_syn.transfer(freqlimits=freqlimits, tfade=tfade) tr_syn.chop(tmin_fit - 2 * tfade, tmax_fit + 2 * tfade) tmin_obs, tmax_obs = self.get_cutout_timespan(tmin_fit + tobs_shift, tmax_fit + tobs_shift, tfade) try: tr_obs = ds.get_waveform( nslc, tinc_cache=1.0 / (config.fmin or 0.1 * config.fmax), tmin=tmin_fit + tobs_shift - tfade, tmax=tmax_fit + tobs_shift + tfade, tfade=tfade, freqlimits=freqlimits, deltat=tr_syn.deltat, cache=True, backazimuth=self.get_backazimuth_for_waveform()) if tobs_shift != 0.0: tr_obs = tr_obs.copy() tr_obs.shift(-tobs_shift) mr = misfit(tr_obs, tr_syn, taper=trace.CosTaper(tmin_fit - tfade_taper, tmin_fit, tmax_fit, tmax_fit + tfade_taper), domain=config.domain, exponent=config.norm_exponent, flip=self.flip_norm, result_mode=self._result_mode, tautoshift_max=config.tautoshift_max, autoshift_penalty_max=config.autoshift_penalty_max, subtargets=self._piggyback_subtargets) self._piggyback_subtargets = [] mr.tobs_shift = float(tobs_shift) mr.tsyn_pick = float_or_none(tsyn) return mr except NotFound as e: logger.debug(str(e)) raise gf.SeismosizerError('no waveform data, %s' % str(e)) def prepare_modelling(self, engine, source, targets): return [self] def finalize_modelling(self, engine, source, modelling_targets, modelling_results): return modelling_results[0] def get_plain_targets(self, engine, source): d = dict((k, getattr(self, k)) for k in gf.Target.T.propnames) return [gf.Target(**d)] def add_piggyback_subtarget(self, subtarget): self._piggyback_subtargets.append(subtarget)
class DataGeneratorBase(Object): '''This is the base class for all generators. This class to dump and load data to/from all subclasses into TFRecordDatasets. ''' fn_tfrecord = String.T(optional=True) noise = Noise.T(optional=True, help='Add noise to feature') station_dropout_rate = Float.T(default=0., help='Rate by which to mask all channels of station') station_dropout_distribution = Bool.T(default=True, help='If *true*, station dropout will be drawn from a uniform ' 'distribution limited by this station_dropout.') nmax = Int.T(optional=True) labeled = Bool.T(default=True) blacklist = List.T(optional=True, help='List of indices to ignore.') random_seed = Int.T(default=0) def __init__(self, *args, **kwargs): self.config = kwargs.pop('config', None) super().__init__(**kwargs) self.blacklist = set() if not self.blacklist else set(self.blacklist) self.n_classes = self.config.n_classes self.evolution = 0 def normalize_label(self, label): if self.labeled: return self.config.normalize_label(label) return label def set_config(self, pinky_config): self.config = pinky_config self.setup() def setup(self): ... def reset(self): self.evolution = 0 @property def tensor_shape(self): return self.config.tensor_shape @property def n_samples(self): return self.config._n_samples @n_samples.setter def n_samples(self, v): self.config._n_samples = v @property @lru_cache(maxsize=1) def nsl_to_indices(self): ''' Returns a dictionary which maps nsl codes to indexing arrays.''' indices = OrderedDict() for nslc, index in self.nslc_to_index.items(): key = nslc[:3] _v = indices.get(key, []) _v.append(index) indices[key] = _v for k in indices.keys(): indices[k] = num.array(indices[k]) return indices @property @lru_cache(maxsize=1) def nsl_indices(self): ''' Returns a 2D array of indices of channels belonging to one station.''' return [v for v in self.nsl_to_indices.values()] @property def nslc_to_index(self): ''' Returns a dictionary which maps nslc codes to trace indices.''' d = OrderedDict() idx = 0 for nslc in self.config.channels: if not util.match_nslc(self.config.blacklist, nslc): d[nslc] = idx idx += 1 return d def reject_blacklisted(self, tr): '''returns `False` if nslc codes of `tr` match any of the blacklisting patters. Otherwise returns `True`''' return not util.match_nslc(self.config.blacklist, tr.nslc_id) def filter_iter(self, iterator): '''Apply *blacklist*ing by example indices :param iterator: producing iterator ''' for i, item in enumerate(iterator): if i not in self.blacklist: yield i, item @property def generate_output_types(self): '''Return data types of features and labels''' return tf.float32, tf.float32 def unpack_examples(self, record_iterator): '''Parse examples stored in TFRecordData to `tf.train.Example`''' for string_record in record_iterator: example = tf.train.Example() example.ParseFromString(string_record) chunk = example.features.feature['data'].bytes_list.value[0] label = example.features.feature['label'].bytes_list.value[0] chunk = num.fromstring(chunk, dtype=num.float32) chunk = chunk.reshape((self.config.n_channels, -1)) label = num.fromstring(label, dtype=num.float32) yield chunk, label @property def tstart_data(self): return None def iter_chunked(self, tinc): # if data has been written to tf records: return self.iter_examples_and_labels() def iter_examples_and_labels(self): '''Subclass this method! Yields: feature, label Chunks that are all NAN will be skipped. ''' record_iterator = tf.python_io.tf_record_iterator( path=self.fn_tfrecord) for chunk, label in self.unpack_examples(record_iterator): if all_NAN(chunk): logger.debug('all NAN. skipping...') continue yield chunk, label def generate_chunked(self, tinc=1): '''Takes the output of `iter_examples_and_labels` and applies post processing (see: `process_chunk`). ''' for i, (chunk, label) in self.filter_iter(self.iter_chunked(tinc)): yield self.process_chunk(chunk), self.normalize_label(label) def generate(self, return_gaps=False): '''Takes the output of `iter_examples_and_labels` and applies post processing (see: `process_chunk`). ''' self.evolution += 1 num.random.seed(self.random_seed + self.evolution) for i, (chunk, label) in self.filter_iter( self.iter_examples_and_labels()): yield self.process_chunk(chunk, return_gaps=return_gaps), self.normalize_label(label) def extract_labels(self): '''Overwrite this method!''' if not self.labeled: return UNLABELED def iter_labels(self): '''Iterate through labels.''' for i, (_, label) in self.filter_iter( self.iter_examples_and_labels()): yield label @property def text_labels(self): '''Returns a list of strings to identify the labels. Overwrite this method for more meaningfull identifiers.''' return ['%i' % (i) for i, d in self.filter_iter(self.iter_examples_and_labels())] def gaps(self): '''Returns a list containing the gaps of each example''' gaps = [] for (_, gap), _ in self.generate(return_gaps=True): gaps.append(gap) return gaps def snrs(self, split_factor): snrs = [] for chunk, _ in self.generate(): snrs.append(snr(chunk, split_factor)) return snrs @property def output_shapes(self): return (self.config.output_shapes) def get_dataset(self): return tf.data.Dataset.from_generator( self.generate, self.generate_output_types, output_shapes=self.output_shapes) def get_chunked_dataset(self, tinc=1.): gen = partial(self.generate_chunked, tinc=tinc) return tf.data.Dataset.from_generator( gen, self.generate_output_types, output_shapes=self.output_shapes) def get_raw_data_chunk(self, shape): '''Return an array of size (Nchannels x Nsamples_max) filled with NANs.''' empty_array = num.empty(shape, dtype=num.float32) empty_array.fill(num.nan) return empty_array def pack_examples(self): '''Serialize Examples to strings.''' for ydata, label in self.iter_examples_and_labels(): yield tf.train.Example( features=tf.train.Features( feature={ 'data': _BytesFeature(ydata.tobytes()), 'label': _BytesFeature(num.array( label, dtype=num.float32).tobytes()), })) def mask(self, chunk, rate): '''For data augmentation: Mask traces in chunks with NaNs. NaNs will be filled by the imputation method provided by the config file. :param rate: probability with which traces are NaN-ed ''' # print(rate) indices = self.nsl_indices a = num.random.random(len(indices)) i = num.where(a < rate)[0] for ii in i: chunk[indices[ii], :] = num.nan def random_trim(self, chunk, margin): '''For data augmentation: Randomly trim examples in time domain with *margin* seconds.''' sample_margin = int(margin / self.config.effective_deltat) nstart = num.random.randint(low=0, high=sample_margin) _, n_samples = self.config.tensor_shape nstop = nstart + n_samples chunk[:, :nstart] = 0. chunk[:, nstop:] = 0. def process_chunk(self, chunk, return_gaps=False): '''Performs preprocessing of data chunks.''' if self.config.t_translation_max: self.random_trim(chunk, self.config.t_translation_max) # add noise if self.noise: self.noise(chunk) # apply normalization self.config.normalization(chunk) # apply station dropout if self.station_dropout_rate: if self.station_dropout_distribution: self.mask(chunk, num.random.uniform( high=self.station_dropout_rate)) else: self.mask(chunk, self.station_dropout_rate) # fill gaps if self.config.imputation: gaps = num.isnan(chunk) chunk[gaps] = self.config.imputation(chunk) if not return_gaps: return chunk else: return chunk, gaps def write(self, directory): '''Write example data to TFRecordDataset using `self.writer`.''' logger.debug('writing TFRecordDataset: %s' % directory) writer = tf.python_io.TFRecordWriter(directory) for ex in self.pack_examples(): writer.write(ex.SerializeToString()) def cleanup(self): '''Remove remaining folders''' delete_if_exists(self.fn_tfrecord)
class SeismosizerData(DataGenerator): fn_sources = String.T( help='filename containing pyrocko.gf.seismosizer.Source instances') fn_targets = String.T( help='filename containing pyrocko.gf.seismosizer.Target instances', optional=True) fn_stations = String.T( help='filename containing pyrocko.model.Station instances. Will be\ converted to Target instances', optional=True) store_id = String.T(optional=True) center_sources = Bool.T( default=False, help='Transform the center of sources to the center of stations') engine = LocalEngine.T() onset_phase = String.T(default='first(p|P)') def setup(self): self.sources = guts.load(filename=self.fn_sources) self.targets = [] if self.fn_targets: self.targets.extend(guts.load(filename=self.fn_targets)) if self.fn_stations: stats = load_stations(self.fn_stations) self.targets.extend(self.cast_stations_to_targets(stats)) if self.store_id: for t in self.targets: t.store_id = self.store_id if self.center_sources: self.move_sources_to_station_center() self.config.channels = [t.codes for t in self.targets] store_ids = [t.store_id for t in self.targets] store_id = set(store_ids) assert len(store_id) == 1, 'More than one store used. Not \ implemented yet' self.store = self.engine.get_store(store_id.pop()) self.sources = filter_oob(self.sources, self.targets, self.store.config) dt = self.config.deltat_want or self.store.config.deltat self.n_samples = int((self.config.sample_length + self.config.tpad) / dt) def move_sources_to_station_center(self): '''Transform the center of sources to the center of stations.''' lat, lon = orthodrome.geographic_midpoint_locations(self.targets) for s in self.sources: s.lat = lat s.lon = lon def cast_stations_to_targets(self, stations): targets = [] channels = 'ENZ' for s in stations: targets.extend( [Target(codes=(s.network, s.station, s.location, c), lat=s.lat, lon=s.lon, elevation=s.elevation,) for c in channels]) return targets def extract_labels(self, source): if not self.labeled: return UNLABELED return (source.north_shift, source.east_shift, source.depth) def iter_examples_and_labels(self): ensure_list(self.sources) ensure_list(self.targets) response = self.engine.process( sources=self.sources, targets=self.targets) for isource, source in enumerate(response.request.sources): traces = [x.trace.pyrocko_trace() for x in \ response.results_list[isource]] for tr in traces: self.preprocess(tr) arrivals = [self.store.t(self.onset_phase, (source.depth, source.distance_to(t))) for t in self.targets] tref = min([a for a in arrivals if a is not None]) chunk = self.get_raw_data_chunk(self.tensor_shape) self.fit_data_into_chunk(traces, chunk=chunk, tref=tref+source.time) label = self.extract_labels(source) yield chunk, label
class GACOSConfig(PluginConfig): grd_filenames = List.T(default=[], help="List of *two* GACOS gridfiles.") applied = Bool.T(default=False, help="Is the correction applied.")
class AutoScaler(Object): ''' Tunable 1D autoscaling based on data range. Instances of this class may be used to determine nice minima, maxima and increments for ax annotations, as well as suitable common exponents for notation. The autoscaling process is guided by the following public attributes: ''' approx_ticks = Float.T( default=7.0, help='Approximate number of increment steps (tickmarks) to generate.') mode = AutoScaleMode.T( default='auto', help='''Mode of operation for auto-scaling.''') exp = Int.T( optional=True, help='If defined, override automatically determined exponent for ' 'notation by the given value.') snap = Bool.T( default=False, help='If set to True, snap output range to multiples of increment. ' 'This parameter has no effect, if mode is set to ``\'off\'``.') inc = Float.T( optional=True, help='If defined, override automatically determined tick increment by ' 'the given value.') space = Float.T( default=0.0, help='Add some padding to the range. The value given, is the fraction ' 'by which the output range is increased on each side. If mode is ' '``\'0-max\'`` or ``\'min-0\'``, the end at zero is kept fixed ' 'at zero. This parameter has no effect if mode is set to ' '``\'off\'``.') exp_factor = Int.T( default=3, help='Exponent of notation is chosen to be a multiple of this value.') no_exp_interval = Tuple.T( 2, Int.T(), default=(-3, 5), help='Range of exponent, for which no exponential notation is a' 'allowed.') def __init__( self, approx_ticks=7.0, mode='auto', exp=None, snap=False, inc=None, space=0.0, exp_factor=3, no_exp_interval=(-3, 5)): ''' Create new AutoScaler instance. The parameters are described in the AutoScaler documentation. ''' Object.__init__( self, approx_ticks=approx_ticks, mode=mode, exp=exp, snap=snap, inc=inc, space=space, exp_factor=exp_factor, no_exp_interval=no_exp_interval) def make_scale(self, data_range, override_mode=None): ''' Get nice minimum, maximum and increment for given data range. Returns ``(minimum, maximum, increment)`` or ``(maximum, minimum, -increment)``, depending on whether data_range is ``(data_min, data_max)`` or ``(data_max, data_min)``. If ``override_mode`` is defined, the mode attribute is temporarily overridden by the given value. ''' data_min = min(data_range) data_max = max(data_range) is_reverse = (data_range[0] > data_range[1]) a = self.mode if self.mode == 'auto': a = self.guess_autoscale_mode(data_min, data_max) if override_mode is not None: a = override_mode mi, ma = 0, 0 if a == 'off': mi, ma = data_min, data_max elif a == '0-max': mi = 0.0 if data_max > 0.0: ma = data_max else: ma = 1.0 elif a == 'min-0': ma = 0.0 if data_min < 0.0: mi = data_min else: mi = -1.0 elif a == 'min-max': mi, ma = data_min, data_max elif a == 'symmetric': m = max(abs(data_min), abs(data_max)) mi = -m ma = m nmi = mi if (mi != 0. or a == 'min-max') and a != 'off': nmi = mi - self.space*(ma-mi) nma = ma if (ma != 0. or a == 'min-max') and a != 'off': nma = ma + self.space*(ma-mi) mi, ma = nmi, nma if mi == ma and a != 'off': mi -= 1.0 ma += 1.0 # make nice tick increment if self.inc is not None: inc = self.inc else: if self.approx_ticks > 0.: inc = nice_value((ma-mi) / self.approx_ticks) else: inc = nice_value((ma-mi)*10.) if inc == 0.0: inc = 1.0 # snap min and max to ticks if this is wanted if self.snap and a != 'off': ma = inc * math.ceil(ma/inc) mi = inc * math.floor(mi/inc) if is_reverse: return ma, mi, -inc else: return mi, ma, inc def make_exp(self, x): '''Get nice exponent for notation of ``x``. For ax annotations, give tick increment as ``x``.''' if self.exp is not None: return self.exp x = abs(x) if x == 0.0: return 0 if 10**self.no_exp_interval[0] <= x <= 10**self.no_exp_interval[1]: return 0 return math.floor(math.log10(x)/self.exp_factor)*self.exp_factor def guess_autoscale_mode(self, data_min, data_max): '''Guess mode of operation, based on data range. Used to map ``'auto'`` mode to ``'0-max'``, ``'min-0'``, ``'min-max'`` or ``'symmetric'``.''' a = 'min-max' if data_min >= 0.0: if data_min < data_max/2.: a = '0-max' else: a = 'min-max' if data_max <= 0.0: if data_max > data_min/2.: a = 'min-0' else: a = 'min-max' if data_min < 0.0 and data_max > 0.0: if abs((abs(data_max)-abs(data_min)) / (abs(data_max)+abs(data_min))) < 0.5: a = 'symmetric' else: a = 'min-max' return a
class DerampConfig(PluginConfig): demean = Bool.T(optional=True, default=True)
class SatelliteTargetDisplacement(PlotConfig): ''' Maps showing surface displacements from satellite and modelled data ''' name = 'satellite' dpi = Int.T( default=250) size_cm = Tuple.T( 2, Float.T(), default=(22., 12.)) colormap = String.T( default='RdBu', help='Colormap for the surface displacements') relative_coordinates = Bool.T( default=False, help='Show relative coordinates, initial location centered at 0N, 0E') fit = StringChoice.T( default='best', choices=['best', 'mean'], help='Show the \'best\' or \'mean\' fits and source model from the' ' ensamble.') show_topo = Bool.T( default=True, help='Drape displacements over the topography.') displacement_unit = StringChoice.T( default='m', choices=['m', 'mm', 'cm', 'rad'], help="Show results in 'm', 'cm', 'mm' or 'rad' for radians.") show_leaf_centres = Bool.T( default=True, help='show the center points of Quadtree leaves') source_outline_color = String.T( default='grey', help='Choose color of source outline from named matplotlib Colors') common_color_scale = Bool.T( default=True, help='Results shown with common color scale for all satellite ' 'data sets (based on the data)') map_limits = Tuple.T( 4, Float.T(), optional=True, help='Overwrite map limits in native coordinates. ' 'Use (xmin, xmax, ymin, ymax)') nticks_x = Int.T( optional=True, help='Number of ticks on the x-axis.') def make(self, environ): cm = environ.get_plot_collection_manager() history = environ.get_history(subset='harvest') optimiser = environ.get_optimiser() ds = environ.get_dataset() environ.setup_modelling() cm.create_group_mpl( self, self.draw_static_fits(ds, history, optimiser), title=u'InSAR Displacements', section='fits', feather_icon='navigation', description=u''' Maps showing subsampled surface displacements as observed, modelled and the residual (observed minus modelled). The displacement values predicted by the orbit-ambiguity ramps are added to the modelled displacements (middle panels). The color shows the LOS displacement values associated with, and the extent of, every quadtree box. The light grey dots show the focal point of pixels combined in the quadtree box. This point corresponds to the position of the modelled data point. The large dark grey dot shows the reference source position. The grey filled box shows the surface projection of the modelled source, with the thick-lined edge marking the upper fault edge. Complete data extent is shown. ''') def draw_static_fits(self, ds, history, optimiser, closeup=False): from pyrocko.orthodrome import latlon_to_ne_numpy problem = history.problem sat_targets = problem.satellite_targets for target in sat_targets: target.set_dataset(ds) if self.fit == 'best': source = history.get_best_source() model = history.get_best_model() elif self.fit == 'mean': source = history.get_mean_source() model = history.get_mean_model() results = problem.evaluate(model, targets=sat_targets) def init_axes(ax, scene, title, last_axes=False): ax.set_title(title, fontsize=self.font_size) ax.tick_params(length=2) if scene.frame.isMeter(): import utm ax.set_xlabel('Easting [km]', fontsize=self.font_size) scale_x = dict(scale=1./km) scale_y = dict(scale=1./km) utm_E, utm_N, utm_zone, utm_zone_letter =\ utm.from_latlon(source.effective_lat, source.effective_lon) scale_x['offset'] = utm_E scale_y['offset'] = utm_N if last_axes: ax.text(0.975, 0.025, 'UTM Zone %d%s' % (utm_zone, utm_zone_letter), va='bottom', ha='right', fontsize=8, alpha=.7, transform=ax.transAxes) ax.set_aspect('equal') elif scene.frame.isDegree(): scale_x = dict(scale=1., suffix='°') scale_y = dict(scale=1., suffix='°') scale_x['offset'] = source.effective_lon scale_y['offset'] = source.effective_lat ax.set_aspect(1./num.cos(source.effective_lat*d2r)) if self.relative_coordinates: scale_x['offset'] = 0. scale_y['offset'] = 0. nticks_x = 4 if abs(scene.frame.llLon) >= 100 else 5 ax.xaxis.set_major_locator(MaxNLocator(self.nticks_x or nticks_x)) ax.yaxis.set_major_locator(MaxNLocator(5)) ax.scale_x = scale_x ax.scale_y = scale_y scale_axes(ax.get_xaxis(), **scale_x) scale_axes(ax.get_yaxis(), **scale_y) def draw_source(ax, scene): if scene.frame.isMeter(): fn, fe = source.outline(cs='xy').T fn -= fn.mean() fe -= fe.mean() elif scene.frame.isDegree(): fn, fe = source.outline(cs='latlon').T fn -= source.effective_lat fe -= source.effective_lon # source is centered ax.scatter(0., 0., color='black', s=3, alpha=.5, marker='o') ax.fill(fe, fn, edgecolor=(0., 0., 0.), facecolor=self.source_outline_color, alpha=0.7) ax.plot(fe[0:2], fn[0:2], 'k', linewidth=1.3) def get_displacement_rgba(displacements, scene, mappable): arr = num.full_like(scene.displacement, fill_value=num.nan) qt = scene.quadtree for syn_v, leaf in zip(displacements, qt.leaves): arr[leaf._slice_rows, leaf._slice_cols] = syn_v arr[scene.displacement_mask] = num.nan if not self.common_color_scale \ and not self.displacement_unit == 'rad': abs_displ = num.abs(displacements).max() mappable.set_clim(-abs_displ, abs_displ) if self.show_topo: try: elevation = scene.get_elevation() return drape_displacements(arr, elevation, mappable) except Exception as e: logger.warning('could not plot hillshaded topo') logger.exception(e) return mappable.to_rgba(arr) def draw_leaves(ax, scene, offset_e=0., offset_n=0.): rects = scene.quadtree.getMPLRectangles() for r in rects: r.set_edgecolor((.4, .4, .4)) r.set_linewidth(.5) r.set_facecolor('none') r.set_x(r.get_x() - offset_e) r.set_y(r.get_y() - offset_n) map(ax.add_artist, rects) if self.show_leaf_centres: ax.scatter(scene.quadtree.leaf_coordinates[:, 0] - offset_e, scene.quadtree.leaf_coordinates[:, 1] - offset_n, s=.25, c='black', alpha=.1) def add_arrow(ax, scene): phi = num.nanmean(scene.phi) los_dx = num.cos(phi + num.pi) * .0625 los_dy = num.sin(phi + num.pi) * .0625 az_dx = num.cos(phi - num.pi/2) * .125 az_dy = num.sin(phi - num.pi/2) * .125 anchor_x = .9 if los_dx < 0 else .1 anchor_y = .85 if los_dx < 0 else .975 az_arrow = patches.FancyArrow( x=anchor_x-az_dx, y=anchor_y-az_dy, dx=az_dx, dy=az_dy, head_width=.025, alpha=.5, fc='k', head_starts_at_zero=False, length_includes_head=True, transform=ax.transAxes) los_arrow = patches.FancyArrow( x=anchor_x-az_dx/2, y=anchor_y-az_dy/2, dx=los_dx, dy=los_dy, head_width=.02, alpha=.5, fc='k', head_starts_at_zero=False, length_includes_head=True, transform=ax.transAxes) ax.add_artist(az_arrow) ax.add_artist(los_arrow) urE, urN, llE, llN = (0., 0., 0., 0.) for target in sat_targets: if target.scene.frame.isMeter(): off_n, off_e = map(float, latlon_to_ne_numpy( target.scene.frame.llLat, target.scene.frame.llLon, source.effective_lat, source.effective_lon)) if target.scene.frame.isDegree(): off_n = source.effective_lat - target.scene.frame.llLat off_e = source.effective_lon - target.scene.frame.llLon turE, turN, tllE, tllN = zip( *[(leaf.gridE.max()-off_e, leaf.gridN.max()-off_n, leaf.gridE.min()-off_e, leaf.gridN.min()-off_n) for leaf in target.scene.quadtree.leaves]) turE, turN = map(max, (turE, turN)) tllE, tllN = map(min, (tllE, tllN)) urE, urN = map(max, ((turE, urE), (urN, turN))) llE, llN = map(min, ((tllE, llE), (llN, tllN))) def generate_plot(sat_target, result, ifig): scene = sat_target.scene fig = plt.figure() fig.set_size_inches(*self.size_inch) gs = gridspec.GridSpec( 2, 3, wspace=.15, hspace=.2, left=.1, right=.975, top=.95, height_ratios=[12, 1]) item = PlotItem( name='fig_%i' % ifig, attributes={'targets': [sat_target.path]}, title=u'Satellite Surface Displacements - %s' % scene.meta.scene_title, description=u''' Surface displacements derived from satellite data. (Left) the input data, (center) the modelled data and (right) the model residual. ''') stat_obs = result.statics_obs['displacement.los'] stat_syn = result.statics_syn['displacement.los'] res = stat_obs - stat_syn if scene.frame.isMeter(): offset_n, offset_e = map(float, latlon_to_ne_numpy( scene.frame.llLat, scene.frame.llLon, source.effective_lat, source.effective_lon)) elif scene.frame.isDegree(): offset_n = source.effective_lat - scene.frame.llLat offset_e = source.effective_lon - scene.frame.llLon im_extent = ( scene.frame.E.min() - offset_e, scene.frame.E.max() - offset_e, scene.frame.N.min() - offset_n, scene.frame.N.max() - offset_n) if self.displacement_unit == 'rad': wavelength = scene.meta.wavelength if wavelength is None: raise AttributeError( 'The satellite\'s wavelength is not set') stat_obs = displ2rad(stat_obs, wavelength) stat_syn = displ2rad(stat_syn, wavelength) res = displ2rad(res, wavelength) self.colormap = 'hsv' data_range = (0., num.pi) else: abs_displ = num.abs([stat_obs.min(), stat_obs.max(), stat_syn.min(), stat_syn.max(), res.min(), res.max()]).max() data_range = (-abs_displ, abs_displ) cmw = cm.ScalarMappable(cmap=self.colormap) cmw.set_clim(*data_range) cmw.set_array(stat_obs) axes = [fig.add_subplot(gs[0, 0]), fig.add_subplot(gs[0, 1]), fig.add_subplot(gs[0, 2])] ax = axes[0] ax.imshow( get_displacement_rgba(stat_obs, scene, cmw), extent=im_extent, origin='lower') draw_leaves(ax, scene, offset_e, offset_n) draw_source(ax, scene) add_arrow(ax, scene) init_axes(ax, scene, 'Observed') ax.text(.025, .025, 'Scene ID: %s' % scene.meta.scene_id, fontsize=8, alpha=.7, va='bottom', transform=ax.transAxes) if scene.frame.isMeter(): ax.set_ylabel('Northing [km]', fontsize=self.font_size) ax = axes[1] ax.imshow( get_displacement_rgba(stat_syn, scene, cmw), extent=im_extent, origin='lower') draw_leaves(ax, scene, offset_e, offset_n) draw_source(ax, scene) add_arrow(ax, scene) init_axes(ax, scene, 'Model') ax.get_yaxis().set_visible(False) ax = axes[2] ax.imshow( get_displacement_rgba(res, scene, cmw), extent=im_extent, origin='lower') draw_leaves(ax, scene, offset_e, offset_n) draw_source(ax, scene) add_arrow(ax, scene) init_axes(ax, scene, 'Residual', last_axes=True) ax.get_yaxis().set_visible(False) for ax in axes: ax.set_xlim(*im_extent[:2]) ax.set_ylim(*im_extent[2:]) if closeup: if scene.frame.isMeter(): fn, fe = source.outline(cs='xy').T elif scene.frame.isDegree(): fn, fe = source.outline(cs='latlon').T fn -= source.effective_lat fe -= source.effective_lon if fn.size > 1: off_n = (fn[0] + fn[1]) / 2 off_e = (fe[0] + fe[1]) / 2 else: off_n = fn[0] off_e = fe[0] fault_size = 2*num.sqrt(max(abs(fn-off_n))**2 + max(abs(fe-off_e))**2) fault_size *= self.map_scale if fault_size == 0.0: extent = (scene.frame.N[-1] + scene.frame.E[-1]) / 2 fault_size = extent * .25 for ax in axes: ax.set_xlim(-fault_size/2 + off_e, fault_size/2 + off_e) ax.set_ylim(-fault_size/2 + off_n, fault_size/2 + off_n) if self.map_limits is not None: xmin, xmax, ymin, ymax = self.map_limits assert xmin < xmax, 'bad map_limits xmin > xmax' assert ymin < ymax, 'bad map_limits ymin > ymax' for ax in axes: ax.set_xlim( xmin/ax.scale_x['scale'] - ax.scale_x['offset'], xmax/ax.scale_x['scale'] - ax.scale_x['offset'],) ax.set_ylim( ymin/ax.scale_y['scale'] - ax.scale_y['offset'], ymax/ax.scale_y['scale'] - ax.scale_y['offset']) if self.displacement_unit == 'm': def cfmt(x, p): return '%.2f' % x elif self.displacement_unit == 'cm': def cfmt(x, p): return '%.1f' % (x * 1e2) elif self.displacement_unit == 'mm': def cfmt(x, p): return '%.0f' % (x * 1e3) elif self.displacement_unit == 'rad': def cfmt(x, p): return '%.2f' % x else: raise AttributeError( 'unknown displacement unit %s' % self.displacement_unit) cbar_args = dict( orientation='horizontal', format=FuncFormatter(cfmt), use_gridspec=True) cbar_label = 'LOS Displacement [%s]' % self.displacement_unit if self.common_color_scale: cax = fig.add_subplot(gs[1, 1]) cax.set_aspect(.05) cbar = fig.colorbar(cmw, cax=cax, **cbar_args) cbar.set_label(cbar_label) else: for idata, data in enumerate((stat_syn, stat_obs, res)): cax = fig.add_subplot(gs[1, idata]) cax.set_aspect(.05) if not self.displacement_unit == 'rad': abs_displ = num.abs(data).max() cmw.set_clim(-abs_displ, abs_displ) cbar = fig.colorbar(cmw, cax=cax, **cbar_args) cbar.set_label(cbar_label) return (item, fig) for ifig, (sat_target, result) in enumerate(zip(sat_targets, results)): yield generate_plot(sat_target, result, ifig)
class PlotSettings(Object): trace_filename = String.T(help='filename of beam or trace to use for ' 'plotting, incl. path.', optional=True) station_filename = String.T(help='filename containing station meta ' 'information related to *trace_filename*.', optional=True) event_filename = String.T(help='filename containing event information ' 'including the expected moment tensor.', default='event.pf') store_id = String.T(help='Store ID to use for generating the synthetic ' 'traces.', optional=True) store_superdirs = List.T(String.T(), optional=True) depth = Float.T(help='Depth [km] where to put the trace.', default=10.) depths = String.T(help='Synthetic source depths [km]. start:stop:delta. ' 'default: 0:15:1', optional=True, default='0:15:1') filters = List.T( FrequencyResponse.T(help='List of filters used to filter ' 'the traces')) zoom = List.T(Float.T(), help='Window to visualize with reference to the P ' 'phase onset [s].', default=[-7, 15]) correction = Float.T(help='time shift, to move beam trace.', default=0.) normalize = Bool.T(help='normalize by maximum amplitude', default=True) save_as = String.T(default='depth_%(array-id)s.png', help='filename of output figure') force_nearest_neighbor = Bool.T(help='Handles OutOfBounds exceptions. ' 'applies only laterally!', default=False) auto_caption = Bool.T( help='Add a caption giving basic information to the figure.', default=False) title = String.T(default='%(array-id)s - %(event_name)s', help='Add default title.') quantity = String.T(default='velocity', help='velocity-> differentiate synthetic.' 'displacement-> integrate recorded') gain = Float.T(default=1., help='Gain factor') gain_record = Float.T(default=1., help='Gain factor') color = String.T(help='Trace color', default='blue') def update_from_args(self, args): kwargs = {} try: hp, lp = args.filter.split(':') filters = [ ButterworthResponse(corner=float(lp), order=4, type='low'), ButterworthResponse(corner=float(hp), order=4, type='high') ] kwargs.update({'filters': filters}) except: pass kwargs.update(self.process_arglist(args)) for arg, v in kwargs.items(): setattr(self, arg, v) @classmethod def from_argument_parser(cls, args): try: hp, lp = args.filter.split(':') except AttributeError: hp, lp = (0.7, 4.5) filters = [ ButterworthResponse(corner=float(lp), order=4, type='low'), ButterworthResponse(corner=float(hp), order=4, type='high') ] kwargs = cls.process_arglist(args) return cls(filters=filters, **kwargs) def do_filter(self, tr): for f in self.filters: tr = tr.transfer(transfer_function=f, tfade=10, cut_off_fading=False) return tr @staticmethod def process_arglist(args): kwargs = {} for arg in arglist: try: val = getattr(args, arg) if arg == 'zoom' and val: val = val.split(':') val = map(float, val) if val: kwargs.update({arg: val}) except AttributeError: logger.debug('%s not defined' % arg) continue return kwargs
class SatelliteTargetDisplacement(PlotConfig): ''' Maps showing surface displacements from satellite and modelled data ''' name = 'satellite' dpi = Int.T(default=250) size_cm = Tuple.T(2, Float.T(), default=(22., 12.)) colormap = String.T(default='RdBu', help='Colormap for the surface displacements') relative_coordinates = Bool.T( default=False, help='Show relative coordinates, initial location centered at 0N, 0E') def make(self, environ): cm = environ.get_plot_collection_manager() history = environ.get_history(subset='harvest') optimiser = environ.get_optimiser() ds = environ.get_dataset() environ.setup_modelling() cm.create_group_mpl(self, self.draw_static_fits(ds, history, optimiser), title=u'InSAR Displacements', section='fits', feather_icon='navigation', description=u''' Maps showing subsampled surface displacements as observed, modelled and the residual (observed minus modelled). The displacement values predicted by the orbit-ambiguity ramps are added to the modelled displacements (middle panels). The color shows the LOS displacement values associated with, and the extent of, every quadtree box. The light grey dots show the focal point of pixels combined in the quadtree box. This point corresponds to the position of the modelled data point. The large dark grey dot shows the reference source position. The grey filled box shows the surface projection of the modelled source, with the thick-lined edge marking the upper fault edge. Complete data extent is shown. ''') def draw_static_fits(self, ds, history, optimiser, closeup=False): from pyrocko.orthodrome import latlon_to_ne_numpy problem = history.problem sat_targets = problem.satellite_targets for target in sat_targets: target.set_dataset(ds) source = history.get_best_source() best_model = history.get_best_model() results = problem.evaluate(best_model, targets=sat_targets) def initAxes(ax, scene, title, last_axes=False): ax.set_title(title) ax.tick_params(length=2) if scene.frame.isMeter(): ax.set_xlabel('Easting [km]') scale_x = {'scale': 1. / km} scale_y = {'scale': 1. / km} if not self.relative_coordinates: import utm utm_E, utm_N, utm_zone, utm_zone_letter =\ utm.from_latlon(source.effective_lat, source.effective_lon) scale_x['offset'] = utm_E scale_y['offset'] = utm_N if last_axes: ax.text(0.975, 0.025, 'UTM Zone %d%s' % (utm_zone, utm_zone_letter), va='bottom', ha='right', fontsize=8, alpha=.7, transform=ax.transAxes) ax.set_aspect('equal') elif scene.frame.isDegree(): ax.set_xlabel('Lon [°]') scale_x = {'scale': 1.} scale_y = {'scale': 1.} if not self.relative_coordinates: scale_x['offset'] = source.effective_lon scale_y['offset'] = source.effective_lat ax.set_aspect(1. / num.cos(source.effective_lat * d2r)) scale_axes(ax.get_xaxis(), **scale_x) scale_axes(ax.get_yaxis(), **scale_y) def drawSource(ax, scene): if scene.frame.isMeter(): fn, fe = source.outline(cs='xy').T fn -= fn.mean() fe -= fe.mean() elif scene.frame.isDegree(): fn, fe = source.outline(cs='latlon').T fn -= source.effective_lat fe -= source.effective_lon # source is centered ax.scatter(0., 0., color='black', s=3, alpha=.5, marker='o') ax.fill(fe, fn, edgecolor=(0., 0., 0.), facecolor=(.5, .5, .5), alpha=0.7) ax.plot(fe[0:2], fn[0:2], 'k', linewidth=1.3) def mapDisplacementGrid(displacements, scene): arr = num.full_like(scene.displacement, fill_value=num.nan) qt = scene.quadtree for syn_v, l in zip(displacements, qt.leaves): arr[l._slice_rows, l._slice_cols] = syn_v arr[scene.displacement_mask] = num.nan return arr def drawLeaves(ax, scene, offset_e=0., offset_n=0.): rects = scene.quadtree.getMPLRectangles() for r in rects: r.set_edgecolor((.4, .4, .4)) r.set_linewidth(.5) r.set_facecolor('none') r.set_x(r.get_x() - offset_e) r.set_y(r.get_y() - offset_n) map(ax.add_artist, rects) ax.scatter(scene.quadtree.leaf_coordinates[:, 0] - offset_e, scene.quadtree.leaf_coordinates[:, 1] - offset_n, s=.25, c='black', alpha=.1) def addArrow(ax, scene): phi = num.nanmean(scene.phi) los_dx = num.cos(phi + num.pi) * .0625 los_dy = num.sin(phi + num.pi) * .0625 az_dx = num.cos(phi - num.pi / 2) * .125 az_dy = num.sin(phi - num.pi / 2) * .125 anchor_x = .9 if los_dx < 0 else .1 anchor_y = .85 if los_dx < 0 else .975 az_arrow = patches.FancyArrow(x=anchor_x - az_dx, y=anchor_y - az_dy, dx=az_dx, dy=az_dy, head_width=.025, alpha=.5, fc='k', head_starts_at_zero=False, length_includes_head=True, transform=ax.transAxes) los_arrow = patches.FancyArrow(x=anchor_x - az_dx / 2, y=anchor_y - az_dy / 2, dx=los_dx, dy=los_dy, head_width=.02, alpha=.5, fc='k', head_starts_at_zero=False, length_includes_head=True, transform=ax.transAxes) ax.add_artist(az_arrow) ax.add_artist(los_arrow) urE, urN, llE, llN = (0., 0., 0., 0.) for target in sat_targets: if target.scene.frame.isMeter(): off_n, off_e = map( float, latlon_to_ne_numpy(target.scene.frame.llLat, target.scene.frame.llLon, source.effective_lat, source.effective_lon)) if target.scene.frame.isDegree(): off_n = source.effective_lat - target.scene.frame.llLat off_e = source.effective_lon - target.scene.frame.llLon turE, turN, tllE, tllN = zip( *[(l.gridE.max() - off_e, l.gridN.max() - off_n, l.gridE.min() - off_e, l.gridN.min() - off_n) for l in target.scene.quadtree.leaves]) turE, turN = map(max, (turE, turN)) tllE, tllN = map(min, (tllE, tllN)) urE, urN = map(max, ((turE, urE), (urN, turN))) llE, llN = map(min, ((tllE, llE), (llN, tllN))) def generate_plot(sat_target, result, ifig): scene = sat_target.scene fig = plt.figure() fig.set_size_inches(*self.size_inch) gs = gridspec.GridSpec(2, 3, wspace=.15, hspace=.2, left=.1, right=.975, top=.95, height_ratios=[12, 1]) item = PlotItem(name='fig_%i' % ifig, attributes={'targets': [sat_target.path]}, title=u'Satellite Surface Displacements - %s' % scene.meta.scene_title, description=u''' Surface displacements derived from satellite data. (Left) the input data, (center) the modelled data and (right) the model residual. '''.format(meta=scene.meta)) stat_obs = result.statics_obs stat_syn = result.statics_syn['displacement.los'] res = stat_obs - stat_syn if scene.frame.isMeter(): offset_n, offset_e = map( float, latlon_to_ne_numpy(scene.frame.llLat, scene.frame.llLon, source.effective_lat, source.effective_lon)) elif scene.frame.isDegree(): offset_n = source.effective_lat - scene.frame.llLat offset_e = source.effective_lon - scene.frame.llLon im_extent = (scene.frame.E.min() - offset_e, scene.frame.E.max() - offset_e, scene.frame.N.min() - offset_n, scene.frame.N.max() - offset_n) abs_displ = num.abs([ stat_obs.min(), stat_obs.max(), stat_syn.min(), stat_syn.max(), res.min(), res.max() ]).max() cmw = cm.ScalarMappable(cmap=self.colormap) cmw.set_clim(vmin=-abs_displ, vmax=abs_displ) cmw.set_array(stat_obs) axes = [ fig.add_subplot(gs[0, 0]), fig.add_subplot(gs[0, 1]), fig.add_subplot(gs[0, 2]) ] ax = axes[0] ax.imshow(mapDisplacementGrid(stat_obs, scene), extent=im_extent, cmap=self.colormap, vmin=-abs_displ, vmax=abs_displ, origin='lower') drawLeaves(ax, scene, offset_e, offset_n) drawSource(ax, scene) addArrow(ax, scene) initAxes(ax, scene, 'Observed') ax.text(.025, .025, 'Scene ID: %s' % scene.meta.scene_id, fontsize=8, alpha=.7, va='bottom', transform=ax.transAxes) if scene.frame.isDegree(): ax.set_ylabel('Lat [°]') elif scene.frame.isMeter(): ax.set_ylabel('Northing [km]') ax = axes[1] ax.imshow(mapDisplacementGrid(stat_syn, scene), extent=im_extent, cmap=self.colormap, vmin=-abs_displ, vmax=abs_displ, origin='lower') drawLeaves(ax, scene, offset_e, offset_n) drawSource(ax, scene) addArrow(ax, scene) initAxes(ax, scene, 'Model') ax.get_yaxis().set_visible(False) ax = axes[2] ax.imshow(mapDisplacementGrid(res, scene), extent=im_extent, cmap=self.colormap, vmin=-abs_displ, vmax=abs_displ, origin='lower') drawLeaves(ax, scene, offset_e, offset_n) drawSource(ax, scene) addArrow(ax, scene) initAxes(ax, scene, 'Residual', last_axes=True) ax.get_yaxis().set_visible(False) for ax in axes: ax.set_xlim(llE, urE) ax.set_ylim(llN, urN) if closeup: if scene.frame.isMeter(): fn, fe = source.outline(cs='xy').T elif scene.frame.isDegree(): fn, fe = source.outline(cs='latlon').T fn -= source.effective_lat fe -= source.effective_lon if fn.size > 1: off_n = (fn[0] + fn[1]) / 2 off_e = (fe[0] + fe[1]) / 2 else: off_n = fn[0] off_e = fe[0] fault_size = 2 * num.sqrt( max(abs(fn - off_n))**2 + max(abs(fe - off_e))**2) fault_size *= self.map_scale if fault_size == 0.0: extent = (scene.frame.N[-1] + scene.frame.E[-1]) / 2 fault_size = extent * .25 for ax in axes: ax.set_xlim(-fault_size / 2 + off_e, fault_size / 2 + off_e) ax.set_ylim(-fault_size / 2 + off_n, fault_size / 2 + off_n) cax = fig.add_subplot(gs[1, :]) cbar = fig.colorbar(cmw, cax=cax, orientation='horizontal', use_gridspec=True) cbar.set_label('LOS Displacement [m]') return (item, fig) for ifig, (sat_target, result) in enumerate(zip(sat_targets, results)): yield generate_plot(sat_target, result, ifig)
class PhaseRatioTargetGroup(TargetGroup): ''' Generate targets to compare ratios or log ratios of two seismic phases. misfit = | a_obs / (a_obs + b_obs) - a_syn / (a_syn + b_syn) | or misfit = | log(a_obs / (a_obs + b_obs) + waterlevel) - log(a_syn / (a_syn + b_syn) + waterlevel) | ''' distance_min = Float.T(optional=True) distance_max = Float.T(optional=True) distance_3d_min = Float.T(optional=True) distance_3d_max = Float.T(optional=True) depth_min = Float.T(optional=True) depth_max = Float.T(optional=True) measure_a = fm.FeatureMeasure.T() measure_b = fm.FeatureMeasure.T() interpolation = gf.InterpolationMethod.T() store_id = gf.StringID.T(optional=True) store_id_selector = StoreIDSelector.T( optional=True, help='select GF store based on event-station geometry.') fit_log_ratio = Bool.T( default=True, help='If true, compare synthetic and observed log ratios') fit_log_ratio_waterlevel = Float.T( default=0.01, help='Waterlevel added to both ratios when comparing on logarithmic ' 'scale, to avoid log(0)') def get_targets(self, ds, event, default_path='none'): logger.debug('Selecting phase ratio targets...') origin = event targets = [] for st in ds.get_stations(): blacklisted = False for measure in [self.measure_a, self.measure_b]: for cha in measure.channels: if ds.is_blacklisted((st.nsl() + (cha,))): blacklisted = True if self.store_id_selector: store_id = self.store_id_selector.get_store_id( event, st, cha) else: store_id = self.store_id target = PhaseRatioTarget( codes=st.nsl(), lat=st.lat, lon=st.lon, north_shift=st.north_shift, east_shift=st.east_shift, depth=st.depth, interpolation=self.interpolation, store_id=store_id, measure_a=self.measure_a, measure_b=self.measure_b, manual_weight=self.weight, normalisation_family=self.normalisation_family, path=self.path or default_path, backazimuth=0.0, fit_log_ratio=self.fit_log_ratio, fit_log_ratio_waterlevel=self.fit_log_ratio_waterlevel) if blacklisted: log_exclude(target, 'blacklisted') continue if self.distance_min is not None and \ target.distance_to(origin) < self.distance_min: log_exclude(target, 'distance < distance_min') continue if self.distance_max is not None and \ target.distance_to(origin) > self.distance_max: log_exclude(target, 'distance > distance_max') continue if self.distance_3d_min is not None and \ target.distance_3d_to(origin) < self.distance_3d_min: log_exclude(target, 'distance_3d < distance_3d_min') continue if self.distance_3d_max is not None and \ target.distance_3d_to(origin) > self.distance_3d_max: log_exclude(target, 'distance_3d > distance_3d_max') continue if self.depth_min is not None and \ target.depth < self.depth_min: log_exclude(target, 'depth < depth_min') continue if self.depth_max is not None and \ target.depth > self.depth_max: log_exclude(target, 'depth > depth_max') continue bazi, _ = target.azibazi_to(origin) target.backazimuth = bazi target.set_dataset(ds) targets.append(target) return targets
class PileData(DataGenerator): '''Data generator for locally saved data.''' fn_stations = String.T() data_paths = List.T(String.T()) data_format = String.T(default='detect') fn_markers = String.T() fn_events = String.T(optional=True) sort_markers = Bool.T(default=False, help= 'Sorting markers speeds up data io. Shuffled markers \ improve generalization') align_phase = String.T(default='P') tstart = String.T(optional=True) tstop = String.T(optional=True) def setup(self): self.data_pile = pile.make_pile( self.data_paths, fileformat=self.data_format) if self.data_pile.is_empty(): sys.exit('Data pile is empty!') self.deltat_want = self.config.deltat_want or \ min(self.data_pile.deltats.keys()) self.n_samples = int( (self.config.sample_length + self.config.tpad) / self.deltat_want) logger.debug('loading marker file %s' % self.fn_markers) # loads just plain markers: markers = marker.load_markers(self.fn_markers) if self.fn_events: markers.extend( [marker.EventMarker(e) for e in load_events(self.fn_events)]) if self.sort_markers: logger.info('sorting markers!') markers.sort(key=lambda x: x.tmin) marker.associate_phases_to_events(markers) markers_by_nsl = {} for m in markers: if not m.match_nsl(self.config.reference_target.codes[:3]): continue if m.get_phasename().upper() != self.align_phase: continue markers_by_nsl.setdefault(m.one_nslc()[:3], []).append(m) assert(len(markers_by_nsl) == 1) # filter markers that do not have an event assigned: self.markers = list(markers_by_nsl.values())[0] if not self.labeled: dummy_event = Event(lat=0., lon=0., depth=0.) for m in self.markers: if not m.get_event(): m.set_event(dummy_event) self.markers = [m for m in self.markers if m.get_event() is not None] if not len(self.markers): raise Exception('No markers left in dataset') self.config.channels = list(self.data_pile.nslc_ids.keys()) self.config.channels.sort() def check_inputs(self): if len(self.data_pile.deltats()) > 1: logger.warn( 'Different sampling rates in dataset. Preprocessing slow') def extract_labels(self, marker): if not self.labeled: return UNLABELED source = marker.get_event() n, e = orthodrome.latlon_to_ne( self.config.reference_target.lat, self.config.reference_target.lon, source.lat, source.lon) return (n, e, source.depth) @property def tstart_data(self): '''Returns start point of data returned by generator.''' return util.stt(self.tstart) if self.tstart else self.data_pile.tmin def iter_chunked(self, tinc): tr_len = self.n_samples * self.deltat_want nslc_to_index = self.nslc_to_index tpad = self.config.effective_tpad tstart = util.stt(self.tstart) if self.tstart else None tstop = util.stt(self.tstop) if self.tstop else None logger.debug('START') for trs in self.data_pile.chopper( tinc=tinc, tmin=tstart, tmax=tstop, tpad=tpad, keep_current_files_open=True, want_incomplete=False, trace_selector=self.reject_blacklisted): chunk = self.get_raw_data_chunk(self.tensor_shape) if not trs: yield chunk, UNLABELED continue for tr in trs: self.preprocess(tr) indices = [nslc_to_index[tr.nslc_id] for tr in trs] self.fit_data_into_chunk(trs, chunk=chunk, indices=indices, tref=trs[0].tmin) if all_NAN(chunk): logger.debug('all NAN. skipping...') continue yield chunk, UNLABELED def iter_labels(self): for m in self.markers: yield self.extract_labels(m) def iter_examples_and_labels(self): tr_len = self.n_samples * self.deltat_want nslc_to_index = self.nslc_to_index tpad = self.config.effective_tpad for i_m, m in enumerate(self.markers): logger.debug('processig marker %s / %s' % (i_m, len(self.markers))) for trs in self.data_pile.chopper( tmin=m.tmin-tpad, tmax=m.tmax+tr_len+tpad, keep_current_files_open=True, want_incomplete=False, trace_selector=self.reject_blacklisted): for tr in trs: self.preprocess(tr) indices = [nslc_to_index[tr.nslc_id] for tr in trs] chunk = self.get_raw_data_chunk(self.tensor_shape) self.fit_data_into_chunk(trs, chunk=chunk, indices=indices, tref=m.tmin) if all_NAN(chunk): logger.debug('all NAN. skipping...') continue label = self.extract_labels(m) yield chunk, label
class Map(Object): lat = Float.T(optional=True) lon = Float.T(optional=True) radius = Float.T(optional=True) width = Float.T(default=20.) height = Float.T(default=14.) margins = List.T(Float.T()) illuminate = Bool.T(default=True) skip_feature_factor = Float.T(default=0.02) show_grid = Bool.T(default=False) show_topo = Bool.T(default=True) show_topo_scale = Bool.T(default=False) show_center_mark = Bool.T(default=False) show_rivers = Bool.T(default=True) illuminate_factor_land = Float.T(default=0.5) illuminate_factor_ocean = Float.T(default=0.25) color_wet = Tuple.T(3, Int.T(), default=(216, 242, 254)) color_dry = Tuple.T(3, Int.T(), default=(172, 208, 165)) topo_resolution_min = Float.T( default=40., help='minimum resolution of topography [dpi]') topo_resolution_max = Float.T( default=200., help='maximum resolution of topography [dpi]') replace_topo_color_only = FloatTile.T( optional=True, help='replace topo color while keeping topographic shading') topo_cpt_wet = String.T(default='light_sea') topo_cpt_dry = String.T(default='light_land') axes_layout = String.T(optional=True) custom_cities = List.T(City.T()) gmt_config = Dict.T(String.T(), String.T()) comment = String.T(optional=True) def __init__(self, **kwargs): Object.__init__(self, **kwargs) self._gmt = None self._scaler = None self._widget = None self._corners = None self._wesn = None self._minarea = None self._coastline_resolution = None self._rivers = None self._dems = None self._have_topo_land = None self._have_topo_ocean = None self._jxyr = None self._prep_topo_have = None self._labels = [] def save(self, outpath, resolution=75., oversample=2., size=None, width=None, height=None): '''Save the image. Save the image to *outpath*. The format is determined by the filename extension. Formats are handled as follows: ``'.eps'`` and ``'.ps'`` produce EPS and PS, respectively, directly with GMT. If the file name ends with ``'.pdf'``, GMT output is fed through ``gmtpy-epstopdf`` to create a PDF file. For any other filename extension, output is first converted to PDF with ``gmtpy-epstopdf``, then with ``pdftocairo`` to PNG with a resolution oversampled by the factor *oversample* and finally the PNG is downsampled and converted to the target format with ``convert``. The resolution of rasterized target image can be controlled either by *resolution* in DPI or by specifying *width* or *height* or *size*, where the latter fits the image into a square with given side length.''' gmt = self.gmt self.draw_labels() self.draw_axes() if self.show_topo and self.show_topo_scale: self._draw_topo_scale() gmt = self._gmt if outpath.endswith('.eps'): tmppath = gmt.tempfilename() + '.eps' elif outpath.endswith('.ps'): tmppath = gmt.tempfilename() + '.ps' else: tmppath = gmt.tempfilename() + '.pdf' gmt.save(tmppath) if any(outpath.endswith(x) for x in ('.eps', '.ps', '.pdf')): shutil.copy(tmppath, outpath) else: convert_graph(tmppath, outpath, resolution=resolution, oversample=oversample, size=size, width=width, height=height) @property def scaler(self): if self._scaler is None: self._setup_geometry() return self._scaler @property def wesn(self): if self._wesn is None: self._setup_geometry() return self._wesn @property def widget(self): if self._widget is None: self._setup() return self._widget @property def layout(self): if self._layout is None: self._setup() return self._layout @property def jxyr(self): if self._jxyr is None: self._setup() return self._jxyr @property def pxyr(self): if self._pxyr is None: self._setup() return self._pxyr @property def gmt(self): if self._gmt is None: self._setup() if self._have_topo_ocean is None: self._draw_background() return self._gmt def _setup(self): if not self._widget: self._setup_geometry() self._setup_lod() self._setup_gmt() def _setup_geometry(self): wpage, hpage = self.width, self.height ml, mr, mt, mb = self._expand_margins() wpage -= ml + mr hpage -= mt + mb wreg = self.radius * 2.0 hreg = self.radius * 2.0 if wpage >= hpage: wreg *= wpage / hpage else: hreg *= hpage / wpage self._corners = corners(self.lon, self.lat, wreg, hreg) west, east, south, north = extent(self.lon, self.lat, wreg, hreg, 10) x, y, z = ((west, east), (south, north), (-6000., 4500.)) xax = gmtpy.Ax(mode='min-max', approx_ticks=4.) yax = gmtpy.Ax(mode='min-max', approx_ticks=4.) zax = gmtpy.Ax(mode='min-max', inc=1000., label='Height', scaled_unit='km', scaled_unit_factor=0.001) scaler = gmtpy.ScaleGuru(data_tuples=[(x, y, z)], axes=(xax, yax, zax)) par = scaler.get_params() west = par['xmin'] east = par['xmax'] south = par['ymin'] north = par['ymax'] self._wesn = west, east, south, north self._scaler = scaler def _setup_lod(self): w, e, s, n = self._wesn if self.radius > 1500. * km: coastline_resolution = 'i' rivers = False else: coastline_resolution = 'f' rivers = True self._minarea = (self.skip_feature_factor * self.radius / km)**2 self._coastline_resolution = coastline_resolution self._rivers = rivers self._prep_topo_have = {} self._dems = {} cm2inch = gmtpy.cm / gmtpy.inch dmin = 2.0 * self.radius * m2d / (self.topo_resolution_max * (self.height * cm2inch)) dmax = 2.0 * self.radius * m2d / (self.topo_resolution_min * (self.height * cm2inch)) for k in ['ocean', 'land']: self._dems[k] = topo.select_dem_names(k, dmin, dmax, self._wesn) if self._dems[k]: logger.debug('using topography dataset %s for %s' % (','.join(self._dems[k]), k)) def _expand_margins(self): if len(self.margins) == 0 or len(self.margins) > 4: ml = mr = mt = mb = 2.0 elif len(self.margins) == 1: ml = mr = mt = mb = self.margins[0] elif len(self.margins) == 2: ml = mr = self.margins[0] mt = mb = self.margins[1] elif len(self.margins) == 4: ml, mr, mt, mb = self.margins return ml, mr, mt, mb def _setup_gmt(self): w, h = self.width, self.height scaler = self._scaler gmtconf = dict(TICK_PEN='1.25p', TICK_LENGTH='0.2c', ANNOT_FONT_PRIMARY='1', ANNOT_FONT_SIZE_PRIMARY='12p', LABEL_FONT='1', LABEL_FONT_SIZE='12p', CHAR_ENCODING='ISOLatin1+', BASEMAP_TYPE='fancy', PLOT_DEGREE_FORMAT='D', PAPER_MEDIA='Custom_%ix%i' % (w * gmtpy.cm, h * gmtpy.cm), GRID_PEN_PRIMARY='thinnest/0/50/0', DOTS_PR_INCH='1200', OBLIQUE_ANNOTATION='6') gmtconf.update( (k.upper(), v) for (k, v) in self.gmt_config.iteritems()) gmt = gmtpy.GMT(config=gmtconf) layout = gmt.default_layout() layout.set_fixed_margins(*[x * cm for x in self._expand_margins()]) widget = layout.get_widget() # widget['J'] = ('-JT%g/%g' % (self.lon, self.lat)) + '/%(width)gp' # widget['J'] = ('-JA%g/%g' % (self.lon, self.lat)) + '/%(width)gp' widget['P'] = widget['J'] widget['J'] = ('-JA%g/%g' % (self.lon, self.lat)) + '/%(width_m)gm' # widget['J'] = ('-JE%g/%g' % (self.lon, self.lat)) + '/%(width)gp' # scaler['R'] = '-R%(xmin)g/%(xmax)g/%(ymin)g/%(ymax)g' scaler['R'] = '-R%g/%g/%g/%gr' % self._corners aspect = gmtpy.aspect_for_projection(*(widget.J() + scaler.R())) widget.set_aspect(aspect) self._gmt = gmt self._layout = layout self._widget = widget self._jxyr = self._widget.JXY() + self._scaler.R() self._pxyr = self._widget.PXY() + [ '-R%g/%g/%g/%g' % (0, widget.width(), 0, widget.height()) ] self._have_drawn_axes = False self._have_drawn_labels = False def _draw_background(self): self._have_topo_land = False self._have_topo_ocean = False if self.show_topo: self._have_topo = self._draw_topo() self._draw_basefeatures() def _get_topo_tile(self, k): t = None demname = None for dem in self._dems[k]: t = topo.get(dem, self._wesn) demname = dem if t is not None: break if not t: raise NoTopo() return t, demname def _prep_topo(self, k): gmt = self._gmt t, demname = self._get_topo_tile(k) if demname not in self._prep_topo_have: grdfile = gmt.tempfilename() gmtpy.savegrd(t.x(), t.y(), t.data, filename=grdfile, naming='lonlat') if self.illuminate: if k == 'ocean': factor = self.illuminate_factor_ocean else: factor = self.illuminate_factor_land ilumfn = gmt.tempfilename() gmt.grdgradient(grdfile, N='e%g' % factor, A=-45, G=ilumfn, out_discard=True) ilumargs = ['-I%s' % ilumfn] else: ilumargs = [] if self.replace_topo_color_only: t2 = self.replace_topo_color_only grdfile2 = gmt.tempfilename() gmtpy.savegrd(t2.x(), t2.y(), t2.data, filename=grdfile2, naming='lonlat') gmt.grdsample(grdfile2, G=grdfile, Q='l', I='%g/%g' % (t.dx, t.dy), R=grdfile, out_discard=True) gmt.grdmath(grdfile, '0.0', 'AND', '=', grdfile2, out_discard=True) grdfile = grdfile2 self._prep_topo_have[demname] = grdfile, ilumargs return self._prep_topo_have[demname] def _draw_topo(self): widget = self._widget scaler = self._scaler gmt = self._gmt cres = self._coastline_resolution minarea = self._minarea JXY = widget.JXY() R = scaler.R() try: grdfile, ilumargs = self._prep_topo('ocean') gmt.pscoast(D=cres, S='c', A=minarea, *(JXY + R)) gmt.grdimage(grdfile, C=topo.cpt(self.topo_cpt_wet), *(ilumargs + JXY + R)) gmt.pscoast(Q=True, *(JXY + R)) self._have_topo_ocean = True except NoTopo: self._have_topo_ocean = False try: grdfile, ilumargs = self._prep_topo('land') gmt.pscoast(D=cres, G='c', A=minarea, *(JXY + R)) gmt.grdimage(grdfile, C=topo.cpt(self.topo_cpt_dry), *(ilumargs + JXY + R)) gmt.pscoast(Q=True, *(JXY + R)) self._have_topo_land = True except NoTopo: self._have_topo_land = False def _draw_topo_scale(self, label='Elevation [km]'): dry = read_cpt(topo.cpt(self.topo_cpt_dry)) wet = read_cpt(topo.cpt(self.topo_cpt_wet)) combi = cpt_merge_wet_dry(wet, dry) for level in combi.levels: level.vmin /= km level.vmax /= km topo_cpt = self.gmt.tempfilename() write_cpt(combi, topo_cpt) (w, h), (xo, yo) = self.widget.get_size() self.gmt.psscale( D='%gp/%gp/%gp/%gph' % (xo + 0.5 * w, yo - 2.0 * gmtpy.cm, w, 0.5 * gmtpy.cm), C=topo_cpt, B='1:%s:' % label) def _draw_basefeatures(self): gmt = self._gmt cres = self._coastline_resolution rivers = self._rivers minarea = self._minarea color_wet = self.color_wet color_dry = self.color_dry if self.show_rivers and rivers: rivers = ['-Ir/0.25p,%s' % gmtpy.color(self.color_wet)] else: rivers = [] fill = {} if not self._have_topo_land: fill['G'] = color_dry if not self._have_topo_ocean: fill['S'] = color_wet gmt.pscoast(D=cres, W='thinnest,%s' % gmtpy.color(darken(gmtpy.color_tup(color_dry))), A=minarea, *(rivers + self._jxyr), **fill) def _draw_axes(self): gmt = self._gmt scaler = self._scaler widget = self._widget if self.axes_layout is None: if self.lat > 0.0: axes_layout = 'WSen' else: axes_layout = 'WseN' else: axes_layout = self.axes_layout scale_km = gmtpy.nice_value(self.radius / 5.) / 1000. if self.show_center_mark: gmt.psxy(in_rows=[[self.lon, self.lat]], S='c20p', W='2p,black', *self._jxyr) if self.show_grid: btmpl = ('%(xinc)gg%(xinc)g:%(xlabel)s:/' '%(yinc)gg%(yinc)g:%(ylabel)s:') else: btmpl = '%(xinc)g:%(xlabel)s:/%(yinc)g:%(ylabel)s:' gmt.psbasemap(B=(btmpl % scaler.get_params()) + axes_layout, L=('x%gp/%gp/%g/%g/%gk' % (6. / 7 * widget.width(), widget.height() / 7., self.lon, self.lat, scale_km)), *self._jxyr) if self.comment: fontsize = self.gmt.to_points( self.gmt.gmt_config['LABEL_FONT_SIZE']) _, east, south, _ = self._wesn gmt.pstext(in_rows=[[1, 0, fontsize, 0, 0, 'BR', self.comment]], N=True, R=(0, 1, 0, 1), D='%gp/%gp' % (-fontsize * 0.2, fontsize * 0.3), *widget.PXY()) def draw_axes(self): if not self._have_drawn_axes: self._draw_axes() self._have_drawn_axes = True def _have_coastlines(self): gmt = self._gmt cres = self._coastline_resolution minarea = self._minarea checkfile = gmt.tempfilename() gmt.pscoast(M=True, D=cres, W='thinnest/black', A=minarea, out_filename=checkfile, *self._jxyr) with open(checkfile, 'r') as f: for line in f: ls = line.strip() if ls.startswith('#') or ls.startswith('>') or ls == '': continue plon, plat = [float(x) for x in ls.split()] if point_in_region((plon, plat), self._wesn): return True return False def project(self, lats, lons): onepoint = False if isinstance(lats, float) and isinstance(lons, float): lats = [lats] lons = [lons] onepoint = True j, _, _, r = self.jxyr (xo, yo) = self.widget.get_size()[1] f = StringIO() self.gmt.mapproject(j, r, in_columns=(lons, lats), out_stream=f, D='p') f.seek(0) data = num.loadtxt(f, ndmin=2) xs, ys = data.T if onepoint: xs = xs[0] ys = ys[0] return xs, ys def _draw_labels(self): if self._labels: fontsize = self.gmt.to_points( self.gmt.gmt_config['LABEL_FONT_SIZE']) n = len(self._labels) lons, lats, texts, sx, sy, styles = zip(*self._labels) sx = num.array(sx, dtype=num.float) sy = num.array(sy, dtype=num.float) j, _, _, r = self.jxyr f = StringIO() self.gmt.mapproject(j, r, in_columns=(lons, lats), out_stream=f, D='p') f.seek(0) data = num.loadtxt(f, ndmin=2) xs, ys = data.T dxs = num.zeros(n) dys = num.zeros(n) g = gmtpy.GMT() g.psbasemap('-G0', finish=True, *(j, r)) l, b, r, t = g.bbox() h = (t - b) w = (r - l) for i in xrange(n): g = gmtpy.GMT() g.pstext(in_rows=[[0, 0, fontsize, 0, 1, 'BL', texts[i]]], finish=True, R=(0, 1, 0, 1), J='x10p', N=True, **styles[i]) fn = g.tempfilename() g.save(fn) (_, stderr) = Popen([ 'gs', '-q', '-dNOPAUSE', '-dBATCH', '-r720', '-sDEVICE=bbox', fn ], stderr=PIPE).communicate() dx, dy = None, None for line in stderr.splitlines(): if line.startswith('%%HiResBoundingBox:'): l, b, r, t = [float(x) for x in line.split()[-4:]] dx, dy = r - l, t - b dxs[i] = dx dys[i] = dy la = num.logical_and anchors_ok = ( la(xs + sx + dxs < w, ys + sy + dys < h), la(xs - sx - dxs > 0., ys - sy - dys > 0.), la(xs + sx + dxs < w, ys - sy - dys > 0.), la(xs - sx - dxs > 0., ys + sy + dys < h), ) arects = [(xs, ys, xs + sx + dxs, ys + sy + dys), (xs - sx - dxs, ys - sy - dys, xs, ys), (xs, ys - sy - dys, xs + sx + dxs, ys), (xs - sx - dxs, ys, xs, ys + sy + dys)] def no_points_in_rect(xs, ys, xmin, ymin, xmax, ymax): xx = not num.any( la(la(xmin < xs, xs < xmax), la(ymin < ys, ys < ymax))) return xx for i in xrange(n): for ianch in xrange(4): anchors_ok[ianch][i] &= no_points_in_rect( xs, ys, *[xxx[i] for xxx in arects[ianch]]) anchor_choices = [] anchor_take = [] for i in xrange(n): choices = [ ianch for ianch in xrange(4) if anchors_ok[ianch][i] ] anchor_choices.append(choices) if choices: anchor_take.append(choices[0]) else: anchor_take.append(None) def roverlaps(a, b): return (a[0] < b[2] and b[0] < a[2] and a[1] < b[3] and b[1] < a[3]) def cost(anchor_take): noverlaps = 0 for i in xrange(n): for j in xrange(n): if i != j: i_take = anchor_take[i] j_take = anchor_take[j] if i_take is None or j_take is None: continue r_i = [xxx[i] for xxx in arects[i_take]] r_j = [xxx[j] for xxx in arects[j_take]] if roverlaps(r_i, r_j): noverlaps += 1 return noverlaps cur_cost = cost(anchor_take) imax = 30 while cur_cost != 0 and imax > 0: for i in xrange(n): for t in anchor_choices[i]: anchor_take_new = list(anchor_take) anchor_take_new[i] = t new_cost = cost(anchor_take_new) if new_cost < cur_cost: anchor_take = anchor_take_new cur_cost = new_cost imax -= 1 while cur_cost != 0: for i in xrange(n): anchor_take_new = list(anchor_take) anchor_take_new[i] = None new_cost = cost(anchor_take_new) if new_cost < cur_cost: anchor_take = anchor_take_new cur_cost = new_cost break anchor_strs = ['BL', 'TR', 'TL', 'BR'] for i in xrange(n): ianchor = anchor_take[i] if ianchor is not None: anchor = anchor_strs[ianchor] yoff = [-sy[i], sy[i]][anchor[0] == 'B'] xoff = [-sx[i], sx[i]][anchor[1] == 'L'] row = (lons[i], lats[i], fontsize, 0, 1, anchor, texts[i]) self.gmt.pstext(in_rows=[row], D='%gp/%gp' % (xoff, yoff), *self.jxyr, **styles[i]) def draw_labels(self): self.gmt if not self._have_drawn_labels: self._draw_labels() self._have_drawn_labels = True def add_label(self, lat, lon, text, offset_x=5., offset_y=5., style={}): self._labels.append((lon, lat, text, offset_x, offset_y, style)) def cities_in_region(self): from pyrocko import geonames cities = geonames.get_cities_region(region=self.wesn, minpop=0) cities.extend(self.custom_cities) cities.sort(key=lambda x: x.population) return cities def draw_cities( self, exact=None, include=[], exclude=['City of London'], # duplicate entry in geonames nmax_soft=10, psxy_style=dict(S='s5p', G='black')): cities = self.cities_in_region() if exact is not None: cities = [c for c in cities if c.name in exact] minpop = None else: cities = [c for c in cities if c.name not in exclude] minpop = 10**3 for minpop_new in [1e3, 3e3, 1e4, 3e4, 1e5, 3e5, 1e6, 3e6, 1e7]: cities_new = [c for c in cities if c.population > minpop_new] if len(cities_new) == 0 or (len(cities_new) < 3 and len(cities) < nmax_soft * 2): break cities = cities_new minpop = minpop_new if len(cities) <= nmax_soft: break if cities: lats = [c.lat for c in cities] lons = [c.lon for c in cities] self.gmt.psxy(in_columns=(lons, lats), *self.jxyr, **psxy_style) for c in cities: try: text = c.name.encode('iso-8859-1') except UnicodeEncodeError: text = c.asciiname self.add_label(c.lat, c.lon, text) self._cities_minpop = minpop
class ReportInfo(Object): title = Unicode.T(optional=True) description = Unicode.T(optional=True) have_archive = Bool.T(optional=True)
class Model(Object): '''Defines your neural network and the basic training strategy.''' name = String.T(default='unnamed', help='Used to identify the model and runs in summy and checkpoint \ directory') config = SilvertineConfig.T(help='') learning_rate = Float.T(default=1e-3) dropout_rate = Float.T(default=0.01) batch_size = Int.T(default=10) n_epochs = Int.T(default=1) max_steps = Int.T(default=5000) outdir = String.T(default=tempfile.mkdtemp(prefix='Silvertine-')) summary_outdir = String.T(default='summary') summary_nth_step = Int.T(default=1) shuffle_size = Int.T( optional=True, help='if set, shuffle examples at given buffer size.') tf.logging.set_verbosity(tf.logging.INFO) force_dropout = Bool.T(optional=True) layers = List.T(Layer.T(), help='A list of `Layer` instances.') def __init__(self, tf_config=None, **kwargs): ''' ''' super().__init__(**kwargs) if not tf_config: tf_config = tf.ConfigProto() tf_config.gpu_options.allow_growth = True self.sess = tf.Session(config=tf_config) self.debug = logger.getEffectiveLevel() == logging.DEBUG self.est = None self.prefix = None self.tinc_detect = 1. def set_tfconfig(self, tf_config): self.sess.close() self.sess = tf.Session(config=tf_config) def enable_debugger(self): '''wrap session to enable breaking into debugger.''' from tensorflow.python import debug as tf_debug # Attach debugger self.sess = tf_debug.LocalCLIDebugWrapperSession(self.sess) # Attach Tensorboard debugger # self.sess = tf_debug.TensorBoardDebugWrapperSession( # self.sess, "localhost:8080") def extend_path(self, p): '''Append subdirectory to path `p` named by the model.''' if self.prefix: return os.path.join(self.prefix, p, self.name) return os.path.join(p, self.name) def get_summary_outdir(self): '''Return the directory where to store the summary.''' d = self.extend_path(self.summary_outdir) ensure_dir(d) return d def get_outdir(self): '''Return the directory where to store the model.''' return self.extend_path(self.outdir) def get_plot_path(self): '''Return the directory where to store figures.''' d = os.path.join(self.get_summary_outdir(), 'plots') ensure_dir(d) return d def clear(self): '''Delete summary and model directories.''' delete_if_exists(self.get_summary_outdir()) self.clear_model() def denormalize_location(self, items): '''Convert normalized carthesian location to true location.''' return self.config.denormalize_label(items) def clear_model(self): '''model directories.''' delete_if_exists(self.get_outdir()) def generate_eval_dataset(self): '''Generator of evaluation dataset.''' return self.config.evaluation_data_generator.get_dataset().batch( self.batch_size) def generate_eval_dataset_3(self): '''Generator of evaluation dataset.''' return self.config.evaluation_data_generator.get_dataset().batch( self.batch_size).repeat(3) def generate_predict_dataset(self): '''Generator of prediction dataset.''' d = self.config.prediction_data_generator if not d: raise Exception('\nNo prediction data generator defined in config!') return d.get_dataset().batch(self.batch_size) def generate_dataset(self): '''Generator of training dataset.''' dataset = self.config.data_generator.get_dataset() if self.shuffle_size is not None: dataset = dataset.shuffle(buffer_size=self.shuffle_size) return dataset.repeat(count=self.n_epochs).batch(self.batch_size) def generate_detect_dataset(self): '''Generator of prediction dataset.''' d = self.config.prediction_data_generator if not d: raise Exception('\nNo prediction data generator defined in config!') return d.get_chunked_dataset(tinc=self.tinc_detect).prefetch(100) def model(self, features, labels, mode): '''Setup the model, summaries and run training or evaluation.''' training = bool(mode == tf.estimator.ModeKeys.TRAIN) if self.debug: view = features[:3] view = tf.expand_dims(view, -1) tf.summary.image('input', view) with tf.name_scope('input'): input = tf.reshape( features, [-1, *self.config.data_generator.tensor_shape, 1]) input = tf.layers.batch_normalization(input, training=training) level = tf.zeros([1], name='level') for layer in self.layers: logger.debug('chain in layer: %s' % layer) input, level = layer.chain(input=input, level=level, training=training or self.force_dropout, dropout_rate=self.dropout_rate) # Final layer predictions = tf.layers.dense(input, self.config.n_classes, name='output', activation=None) if mode == tf.estimator.ModeKeys.PREDICT: return tf.estimator.EstimatorSpec(mode, predictions = { 'predictions': self.denormalize_location(predictions), 'level': level}) # do not denormalize labels and predictions for loss loss = tf.losses.mean_squared_error(labels, predictions) # transform to carthesian coordiantes labels = self.denormalize_location(labels) predictions = self.denormalize_location(predictions) predictions = tf.transpose(predictions) labels = tf.transpose(labels) errors = predictions - labels abs_errors = tf.abs(errors) variable_summaries(errors[0], 'error_abs_x') variable_summaries(errors[1], 'error_abs_y') variable_summaries(errors[2], 'error_abs_z') loss_carthesian = tf.sqrt(tf.reduce_sum(errors ** 2, axis=0, keepdims=False)) variable_summaries(loss_carthesian, 'training_loss') loss_ = tf.reduce_mean(loss_carthesian) tf.summary.scalar('mean_loss_cart', loss_) tf.summary.scalar('loss_normalized', loss) if mode == tf.estimator.ModeKeys.TRAIN: optimizer = tf.train.AdamOptimizer(self.learning_rate) update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS) with tf.control_dependencies(update_ops): train_op = optimizer.minimize( loss=loss, global_step=tf.train.get_global_step()) logging_hook = tf.train.LoggingTensorHook( {"loss": loss, "step": tf.train.get_global_step()}, every_n_iter=10) return tf.estimator.EstimatorSpec( mode=mode, loss=loss, train_op=train_op, evaluation_hooks=[self.get_summary_hook('eval'),], training_hooks=[ self.get_summary_hook('train'), logging_hook]) elif mode == tf.estimator.ModeKeys.EVAL: metrics = { 'rmse_eval': tf.metrics.root_mean_squared_error( labels=labels, predictions=predictions, name='rmse_eval'), 'mse_eval': tf.metrics.mean_squared_error( labels=labels, predictions=predictions, name='mse_eval'), 'mae_eval': tf.metrics.mean_absolute_error( labels=labels, predictions=predictions, name='mae_eval')} with tf.name_scope('eval'): for k, v in metrics.items(): tf.summary.scalar(k, v[1]) return tf.estimator.EstimatorSpec( mode=mode, loss=loss, eval_metric_ops=metrics, evaluation_hooks=[self.get_summary_hook('eval'),]) def get_summary_hook(self, subdir=''): '''Return a summary hook storing summaries at *subdir*.''' return tf.train.SummarySaverHook( self.summary_nth_step, output_dir=pjoin(self.get_summary_outdir(), subdir), scaffold=tf.train.Scaffold(summary_op=tf.summary.merge_all())) def train_and_evaluate(self): '''Execute training and evaluation and visualize kernels as well as activation maps at the end of all epochs.''' self.save_model_in_summary() with self.sess as default: self.est = tf.estimator.Estimator( model_fn=self.model, model_dir=self.get_outdir()) train_spec = tf.estimator.TrainSpec( input_fn=self.generate_dataset, max_steps=self.max_steps) eval_spec = tf.estimator.EvalSpec( input_fn=self.generate_eval_dataset, steps=None) result = tf.estimator.train_and_evaluate(self.est, train_spec, eval_spec) self.save_kernels() self.save_activation_maps() return result def predict(self): '''Predict locations prediction data generator and store results as a list of carthesian coordinates.''' import time import csv with self.sess as default: self.est = tf.estimator.Estimator( model_fn=self.model, model_dir=self.get_outdir()) predictions = [] for i, p in enumerate(self.est.predict( input_fn=self.generate_predict_dataset, yield_single_examples=False)): if not i: tstart = time.time() predictions.extend(p['predictions']) fn_out = 'predictions.csv' with open(fn_out, 'w') as f: w = csv.writer(f, delimiter=' ') for p in predictions: w.writerow(p) logger.info('This took %1.1f seconds ' % (time.time()-tstart)) logger.info('Saved locations in %s' % fn_out) def detect(self, tinc=None, detector_threshold=1.8): '''Detect events Summarizes the energy of layers in your network that have their *is_detector* flag set to *True*. :param tinc: time increment to step through the dataset. :param detector_threshold: triggers a detection when summed energy exceeds this value. ''' tpeaksearch = 5. self.tinc_detect = tinc or 1.0 fn_detector_trace = 'detector.mseed' fn_detections = 'detections.pf' with self.sess as default: self.est = tf.estimator.Estimator( model_fn=self.model, model_dir=self.get_outdir()) detector_level = [] for ip, p in enumerate(self.est.predict( input_fn=self.generate_detect_dataset, yield_single_examples=True)): detector_level.append(p['level']) print(self.config.prediction_data_generator.tstart_data, self.tinc_detect) tr = trace.Trace( tmin=self.config.prediction_data_generator.tstart_data, ydata=num.array(detector_level), deltat=self.tinc_detect) tpeaks, apeaks = tr.peaks(detector_threshold, tpeaksearch) logger.info('Fount %i detections' % len(tpeaks)) markers = [] for (tpeak, apeak) in zip(tpeaks, apeaks): markers.append( EventMarker(pmodel.Event(time=tpeak, name=str(apeak)))) logger.info('Saving detections: %s' % fn_detections) Marker.save_markers(markers, fn_detections) logger.info('Saving detector level: %s' % fn_detector_trace) io.save([tr], fn_detector_trace) def evaluate_errors(self, n_predict=100, force_dropout=0.333): '''Repeatedly run the prediction for *n_predict* times where the first run is without and the subsequent are with enforced dropout rate *force_dropout*. This is allows to evaluate location accuracy without a given reference catatog of known event locations. It is basically a jacknife applied to the neural network layers. ''' logger.debug('evaluation...') self.dropout_rate = force_dropout with self.sess as default: self.est = tf.estimator.Estimator( model_fn=self.model, model_dir=self.get_outdir()) labels = [l for _, l in self.config.evaluation_data_generator.generate()] labels = self.denormalize_location(num.array(labels)) all_predictions = [] for n in range(n_predict): predictions = [] for p in self.est.predict( input_fn=self.generate_eval_dataset, yield_single_examples=False): predictions.append(p['predictions']) # predictions = self.denormalize_location(num.array(predictions)) all_predictions.append(predictions) # activate dropout after first iteration. hence, first are # 'correct locations' self.force_dropout = True all_predictions = num.array(all_predictions) save_name = pjoin(self.get_plot_path(), 'errors') plot.evaluate_errors( all_predictions, labels, name=save_name) def evaluate(self, annotate=False): ''' Run the evaluation and visualize the results. :param annotate: label all events ''' logger.debug('evaluation...') with self.sess as default: self.est = tf.estimator.Estimator( model_fn=self.model, model_dir=self.get_outdir()) labels = [l for _, l in self.config.evaluation_data_generator.generate()] predictions = [] for p in self.est.predict( input_fn=self.generate_eval_dataset, yield_single_examples=False): predictions.extend(p['predictions']) save_name = pjoin(self.get_plot_path(), 'mislocation') predictions = num.array(predictions) labels = self.denormalize_location(num.array(labels)) text_labels = None if annotate: text_labels = self.config.evaluation_data_generator.text_labels plot.plot_predictions_and_labels( predictions, labels, name=save_name, text_labels=text_labels) save_name = pjoin(self.get_plot_path(), 'mislocation_hists') plot.mislocation_hist(predictions, labels, name=save_name) save_name = pjoin(self.get_plot_path(), 'mislocation_snrs') snrs = self.config.evaluation_data_generator.snrs(split_factor=0.8) plot.mislocation_vs_snr(snrs, predictions, labels, name=save_name) save_name = pjoin(self.get_plot_path(), 'mislocation_vs_gaps') gaps = self.config.evaluation_data_generator.gaps() plot.mislocation_vs_gaps(predictions, labels, gaps, name=save_name) def evaluate_station_dropout(self, start=0., stop=0.8, inc=0.1): ''' Predict for a range of station dropouts and visualize the results. ''' with self.sess as default: self.est = tf.estimator.Estimator( model_fn=self.model, model_dir=self.get_outdir()) labels = [l for _, l in self.config.evaluation_data_generator.generate()] sdropouts = num.linspace(start, stop, inc) results = {} for sdropout in sdropouts: predictions = [] for p in self.est.predict( input_fn=self.generate_eval_dataset, yield_single_examples=False): predictions.extend(p['predictions']) results[sdropout] = predictions plot.mislocation_vs_gaps_many(results, labels, self.config.evaluation_data_generator.gaps(), name=save_name) def train_multi_gpu(self, params=None): ''' Use multiple GPUs for training. Buggy...''' params = params or {} self.training_hooks = [] # saver_hook = tf.train.CheckpointSaverHook() # summary_hook = tf.train.SummarySaverHook() with tf.train.MonitoredSession( # session_creator=tf.train.ChiefSessionCreator(), # hooks=[summary_hook]) as sess: # hooks=[saver_hook, summary_hook]) as sess: ) as sess: distribution = tf.compat.v1.layers.distribute.MirroredStrategy(num_gpus=2) run_config = tf.estimator.RunConfig(train_distribute=distribution) est = tf.estimator.Estimator( model_fn=self.model, model_dir=self.outdir, # params=params, config=run_config) est.train(input_fn=self.generate_dataset) logger.info('====== start evaluation') return est.evaluate(input_fn=self.generate_eval_dataset) def save_kernels(self, index=0): '''save weight kernels of all layers (at index=`index`).''' save_path = pjoin(self.get_summary_outdir(), 'kernels') ensure_dir(save_path) logger.info('Storing weight matrices at %s' % save_path) for layer in self.layers: layer.visualize_kernel(self.est, save_path=save_path) def save_activation_maps(self, index=0): '''Visualizes all activation maps at given *index* of all layers.''' save_path = pjoin(self.get_summary_outdir(), 'kernels') ensure_dir(save_path) # for layer in self.layers: # plot.getActivations(layer, stimuli) def save_model_in_summary(self): '''Dump neural network configuration to summary directory as YAML.''' self.regularize() self.dump(filename=pjoin(self.get_summary_outdir(), 'model.config'))
class Map(Object): lat = Float.T(optional=True) lon = Float.T(optional=True) radius = Float.T(optional=True) width = Float.T(default=20.) height = Float.T(default=14.) margins = List.T(Float.T()) illuminate = Bool.T(default=True) skip_feature_factor = Float.T(default=0.02) show_grid = Bool.T(default=False) show_topo = Bool.T(default=True) show_topo_scale = Bool.T(default=False) show_center_mark = Bool.T(default=False) show_rivers = Bool.T(default=True) show_plates = Bool.T(default=False) illuminate_factor_land = Float.T(default=0.5) illuminate_factor_ocean = Float.T(default=0.25) color_wet = Tuple.T(3, Int.T(), default=(216, 242, 254)) color_dry = Tuple.T(3, Int.T(), default=(172, 208, 165)) topo_resolution_min = Float.T( default=40., help='minimum resolution of topography [dpi]') topo_resolution_max = Float.T( default=200., help='maximum resolution of topography [dpi]') replace_topo_color_only = FloatTile.T( optional=True, help='replace topo color while keeping topographic shading') topo_cpt_wet = String.T(default='light_sea') topo_cpt_dry = String.T(default='light_land') axes_layout = String.T(optional=True) custom_cities = List.T(City.T()) gmt_config = Dict.T(String.T(), String.T()) comment = String.T(optional=True) def __init__(self, gmtversion='newest', **kwargs): Object.__init__(self, **kwargs) self._gmt = None self._scaler = None self._widget = None self._corners = None self._wesn = None self._minarea = None self._coastline_resolution = None self._rivers = None self._dems = None self._have_topo_land = None self._have_topo_ocean = None self._jxyr = None self._prep_topo_have = None self._labels = [] self._area_labels = [] self._gmtversion = gmtversion def save(self, outpath, resolution=75., oversample=2., size=None, width=None, height=None): ''' Save the image. Save the image to ``outpath``. The format is determined by the filename extension. Formats are handled as follows: ``'.eps'`` and ``'.ps'`` produce EPS and PS, respectively, directly with GMT. If the file name ends with ``'.pdf'``, GMT output is fed through ``gmtpy-epstopdf`` to create a PDF file. For any other filename extension, output is first converted to PDF with ``gmtpy-epstopdf``, then with ``pdftocairo`` to PNG with a resolution oversampled by the factor ``oversample`` and finally the PNG is downsampled and converted to the target format with ``convert``. The resolution of rasterized target image can be controlled either by ``resolution`` in DPI or by specifying ``width`` or ``height`` or ``size``, where the latter fits the image into a square with given side length. ''' gmt = self.gmt self.draw_labels() self.draw_axes() if self.show_topo and self.show_topo_scale: self._draw_topo_scale() gmt.save(outpath, resolution=resolution, oversample=oversample, size=size, width=width, height=height) @property def scaler(self): if self._scaler is None: self._setup_geometry() return self._scaler @property def wesn(self): if self._wesn is None: self._setup_geometry() return self._wesn @property def widget(self): if self._widget is None: self._setup() return self._widget @property def layout(self): if self._layout is None: self._setup() return self._layout @property def jxyr(self): if self._jxyr is None: self._setup() return self._jxyr @property def pxyr(self): if self._pxyr is None: self._setup() return self._pxyr @property def gmt(self): if self._gmt is None: self._setup() if self._have_topo_ocean is None: self._draw_background() return self._gmt def _setup(self): if not self._widget: self._setup_geometry() self._setup_lod() self._setup_gmt() def _setup_geometry(self): wpage, hpage = self.width, self.height ml, mr, mt, mb = self._expand_margins() wpage -= ml + mr hpage -= mt + mb wreg = self.radius * 2.0 hreg = self.radius * 2.0 if wpage >= hpage: wreg *= wpage/hpage else: hreg *= hpage/wpage self._wreg = wreg self._hreg = hreg self._corners = corners(self.lon, self.lat, wreg, hreg) west, east, south, north = extent(self.lon, self.lat, wreg, hreg, 10) x, y, z = ((west, east), (south, north), (-6000., 4500.)) xax = gmtpy.Ax(mode='min-max', approx_ticks=4.) yax = gmtpy.Ax(mode='min-max', approx_ticks=4.) zax = gmtpy.Ax(mode='min-max', inc=1000., label='Height', scaled_unit='km', scaled_unit_factor=0.001) scaler = gmtpy.ScaleGuru(data_tuples=[(x, y, z)], axes=(xax, yax, zax)) par = scaler.get_params() west = par['xmin'] east = par['xmax'] south = par['ymin'] north = par['ymax'] self._wesn = west, east, south, north self._scaler = scaler def _setup_lod(self): w, e, s, n = self._wesn if self.radius > 1500.*km: coastline_resolution = 'i' rivers = False else: coastline_resolution = 'f' rivers = True self._minarea = (self.skip_feature_factor * self.radius/km)**2 self._coastline_resolution = coastline_resolution self._rivers = rivers self._prep_topo_have = {} self._dems = {} cm2inch = gmtpy.cm/gmtpy.inch dmin = 2.0 * self.radius * m2d / (self.topo_resolution_max * (self.height * cm2inch)) dmax = 2.0 * self.radius * m2d / (self.topo_resolution_min * (self.height * cm2inch)) for k in ['ocean', 'land']: self._dems[k] = topo.select_dem_names(k, dmin, dmax, self._wesn) if self._dems[k]: logger.debug('using topography dataset %s for %s' % (','.join(self._dems[k]), k)) def _expand_margins(self): if len(self.margins) == 0 or len(self.margins) > 4: ml = mr = mt = mb = 2.0 elif len(self.margins) == 1: ml = mr = mt = mb = self.margins[0] elif len(self.margins) == 2: ml = mr = self.margins[0] mt = mb = self.margins[1] elif len(self.margins) == 4: ml, mr, mt, mb = self.margins return ml, mr, mt, mb def _setup_gmt(self): w, h = self.width, self.height scaler = self._scaler if gmtpy.is_gmt5(self._gmtversion): gmtconf = dict( MAP_TICK_PEN_PRIMARY='1.25p', MAP_TICK_PEN_SECONDARY='1.25p', MAP_TICK_LENGTH_PRIMARY='0.2c', MAP_TICK_LENGTH_SECONDARY='0.6c', FONT_ANNOT_PRIMARY='12p,1,black', FONT_LABEL='12p,1,black', PS_CHAR_ENCODING='ISOLatin1+', MAP_FRAME_TYPE='fancy', FORMAT_GEO_MAP='D', PS_MEDIA='Custom_%ix%i' % ( w*gmtpy.cm, h*gmtpy.cm), PS_PAGE_ORIENTATION='portrait', MAP_GRID_PEN_PRIMARY='thinnest,0/50/0', MAP_ANNOT_OBLIQUE='6') else: gmtconf = dict( TICK_PEN='1.25p', TICK_LENGTH='0.2c', ANNOT_FONT_PRIMARY='1', ANNOT_FONT_SIZE_PRIMARY='12p', LABEL_FONT='1', LABEL_FONT_SIZE='12p', CHAR_ENCODING='ISOLatin1+', BASEMAP_TYPE='fancy', PLOT_DEGREE_FORMAT='D', PAPER_MEDIA='Custom_%ix%i' % ( w*gmtpy.cm, h*gmtpy.cm), GRID_PEN_PRIMARY='thinnest/0/50/0', DOTS_PR_INCH='1200', OBLIQUE_ANNOTATION='6') gmtconf.update( (k.upper(), v) for (k, v) in self.gmt_config.items()) gmt = gmtpy.GMT(config=gmtconf, version=self._gmtversion) layout = gmt.default_layout() layout.set_fixed_margins(*[x*cm for x in self._expand_margins()]) widget = layout.get_widget() widget['P'] = widget['J'] widget['J'] = ('-JA%g/%g' % (self.lon, self.lat)) + '/%(width)gp' scaler['R'] = '-R%g/%g/%g/%gr' % self._corners # aspect = gmtpy.aspect_for_projection( # gmt.installation['version'], *(widget.J() + scaler.R())) aspect = self._map_aspect(jr=widget.J() + scaler.R()) widget.set_aspect(aspect) self._gmt = gmt self._layout = layout self._widget = widget self._jxyr = self._widget.JXY() + self._scaler.R() self._pxyr = self._widget.PXY() + [ '-R%g/%g/%g/%g' % (0, widget.width(), 0, widget.height())] self._have_drawn_axes = False self._have_drawn_labels = False def _draw_background(self): self._have_topo_land = False self._have_topo_ocean = False if self.show_topo: self._have_topo = self._draw_topo() self._draw_basefeatures() def _get_topo_tile(self, k): t = None demname = None for dem in self._dems[k]: t = topo.get(dem, self._wesn) demname = dem if t is not None: break if not t: raise NoTopo() return t, demname def _prep_topo(self, k): gmt = self._gmt t, demname = self._get_topo_tile(k) if demname not in self._prep_topo_have: grdfile = gmt.tempfilename() is_flat = num.all(t.data[0] == t.data) gmtpy.savegrd( t.x(), t.y(), t.data, filename=grdfile, naming='lonlat') if self.illuminate and not is_flat: if k == 'ocean': factor = self.illuminate_factor_ocean else: factor = self.illuminate_factor_land ilumfn = gmt.tempfilename() gmt.grdgradient( grdfile, N='e%g' % factor, A=-45, G=ilumfn, out_discard=True) ilumargs = ['-I%s' % ilumfn] else: ilumargs = [] if self.replace_topo_color_only: t2 = self.replace_topo_color_only grdfile2 = gmt.tempfilename() gmtpy.savegrd( t2.x(), t2.y(), t2.data, filename=grdfile2, naming='lonlat') if gmt.is_gmt5(): gmt.grdsample( grdfile2, G=grdfile, n='l', I='%g/%g' % (t.dx, t.dy), # noqa R=grdfile, out_discard=True) else: gmt.grdsample( grdfile2, G=grdfile, Q='l', I='%g/%g' % (t.dx, t.dy), # noqa R=grdfile, out_discard=True) gmt.grdmath( grdfile, '0.0', 'AND', '=', grdfile2, out_discard=True) grdfile = grdfile2 self._prep_topo_have[demname] = grdfile, ilumargs return self._prep_topo_have[demname] def _draw_topo(self): widget = self._widget scaler = self._scaler gmt = self._gmt cres = self._coastline_resolution minarea = self._minarea JXY = widget.JXY() R = scaler.R() try: grdfile, ilumargs = self._prep_topo('ocean') gmt.pscoast(D=cres, S='c', A=minarea, *(JXY+R)) gmt.grdimage(grdfile, C=topo.cpt(self.topo_cpt_wet), *(ilumargs+JXY+R)) gmt.pscoast(Q=True, *(JXY+R)) self._have_topo_ocean = True except NoTopo: self._have_topo_ocean = False try: grdfile, ilumargs = self._prep_topo('land') gmt.pscoast(D=cres, G='c', A=minarea, *(JXY+R)) gmt.grdimage(grdfile, C=topo.cpt(self.topo_cpt_dry), *(ilumargs+JXY+R)) gmt.pscoast(Q=True, *(JXY+R)) self._have_topo_land = True except NoTopo: self._have_topo_land = False def _draw_topo_scale(self, label='Elevation [km]'): dry = read_cpt(topo.cpt(self.topo_cpt_dry)) wet = read_cpt(topo.cpt(self.topo_cpt_wet)) combi = cpt_merge_wet_dry(wet, dry) for level in combi.levels: level.vmin /= km level.vmax /= km topo_cpt = self.gmt.tempfilename() + '.cpt' write_cpt(combi, topo_cpt) (w, h), (xo, yo) = self.widget.get_size() self.gmt.psscale( D='%gp/%gp/%gp/%gph' % (xo + 0.5*w, yo - 2.0*gmtpy.cm, w, 0.5*gmtpy.cm), C=topo_cpt, B='1:%s:' % label) def _draw_basefeatures(self): gmt = self._gmt cres = self._coastline_resolution rivers = self._rivers minarea = self._minarea color_wet = self.color_wet color_dry = self.color_dry if self.show_rivers and rivers: rivers = ['-Ir/0.25p,%s' % gmtpy.color(self.color_wet)] else: rivers = [] fill = {} if not self._have_topo_land: fill['G'] = color_dry if not self._have_topo_ocean: fill['S'] = color_wet gmt.pscoast( D=cres, W='thinnest,%s' % gmtpy.color(darken(gmtpy.color_tup(color_dry))), A=minarea, *(rivers+self._jxyr), **fill) if self.show_plates: self.draw_plates() def _draw_axes(self): gmt = self._gmt scaler = self._scaler widget = self._widget if self.axes_layout is None: if self.lat > 0.0: axes_layout = 'WSen' else: axes_layout = 'WseN' else: axes_layout = self.axes_layout scale_km = gmtpy.nice_value(self.radius/5.) / 1000. if self.show_center_mark: gmt.psxy( in_rows=[[self.lon, self.lat]], S='c20p', W='2p,black', *self._jxyr) if self.show_grid: btmpl = ('%(xinc)gg%(xinc)g:%(xlabel)s:/' '%(yinc)gg%(yinc)g:%(ylabel)s:') else: btmpl = '%(xinc)g:%(xlabel)s:/%(yinc)g:%(ylabel)s:' gmt.psbasemap( B=(btmpl % scaler.get_params())+axes_layout, L=('x%gp/%gp/%g/%g/%gk' % ( 6./7*widget.width(), widget.height()/7., self.lon, self.lat, scale_km)), *self._jxyr) if self.comment: font_size = self.gmt.label_font_size() _, east, south, _ = self._wesn if gmt.is_gmt5(): row = [ 1, 0, '%gp,%s,%s' % (font_size, 0, 'black'), 'BR', self.comment] farg = ['-F+f+j'] else: row = [1, 0, font_size, 0, 0, 'BR', self.comment] farg = [] gmt.pstext( in_rows=[row], N=True, R=(0, 1, 0, 1), D='%gp/%gp' % (-font_size*0.2, font_size*0.3), *(widget.PXY() + farg)) def draw_axes(self): if not self._have_drawn_axes: self._draw_axes() self._have_drawn_axes = True def _have_coastlines(self): gmt = self._gmt cres = self._coastline_resolution minarea = self._minarea checkfile = gmt.tempfilename() gmt.pscoast( M=True, D=cres, W='thinnest,black', A=minarea, out_filename=checkfile, *self._jxyr) points = [] with open(checkfile, 'r') as f: for line in f: ls = line.strip() if ls.startswith('#') or ls.startswith('>') or ls == '': continue plon, plat = [float(x) for x in ls.split()] points.append((plat, plon)) points = num.array(points, dtype=num.float) return num.any(points_in_region(points, self._wesn)) def have_coastlines(self): self.gmt return self._have_coastlines() def project(self, lats, lons, jr=None): onepoint = False if isinstance(lats, float) and isinstance(lons, float): lats = [lats] lons = [lons] onepoint = True if jr is not None: j, r = jr gmt = gmtpy.GMT(version=self._gmtversion) else: j, _, _, r = self.jxyr gmt = self.gmt f = BytesIO() gmt.mapproject(j, r, in_columns=(lons, lats), out_stream=f, D='p') f.seek(0) data = num.loadtxt(f, ndmin=2) xs, ys = data.T if onepoint: xs = xs[0] ys = ys[0] return xs, ys def _map_box(self, jr=None): ll_lon, ll_lat, ur_lon, ur_lat = self._corners xs_corner, ys_corner = self.project( (ll_lat, ur_lat), (ll_lon, ur_lon), jr=jr) w = xs_corner[1] - xs_corner[0] h = ys_corner[1] - ys_corner[0] return w, h def _map_aspect(self, jr=None): w, h = self._map_box(jr=jr) return h/w def _draw_labels(self): points_taken = [] regions_taken = [] def no_points_in_rect(xs, ys, xmin, ymin, xmax, ymax): xx = not num.any(la(la(xmin < xs, xs < xmax), la(ymin < ys, ys < ymax))) return xx def roverlaps(a, b): return (a[0] < b[2] and b[0] < a[2] and a[1] < b[3] and b[1] < a[3]) w, h = self._map_box() label_font_size = self.gmt.label_font_size() if self._labels: n = len(self._labels) lons, lats, texts, sx, sy, colors, fonts, font_sizes, styles = \ list(zip(*self._labels)) font_sizes = [ (font_size or label_font_size) for font_size in font_sizes] sx = num.array(sx, dtype=num.float) sy = num.array(sy, dtype=num.float) xs, ys = self.project(lats, lons) points_taken.append((xs, ys)) dxs = num.zeros(n) dys = num.zeros(n) for i in range(n): dx, dy = gmtpy.text_box( texts[i], font=fonts[i], font_size=font_sizes[i], **styles[i]) dxs[i] = dx dys[i] = dy la = num.logical_and anchors_ok = ( la(xs + sx + dxs < w, ys + sy + dys < h), la(xs - sx - dxs > 0., ys - sy - dys > 0.), la(xs + sx + dxs < w, ys - sy - dys > 0.), la(xs - sx - dxs > 0., ys + sy + dys < h), ) arects = [ (xs, ys, xs + sx + dxs, ys + sy + dys), (xs - sx - dxs, ys - sy - dys, xs, ys), (xs, ys - sy - dys, xs + sx + dxs, ys), (xs - sx - dxs, ys, xs, ys + sy + dys)] for i in range(n): for ianch in range(4): anchors_ok[ianch][i] &= no_points_in_rect( xs, ys, *[xxx[i] for xxx in arects[ianch]]) anchor_choices = [] anchor_take = [] for i in range(n): choices = [ianch for ianch in range(4) if anchors_ok[ianch][i]] anchor_choices.append(choices) if choices: anchor_take.append(choices[0]) else: anchor_take.append(None) def cost(anchor_take): noverlaps = 0 for i in range(n): for j in range(n): if i != j: i_take = anchor_take[i] j_take = anchor_take[j] if i_take is None or j_take is None: continue r_i = [xxx[i] for xxx in arects[i_take]] r_j = [xxx[j] for xxx in arects[j_take]] if roverlaps(r_i, r_j): noverlaps += 1 return noverlaps cur_cost = cost(anchor_take) imax = 30 while cur_cost != 0 and imax > 0: for i in range(n): for t in anchor_choices[i]: anchor_take_new = list(anchor_take) anchor_take_new[i] = t new_cost = cost(anchor_take_new) if new_cost < cur_cost: anchor_take = anchor_take_new cur_cost = new_cost imax -= 1 while cur_cost != 0: for i in range(n): anchor_take_new = list(anchor_take) anchor_take_new[i] = None new_cost = cost(anchor_take_new) if new_cost < cur_cost: anchor_take = anchor_take_new cur_cost = new_cost break anchor_strs = ['BL', 'TR', 'TL', 'BR'] for i in range(n): ianchor = anchor_take[i] color = colors[i] if color is None: color = 'black' if ianchor is not None: regions_taken.append([xxx[i] for xxx in arects[ianchor]]) anchor = anchor_strs[ianchor] yoff = [-sy[i], sy[i]][anchor[0] == 'B'] xoff = [-sx[i], sx[i]][anchor[1] == 'L'] if self.gmt.is_gmt5(): row = ( lons[i], lats[i], '%i,%s,%s' % (font_sizes[i], fonts[i], color), anchor, texts[i]) farg = ['-F+f+j'] else: row = ( lons[i], lats[i], font_sizes[i], 0, fonts[i], anchor, texts[i]) farg = ['-G%s' % color] self.gmt.pstext( in_rows=[row], D='%gp/%gp' % (xoff, yoff), *(self.jxyr + farg), **styles[i]) if self._area_labels: for lons, lats, text, color, font, font_size, style in \ self._area_labels: if font_size is None: font_size = label_font_size if color is None: color = 'black' if self.gmt.is_gmt5(): farg = ['-F+f+j'] else: farg = ['-G%s' % color] xs, ys = self.project(lats, lons) dx, dy = gmtpy.text_box( text, font=font, font_size=font_size, **style) rects = [xs-0.5*dx, ys-0.5*dy, xs+0.5*dx, ys+0.5*dy] locs_ok = num.ones(xs.size, dtype=num.bool) for iloc in range(xs.size): rcandi = [xxx[iloc] for xxx in rects] locs_ok[iloc] = True locs_ok[iloc] &= ( 0 < rcandi[0] and rcandi[2] < w and 0 < rcandi[1] and rcandi[3] < h) overlap = False for r in regions_taken: if roverlaps(r, rcandi): overlap = True break locs_ok[iloc] &= not overlap for xs_taken, ys_taken in points_taken: locs_ok[iloc] &= no_points_in_rect( xs_taken, ys_taken, *rcandi) if not locs_ok[iloc]: break rows = [] for iloc, (lon, lat) in enumerate(zip(lons, lats)): if not locs_ok[iloc]: continue if self.gmt.is_gmt5(): row = ( lon, lat, '%i,%s,%s' % (font_size, font, color), 'MC', text) else: row = ( lon, lat, font_size, 0, font, 'MC', text) rows.append(row) regions_taken.append([xxx[iloc] for xxx in rects]) break self.gmt.pstext( in_rows=rows, *(self.jxyr + farg), **style) def draw_labels(self): self.gmt if not self._have_drawn_labels: self._draw_labels() self._have_drawn_labels = True def add_label( self, lat, lon, text, offset_x=5., offset_y=5., color=None, font='1', font_size=None, style={}): if 'G' in style: style = style.copy() color = style.pop('G') self._labels.append( (lon, lat, text, offset_x, offset_y, color, font, font_size, style)) def add_area_label( self, lat, lon, text, color=None, font='3', font_size=None, style={}): self._area_labels.append( (lon, lat, text, color, font, font_size, style)) def cities_in_region(self): from pyrocko.dataset import geonames cities = geonames.get_cities_region(region=self.wesn, minpop=0) cities.extend(self.custom_cities) cities.sort(key=lambda x: x.population) return cities def draw_cities(self, exact=None, include=[], exclude=[], nmax_soft=10, psxy_style=dict(S='s5p', G='black')): cities = self.cities_in_region() if exact is not None: cities = [c for c in cities if c.name in exact] minpop = None else: cities = [c for c in cities if c.name not in exclude] minpop = 10**3 for minpop_new in [1e3, 3e3, 1e4, 3e4, 1e5, 3e5, 1e6, 3e6, 1e7]: cities_new = [ c for c in cities if c.population > minpop_new or c.name in include] if len(cities_new) == 0 or ( len(cities_new) < 3 and len(cities) < nmax_soft*2): break cities = cities_new minpop = minpop_new if len(cities) <= nmax_soft: break if cities: lats = [c.lat for c in cities] lons = [c.lon for c in cities] self.gmt.psxy( in_columns=(lons, lats), *self.jxyr, **psxy_style) for c in cities: try: text = c.name.encode('iso-8859-1').decode('iso-8859-1') except UnicodeEncodeError: text = c.asciiname self.add_label(c.lat, c.lon, text) self._cities_minpop = minpop def add_stations(self, stations, psxy_style=dict(S='t8p', G='black')): lats = [s.lat for s in stations] lons = [s.lon for s in stations] self.gmt.psxy( in_columns=(lons, lats), *self.jxyr, **psxy_style) for station in stations: self.add_label(station.lat, station.lon, '.'.join( x for x in (station.network, station.station) if x)) def add_kite_scene(self, scene): tile = FloatTile( scene.frame.llLon, scene.frame.llLat, scene.frame.dLon, scene.frame.dLat, scene.displacement) return tile def add_gnss_campaign(self, campaign, psxy_style=dict(), labels=True): offsets = num.array([math.sqrt(s.east.shift**2 + s.north.shift**2) for s in campaign.stations]) scale = 1./offsets.max() default_psxy_style = { 'h': 2, 'h': 2, 'W': '0.5p,black', 't': '30', 'G': 'black', 'L': True, 'S': 'e%d/0.95/8' % scale, } default_psxy_style.update(psxy_style) if labels: rows = [(s.lon, s.lat, s.east.shift, s.north.shift, s.east.sigma, s.north.sigma, 0) for s in campaign.stations] else: rows = [(s.lon, s.lat, s.east.shift, s.north.shift, s.east.sigma, s.north.sigma, 0, s.code) for s in campaign.stations] kwargs = {} kwargs.update(default_psxy_style) kwargs.update(self.jxyr) self.gmt.psvelo(in_rows=rows, **kwargs) def draw_plates(self): from pyrocko.dataset import tectonics neast = 20 nnorth = max(1, int(round(num.round(self._hreg/self._wreg * neast)))) norths = num.linspace(-self._hreg*0.5, self._hreg*0.5, nnorth) easts = num.linspace(-self._wreg*0.5, self._wreg*0.5, neast) norths2 = num.repeat(norths, neast) easts2 = num.tile(easts, nnorth) lats, lons = od.ne_to_latlon( self.lat, self.lon, norths2, easts2) bird = tectonics.PeterBird2003() plates = bird.get_plates() color_plates = gmtpy.color('aluminium5') color_velocities = gmtpy.color('skyblue1') color_velocities_lab = gmtpy.color(darken(gmtpy.color_tup('skyblue1'))) points = num.vstack((lats, lons)).T used = [] for plate in plates: mask = plate.contains_points(points) if num.any(mask): used.append((plate, mask)) if len(used) > 1: candi_fixed = {} label_data = [] for plate, mask in used: mean_north = num.mean(norths2[mask]) mean_east = num.mean(easts2[mask]) iorder = num.argsort(num.sqrt( (norths2[mask] - mean_north)**2 + (easts2[mask] - mean_east)**2)) lat_candis = lats[mask][iorder] lon_candis = lons[mask][iorder] candi_fixed[plate.name] = lat_candis.size label_data.append(( lat_candis, lon_candis, plate, color_plates)) boundaries = bird.get_boundaries() size = 2 psxy_kwargs = [] for boundary in boundaries: if num.any(points_in_region(boundary.points, self._wesn)): for typ, part in boundary.split_types( [['SUB'], ['OSR', 'OTF', 'OCB', 'CTF', 'CCB', 'CRB']]): lats, lons = part.T kwargs = {} if typ[0] == 'SUB': if boundary.kind == '\\': kwargs['S'] = 'f%g/%gp+t+r' % ( 0.45*size, 3.*size) elif boundary.kind == '/': kwargs['S'] = 'f%g/%gp+t+l' % ( 0.45*size, 3.*size) kwargs['G'] = color_plates kwargs['in_columns'] = (lons, lats) kwargs['W'] = '%gp,%s' % (size, color_plates), psxy_kwargs.append(kwargs) if boundary.kind == '\\': if boundary.name2 in candi_fixed: candi_fixed[boundary.name2] += neast*nnorth elif boundary.kind == '/': if boundary.name1 in candi_fixed: candi_fixed[boundary.name1] += neast*nnorth candi_fixed = [name for name in sorted( list(candi_fixed.keys()), key=lambda name: -candi_fixed[name])] candi_fixed.append(None) gsrm = tectonics.GSRM1() for name in candi_fixed: if name not in gsrm.plate_names() \ and name not in gsrm.plate_alt_names(): continue lats, lons, vnorth, veast, vnorth_err, veast_err, corr = \ gsrm.get_velocities(name, region=self._wesn) fixed_plate_name = name self.gmt.psvelo( in_columns=( lons, lats, veast, vnorth, veast_err, vnorth_err, corr), W='0.25p,%s' % color_velocities, A='9p+e+g%s' % color_velocities, S='e0.2p/0.95/10', *self.jxyr) for _ in range(len(lons) // 50 + 1): ii = random.randint(0, len(lons)-1) v = math.sqrt(vnorth[ii]**2 + veast[ii]**2) self.add_label( lats[ii], lons[ii], '%.0f' % v, font_size=0.7*self.gmt.label_font_size(), style=dict( G=color_velocities_lab)) break for (lat_candis, lon_candis, plate, color) in label_data: full_name = bird.full_name(plate.name) if plate.name == fixed_plate_name: full_name = '@_' + full_name + '@_' self.add_area_label( lat_candis, lon_candis, full_name, color=color, font='3') for kwargs in psxy_kwargs: self.gmt.psxy(*self.jxyr, **kwargs)
class CNNLayer(Layer): '''2D CNN layer''' kernel_width = Int.T() kernel_height = Int.T(optional=True, help='If this parameter is not set use *N* channels as height.') pool_width = Int.T( optional=True, help='If not set pool_height=kernel_height') pool_height = Int.T( optional=True, help='If not set pool_height=kernel_height') dilation_rate = Int.T(default=0) strides = Tuple.T(2, Int.T(), default=(1, 1)) is_detector= Bool.T(default=False, help='If *True* this layer\'s activations contributes to detector.') def __init__(self, *args, **kwargs): Layer.__init__(self, *args, **kwargs) if not self.pool_width: logger.info( 'Pool width is undefined. Setting equal to kernel width') self.pool_width = self.kernel_width if not self.pool_height: logger.info( 'Pool heigth is undefined. Setting equal to kernel height') self.pool_height = self.kernel_height def chain(self, input, level, training=False, dropout_rate=0.): ''' :param level: detection level ''' _, n_channels, n_samples, _ = input.shape logger.debug('input shape %s' % input.shape) kernel_height = self.kernel_height or n_channels kwargs = {} if self.dilation_rate: kwargs.update({'dilation_rate': self.dilation_rate}) inpower = tf.sqrt(tf.reduce_sum(tf.square(input))) input = tf.layers.conv2d( inputs=input, filters=self.n_filters, kernel_size=(kernel_height, self.kernel_width), activation=self.get_activation(), strides=self.strides, name=self.name, **kwargs) input = tf.layers.max_pooling2d(input, pool_size=(self.pool_width, self.pool_height), strides=(1, 1), name=self.name+'maxpool') if logger.getEffectiveLevel() == logging.DEBUG: tf.summary.image( 'post-%s' % self.name, tf.split( input, num_or_size_splits=self.n_filters, axis=-1)[0]) if self.is_detector: batch_mean = tf.reduce_mean(input, reduction_indices=(1, 2, 3), keepdims=True) stdev = tf.sqrt(tf.reduce_mean(tf.square(input - batch_mean), reduction_indices=(1, 2, 3))) level += (tf.sqrt(tf.reduce_sum(tf.square(input))) / inpower) input = tf.layers.dropout( input, rate=dropout_rate, training=training) return tf.layers.batch_normalization(input, training=training), level def visualize_kernel(self, estimator, index=0, save_path=None, **kwargs): save_name = pjoin(save_path, 'kernel-%s' % self.name) weights = estimator.get_variable_value('%s/kernel' % self.name) plot.show_kernels(weights[:, :, index, ...], name=save_name)
class GNSSTargetMisfitPlot(PlotConfig): ''' Maps showing horizontal surface displacements of a GNSS campaign and model ''' name = 'gnss' size_cm = Tuple.T(2, Float.T(), default=(30., 30.), help='width and length of the figure in cm') show_topo = Bool.T(default=False, help='show topography') show_grid = Bool.T(default=True, help='show the lat/lon grid') show_rivers = Bool.T(default=True, help='show rivers on the map') radius = Float.T(optional=True, help='radius of the map around campaign center lat/lon') def make(self, environ): cm = environ.get_plot_collection_manager() history = environ.get_history(subset='harvest') optimiser = environ.get_optimiser() ds = environ.get_dataset() environ.setup_modelling() cm.create_group_automap(self, self.draw_gnss_fits(ds, history, optimiser), title=u'GNSS Displacements', section='fits', feather_icon='map', description=u''' Maps showing station positions and statiom names of the GNSS targets. Arrows the observed surface displacements (black arrows) and synthetic displacements (red arrows). The top plot shows the horizontal displacements and the bottom plot the vertical displacements. The grey filled box shows the surface projection of the modelled source, with the thick-lined edge marking the upper fault edge. ''') def draw_gnss_fits(self, ds, history, optimiser, vertical=False): problem = history.problem gnss_targets = problem.gnss_targets for target in gnss_targets: target.set_dataset(ds) xbest = history.get_best_model() source = history.get_best_source() results = problem.evaluate(xbest, result_mode='full', targets=gnss_targets) def plot_gnss(gnss_target, result, ifig, vertical=False): campaign = gnss_target.campaign item = PlotItem( name='fig_%i' % ifig, attributes={'targets': gnss_target.path}, title=u'Static GNSS Surface Displacements - Campaign %s' % campaign.name, description=u''' Static surface displacement from GNSS campaign %s (black vectors) and displacements derived from best model (red). ''' % campaign.name) event = source.pyrocko_event() locations = campaign.stations + [event] lat, lon = od.geographic_midpoint_locations(locations) if self.radius is None: coords = num.array([loc.effective_latlon for loc in locations]) radius = od.distance_accurate50m_numpy(lat[num.newaxis], lon[num.newaxis], coords[:, 0].max(), coords[:, 1]).max() radius *= 1.1 if radius < 30. * km: logger.warn('Radius of GNSS campaign %s too small, defaulting' ' to 30 km' % campaign.name) radius = 30 * km model_camp = gnss.GNSSCampaign(stations=copy.deepcopy( campaign.stations), name='grond model') for ista, sta in enumerate(model_camp.stations): sta.north.shift = result.statics_syn['displacement.n'][ista] sta.north.sigma = 0. sta.east.shift = result.statics_syn['displacement.e'][ista] sta.east.sigma = 0. if sta.up: sta.up.shift = -result.statics_syn['displacement.d'][ista] sta.up.sigma = 0. m = automap.Map(width=self.size_cm[0], height=self.size_cm[1], lat=lat, lon=lon, radius=radius, show_topo=self.show_topo, show_grid=self.show_grid, show_rivers=self.show_rivers, color_wet=(216, 242, 254), color_dry=(238, 236, 230)) all_stations = campaign.stations + model_camp.stations offset_scale = num.zeros(len(all_stations)) for ista, sta in enumerate(all_stations): for comp in sta.components.values(): offset_scale[ista] += comp.shift offset_scale = num.sqrt(offset_scale**2).max() m.add_gnss_campaign(campaign, psxy_style={ 'G': 'black', 'W': '0.8p,black', }, offset_scale=offset_scale, vertical=vertical) m.add_gnss_campaign(model_camp, psxy_style={ 'G': 'red', 'W': '0.8p,red', 't': 30, }, offset_scale=offset_scale, vertical=vertical, labels=False) if isinstance(problem, CMTProblem) \ or isinstance(problem, VLVDProblem): from pyrocko import moment_tensor from pyrocko.plot import gmtpy mt = event.moment_tensor.m_up_south_east() ev_lat, ev_lon = event.effective_latlon xx = num.trace(mt) / 3. mc = num.matrix([[xx, 0., 0.], [0., xx, 0.], [0., 0., xx]]) mc = mt - mc mc = mc / event.moment_tensor.scalar_moment() * \ moment_tensor.magnitude_to_moment(5.0) m6 = tuple(moment_tensor.to6(mc)) symbol_size = 20. m.gmt.psmeca(S='%s%g' % ('d', symbol_size / gmtpy.cm), in_rows=[(ev_lon, ev_lat, 10) + m6 + (1, 0, 0)], M=True, *m.jxyr) elif isinstance(problem, RectangularProblem): m.gmt.psxy(in_rows=source.outline(cs='lonlat'), L='+p2p,black', W='1p,black', G='black', t=60, *m.jxyr) elif isinstance(problem, VolumePointProblem): ev_lat, ev_lon = event.effective_latlon dV = abs(source.volume_change) sphere_radius = num.cbrt(dV / (4. / 3. * num.pi)) volcanic_circle = [ev_lon, ev_lat, '%fe' % sphere_radius] m.gmt.psxy(S='E-', in_rows=[volcanic_circle], W='1p,black', G='orange3', *m.jxyr) return (item, m) ifig = 0 for vertical in (False, True): for gnss_target, result in zip(problem.gnss_targets, results): yield plot_gnss(gnss_target, result, ifig, vertical) ifig += 1
class HistogramPlot(PlotConfig): ''' Histograms or Gaussian kernel densities (default) of all parameters (marginal distributions of model parameters). The histograms (by default shown as Gaussian kernel densities) show (red curved solid line) the distributions of the parameters (marginals) along with some characteristics: The red solid vertical line gives the median of the distribution and the dashed red vertical line the mean value. Dark gray vertical lines show reference values (given in the event.txt file). The overlapping red-shaded areas show the 68% confidence intervals (innermost area), the 90% confidence intervals (middle area) and the minimum and maximum values (widest area). The plot ranges are defined by the given parameter bounds and show the model space. Well resolved model parameters show peaked distributions. ''' name = 'histogram' size_cm = Tuple.T(2, Float.T(), default=(12.5, 7.5)) exclude = List.T(String.T()) include = List.T(String.T()) method = StringChoice.T( choices=['gaussian_kde', 'histogram'], default='gaussian_kde') show_reference = Bool.T(default=True) def make(self, environ): cm = environ.get_plot_collection_manager() history = environ.get_history(subset='harvest') mpl_init(fontsize=self.font_size) cm.create_group_mpl( self, self.draw_figures(history), title=u'Histogram', section='solution', feather_icon='bar-chart-2', description=u''' Distribution of the problem's parameters. The histograms are shown either as Gaussian kernel densities (red curved solid line) or as bar plots the distributions of the parameters (marginals) along with some characteristics: The red solid vertical line gives the median of the distribution and the dashed red vertical line the mean value. Dark gray vertical lines show reference parameter values if given in the event.txt file. The overlapping red-shaded areas show the 68% confidence intervals (innermost area), the 90% confidence intervals (middle area) and the minimum and maximum values (widest area). The plot ranges are defined by the given parameter bounds and show the model space. ''') def draw_figures(self, history): import scipy.stats from grond.core import make_stats exclude = self.exclude include = self.include figsize = self.size_inch fontsize = self.font_size method = self.method ref_color = mpl_color('aluminium6') stats_color = mpl_color('scarletred2') bar_color = mpl_color('scarletred1') stats_color3 = mpl_color('scarletred3') problem = history.problem models = history.models bounds = problem.get_combined_bounds() exclude = list(exclude) for ipar in range(problem.ncombined): par = problem.combined[ipar] vmin, vmax = bounds[ipar] if vmin == vmax: exclude.append(par.name) xref = problem.get_reference_model() smap = {} iselected = 0 for ipar in range(problem.ncombined): par = problem.combined[ipar] if exclude and par.name in exclude or \ include and par.name not in include: continue smap[iselected] = ipar iselected += 1 nselected = iselected del iselected pnames = [ problem.combined[smap[iselected]].name for iselected in range(nselected)] rstats = make_stats(problem, models, history.get_primary_chain_misfits(), pnames=pnames) for iselected in range(nselected): ipar = smap[iselected] par = problem.combined[ipar] vs = problem.extract(models, ipar) vmin, vmax = bounds[ipar] fig = plt.figure(figsize=figsize) labelpos = mpl_margins( fig, nw=1, nh=1, w=7., bottom=5., top=1, units=fontsize) axes = fig.add_subplot(1, 1, 1) labelpos(axes, 2.5, 2.0) axes.set_xlabel(par.get_label()) axes.set_ylabel('PDF') axes.set_xlim(*fixlim(*par.scaled((vmin, vmax)))) if method == 'gaussian_kde': try: kde = scipy.stats.gaussian_kde(vs) except Exception: logger.warn( 'Cannot create plot histogram with gaussian_kde: ' 'possibly all samples have the same value.') continue vps = num.linspace(vmin, vmax, 600) pps = kde(vps) axes.plot( par.scaled(vps), par.inv_scaled(pps), color=stats_color) elif method == 'histogram': pps, edges = num.histogram( vs, bins=num.linspace(vmin, vmax, num=40), density=True) vps = 0.5 * (edges[:-1] + edges[1:]) axes.bar(par.scaled(vps), par.inv_scaled(pps), par.scaled(2.*(vps - edges[:-1])), color=bar_color) pstats = rstats.parameter_stats_list[iselected] axes.axvspan( par.scaled(pstats.minimum), par.scaled(pstats.maximum), color=stats_color, alpha=0.1) axes.axvspan( par.scaled(pstats.percentile16), par.scaled(pstats.percentile84), color=stats_color, alpha=0.1) axes.axvspan( par.scaled(pstats.percentile5), par.scaled(pstats.percentile95), color=stats_color, alpha=0.1) axes.axvline( par.scaled(pstats.median), color=stats_color3, alpha=0.5) axes.axvline( par.scaled(pstats.mean), color=stats_color3, ls=':', alpha=0.5) if self.show_reference: axes.axvline( par.scaled(problem.extract(xref, ipar)), color=ref_color) item = PlotItem(name=par.name) item.attributes['parameters'] = [par.name] yield item, fig