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.
siria/src/si/ids/report/plot.py

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]