-
Notifications
You must be signed in to change notification settings - Fork 31
/
Mogi.py
261 lines (204 loc) · 8.98 KB
/
Mogi.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
260
261
'''
A Mogi sub-class
Written by T. Shreve, June 2019
'''
# Import Externals stuff
import numpy as np
from argparse import Namespace
import warnings
# Personals
from . import mogifull
from .Pressure import Pressure
#sub-class Mogi
class Mogi(Pressure):
# ----------------------------------------------------------------------
# Initialize class
def __init__(self, name, utmzone=None, ellps='WGS84', lon0=None, lat0=None, verbose=True):
'''
Sub-class implementing Mogi pressure objects.
Args:
* name : Name of the pressure source.
* utmzone : UTM zone (optional, default=None)
* ellps : ellipsoid (optional, default='WGS84')
Kwargs:
* x0, y0 : Center of pressure source in lat/lon or utm
* z0 : Depth
* ax : semi-axes of the Mogi source, should be equal.
* ellps : ellipsoid (optional, default='WGS84')
'''
# Base class init
super(Mogi,self).__init__(name, utmzone=utmzone, ellps=ellps,
lon0=lon0, lat0=lat0, verbose=verbose)
self.source = "Mogi"
self.deltapressure = None # Dimensionless pressure
self.deltavolume = None
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
'''
#Check if deltavolume already defined
self.deltapressure = deltaPressure
self.pressure2volume()
# All done
return
# ----------------------------------------------------------------------
# ----------------------------------------------------------------------
# Convert pressure change to volume change for Mogi
def pressure2volume(self):
'''
Converts pressure change to volume change (m3) for Mogi.
Uses formulation (eq. 15) from:
Amoruso and Crescentini, 2009, Shape and volume change of pressurized ellipsoidal cavities from deformation and seismic data
Assuming radius (a) << depth (z0), formulation is:
deltaV = (3/4)*(V*deltaP/mu)
i.e. point source approximation for eq. 19 in:
Battaglia, Maurizio, Cervelli, P.F., and Murray, J.R., 2013, Modeling crustal deformation near active faults and volcanic centers
If no assumptions on radius vs. depth:
deltaV = (pi*a^3)*(deltaP/mu)*[1+(a/z0)^4]
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()
self.deltavolume = (3./4.)*self.volume*self.deltapressure/self.mu
# All done
return self.deltavolume
# ----------------------------------------------------------------------
# ----------------------------------------------------------------------
# Convert volume change to pressure change for Mogi
def volume2pressure(self):
'''
Converts volume change (m3) to pressure change for Mogi.
Uses formulation (eq. 15) from:
Amoruso and Crescentini, 2009, Shape and volume change of pressurized ellipsoidal cavities from deformation and seismic data
Assuming radius (a) << depth (z0), formulation is:
deltaP = (4/3)*(mu/V)*deltaV
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()
self.deltapressure = (4./3.)*self.mu*self.deltavolume/self.volume
# All done
return self.deltapressure
# ----------------------------------------------------------------------
# ----------------------------------------------------------------------
# Find volume of ellipsoidal cavity
def computeVolume(self):
'''
Computes volume (m3) of spheroidal cavity, given the semimajor axis.
Returns:
* volume : Volume of cavity
'''
a = self.ellipshape['ax']
self.volume = (4./3.)*np.pi*a**3 #Volume of sphere = 4/3*pi*a^3
# 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 Mogi formulations.
Args:
data (Data): Data object from GPS or InSAR.
delta (str): Unit pressure is assumed. Default is "pressure".
volume (float): Volume change. If None, the volume change is calculated based on the pressure change.
Returns:
numpy.ndarray: Array of x, y, and z displacements.
Raises:
Exception: If the shape of the spheroid is not defined.
Notes:
This method uses Mogi's equations to compute the surface displacement at the data location.
The pressure change or volume change can be specified to calculate the displacement.
'''
# 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 Mogi 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
if (DP!=0.0):
# x0, y0 need to be in utm and meters
Ux,Uy,Uz = mogifull.displacement(x, y, z, ellipse.x0m*1000, ellipse.y0m*1000, ellipse.z0, ellipse.ax, 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 spheroid
#
# ----------------------------------------------------------------------
def createShape(self, x, y, z0, a, latlon=True):
'''
Defines the shape of the mogi pressure source.
Args:
* x, y : Center of pressure source in lat/lon or utm
* z0 : Depth
* a : Radius (m)
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
self.ellipshape = {'x0': lon1,'x0m': x1,'y0': lat1, 'y0m': y1, 'z0': z0,'ax': a, 'ay': a, 'az': a, 'strike': 0.0, 'dip': 0.0, 'plunge': 0.0}
#if radius is not much smaller than depth, mogi equations don't hold
if float(a)/float(z0)> 0.1 :
warnings.warn('Results may be inaccurate if depth is not much greater than radius',Warning)
return