1# http://pyrocko.org - GPLv3
2#
3# The Pyrocko Developers, 21st Century
4# ---|P------/S----------~Lg----------
5from __future__ import absolute_import, division, print_function
7import numpy as num
9from pyrocko.guts import Float, Int
10from pyrocko import gf
12from .base import SourceGenerator
14km = 1e3
15guts_prefix = 'pf.scenario'
18class PseudoDynamicRuptureGenerator(SourceGenerator):
19 depth_min = Float.T(
20 default=0.0)
21 depth_max = Float.T(
22 default=5*km)
23 decimation_factor = Int.T(
24 default=1)
26 slip_min = Float.T(
27 optional=True)
28 slip_max = Float.T(
29 optional=True)
31 strike = Float.T(
32 optional=True)
33 dip = Float.T(
34 optional=True)
35 rake = Float.T(
36 optional=True)
37 depth = Float.T(
38 optional=True)
39 nx = Int.T(
40 default=5,
41 optional=True)
42 ny = Int.T(
43 default=5,
44 optional=True)
45 nucleation_x = Float.T(
46 optional=True)
47 nucleation_y = Float.T(
48 optional=True)
50 width = Float.T(
51 optional=True)
52 length = Float.T(
53 optional=True)
55 gamma = Float.T(
56 default=0.8)
58 def draw_slip(self, rstate):
59 if self.slip_min is not None and self.slip_max is not None:
60 return rstate.uniform(self.slip_min, self.slip_max)
61 else:
62 return rstate.rand()
64 def get_source(self, ievent):
65 rstate = self.get_rstate(ievent)
66 time = rstate.uniform(self.time_min, self.time_max)
67 lat, lon = self.get_latlon(ievent)
68 depth = rstate.uniform(self.depth_min, self.depth_max)
69 nucleation_x = self.nucleation_x if self.nucleation_x is not None \
70 else rstate.uniform(-1., 1.)
71 nucleation_y = self.nucleation_y if self.nucleation_y is not None \
72 else rstate.uniform(-1., 1.)
74 def scale_from_slip(slip, a, b):
75 return 10**((num.log10(slip) - a) / b)
77 if self.slip_min is not None and self.slip_max is not None:
78 slip = self.draw_slip(rstate)
79 # After K. Thingbaijam et al. (2017) - Tab. 2, Normal faulting
80 length = scale_from_slip(slip, a=-2.302, b=1.302)
81 width = scale_from_slip(slip, a=-3.698, b=2.512)
83 else:
84 slip = self.draw_slip(rstate)
85 length = scale_from_slip(slip, a=-2.302, b=1.302)
86 width = scale_from_slip(slip, a=-3.698, b=2.512)
88 length = length if not self.length else self.length
89 width = width if not self.width else self.width
90 depth = depth if not self.depth else self.depth
92 if self.strike is None and self.dip is None and self.rake is None:
93 strike, rake = rstate.uniform(-180., 180., 2)
94 dip = rstate.uniform(0., 90.)
95 else:
96 if None in (self.strike, self.dip, self.rake):
97 raise ValueError(
98 'PseudoDynamicRuptureGenerator: '
99 'strike, dip, rake must be used in combination.')
101 strike = self.strike
102 dip = self.dip
103 rake = self.rake
105 source = gf.PseudoDynamicRupture(
106 time=float(time),
107 lat=float(lat),
108 lon=float(lon),
109 anchor='top',
110 depth=float(depth),
111 length=float(length),
112 width=float(width),
113 strike=float(strike),
114 dip=float(dip),
115 rake=float(rake),
116 slip=slip,
117 nucleation_x=float(nucleation_x),
118 nucleation_y=float(nucleation_y),
119 nx=self.nx,
120 ny=self.ny,
121 decimation_factor=self.decimation_factor,
122 smooth_rupture=True,
123 gamma=self.gamma)
125 return source
127 def add_map_artists(self, automap):
128 for source in self.get_sources():
129 automap.gmt.psxy(
130 in_rows=source.outline(cs='lonlat'),
131 L='+p2p,black',
132 W='1p,black',
133 G='black',
134 t=50,
135 *automap.jxyr)