-
Notifications
You must be signed in to change notification settings - Fork 31
/
Yang.py
259 lines (206 loc) · 9.39 KB
/
Yang.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
'''
A Yang sub-class
Written by T. Shreve, June 2019
'''
# Import Externals stuff
import numpy as np
from argparse import Namespace
# Personals
from . import yangfull
from .Pressure import Pressure
#sub-class Yang
class Yang(Pressure):
# ----------------------------------------------------------------------
# Initialize class
def __init__(self, name, utmzone=None, ellps='WGS84', lon0=None, lat0=None, verbose=True):
'''
Sub-class implementing Yang pressure objects.
Kwargs:
* name : Name of the pressure source.
* utmzone : UTM zone (optional, default=None)
* ellps : ellipsoid (optional, default='WGS84')
'''
# Base class init
super(Yang,self).__init__(name, utmzone=utmzone, ellps=ellps, lon0=lon0, lat0=lat0, verbose=True)
self.source = "Yang"
self.deltapressure = None # Dimensionless pressure
self.deltavolume = None
self.verbose = verbose
return
# ----------------------------------------------------------------------
# ----------------------------------------------------------------------
# Set volume and pressure changes
def setVolume(self, deltaVolume):
'''
Set deltapressure given deltavolume.
Returns:
* deltavolume : Volume change
'''
self.deltavolume = deltaVolume
self.volume2pressure()
# All done
return
# ----------------------------------------------------------------------
# ----------------------------------------------------------------------
# Set volume and pressure changes
def setPressure(self, deltaPressure):
'''
Set deltavolume given deltapressure.
Returns:
* deltapressure : Pressure change
'''
self.deltapressure = deltaPressure
self.pressure2volume()
# All done
return
# ----------------------------------------------------------------------
# ----------------------------------------------------------------------
# Convert pressure change to volume change for Yang
def pressure2volume(self):
'''
Converts pressure change to volume change (m3) for Yang.
Uses empirical formulation from:
Battaglia, Maurizio, Cervelli, P.F., and Murray, J.R., 2013, Modeling crustal deformation near active faults and volcanic centers
Rigorous formulation:
deltaV = ((1-2v)/(2*(1+v)))*V*(deltaP/mu)*((p^T/deltaP)-3),
where V is the volume of the ellipsoidal cavity and p^T is the trace of the stress inside the ellipsoidal cavity.
Empirical formulation:
deltaV = V*(deltaP/mu)((A^2/3)-0.7A+1.37)
Returns:
* deltavolume : Volume change
'''
#Check if deltapressure already defined
if self.deltapressure is None:
raise ValueError("Need to set self.deltapressure with self.setPressure.")
self.volume = self.computeVolume()
A = self.ellipshape['ax']/self.ellipshape['az']
self.deltavolume = (self.volume)*(self.deltapressure/self.mu)*(((A**2)/3.)-(0.7*A)+1.37)
# All done
return self.deltavolume
# ----------------------------------------------------------------------
# ----------------------------------------------------------------------
# Convert volume change to pressure change for Yang
def volume2pressure(self):
'''
Converts volume change (m3) to pressure change for Yang.
Uses empirical formulation from:
Battaglia, Maurizio, Cervelli, P.F., and Murray, J.R., 2013, Modeling crustal deformation near active faults and volcanic centers
Empirical formulation:
deltaP = (deltaV/V)*(mu/((A^2/3)-0.7A+1.37))
Returns:
* deltapressure : Pressure change
'''
#Check if deltavolume already defined
if self.deltavolume is None:
raise ValueError("Need to set self.deltavolume with self.setVolume.")
self.volume = self.computeVolume()
A = self.ellipshape['ax']/self.ellipshape['az']
self.deltapressure = (self.deltavolume/self.volume)*(self.mu/(((A**2)/3.)-(0.7*A)+1.37))
# All done
return self.deltapressure
# ----------------------------------------------------------------------
# ----------------------------------------------------------------------
# Find volume of ellipsoidal cavity
def computeVolume(self):
'''
Computes volume (m3) of ellipsoidal cavity, given the semimajor axis.
Returns:
* volume : Volume of cavity
'''
a = self.ellipshape['az']
A = self.ellipshape['ax']/self.ellipshape['az']
self.volume = (4./3.)*np.pi*a*((a*A)**2) #Volume of ellipsoid = 4/3*pi*a*b*c
# All done
return self.volume
# ----------------------------------------------------------------------
# ----------------------------------------------------------------------
# ----------------------------------------------------------------------
# Some building routines that can be touched... I guess
# ----------------------------------------------------------------------
# ----------------------------------------------------------------------
# ----------------------------------------------------------------------
def pressure2dis(self, data, delta="pressure", volume=None):
'''
Computes the surface displacement at the data location using yang. ~~~ This is where the good stuff happens ~~
Args:
* data : Data object from gps or insar.
* delta : Unit pressure is assumed.
Returns:
* u : x, y, and z displacements
'''
# Set the volume change
if volume is None:
self.volume2pressure()
DP = self.deltapressure
else:
# Set the pressure value
if delta=="pressure":
DP = self.mu #Dimensionless unit pressure
elif delta=="volume":
print("Converting to pressure for Yang Green's function calculations")
DP = self.mu
# Set the shear modulus and poisson's ratio
if self.mu is None:
self.mu = 30e9
if self.nu is None:
self.nu = 0.25
# Get parameters
if self.ellipshape is None:
raise Exception("Need to define shape of spheroid (run self.createShape)")
#define dictionary entries as variables
ellipse = Namespace(**self.ellipshape)
# Get data position -- in m
x = data.x*1000
y = data.y*1000
z = np.zeros(x.shape) # Data are at the surface
strike = ellipse.strike + 90
# Run it for yang pressure source
if (DP!=0.0):
# x0, y0 need to be in utm and meters
Ux,Uy,Uz = yangfull.displacement(x, y, z, ellipse.x0m*1000, ellipse.y0m*1000, ellipse.z0, ellipse.az, ellipse.ax/ellipse.az, ellipse.dip, strike, DP/self.mu, self.nu)
else:
dp_dis = np.zeros((len(x), 3))
# All done
# concatenate into one array
u = np.vstack([Ux, Uy, Uz]).T
return u
# ----------------------------------------------------------------------
# Define the shape of the ellipse
#
# ----------------------------------------------------------------------
def createShape(self, x, y, z0, ax, ay, az, dip, strike, latlon=True):
'''
Defines the shape of the mogi pressure source.
Args:
* x, y : Center of pressure source in lat/lon or utm
* z0 : Depth
* (ax, ay, az) : Principle semi-axes (m) before rotations applied. az will be the semi-major axis, while ax = ay.
* dip : Plunge angle (dip=90 is vertical source)
* strike : Azimuth (azimuth=0 is aligned North)
Returns:
None
'''
if latlon is True:
self.lon, self.lat = x, y
lon1, lat1 = x, y
self.pressure2xy()
x1, y1 = self.xf, self.yf
else:
self.xf, self.yf = x, y
x1, y1 = x, y
self.pressure2ll()
lon1, lat1 = self.lon, self.lat
### before rotation, so az = semi-major axis and ax = ay
c, b, a = np.sort([ax,ay,az])
A = b/a
if A == 1:
if self.verbose: print('If semi-minor and semi-major axes are equal, more efficient to use Mogi.py')
### Double check this
if float(z0) < (float(A)*float(a))**2/float(a):
if self.verbose: print('WARNING: radius of curvature has to be less than the depth, will output "null" shape')
#... this model is still taken into account in Bayesian inverison, but should be rejected.')
self.ellipshape = {'x0': 0.,'x0m': 0.,'y0': 0.,'y0m': 0.,'z0': 0.,'a': 0.,'A': 0.,'dip': 0.,'strike': 0.}
else:
if self.verbose: print('Using CDM conventions for rotation - dip = 90 is vertical, rotation clockwise around Y-axis (N-S). dip = 0, strike = 0 source elongated N-S')
self.ellipshape = {'x0': lon1,'x0m': x1,'y0': lat1,'y0m': y1,'z0': z0,'ax': b,'ay': c,'az': a,'dip': dip,'strike': strike,'plunge': 0.0}
return