-
Notifications
You must be signed in to change notification settings - Fork 8
Expand file tree
/
Copy pathfegn17.py
More file actions
executable file
·448 lines (391 loc) · 17.8 KB
/
fegn17.py
File metadata and controls
executable file
·448 lines (391 loc) · 17.8 KB
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
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
#!/usr/bin/python2.7
from __future__ import print_function
import matplotlib.pyplot as plt
import subprocess
import numpy as np
import sys, os
from scipy.io import loadmat, savemat
gcf, gca = plt.gcf, plt.gca
"""
fegn17 is the same as egn17 except that the computation has been embeded into a function
so that it can be easilly called for several models
dfegn17 computes finite difference eigenfunction kernels
=> outputs are still messy
=> the subsequent calls to fegn17 should be parallelized
=> display is only for demo
"""
####################
def execbash(script, tmpdir):
proc = subprocess.Popen("/bin/bash", stdin=subprocess.PIPE, stdout=subprocess.PIPE, cwd = tmpdir)
stdout, stderr = proc.communicate(script)
return stdout, stderr
####################
def read_TXTout(fin):
with open(fin, 'r') as fid:
out = {}
while True:
l = fid.readline()
if l == "": break
l = l.split('\n')[0].strip()
####################
if "Model:" in l:
#first section in the file
fid.readline() #skip header line like "LAYER H(km) Vp(km/s) Vs(km/s) Density QA(inv) QB(inv)"
LAYER, H, Vp, Vs, Density, QA, QB = [], [], [], [], [], [], []
while True:
l = fid.readline()
if l == "": break
layer, h, vp, vs, density, qa, qb = np.asarray(l.split('\n')[0].strip().split(), float)
for W, w in zip([LAYER, H, Vp, Vs, Density, QA, QB], [layer, h, vp, vs, density, qa, qb]):
W.append(w)
if h == 0: break
del layer, h, vp, vs, density, qa, qb #prevent confusion with captial variables
Z = np.concatenate(([0.], np.cumsum(H[:-1])))
out['model'] = {"Z" : Z, "H" : H, "Vp" : Vp, "Vs" : Vs, "Rh" : Density}
####################
elif "WAVE" in l and "MODE #" in l:
#------------
wavetype = l.split('WAVE')[0].strip()[0] #first letter R or L
modenum = int(l.split('#')[-1].split()[0])
key = "%s%d" % (wavetype, modenum)
#------------
l = fid.readline()
l = l.split('\n')[0].strip()
l = l.split()
T, C, U = np.asarray([l[2], l[5], l[8]], float)
#------------
l = fid.readline()
l = l.split('\n')[0].strip()
l = l.split()
AR, GAMMA, ZREF = np.asarray([l[1], l[3], l[5]], float) #dont know what it is
out[key] = {"T" : T, "C" : C, "U" : U, "AR" : AR, "GAMMA" : GAMMA, "ZREF" : ZREF}
#------------
l = fid.readline() #skip header line
#------------
if wavetype == "R":
UR, TR, UZ, TZ, DCDH, DCDA, DCDB, DCDR = [np.empty(len(H), float) for _ in xrange(8)]
for i in xrange(len(H)):
l = np.asarray(fid.readline().split('\n')[0].split(), float)
M = int(l[0])
for W, w in zip([UR, TR, UZ, TZ, DCDH, DCDA, DCDB, DCDR], l[1:]):
W[M - 1] = w
out[key]['UR'] = UR
out[key]['TR'] = TR
out[key]['UZ'] = UZ
out[key]['TZ'] = TZ
out[key]['DCDH'] = DCDH
out[key]['DCDA'] = DCDA
out[key]['DCDB'] = DCDB
out[key]['DCDR'] = DCDR
elif wavetype == "L":
UT, TT, DCDH, DCDB, DCDR = [np.empty(len(H), float) for _ in xrange(5)]
for i in xrange(len(H)):
l = np.asarray(fid.readline().split('\n')[0].split(), float)
M = int(l[0])
for W, w in zip([UT, TT, DCDH, DCDB, DCDR], l[1:]):
W[M - 1] = w
out[key]['UT'] = UT
out[key]['TT'] = TT
out[key]['DCDH'] = DCDH
out[key]['DCDB'] = DCDB
out[key]['DCDR'] = DCDR
else: raise Exception('error')
return out
####################
def readmod96(mod96file):
"""read files at mod96 format (see Herrmann's doc)"""
with open(mod96file, 'r') as fid:
while True:
l = fid.readline()
if l == "": break
l = l.split('\n')[0]
if "H" in l and "VP" in l and "VS" in l:
H, VP, VS, RHO, QP, QS, ETAP, ETAS, FREFP, FREFS = [[] for _ in xrange(10)]
while True:
l = fid.readline()
l = l.split('\n')[0]
l = np.asarray(l.split(), float)
for W, w in zip([H, VP, VS, RHO, QP, QS, ETAP, ETAS, FREFP, FREFS], l):
W.append(w)
if l[0] == 0.: #thickness is 0 = ending signal (half space)
break
if l[0] == 0.: break
H, VP, VS, RHO, QP, QS, ETAP, ETAS, FREFP, FREFS = [np.asarray(_, float) for _ in H, VP, VS, RHO, QP, QS, ETAP, ETAS, FREFP, FREFS]
Z = np.concatenate(([0.], H[:-1].cumsum()))
return Z, H, VP, VS, RHO, QP, QS, ETAP, ETAS, FREFP, FREFS
####################
def writemod96(filename, H, VP, VS, RHO, QP=None, QS=None, ETAP=None, ETAS=None, FREFP=None, FREFS=None):
header ='''MODEL.01
whatever
ISOTROPIC
KGS
FLAT EARTH
1-D
CONSTANT VELOCITY
LINE08
LINE09
LINE10
LINE11
H(KM) VP(KM/S) VS(KM/S) RHO(GM/CC) QP QS ETAP ETAS FREFP FREFS
'''
assert filename.endswith('.mod96')
if QP is None:
QP = np.zeros_like(H)
ETAP = np.zeros_like(H)
FREFP = np.ones_like(H)
if QS is None:
QS = np.zeros_like(H)
ETAS = np.zeros_like(H)
FREFS = np.ones_like(H)
with open(filename, 'w') as fid:
fid.write(header)
for h, vp, vs, rho, qp, qs, etap, etas, frefp, frefs in \
zip(H, VP, VS, RHO, QP, QS, ETAP, ETAS, FREFP, FREFS):
fid.write('%f %f %f %f %f %f %f %f %f %f\n' % (h, vp, vs, rho, qp, qs, etap, etas, frefp, frefs))
####################
def checkCPS():
"verify if CPS is installed"
stdout, _ = execbash('which sdisp96', ".")
if stdout == "":
raise Exception('sdisp96 not found, make sure CPS is installed and added to the path')
####################
def fegn17(mod96file, wavetype, nmod, freq, tmpdir = None, fig = None, cleanup = True):
""" function to compute eigenfunctions and phase velocity sensitivity kernels
input
mod96file = ascii file at mod96 format (see Herrmann's documentation)
wavetype = R or L for Rayleigh or Love
nmod = mode number >= 0
freq = frequency (Hz) > 0
tmpdir = non existent directory name to create and remove after running
fig = figure for display or None
output
out = dictionnary containing the function and phase velocity sensitivity kernels
for all modes between 0 and nmod
"""
#------------------- check inputs
assert os.path.exists(mod96file)
assert wavetype in 'RL'
assert nmod >= 0
assert freq > 0.0
#------------------- create temporary directory
if tmpdir is None: tmpdir = "/tmp/tmpdir_fegn17_%10d" % (np.random.rand() * 1e10)
while os.path.isdir(tmpdir):
#make sure tmpdir does not exists
tmpdir += "_%10d" % (np.random.rand() * 1.0e10)
os.mkdir(tmpdir)
#------------------- write bash script
script = """
#rm -f DISTFILE.dst sdisp96.??? s[l,r]egn96.??? S[L,R]DER.PLT S[R,L]DER.TXT
cat << END > DISTFILE.dst
10. 0.125 256 -1.0 6.0
END
ln -s {mod96file} model.mod96
############################### prepare dispersion
sprep96 -M model.mod96 -dfile DISTFILE.dst -NMOD {nmodmax} -{wavetype} -FREQ {freq}
############################### run dispersion
sdisp96
############################### compute eigenfunctions
s{minuswavetype}egn96 -DE
############################### ouput eigenfunctions
sdpder96 -{wavetype} -TXT # plot and ascii output
############################### clean up
#rm -f sdisp96.dat DISTFILE.dst sdisp96.??? s[l,r]egn96.??? S[L,R]DER.PLT
""".format(mod96file = os.path.realpath(mod96file),
nmodmax = nmod + 1,
wavetype = wavetype,
minuswavetype = wavetype.lower(),
freq = freq)
script = "\n".join([_.strip() for _ in script.split('\n')]) #remove indentation from script
#------------------- execute bash commands
stdout, stderr = execbash(script, tmpdir = tmpdir)
expected_output = "%s/S%sDER.TXT" % (tmpdir, wavetype)
if not os.path.exists(expected_output):
raise Exception('output file %s not found, script failed \n%s' % (expected_output, stderr))
#------------------- read Herrmann's output
out = read_TXTout(expected_output)
if cleanup:
#remove temporary directory
execbash('rm -rf %s' % tmpdir, ".")
#------------------- display results for last mode
if fig is not None:
"""not ready, only for demo"""
key = '%s%d' % (wavetype, nmod)
if key not in out.keys():
raise Exception('key %s not found in output file, is the frequency (%fHz) below the cut-off frequency for mode %d?' % (key, freq, nmod))
ax1 = fig.add_subplot(121)
ax2 = fig.add_subplot(122, sharey = ax1)
ax1.invert_yaxis()
#------------------
z = np.concatenate((np.repeat(out['model']["Z"], 2)[1:], [sum(out['model']["H"]) * 1.1]))
vp = np.repeat(out['model']["Vp"], 2)
vs = np.repeat(out['model']["Vs"], 2)
rh = np.repeat(out['model']["Rh"], 2)
ax1.plot(vp, z, label = "Vp")
ax1.plot(vs, z, label = "Vs")
ax1.plot(rh, z, label = "Rh")
ax1.legend()
ax1.grid(True)
#------------------
if wavetype == "R":
ax2.plot(out[key]['UR'], out['model']['Z'], label = "UR") #radial displacement
ax2.plot(out[key]['UZ'], out['model']['Z'], label = "UZ")
ax2.plot(out[key]['TR'], out['model']['Z'], label = "TR") #radial stress
ax2.plot(out[key]['TZ'], out['model']['Z'], label = "TZ")
if wavetype == "L":
ax2.plot(out[key]['UT'], out['model']['Z'], label = "UT")
ax2.plot(out[key]['TT'], out['model']['Z'], label = "TT")
ax2.set_title("%s : T = %fs" % (key, out[key]["T"]))
ax2.legend()
ax1.set_ylabel('depth (km)')
ax2.set_xlabel('eigenfunctions')
ax2.grid(True)
#-------------------
return out
####################
def dfegn17(mod96file, wavetype, nmod, freq, pertu = 0.05, tmpdir = None, cleanup = True, fig = None):
"""compute eigenfunctions sensitivity kernels using finite differences (serial)
input :
see function egn17
pertu is the relative perturbation to impose in each layer
output :
dout : dictionnary
dout['Z'] : 1d depth array, similar for depth profile and eigenfunctions
dout['DURDVS'] : 3d kernel, first derivative of UR relative to VS in each layer
first index = mode number
second index = number of the perturbated layer
third index = sample of the eigenfunction that has been perturbated
"""
#create the main temporary directory
if tmpdir is None:
tmpdir = "/tmp/tmpdir_dfegn17_%10d" % (np.random.rand() * 1e10)
while os.path.isdir(tmpdir):
tmpdir += "_%10d" % (np.random.rand() * 1e10)
os.mkdir(tmpdir)
#read starting model
Z, H, VP, VS, RHO, QP, QS, ETAP, ETAS, FREFP, FREFS = readmod96(mod96file)
model0 = np.concatenate((H, VP, VS, RHO))
nlayer = len(H)
IH = np.arange(nlayer) #index of thickness parameters
IVP = np.arange(1 * nlayer, 2 * nlayer) #index of Vp parameters
IVS = np.arange(2 * nlayer, 3 * nlayer)
IRH = np.arange(3 * nlayer, 4 * nlayer)
#compute eigenfunctions for starting model
out0 = fegn17(mod96file, wavetype, nmod, freq, tmpdir = "%s/%s" % (tmpdir, "startingmodel"), fig = None, cleanup = False)
#initiate kernels
if wavetype == "R":
DURDN = np.zeros((nmod + 1, nlayer, len(model0)), float) * np.nan
DUZDN = np.zeros((nmod + 1, nlayer, len(model0)), float) * np.nan
DTRDN = np.zeros((nmod + 1, nlayer, len(model0)), float) * np.nan
DTZDN = np.zeros((nmod + 1, nlayer, len(model0)), float) * np.nan
elif wavetype == "L":
DUTDN = np.zeros((nmod + 1, nlayer, len(model0)), float) * np.nan
DTTDN = np.zeros((nmod + 1, nlayer, len(model0)), float) * np.nan
#perturbate each parameter subsequently
for nparam in xrange(len(model0)):
if nparam == nlayer - 1 :
#thickness of the half space: meaningless
continue
#prepare the perturbated model and determine the sub-directory name
modeln = model0.copy()
modeln[nparam] *= (1.0 + pertu)
tmpdir_n = "%s/param%06d" % (tmpdir, nparam)
mod96file_n = "%s/model_param%06d.mod96" % (tmpdir, nparam) #write the perturbated file in the main temporary directory
#write perturbated model
writemod96(mod96file_n,
H = modeln[IH],
VP = modeln[IVP],
VS = modeln[IVS],
RHO = modeln[IRH],
QP = QP, QS = QS, ETAP=ETAP, ETAS=ETAS, FREFP=FREFP, FREFS=FREFS) #keep attenuation untouched
#call fegn17 with perturbated model
out_n = fegn17(mod96file_n, wavetype, nmod, freq, tmpdir = tmpdir_n, fig = None, cleanup = False)
dm = (modeln[nparam] - model0[nparam])
for modnum in xrange(nmod+1):
key = "%s%d" % (wavetype, modnum)
if wavetype == "R":
DURDN[modnum, :, nparam] = (out_n[key]['UR'] - out0[key]['UR']) / dm
DUZDN[modnum, :, nparam] = (out_n[key]['UZ'] - out0[key]['UZ']) / dm
DTRDN[modnum, :, nparam] = (out_n[key]['TR'] - out0[key]['TR']) / dm
DTZDN[modnum, :, nparam] = (out_n[key]['TZ'] - out0[key]['TZ']) / dm
elif wavetype == "L":
DUTDN[modnum, :, nparam] = (out_n[key]['UT'] - out0[key]['UT']) / dm
DTTDN[modnum, :, nparam] = (out_n[key]['TT'] - out0[key]['TT']) / dm
#-------------------
dout = {"Z" : out0['model']["Z"], "T" : 1. / freq}
if wavetype == "R":
dout['DURDH'] = DURDN[:, :, IH ]
dout['DURDVP'] = DURDN[:, :, IVP]
dout['DURDVS'] = DURDN[:, :, IVS]
dout['DURDRH'] = DURDN[:, :, IRH]
dout['DUZDH'] = DUZDN[:, :, IH ]
dout['DUZDVP'] = DUZDN[:, :, IVP]
dout['DUZDVS'] = DUZDN[:, :, IVS]
dout['DUZDRH'] = DUZDN[:, :, IRH]
dout['DTRDH'] = DTRDN[:, :, IH ]
dout['DTRDVP'] = DTRDN[:, :, IVP]
dout['DTRDVS'] = DTRDN[:, :, IVS]
dout['DTRDRH'] = DTRDN[:, :, IRH]
dout['DTZDH'] = DTZDN[:, :, IH ]
dout['DTZDVP'] = DTZDN[:, :, IVP]
dout['DTZDVS'] = DTZDN[:, :, IVS]
dout['DTZDRH'] = DTZDN[:, :, IRH]
elif wavetype == "L":
dout['DUTDH'] = DUTDN[:, :, IH ]
dout['DUTDVP'] = DUTDN[:, :, IVP]
dout['DUTDVS'] = DUTDN[:, :, IVS]
dout['DUTDRH'] = DUTDN[:, :, IRH]
dout['DTTDH'] = DTTDN[:, :, IH ]
dout['DTTDVP'] = DTTDN[:, :, IVP]
dout['DTTDVS'] = DTTDN[:, :, IVS]
dout['DTTDRH'] = DTTDN[:, :, IRH]
if cleanup:
#remove temporary directory
execbash('rm -rf %s' % tmpdir, ".")
if fig is not None:
ax2 = fig.add_subplot(224)
ax1 = fig.add_subplot(223, sharey = ax2)
ax3 = fig.add_subplot(222, sharex = ax2)
#------------------
ax1.invert_yaxis()
#------------------
z = np.concatenate((np.repeat(out0['model']["Z"], 2)[1:], [sum(out0['model']["H"]) * 1.1]))
vp = np.repeat(out0['model']["Vp"], 2)
vs = np.repeat(out0['model']["Vs"], 2)
rh = np.repeat(out0['model']["Rh"], 2)
ax1.plot(vp, z, label = "Vp")
ax1.plot(vs, z, label = "Vs")
ax1.plot(rh, z, label = "Rh")
ax1.legend()
ax1.grid(True)
ax1.set_ylabel('model depth (km)')
#------------------
if wavetype == "R":
vmax = abs(dout['DUZDVS'][nmod, :, :]).max()
ax3.plot(dout["Z"], out0["%s%d" % (wavetype, nmod)]['UZ'], label = "UZ")
Y = dout['DUZDVS'][nmod, :, :]
Y = np.ma.masked_where(np.isnan(Y), Y)
ax2.pcolormesh(dout["Z"], dout["Z"], Y, vmin = -vmax, vmax = vmax)
ax2.set_title("DUZ/DVS, T = %f, mode %d" % (1. / freq, nmod))
ax3.set_xlabel('eigenfunction depth (km)')
elif wavetype == "L":
vmax = abs(dout['DUTDVS'][nmod, :, :]).max()
ax3.plot(dout["Z"], out0["%s%d" % (wavetype, nmod)]['UT'], label = "UT")
Y = dout['DUTDVS'][nmod, :, :]
Y = np.ma.masked_where(np.isnan(Y), Y)
ax2.pcolormesh(dout["Z"], dout["Z"], Y, vmin = -vmax, vmax = vmax)
ax2.set_title("DUT/DVS, T = %f, mode %d" % (1. / freq, nmod))
ax3.set_xlabel('eigenfunction depth (km)')
ax3.xaxis.set_label_position("top")
ax3.legend()
ax2.grid(True)
ax3.grid(True)
return dout
####################
if __name__ == "__main__":
mod96file = "./example.mod96"
wavetype = "R"
nmod = 1
freq = 0.5
dout = dfegn17(mod96file, wavetype, nmod, freq, pertu = 0.05, tmpdir = "./tmptmptmp", fig = gcf())
gcf().show()
raw_input('pause')