1# http://pyrocko.org - GPLv3 

2# 

3# The Pyrocko Developers, 21st Century 

4# ---|P------/S----------~Lg---------- 

5from __future__ import absolute_import, division, print_function 

6 

7import numpy as num 

8 

9from pyrocko.guts import Float, Int 

10from pyrocko import gf 

11 

12from .base import SourceGenerator 

13 

14km = 1e3 

15guts_prefix = 'pf.scenario' 

16 

17 

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) 

25 

26 slip_min = Float.T( 

27 optional=True) 

28 slip_max = Float.T( 

29 optional=True) 

30 

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) 

49 

50 width = Float.T( 

51 optional=True) 

52 length = Float.T( 

53 optional=True) 

54 

55 gamma = Float.T( 

56 default=0.8) 

57 

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() 

63 

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.) 

73 

74 def scale_from_slip(slip, a, b): 

75 return 10**((num.log10(slip) - a) / b) 

76 

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) 

82 

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) 

87 

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 

91 

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.') 

100 

101 strike = self.strike 

102 dip = self.dip 

103 rake = self.rake 

104 

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) 

124 

125 return source 

126 

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)