1# http://pyrocko.org - GPLv3 

2# 

3# The Pyrocko Developers, 21st Century 

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

5 

6import numpy as num 

7 

8from pyrocko.guts import Float, Int 

9from pyrocko import gf 

10 

11from .base import SourceGenerator 

12 

13km = 1e3 

14guts_prefix = 'pf.scenario' 

15 

16 

17class PseudoDynamicRuptureGenerator(SourceGenerator): 

18 depth_min = Float.T( 

19 default=0.0) 

20 depth_max = Float.T( 

21 default=5*km) 

22 decimation_factor = Int.T( 

23 default=1) 

24 

25 slip_min = Float.T( 

26 optional=True) 

27 slip_max = Float.T( 

28 optional=True) 

29 

30 strike = Float.T( 

31 optional=True) 

32 dip = Float.T( 

33 optional=True) 

34 rake = Float.T( 

35 optional=True) 

36 depth = Float.T( 

37 optional=True) 

38 nx = Int.T( 

39 default=5, 

40 optional=True) 

41 ny = Int.T( 

42 default=5, 

43 optional=True) 

44 nucleation_x = Float.T( 

45 optional=True) 

46 nucleation_y = Float.T( 

47 optional=True) 

48 

49 width = Float.T( 

50 optional=True) 

51 length = Float.T( 

52 optional=True) 

53 

54 gamma = Float.T( 

55 default=0.8) 

56 

57 def draw_slip(self, rstate): 

58 if self.slip_min is not None and self.slip_max is not None: 

59 return rstate.uniform(self.slip_min, self.slip_max) 

60 else: 

61 return rstate.rand() 

62 

63 def get_source(self, ievent): 

64 rstate = self.get_rstate(ievent) 

65 time = rstate.uniform(self.time_min, self.time_max) 

66 lat, lon = self.get_latlon(ievent) 

67 depth = rstate.uniform(self.depth_min, self.depth_max) 

68 nucleation_x = self.nucleation_x if self.nucleation_x is not None \ 

69 else rstate.uniform(-1., 1.) 

70 nucleation_y = self.nucleation_y if self.nucleation_y is not None \ 

71 else rstate.uniform(-1., 1.) 

72 

73 def scale_from_slip(slip, a, b): 

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

75 

76 if self.slip_min is not None and self.slip_max is not None: 

77 slip = self.draw_slip(rstate) 

78 # After K. Thingbaijam et al. (2017) - Tab. 2, Normal faulting 

79 length = scale_from_slip(slip, a=-2.302, b=1.302) 

80 width = scale_from_slip(slip, a=-3.698, b=2.512) 

81 

82 else: 

83 slip = self.draw_slip(rstate) 

84 length = scale_from_slip(slip, a=-2.302, b=1.302) 

85 width = scale_from_slip(slip, a=-3.698, b=2.512) 

86 

87 length = length if not self.length else self.length 

88 width = width if not self.width else self.width 

89 depth = depth if not self.depth else self.depth 

90 

91 if self.strike is None and self.dip is None and self.rake is None: 

92 strike, rake = rstate.uniform(-180., 180., 2) 

93 dip = rstate.uniform(0., 90.) 

94 else: 

95 if None in (self.strike, self.dip, self.rake): 

96 raise ValueError( 

97 'PseudoDynamicRuptureGenerator: ' 

98 'strike, dip, rake must be used in combination.') 

99 

100 strike = self.strike 

101 dip = self.dip 

102 rake = self.rake 

103 

104 source = gf.PseudoDynamicRupture( 

105 time=float(time), 

106 lat=float(lat), 

107 lon=float(lon), 

108 anchor='top', 

109 depth=float(depth), 

110 length=float(length), 

111 width=float(width), 

112 strike=float(strike), 

113 dip=float(dip), 

114 rake=float(rake), 

115 slip=slip, 

116 nucleation_x=float(nucleation_x), 

117 nucleation_y=float(nucleation_y), 

118 nx=self.nx, 

119 ny=self.ny, 

120 decimation_factor=self.decimation_factor, 

121 smooth_rupture=True, 

122 gamma=self.gamma) 

123 

124 return source 

125 

126 def add_map_artists(self, automap): 

127 for source in self.get_sources(): 

128 automap.gmt.psxy( 

129 in_rows=source.outline(cs='lonlat'), 

130 L='+p2p,black', 

131 W='1p,black', 

132 G='black', 

133 t=50, 

134 *automap.jxyr)