You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
492 lines
14 KiB
Python
492 lines
14 KiB
Python
# GPLv3
|
|
#
|
|
# The Developers, 21st Century
|
|
import numpy as num
|
|
import matplotlib.pyplot as plt
|
|
|
|
from pyrocko.guts import Bool, Object, String
|
|
from pyrocko.plot import mpl_init, mpl_margins, mpl_papersize, mpl_graph_color
|
|
from pyrocko.plot.automap import gmtpy
|
|
from pyrocko.util import tts
|
|
|
|
from ewrica.scaling import length_blaser
|
|
from ewrica.plot import ids as idsplot, nice_plot_simple
|
|
|
|
mpl_init(fontsize=15)
|
|
|
|
km = 1e3
|
|
|
|
|
|
class PlotGroup(Object):
|
|
title = String.T(default='')
|
|
description = String.T(default='')
|
|
|
|
|
|
class PlotItem(Object):
|
|
title = String.T(default='')
|
|
description = String.T(default='')
|
|
name = String.T(default='')
|
|
|
|
|
|
class Plot(Object):
|
|
name = String.T(default='')
|
|
|
|
def make(self, source, *args, **kwargs):
|
|
return self, PlotGroup(), self.draw_figures(source, *args, **kwargs)
|
|
|
|
def draw_figures(self, source, *args, **kwargs):
|
|
raise NotImplementedError('Method not implemented')
|
|
|
|
|
|
class MPLPlot(Plot):
|
|
pass
|
|
|
|
|
|
class ViewPlot(Plot):
|
|
pass
|
|
|
|
|
|
class GMTPlot(Plot):
|
|
show_grid = Bool.T(default=True)
|
|
show_topo = Bool.T(default=False)
|
|
|
|
def basemap(self, source):
|
|
length = source.length
|
|
if not length:
|
|
length = num.mean([
|
|
length_blaser(source.magnitude, rake)
|
|
for rake in [0., 90., 180.]])
|
|
|
|
return idsplot.RuptureMap(
|
|
source=source,
|
|
lat=source.lat,
|
|
lon=source.lon,
|
|
radius=length,
|
|
show_topo=self.show_topo,
|
|
show_grid=self.show_grid)
|
|
|
|
|
|
class FinalSlipMap(GMTPlot):
|
|
name = 'slipmap'
|
|
|
|
def make(self, source, *args, **kwargs):
|
|
return (
|
|
self,
|
|
PlotGroup(
|
|
title='Final slip map',
|
|
description=''),
|
|
self.draw_figures(source, *args, **kwargs))
|
|
|
|
def draw_figures(self, source, *args, **kwargs):
|
|
m = self.basemap(source)
|
|
m.draw_dislocation()
|
|
m.draw_nucleation_point()
|
|
m.draw_top_edge()
|
|
|
|
item = PlotItem(
|
|
title='best slip distribution map',
|
|
description='',
|
|
name='slip_map')
|
|
|
|
yield item, m
|
|
|
|
|
|
class FinalStaticGNSSMap(GMTPlot):
|
|
name = 'staticgnss_map'
|
|
|
|
def make(self, source, *args, **kwargs):
|
|
return (
|
|
self,
|
|
PlotGroup(
|
|
title='Static GNSS Fit',
|
|
description=''),
|
|
self.draw_figures(source, *args, **kwargs))
|
|
|
|
def draw_figures(self, source, gnss_obs, gnss_syn):
|
|
m = self.basemap(source)
|
|
m.draw_dislocation()
|
|
m.draw_nucleation_point()
|
|
m.draw_top_edge()
|
|
m.draw_gnss_data_fit(
|
|
campaign_obs=gnss_obs, campaign_syn=gnss_syn, vertical=False)
|
|
|
|
item = PlotItem(
|
|
title='static gnss fit map',
|
|
description='',
|
|
name='gnss_fit')
|
|
|
|
yield item, m
|
|
|
|
|
|
class FinalSlipView(ViewPlot):
|
|
name = 'slipview'
|
|
|
|
def make(self, source, *args, **kwargs):
|
|
return (
|
|
self,
|
|
PlotGroup(
|
|
title='Final Slip View',
|
|
description=''),
|
|
self.draw_figures(source, *args, **kwargs))
|
|
|
|
def draw_figures(self, source, *args, **kwargs):
|
|
v = None
|
|
if not source.curved:
|
|
v = idsplot.RuptureView(source=source)
|
|
v.draw_dislocation()
|
|
v.draw_nucleation_point()
|
|
v.draw_dislocation_contour()
|
|
|
|
item = PlotItem(
|
|
title='best slip distribution view',
|
|
description='',
|
|
name='slip_view')
|
|
|
|
yield item, v
|
|
|
|
|
|
class SourceTimeFunction(ViewPlot):
|
|
name = 'stf'
|
|
|
|
def make(self, source, *args, **kwargs):
|
|
return (
|
|
self,
|
|
PlotGroup(
|
|
title='Source Time Function',
|
|
description=''),
|
|
self.draw_figures(source, *args, **kwargs))
|
|
|
|
def draw_figures(self, source, *args, **kwargs):
|
|
v = idsplot.RuptureView(source=source)
|
|
v.draw_source_dynamics('stf')
|
|
|
|
item = PlotItem(
|
|
title='source time function of best model',
|
|
description='',
|
|
name='stf')
|
|
|
|
yield item, v
|
|
|
|
|
|
class TracePlot(MPLPlot):
|
|
name = 'waveforms'
|
|
|
|
def make(self, source, *args, **kwargs):
|
|
return (
|
|
self,
|
|
PlotGroup(
|
|
title='Waveform Fit',
|
|
description='Black - observed, red - modelled'),
|
|
self.draw_figures(source, *args, **kwargs))
|
|
|
|
def draw_figures(self, source, waveform_result, *args, **kwargs):
|
|
try:
|
|
plt.rcParams['ytick.right'] = True
|
|
plt.rcParams['ytick.labelright'] = True
|
|
plt.rcParams['ytick.left'] = False
|
|
plt.rcParams['ytick.labelleft'] = False
|
|
except KeyError:
|
|
plt.rcParams['ytick.right'] = True
|
|
plt.rcParams['ytick.left'] = False
|
|
|
|
channels = num.unique([
|
|
tr.channel[-1] for tr in waveform_result.iter_traces(
|
|
subset='with_traces')])
|
|
channels = num.sort(channels)
|
|
|
|
axes = []
|
|
for i, res_sta in enumerate(waveform_result.grouped_results(
|
|
gather=lambda res: res.nslc[:3],
|
|
subset='with_traces')):
|
|
|
|
if not res_sta:
|
|
continue
|
|
|
|
for ic, res_cha in enumerate(res_sta.grouped_results(
|
|
gather=lambda res: res.nslc,
|
|
subset='with_traces')):
|
|
|
|
res_cha = res_cha.results[0]
|
|
|
|
c = res_cha.nslc[3]
|
|
|
|
tr_obs = res_cha.observed
|
|
tr_syn = res_cha.synthetic
|
|
|
|
if not tr_obs or not tr_syn:
|
|
continue
|
|
|
|
fig = plt.figure(
|
|
figsize=mpl_papersize(paper='a6', orientation='landscape'))
|
|
|
|
mpl_margins(
|
|
fig,
|
|
top=4., bottom=40.,
|
|
left=4., right=75.,
|
|
units='point')
|
|
|
|
ax = fig.add_subplot(111)
|
|
|
|
line_obs, = ax.plot(
|
|
tr_obs.get_xdata() - source.time, tr_obs.get_ydata(),
|
|
c='dimgrey')
|
|
line_syn, = ax.plot(
|
|
tr_syn.get_xdata() - source.time, tr_syn.get_ydata(),
|
|
c=mpl_graph_color(0))
|
|
|
|
if any(tr_obs.get_xdata() < source.time):
|
|
ax.axvline(
|
|
x=0.,
|
|
color='darkgrey')
|
|
|
|
ax.axhline(
|
|
y=0.,
|
|
color='lightgrey',
|
|
zorder=0.)
|
|
|
|
text_kwargs = dict(
|
|
x=0.95, y=0.15,
|
|
s='{}\nmisfit = {} m'.format(
|
|
'.'.join(tr_syn.nslc_id[:3] + (c,)),
|
|
res_cha.misfit),
|
|
transform=ax.transAxes,
|
|
horizontalalignment='right',
|
|
verticalalignment='center')
|
|
|
|
try:
|
|
ax.text(
|
|
fontfamily='monospace',
|
|
**text_kwargs)
|
|
except (TypeError, AttributeError):
|
|
from matplotlib.font_manager import FontProperties
|
|
font0 = FontProperties()
|
|
font0.set_family('monospace')
|
|
ax.text(
|
|
fontproperties=font0,
|
|
**text_kwargs)
|
|
|
|
line_obs.set_label('observed')
|
|
line_syn.set_label('modelled')
|
|
plt.legend(
|
|
ncol=2,
|
|
bbox_to_anchor=(-0.5, -0.9),
|
|
borderaxespad=0.,
|
|
loc='center')
|
|
|
|
ax.tick_params(direction="in", width=1.5, length=6)
|
|
ax.yaxis.set_label_position("right")
|
|
|
|
nice_plot_simple(
|
|
ax,
|
|
xlabel='seconds after {}'.format(tts(source.time)),
|
|
ylabel='displacement [m]',
|
|
title=False,
|
|
spine_pos=['right', 'bottom'])
|
|
|
|
axes.append(ax)
|
|
|
|
item = PlotItem(
|
|
title='trace fit observed to modelled ',
|
|
description='',
|
|
name='trace_fit_{}'.format(
|
|
'.'.join(tr_obs.nslc_id[:3] + (c,))))
|
|
|
|
yield item, fig
|
|
|
|
plt.cla()
|
|
plt.clf()
|
|
plt.close()
|
|
|
|
|
|
class SeismicStationMap(GMTPlot):
|
|
name = 'station_map'
|
|
|
|
def basemap(self, source, radius):
|
|
return idsplot.RuptureMap(
|
|
source=source,
|
|
lat=source.lat, lon=source.lon,
|
|
radius=radius,
|
|
show_topo=self.show_topo,
|
|
show_grid=self.show_grid)
|
|
|
|
def make(self, source, *args, **kwargs):
|
|
return (
|
|
self,
|
|
PlotGroup(
|
|
title='Station Overview Map',
|
|
description=''),
|
|
self.draw_figures(source, *args, **kwargs))
|
|
|
|
def draw_figures(self, source, waveform_result, *args, **kwargs):
|
|
from pyrocko.orthodrome import distance_accurate50m_numpy
|
|
|
|
waveform_result = waveform_result.results
|
|
|
|
dists = distance_accurate50m_numpy(
|
|
[source.effective_lat], [source.effective_lon],
|
|
[s.effective_lat for s in waveform_result],
|
|
[s.effective_lon for s in waveform_result])
|
|
|
|
radius = max((
|
|
dists.max() + dists.max() * 0.05,
|
|
source.length))
|
|
|
|
lats = num.array([s.lat for s in waveform_result])
|
|
lons = num.array([s.lon for s in waveform_result])
|
|
labels = num.array(['.'.join(s.nslc[:3]) for s in waveform_result])
|
|
channels = num.array([s.nslc[3] for s in waveform_result])
|
|
|
|
for c in set(channels):
|
|
m = self.basemap(source, radius=radius)
|
|
m.draw_dislocation()
|
|
m.draw_nucleation_point()
|
|
m.draw_top_edge()
|
|
|
|
mask_c = channels == c
|
|
|
|
mask_used = num.array([
|
|
True if res.observed is not None and res.synthetic is not None
|
|
else False for res in waveform_result])
|
|
|
|
mask = ~mask_used & mask_c
|
|
m.gmt.psxy(
|
|
in_columns=(lons[mask], lats[mask]),
|
|
S='t20p',
|
|
G=gmtpy.color('aluminium3'),
|
|
*m.jxyr)
|
|
|
|
mask = mask_used & mask_c
|
|
m.gmt.psxy(
|
|
in_columns=(lons[mask], lats[mask]),
|
|
S='t20p',
|
|
G=gmtpy.color('skyblue2'),
|
|
*m.jxyr)
|
|
|
|
for lat, (lon, label) in zip(
|
|
lats[mask], zip(lons[mask], labels[mask])):
|
|
|
|
m.add_label(
|
|
lat, lon, label, font_size=m.gmt.label_font_size() / 2.)
|
|
|
|
item = PlotItem(
|
|
title='Overview of used seismic stations for {} '
|
|
'component'.format(c),
|
|
description='Blue and labeled stations ared used, grey not',
|
|
name='station_map_{}'.format(c))
|
|
|
|
yield item, m
|
|
|
|
|
|
class MisfitPlot(MPLPlot):
|
|
name = 'misfit'
|
|
|
|
def make(self, source, *args, **kwargs):
|
|
return (
|
|
self,
|
|
PlotGroup(
|
|
title='Model misfit',
|
|
description=''),
|
|
self.draw_figures(source, *args, **kwargs))
|
|
|
|
def draw_figures(self, source, *args, **kwargs):
|
|
try:
|
|
plt.rcParams['ytick.right'] = True
|
|
plt.rcParams['ytick.labelright'] = True
|
|
plt.rcParams['ytick.left'] = False
|
|
plt.rcParams['ytick.labelleft'] = False
|
|
except KeyError:
|
|
plt.rcParams['ytick.right'] = True
|
|
plt.rcParams['ytick.left'] = False
|
|
|
|
fig = plt.figure(
|
|
figsize=mpl_papersize(paper='a6', orientation='landscape'))
|
|
|
|
mpl_margins(
|
|
fig,
|
|
top=4., bottom=80.,
|
|
left=4., right=75.,
|
|
units='point')
|
|
|
|
ax = fig.add_subplot(111)
|
|
|
|
mf_log = source.misfit_log
|
|
mf_subsets = mf_log.misfit_subsets
|
|
mf_subsets['joined'] = mf_log.misfit_all
|
|
|
|
iterations = num.arange(mf_log.n_iterations) + 1
|
|
|
|
colors = {k: mpl_graph_color(i) for i, k in enumerate(
|
|
'joined waveform sm_offset gnss insar'.split())}
|
|
|
|
for label, mf_subset in mf_subsets.items():
|
|
line_mf, = ax.plot(iterations, mf_subset, c=colors[label])
|
|
|
|
line_mf.set_label(label)
|
|
|
|
ax.axvline(
|
|
x=mf_log.n_iterations,
|
|
color='lightgrey',
|
|
zorder=0.)
|
|
|
|
ylim = ax.get_ylim()
|
|
|
|
text_kwargs = dict(
|
|
x=mf_log.n_iterations, y=ylim[1] - (ylim[1] - ylim[0]) * 0.1,
|
|
s='Stop cause:\n{}'.format(mf_log.stop_cause.replace('_', ' ')),
|
|
transform=ax.transData,
|
|
horizontalalignment='right',
|
|
verticalalignment='center')
|
|
|
|
try:
|
|
ax.text(
|
|
fontfamily='monospace',
|
|
**text_kwargs)
|
|
except (TypeError, AttributeError):
|
|
from matplotlib.font_manager import FontProperties
|
|
font0 = FontProperties()
|
|
font0.set_family('monospace')
|
|
ax.text(
|
|
fontproperties=font0,
|
|
**text_kwargs)
|
|
|
|
plt.legend(
|
|
ncol=5,
|
|
bbox_to_anchor=(0.5, -0.3),
|
|
borderaxespad=0.,
|
|
loc='center',
|
|
fontsize='x-small',
|
|
fancybox=False,
|
|
frameon=False)
|
|
|
|
ax.tick_params(direction="in", width=1.5, length=6)
|
|
ax.yaxis.set_label_position("right")
|
|
|
|
nice_plot_simple(
|
|
ax, xlabel='# iterations', ylabel='Normalized residual variance',
|
|
title=False,
|
|
spine_pos=['right', 'bottom'])
|
|
|
|
item = PlotItem(
|
|
title='Joined and single targets misfit',
|
|
description='',
|
|
name='misfit')
|
|
|
|
yield item, fig
|
|
|
|
plt.cla()
|
|
plt.clf()
|
|
plt.close()
|
|
|
|
|
|
plot_classes = [
|
|
FinalSlipMap,
|
|
FinalSlipView,
|
|
TracePlot,
|
|
SourceTimeFunction,
|
|
SeismicStationMap,
|
|
MisfitPlot]
|
|
|
|
|
|
plot_classes_gnss = [
|
|
FinalStaticGNSSMap]
|