Compare commits
4 Commits
develop
...
tomography
Author | SHA1 | Date | |
---|---|---|---|
274bf31098 | |||
f48793e359 | |||
80fd07f0f8 | |||
cec6cc24c5 |
@ -5,8 +5,12 @@
|
||||
#$ -pe smp 40
|
||||
##$ -l mem=3G
|
||||
#$ -l h_vmem=6G
|
||||
#$ -l os=*stretch
|
||||
##$ -l os=*stretch
|
||||
#$ -q low.q@minos11,low.q@minos12,low.q@minos13,low.q@minos14,low.q@minos15
|
||||
|
||||
conda activate pylot_311
|
||||
|
||||
python ./autoPyLoT.py -i /home/marcel/.pylot/pylot_adriaarray.in -c 20 -dmt processed
|
||||
#python ./autoPyLoT.py -i /home/marcel/.pylot/pylot_adriaarray_m5.0-5.4.in -c 20 -dmt processed
|
||||
#python ./autoPyLoT.py -i /home/marcel/.pylot/pylot_adriaarray_m5.4-5.7.in -c 20 -dmt processed
|
||||
#python ./autoPyLoT.py -i /home/marcel/.pylot/pylot_adriaarray_m5.7-6.0.in -c 20 -dmt processed
|
||||
python ./autoPyLoT.py -i /home/marcel/.pylot/pylot_adriaarray_m6.0-10.0.in -c 20 -dmt processed
|
||||
|
0
pylot/tomography/__init__.py
Normal file
0
pylot/tomography/__init__.py
Normal file
23
pylot/tomography/fmtomo.py
Normal file
23
pylot/tomography/fmtomo.py
Normal file
@ -0,0 +1,23 @@
|
||||
from pylot.tomography.fmtomo_utils import Tomo3d
|
||||
import os
|
||||
|
||||
citer = 0
|
||||
niter = 12
|
||||
n_proc = 4 # only four processes for minimal example with four sources
|
||||
|
||||
# for some reason this did not work as expected and was commented out
|
||||
#if os.path.isfile('inviter.in'):
|
||||
# with open('inviter.in', 'r') as infile:
|
||||
# citer = int(infile.read())
|
||||
# print ('Continue on iteration step ', citer)
|
||||
|
||||
tomo = Tomo3d(os.getcwd(), os.getcwd(), overwrite=True, buildObs=False, saveRays=[6, 12], citer=citer)
|
||||
|
||||
try:
|
||||
tomo.runTOMO3D(n_proc, niter)
|
||||
except KeyboardInterrupt:
|
||||
print('runTTOMO3D interrupted by user or machine. Cleaning up.')
|
||||
except Exception as e:
|
||||
print(f'Catching unknown Exception in runTOMO3D: {e}. Trying to clean up...')
|
||||
finally:
|
||||
tomo.removeDirectories()
|
2
pylot/tomography/fmtomo_tools/__init__.py
Normal file
2
pylot/tomography/fmtomo_tools/__init__.py
Normal file
@ -0,0 +1,2 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
177
pylot/tomography/fmtomo_tools/compare_arrivals_hybrid.py
Executable file
177
pylot/tomography/fmtomo_tools/compare_arrivals_hybrid.py
Executable file
@ -0,0 +1,177 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Small script to compare arrival times of hybrid tau-p/fmm with simple tau-p for standard earth model
|
||||
|
||||
import os
|
||||
import argparse
|
||||
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
from obspy.taup import TauPyModel
|
||||
|
||||
from pylot.tomography.fmtomo_tools.fmtomo_teleseismic_utils import organize_receivers, organize_sources, organize_event_names
|
||||
|
||||
|
||||
def read_file(fnin):
|
||||
infile = open(fnin, 'r')
|
||||
return infile.readlines()
|
||||
|
||||
|
||||
def compare_arrivals(arrivals_file, receivers_file, sources_file, input_source_file, model='ak135_diehl_v2', exclude_phases=[]):
|
||||
'''
|
||||
Reads FMTOMO arrivals.dat file together with corresponding receiver, source and input_source files to match
|
||||
each arrival to a source and receiver combination, calculate tau-p arrivals for a given earth model and
|
||||
calculate differences
|
||||
'''
|
||||
arrivals_taup = {}
|
||||
arrivals_tomo = {}
|
||||
receiver_ids = {}
|
||||
event_names = {}
|
||||
events = organize_event_names(input_source_file)
|
||||
model = TauPyModel(model)
|
||||
|
||||
# organize sources and receivers in dictionaries containing their ID used by FMTOMO as dictionary key
|
||||
receivers_dict = organize_receivers(receivers_file)
|
||||
sources_dict = organize_sources(sources_file)
|
||||
|
||||
# read arrivals file
|
||||
with open(arrivals_file, 'r') as infile_arrivals:
|
||||
arrivals = infile_arrivals.readlines()
|
||||
|
||||
count = 0
|
||||
for src_number, source in sources_dict.items():
|
||||
if source['phase'] in exclude_phases:
|
||||
continue
|
||||
src_name = events[src_number - 1]
|
||||
if not src_name in event_names.keys():
|
||||
arrivals_taup[src_name] = []
|
||||
arrivals_tomo[src_name] = []
|
||||
receiver_ids[src_name] = []
|
||||
event_names[src_name] = count
|
||||
count += 1
|
||||
|
||||
for line in arrivals:
|
||||
# read line by line from fmtomo_tools output file arrivals.dat
|
||||
rec_id, src_id, ray_id, refl, arrival_time, diff, head = line.split()
|
||||
arrival_time = float(arrival_time)
|
||||
rec_id = int(rec_id)
|
||||
src_id = int(src_id)
|
||||
ray_id = int(ray_id)
|
||||
|
||||
# identify receiver and source using dictionary
|
||||
receiver = receivers_dict[rec_id]
|
||||
source = sources_dict[src_id]
|
||||
src_name = events[src_id - 1]
|
||||
phase = source['phase']
|
||||
if phase in exclude_phases: continue
|
||||
taup_arrival = model.get_travel_times_geo(source_depth_in_km=source['depth'],
|
||||
source_latitude_in_deg=source['lat'],
|
||||
source_longitude_in_deg=source['lon'],
|
||||
receiver_latitude_in_deg=receiver['lat'],
|
||||
receiver_longitude_in_deg=receiver['lon'],
|
||||
phase_list=[phase])
|
||||
receiver_depth_in_km = 6371. - receiver['rad']
|
||||
if len(taup_arrival) == 1:
|
||||
taup_arrival_time = taup_arrival[0].time
|
||||
else:
|
||||
taup_arrival_time = np.nan
|
||||
arrivals_taup[src_name].append(taup_arrival_time)
|
||||
arrivals_tomo[src_name].append(arrival_time)
|
||||
receiver_ids[src_name].append(rec_id)
|
||||
|
||||
#plt.plot([min(arrivals_taup),max(arrivals_taup)],[min(arrivals_taup), max(arrivals_taup)], 'k-')
|
||||
sorted_by_first_arrival = sorted([(src_name, min(arrivals)) for src_name, arrivals in arrivals_taup.items()],
|
||||
key=lambda x: x[1])
|
||||
|
||||
# print some output for analysis
|
||||
for item in sorted_by_first_arrival:
|
||||
print(item)
|
||||
#[print(source) for source in sources_dict.items()]
|
||||
#[print(item) for item in enumerate(events)]
|
||||
|
||||
current_fmtomo_folder_name = os.path.split(os.path.abspath(arrivals_file))[-2]
|
||||
fname_savefig = '{}'.format(current_fmtomo_folder_name)
|
||||
if exclude_phases:
|
||||
fname_savefig += '_e'
|
||||
for phase in exclude_phases:
|
||||
fname_savefig += '_{}'.format(phase)
|
||||
|
||||
plot_differences(arrivals_taup, arrivals_tomo, sorted_by_first_arrival, fname_savefig)
|
||||
#for event_name in ['20160124_103037.a_P.ttf', '20160729_211833.a_Pdiff.ttf', '20160729_211833.a_P.ttf']:
|
||||
# plot_event(arrivals_tomo, arrivals_taup, receiver_ids, receivers_dict, src_name=event_name)
|
||||
|
||||
|
||||
def plot_differences(arrivals_taup, arrivals_tomo, sorted_by_first_arrival, fname_savefig):
|
||||
fig = plt.figure(figsize=(16,9))
|
||||
ax = fig.add_subplot(111)
|
||||
|
||||
# init plot for tt differences
|
||||
cmap = plt.get_cmap('jet')
|
||||
colors = cmap(np.linspace(0, 1, len(sorted_by_first_arrival)))
|
||||
|
||||
for index, item in enumerate(sorted_by_first_arrival):
|
||||
src_name = item[0]
|
||||
ax.scatter(arrivals_taup[src_name], np.array(arrivals_tomo[src_name]) - np.array(arrivals_taup[src_name]),
|
||||
c=colors[index], s=25, marker='.', label=src_name, edgecolors='none')
|
||||
|
||||
# shrink box for legend
|
||||
box = ax.get_position()
|
||||
ax.set_position([box.x0, box.y0, box.width * 0.8, box.height])
|
||||
|
||||
ax.legend(bbox_to_anchor=[1, 1], loc='upper left')
|
||||
|
||||
plt.title(fname_savefig)
|
||||
ax.set_xlabel('Absolute time $t_{tau-p}$')
|
||||
ax.set_ylabel('Time difference $t_{hybrid} - t_{tau-p}$')
|
||||
|
||||
print('Saving plot to {}.png'.format(fname_savefig))
|
||||
fig.savefig(fname_savefig + '.png', dpi=300)
|
||||
#plt.show()
|
||||
|
||||
|
||||
def plot_event(arrivals_tomo, arrivals_taup, receiver_ids_dict, receivers_dict, src_name):
|
||||
arrivals_diff = np.array(arrivals_tomo[src_name]) - np.array(arrivals_taup[src_name])
|
||||
receiver_ids = receiver_ids_dict[src_name]
|
||||
x = np.array([receivers_dict[rec_id]['lon'] for rec_id in receiver_ids])
|
||||
y = np.array([receivers_dict[rec_id]['lat'] for rec_id in receiver_ids])
|
||||
sc = plt.scatter(x, y, c=arrivals_diff, edgecolor='none')
|
||||
plt.xlabel('Longitude [deg]')
|
||||
plt.ylabel('Latitude [deg]')
|
||||
cbar = plt.colorbar(sc)
|
||||
cbar.ax.set_ylabel('traveltime difference ($t_{hybrid} - t_{tau-p}$)')
|
||||
plt.title('{}'.format(src_name))
|
||||
plt.show()
|
||||
|
||||
# folders=[
|
||||
# 'alparray_0_receiver_elev_zero',
|
||||
# 'alparray_0_receiver_elev_zero_finer_pgrid_vgrid_r',
|
||||
# 'alparray_0_receiver_elev_zero_finer_vgrid_llr',
|
||||
# 'alparray_0_receiver_elev_zero_smaller_box',
|
||||
# 'alparray_0_receiver_elev_zero_shallow_box',
|
||||
# 'alparray_0_receiver_elev_zero_finer_pgrid_llr',
|
||||
# 'alparray_0_receiver_elev_zero_finer_interface',
|
||||
# #'alparray_0_receiver_elev_zero_bigger_box',
|
||||
# ]
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description='Compare arrivals with TauP-times')
|
||||
parser.add_argument('fmtomodir', help='path containing fm3d output')
|
||||
parser.add_argument('-e', dest='exclude', default=[], help='exclude phases, comma separated, no spaces')
|
||||
args = parser.parse_args()
|
||||
|
||||
fdir = args.fmtomodir
|
||||
|
||||
arrivals_file = os.path.join(fdir, 'arrivals.dat')
|
||||
receivers_file = os.path.join(fdir, 'receivers.in')
|
||||
sources_file = os.path.join(fdir, 'sources.in')
|
||||
input_source_file = os.path.join(fdir, 'input_source_file_P.in')
|
||||
|
||||
exclude_phases = args.exclude
|
||||
if exclude_phases:
|
||||
exclude_phases = exclude_phases.split(',')
|
||||
compare_arrivals(arrivals_file, receivers_file, sources_file, input_source_file=input_source_file, exclude_phases=exclude_phases)
|
||||
|
||||
|
39
pylot/tomography/fmtomo_tools/create_tradeoff_runs.py
Normal file
39
pylot/tomography/fmtomo_tools/create_tradeoff_runs.py
Normal file
@ -0,0 +1,39 @@
|
||||
import os
|
||||
|
||||
cwdir = '/data/AlpArray_Data/fmtomo/v5/tradeoff_curves'
|
||||
parent_dir_name = 'crust_included_grad_smooth_FIXED_dts'#_grad_1.5'
|
||||
|
||||
dampings = [3., 10., 30.]#, 30.]
|
||||
smoothings = [5.6]
|
||||
|
||||
def main(submit_run=True):
|
||||
fdir_parent = os.path.join(cwdir, parent_dir_name)
|
||||
for damp in dampings:
|
||||
for smooth in smoothings:
|
||||
fdir_out = fdir_parent + '_sm{}_damp{}'.format(smooth, damp)
|
||||
if not os.path.isdir(fdir_out):
|
||||
os.mkdir(fdir_out)
|
||||
os.system('cp -P {}/* {}'.format(fdir_parent, fdir_out))
|
||||
invertfile = os.path.join(fdir_out, 'invert3d.in')
|
||||
modify_invert_in(invertfile, damp, smooth)
|
||||
if submit_run:
|
||||
os.chdir(fdir_out)
|
||||
os.system('qsub submit_fmtomo.sh')
|
||||
|
||||
def modify_invert_in(fnin, damp, smooth):
|
||||
with open(fnin, 'r') as infile:
|
||||
lines = infile.readlines()
|
||||
|
||||
with open(fnin, 'w') as outfile:
|
||||
for line in lines:
|
||||
if not line.startswith('c'):
|
||||
value, comment = line.split('c:')
|
||||
if 'Global damping' in comment:
|
||||
line = line.replace(value.strip(), str(damp) + ' ')
|
||||
elif 'Global smoothing' in comment:
|
||||
line = line.replace(value.strip(), str(smooth) + ' ')
|
||||
outfile.write(line)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
29
pylot/tomography/fmtomo_tools/dist_first_src.py
Executable file
29
pylot/tomography/fmtomo_tools/dist_first_src.py
Executable file
@ -0,0 +1,29 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import argparse
|
||||
|
||||
from obspy.geodetics.base import gps2dist_azimuth
|
||||
|
||||
|
||||
def main(infile):
|
||||
per = 4e4 # earth perimeter
|
||||
|
||||
fid = open(infile, 'r')
|
||||
nrec = fid.readline()
|
||||
latsrc, lonsrc, depsrc = [float(value) for value in fid.readline().split()]
|
||||
phase = fid.readline()
|
||||
latrec, lonrec, deprec = [float(value) for value in fid.readline().split()[:3]]
|
||||
|
||||
print ('Lat/Lon Source: {} / {}'.format(latsrc, lonsrc))
|
||||
print ('Lat/Lon Receiver: {} / {}'.format(latrec, lonrec))
|
||||
|
||||
dist_deg = gps2dist_azimuth(latsrc, lonsrc, latrec, lonrec)[0] / 1e3 / per * 360
|
||||
print ('Distance: {} [deg]'.format(dist_deg))
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser('Estimate distance from source to first'
|
||||
' receiver in WGS84 ellipsoid in FMTOMO pick file.')
|
||||
parser.add_argument('infile', help='FMTOMO pickfile (*.ttf)')
|
||||
args = parser.parse_args()
|
||||
main(args.infile)
|
185
pylot/tomography/fmtomo_tools/event_thinning.py
Normal file
185
pylot/tomography/fmtomo_tools/event_thinning.py
Normal file
@ -0,0 +1,185 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import glob, os, shutil
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
from matplotlib.patches import Rectangle
|
||||
from matplotlib.collections import PatchCollection
|
||||
|
||||
from obspy.geodetics import gps2dist_azimuth
|
||||
|
||||
#pwd = '/rscratch/minos13/marcel/fmtomo_alparray/v3.5/alparray_events_thinned/picks'
|
||||
pwd = '/data/AlpArray_Data/fmtomo/v6/crust_incl_hf_sm_FIX_DTS_grad_sm30_dm10_EASI_test_Plomerova_NS_events/picks'
|
||||
os.chdir(pwd)
|
||||
infiles = glob.glob('*.ttf')
|
||||
|
||||
clat=46.
|
||||
clon=11.
|
||||
|
||||
ddist = 5
|
||||
dazim = 5
|
||||
|
||||
|
||||
def make_grid(ddist, dazim):
|
||||
distgrid = np.arange(35, 135, ddist)
|
||||
bazimgrid = np.arange(0, 360, dazim)
|
||||
grid = []
|
||||
|
||||
for bazim in np.deg2rad(bazimgrid):
|
||||
for dist in distgrid:
|
||||
grid.append(np.array([bazim, dist]))
|
||||
return np.array(grid)
|
||||
|
||||
|
||||
def make_axes():
|
||||
fig = plt.figure(figsize=(16,9))
|
||||
ax1 = fig.add_subplot(121, projection='polar')
|
||||
ax2 = fig.add_subplot(122)
|
||||
ax1.set_theta_direction(-1)
|
||||
ax1.set_theta_zero_location('N')
|
||||
return ax1, ax2
|
||||
|
||||
|
||||
def load_events():
|
||||
events = {}
|
||||
for infile in infiles:
|
||||
with open(infile, 'r') as fid:
|
||||
eventid = infile.split('.ttf')[0]
|
||||
npicks = int(fid.readline())
|
||||
lat, lon, depth = [float(item) for item in fid.readline().split()]
|
||||
dist, bazim, azim = gps2dist_azimuth(clat, clon, lat, lon, a=6.371e6, f=0)
|
||||
bazim = np.deg2rad(bazim)
|
||||
dist = dist / (np.pi * 6371) * 180 / 1e3
|
||||
events[eventid] = dict(dist=dist, bazim=bazim, npicks=npicks)
|
||||
return events
|
||||
|
||||
|
||||
def get_events_in_grid():
|
||||
events_in_grid = []
|
||||
for index, gcorner in enumerate(grid):
|
||||
bazim_l, dist_l = gcorner
|
||||
bazim_u = bazim_l + np.deg2rad(dazim)
|
||||
dist_u = dist_l + ddist
|
||||
events_in_grid.append(dict(bazims=(bazim_l, bazim_u), dists=(dist_l, dist_u), events=[]))
|
||||
for eventid, event in events.items():
|
||||
if (dist_l <= event['dist'] < dist_u) and (bazim_l <= event['bazim'] <= bazim_u):
|
||||
events_in_grid[index]['events'].append(eventid)
|
||||
return events_in_grid
|
||||
|
||||
|
||||
def filter_events():
|
||||
filtered_events = {}
|
||||
for eventdict in events_in_grid:
|
||||
cur_events = eventdict['events']
|
||||
if not cur_events: continue
|
||||
eventid = get_best_event(cur_events)
|
||||
filtered_events[eventid] = events[eventid]
|
||||
return filtered_events
|
||||
|
||||
|
||||
def get_best_event(cur_events):
|
||||
''' return eventid with highest number of picks'''
|
||||
select_events = {key: events[key] for key in cur_events}
|
||||
npicks = {key: value['npicks'] for key, value in select_events.items()}
|
||||
eventid = max(npicks, key=npicks.get)
|
||||
return eventid
|
||||
|
||||
|
||||
def plot_distribution(events_dict):
|
||||
cmap_bnd = plt.get_cmap('Greys_r')
|
||||
cmap_center = plt.get_cmap('viridis')
|
||||
nevents = [len(grid_dict['events']) for grid_dict in events_dict]
|
||||
npicks = []
|
||||
for ev_dict in events_dict:
|
||||
npick = 0
|
||||
for eventid in ev_dict['events']:
|
||||
npick += events[eventid]['npicks']
|
||||
npicks.append(npick)
|
||||
|
||||
npicks = np.array(npicks)
|
||||
|
||||
ev_max = max(nevents)
|
||||
np_max = max(npicks)
|
||||
|
||||
print('N picks total:', np.sum(npicks))
|
||||
print('N picks max: ', np_max)
|
||||
print('N events max: ', ev_max)
|
||||
|
||||
ax_polar, ax_hist = make_axes()
|
||||
patches = []
|
||||
for npick, ev_dict in zip(npicks, events_dict):
|
||||
bazim_l, bazim_u = ev_dict.get('bazims')
|
||||
dist_l, dist_u = ev_dict.get('dists')
|
||||
n_ev = len(ev_dict.get('events'))
|
||||
color_edge = cmap_bnd(n_ev / ev_max)
|
||||
color_center = cmap_center(npick / np_max)
|
||||
# color = cmap(np.random.rand())
|
||||
rect = Rectangle((bazim_l, dist_l), np.deg2rad(dazim), ddist, edgecolor=color_edge)#, facecolor=color_center)
|
||||
patches.append(rect)
|
||||
|
||||
collection = PatchCollection(patches, cmap=cmap_center)
|
||||
collection.set_array(npicks)
|
||||
ax_polar.add_collection(collection)
|
||||
ax_polar.set_ylim((10, 135))
|
||||
cbar = plt.colorbar(collection)
|
||||
cbar.set_label('N picks')
|
||||
|
||||
# ax.scatter(grid[:, 0] + 0.5 * dazim, grid[:, 1] + 0.5 * ddist, c=nevents, s=50)
|
||||
bazims = []
|
||||
dists = []
|
||||
for event in events.values():
|
||||
bazims.append(event.get('bazim'))
|
||||
dists.append(event.get('dist'))
|
||||
|
||||
ax_polar.scatter(bazims, dists, c='k', zorder=3, s=5, alpha=0.5)
|
||||
ax_hist.hist(np.rad2deg(bazims), bins=np.arange(0, 360, dazim))
|
||||
ax_hist.set_xlabel('Backazimuth (deg)')
|
||||
ax_hist.set_ylabel('Number of events')
|
||||
plt.title('Polar event distribution and histogram of backazimuths')
|
||||
|
||||
|
||||
def export_filtered_events(fdir_save='picks_save'):
|
||||
if not os.path.isdir(fdir_save):
|
||||
os.mkdir(fdir_save)
|
||||
for infile in infiles:
|
||||
eventid = infile.split('.ttf')[0]
|
||||
if not eventid in events:
|
||||
for fname in glob.glob('{}.*'.format(eventid)):
|
||||
shutil.move(fname, fdir_save)
|
||||
print('Moved file {} to path {}'.format(fname, fdir_save))
|
||||
|
||||
|
||||
def write_input_source_file(fname='input_source_file_P_new.in'):
|
||||
with open(fname, 'w') as outfile:
|
||||
outfile.write('{}\n'.format(len(events)))
|
||||
for eventid in sorted(list(events.keys())):
|
||||
outfile.write('1 1 {}.ttf\n'.format(eventid))
|
||||
|
||||
|
||||
def filter_bazim(events, bazims_list):
|
||||
events_filtered = {}
|
||||
for eventid, event_dict in events.items():
|
||||
for baz_min, baz_max in bazims_list:
|
||||
if baz_min <= event_dict['bazim'] * 180. <= baz_max:
|
||||
events_filtered[eventid] = event_dict
|
||||
|
||||
return events_filtered
|
||||
|
||||
|
||||
filter_bazims = [(330, 360), (0, 30), (150, 210)]
|
||||
|
||||
events = load_events()
|
||||
#plot_distribution(events)
|
||||
|
||||
events = filter_bazim(events, bazims_list=filter_bazims)
|
||||
print()
|
||||
#grid = make_grid(ddist, dazim)
|
||||
#events_in_grid = get_events_in_grid()
|
||||
#plot_distribution()
|
||||
#events = filter_events()
|
||||
#events_in_grid = get_events_in_grid()
|
||||
#plot_distribution(events)
|
||||
#plt.show()
|
||||
export_filtered_events()
|
||||
write_input_source_file()
|
@ -0,0 +1,28 @@
|
||||
import os
|
||||
|
||||
import numpy as np
|
||||
from obspy.geodetics import gps2dist_azimuth
|
||||
|
||||
from fmtomo_tools.fmtomo_teleseismic_utils import organize_sources
|
||||
|
||||
fmtomodir = '/data/AdriaArray_Data/fmtomo_adriaarray/alpadege/no_crust_correction'
|
||||
clon = 17.5
|
||||
clat = 42.25
|
||||
|
||||
|
||||
os.chdir(fmtomodir)
|
||||
|
||||
sources = organize_sources('sources.in')
|
||||
|
||||
dists = []
|
||||
lats = []
|
||||
lons = []
|
||||
|
||||
for source_id, source_dict in sources.items():
|
||||
slat = source_dict['lat']
|
||||
slon = source_dict['lon']
|
||||
lats.append(slat)
|
||||
lons.append(slon)
|
||||
dist_m = gps2dist_azimuth(slat, slon, clat, clon, a=6.371e6, f=0)[0]
|
||||
dist = dist_m / (np.pi * 6371) * 180 / 1e3
|
||||
dists.append(dist)
|
2019
pylot/tomography/fmtomo_tools/fmtomo_grid_utils.py
Normal file
2019
pylot/tomography/fmtomo_tools/fmtomo_grid_utils.py
Normal file
File diff suppressed because it is too large
Load Diff
52
pylot/tomography/fmtomo_tools/fmtomo_teleseismic.py
Normal file
52
pylot/tomography/fmtomo_tools/fmtomo_teleseismic.py
Normal file
@ -0,0 +1,52 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
import os
|
||||
import argparse
|
||||
|
||||
from fmtomo_teleseismic_utils import setup_fmtomo_sim
|
||||
|
||||
#setup_fmtomo_sim('/data/AlpArray_Data/dmt_database/', '//data/AlpArray_Data/fmtomo/v6_S/crust_incl_hf_sm_FIX_DTS_grad_sm30_dm10')
|
||||
#setup_fmtomo_sim('/rscratch/minos13/marcel/dmt_database_test_event', '/home/marcel/marcel_scratch/alparray/fmtomo_traveltime_tomo/alparray_0/')
|
||||
#setup_fmtomo_sim('/rscratch/minos13/marcel/dmt_database_m7', '/home/marcel/marcel_scratch/alparray/fmtomo_traveltime_tomo/alparray_0/')
|
||||
#setup_fmtomo_sim('/rscratch/minos22/marcel/dmt_database_m7', '/rscratch/minos22/marcel/alparray/fmtomo_traveltime_tomo/alparray_0_receiver_elev_zero_smaller_box')
|
||||
#setup_fmtomo_sim('/rscratch/minos22/marcel/dmt_database_m7', '/rscratch/minos22/marcel/alparray/fmtomo_traveltime_tomo/alparray_0_receiver_elev_zero_bigger_box')
|
||||
#setup_fmtomo_sim('/rscratch/minos22/marcel/dmt_database_m7', '/rscratch/minos22/marcel/alparray/fmtomo_traveltime_tomo/alparray_0_receiver_elev_zero_shallow_box')
|
||||
#setup_fmtomo_sim('/rscratch/minos22/marcel/dmt_database_m7', '/rscratch/minos22/marcel/alparray/fmtomo_traveltime_tomo/alparray_0_receiver_elev_zero_finer_interface')
|
||||
#setup_fmtomo_sim('/rscratch/minos22/marcel/dmt_database_m7', '/rscratch/minos22/marcel/alparray/fmtomo_traveltime_tomo/alparray_0_receiver_elev_zero_bigger_box_equal_dist_2')
|
||||
|
||||
#pgrid = Propgrid('/rscratch/minos22/marcel/alparray/fmtomo_traveltime_tomo/alparray_0/propgrid.in')
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description='Prepare grid for fm3d teleseismic hybrid calculation.')
|
||||
parser.add_argument('dmt_path', help='path containing dmt_database with PyLoT picks')
|
||||
parser.add_argument('fmtomodir', help='path containing fm3d output')
|
||||
parser.add_argument('fname_extension', help='filename extension of pick files')
|
||||
parser.add_argument('--blacklist', default=None,
|
||||
help='station blacklist file (csv). After first line: NW_id,ST_id in each line')
|
||||
parser.add_argument('--no_write_init_nodes', default=False, action='store_true',
|
||||
help='do not calculate and write init nodes')
|
||||
parser.add_argument('--no_recalculate_init_nodes', default=False, action='store_true',
|
||||
help='do not recalculate init nodes if nodes file exists')
|
||||
parser.add_argument('-n', dest='ncores', default=None, help='number of cores for multiprocessing')
|
||||
parser.add_argument('--model', default='ak135')
|
||||
parser.add_argument('--s_phase', default=False, action='store_true')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
database_path = os.path.abspath(args.dmt_path)
|
||||
fmtomodir = os.path.abspath(args.fmtomodir)
|
||||
if args.ncores is not None:
|
||||
ncores = int(args.ncores)
|
||||
else:
|
||||
ncores = None
|
||||
fname_extension = args.fname_extension
|
||||
|
||||
sub_phases = {'P': ['P', 'PKP'], 'S': ['S', 'SKS']}
|
||||
phase_types = ['P']
|
||||
if args.s_phase:
|
||||
phase_types.append('S')
|
||||
|
||||
setup_fmtomo_sim(database_path, fmtomodir, fname_extension, sub_phases, ncores=ncores, check_notesfile=False,
|
||||
model=args.model, fname_station_blacklist=args.blacklist,
|
||||
no_write_init_nodes=args.no_write_init_nodes,
|
||||
no_recalculate_init_nodes=args.no_recalculate_init_nodes, phase_types=phase_types)
|
930
pylot/tomography/fmtomo_tools/fmtomo_teleseismic_utils.py
Normal file
930
pylot/tomography/fmtomo_tools/fmtomo_teleseismic_utils.py
Normal file
@ -0,0 +1,930 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
import subprocess
|
||||
import warnings
|
||||
import os
|
||||
import glob
|
||||
from datetime import datetime
|
||||
|
||||
import multiprocessing
|
||||
import numpy as np
|
||||
import json
|
||||
|
||||
from obspy import read_events, UTCDateTime
|
||||
from obspy.taup import TauPyModel
|
||||
from obspy.geodetics import locations2degrees, gps2dist_azimuth
|
||||
|
||||
from pylot.core.util.utils import identifyPhaseID
|
||||
from pylot.core.util.obspyDMT_interface import qml_from_obspyDMT
|
||||
from pylot.tomography.utils import pol2cart, pol2cart_vector
|
||||
from pylot.tomography.utils import get_metadata
|
||||
|
||||
|
||||
class Propgrid(object):
|
||||
'''
|
||||
small class that is built from an fm3d propgrid.in file; generates a regular grid in the same way as fm3d
|
||||
'''
|
||||
def __init__(self, filename):
|
||||
self.r_earth = 6371. # earth radius in km
|
||||
self.init_propgrid(filename)
|
||||
|
||||
def init_propgrid(self, filename_propgrid):
|
||||
self.read_propgrid(filename_propgrid)
|
||||
self.init_rGrid()
|
||||
self.init_latGrid()
|
||||
self.init_longGrid()
|
||||
|
||||
def init_rGrid(self):
|
||||
self.rs = np.zeros(self.nR)
|
||||
self.rbot = self.r_earth + self.zTop - (self.nR - 1) * self.deltaR
|
||||
for index in range(self.nR):
|
||||
self.rs[index] = self.rbot + index * self.deltaR
|
||||
|
||||
def init_latGrid(self):
|
||||
self.lats = np.zeros(self.nLat)
|
||||
self.latS = np.deg2rad(self.latSdeg)
|
||||
self.deltaLat = np.deg2rad(self.deltaLatDeg)
|
||||
for index in range(self.nLat):
|
||||
self.lats[index] = self.latS + index * self.deltaLat
|
||||
|
||||
def init_longGrid(self):
|
||||
self.longs = np.zeros(self.nLong)
|
||||
self.longW = np.deg2rad(self.longWdeg)
|
||||
self.deltaLong = np.deg2rad(self.deltaLongDeg)
|
||||
for index in range(self.nLong):
|
||||
self.longs[index] = self.longW + index * self.deltaLong
|
||||
|
||||
def read_propgrid(self, filename_propgrid):
|
||||
infile = open(filename_propgrid, 'r')
|
||||
self.nR, self.nLat, self.nLong = [int(value) for value in infile.readline().split()]
|
||||
self.deltaR, self.deltaLatDeg, self.deltaLongDeg = [np.float64(value) for value in infile.readline().split()]
|
||||
self.zTop, self.latSdeg, self.longWdeg = [np.float64(value) for value in infile.readline().split()]
|
||||
infile.close()
|
||||
|
||||
|
||||
def check_event_notes(eventdir):
|
||||
eventfile = os.path.join(eventdir, 'notes.txt')
|
||||
if os.path.isfile(eventfile):
|
||||
with open(eventfile, 'r') as infile:
|
||||
notes = infile.readline()
|
||||
print(notes)
|
||||
if 'exclude' in notes:
|
||||
print('Will exclude this event!')
|
||||
return False
|
||||
else:
|
||||
print('No notes file found.')
|
||||
return True
|
||||
|
||||
|
||||
def prepare_fmtomo_dir_first_run(fmtomo_dir):
|
||||
"""
|
||||
This helper function calls the file 'fm3d_prepare_tele' in the fmtomo binary after creating a "fake" receivers.in
|
||||
and sources.in file so that fm3d can prepare the teleseismic run and writes the file "init_nodes.out" that contains
|
||||
information on the boundary nodes of the current propagation grid. Make sure that "grid3dg" has already been called
|
||||
for this function to work.
|
||||
:param fmtomo_dir:
|
||||
:return:
|
||||
"""
|
||||
def write1(fid):
|
||||
fid.write('1\n')
|
||||
|
||||
def write_rec_file():
|
||||
""" Writes a fake receiver (has to be inside the grid) """
|
||||
with open(recfile, 'w') as fid:
|
||||
write1(fid)
|
||||
fid.write(f'0 {clat} {clon}\n')
|
||||
for _ in range(3):
|
||||
write1(fid)
|
||||
|
||||
def write_src_file():
|
||||
""" Writes a fake source """
|
||||
fakesrc_str = '''
|
||||
1
|
||||
1
|
||||
P
|
||||
0.0000 0.0000 0.0000 0.00000 0.00000 0.00000
|
||||
1
|
||||
1
|
||||
2 1
|
||||
1
|
||||
1
|
||||
'''
|
||||
|
||||
with open(srcfile, 'w') as fid:
|
||||
fid.write(fakesrc_str)
|
||||
|
||||
def get_clat_clon():
|
||||
if not os.path.isfile(propgrid_file):
|
||||
raise IOError(f'Missing file {propgrid_file} for fmtomo first run preparation.')
|
||||
|
||||
with open(propgrid_file, 'r') as fid:
|
||||
_, nlat, nlon = (int(item) for item in fid.readline().split())
|
||||
_, dlat, dlon = (float(item) for item in fid.readline().split())
|
||||
_, lat0, lon0 = (float(item) for item in fid.readline().split())
|
||||
clat = lat0 + nlat / 2 * dlat
|
||||
clon = lon0 + nlon / 2 * dlon
|
||||
return clat, clon
|
||||
|
||||
# check if binary file exists
|
||||
fmtomo_prep_tele = os.path.join(fmtomo_dir, 'fm3d_prepare_tele')
|
||||
assert os.path.isfile(fmtomo_prep_tele), f'Missing binary file {fmtomo_prep_tele}'
|
||||
|
||||
# set filenames for propgrid, sources and receivers
|
||||
propgrid_file = os.path.join(fmtomo_dir, 'propgrid.in')
|
||||
recfile = os.path.join(fmtomo_dir, 'receivers.in')
|
||||
srcfile = os.path.join(fmtomo_dir, 'sources.in')
|
||||
|
||||
# get coords from propgrid
|
||||
clat, clon = get_clat_clon()
|
||||
|
||||
# write fake source and receiver files
|
||||
write_src_file()
|
||||
write_rec_file()
|
||||
|
||||
# execute fm3d_prepare tele from local directory
|
||||
curdir = os.getcwd()
|
||||
os.chdir(fmtomo_dir)
|
||||
rval = subprocess.check_output([fmtomo_prep_tele]).decode('utf-8')
|
||||
if not rval.split('\n')[-2] == ' Finished teleseismic preparation. Stop.':
|
||||
raise ValueError('Unexpected output initialisation run.')
|
||||
|
||||
if not os.path.isfile(os.path.join(fmtomo_dir, 'init_nodes.out')):
|
||||
raise Exception('Could not create output file init_nodes.')
|
||||
os.chdir(curdir)
|
||||
|
||||
# clean up
|
||||
os.remove(srcfile)
|
||||
os.remove(recfile)
|
||||
|
||||
print('Prepare fmtomo dir first run: Success')
|
||||
|
||||
|
||||
def setup_fmtomo_sim(database_path_dmt, fmtomo_dir, fname_extension, sub_phases, ncores=None, model='ak135',
|
||||
min_picks_per_phase=10, write_vtk=True, check_notesfile=True, fname_station_blacklist=None,
|
||||
no_write_init_nodes=False, no_recalculate_init_nodes=False, phase_types=('P, S')):
|
||||
'''
|
||||
main function of this program, writes picks and input source file for FMTOMO obsdata program
|
||||
'''
|
||||
|
||||
assert os.path.isdir(database_path_dmt), 'Unrecognized directory {}'.format(database_path_dmt)
|
||||
assert os.path.isdir(fmtomo_dir), 'Unrecognized directory {}'.format(fmtomo_dir)
|
||||
|
||||
tstart = datetime.now()
|
||||
print('Starting script at {}'.format(tstart))
|
||||
print('Check notesfile set to {}'.format(check_notesfile))
|
||||
|
||||
# save means of event traveltimes to dictionary for statistical analysis
|
||||
#tt_means = {}
|
||||
|
||||
fname_fmtomo_nodes = os.path.join(fmtomo_dir, 'init_nodes.out')
|
||||
|
||||
# do first initialisation of FMTOMO to generate init_nodes file if required
|
||||
if not no_recalculate_init_nodes:
|
||||
prepare_fmtomo_dir_first_run(fmtomo_dir)
|
||||
|
||||
eventdirs = glob.glob(os.path.join(database_path_dmt, '*.?'))
|
||||
nEvents = len(eventdirs)
|
||||
# create directory that will contain the picks
|
||||
picksdir = os.path.join(fmtomo_dir, 'picks')
|
||||
if not os.path.isdir(picksdir):
|
||||
os.mkdir(picksdir)
|
||||
# track and count ALL source locations & phases (P and S) for fmtomo_tools
|
||||
associations_str = ''
|
||||
nsrc_total = 0
|
||||
# iterate over P and S to create one model each
|
||||
for phase_type in phase_types:
|
||||
sourcefile_str = ''
|
||||
nsrc = 0
|
||||
# iterate over all events in "database_path_dmt"
|
||||
for eventindex, eventdir in enumerate(eventdirs):
|
||||
print('Working on {}-picks for event {} ({}/{})'.format(phase_type, eventdir,
|
||||
eventindex + 1, len(eventdirs)))
|
||||
if check_notesfile and not check_event_notes(eventdir):
|
||||
continue
|
||||
# create ObsPy event from .pkl file in dmt eventdir
|
||||
event = get_event_obspy_dmt(eventdir)
|
||||
if len(event.origins) > 1:
|
||||
raise Exception('Ambiguous origin information for event {}'.format(event))
|
||||
origin = event.origins[0]
|
||||
# get all picks from PyLoT *.xml file
|
||||
picks = get_picks(eventdir, extension=fname_extension)
|
||||
if not picks:
|
||||
print('No picks for event {} found.'.format(eventdir))
|
||||
continue
|
||||
# remove picks for blacklisted stations
|
||||
if fname_station_blacklist:
|
||||
picks, n_deleted_blacklist = remove_blacklisted_station_picks(picks, fname_station_blacklist)
|
||||
# get metadata for current event from dmt_database
|
||||
metadata = get_metadata(eventdir)
|
||||
# get a dictionary containing coordinates for all stations
|
||||
stations_dict = metadata.get_all_coordinates()
|
||||
# catch event id
|
||||
event_id = get_event_id(eventdir)
|
||||
# map specific phases to another phase (Pdiff -> P)
|
||||
merge_phases = {'Pdiff': 'P'}
|
||||
for key, value in merge_phases.items():
|
||||
print('Will map phase {} to phase {} if present.'.format(key, value))
|
||||
# assign all picks of this event to a phase and add to sorted_picks dictionary
|
||||
sorted_picks = sort_picks_by_phase(picks, stations_dict, origin,
|
||||
sub_phases[phase_type], model, merge_phases=merge_phases)
|
||||
sorted_picks = remove_uncommon_picks(sorted_picks, min_picks_per_phase)
|
||||
# the following should not be necessary when calculating traveltimes on borders using TauPy
|
||||
#sorted_picks = translate_phase_names_TAUP(sorted_picks)
|
||||
#
|
||||
# iterate over sorted picks and write a traveltime file for each phase type
|
||||
for phase, picks_list in sorted_picks.items():
|
||||
pickfile_base = '{}_{}'.format(event_id, phase)
|
||||
pickfile_name = pickfile_base + '.ttf'
|
||||
init_nodes_name = pickfile_base + '.nodes'
|
||||
vtk_file_name = pickfile_base + '.vtk'
|
||||
pickfile = os.path.join(picksdir, pickfile_name)
|
||||
init_nodes_file = os.path.join(picksdir, init_nodes_name)
|
||||
vtk_file = os.path.join(picksdir, vtk_file_name)
|
||||
# remove source time
|
||||
picks_list = subtract_source_time(picks_list, origin.time)
|
||||
# save mean for statistical analysis
|
||||
#tt_means[pickfile_base] = mean
|
||||
# create a list with all "true" phases used for taupy to calculate boundary ttimes/ray parameters
|
||||
phases = list(set([item['true_phase'] for item in picks_list]))
|
||||
if no_write_init_nodes == False:
|
||||
if no_recalculate_init_nodes and os.path.isfile(init_nodes_file):
|
||||
print('Found previously calculated init nodes. Will not recalculate.')
|
||||
else:
|
||||
# calculate travel times and ray parameters for boundary nodes and write to file
|
||||
points_list = initNodesFm3d(fname_fmtomo_nodes, origin, model, phases, ncores=ncores)
|
||||
# in case initNodes fails and returns None continue with next phase
|
||||
if not points_list:
|
||||
print('No points list, initNodesFm3d failed for event.')
|
||||
continue
|
||||
if not write_init_points(points_list, init_nodes_file):
|
||||
print('Write init points failed for event.')
|
||||
continue
|
||||
if write_vtk==True:
|
||||
write_vtk_file(points_list, vtk_file)
|
||||
# write picks to pickfile for fm3d
|
||||
write_picks_to_pickfile(pickfile, phase, picks_list, stations_dict, origin)
|
||||
# add pickfile to sourcefile string and count number of pickfiles
|
||||
sourcefile_str += '1 1 {}\n'.format(pickfile_name)
|
||||
# add source location, phaseID and .nodes filename to association string
|
||||
source_string = '{phase} {lat} {lon} {depth} {fname}\n'
|
||||
associations_str += source_string.format(phase=phase, lat=origin.latitude,
|
||||
lon=origin.longitude, depth=origin.depth,
|
||||
fname=init_nodes_name)
|
||||
nsrc += 1
|
||||
nsrc_total += 1
|
||||
average_time = (datetime.now() - tstart) / (eventindex + 1)
|
||||
print('Average time for event (phase {}): {}'.format(phase_type, average_time))
|
||||
print('ETA for {}-events: {}'.format(phase_type, tstart + nEvents * average_time))
|
||||
|
||||
write_src_file(fmtomo_dir, phase_type, nsrc, sourcefile_str)
|
||||
|
||||
write_assc_file(fmtomo_dir, nsrc_total, associations_str)
|
||||
#write_json(tt_means, os.path.join(fmtomo_dir, 'ttmeans.json'))
|
||||
|
||||
print('Script finished! Good Bye!')
|
||||
|
||||
|
||||
def write_src_file(fmtomo_dir, phase_type, nsrc, sourcefile_str):
|
||||
# write input_source_file for obsdata
|
||||
input_source_file = open(os.path.join(fmtomo_dir, 'input_source_file_{}.in'.format(phase_type)), 'w')
|
||||
input_source_file.write('{}\n'.format(nsrc))
|
||||
input_source_file.write(sourcefile_str)
|
||||
input_source_file.close()
|
||||
|
||||
|
||||
def write_assc_file(fmtomo_dir, nsrc_total, associations_str):
|
||||
# write input_associations file for fm3d
|
||||
input_assc_file = open(os.path.join(fmtomo_dir, 'input_associations_file.in'), 'w')
|
||||
input_assc_file.write('{}\n'.format(nsrc_total))
|
||||
input_assc_file.write(associations_str)
|
||||
input_assc_file.close()
|
||||
|
||||
|
||||
def write_json(object, fname):
|
||||
with open(fname, 'w') as outfile:
|
||||
json.dump(object, outfile, sort_keys=True, indent=4)
|
||||
|
||||
|
||||
def write_vtk_file(points_list, fname):
|
||||
outfile = open(fname, 'w')
|
||||
|
||||
nPoints = len(points_list)
|
||||
|
||||
outfile.write('# vtk DataFile Version 2.0\n')
|
||||
outfile.write('FMM Init points\n')
|
||||
outfile.write('ASCII\n')
|
||||
outfile.write('DATASET POLYDATA\n')
|
||||
outfile.write('POINTS {} float\n'.format(nPoints))
|
||||
|
||||
for point in points_list:
|
||||
lat = point['pt_lat']
|
||||
lon = point['pt_lon']
|
||||
rad = point['pt_R']
|
||||
x, y, z = pol2cart(lat, lon, rad)
|
||||
outfile.write('{x} {y} {z}\n'.format(x=x, y=y, z=z))
|
||||
|
||||
# write number of vertices and their indices
|
||||
outfile.write('\nVERTICES {} {}\n'.format(nPoints, 2*nPoints))
|
||||
for index, point in enumerate(points_list):
|
||||
outfile.write('{} {}\n'.format(1, index))
|
||||
|
||||
# write header with number of data points
|
||||
outfile.write('\nPOINT_DATA {}\n'.format(nPoints))
|
||||
|
||||
# write header for traveltimes
|
||||
outfile.write('SCALARS traveltime float 1\n')
|
||||
outfile.write('LOOKUP_TABLE default\n')
|
||||
|
||||
for point in points_list:
|
||||
time = point.get('time')
|
||||
outfile.write('{}\n'.format(time if time else 0))
|
||||
|
||||
# write header for point indices
|
||||
outfile.write('SCALARS point_index integer 1\n')
|
||||
outfile.write('LOOKUP_TABLE default\n')
|
||||
|
||||
for point in points_list:
|
||||
pt_index = point.get('pt_index')
|
||||
outfile.write('{}\n'.format(pt_index if pt_index else 0))
|
||||
|
||||
outfile.write('VECTORS tt_grad float\n')
|
||||
for point in points_list:
|
||||
lat = point['pt_lat']
|
||||
lon = point['pt_lon']
|
||||
r_comp = point.get('ray_param_km_z_comp')
|
||||
n_comp = point.get('ray_param_km_n_comp')
|
||||
e_comp = point.get('ray_param_km_e_comp')
|
||||
sx, sy, sz = pol2cart_vector(lat, lon, n_comp, e_comp, r_comp)
|
||||
outfile.write('{sx} {sy} {sz}\n'.format(sx=sx if sx else 0.,
|
||||
sy=sy if sy else 0.,
|
||||
sz=sz if sz else 0.,))
|
||||
|
||||
outfile.close()
|
||||
|
||||
|
||||
|
||||
|
||||
def write_init_points(points_list, outfile):
|
||||
fid = open(outfile, 'w')
|
||||
print('Writing {} init points to file {}.'.format(len(points_list), outfile))
|
||||
output_str = ''
|
||||
# count number of points for file header
|
||||
npoints = 0
|
||||
for point_dict in points_list:
|
||||
nArrivals = point_dict.get('nArrivals')
|
||||
if not nArrivals:
|
||||
#print('No arrivals for point:')
|
||||
#print(point_dict)
|
||||
continue
|
||||
if nArrivals > 1:
|
||||
fid.close()
|
||||
os.remove(outfile)
|
||||
warning_template = 'Ambiguous information for point: {}, having {} different arrivals. Skipping event.'
|
||||
print(warning_template.format(point_dict, nArrivals))
|
||||
return False
|
||||
output_template = '{index} {time} {ray_param_z} {ray_param_n} {ray_param_e}\n'
|
||||
output_str += (output_template.format(index=point_dict['pt_index'],
|
||||
time=point_dict.get('time'),
|
||||
ray_param_z=point_dict.get('ray_param_km_z_comp'),
|
||||
ray_param_n=point_dict.get('ray_param_km_n_comp'),
|
||||
ray_param_e=point_dict.get('ray_param_km_e_comp')))
|
||||
npoints += 1
|
||||
fid.write('{}\n'.format(npoints))
|
||||
fid.write(output_str)
|
||||
fid.close()
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def initNodesFm3d(filename_init_nodes, source_origin, model, phases, min_dist=25., ncores=None, R=6371000.):
|
||||
# This function uses obspy TauPy to calculate travel times and ray parameters on boundary points of the fm3d grid
|
||||
|
||||
def exposed_sides(grid_boundaries, src_lat, src_lon):
|
||||
# check which sides of grid are exposed to source (taken from fm3d teleseismic.f90 code)
|
||||
# here: baz is calculated, not az
|
||||
north = grid_boundaries['north']
|
||||
east = grid_boundaries['east']
|
||||
south = grid_boundaries['south']
|
||||
west = grid_boundaries['west']
|
||||
|
||||
# north
|
||||
baz1 = gps2dist_azimuth(north, west, src_lat, src_lon, a=R, f=0)[1]
|
||||
baz2 = gps2dist_azimuth(north, east, src_lat, src_lon, a=R, f=0)[1]
|
||||
exposed_north = all(np.cos(np.deg2rad(baz)) > 0.01 for baz in [baz1, baz2])
|
||||
# east
|
||||
baz1 = gps2dist_azimuth(north, east, src_lat, src_lon, a=R, f=0)[1]
|
||||
baz2 = gps2dist_azimuth(south, east, src_lat, src_lon, a=R, f=0)[1]
|
||||
exposed_east = all(np.sin(np.deg2rad(baz)) > 0.01 for baz in [baz1, baz2])
|
||||
# south
|
||||
baz1 = gps2dist_azimuth(south, west, src_lat, src_lon, a=R, f=0)[1]
|
||||
baz2 = gps2dist_azimuth(south, east, src_lat, src_lon, a=R, f=0)[1]
|
||||
exposed_south = all(np.cos(np.deg2rad(baz)) < -0.01 for baz in [baz1, baz2])
|
||||
# west
|
||||
baz1 = gps2dist_azimuth(north, west, src_lat, src_lon, a=R, f=0)[1]
|
||||
baz2 = gps2dist_azimuth(south, west, src_lat, src_lon, a=R, f=0)[1]
|
||||
exposed_west = all(np.sin(np.deg2rad(baz)) < -0.01 for baz in [baz1, baz2])
|
||||
|
||||
exposed_dict = dict(north=exposed_north, east=exposed_east, south=exposed_south, west=exposed_west,
|
||||
rbot=True)
|
||||
|
||||
return exposed_dict
|
||||
|
||||
def point_valid(pt_lat, pt_lon, pt_R, exposed_dict, grid_boundaries, R_earth):
|
||||
# check whether point has to be active or not depending on its position to the source
|
||||
|
||||
def check_point(actual, desired, threshold=0.001):
|
||||
# checks receiver (boundary) point orientation by comparison to corner boundary points
|
||||
return abs(actual - desired) < threshold
|
||||
|
||||
# check for negative depth (above surface)
|
||||
if pt_R > R_earth:
|
||||
return False
|
||||
|
||||
# check if point belongs to bottom interface
|
||||
#if check_point(pt_R, grid_boundaries['rbot']):
|
||||
# return True
|
||||
|
||||
pt_faces_dir = {}
|
||||
pt_exposed_dir = {}
|
||||
|
||||
directions = {'north': pt_lat,
|
||||
'east': pt_lon,
|
||||
'south': pt_lat,
|
||||
'west': pt_lon,
|
||||
'rbot': pt_R,}
|
||||
|
||||
for direction, pt_lat_or_lon in directions.items():
|
||||
# check if point belongs to boundary of this direction and save to pt_faces_dir
|
||||
pt_direction_check = check_point(pt_lat_or_lon, grid_boundaries[direction])
|
||||
pt_faces_dir[direction] = pt_direction_check
|
||||
# check if point is exposed to source in this direction and save to pt_exposed_dir
|
||||
pt_exposed_dir[direction] = pt_direction_check and exposed_dict[direction]
|
||||
|
||||
# compare number of points facing source direction to the actual direction they are facing
|
||||
return sum(pt_faces_dir.values()) == sum(pt_exposed_dir.values())
|
||||
|
||||
|
||||
import datetime
|
||||
now = datetime.datetime.now()
|
||||
infile = open(filename_init_nodes, 'r')
|
||||
R_earth = 6371.
|
||||
|
||||
# read input files containing fm3d boundary points in each line in the order: lat lon radius
|
||||
init_nodes = infile.readlines()
|
||||
nPoints = len(init_nodes)
|
||||
# split lines and convert to floats except for first value which is converted to int
|
||||
init_nodes = [[float(val) if index != 0 else int(val) for index, val in enumerate(line.split())]
|
||||
for line in init_nodes]
|
||||
|
||||
grid_boundaries = dict(north=max(init_nodes, key=lambda x: x[1])[1],
|
||||
east=max(init_nodes, key=lambda x: x[2])[2],
|
||||
south=min(init_nodes, key=lambda x: x[1])[1],
|
||||
west=min(init_nodes, key=lambda x: x[2])[2],
|
||||
rbot=min(init_nodes, key=lambda x: x[3])[3],
|
||||
rtop=max(init_nodes, key=lambda x: x[3])[3])
|
||||
|
||||
# get source coordinates
|
||||
src_lat, src_lon, src_depth = source_origin.latitude, source_origin.longitude, source_origin.depth
|
||||
#src_lat = 0; src_lon = 180; src_depth=50
|
||||
|
||||
# calculate which sides are exposed to source
|
||||
exposed_dict = exposed_sides(grid_boundaries, src_lat, src_lon)
|
||||
|
||||
# iterate over all points and calculate distance. Append a dictionary to an input list for
|
||||
# multiprocessing worker for each point
|
||||
points_list = []
|
||||
for point in init_nodes:
|
||||
pt_index, pt_lat, pt_lon, pt_R = point
|
||||
pt_depth = R_earth - pt_R
|
||||
dist = locations2degrees(pt_lat, pt_lon, src_lat, src_lon)
|
||||
|
||||
# check minimum distance for this point
|
||||
if dist < min_dist:
|
||||
warnings.warn('Distance for point {} less than minimum'
|
||||
' distance ({} km). Skipping event!'.format(point, min_dist))
|
||||
return
|
||||
|
||||
# check if point is exposed to source and has to be initiated
|
||||
if not point_valid(pt_lat, pt_lon, pt_R, exposed_dict, grid_boundaries, R_earth):
|
||||
continue
|
||||
|
||||
input_dict = {'pt_index': pt_index,
|
||||
'pt_depth': pt_depth,
|
||||
'pt_R': pt_R,
|
||||
'pt_lat': pt_lat,
|
||||
'pt_lon': pt_lon,
|
||||
'src_lat': src_lat,
|
||||
'src_lon': src_lon,
|
||||
'src_depth': src_depth,
|
||||
'dist': dist,
|
||||
'model': model,
|
||||
'phases': phases,
|
||||
}
|
||||
points_list.append(input_dict)
|
||||
|
||||
print('n Points init:', len(points_list))
|
||||
#plot_points(points_list)
|
||||
|
||||
rvals = []
|
||||
pool = multiprocessing.Pool(ncores, maxtasksperchild=1000)
|
||||
for rval in pool.imap(taup_worker, points_list, chunksize=10):
|
||||
rvals.append(rval)
|
||||
pool.close()
|
||||
pool.join()
|
||||
|
||||
print('Done after {}'.format(datetime.datetime.now() - now))
|
||||
|
||||
#plot_points(points_list)
|
||||
return rvals
|
||||
|
||||
|
||||
def taup_worker(input_dict):
|
||||
# initiate model for TauP method
|
||||
model = TauPyModel(model=input_dict['model'])
|
||||
huge_time = 1e7
|
||||
try:
|
||||
arrivals = model.get_ray_paths(input_dict['src_depth'], input_dict['dist'],
|
||||
phase_list=input_dict['phases'],
|
||||
receiver_depth_in_km=input_dict['pt_depth'], )
|
||||
if len(arrivals) < 1:
|
||||
#print('No arrivals for phase {}'.format(input_dict['phase']))
|
||||
#print(input_dict)
|
||||
return input_dict
|
||||
|
||||
arr = arrivals[0]
|
||||
|
||||
input_dict['nArrivals'] = len(arrivals)
|
||||
baz = gps2dist_azimuth(input_dict['pt_lat'], input_dict['pt_lon'],
|
||||
input_dict['src_lat'], input_dict['src_lon'],
|
||||
a=6371.*1e3, f=0)[1]
|
||||
|
||||
# calculate traveltime gradient, division by R to transform from rad to km
|
||||
# multiply with -1 to get values for azimuth instead of back-azimuth
|
||||
ray_param_km_z_comp = arr.ray_param / np.tan(np.deg2rad(arr.incident_angle)) / input_dict['pt_R']
|
||||
ray_param_km_n_comp = arr.ray_param * (-1) * np.cos(np.deg2rad(baz)) / input_dict['pt_R']
|
||||
ray_param_km_e_comp = arr.ray_param * (-1) * np.sin(np.deg2rad(baz)) / input_dict['pt_R']
|
||||
input_dict['ray_param_km_z_comp'] = ray_param_km_z_comp
|
||||
input_dict['ray_param_km_n_comp'] = ray_param_km_n_comp
|
||||
input_dict['ray_param_km_e_comp'] = ray_param_km_e_comp
|
||||
|
||||
input_dict['time'] = arr.time
|
||||
|
||||
except Exception as e:
|
||||
print(e, input_dict)
|
||||
return input_dict
|
||||
|
||||
|
||||
def plot_points(points_list):
|
||||
import matplotlib.pyplot as plt
|
||||
fig = plt.figure()
|
||||
ax = fig.add_subplot(111, projection='3d')
|
||||
x = []
|
||||
y = []
|
||||
z = []
|
||||
color = np.full(len(points_list), np.nan)
|
||||
for index, points_dict in enumerate(points_list):
|
||||
x.append(points_dict['pt_lat'])
|
||||
y.append(points_dict['pt_lon'])
|
||||
z.append(points_dict['pt_depth'])
|
||||
arrivals = points_dict.get('arrivals')
|
||||
if arrivals:
|
||||
color[index] = arrivals[0].time
|
||||
ax.scatter(x, y, z, c=color)
|
||||
plt.show()
|
||||
|
||||
|
||||
def subtract_source_time(picks_list, origin_time):
|
||||
# WAS FUNCTION DEMEAN, but demean after calculating residuals using rtimes_tele in FMTOMO!!
|
||||
for phase_dict in picks_list:
|
||||
taup_time = phase_dict['taup_time']
|
||||
pick = phase_dict['pick']
|
||||
pick_time = pick.time
|
||||
ttres = pick_time - taup_time
|
||||
# I think doing this is wrong because taup times do NOT include station elevation!!! 5.3.20
|
||||
phase_dict['ttres'] = ttres
|
||||
|
||||
mean = np.mean([phase_dict['pick'].time.timestamp for phase_dict in picks_list])
|
||||
|
||||
for phase_dict in picks_list:
|
||||
phase_dict['ttres_demeaned'] = phase_dict['ttres'] - mean # see above comment
|
||||
phase_dict['abstimes'] = phase_dict['pick'].time - origin_time
|
||||
|
||||
return picks_list
|
||||
|
||||
|
||||
def translate_phase_names_TAUP(sorted_picks):
|
||||
# PKP and PKIKP are all just 'P' phases in fm3d (not very sure about this!)
|
||||
translations = {'PKIKP': 'PKP',
|
||||
'SKIKS': 'SKS',}
|
||||
for phase_name in sorted_picks.keys():
|
||||
if phase_name in translations.keys():
|
||||
sorted_picks[translations[phase_name]] = sorted_picks.pop(phase_name)
|
||||
return sorted_picks
|
||||
|
||||
|
||||
def write_picks_to_pickfile(pickfile, phase, picks_list, stations_dict, origin, picks_mode='abs'):
|
||||
fid = open(pickfile, 'w')
|
||||
header = '{npicks}\n' \
|
||||
'{lat_src:<15} {lon_src:<15} {depth_src:<15}\n' \
|
||||
'{phase_name}\n'
|
||||
# pickfile header including npicks, src coords, phasetype
|
||||
formatted_str = header.format(npicks=len(picks_list),
|
||||
lat_src=origin.latitude,
|
||||
lon_src=origin.longitude,
|
||||
depth_src=origin.depth,
|
||||
phase_name=phase)
|
||||
fid.write(formatted_str)
|
||||
|
||||
# write picks for each station to pickfile
|
||||
for phase_dict in picks_list:
|
||||
pick = phase_dict['pick']
|
||||
seed_id = pick.waveform_id.get_seed_string()
|
||||
# travel time residual (demeaned or abs)
|
||||
if picks_mode == 'abs':
|
||||
picktime = phase_dict['abstimes']
|
||||
elif picks_mode == 'res':
|
||||
warnings.warn('Using residuals here might not be exact. See above code where ttres_demeaned is calculated.')
|
||||
picktime = phase_dict['ttres_demeaned']
|
||||
else:
|
||||
raise IOError(f'Unknown picks_mode {picks_mode}')
|
||||
uncertainty = pick.time_errors.uncertainty
|
||||
network, station = seed_id.split('.')[:2]
|
||||
# get stations coords from metadata dict
|
||||
station_coords = stations_dict.get('{}.{}'.format(network, station))
|
||||
# prepare line to be written to pickfile and format, use traveltime residual
|
||||
line = '{lat_rec:<15} {lon_rec:<15} {depth_rec:<15} {picktime:<15} {uncert:15}\n'
|
||||
formatted_line = line.format(lat_rec=station_coords['latitude'],
|
||||
lon_rec=station_coords['longitude'],
|
||||
depth_rec=station_coords['elevation'] * (-1e-3),
|
||||
picktime=picktime,
|
||||
uncert=uncertainty)
|
||||
fid.write(formatted_line)
|
||||
print('Wrote {} picks for phase {} to file {}'.format(len(picks_list), phase, pickfile))
|
||||
fid.close()
|
||||
|
||||
|
||||
def remove_blacklisted_station_picks(picks, fname_blacklist, verbosity=1):
|
||||
blacklisted_stations = get_station_blacklist(fname_blacklist)
|
||||
deleted_picks = []
|
||||
deleted_stations = []
|
||||
for index, pick in list(reversed(list(enumerate(picks)))):
|
||||
seed_id = pick.waveform_id.get_seed_string()
|
||||
network, station = seed_id.split('.')[:2]
|
||||
nwst_id = '{}.{}'.format(network, station)
|
||||
if nwst_id in blacklisted_stations.keys():
|
||||
timewindow = blacklisted_stations[nwst_id]
|
||||
if not timewindow == 'always':
|
||||
tstart, tstop = [UTCDateTime(time) for time in timewindow.split('to')]
|
||||
if not tstart <= picks[index].time <= tstop:
|
||||
continue
|
||||
deleted_picks.append(picks.pop(index))
|
||||
deleted_stations.append(nwst_id)
|
||||
if verbosity > 0:
|
||||
print('Deleted picks for blacklisted stations:\n{}'.format(deleted_stations))
|
||||
return picks, deleted_stations
|
||||
|
||||
|
||||
def get_station_blacklist(fname_csv):
|
||||
with open(fname_csv, 'r') as infile:
|
||||
# skip first line
|
||||
blacklist = infile.readlines()[1:]
|
||||
blacklisted_stations = {}
|
||||
for line in blacklist:
|
||||
network, station, time = line.split(',')[:3]
|
||||
nwst_id = '{}.{}'.format(network, station)
|
||||
blacklisted_stations[nwst_id] = time
|
||||
return blacklisted_stations
|
||||
|
||||
|
||||
def remove_uncommon_picks(sorted_picks, min_picks_per_phase):
|
||||
for phase_name, picks_list in reversed(list(sorted_picks.items())):
|
||||
if len(picks_list) < min_picks_per_phase:
|
||||
msg = 'Removed picks for phase {}, as there are only {} picks given (threshold is {})'
|
||||
print(msg.format(phase_name, len(picks_list), min_picks_per_phase))
|
||||
del(sorted_picks[phase_name])
|
||||
return sorted_picks
|
||||
|
||||
|
||||
def sort_picks_by_phase(picks, stations_dict, source_origin, phase_types, model,
|
||||
max_phase_diff=50., merge_phases=None, verbosity=0):
|
||||
'''
|
||||
# First: Iterate through all picks to estimate possible phases for source/location combination, then assign
|
||||
# each pick to one phase and return sorted picks dictionary, as there has to be one pickfile for each phasetype
|
||||
|
||||
:param picks:
|
||||
:param stations_dict:
|
||||
:param source_origin:
|
||||
:param phase_types:
|
||||
:param model:
|
||||
:param max_phase_diff:
|
||||
:param merge_phases: assign a phase (key) to another phase (value) e.g. {Pdiff: P}
|
||||
:param verbosity:
|
||||
:return:
|
||||
'''
|
||||
print('Starting to sort picks by phase calculating theoretical travel times for each pick...')
|
||||
phases_dict = {}
|
||||
for pick in picks:
|
||||
# PROBLEM: seed_id from PyLoT does not contain Location ID!!! Makes it hard to find station coords
|
||||
# workaround: use stations_dict (ignore channel and location ID) instead of metadata.get_coordinates()
|
||||
seed_id = pick.waveform_id.get_seed_string()
|
||||
network, station = seed_id.split('.')[:2]
|
||||
phaseID = pick.phase_hint
|
||||
uncertainty = pick.time_errors.uncertainty
|
||||
# skip different phase
|
||||
if not phaseID in phase_types:
|
||||
continue
|
||||
# pick invalid if no uncertainty is given
|
||||
if not uncertainty:
|
||||
continue
|
||||
station_coords = stations_dict.get('{}.{}'.format(network, station))
|
||||
if not station_coords:
|
||||
print('Could not find coordinates for station: {}'.format(seed_id))
|
||||
continue
|
||||
# estimate phase type by taking the closest phase from Tau-P method (time is relative to source origin)
|
||||
phase_name, phase_time_rel, phase_diff = get_closest_taup_phase(source_origin, station_coords,
|
||||
phaseID, pick.time, model)
|
||||
if phase_diff > max_phase_diff:
|
||||
if verbosity > 0:
|
||||
print ('Warning, max_diff too large (> {} s) for phase {} at {}. Skipping'.format(max_phase_diff,
|
||||
phase_name, seed_id))
|
||||
continue
|
||||
phase_time = source_origin.time + phase_time_rel
|
||||
|
||||
# merge phase if selected, keep track of original phase for TauPy node initiation
|
||||
true_phase = phase_name
|
||||
if merge_phases and phase_name in merge_phases.keys():
|
||||
phase_name = merge_phases[phase_name]
|
||||
|
||||
if not phase_name in phases_dict.keys():
|
||||
print('Adding phase to sorted picks dictionary: {}'.format(phase_name))
|
||||
phases_dict[phase_name] = []
|
||||
phases_dict[phase_name].append({'pick': pick,
|
||||
'taup_time': phase_time,
|
||||
'true_phase': true_phase,})
|
||||
return phases_dict
|
||||
|
||||
def get_closest_taup_phase(source_origin, station_coords, phase, pick_time, model='ak135'):
|
||||
'''
|
||||
Estimate phase that was picked by searching for earliest P/S phase arriving at
|
||||
station_coords for given source_coords using TauPy. As PyLoT only searches for first arrivals
|
||||
using the same method, this should(!) yield the correct phase.
|
||||
:return:
|
||||
'''
|
||||
phases = {'P': [],
|
||||
'S': []}
|
||||
phase_lists = {'P': ['ttp'],
|
||||
'S': ['tts']}
|
||||
|
||||
# possible phases for fm3d (*K* and *KIK* -> *)
|
||||
#phase_list = {'P': ['P', 'PKP', 'PKiKP', 'PKIKP', 'PcP', 'ScP', 'SKP', 'PKKP', 'SKKP', 'PP',],
|
||||
# 'S': ['S', 'SKS', 'SKIKS', 'ScS']}
|
||||
|
||||
# in case pick phase hint is just P or S
|
||||
if phase in phases.keys():
|
||||
phase_list = phase_lists[phase]
|
||||
common_phase = phase
|
||||
# in case an explicit phase name is given use only this phase
|
||||
else:
|
||||
phase_list = [phase]
|
||||
common_phase = identifyPhaseID(phase)
|
||||
|
||||
model = TauPyModel(model=model)
|
||||
arrivals = model.get_travel_times_geo(source_origin.depth,
|
||||
source_origin.latitude,
|
||||
source_origin.longitude,
|
||||
station_coords['latitude'],
|
||||
station_coords['longitude'],
|
||||
phase_list)
|
||||
|
||||
# identifies phases from arrivals as P or S phase, not necessary when using 'ttp' or 'tts' for get_travel_times_geo
|
||||
for arr in arrivals:
|
||||
phases[identifyPhaseID(arr.phase.name)].append(arr)
|
||||
|
||||
source_time = source_origin.time
|
||||
|
||||
if not arrivals:
|
||||
raise Exception('No arrivals found for source.')
|
||||
|
||||
# get first P or S onsets from arrivals list
|
||||
arr, min_diff = min([(arr, abs(source_time + arr.time - pick_time)) for arr in phases[common_phase]],
|
||||
key=lambda t: t[1])
|
||||
return arr.name, arr.time, min_diff
|
||||
|
||||
|
||||
def get_event_obspy_dmt(eventdir):
|
||||
event_pkl_file = os.path.join(eventdir, 'info', 'event.pkl')
|
||||
if not os.path.exists(event_pkl_file):
|
||||
raise IOError('Could not find event path for event: {}'.format(eventdir))
|
||||
event = qml_from_obspyDMT(event_pkl_file)
|
||||
return event
|
||||
|
||||
|
||||
def get_event_pylot(eventdir, extension=''):
|
||||
event_id = get_event_id(eventdir)
|
||||
filename = os.path.join(eventdir, 'PyLoT_{}{}.xml'.format(event_id, extension))
|
||||
if not os.path.isfile(filename):
|
||||
return
|
||||
cat = read_events(filename)
|
||||
return cat[0]
|
||||
|
||||
def get_event_id(eventdir):
|
||||
event_id = os.path.split(eventdir)[-1]
|
||||
return event_id
|
||||
|
||||
def get_picks(eventdir, extension=''):
|
||||
event_id = get_event_id(eventdir)
|
||||
filename = 'PyLoT_{}{}.xml'
|
||||
filename = filename.format(event_id, extension)
|
||||
fpath = os.path.join(eventdir, filename)
|
||||
fpaths = glob.glob(fpath)
|
||||
if len(fpaths) == 1:
|
||||
cat = read_events(fpaths[0])
|
||||
picks = cat[0].picks
|
||||
return picks
|
||||
elif len(fpaths) == 0:
|
||||
print('get_picks: File not found: {}'.format(fpath))
|
||||
return
|
||||
print(f'WARNING: Ambiguous pick file specification. Found the following pick files {fpaths}\nFilemask: {fpath}')
|
||||
return
|
||||
|
||||
def init_sources_file(nsrc, filename='sources.in'):
|
||||
infile_source = open(filename, 'w')
|
||||
infile_source.write('{}\n'.format(nsrc))
|
||||
return infile_source
|
||||
|
||||
def init_receivers_file(nrec, filename='receivers.in'):
|
||||
infile_rec = open(filename, 'w')
|
||||
infile_rec.write('{}\n'.format(nrec))
|
||||
return infile_rec
|
||||
|
||||
def append_source(fid, origin):
|
||||
pass
|
||||
|
||||
def append_receiver(fid, station_coords):
|
||||
# TODO check if order is correct
|
||||
fid.write('{rad:15} {lat:15} {lon:15} \n'.format(rad=station_coords['elevation'],
|
||||
lat=station_coords['latitude'],
|
||||
lon=station_coords['longitude']))
|
||||
|
||||
|
||||
def organize_receivers(fnin, unique=False):
|
||||
''' Open FMTOMO receivers.in file and read position of each receiver into a dict.'''
|
||||
|
||||
with open(fnin, 'r') as infile:
|
||||
nRec = int(infile.readline())
|
||||
rec_dict = {}
|
||||
for rec_number in range(1, nRec + 1):
|
||||
rad, lat, lon = [float(value) for value in infile.readline().split()]
|
||||
# dummy read next 3 lines
|
||||
for ind in range(3):
|
||||
infile.readline()
|
||||
|
||||
receiver = {'rad': rad,
|
||||
'lat': lat,
|
||||
'lon': lon, }
|
||||
if unique:
|
||||
if receiver in rec_dict.values():
|
||||
continue
|
||||
|
||||
rec_dict[rec_number] = receiver
|
||||
|
||||
return rec_dict
|
||||
|
||||
|
||||
def organize_sources(fnin):
|
||||
''' Open FMTOMO sources.in file and read position and phase of each source into a dict.'''
|
||||
with open(fnin, 'r') as infile:
|
||||
nSrc = int(infile.readline())
|
||||
src_dict = {}
|
||||
for src_number in range(nSrc):
|
||||
src_number += 1
|
||||
teleseismic_flag = int(infile.readline())
|
||||
if teleseismic_flag != 1: raise ValueError('Source not teleseismic!')
|
||||
phase = infile.readline().strip()
|
||||
rad, lat, lon = [float(value) for value in infile.readline().split()[:3]]
|
||||
# dummy read next 4 lines
|
||||
for ind in range(4):
|
||||
infile.readline()
|
||||
src_dict[src_number] = {'phase': phase,
|
||||
'depth': rad,
|
||||
'lat': lat,
|
||||
'lon': lon,}
|
||||
return src_dict
|
||||
|
||||
|
||||
def organize_event_names(fnin):
|
||||
infile = open(fnin, 'r')
|
||||
events = [line.split()[-1].strip() for line in infile.readlines()[1:]]
|
||||
infile.close()
|
||||
return events
|
||||
|
||||
|
||||
def export_otimes(otimes, fn_out):
|
||||
np.savetxt(fn_out, otimes, fmt=['%8d', '%8d', '%8d', '%8d', '% 4.6f', '% 4.6f'],
|
||||
header=str(len(otimes)), comments='')
|
||||
print('Wrote file:', fn_out)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# testing area
|
||||
prepare_fmtomo_dir_first_run('/data/AdriaArray_Data/fmtomo_adriaarray/v0/test_init')
|
104
pylot/tomography/fmtomo_tools/get_tt_residuals.py
Normal file
104
pylot/tomography/fmtomo_tools/get_tt_residuals.py
Normal file
@ -0,0 +1,104 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# get traveltime residuals using synthetic 1D travel times from FMTOMO hybrid method
|
||||
#
|
||||
# this script is used to make sure that synthetic travel times are the same for synthetic data residuals (rtimes)
|
||||
# and observed data residuals, because of small differences between hybrid 1D solver and taup method
|
||||
# Therefore, correlation output times are absolute (minus source time) values and residuals are calcuated using
|
||||
# FMTOMO hybrid reference times (e.g. rtimes.dat)
|
||||
#
|
||||
import os
|
||||
import argparse
|
||||
import shutil
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
from pylot.tomography.fmtomo_tools.fmtomo_teleseismic_utils import organize_event_names
|
||||
|
||||
# Note: weighting of mean might not be reasonable due to uncertainty distribution along the array
|
||||
def main(infile_rtimes, fn_events, kde_file, demean=True, no_deref=False, weight_kde=True, weight=False):
|
||||
eventnames = organize_event_names(fn_events)
|
||||
assert not(weight and weight_kde), 'Cannot use two weights'
|
||||
|
||||
if not no_deref:
|
||||
reftimes = np.genfromtxt(infile_rtimes)
|
||||
if weight_kde:
|
||||
kde_values = np.genfromtxt(kde_file)
|
||||
assert len(kde_values) == len(reftimes), 'Missmatch in reftimes and kde file length'
|
||||
|
||||
nSrc = len(eventnames)
|
||||
|
||||
diff_in_means = []
|
||||
# iterate over all sources
|
||||
for index in range(nSrc):
|
||||
srcid = index + 1
|
||||
eventname = eventnames[index]
|
||||
print('{} ({}/{})'.format(eventname, srcid, nSrc))
|
||||
# get pick filename and read observed times
|
||||
pickfile = os.path.join('picks', eventname)
|
||||
obsdata_src = np.loadtxt(pickfile, skiprows=3)
|
||||
# make safety copy
|
||||
picksafedir = os.path.join('picks', 'save')
|
||||
if not os.path.isdir(picksafedir):
|
||||
os.mkdir(picksafedir)
|
||||
shutil.copy(pickfile, picksafedir)
|
||||
npicks = len(obsdata_src)
|
||||
# read observed times from pickfile
|
||||
tt_obs = obsdata_src[:, 3]
|
||||
# read uncertainties from pickfile
|
||||
uncs = obsdata_src[:, 4]
|
||||
|
||||
if no_deref:
|
||||
tt_res = tt_obs
|
||||
else:
|
||||
indices = np.where(reftimes[:, 1] == srcid)[0]
|
||||
assert (len(indices) == npicks), 'Missmatch in indices for srcids'
|
||||
# read reference teleseismic times from FMTOMO run
|
||||
tt_ref = reftimes[:, 4][indices]
|
||||
# calculate residuals
|
||||
tt_res = tt_obs - tt_ref
|
||||
|
||||
# set new residual to obsdata array
|
||||
obsdata_src[:, 3] = tt_res
|
||||
# get kde values
|
||||
#weights = 1./kde_values[:, 2][indices] if weight_kde else None
|
||||
#
|
||||
weights = 1. / uncs ** 2 if weight else None
|
||||
# demean residuals for current source
|
||||
mean = np.average(tt_res, weights=weights)
|
||||
mean_unweighted = np.mean(tt_res)
|
||||
diff_in_means.append(mean - mean_unweighted)
|
||||
print('Mean:', mean)
|
||||
print('Unweighted Mean:', mean_unweighted)
|
||||
print('Demean setting:', demean)
|
||||
if demean:
|
||||
obsdata_src[:, 3] -= mean
|
||||
#obsdata_src[:, 3] = mtimes[:, 4][indices]
|
||||
# Write new pickfiles
|
||||
with open(pickfile, 'r') as infile_pick:
|
||||
header = infile_pick.readlines()[:3]
|
||||
with open(pickfile, 'w') as outfile_pick:
|
||||
for line in header:
|
||||
outfile_pick.write(line)
|
||||
for line in obsdata_src:
|
||||
outfile_pick.write('{:10.4f} {:10.4f} {:10.4f} {:15.8f} {:15.8f}\n'.format(*line))
|
||||
plt.hist(diff_in_means, bins=100)
|
||||
plt.title('Diffs in means (weighted - unweighted) [s]')
|
||||
plt.show()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Calculate residuals for absolute observation times using reference file from FMTOMO')
|
||||
parser.add_argument('--rtimes', default='rtimes_tele.dat', help='reference_file')
|
||||
parser.add_argument('--sourcefile', default='input_source_file_P.in', help='input_source_file')
|
||||
#parser.add_argument('--kdefile', default='kde_weights.txt', help='input file with station kde values for weighting')
|
||||
parser.add_argument('-nd', '--no_demean', action='store_true', default=False,
|
||||
help='do not mean correct travel times')
|
||||
parser.add_argument('-nr', '--no_reftimes', action='store_true', default=False,
|
||||
help='do not use reference times (e.g. picks are already tt residuals). rtimes file still required at the moment.')
|
||||
#parser.add_argument('-nwk', '--no_weight_kde', action='store_true', default=True, help='do not weight travel times')
|
||||
args = parser.parse_args()
|
||||
#args.kdefile
|
||||
main(args.rtimes, args.sourcefile, None, not(args.no_demean), weight_kde=False, no_deref=args.no_reftimes) #not(args.no_weight))
|
174
pylot/tomography/fmtomo_tools/gmtslice_sidehook.py
Normal file
174
pylot/tomography/fmtomo_tools/gmtslice_sidehook.py
Normal file
@ -0,0 +1,174 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
import os
|
||||
import numpy as np
|
||||
|
||||
from obspy.geodetics import gps2dist_azimuth, locations2degrees
|
||||
from pylot.tomography.utils import get_coordinate_from_dist_baz
|
||||
|
||||
R=6371.
|
||||
|
||||
def main():
|
||||
profiles_infile = '/home/marcel/AlpArray/vtk_files/points_mark.txt'
|
||||
gmtslice_dir = '/rscratch/minos13/marcel/fmtomo_alparray/v3.5/alparray_mantle_diehl_crust_included_hf_gradient_smoothing/plot'
|
||||
####
|
||||
# maximum point spacing in km (decreases with depth)
|
||||
dpoints = 1.
|
||||
dpi = 100
|
||||
model_depth = 606.
|
||||
###
|
||||
with open(profiles_infile, 'r') as infile:
|
||||
profiles = infile.readlines()
|
||||
|
||||
for profile in profiles:
|
||||
print('Profile: ', profile)
|
||||
name = profile.split()[0]
|
||||
lon1, lat1, lon2, lat2 = [float(item) for item in profile.split()[1:]]
|
||||
|
||||
|
||||
def get_gmtslice_gc(gmtplot_dir, lat1, lon1, lat2, lon2, model_depth=606., dpoints=1, fn_out_slice='grid2dvgc.z',
|
||||
rel=True, fname_vgrid='../vgrids.in', fname_vgrid_ref='../vgridsref.in'):
|
||||
parfile_str_len = 28
|
||||
assert (len(fname_vgrid) < parfile_str_len), 'Check length of filename: {}'.format(fname_vgrid)
|
||||
assert (len(fname_vgrid_ref) < parfile_str_len), 'Check length of filename: {}'.format(fname_vgrid_ref)
|
||||
|
||||
infile_default = 'gmtslice_default.in'
|
||||
fn_out = 'gmtslice.in'
|
||||
bounds_out_gmt = 'boundgc.gmt'
|
||||
|
||||
cwd = os.getcwd()
|
||||
|
||||
os.chdir(gmtplot_dir)
|
||||
with open(infile_default, 'r') as infile:
|
||||
gmt_infile = infile.readlines()
|
||||
|
||||
# calculate great circle distance
|
||||
max_dist = gps2dist_azimuth(lat1, lon1, lat2, lon2, a=R * 1e3, f=0)[0]/1e3
|
||||
npointsR = int(model_depth / dpoints + 1)
|
||||
npointsLateral = int(max_dist / dpoints + 1)
|
||||
print('nR, nLateral:', npointsR, npointsLateral)
|
||||
|
||||
# filename
|
||||
gmt_infile[3] = '{} \n'.format(fname_vgrid)
|
||||
gmt_infile[4] = '{} \n'.format(fname_vgrid_ref)
|
||||
|
||||
# activate gc generation
|
||||
gmt_infile[51] = '1 \n'
|
||||
# modify lines with lat lon boundaries
|
||||
gmt_infile[52] = '{:5.2f} {:5.2f}\n'.format(lat1, lon1)
|
||||
gmt_infile[53] = '{:5.2f} {:5.2f}\n'.format(lat2, lon2)
|
||||
|
||||
abs_rel = 1 if rel else 0
|
||||
gmt_infile[58] = '{} \n'.format(abs_rel)
|
||||
|
||||
# modify lines with n Points
|
||||
gmt_infile[65] = '{:<5d} {:<5d}\n'.format(npointsLateral, npointsR)
|
||||
|
||||
with open(fn_out, 'w') as outfile:
|
||||
for line in gmt_infile:
|
||||
outfile.write(line)
|
||||
|
||||
print('Executing gmtslice...')
|
||||
os.system('gmtslice')
|
||||
print('Done!')
|
||||
|
||||
with open(bounds_out_gmt, 'r') as infile:
|
||||
bds = [float(bd) for bd in infile.readlines()]
|
||||
|
||||
bounds = '-R{bds[0]}/{bds[1]}/{bds[2]}/{bds[3]}'.format(bds=bds)
|
||||
xyz_string = 'gmt xyz2grd {grid_out} -Ggrid2dvgc.grd -I{bds[4]}+/{bds[5]}+ -ZLB {bounds}'.format(
|
||||
grid_out=fn_out_slice, bds=bds, bounds=bounds)
|
||||
print(xyz_string)
|
||||
os.system(xyz_string)
|
||||
|
||||
grid = np.loadtxt(fn_out_slice)
|
||||
dist_grid = []
|
||||
lon_grid = []
|
||||
lat_grid = []
|
||||
_, azim, bazim = gps2dist_azimuth(lat1, lon1, lat2, lon2, a=R * 1e3, f=0)
|
||||
#ddist = / (npointsLateral - 1)
|
||||
#ddepth = (bds[3] - bds[2]) / (npointsR - 1)
|
||||
for depth in np.linspace(-bds[3], -bds[2], num=npointsR):
|
||||
for dist in np.linspace(0, locations2degrees(lat1, lon1, lat2, lon2), num=npointsLateral):
|
||||
lon, lat = get_coordinate_from_dist_baz((lon1, lat1), dist, azim)
|
||||
lon_grid.append(lon)
|
||||
lat_grid.append(lat)
|
||||
#dist = ddist * indexLat
|
||||
#depth = ddepth * indexR
|
||||
dist_grid.append(np.array([dist, depth]))
|
||||
|
||||
dist_grid = np.array(dist_grid)
|
||||
lat_grid = np.array(lat_grid)
|
||||
lon_grid = np.array(lon_grid)
|
||||
|
||||
os.chdir(cwd)
|
||||
return grid, dist_grid, lat_grid, lon_grid, max_dist
|
||||
|
||||
|
||||
def get_gmtslice_depth(gmtplot_dir, depth, fn_out_slice='grid2dvd.z', fname_vgrid='../vgrids.in',
|
||||
fname_vgrid_ref='../vgridsref.in'):
|
||||
infile_default = 'gmtslice_default.in'
|
||||
fn_out = 'gmtslice.in'
|
||||
bounds_out_gmt = 'bounddp.gmt'
|
||||
|
||||
cwd = os.getcwd()
|
||||
|
||||
os.chdir(gmtplot_dir)
|
||||
with open(infile_default, 'r') as infile:
|
||||
gmt_infile = infile.readlines()
|
||||
|
||||
# filename
|
||||
gmt_infile[3] = '{} \n'.format(fname_vgrid)
|
||||
gmt_infile[4] = '{} \n'.format(fname_vgrid_ref)
|
||||
|
||||
# activate depth slice generation
|
||||
gmt_infile[41] = '1 \n'
|
||||
# set depth (negative for whatever reason)
|
||||
gmt_infile[42] = '{:5.2f} \n'.format(-depth)
|
||||
|
||||
with open(fn_out, 'w') as outfile:
|
||||
for line in gmt_infile:
|
||||
outfile.write(line)
|
||||
|
||||
print('Executing gmtslice...')
|
||||
os.system('gmtslice')
|
||||
print('Done!')
|
||||
|
||||
with open(bounds_out_gmt, 'r') as infile:
|
||||
bds = [float(bd) for bd in infile.readlines()]
|
||||
|
||||
bounds = '-R{bds[0]}/{bds[1]}/{bds[2]}/{bds[3]}'.format(bds=bds)
|
||||
xyz_string = 'gmt xyz2grd {grid_out} -Ggrid2dvd.grd -I{bds[4]}+/{bds[5]}+ -ZLB {bounds}'.format(
|
||||
grid_out=fn_out_slice, bds=bds, bounds=bounds)
|
||||
print(xyz_string)
|
||||
#os.system(xyz_string)
|
||||
|
||||
grid = np.loadtxt(fn_out_slice)
|
||||
lonlat = []
|
||||
lon0, lon1 = bds[:2]
|
||||
lat0, lat1 = bds[2:4]
|
||||
nlon = int(bds[4])
|
||||
nlat = int(bds[5])
|
||||
for lon in np.linspace(lon0, lon1, num=nlon):
|
||||
for lat in np.linspace(lat0, lat1, num=nlat):
|
||||
lonlat.append(np.array([lon, lat]))
|
||||
|
||||
lonlat = np.array(lonlat)
|
||||
|
||||
os.chdir(cwd)
|
||||
return grid, lonlat
|
||||
|
||||
|
||||
def grdimage_slice(lat1, lon1, lat2, lon2, bounds, name, npointsLateral, npointsR, dpi):
|
||||
proj = "-JX{:05.2f}i/{:05.2f}i".format(npointsLateral / dpi, npointsR / dpi)
|
||||
fnout = 'gmtslice_{}_{:.1f}_{:.1f}-{:.1f}_{:.1f}'.format(name, lat1, lon1, lat2, lon2)
|
||||
grdimage_string = 'gmt grdimage grid2dvgc.grd {bounds} {proj} -Ba50f10/a50f10 -Cpolar_inv_3.cpt -K > {fnout}'.format(
|
||||
bounds=bounds, proj=proj, fnout=fnout+'.ps')
|
||||
print(grdimage_string)
|
||||
os.system(grdimage_string)
|
||||
print('Convert to png...')
|
||||
os.system('convert {} {}'.format(fnout + '.ps', fnout + '.png'))
|
||||
|
||||
|
||||
|
43
pylot/tomography/fmtomo_tools/heatmap_two_models.py
Normal file
43
pylot/tomography/fmtomo_tools/heatmap_two_models.py
Normal file
@ -0,0 +1,43 @@
|
||||
import sys
|
||||
|
||||
fnin1 = '/data/AlpArray_Data/fmtomo/v6/crust_incl_hf_sm_FIX_DTS_grad_sm30_dm10/it_12/vgrids.in'
|
||||
fnin2 = '/data/AlpArray_Data/fmtomo/v6/crust_incl_hf_sm_FIX_TESAURO_grad_sm30_dm10/it_12/vgrids.in'
|
||||
fnout = '/data/AlpArray_Data/fmtomo/v6/crust_incl_hf_sm_FIX_DTS_grad_sm30_dm10/vg_TES_i12sm30dm10.in'
|
||||
|
||||
|
||||
def check_vgrids_header_line(l1, l2, epsilon=1e-6):
|
||||
items1 = [float(item) for item in l1.split()]
|
||||
items2 = [float(item) for item in l2.split()]
|
||||
for i1, i2 in zip(items1, items2):
|
||||
if not abs(i1 - i2) < epsilon:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
|
||||
def vgrids_diff(fnin1, fnin2, fnout):
|
||||
diffs = []
|
||||
with open(fnin1, 'r') as infile1:
|
||||
with open(fnin2, 'r') as infile2:
|
||||
with open(fnout, 'w') as outfile:
|
||||
for index, l1 in enumerate(infile1):
|
||||
l2 = infile2.readline()
|
||||
if index < 4:
|
||||
assert check_vgrids_header_line(l1, l2), 'Different grid dimensions!'
|
||||
outfile.write(l1)
|
||||
else:
|
||||
try:
|
||||
v1 = float(l1.split()[0])
|
||||
v2 = float(l2.split()[0])
|
||||
except Exception as e:
|
||||
print('Read problem: {}'.format(e))
|
||||
sys.exit()
|
||||
vdiff = abs(v2 - v1)
|
||||
diffs.append(vdiff)
|
||||
outfile.write('{}\n'.format(vdiff))
|
||||
|
||||
print('Finished writing file', fnout)
|
||||
print('Max diff:', max(diffs))
|
||||
|
||||
|
||||
vgrids_diff(fnin1, fnin2, fnout)
|
126
pylot/tomography/fmtomo_tools/misfit_evaluation.py
Normal file
126
pylot/tomography/fmtomo_tools/misfit_evaluation.py
Normal file
@ -0,0 +1,126 @@
|
||||
import os
|
||||
import json
|
||||
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
from pylot.tomography.fmtomo_tools.fmtomo_teleseismic_utils import organize_receivers
|
||||
|
||||
# TODO: demeaning source by source??
|
||||
|
||||
def calc_misfit(data, synth, unc):
|
||||
return np.sum(((data - synth) / unc) ** 2) / len(data)
|
||||
|
||||
def calc_mean_residuals(data, synth):
|
||||
return np.sum(abs(data - synth)) / len(data)
|
||||
|
||||
|
||||
def sort_stations_for_search(station_coords, receivers_dict):
|
||||
epsilon = 1e-1 # very large epsilon actually...
|
||||
coords_unique = coords_unique_receivers(receivers_dict)
|
||||
latlon_dict = {}
|
||||
for nwst_id, coords in station_coords.items():
|
||||
lat = coords['latitude']
|
||||
lon = coords['longitude']
|
||||
for latu, lonu in coords_unique:
|
||||
if abs(lat - latu) < epsilon and abs(lon - lonu) < epsilon:
|
||||
latlon_dict[(latu, lonu)] = nwst_id
|
||||
return latlon_dict
|
||||
|
||||
|
||||
def extract_stations_for_network(station_coords, network_id):
|
||||
station_coords_extracted = {}
|
||||
for nwst_id, coords in station_coords.items():
|
||||
nw, st = nwst_id.split('.')
|
||||
if nw == network_id:
|
||||
station_coords_extracted[nwst_id] = coords
|
||||
return station_coords_extracted
|
||||
|
||||
|
||||
def get_receiver_ids(latlon_list, receivers_dict):
|
||||
receiver_ids = []
|
||||
for recid, coords in receivers_dict.items():
|
||||
lat, lon = (coords['lat'], coords['lon'])
|
||||
if (lat, lon) in latlon_list:
|
||||
receiver_ids.append(recid)
|
||||
return receiver_ids
|
||||
|
||||
|
||||
def coords_unique_receivers(receivers_dict):
|
||||
coords_unique = []
|
||||
for coords in receivers_dict.values():
|
||||
latlon_tuple = (coords['lat'], coords['lon'])
|
||||
if not latlon_tuple in coords_unique:
|
||||
coords_unique.append(latlon_tuple)
|
||||
return coords_unique
|
||||
|
||||
#def plot_residuals()
|
||||
|
||||
def main():
|
||||
#wdir = '/rscratch/minos13/marcel/fmtomo_alparray/alparray_mantle_diehl_crust_included_v3_hf'
|
||||
wdir = '/data/AlpArray_Data/fmtomo/v5/crust_incl_hf_sm_FIX_DTS_grad_sm30_dm10'
|
||||
os.chdir(wdir)
|
||||
|
||||
receivers_dict = organize_receivers('receivers.in')
|
||||
|
||||
print(wdir)
|
||||
misfits = {}
|
||||
mean_residuals = {}
|
||||
for extract_network in [None, 'Z3', 'ZS', ]:
|
||||
misfits[extract_network] = []
|
||||
mean_residuals[extract_network] = []
|
||||
with open('/rscratch/minos13/marcel/alparray/station_coords.json', 'r') as infile:
|
||||
station_coords = json.load(infile)
|
||||
|
||||
print('\nCalculating residuals for network:', extract_network)
|
||||
if extract_network:
|
||||
station_coords = extract_stations_for_network(station_coords, extract_network)
|
||||
latlon_dict = sort_stations_for_search(station_coords, receivers_dict)
|
||||
rec_ids = get_receiver_ids(list(latlon_dict.keys()), receivers_dict)
|
||||
|
||||
otimes = np.genfromtxt('otimes.dat', skip_header=1)
|
||||
#otimes_orig = np.genfromtxt('otimes_orig.dat', skip_header=1)
|
||||
ttimes = otimes[:, 4]
|
||||
uncs = otimes[:, 5]
|
||||
nsrc = int(otimes[-1, 1])
|
||||
|
||||
rtimes = np.genfromtxt('rtimes_tele.dat')[:, 4]
|
||||
|
||||
for itstep in range(0, 25):
|
||||
nRays = 0
|
||||
mtimes_all = np.genfromtxt('it_{}/arrivals.dat'.format(itstep))
|
||||
mtimes = mtimes_all[:, 4]
|
||||
mtimes_diff = mtimes - rtimes
|
||||
|
||||
for srcid in range(nsrc):
|
||||
srcid += 1
|
||||
# get all indices of current source id
|
||||
indices = np.where(mtimes_all[:, 1] == srcid)
|
||||
nRays += len(indices[0])
|
||||
mtimes_diff[indices] -= np.mean(mtimes_diff[indices])
|
||||
|
||||
# get all indices that are in rec_ids list as well
|
||||
mask_recs = np.isin(mtimes_all[:, 0].astype(int), rec_ids)
|
||||
|
||||
mf = calc_misfit(ttimes[mask_recs], mtimes_diff[mask_recs], uncs[mask_recs])
|
||||
sr = calc_mean_residuals(ttimes[mask_recs], mtimes_diff[mask_recs])
|
||||
misfits[extract_network].append(mf)
|
||||
mean_residuals[extract_network].append(sr)
|
||||
print('It: {}, misfit: {}, mean res: {}, nrays: {}'.format(itstep, mf, sr, nRays))
|
||||
|
||||
#plt.plot(mean_residuals[extract_network], label=extract_network)
|
||||
plt.plot(misfits[extract_network], label=extract_network)
|
||||
|
||||
plt.title(wdir)
|
||||
plt.ylabel('Mean residuals [s]')
|
||||
plt.xlabel('Iteration')
|
||||
plt.legend()
|
||||
plt.show()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
|
||||
|
||||
|
||||
|
69
pylot/tomography/fmtomo_tools/model_slice_plomerova.py
Normal file
69
pylot/tomography/fmtomo_tools/model_slice_plomerova.py
Normal file
@ -0,0 +1,69 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import glob, os, shutil
|
||||
|
||||
from fmtomo_tools.fmtomo_grid_utils import read_vgrid, write_vgrid
|
||||
|
||||
pjoin = os.path.join
|
||||
|
||||
pwd = '/data/AlpArray_Data/fmtomo/v6/crust_incl_hf_sm_FIX_DTS_grad_sm30_dm10_EASI_test_Plomerova/'
|
||||
|
||||
vgrid_file_in = pjoin(pwd, 'vgrids_dts_crust.in')
|
||||
vgrid_file_out = pjoin(pwd, 'vgrids_dts_crust_variance_slice.in')
|
||||
|
||||
latmin_rec, latmax_rec = 44.8, 51.3
|
||||
lonmin_rec, lonmax_rec = 11.94, 14.66
|
||||
|
||||
latmin_grid, latmax_grid = 44.45, 52.55
|
||||
lonmin_grid, lonmax_grid = 10.83, 15.77
|
||||
|
||||
|
||||
def modify_pickfiles():
|
||||
picks_path = pjoin(pwd, 'picks_orig')
|
||||
os.chdir(picks_path)
|
||||
|
||||
outdir = pjoin(pwd, 'picks')
|
||||
|
||||
|
||||
infiles = glob.glob('*.ttf')
|
||||
for infile in infiles:
|
||||
lines_out = []
|
||||
with open(infile, 'r') as fid:
|
||||
eventid = infile.split('.ttf')[0]
|
||||
npicks = int(fid.readline())
|
||||
|
||||
# copy source/phase header
|
||||
for _ in range(2):
|
||||
lines_out.append(fid.readline())
|
||||
|
||||
for line in fid:
|
||||
lat, lon = [float(item) for item in line.split()[:2]]
|
||||
if latmin_rec < lat < latmax_rec and lonmin_rec < lon < lonmax_rec:
|
||||
lines_out.append(line)
|
||||
|
||||
fn_out = pjoin(outdir, infile)
|
||||
with open(fn_out, 'w') as outfile:
|
||||
# number of picks: list contents - 2 header lines
|
||||
outfile.write(f'{len(lines_out) - 2}\n')
|
||||
for line in lines_out:
|
||||
outfile.write(line)
|
||||
|
||||
print(f'Modified {eventid}: Removed {npicks - len(lines_out)} out of {npicks} picks/stations')
|
||||
|
||||
os.chdir(pwd)
|
||||
|
||||
|
||||
def modify_variance_slice():
|
||||
grid, gridN, gridDelta, gridStart = read_vgrid(vgrid_file_in)
|
||||
|
||||
for index, latlon in enumerate(zip(grid['lats'], grid['lons'])):
|
||||
lat, lon = latlon
|
||||
if not latmin_grid < lat < latmax_grid or not lonmin_grid < lon < lonmax_grid:
|
||||
grid['covs'][index] = 1e-6
|
||||
|
||||
write_vgrid(grid, gridN, gridDelta, gridStart, vgrid_file_out)
|
||||
|
||||
|
||||
#modify_pickfiles()
|
||||
modify_variance_slice()
|
294
pylot/tomography/fmtomo_tools/modify_otimes.py
Normal file
294
pylot/tomography/fmtomo_tools/modify_otimes.py
Normal file
@ -0,0 +1,294 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
import os
|
||||
|
||||
import numpy as np
|
||||
from scipy.interpolate import RegularGridInterpolator
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
from pylot.tomography.fmtomo_tools.fmtomo_teleseismic_utils import export_otimes, organize_receivers
|
||||
|
||||
def crustal_correction_using_differences(fname_otimes, fname_otimes_diff, fname_otimes_out):
|
||||
otimes = np.loadtxt(fname_otimes, skiprows=1)
|
||||
otimes_diff = np.loadtxt(fname_otimes_diff, skiprows=1)
|
||||
|
||||
src_means = source_means(otimes_diff)
|
||||
|
||||
#mean_diffs = np.mean([float(item.split()[4]) for item in otimes_diff[1:]])
|
||||
print('Mean_diffs of all sources: ', np.mean(list(src_means.values())))
|
||||
# TODO: Check if demeaning is useful for crustal correction using synthetics (was it b4 using ak135_diehl 1D?)
|
||||
# Update 27.5.2020: If not demeaned, overall velocity perturbation shifted to negative in whole upper mantle
|
||||
# BUT: demean has to be executed for each source, not with global mean!
|
||||
|
||||
with open(fname_otimes_out, 'w') as outfile:
|
||||
outfile.write('{}\n'.format(len(otimes)))
|
||||
for index, line in enumerate(otimes):
|
||||
src_id = line[1]
|
||||
ttime = line[4]
|
||||
ttime_diff = otimes_diff[index][4]
|
||||
new_time = ttime - ttime_diff + src_means[src_id]
|
||||
line[4] = new_time
|
||||
for col_index, item in enumerate(line):
|
||||
if col_index < 4:
|
||||
fmt = '{:10g} '
|
||||
else:
|
||||
fmt = '{:10.8f} '
|
||||
outfile.write(fmt.format(item))
|
||||
outfile.write('\n')
|
||||
|
||||
|
||||
def source_means(otimes):
|
||||
# catch all source ids from otimes file
|
||||
src_ids = np.unique(otimes[:, 1])
|
||||
print('Source IDs:', src_ids)
|
||||
|
||||
# create dictionary containing means of travel time (differences) for each source (key)
|
||||
src_means = {src_id: np.mean(otimes[otimes[:, 1] == src_id][:, 4]) for src_id in src_ids}
|
||||
|
||||
print('Source means:')
|
||||
for srcid, val in src_means.items():
|
||||
print(srcid, ': ', val)
|
||||
|
||||
return src_means
|
||||
|
||||
|
||||
def crustal_correction_using_residual_map(fname_otimes, fname_residuals, receivers_in, fname_otimes_out):
|
||||
''' correct using residuals from numpy file with an array of shape (lons, lats, residuals)'''
|
||||
otimes = np.loadtxt(fname_otimes, skiprows=1)
|
||||
rec_dict = organize_receivers(receivers_in)
|
||||
lons, lats, res = np.load(fname_residuals, allow_pickle=True)
|
||||
|
||||
lonsU = np.unique(lons)
|
||||
latsU = np.unique(lats)
|
||||
#grid = (lonsU, latsU)
|
||||
#shape = [len(item) for item in grid]
|
||||
#res = res.reshape(shape)
|
||||
|
||||
# not very efficient but very lazy (just interpolate value for each ray, i.e. receiver, not unique,
|
||||
# which is quite redundant)
|
||||
rginter = RegularGridInterpolator((lonsU, latsU), res, bounds_error=False, fill_value=0.)
|
||||
|
||||
test_rginter(rginter)
|
||||
|
||||
for line in otimes:
|
||||
rec_id = int(line[0])
|
||||
lonlat = (rec_dict[rec_id]['lon'], rec_dict[rec_id]['lat'])
|
||||
res = rginter(lonlat)
|
||||
line[4] -= res
|
||||
|
||||
src_means = source_means(otimes)
|
||||
|
||||
for src_id, src_mean in src_means.items():
|
||||
otimes[otimes[:, 1] == src_id, 4] -= src_mean
|
||||
|
||||
with open(fname_otimes_out, 'w') as outfile:
|
||||
outfile.write('{}\n'.format(len(otimes)))
|
||||
for line in otimes:
|
||||
for col_index, item in enumerate(line):
|
||||
if col_index < 4:
|
||||
fmt = '{:10g} '
|
||||
else:
|
||||
fmt = '{:10.8f} '
|
||||
outfile.write(fmt.format(item))
|
||||
outfile.write('\n')
|
||||
|
||||
|
||||
def test_rginter(rginter):
|
||||
lons = np.linspace(0, 22, 220)
|
||||
lats = np.linspace(40, 52, 120)
|
||||
lonsg, latsg = np.meshgrid(lons, lats)
|
||||
data = rginter((lonsg, latsg))
|
||||
pcm = plt.pcolormesh(lonsg, latsg, data)
|
||||
plt.colorbar(pcm)
|
||||
plt.show()
|
||||
|
||||
|
||||
def get_otimes_diff(fname_arrivals1, fname_arrivals2, fname_out='otimes_diff.dat'):
|
||||
'''
|
||||
Calculate file containing travel time differences between fname_arrivals1 and fname_arrivals2, e.g. for crustal
|
||||
correction.
|
||||
:param fname_arrivals1:
|
||||
:param fname_arrivals2:
|
||||
:return:
|
||||
'''
|
||||
with open(fname_arrivals1, 'r') as infile:
|
||||
arrivals1 = infile.readlines()
|
||||
with open(fname_arrivals2, 'r') as infile:
|
||||
arrivals2 = infile.readlines()
|
||||
|
||||
assert len(arrivals1) == len(arrivals2), 'Length of input arrival files differs'
|
||||
|
||||
print('Calculating differences between file {} and {}'.format(fname_arrivals1, fname_arrivals2))
|
||||
columnString = '{} '
|
||||
|
||||
nArrivals = len(arrivals1)
|
||||
with open(fname_out, 'w') as outfile:
|
||||
outfile.write('{}\n'.format(nArrivals))
|
||||
for index in range(nArrivals):
|
||||
line1 = arrivals1[index]
|
||||
line2 = arrivals2[index]
|
||||
for item in line1.split()[:4]:
|
||||
outfile.write(columnString.format(item))
|
||||
diff = float(line1.split()[4]) - float(line2.split()[4])
|
||||
outfile.write(columnString.format(diff))
|
||||
outfile.write('\n')
|
||||
print('Wrote {} lines to file {}'.format(nArrivals, fname_out))
|
||||
|
||||
|
||||
def get_synthetic_obsdata_legacy(fname_m1, fname_m2, fname_otimes_orig, fname_out='otimes_modif.dat'):#, p_err=0.1):
|
||||
'''
|
||||
Create synthetic obsdata of model 1 relative to model 2 (usually 1D model synthetic travel times)
|
||||
:param fname_m1: arrivals.dat file of synthetic travel times (e.g. from checkerboard test)
|
||||
:param fname_m2: arrivals.dat file of synthetic travel times (e.g. ak135 model)
|
||||
:param fname_otimes_orig: original otimes file for pick uncertainties
|
||||
:param fname_out: otimes.dat file with output
|
||||
:param p_err: picking error
|
||||
:return:
|
||||
'''
|
||||
with open(fname_m1, 'r') as infile:
|
||||
arrivals_model1 = infile.readlines()
|
||||
with open(fname_m2, 'r') as infile:
|
||||
arrivals_model2 = infile.readlines()
|
||||
with open(fname_otimes_orig, 'r') as infile:
|
||||
otimes = infile.readlines()[1:]
|
||||
|
||||
assert(len(arrivals_model1) == len(arrivals_model2) == len(otimes)), 'missmatch in lengths of files!'
|
||||
|
||||
diffs = []
|
||||
with open(fname_out, 'w') as outfile:
|
||||
outfile.write('{}\n'.format(len(arrivals_model1)))
|
||||
|
||||
for line_model1, line_model2, line_otimes in zip(arrivals_model1, arrivals_model2, otimes):
|
||||
for item in line_model2.split()[:4]:
|
||||
outfile.write('{} '.format(item))
|
||||
|
||||
ttime_model1 = float(line_model1.split()[4])
|
||||
ttime_model2 = float(line_model2.split()[4])
|
||||
pickerror = float(line_otimes.split()[5])
|
||||
# pickerror = (p_err+2*p_err*abs(np.random.randn()))
|
||||
|
||||
ttime_diff = ttime_model1 - ttime_model2
|
||||
diffs.append(ttime_diff)
|
||||
|
||||
outfile.write('{} {}\n'.format(ttime_diff, pickerror))
|
||||
|
||||
mean_diff = np.mean(diffs)
|
||||
abs_mean_diff = np.mean(np.abs(diffs))
|
||||
|
||||
print('Mean_diff: {} - abs_mean_diff: {}'.format(mean_diff, abs_mean_diff))
|
||||
print('Done with {} travel times. Output in file: {}'.format(len(arrivals_model1), fname_out))
|
||||
|
||||
|
||||
def get_synthetic_obsdata(fname_m1, fname_m2, fname_otimes_orig, fname_out='otimes_modif.dat', sigma='original'):
|
||||
'''
|
||||
Create synthetic obsdata of model 1 relative to model 2 (usually 1D model synthetic travel times)
|
||||
:param fname_m1: arrivals.dat file of synthetic travel times (e.g. from checkerboard test)
|
||||
:param fname_m2: arrivals.dat file of synthetic travel times (e.g. ak135 model)
|
||||
:param fname_otimes_orig: original otimes file for pick uncertainties
|
||||
:param fname_out: otimes.dat file with output
|
||||
:param p_err: picking error
|
||||
:return:
|
||||
'''
|
||||
arrivals_model1 = np.genfromtxt(fname_m1)
|
||||
arrivals_model2 = np.genfromtxt(fname_m2)
|
||||
otimes = np.genfromtxt(fname_otimes_orig, skip_header=1)
|
||||
|
||||
assert(len(arrivals_model1) == len(arrivals_model2) == len(otimes)), 'missmatch in lengths of files!'
|
||||
|
||||
# get new array as copy from model1 after deleting last column (not needed for otimes)
|
||||
arrivals_diff = np.delete(arrivals_model1, 6, 1)
|
||||
|
||||
# overwrite time column by diffs
|
||||
arrivals_diff[:, 4] -= arrivals_model2[:, 4]
|
||||
# overwrite last column by pick errors
|
||||
arrivals_diff[:, 5] = otimes[:, 5]
|
||||
|
||||
# get max nSrc (in last line of sorted arrivals files)
|
||||
nSrc = int(arrivals_diff[-1, 1])
|
||||
|
||||
print('N sources:', nSrc)
|
||||
if sigma not in [None, False]:
|
||||
print('Applying gaussian noise with sigma={}'.format(sigma))
|
||||
if sigma == 'original':
|
||||
sigma_val = otimes[:, 5]
|
||||
else:
|
||||
sigma_val = sigma
|
||||
gauss_noise = np.random.normal(0., sigma_val, len(arrivals_diff[:, 4]))
|
||||
plt.axhline(0., color='grey')
|
||||
plt.vlines(np.arange(0, len(arrivals_diff)), arrivals_diff[:, 4], arrivals_diff[:, 4] + gauss_noise,
|
||||
color='grey', linestyles='dashed')
|
||||
plt.plot(arrivals_diff[:, 4], 'r.')
|
||||
arrivals_diff[:, 4] += gauss_noise
|
||||
|
||||
for index in range(nSrc):
|
||||
src_id = index + 1
|
||||
# get indices for this source ID
|
||||
indices = np.where(arrivals_diff[:, 1] == src_id)
|
||||
# get mean for that srcid
|
||||
mean = np.mean(arrivals_diff[:, 4][indices])
|
||||
print('Srcid: {}, mean: {}'.format(src_id, mean))
|
||||
# de-mean array
|
||||
arrivals_diff[:, 4][indices] -= mean
|
||||
|
||||
if sigma not in [None, False]:
|
||||
plt.axhline(0., color='grey')
|
||||
#plt.vlines(np.arange(0, len(arrivals_diff)), arrivals_diff[:, 4], arrivals_diff[:, 4] + gauss_noise)
|
||||
plt.plot(arrivals_diff[:, 4], 'b.')
|
||||
plt.figure()
|
||||
plt.hist(gauss_noise, bins=100)
|
||||
plt.show()
|
||||
|
||||
export_otimes(arrivals_diff, fname_out)
|
||||
|
||||
|
||||
def plot_histograms_for_crustal_corrections():
|
||||
import matplotlib.pyplot as plt
|
||||
ttdiffs_no_crust = []
|
||||
ttdiffs_crust = []
|
||||
ttdiffs_corrected = []
|
||||
|
||||
#get_synthetic_obsdata('it_synth_forward/arrivals.dat', 'arrivals_ak135_diehl.dat', 'otimes_three_boxes_no_crust.dat')
|
||||
#get_synthetic_obsdata('it_synth_forward_with_crust/arrivals.dat', 'arrivals_ak135_diehl.dat', 'otimes_three_boxes_with_diehl_crust.dat')
|
||||
with open('otimes_three_boxes_with_diehl_crust.dat', 'r') as infile:
|
||||
lines_crust = infile.readlines()[1:]
|
||||
with open('otimes_three_boxes_no_crust.dat', 'r') as infile:
|
||||
lines_no_crust = infile.readlines()[1:]
|
||||
with open('otimes_three_boxes_crustal_corrected.dat', 'r') as infile:
|
||||
lines_corrected = infile.readlines()[1:]
|
||||
|
||||
for line in lines_crust:
|
||||
ttdiffs_crust.append(float(line.split()[4]))
|
||||
for line in lines_no_crust:
|
||||
ttdiffs_no_crust.append(float(line.split()[4]))
|
||||
for line in lines_corrected:
|
||||
ttdiffs_corrected.append(float(line.split()[4]))
|
||||
|
||||
plt.hist(ttdiffs_crust, bins=100, label='tt-diff including crustal structure.')
|
||||
plt.hist(ttdiffs_no_crust, bins=100, label='tt-diff without crustal structure')
|
||||
plt.hist(ttdiffs_corrected, bins=100, label='tt-diff after correcting for crustal structure')
|
||||
plt.xlabel('Travel time differences to ak135_diehl 1D model [s]')
|
||||
plt.legend()
|
||||
|
||||
if __name__ == '__main__':
|
||||
#wdir = '/rscratch/minos13/marcel/fmtomo_alparray/alparray_mantle_diehl_crustal_corrections_v3_hf'
|
||||
#fname1 = os.path.join(wdir, 'arrivals_ak135di_crust.dat')
|
||||
#fname2 = os.path.join(wdir, 'arrivals_ak135di.dat')
|
||||
#get_otimes_diff(fname1, fname2, os.path.join(wdir, 'otimes_diff.dat'))
|
||||
|
||||
#get_synthetic_obsdata('arrivals_cb_N2.dat', 'arrivals_ak135.dat', 'otimes_cb_N2.dat')
|
||||
#os.chdir('/data/AlpArray_Data/fmtomo/v4/different_test_runs/alparray_mantle_waldhauser_crust_corrected_covs_hf_gradient_smoothing')
|
||||
#os.chdir('/data/AlpArray_Data/fmtomo/v4/different_test_runs/alparray_mantle_waldhauser_crust_corrected_covs_hf_gradient_smoothing')
|
||||
#crustal_correction_using_residual_map('otimes.dat', 'wh_residuals.npy', 'receivers.in', 'otimes_corr_wh.dat')
|
||||
|
||||
#os.chdir('/data/AlpArray_Data/fmtomo/v4/different_test_runs/alparray_mantle_diehl_crust_corrected_residuals_stacked_gradient_smoothing')
|
||||
#crustal_correction_using_residual_map('otimes.dat', 'diehl_2009_residuals.npy', 'receivers.in', 'otimes_corr_diehl.dat')
|
||||
|
||||
#os.chdir('/data/AlpArray_Data/fmtomo/v6/crust_corrected_VERTICAL_hf_sm_FIX_DTS_grad_sm30_dm10')
|
||||
#crustal_correction_using_residual_map('otimes.dat', 'dts_filt_12.5_12.5_7.5_CRUST_ONLY_RESIDUALS.npy', 'receivers.in', 'otimes_corr_dts.dat')
|
||||
|
||||
os.chdir('/data/AlpArray_Data/fmtomo/v6/crust_corrected_WH_hf_sm_FIX_DTS_grad_sm30_dm10')
|
||||
crustal_correction_using_residual_map('otimes.dat', 'wh_residuals_SORTED.npy', 'receivers.in', 'otimes_corr_wh.dat')
|
||||
|
||||
|
||||
|
||||
#crustal_correction_using_residual_map('otimes.dat', 'wh_residuals.npy', 'receivers.in', 'otimes_corr_wh.dat')
|
154
pylot/tomography/fmtomo_tools/modify_vgrid.py
Normal file
154
pylot/tomography/fmtomo_tools/modify_vgrid.py
Normal file
@ -0,0 +1,154 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Different functions to modify FMTOMO velocity(inversion) grid
|
||||
import argparse
|
||||
import numpy as np
|
||||
from math import erf
|
||||
|
||||
from pylot.tomography.fmtomo_tools.fmtomo_grid_utils import read_vgrid, write_vgrid, write_vtk
|
||||
|
||||
|
||||
def main(fname_in, fname_out, fname_vtk_out=None, cov_perc=0.15, bounds=[40, 60]):
|
||||
vgrid, gridN, gridDelta, gridStart = read_vgrid(fname_in)
|
||||
|
||||
print('Modifying covariances...')
|
||||
vgrid['covs'] = []
|
||||
for depth, vp in zip(vgrid['depths'], vgrid['vps']):
|
||||
vgrid['covs'].append(covariance_for_depth(depth, vp, cov_perc, bounds))
|
||||
|
||||
write_vgrid(vgrid, npts=gridN, delta=gridDelta, start=gridStart, fname=fname_out)
|
||||
if fname_vtk_out:
|
||||
write_vtk(vgrid, fname_vtk_out, write_data=['vps', 'covs'])
|
||||
|
||||
|
||||
def modify_vgrid_box(vgrid, vgrid_key, min_lon, max_lon, min_lat, max_lat, min_depth, max_depth,
|
||||
val_center, val_border, scale_factor, use_erf=True, extend_to_top=False):
|
||||
'''
|
||||
Modify a value (vgrid_key) of vgrids.in using an error function. Value will be increase smoothly to the borders
|
||||
of the external model. This function was used to prevent changes of the inversion result in the region of the
|
||||
initial crustal model that was corrected for. Value smoothly increases at the borders of the initial model.
|
||||
'''
|
||||
|
||||
# get central points of external model (here value will be minimal)
|
||||
c_lon = 0.5 * (min_lon + max_lon)
|
||||
c_lat = 0.5 * (min_lat + max_lat)
|
||||
c_depth = 0.5 * (min_depth + max_depth)
|
||||
|
||||
# get half the extent of the model dimensions for function scaling
|
||||
dlon = 0.5 * (max_lon - min_lon)
|
||||
dlat = 0.5 * (max_lat - min_lat)
|
||||
ddepth = 0.5 * (max_depth - min_depth)
|
||||
|
||||
print('Modifying {}...'.format(vgrid_key))
|
||||
print('Center {key}: {val_c}, border {key}: {val_b}'.format(key=vgrid_key, val_c=val_center, val_b=val_border))
|
||||
if not vgrid_key in vgrid.keys() or not vgrid[vgrid_key]:
|
||||
vgrid[vgrid_key] = list(np.ones(len(vgrid['depths'])))
|
||||
for index, tup in enumerate(zip(vgrid['lons'], vgrid['lats'], vgrid['depths'])):
|
||||
lon, lat, depth = tup
|
||||
if (min_lon <= lon <= max_lon and min_lat <= lat <= max_lat and depth <= max_depth):
|
||||
x = abs(lon - c_lon) / dlon
|
||||
y = abs(lat - c_lat) / dlat
|
||||
if extend_to_top and depth < c_depth:
|
||||
z = 0
|
||||
else:
|
||||
z = abs(depth - c_depth) / ddepth
|
||||
if use_erf:
|
||||
point_val = smoothing_erf(x, y, z, val_center, val_border, scale_factor=scale_factor)
|
||||
else:
|
||||
point_val = val_center
|
||||
else:
|
||||
point_val = val_border
|
||||
vgrid[vgrid_key][index] *= point_val
|
||||
|
||||
return vgrid
|
||||
|
||||
|
||||
def modify_vgrid_gradient(vgrid, vgrid_key, val_top, val_bot):
|
||||
'''
|
||||
Modify a value (vgrid_key) of vgrids.in using a linear gradient from val_top to val_bot
|
||||
'''
|
||||
|
||||
print('Modifying {} with linear gradient...'.format(vgrid_key))
|
||||
print('Top: {}, bot: {}'.format(val_top, val_bot))
|
||||
if not vgrid_key in vgrid.keys():
|
||||
vgrid[vgrid_key] = list(np.ones(len(vgrid['depths'])))
|
||||
depth_min = min(vgrid['depths'])
|
||||
depth_max = max(vgrid['depths'])
|
||||
for index, tup in enumerate(zip(vgrid['lons'], vgrid['lats'], vgrid['depths'])):
|
||||
lon, lat, depth = tup
|
||||
vgrid[vgrid_key][index] *= linear_gradient(depth, depth_min=depth_min, depth_max=depth_max, val_top=val_top,
|
||||
val_bot=val_bot)
|
||||
return vgrid
|
||||
|
||||
|
||||
def linear_gradient(depth, depth_min, depth_max, val_top, val_bot):
|
||||
return (val_bot - val_top) * depth / (depth_max - depth_min) + val_top
|
||||
|
||||
|
||||
def smoothing_erf(x, y, z, val_border, val_center, scale_factor=1.):
|
||||
a = val_center
|
||||
b = val_border
|
||||
sx = sy = sz = scale_factor
|
||||
f = (a - b) * (1. / 3. * (erf(2. * sx * (x - 1)) + erf(2. * sy * (y - 1)) + erf(2. * sz * (z - 1))) + 1.) + b
|
||||
return f
|
||||
|
||||
|
||||
def covariance_for_depth(depth, vp, cov_perc, bounds):
|
||||
'''
|
||||
Function that returns covariance values for certain depths, written to give low variance to crust, intermediate
|
||||
to an interlayer and high variance to everything else.
|
||||
:param depth: depth in kilometers
|
||||
:return: covariance
|
||||
'''
|
||||
if depth <= bounds[0]:
|
||||
return 0.05
|
||||
elif bounds[0] < depth <= bounds[1]:
|
||||
return 0.5 * cov_perc * vp
|
||||
else:
|
||||
return cov_perc * vp
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description='Prepare grid for fm3d teleseismic hybrid calculation.')
|
||||
parser.add_argument('fname_in', help='input filename (vgrids.in)')
|
||||
parser.add_argument('fname_out', help='output filename (vgrids_new.in)')
|
||||
parser.add_argument('--fname_out_vtk', default=None, help='vtk output filename')
|
||||
parser.add_argument('--cov_border', default=1.0, help='covariance for the model outside crustal model boundaries')
|
||||
parser.add_argument('--cov_center', default=0.05, help='maximum covariance in the center of the crustal model')
|
||||
parser.add_argument('--smooth_border', default=1., help='smoothing factor for the rest of the box')
|
||||
parser.add_argument('--smooth_center', default=0.5, help='smoothing factor inside SWATH-D box')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
vgrid, gridN, gridDelta, gridStart = read_vgrid(args.fname_in)
|
||||
|
||||
#main(args.fname_in, args.fname_out, args.fname_out_vtk)
|
||||
|
||||
#vgrid = modify_vgrid_gradient(vgrid, 'smoothfactors', 1.0, 2.0)
|
||||
|
||||
# bounds for waldhauser residual corrections
|
||||
wh_bounds = dict(lon0 = 3.23, lon1 = 13.96, lat0 = 43.18, lat1 = 49.45)
|
||||
vgrid = modify_vgrid_box(vgrid, vgrid_key='covs', min_lon=wh_bounds['lon0'], max_lon=wh_bounds['lon1'],
|
||||
min_lat=wh_bounds['lat0'], max_lat=wh_bounds['lat1'], min_depth=-5.0, max_depth=60.0,
|
||||
val_center=float(args.cov_center), val_border=float(args.cov_border),
|
||||
scale_factor=3, use_erf=True, extend_to_top=True)
|
||||
|
||||
#vgrid = modify_vgrid_box(vgrid, vgrid_key='covs', min_lon=2.2547, max_lon=17.0999, min_lat=41.0517,
|
||||
# max_lat=50.4984, min_depth=-5.0, max_depth=70.0, val_center=float(args.cov_center),
|
||||
# val_border=float(args.cov_border), scale_factor=3, use_erf=True, extend_to_top=True)
|
||||
|
||||
#vgrid = modify_vgrid_box(vgrid, vgrid_key='smoothfactors', min_lon=8.9, max_lon=15.3, min_lat=45.,
|
||||
# max_lat=48., min_depth=-15.0, max_depth=610.0, val_center=float(args.smooth_center),
|
||||
# val_border=float(args.smooth_border), scale_factor=1., use_erf=True)
|
||||
|
||||
# MP MP TEST TEST TEST ++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
#grid = modify_vgrid_box(grid, vgrid_key='smoothfactors', min_lon=11., max_lon=35., min_lat=30.,
|
||||
# max_lat=61., min_depth=-5.0, max_depth=600.0, val_center=float(args.smooth_center),
|
||||
# val_border=float(args.smooth_border), scale_factor=.4)
|
||||
# MP MP TEST TEST TEST ------------------------------------------
|
||||
|
||||
write_vgrid(vgrid, npts=gridN, delta=gridDelta, start=gridStart, fname=args.fname_out)
|
||||
if args.fname_out_vtk:
|
||||
write_vtk(vgrid, args.fname_out_vtk, write_data=['vps', 'covs', 'smoothfactors'])
|
||||
|
||||
|
26
pylot/tomography/fmtomo_tools/plot_obsdata.py
Executable file
26
pylot/tomography/fmtomo_tools/plot_obsdata.py
Executable file
@ -0,0 +1,26 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import argparse
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
def plot_otimes(otimes_fname):
|
||||
with open(otimes_fname, 'r') as infile:
|
||||
input_list = infile.readlines()[1:]
|
||||
ray_id = [int(line.split()[0]) for line in input_list]
|
||||
ttimes = [float(line.split()[-2]) for line in input_list]
|
||||
uncertainties = [float(line.split()[-1]) for line in input_list]
|
||||
plt.axhline(0, linestyle=':', color='k')
|
||||
plt.errorbar(ray_id, ttimes, yerr=uncertainties, fmt='o', markersize=1, ecolor='0.5', elinewidth=0.5)
|
||||
plt.show()
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description='Plot ttimes with errors of FMTOMO file otimes.dat')
|
||||
parser.add_argument('infile', help='inputfile')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
plot_otimes(args.infile)
|
341
pylot/tomography/fmtomo_tools/plot_residuals_map.py
Executable file
341
pylot/tomography/fmtomo_tools/plot_residuals_map.py
Executable file
@ -0,0 +1,341 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import os
|
||||
import argparse
|
||||
import json
|
||||
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
from scipy.interpolate import griddata
|
||||
|
||||
from obspy.geodetics.base import gps2dist_azimuth
|
||||
|
||||
import cartopy.crs as ccrs
|
||||
|
||||
from pylot.tomography.fmtomo_tools.compare_arrivals_hybrid import organize_receivers, organize_sources, organize_event_names
|
||||
from pylot.tomography.map_utils import make_map
|
||||
from pylot.tomography.utils import normed_figure
|
||||
|
||||
|
||||
def plot_map(otimes_fname, sources_fname, receivers_fname, isf, rtimes_fname=None, arrivals_fname=None, colorbar=True,
|
||||
title=True, stack=False, source_ids=(), savefig_dir='', demean=True, absolute=False, clat=42.25, clon=17.5,#clat=46., clon=11.,
|
||||
file_ext='png', max_abs=3., relative_to_otimes=False, only_otimes=False):
|
||||
if savefig_dir:
|
||||
if not os.path.isdir(savefig_dir):
|
||||
os.mkdir(savefig_dir)
|
||||
|
||||
src_dict = organize_sources(sources_fname)
|
||||
rec_dict = organize_receivers(receivers_fname)
|
||||
eventnames = organize_event_names(isf)
|
||||
|
||||
residuals_dict = read_fmtomo_tt_file(otimes_fname, rec_dict, rtimes_fname=rtimes_fname, synth_fname=arrivals_fname,
|
||||
demean=demean, absolute=absolute, height_correction_vp=5.5,
|
||||
relative_to_otimes=relative_to_otimes, only_otimes=only_otimes)
|
||||
|
||||
if stack:
|
||||
residuals_dict = stack_sources(residuals_dict)
|
||||
with open('stacked_residuals.json', 'w') as outfile:
|
||||
json.dump(residuals_dict, outfile)
|
||||
|
||||
count = 0
|
||||
if savefig_dir:
|
||||
fig = normed_figure(width_cm=12, ratio=12./9.)
|
||||
#fig = plt.figure(figsize=(12, 9))
|
||||
else:
|
||||
fig = plt.figure()
|
||||
if stack or source_ids is not [] or savefig_dir:
|
||||
count = 1
|
||||
|
||||
# increase point size if not stacked
|
||||
sizefactor_stacked = {True: 30,
|
||||
False: .2e4}
|
||||
|
||||
for src_id, dic in residuals_dict.items():
|
||||
eventname = eventnames[src_id - 1]
|
||||
if source_ids != []:
|
||||
if not src_id in source_ids:
|
||||
continue
|
||||
if max_abs == 'auto':
|
||||
max_abs = np.max(np.abs(np.array(dic['ttimes'])))
|
||||
# if not stack and not savefig_dir and source_ids == []:
|
||||
# count += 1
|
||||
# if count == 1:
|
||||
# ax = fig.add_subplot(3, 3, count)
|
||||
# ax0 = ax
|
||||
# else:
|
||||
# ax = fig.add_subplot(3, 3, count, sharex=ax0, sharey=ax0)
|
||||
#
|
||||
# ax.text(0.1, 0.9, 'Source ID {}, {}'.format(src_id, src_dict[src_id]['phase']), transform=ax.transAxes)
|
||||
# else:
|
||||
# ax = fig.add_subplot(111)
|
||||
if stack and title:
|
||||
plt.title('Stacked {} sources. Size relates to number of stacks per receiver.'.format(len(src_dict)),
|
||||
y=1.05)
|
||||
ax = make_map(draw_model=False, draw_faults=True, model_legends=False, clon=clon, clat=clat,
|
||||
width = 30, height = 21,) # , width=6e6, height=5e6)
|
||||
if not stack:
|
||||
slat, slon = src_dict[src_id]['lat'], src_dict[src_id]['lon']
|
||||
dist = plot_source(slat, slon)
|
||||
baz = gps2dist_azimuth(clat, clon, slat, slon, a=6.371e6, f=0)[1]
|
||||
if title:
|
||||
plt.title(
|
||||
'Plot of source {}. Distance: {:.0f}$^\circ$. BAZ: {:.0f}$^\circ$'.format(eventname, dist, baz),
|
||||
y=1.05)
|
||||
|
||||
#ax.set_xlabel('Longitude [$^\circ$]')
|
||||
#ax.set_ylabel('Latitude [$^\circ$]')
|
||||
|
||||
# prepare grids for contour plot
|
||||
#lonaxis = np.linspace(min(dic['lons']), max(dic['lons']), 250)
|
||||
#lataxis = np.linspace(min(dic['lats']), max(dic['lats']), 250)
|
||||
#longrid, latgrid = np.meshgrid(lonaxis, lataxis)
|
||||
#ttimes_grid = griddata((dic['lats'], dic['lons']), dic['ttimes'], (latgrid, longrid), method='linear')
|
||||
#levels = np.linspace(min(dic['ttimes']), max(dic['ttimes']), 75)
|
||||
|
||||
cmap = plt.get_cmap('seismic') if not absolute else plt.get_cmap('viridis')
|
||||
|
||||
vmin = -max_abs if not absolute else None
|
||||
vmax = max_abs if not absolute else None
|
||||
sc = ax.scatter(dic['lons'], dic['lats'], c=np.array(dic['ttimes']), cmap=cmap,
|
||||
vmin=vmin, vmax=vmax, edgecolors='grey', linewidths=0.2,
|
||||
s=sizefactor_stacked[stack]*np.array(dic['nttimes'])/len(src_dict), zorder=10,
|
||||
transform=ccrs.PlateCarree(), alpha=0.75)
|
||||
sc.set_edgecolor('0.')
|
||||
if colorbar:
|
||||
cb = plt.colorbar(sc, label='ttime [s]', shrink=0.5)
|
||||
|
||||
if stack:
|
||||
savefig_path = os.path.join(savefig_dir, 'stacked_events.{}'.format(file_ext))
|
||||
else:
|
||||
#fname = f'baz{baz:03.0f}_dist{dist:03.0f}_{eventname}_srcID{src_id}.{file_ext}'
|
||||
fname = f'{eventname}_srcID{src_id}.{file_ext}'
|
||||
savefig_path = os.path.join(savefig_dir, fname)
|
||||
|
||||
|
||||
if not savefig_dir and not count % 9:
|
||||
plt.show()
|
||||
fig = plt.figure()#figsize=(16, 9), dpi=300.)
|
||||
count = 0
|
||||
if savefig_dir:
|
||||
ax.figure.savefig(savefig_path, dpi=300., bbox_inches='tight', pad_inches=0.)
|
||||
print('Wrote file {}'.format(savefig_path))
|
||||
plt.clf()
|
||||
|
||||
if not savefig_dir:
|
||||
plt.show()
|
||||
|
||||
|
||||
def plot_source(lat, lon, clat=46., clon=11.):
|
||||
#basemap.drawgreatcircle(lon, lat, clon, clat, color='k', zorder=15, linestyle='dashed')
|
||||
dist, azim, bazim = gps2dist_azimuth(lat, lon, clat, clon, a=6.371e6, f=0)
|
||||
|
||||
dist_deg = dist/1000./np.pi/2./6371.*360.
|
||||
return dist_deg
|
||||
|
||||
#x = np.cos(np.deg2rad(azim))
|
||||
#y = np.sin(np.deg2rad(azim))
|
||||
#print(x, y)
|
||||
#ax.plot([0, x], [0, y], 'r', zorder=15)
|
||||
|
||||
|
||||
def read_fmtomo_tt_file(otimes_fname, rec_dict, synth_fname=None, rtimes_fname=None, demean=True, absolute=False,
|
||||
height_correction_vp=None, R=6371., relative_to_otimes=True, only_otimes=False):
|
||||
#with open(otimes_fname, 'r') as infile:
|
||||
# # skip first line for otimes.dat. Frist line contains N_rays
|
||||
# obsarray = infile.readlines()[1:]
|
||||
obsarray = np.loadtxt(otimes_fname, skiprows=1)
|
||||
|
||||
if height_correction_vp:
|
||||
if absolute:
|
||||
print('APPLICATION OF HEIGHT CORRECTION FOR ABS NOT IMPLEMENTED YET. Not needed!?')
|
||||
#print('Applying height correction of {} km/s'.format(height_correction_vp))
|
||||
else:
|
||||
height_correction_vp = None
|
||||
print('Will not apply height correction for relative values.')
|
||||
|
||||
if synth_fname:
|
||||
synth_array = np.genfromtxt(synth_fname)
|
||||
|
||||
if rtimes_fname:
|
||||
ref_array = np.genfromtxt(rtimes_fname)
|
||||
|
||||
if synth_fname and rtimes_fname:
|
||||
#with open(synth_fname, 'r') as infile:
|
||||
# synth_array = infile.readlines()
|
||||
#with open(rtimes_fname, 'r') as infile:
|
||||
# ref_array = infile.readlines()
|
||||
|
||||
#mean_obs = np.mean(obsarray[:, 4])
|
||||
#mean_synth = np.mean(synth_array[:, 4])
|
||||
#mean_ref = np.mean(ref_array[:, 4])
|
||||
|
||||
#mean_rel_synth = mean_synth - mean_ref
|
||||
#print('Means:\nobserved: {}, relative {}'.format(mean_obs, mean_rel_synth))
|
||||
synth_src_means = {}
|
||||
|
||||
nsrc = obsarray[-1, 1]
|
||||
for index in range(int(nsrc)):
|
||||
srcid = index + 1
|
||||
indices = np.where(synth_array[:, 1].astype(int) == srcid)
|
||||
synth_src_means[srcid] = np.mean(synth_array[indices, 4] - ref_array[indices, 4])
|
||||
|
||||
print('Average mean for sources: {} s'.format(np.mean(list(synth_src_means.values()))))
|
||||
|
||||
if demean:
|
||||
print('Removing mean from synthetic times...')
|
||||
else:
|
||||
print('Will not mean-correct travel times')
|
||||
# mean_rel_synth = 0.
|
||||
|
||||
residuals_dict = {}
|
||||
|
||||
for index, line in enumerate(obsarray):
|
||||
rec_id = int(line[0])
|
||||
src_id = int(line[1])
|
||||
ttime = line[4] if relative_to_otimes or only_otimes else 0
|
||||
|
||||
# get residuals from reference file if fname is given
|
||||
if synth_fname:
|
||||
# get synthetic time
|
||||
ttime_syn = synth_array[index][4]
|
||||
if rtimes_fname:
|
||||
# get reference time (1d travel time)
|
||||
ttime_ref = ref_array[index][4]
|
||||
if synth_fname and rtimes_fname:
|
||||
# calculate synthetic time relative to 1d travel time
|
||||
ttime_rel_syn = ttime_syn - ttime_ref
|
||||
#print(ttime_ref, ttime_syn, ttime_rel_syn, ttime, ttime-ttime_rel_syn)
|
||||
# calculate difference between relative observed and model times, multiply with -1 to get tsynth - tobs
|
||||
mean_rel_src = synth_src_means[srcid] if demean else 0.
|
||||
if not only_otimes:
|
||||
ttime = -1 * (ttime_rel_syn - ttime - mean_rel_src)
|
||||
if absolute:
|
||||
ttime = ttime_syn
|
||||
try:
|
||||
uncertainty = line[5]
|
||||
except:
|
||||
uncertainty = 0
|
||||
|
||||
# create dictionary for source if not exists
|
||||
if not src_id in residuals_dict.keys():
|
||||
residuals_dict[src_id] = {'lats': [],
|
||||
'lons': [],
|
||||
'rads': [],
|
||||
'ttimes':[],
|
||||
'nttimes': [],
|
||||
'uncerts': []}
|
||||
# chose correct dictionary
|
||||
dic = residuals_dict[src_id]
|
||||
# get coordinates from organized receivers dictionary
|
||||
dic['lats'].append(rec_dict[rec_id]['lat'])
|
||||
dic['lons'].append(rec_dict[rec_id]['lon'])
|
||||
dic['rads'].append(rec_dict[rec_id]['rad'])
|
||||
# rad is depth?
|
||||
#elev = - rec_dict[rec_id]['rad']
|
||||
#height_correction_vp = 0
|
||||
#station_height_corr = elev / (height_correction_vp) if height_correction_vp else 0.
|
||||
#if elev < 0: print(elev, station_height_corr)
|
||||
# MP MP NO HEIGHT CORRECTION FOR FMTOMO! REFTIMES CONTAIN STATION HIGHT!!!!!
|
||||
dic['ttimes'].append(ttime) # - station_height_corr)
|
||||
# number of ttimes will be set to 1 (determines size in scatter, only relevant for stacked ttimes)
|
||||
dic['nttimes'].append(1)
|
||||
dic['uncerts'].append(uncertainty)
|
||||
|
||||
return residuals_dict
|
||||
|
||||
|
||||
def stack_sources(residuals_dict):
|
||||
all_ttimes = {}
|
||||
all_uncs = {}
|
||||
for src_id, dic in residuals_dict.items():
|
||||
for index in range(len(dic['lats'])):
|
||||
lat = dic['lats'][index]
|
||||
lon = dic['lons'][index]
|
||||
rad = dic['rads'][index]
|
||||
source_tuple = (lat, lon, rad)
|
||||
if not source_tuple in all_ttimes.keys():
|
||||
all_ttimes[source_tuple] = []
|
||||
all_uncs[source_tuple] = []
|
||||
all_ttimes[source_tuple].append(dic['ttimes'][index])
|
||||
all_uncs[source_tuple].append(dic['uncerts'][index])
|
||||
# create new dictionary in the shape of residuals dict with only one source
|
||||
stacked_dict = {}
|
||||
stacked_dict[1] = {'lats': [],
|
||||
'lons': [],
|
||||
'rads': [],
|
||||
'ttimes':[],
|
||||
'nttimes': [],
|
||||
'misfits': []}
|
||||
|
||||
|
||||
for source_tuple in all_ttimes.keys():
|
||||
ttimes_list = all_ttimes[source_tuple]
|
||||
uncs_list = all_uncs[source_tuple]
|
||||
misfit = np.sum((np.array(ttimes_list) / np.array(uncs_list))**2) / len(ttimes_list)
|
||||
lat, lon, rad = source_tuple
|
||||
stacked_dict[1]['lats'].append(lat)
|
||||
stacked_dict[1]['lons'].append(lon)
|
||||
stacked_dict[1]['rads'].append(rad)
|
||||
stacked_dict[1]['ttimes'].append(np.sum(np.mean(ttimes_list)))
|
||||
stacked_dict[1]['nttimes'].append(len(ttimes_list))
|
||||
stacked_dict[1]['misfits'].append(misfit)
|
||||
#stacked_dict[1]['ttimes_list'].append(ttimes_list)
|
||||
|
||||
ttimes = [d['ttimes'] for d in stacked_dict.values()]
|
||||
plt.hist(ttimes, 100)
|
||||
plt.axvline(np.mean(ttimes), c='r')
|
||||
plt.xlabel('ttime [s]')
|
||||
|
||||
return stacked_dict
|
||||
|
||||
|
||||
def compare_residuals():
|
||||
wdir = '/rscratch/minos13/marcel/fmtomo_alparray/'
|
||||
os.chdir(wdir)
|
||||
with open('alparray_mantle_diehl_crust_included_v3_hf/stacked_residuals.json', 'r') as infile:
|
||||
sr = json.load(infile)
|
||||
with open('alparray_mantle_diehl_crust_included_v3_no_density_hf/stacked_residuals.json', 'r') as infile:
|
||||
srnd = json.load(infile)
|
||||
mfdiff = np.array(sr['1']['misfits']) - np.array(srnd['1']['misfits'])
|
||||
np.mean(mfdiff)
|
||||
sc = plt.scatter(sr['1']['lons'], sr['1']['lats'], c=mfdiff, cmap='seismic', vmin=-7.5, vmax=7.5)
|
||||
cb = plt.colorbar(sc)
|
||||
title = 'Difference in station misfit after 12 iterations (density kernel - no density).' \
|
||||
' Blue: decrease in residuals using density. Mean: {:.4f}'
|
||||
plt.title(title.format(np.mean(mfdiff)))
|
||||
plt.xlabel('Latitude (deg)')
|
||||
plt.ylabel('Longitude (deg)')
|
||||
cb.set_label('Misfit')
|
||||
sc.set_linewidth(0.2)
|
||||
sc.set_edgecolor('k')
|
||||
plt.show()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description='Plot residuals of fm3d output file (arrivals.dat format) on map.')
|
||||
parser.add_argument('--otimes', default='otimes.dat', help='otimes.dat file (default: otimes.dat)')
|
||||
parser.add_argument('--sources', default='sources.in', help='sources.in file (default: sources.in)')
|
||||
parser.add_argument('--receivers', default='receivers.in', help='receivers.in file (default receivers.in)')
|
||||
parser.add_argument('--sourcefile', default='input_source_file.in', help='input_source_file (default: input_source_file.in)')
|
||||
parser.add_argument('--arrivals', default=None, help='arrivals_file')
|
||||
parser.add_argument('--rtimes', default=None, help='reference_file')
|
||||
parser.add_argument('--fext', default='png', help='file extension for images (if -w)')
|
||||
parser.add_argument('-s', '--stack', action='store_true', dest='stack', help='stack picks')
|
||||
parser.add_argument('-w', '--write', action='store_true', default=False, help='write figures to disk')
|
||||
parser.add_argument('-a', '--abs', action='store_true', dest='abs', default=False, help='compute absolute values')
|
||||
parser.add_argument('-nd', '--no_demean', action='store_true', default=False, help='do not demean travel times')
|
||||
parser.add_argument('-nc', '--no_colorbar', action='store_true', default=False, help='do not plot colorbar')
|
||||
parser.add_argument('-nt', '--no_title', action='store_true', default=False, help='do not plot title')
|
||||
parser.add_argument('-no', '--no_otimes', action='store_true', default=False, help='do not plot relative to otimes')
|
||||
parser.add_argument('-oo', '--only_otimes', action='store_true', default=False, help='only plot observed times (otimes)')
|
||||
parser.add_argument('-i', '--ids', dest='source_ids', type=int, default=[], nargs='*',
|
||||
help='Plot only one source with FMTOMO internal id(s).')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
savefig_dir = 'residual_maps_out' if args.write else ''
|
||||
plot_map(args.otimes, args.sources, args.receivers, args.sourcefile, arrivals_fname=args.arrivals,
|
||||
rtimes_fname=args.rtimes, stack=args.stack, source_ids=args.source_ids, colorbar=not args.no_colorbar,
|
||||
title=not args.no_title, savefig_dir=savefig_dir, demean=not args.no_demean, absolute=args.abs,
|
||||
file_ext=args.fext, relative_to_otimes=not args.no_otimes, only_otimes= args.only_otimes)
|
270
pylot/tomography/fmtomo_tools/quantify_ray_crossing.py
Normal file
270
pylot/tomography/fmtomo_tools/quantify_ray_crossing.py
Normal file
@ -0,0 +1,270 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
'''
|
||||
Estimate ray crossing at each depth using rays (as npy objects) precalculated from FMTOMO file rays.dat
|
||||
(e.g. script: plot_rays_on_plane)
|
||||
'''
|
||||
import os
|
||||
import glob
|
||||
import json
|
||||
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
from scipy.interpolate import RegularGridInterpolator
|
||||
|
||||
from pylot.tomography.fmtomo_tools.fmtomo_grid_utils import read_vgrid_regular, read_vgrid
|
||||
from pylot.tomography.map_utils import angle_marker, make_map
|
||||
|
||||
pjoin = os.path.join
|
||||
|
||||
|
||||
def main(fdir, it=1, station_coords_file='/data/AdriaArray_Data/various/station_coords.json'):
|
||||
fnin_vgrid = pjoin(fdir, f'it_{it}/vgrids.in')
|
||||
fnout_vtk = pjoin(fdir, f'vgrids_it{it}_w_ray_crossings.vtk')
|
||||
fnout_npy = pjoin(fdir, f'vgrids_hf_it{it}' + '_crossing_bins_{}-{}_mincount_{}_STEP_{}.npy')
|
||||
|
||||
# binsize (degrees), min_count for each direction to raise quality by 1 increment
|
||||
binsize_az = 90.
|
||||
binsize_incl = 60.
|
||||
min_count = 5
|
||||
plot = False
|
||||
write = True
|
||||
draw_hists = True
|
||||
draw_simple_hists = True
|
||||
step = 2 # take nth sample of vgrid for calculating cells
|
||||
|
||||
# for stations plotting
|
||||
with open(station_coords_file, 'r') as infile:
|
||||
stations = json.load(infile)
|
||||
lonsStations, latsStations = zip(*[(sta['longitude'], sta['latitude']) for sta in stations.values()])
|
||||
|
||||
# some constants etc.
|
||||
R = 6371.
|
||||
bins_azim = np.arange(0, 360 + binsize_az, binsize_az)
|
||||
bins_incl = np.arange(0., 60. + binsize_incl, binsize_incl)
|
||||
nbins_azim = len(bins_azim) - 1
|
||||
nbins_incl = len(bins_incl) - 1
|
||||
|
||||
# for plotting:
|
||||
bins = bins_azim
|
||||
binsize = binsize_az
|
||||
nbins = nbins_azim
|
||||
|
||||
# get vgrid and estimate dlat/dlon
|
||||
vgrid_reg = read_vgrid_regular(fnin_vgrid)
|
||||
lons, lats, rads = [array[::step] for array in vgrid_reg[0]]
|
||||
vps, covs, sms, pdvs = [array[::step, ::step, ::step] for array in vgrid_reg[1:]]
|
||||
|
||||
dlat = lats[1] - lats[0]
|
||||
dlon = lons[1] - lons[0]
|
||||
|
||||
# grid = init_dict()
|
||||
# grid['vps'] = list(vps.ravel())
|
||||
# grid['covs'] = list(covs.ravel())
|
||||
# grid['sms'] = list(sms.ravel())
|
||||
# grid['pdvs'] = list(pdvs.ravel())
|
||||
# grid['res'] = []
|
||||
#
|
||||
# for rad in rads:
|
||||
# for lat in lats:
|
||||
# for lon in lons:
|
||||
# depth = R - rad
|
||||
# grid['lons'].append(lon)
|
||||
# grid['lats'].append(lat)
|
||||
# grid['depths'].append(depth)
|
||||
# x, y, z = pol2cart(lat, lon, R - depth)
|
||||
# grid['xs'].append(x)
|
||||
# grid['ys'].append(y)
|
||||
# grid['zs'].append(z)
|
||||
|
||||
# just for plotting at certain depths (not for write!!!)
|
||||
depths = np.arange(0., 800., 100)
|
||||
if not write:
|
||||
rads = R - depths
|
||||
|
||||
resolutions = []
|
||||
lons_export = []
|
||||
lats_export = []
|
||||
rads_export = []
|
||||
|
||||
print(rads)
|
||||
for rad in rads:
|
||||
# prepare dict containing horizontal angle for key tuple (ilat, ilon)
|
||||
vgrid_azim_ids = {}
|
||||
vgrid_incl_ids = {}
|
||||
|
||||
print('Working on radius', rad)
|
||||
# iterate over rays
|
||||
for fnin_rays in glob.glob1(fpath_events, '*.npz'):
|
||||
rays = np.load(os.path.join(fpath_events, fnin_rays), allow_pickle=True)
|
||||
# raypoints = np.zeros((len(rays), 3))
|
||||
#for index in range(len(rays)):
|
||||
for ray in rays.values():
|
||||
#ray = rays[index]
|
||||
# get index of closest depth here from ray (TAKE CARE OF ALIASING! Using nearest value)
|
||||
ind_min = np.abs(ray[:, 0] - rad).argmin()
|
||||
# in case ind_min is at upper boundary (surface)
|
||||
if ind_min == len(ray[:, 0]) - 1:
|
||||
ind_min -= 1
|
||||
# get diffs to following ray index (km, deg, deg)!
|
||||
ray_diff = np.diff(ray[ind_min:ind_min + 2], axis=0)[0]
|
||||
lat = ray[ind_min, 1]
|
||||
lat_diff_km = ray_diff[1] * (np.pi * R) / 180.
|
||||
lon_diff_km = ray_diff[2] * (np.pi * np.cos(np.deg2rad(lat)) * R) / 180.
|
||||
r_diff_km = ray_diff[0]
|
||||
dlateral = np.sqrt(lat_diff_km ** 2 + lon_diff_km ** 2)
|
||||
# calculate horizontal angle
|
||||
azim = np.rad2deg(np.arctan2(lon_diff_km, lat_diff_km))
|
||||
incl = np.rad2deg(np.arctan(dlateral / r_diff_km))
|
||||
# correct angle from -180-180 to 0-360 and also change azim to bazim
|
||||
bazim = azim + 180.
|
||||
# angles[index] = angle
|
||||
# raypoints[index] = ray[ind_min]
|
||||
lat, lon = ray[ind_min, 1:]
|
||||
lati = np.where((lats <= lat + dlat / 2.) & (lats > lat - dlat / 2.))[0][0]
|
||||
loni = np.where((lons <= lon + dlon / 2.) & (lons > lon - dlon / 2.))[0][0]
|
||||
key = (lati, loni)
|
||||
if not key in vgrid_azim_ids.keys():
|
||||
vgrid_azim_ids[key] = []
|
||||
vgrid_incl_ids[key] = []
|
||||
vgrid_azim_ids[key].append(bazim)
|
||||
vgrid_incl_ids[key].append(incl)
|
||||
# vgrid_ids[index] = np.array([lati, loni])
|
||||
# plt.scatter(lons[loni], lats[lati], c='r', marker='o', facecolor='none', alpha=0.5)
|
||||
# sc = plt.scatter(raypoints[:,2], raypoints[:,1], c=angles[:])
|
||||
|
||||
vgrid_angles_quality = {}
|
||||
vgrid_azim_hist = {}
|
||||
|
||||
for inds in vgrid_azim_ids.keys():
|
||||
azims = vgrid_azim_ids[inds]
|
||||
incls = vgrid_incl_ids[inds]
|
||||
lati, loni = inds
|
||||
hist_az, _ = np.histogram(azims, bins=bins_azim)
|
||||
hist_incl, _ = np.histogram(incls, bins=bins_incl)
|
||||
hist2d, _, _ = np.histogram2d(azims, incls, bins=[bins_azim, bins_incl])
|
||||
# hist_az, hist_inc = hist2d
|
||||
hist = hist2d.ravel()
|
||||
quality = len(hist[hist > min_count - 1]) / (nbins_azim * nbins_incl)
|
||||
# quality = len(hist_az[hist_az > min_count - 1]) / nbins_azim
|
||||
# quality = len(hist_incl[hist_incl > min_count - 1]) / nbins_incl
|
||||
vgrid_angles_quality[inds] = quality
|
||||
vgrid_azim_hist[inds] = hist_az
|
||||
|
||||
# LATS = []
|
||||
# LONS = []
|
||||
qualities = []
|
||||
hists = []
|
||||
|
||||
for ilat, lat in enumerate(lats):
|
||||
for ilon, lon in enumerate(lons):
|
||||
# LATS.append(lat)
|
||||
# LONS.append(lon)
|
||||
key = (ilat, ilon)
|
||||
quality = vgrid_angles_quality.get(key)
|
||||
hist = vgrid_azim_hist.get(key)
|
||||
# print(key, quality)
|
||||
if not quality:
|
||||
quality = 0.
|
||||
# hist for plotting only!
|
||||
if hist is None:
|
||||
hist = np.zeros(nbins)
|
||||
qualities.append(quality)
|
||||
hists.append(hist)
|
||||
lons_export.append(lon)
|
||||
lats_export.append(lat)
|
||||
rads_export.append(rad)
|
||||
|
||||
resolutions += qualities
|
||||
|
||||
if plot:
|
||||
# TODO: still old basemap code
|
||||
raise NotImplementedError('Still using old basemap code')
|
||||
fig = plt.figure()
|
||||
ax = fig.add_subplot(111)
|
||||
bmap = make_map(ax, resolution='l')
|
||||
|
||||
LONS, LATS = np.meshgrid(lons, lats)
|
||||
|
||||
sc = bmap.pcolormesh(LONS - dlon / 2., LATS - dlat / 2.,
|
||||
np.array(qualities).reshape(LATS.shape), latlon=True, zorder=1.5,
|
||||
shading='nearest') # , s=np.array(qualities)*100)
|
||||
|
||||
# sc = plt.contourf(LONS, LATS, np.array(qualities).reshape(LATS.shape), levels=21)
|
||||
|
||||
if draw_hists:
|
||||
for index, bin in enumerate(bins[:-1]):
|
||||
marker = angle_marker(bin, bin + binsize)
|
||||
s = np.array(hists)[:, index]
|
||||
if draw_simple_hists:
|
||||
s[s < min_count] = 0
|
||||
s[s >= min_count] = 150
|
||||
else:
|
||||
s *= 10.
|
||||
sc_angle = bmap.scatter(LONS, LATS, s=s, marker=marker, edgecolors='0.75', alpha=1., linewidths=0.6,
|
||||
latlon=True, zorder=1.5, )
|
||||
sc_angle.set_facecolor('none')
|
||||
# sc_angle = plt.scatter(LONS, LATS, s=1, marker='.', edgecolors='1.', alpha=1., linewidths=1.)
|
||||
|
||||
bmap.scatter(lonsStations, latsStations, c='k', s=0.5, latlon=True, zorder=1.5, ) # , alpha=0.5)
|
||||
plt.title('Azimuthal coverage at depth of {}km, {} bins'.format((R - rad), nbins_azim * nbins_incl))
|
||||
# plt.xlim([0, 22])
|
||||
# plt.ylim([40, 53])
|
||||
# plt.gca().set_aspect('equal')
|
||||
cbar = plt.colorbar(sc)
|
||||
cbar.set_label('Azimuthal coverage')
|
||||
plt.show()
|
||||
|
||||
if write:
|
||||
rginter_res = RegularGridInterpolator((rads, lats, lons),
|
||||
np.array(resolutions).reshape((len(rads), len(lats), len(lons))),
|
||||
bounds_error=False, fill_value=0.)
|
||||
grid = read_vgrid(fnin_vgrid)[0]
|
||||
grid['res'] = []
|
||||
for lon, lat, depth in zip(grid['lons'], grid['lats'], grid['depths']):
|
||||
grid['res'].append(rginter_res((R - depth, lat, lon)))
|
||||
a = np.array([lons_export, lats_export, rads_export, resolutions])
|
||||
np.save(fnout_npy.format(nbins_azim, nbins_incl, min_count, step), a)
|
||||
write_vtk(grid, fnout_vtk,
|
||||
write_data=['vps', 'res', 'covs', 'frechs']) # , clon=11., clat=46., dlon=12., dlat=6., sort=True)
|
||||
|
||||
|
||||
def rays_to_npy(infile, fnout_npy, n_points=10):
|
||||
rays = {}
|
||||
i = 0
|
||||
src_id_old = 1
|
||||
while True:
|
||||
i += 1
|
||||
l1 = infile.readline()
|
||||
if l1 == '': break
|
||||
rec_id, src_id, ray_id = [int(item) for item in l1.split()[:3]]
|
||||
if not src_id in rays.keys():
|
||||
rays[src_id] = []
|
||||
l2 = infile.readline()
|
||||
n = int(l2.split()[0])
|
||||
ray = np.zeros((n, 3))
|
||||
for index in range(n):
|
||||
r, lat, lon = [float(item) for item in infile.readline().split()]
|
||||
ray[index] = np.array([r, np.rad2deg(lat), np.rad2deg(lon)])
|
||||
rays[src_id].append(ray[::n_points])
|
||||
|
||||
dirname = os.path.split(fnout_npy)[0]
|
||||
if not os.path.isdir(dirname):
|
||||
os.mkdir(dirname)
|
||||
|
||||
for src_id, ray in rays.items():
|
||||
np.savez(fnout_npy.format(src_id), *ray)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
fmtomodir = '/data/AdriaArray_Data/fmtomo_adriaarray/alpadege/crust_incl_TESAURO_sm30_dm1/'
|
||||
it = 12
|
||||
|
||||
fpath_events = pjoin(fmtomodir, f'rays_npy_it{it}/')
|
||||
infile = open(pjoin(fmtomodir, f'it_{it}/rays.dat'), 'r')
|
||||
fnout_npy = pjoin(fpath_events, f'it_{it}_rays_event_' + '{}.npz')
|
||||
|
||||
rays_to_npy(infile, fnout_npy)
|
||||
|
||||
main(fmtomodir, it=it)
|
43
pylot/tomography/fmtomo_tools/residual_histograms.py
Normal file
43
pylot/tomography/fmtomo_tools/residual_histograms.py
Normal file
@ -0,0 +1,43 @@
|
||||
import os
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
fpaths = ['/data/AlpArray_Data/fmtomo/v6/fwi_model_wolfgang_test',
|
||||
'/data/AlpArray_Data/fmtomo/v6/final']
|
||||
arr_path_ref = 'rtimes_tele.dat'
|
||||
|
||||
iterations = [0, 1, 2, 3, 4]
|
||||
labels = ['rel_1d'] + [f'rel_it{it}' for it in iterations]
|
||||
|
||||
colors = plt.get_cmap('viridis')(np.linspace(0,1, len(iterations)))
|
||||
colors = np.vstack(((.5, .5, .5, 1), colors))
|
||||
|
||||
axes = []
|
||||
for fpath in fpaths:
|
||||
rtimes = np.genfromtxt(os.path.join(fpath, arr_path_ref))[:, 4]
|
||||
otimes = np.genfromtxt(os.path.join(fpath, 'otimes.dat'), skip_header=1)[:, 4]
|
||||
|
||||
res = np.empty((len(rtimes), len(iterations) + 1))
|
||||
|
||||
res[:, 0] = otimes # = otimes + rtimes - rtimes
|
||||
|
||||
for index, itr in enumerate(iterations):
|
||||
arr_path = f'it_{itr}/arrivals.dat'
|
||||
|
||||
arrivals = np.genfromtxt(os.path.join(fpath, arr_path))[:, 4]
|
||||
res[:, index + 1] = (otimes + rtimes) - arrivals # otimes (rel) + rtimes => otimes (abs)
|
||||
|
||||
fig = plt.figure()
|
||||
ax = fig.add_subplot(111)
|
||||
axes.append(ax)
|
||||
ax.hist(res, bins=np.linspace(-5, 5, 50), rwidth=0.9, color=colors, label=labels)
|
||||
ax.set_xlim(-1.5, 1.5)
|
||||
plt.title(fpath)
|
||||
|
||||
ax.legend()
|
||||
|
||||
ylim_max = max([ax.get_ylim()[1] for ax in axes])
|
||||
for ax in axes:
|
||||
ax.set_ylim(0, ylim_max)
|
||||
|
||||
plt.show()
|
187
pylot/tomography/fmtomo_tools/station_density_kde.py
Normal file
187
pylot/tomography/fmtomo_tools/station_density_kde.py
Normal file
@ -0,0 +1,187 @@
|
||||
# This script estimates density of station distribution using Gaussian KDE. For each point a Gaussian Kernel with
|
||||
# sigma ~ grid size (?) is summed in 2D, calculating estimate density function. If the sum of all points evaluated
|
||||
# at the data points increases that means there are more regions with high density
|
||||
|
||||
import os
|
||||
import copy
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
from scipy.stats import gaussian_kde
|
||||
|
||||
from pylot.tomography.fmtomo_tools.fmtomo_teleseismic_utils import organize_receivers, organize_event_names, export_otimes
|
||||
|
||||
def get_events_dict(otimes, recs):
|
||||
events_dict = {}
|
||||
for otime in otimes:
|
||||
rec_id, src_id = int(otime[0]), int(otime[1])
|
||||
if not src_id in events_dict.keys():
|
||||
events_dict[src_id] = dict(lons=[], lats=[], rec_ids=[])
|
||||
events_dict[src_id]['lons'].append(recs[rec_id]['lon'])
|
||||
events_dict[src_id]['lats'].append(recs[rec_id]['lat'])
|
||||
events_dict[src_id]['rec_ids'].append(rec_id)
|
||||
return events_dict
|
||||
|
||||
|
||||
def transfer_kde_to_weight(kdes):
|
||||
weights = 0.1 - kdes
|
||||
indices_negative = np.where(weights < 0)[0]
|
||||
if indices_negative:
|
||||
print('Warning! {} negative indices in weights. Set to 0.'.format(len(indices_negative)))
|
||||
weights[indices_negative] = 0.
|
||||
return weights
|
||||
|
||||
|
||||
def calc_event_kernels(events_dict, eventnames, kde_factor, plot_single=False, fdir_out=None):
|
||||
kernels = {}
|
||||
evaluated_kernels = {}
|
||||
|
||||
if plot_single:
|
||||
fig = plt.figure(figsize=(16, 9))
|
||||
|
||||
for index in range(len(eventnames)):
|
||||
eventname = eventnames[index]
|
||||
source = index + 1
|
||||
lons = events_dict[source]['lons']
|
||||
lats = events_dict[source]['lats']
|
||||
kernel = gaussian_kde(np.array([lons, lats]), bw_method=kde_factor)
|
||||
evaluated_kernel = kernel((lons, lats))
|
||||
kernels[source] = kernel
|
||||
evaluated_kernels[source] = evaluated_kernel
|
||||
if plot_single:
|
||||
assert fdir_out, 'Need to specify output directory for plots'
|
||||
if not os.path.isdir(fdir_out):
|
||||
os.mkdir(fdir_out)
|
||||
weights = transfer_kde_to_weight(evaluated_kernel)
|
||||
iterdict = {'kde': evaluated_kernel, 'weight': weights}
|
||||
for name, colorarray in iterdict.items():
|
||||
if name == 'kde':
|
||||
vmin=0
|
||||
vmax=0.05
|
||||
else:
|
||||
vmin=None
|
||||
vmax=None
|
||||
scs = plt.scatter(lons, lats, marker='o', c=colorarray, lw=1.0, vmin=vmin, vmax=vmax)
|
||||
cbar = plt.colorbar(scs)
|
||||
plt.title('Station {} for event {}'.format(name, eventname))
|
||||
fig.savefig(os.path.join(fdir_out, '{}_{}.svg'.format(eventname, name)))
|
||||
fig.clear()
|
||||
#plt.show()
|
||||
if plot_single:
|
||||
plt.close('all')
|
||||
return kernels, evaluated_kernels
|
||||
|
||||
|
||||
def plot_hist_kernels(evaluated_kernels):
|
||||
all_kernels = np.array([])
|
||||
for kernel in evaluated_kernels.values():
|
||||
all_kernels = np.append(all_kernels, kernel)
|
||||
plt.hist(all_kernels, bins=200, label='kernels')
|
||||
plt.title('Distribution of all kernels.')
|
||||
plt.show()
|
||||
|
||||
|
||||
def plot_hist_uncertainties(otimes_new, otimes_orig, bins=200):
|
||||
plt.hist(otimes_orig[:, -1], lw=1, bins=bins, label='Unmodified uncertainties', fc=(.5, .5, 0., 0.5))
|
||||
plt.hist(otimes_new[:, -1], lw=1, bins=bins, label='New uncertainties', fc=(0., .5, .5, 0.5))
|
||||
plt.legend()
|
||||
plt.xlabel('Uncertainty [s]')
|
||||
plt.title('Uncertainty distribution before and after correction.')
|
||||
plt.show()
|
||||
|
||||
|
||||
def plot_uncertainties(otimes, recs, eventnames, fdir_out, vmin=0.1, vmax=0.4):
|
||||
if not os.path.isdir(fdir_out):
|
||||
os.mkdir(fdir_out)
|
||||
|
||||
print('Writing output to directory:', fdir_out)
|
||||
epsilon = 1e-6
|
||||
|
||||
fig = plt.figure(figsize=(16, 9))
|
||||
for index, eventname in enumerate(eventnames):
|
||||
src_id = index + 1
|
||||
indices = np.where(abs(otimes[:, 1] - src_id) <= epsilon)
|
||||
rec_ids = otimes[:, 0][indices]
|
||||
uncs = otimes[:, -1][indices]
|
||||
lats = [recs[rec_id]['lat'] for rec_id in rec_ids]
|
||||
lons = [recs[rec_id]['lon'] for rec_id in rec_ids]
|
||||
sc = plt.scatter(lons, lats, c=uncs, vmin=vmin, vmax=vmax)
|
||||
plt.title(eventname)
|
||||
cb = plt.colorbar(sc)
|
||||
fig.savefig(os.path.join(fdir_out, '{}.svg'.format(eventname)))
|
||||
fig.clear()
|
||||
plt.close('all')
|
||||
|
||||
|
||||
def exp_func(uncertainty, eval_kernel, exponent=30):
|
||||
return uncertainty * np.exp(exponent * eval_kernel)
|
||||
|
||||
|
||||
def modify_otimes(otimes, recs, kernels):
|
||||
print('Applying Kernel on otimes and modifying uncertainties...')
|
||||
for otime in otimes:
|
||||
rec_id, src_id = int(otime[0]), int(otime[1])
|
||||
lon = recs[rec_id]['lon']
|
||||
lat = recs[rec_id]['lat']
|
||||
eval_kernel = kernels[src_id]((lon, lat))
|
||||
otime[-1] = exp_func(otime[-1], eval_kernel)
|
||||
return otimes
|
||||
|
||||
|
||||
def plot_average_kernel(evaluated_kernels, eventnames):
|
||||
ids_names = [(index + 1, eventname) for index, eventname in enumerate(eventnames)]
|
||||
sorted_events = sorted(ids_names, key=lambda x: x[1])
|
||||
kernel_sums = {src_id: np.sum(kernel) for src_id, kernel in evaluated_kernels.items()}
|
||||
src_ids = [item[0] for item in sorted_events]
|
||||
eventnames = [item[1] for item in sorted_events]
|
||||
sums = [kernel_sums[src_id] for src_id in src_ids]
|
||||
plt.plot(sums)
|
||||
xticks = np.arange(0, len(eventnames), step=len(eventnames)//10)
|
||||
xticklabels = [eventname[:8] for index, eventname in enumerate(eventnames) if index in xticks]
|
||||
plt.xticks(xticks, xticklabels)
|
||||
plt.title('Average kernel per station. High value means inhomogeneous station distribution.')
|
||||
plt.show()
|
||||
|
||||
|
||||
def export_station_kdes(events_dict, evaluated_kernels, fnout):
|
||||
with open(fnout, 'w') as outfile:
|
||||
for src_id in range(1, len(events_dict) + 1):
|
||||
kernel = evaluated_kernels[src_id]
|
||||
for kde, rec_id in zip(kernel, events_dict[src_id]['rec_ids']):
|
||||
outfile.write('{} {} {}\n'.format(rec_id, src_id, kde))
|
||||
|
||||
|
||||
def main(plot=False, plot_detailed=False):
|
||||
working_path = '/rscratch/minos13/marcel/fmtomo_alparray/v4/alparray_mantle_diehl_crust_included_hf_gradient_smoothing'
|
||||
fdir_out = 'station_density'
|
||||
fnout = os.path.join(fdir_out, 'station_kdes.txt')
|
||||
|
||||
os.chdir(working_path)
|
||||
eventnames = organize_event_names('input_source_file_P.in')
|
||||
recs = organize_receivers('receivers.in')
|
||||
otimes_data = np.genfromtxt('otimes_orig.dat', skip_header=1)
|
||||
|
||||
# kernel width ~ grid size? or station spacing?
|
||||
kde_size = 30 # km
|
||||
kde_factor = kde_size / (6371. * np.pi / 180.)
|
||||
print('KDE width (degree):', kde_factor)
|
||||
|
||||
events_dict = get_events_dict(otimes_data, recs)
|
||||
kernels, evaluated_kernels = calc_event_kernels(events_dict, eventnames, kde_factor, plot_single=plot_detailed,
|
||||
fdir_out=fdir_out)
|
||||
|
||||
export_station_kdes(events_dict, evaluated_kernels, fnout)
|
||||
|
||||
otimes_modif = modify_otimes(copy.deepcopy(otimes_data), recs, kernels)
|
||||
#export_otimes(otimes_modif, os.path.join(working_path, 'otimes_modif_kernel.dat'))
|
||||
if plot:
|
||||
plot_average_kernel(evaluated_kernels, eventnames)
|
||||
plot_hist_kernels(evaluated_kernels)
|
||||
plot_hist_uncertainties(otimes_modif, otimes_data)
|
||||
if plot_detailed:
|
||||
plot_uncertainties(otimes_data, recs, eventnames, 'uncerts_old')
|
||||
plot_uncertainties(otimes_modif, recs, eventnames, 'uncerts_new')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main(plot=True, plot_detailed=True)
|
17
pylot/tomography/fmtomo_tools/submit_fmtomo.sh
Executable file
17
pylot/tomography/fmtomo_tools/submit_fmtomo.sh
Executable file
@ -0,0 +1,17 @@
|
||||
#!/bin/bash
|
||||
|
||||
ulimit -s 8192
|
||||
|
||||
conda activate pylot_311
|
||||
|
||||
##qsub -l low -cwd -l "os=*stretch" -pe mpi-fu 40 submit_fmtomo.sh
|
||||
#$ -l low
|
||||
#$ -cwd
|
||||
#$ -pe smp 40
|
||||
#$ -q "*@minos11, *@minos12, *@minos15"
|
||||
|
||||
export PYTHONPATH="$PYTHONPATH:/home/marcel/git/"
|
||||
export PYTHONPATH="$PYTHONPATH:/home/marcel/git/pylot/"
|
||||
export PYTHONPATH="$PYTHONPATH:/home/marcel/git/pylot_tools/"
|
||||
|
||||
python fmtomo.py
|
10
pylot/tomography/fmtomo_tools/submit_fmtomo_grid_utils.sh
Normal file
10
pylot/tomography/fmtomo_tools/submit_fmtomo_grid_utils.sh
Normal file
@ -0,0 +1,10 @@
|
||||
#!/bin/bash
|
||||
|
||||
conda activate py37
|
||||
|
||||
#qsub -l low -cwd -l "os=*stretch" -pe mpi-fu 40 submit_fmtomo_grid_utils.sh
|
||||
|
||||
export PYTHONPATH="$PYTHONPATH:/home/marcel/git/"
|
||||
export PYTHONPATH="$PYTHONPATH:/home/marcel/git/pylot_tools/"
|
||||
|
||||
python fmtomo_grid_utils.py
|
21
pylot/tomography/fmtomo_tools/submit_fmtomo_run.py
Normal file
21
pylot/tomography/fmtomo_tools/submit_fmtomo_run.py
Normal file
@ -0,0 +1,21 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import os
|
||||
from pylot.tomography.fmtomo_utils import Tomo3d
|
||||
|
||||
#tomo = Tomo3d('/rscratch/minos13/marcel/fmtomo_alparray/alparray_mantle_from_m6.0_tesauro_model_on_top',
|
||||
# '/rscratch/minos13/marcel/fmtomo_alparray/alparray_mantle_from_m6.0_tesauro_model_on_top',
|
||||
# buildObs=False, saveRays=False)
|
||||
|
||||
wdir = '/rscratch/minos13/marcel/fmtomo_alparray/'
|
||||
|
||||
#directories = ['alparray_mantle_from_m6.0_diehl_crustal_corrections_sm1_damp3',
|
||||
#'alparray_mantle_from_m6.0_diehl_crustal_corrections_sm1_damp30',]
|
||||
directories= ['alparray_mantle_from_m6.0_diehl_crustal_corrections_sm10_damp3',
|
||||
'alparray_mantle_from_m6.0_diehl_crustal_corrections_sm10_damp30']
|
||||
|
||||
for dire in directories:
|
||||
path = os.path.join(wdir, dire)
|
||||
tomo = Tomo3d(path, path, buildObs=False, saveRays=False)
|
||||
tomo.runTOMO3D(40, 8)
|
12
pylot/tomography/fmtomo_tools/submit_fmtomo_run.sh
Normal file
12
pylot/tomography/fmtomo_tools/submit_fmtomo_run.sh
Normal file
@ -0,0 +1,12 @@
|
||||
#!/bin/bash
|
||||
ulimit -s 8192
|
||||
|
||||
#$ -l low,os=*stretch
|
||||
#$ -l h_vmem=5G
|
||||
#$ -cwd
|
||||
#$ -pe smp 40
|
||||
|
||||
export PYTHONPATH="$PYTHONPATH:/home/marcel/git/"
|
||||
export PYTHONPATH="$PYTHONPATH:/home/marcel/git/pylot_tools/"
|
||||
|
||||
python submit_fmtomo_run.py
|
62
pylot/tomography/fmtomo_tools/submit_fmtomo_teleseismic.sh
Executable file
62
pylot/tomography/fmtomo_tools/submit_fmtomo_teleseismic.sh
Executable file
@ -0,0 +1,62 @@
|
||||
#!/bin/bash
|
||||
|
||||
ulimit -s 8192
|
||||
|
||||
conda activate pylot_311
|
||||
|
||||
##qsub -l low -cwd -l "os=*stretch" -pe mpi-fu 40 submit_fmtomo_teleseismic.sh
|
||||
#$ -l low
|
||||
##$ -l os=*stretch
|
||||
#$ -cwd
|
||||
#$ -pe smp 40
|
||||
#$ -q "*@minos11, *@minos12, *@minos15"
|
||||
### WARNING fm3d_prepare_tele not compiled correctly for all cluster machines (e.g. not working on minos12, minos15)
|
||||
|
||||
export PYTHONPATH="$PYTHONPATH:/home/marcel/git/"
|
||||
export PYTHONPATH="$PYTHONPATH:/home/marcel/git/pylot/"
|
||||
export PYTHONPATH="$PYTHONPATH:/home/marcel/git/pylot_tools/"
|
||||
|
||||
#python fmtomo_teleseismic.py '/data/AlpArray_Data/dmt_database_mantle_0.03-0.1_revised' \
|
||||
#'/data/AlpArray_Data/fmtomo/v6/crust_incl_lf_sm_FIX_DTS_grad_sm30_dm10' \
|
||||
#'_correlated_0.03-0.1' -n $NSLOTS --blacklist '/rscratch/minos22/marcel/alparray/station_blacklist.csv'
|
||||
# --model 'ak135_diehl_v2'
|
||||
|
||||
#python fmtomo_teleseismic.py '/data/AlpArray_Data/dmt_database_mantle_0.03-0.5_revised' \
|
||||
#'/data/AlpArray_Data/fmtomo/v6/crust_incl_hf_sm_FIX_DTS_grad_sm30_dm10' \
|
||||
#'_correlated_0.03-0.5' -n $NSLOTS --blacklist '/rscratch/minos22/marcel/alparray/station_blacklist.csv'
|
||||
# --model 'ak135_diehl_v2'
|
||||
|
||||
#python fmtomo_teleseismic.py '/data/AlpArray_Data/dmt_database_mantle_0.01-0.2_S_SKS' \
|
||||
#'/data/AlpArray_Data/fmtomo/v6_S/crust_incl_hf_KSTL_grad_sm6_dm10/' \
|
||||
#'_correlated_0.01-0.2_S*' -n $NSLOTS --blacklist '/data/AlpArray_Data/various/alparray/station_blacklist.csv'
|
||||
# --model 'ak135_diehl_v2'
|
||||
#
|
||||
#python fmtomo_teleseismic.py '/data/AlpArray_Data/dmt_database_mantle_0.03-0.5_revised' \
|
||||
# '/rscratch/minos13/marcel/fmtomo_alparray/v3.5/alparray_mantle_diehl_crust_included_hf_no_init' \
|
||||
# '_correlated_0.03-0.5' -n $NSLOTS --no_write_init_nodes --model 'ak135_diehl_v2'
|
||||
|
||||
#python fmtomo_teleseismic.py '/data/AlpArray_Data/dmt_database_hybrid_test' \
|
||||
# '/rscratch/minos13/marcel/fmtomo_alparray/v3/hybrid_method_test_pgrid_fine' \
|
||||
# '' -n $NSLOTS #--blacklist '/rscratch/minos22/marcel/alparray/station_blacklist.csv' --model 'ak135_diehl_v2'
|
||||
|
||||
#python fmtomo_teleseismic.py '/data/AlpArray_Data/dmt_database_hybrid_test_diehl_v2' \
|
||||
#'/data/AlpArray_Data/fmtomo/v6/hybrid_method_test' \
|
||||
#'' -n $NSLOTS --model 'ak135_diehl_v2'
|
||||
#--blacklist '/rscratch/minos22/marcel/alparray/station_blacklist.csv'
|
||||
|
||||
#python fmtomo_teleseismic.py '/data/AlpArray_Data/dmt_database_hybrid_test_diehl_v2_orig_box_more_stations' \
|
||||
#'/data/AlpArray_Data/fmtomo/v5/hybrid_method_tests_diss/v5_orig_box_size_more_receiver_no_demean_CORRECTED_depth_sampling' \
|
||||
#'' -n $NSLOTS --model 'ak135_diehl_v2'
|
||||
|
||||
#python fmtomo_teleseismic.py '/data/AlpArray_Data/dmt_database_synth_model_mk6_it3_no_rotation' \
|
||||
#'/data/AlpArray_Data/fmtomo/v6_resolution_analysis/fwi_mk6_it3_P' \
|
||||
#'_correlated_P' -n $NSLOTS --model 'ak135_diehl_v2'
|
||||
|
||||
#python fmtomo_teleseismic.py '/data/AdriaArray_Data/dmt_database_alpadege' \
|
||||
#'/data/AdriaArray_Data/fmtomo_adriaarray/alpadege/no_crust_correction' \
|
||||
#'_correlated_0.03-0.5' -n $NSLOTS --model 'ak135'
|
||||
|
||||
python fmtomo_teleseismic.py '/data/AdriaArray_Data/dmt_database_minimal' \
|
||||
'/data/AdriaArray_Data/fmtomo_adriaarray/alpadege/test_minimal' \
|
||||
'_correlated_0.03-0.5' -n $NSLOTS --model 'ak135'
|
||||
|
266
pylot/tomography/fmtomo_tools/tradeoff_misfit_norm.py
Normal file
266
pylot/tomography/fmtomo_tools/tradeoff_misfit_norm.py
Normal file
@ -0,0 +1,266 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
import os
|
||||
import glob
|
||||
import subprocess
|
||||
|
||||
import json
|
||||
|
||||
import numpy as np
|
||||
import numpy.polynomial.polynomial as poly
|
||||
|
||||
from scipy.sparse import spdiags
|
||||
from scipy.optimize import curve_fit
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
from itertools import cycle
|
||||
|
||||
from pylot.tomography.fmtomo_tools.fmtomo_grid_utils import read_vgrid
|
||||
|
||||
# def calc_dampnorm(vgrid, vgrid_ref):
|
||||
# # calculate m - m0
|
||||
# m_m0 = np.array(vgrid['vps']) - np.array(vgrid_ref['vps'])
|
||||
#
|
||||
# # calculate inverse of diagonal elements of a priori model covariance matrix (which should be a diagonal matrix)
|
||||
# # IMPORTANT: COVARIANCES ARE MOST LIKELY STANDARD DEVIATIONS -> square them
|
||||
# covs = np.array(vgrid_ref['covs'])**2
|
||||
#
|
||||
# covs_inv = 1. / covs
|
||||
#
|
||||
# #cm_inv = spdiags(covs_inv, 0, covs_inv.size, covs_inv.size)#
|
||||
#
|
||||
# #norm_calc_old = np.dot(m_m0.transpose(), m_m0 * cm_inv)
|
||||
#
|
||||
# norm = np.dot(m_m0, m_m0 * covs_inv)
|
||||
#
|
||||
# return norm
|
||||
#
|
||||
#
|
||||
# def calc_smoothnorm(vgrid, gridn, R=6371.):
|
||||
# nR, nTheta, nPhi = gridn
|
||||
#
|
||||
# vps = np.array(vgrid['vps'])
|
||||
# lats = np.array(vgrid['lats'])
|
||||
# lons = np.array(vgrid['lons'])
|
||||
# depths = np.array(vgrid['depths'])
|
||||
# #vgridref = np.array(vgridref['vps'])
|
||||
# #dvgrid = vgrid - vgridref
|
||||
#
|
||||
# vgarray = np.zeros((nR, nTheta, nPhi))
|
||||
# lonsarray_km = np.zeros((nR, nTheta, nPhi))
|
||||
# latsarray_km = np.zeros((nR, nTheta, nPhi))
|
||||
# radsarray_km = np.zeros((nR, nTheta, nPhi))
|
||||
# #vgarray_diff = np.zeros((nR, nTheta, nPhi))
|
||||
# smootharray = np.zeros((nR, nTheta, nPhi))
|
||||
# #for iLayer in range(nlayers):
|
||||
# globInd = 0
|
||||
# for iR in range(nR):
|
||||
# for iTheta in range(nTheta):
|
||||
# for iPhi in range(nPhi):
|
||||
# r = R - depths[globInd]
|
||||
# lat = lats[globInd]
|
||||
# lon = lons[globInd]
|
||||
# r_minor = np.cos(np.deg2rad(lat)) * r
|
||||
# vgarray[iR, iTheta, iPhi] = vps[globInd]
|
||||
# radsarray_km[iR, iTheta, iPhi] = r
|
||||
# latsarray_km[iR, iTheta, iPhi] = np.pi * r * lat / 180.
|
||||
# lonsarray_km[iR, iTheta, iPhi] = np.pi * r_minor * lon / 180.
|
||||
# #vgarray_diff[iR, iTheta, iPhi] = vgrid[globInd]
|
||||
# globInd += 1
|
||||
#
|
||||
# # iterate over grid diffs (correct?) and sum 1 * point left -2 * point + 1 * point right in all 3 dim.
|
||||
# smsum = 0.
|
||||
# for iR in range(nR):
|
||||
# for iTheta in range(nTheta):
|
||||
# for iPhi in range(nPhi):
|
||||
# vg = vgarray[iR, iTheta, iPhi]
|
||||
# sum1 = sum2 = sum3 = 0.
|
||||
# if 0 < iPhi < nPhi - 1:
|
||||
# h = abs(lonsarray_km[iR, iTheta, iPhi + 1] - lonsarray_km[iR, iTheta, iPhi - 1]) / 2
|
||||
# sum1 = (vgarray[iR, iTheta, iPhi - 1] - 2 * vg + vgarray[iR, iTheta, iPhi + 1]) / h**2
|
||||
# if 0 < iTheta < nTheta - 1:
|
||||
# h = abs(latsarray_km[iR, iTheta + 1, iPhi] - latsarray_km[iR, iTheta - 1, iPhi]) / 2
|
||||
# sum2 = (vgarray[iR, iTheta - 1, iPhi] - 2 * vg + vgarray[iR, iTheta + 1, iPhi]) / h**2
|
||||
# if 0 < iR < nR - 1:
|
||||
# h = abs(radsarray_km[iR - 1, iTheta, iPhi] - radsarray_km[iR + 1, iTheta, iPhi]) / 2
|
||||
# sum3 = (vgarray[iR - 1, iTheta, iPhi] - 2 * vg + vgarray[iR + 1, iTheta, iPhi]) / h**2
|
||||
# smsum += np.sqrt(sum1**2 + sum2**2 + sum3**2)
|
||||
# #print(sum1, sum2, sum3, smsum)
|
||||
# smootharray[iR, iTheta, iPhi] = smsum#sum1 + sum2 + sum3
|
||||
#
|
||||
# # m_T * D_T * D * m ?? todo: unsure
|
||||
# norm = np.sum(smootharray ** 2)
|
||||
#
|
||||
# return norm, smootharray
|
||||
from pylot.tomography.utils import normed_figure
|
||||
|
||||
|
||||
def calc_smoothnorm(wdir, iter):
|
||||
smv = np.loadtxt(os.path.join(wdir, 'it_{}/smv.out'.format(iter + 1)), skiprows=1)
|
||||
dm = np.loadtxt(os.path.join(wdir, 'it_{}/dm.out'.format(iter + 1)), skiprows=1)
|
||||
norm = np.sum(smv*dm)
|
||||
return norm
|
||||
|
||||
|
||||
def calc_dampnorm(wdir, iter):
|
||||
ecmi = np.loadtxt(os.path.join(wdir, 'it_{}/ecmi.out'.format(iter + 1)), skiprows=1)
|
||||
dm = np.loadtxt(os.path.join(wdir, 'it_{}/dm.out'.format(iter + 1)), skiprows=1)
|
||||
norm = np.sum(ecmi * dm**2)
|
||||
return norm
|
||||
|
||||
|
||||
def calc_norm(wdir, iteration_number):
|
||||
dampnorm = calc_dampnorm(wdir, iteration_number)
|
||||
smoothnorm = calc_smoothnorm(wdir, iteration_number)
|
||||
|
||||
print('dampnorm: ', dampnorm)
|
||||
print('smoothnorm: ', smoothnorm)
|
||||
|
||||
norm = dampnorm + smoothnorm
|
||||
|
||||
print('Calculated summed norm of', norm)
|
||||
|
||||
return norm, dampnorm, smoothnorm
|
||||
|
||||
|
||||
def calc_tradeoff(fpath_in, fname_out=None, iteration_number = 12):
|
||||
results = {}
|
||||
|
||||
for wdir in glob.glob(fpath_in):
|
||||
#wdir = '/rscratch/minos13/marcel/fmtomo_alparray/alparray_mantle_from_m6.0_diehl_crustal_corrections_sm1000_damp100/'
|
||||
smooth = float(wdir.split('_')[-2].split('sm')[-1])
|
||||
damp = float(wdir.split('_damp')[-1].split('/')[0])
|
||||
|
||||
print('Calculating tradeoff for smoothing and damping of {}, {}'.format(smooth, damp))
|
||||
if not smooth in results.keys():
|
||||
results[smooth] = {}
|
||||
|
||||
iteration_number_new = iteration_number
|
||||
ecmi_path = os.path.join(wdir, 'it_{}'.format(iteration_number_new + 1), 'ecmi.out')
|
||||
smv_path = os.path.join(wdir, 'it_{}'.format(iteration_number_new + 1), 'smv.out')
|
||||
while not os.path.isfile(ecmi_path) or not os.path.isfile(smv_path):
|
||||
iteration_number_new -= 1
|
||||
ecmi_path = os.path.join(wdir, 'it_{}'.format(iteration_number_new + 1), 'ecmi.out')
|
||||
smv_path = os.path.join(wdir, 'it_{}'.format(iteration_number_new + 1), 'smv.out')
|
||||
print('WARNING: Iteration number lowered by 1:', iteration_number_new)
|
||||
if iteration_number_new <= 1:
|
||||
break
|
||||
|
||||
if iteration_number_new <= 1:
|
||||
continue
|
||||
else:
|
||||
iteration_number = iteration_number_new
|
||||
|
||||
#vgrid, gridn, griddelta, gridstart = read_vgrid(vgrid_path)
|
||||
#vgrid_ref = read_vgrid(os.path.join(wdir, 'vgridsref.in'))[0]
|
||||
|
||||
norm, dampnorm, smoothnorm = calc_norm(wdir, iteration_number)
|
||||
|
||||
try:
|
||||
fpath = os.path.join(wdir, 'residuals.dat')
|
||||
chi = float(subprocess.check_output(['tail', fpath]).split()[-1])
|
||||
except Exception as e:
|
||||
print(e)
|
||||
chi = np.nan
|
||||
|
||||
results[smooth][wdir] = {'dampnorm': dampnorm, 'smoothnorm': smoothnorm,
|
||||
'norm': norm, 'chi': chi, 'damp': damp}
|
||||
|
||||
#print some output
|
||||
for smooth, result in results.items():
|
||||
print('Smoothing:', smooth)
|
||||
for wdir, item in result.items():
|
||||
print(item['chi'], item['norm'])
|
||||
print(20*'#')
|
||||
|
||||
if fname_out:
|
||||
with open(fname_out, 'w') as outfile:
|
||||
json.dump(results, outfile)
|
||||
return results
|
||||
|
||||
def quadratic_function(x, a, b, c):
|
||||
return a * x ** 2 + b * x + c
|
||||
|
||||
def one_over_x(x, a, b, c):
|
||||
return a / (x - b) + c
|
||||
|
||||
def exp_func(x, a, b, c):
|
||||
return a * np.exp(-b * x) + c
|
||||
|
||||
def plot_tradeoff(fname_in, fix='smooth', plot_norm='both', min_smooth=0, min_damp=0, max_smooth=1e6, max_damp=1e6):
|
||||
with open(fname_in, 'r') as infile:
|
||||
results_smooth = json.load(infile)
|
||||
|
||||
lines = ["-", "--", "-.", ":"]
|
||||
linecycler = cycle(lines)
|
||||
|
||||
# array will be built for each line: (smooth, damp, norm, chi)
|
||||
plot_values = []
|
||||
for smooth, result in results_smooth.items():
|
||||
for item in result.values():
|
||||
smooth = float(smooth)
|
||||
damping = item['damp']
|
||||
if smooth < min_smooth or damping < min_damp or smooth > max_smooth or damping > max_damp:
|
||||
continue
|
||||
plot_values.append(np.array([smooth, damping, item[plot_norm], item['chi']]))
|
||||
|
||||
plot_values = np.array(plot_values)
|
||||
|
||||
column_index = {'smooth': 0, 'damp': 1}
|
||||
|
||||
keys = np.unique(plot_values[:, column_index[fix]])
|
||||
names = {'smooth': 'Smoothing', 'damp': 'Damping'}
|
||||
|
||||
for key in keys:
|
||||
plot_line = plot_values[plot_values[:, column_index[fix]] == key]
|
||||
second_index = column_index['smooth'] if fix == 'damp' else column_index['damp']
|
||||
plot_line = np.array(sorted(plot_line, key=lambda x: x[second_index]))
|
||||
norms = plot_line[:, 2]
|
||||
chis = plot_line[:, 3]
|
||||
#text = [str(item) for item in plot_line[:, second_index]]
|
||||
|
||||
x = np.linspace(min(norms), max(norms), num=100)
|
||||
|
||||
#popt, pcov = curve_fit(one_over_x, norms, chis, method='trf')#, bounds=[min(norms), max(norms)])
|
||||
#fit_result = one_over_x(x, *popt)
|
||||
#line = plt.plot(x, fit_result, ':', lw=0.8)[0]
|
||||
fninfo = os.path.split(fname_in)[-1].replace('.json', '').split('_f')[-1]
|
||||
label = '{}: {:g}'.format(names[fix], float(key))
|
||||
|
||||
line = plt.plot(norms, chis, linestyle=next(linecycler), lw=0.8, label=label)[0]
|
||||
#coefs = poly.polyfit(norms, chis, 4)
|
||||
#ffit = poly.polyval(x, coefs)
|
||||
#line = plt.plot(x, ffit, ':', lw=0.8)[0]
|
||||
|
||||
#label = label='{}: {:g} (smgrad: {})'.format(names[fix], float(key), fninfo)
|
||||
plt.plot(norms, chis, c=line.get_color(), marker='.', lw=0.)
|
||||
#plt.text(norms, chis, text)
|
||||
for item in plot_line:
|
||||
plt.text(item[2], item[3], str(item[second_index]), horizontalalignment='left')
|
||||
#plt.title('Plot of Misfit against Norm ({})'.format(plot_norm))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
#calc_tradeoff('/data/AlpArray_Data/fmtomo/v5/tradeoff_curves/crust_included_grad_smooth_FIXED_dts_grad_1.5_sm*_damp*/',
|
||||
# '/data/AlpArray_Data/various/alparray/tradeoff_v5_f1.5.json')
|
||||
#calc_tradeoff('/data/AlpArray_Data/fmtomo/v5/tradeoff_curves/crust_included_grad_smooth_FIXED_dts_sm*_damp*/',
|
||||
# '/data/AlpArray_Data/various/alparray/tradeoff_v5_f2.0.json')
|
||||
|
||||
fig = normed_figure(width_cm=10, ratio=1.)
|
||||
#tradeoff_infiles = ['tradeoff_v4_f1.5.json', 'tradeoff_v4_f3.json', 'tradeoff_v4_f10.json']
|
||||
tradeoff_infiles = ['tradeoff_v5_f2.0.json']#, 'tradeoff_v5_f1.5.json']
|
||||
for infile in tradeoff_infiles:
|
||||
infile = os.path.join('/data/AlpArray_Data/various/alparray/', infile)
|
||||
plot_tradeoff(infile, fix='damp', plot_norm='norm')
|
||||
|
||||
plt.xlim([1900, 16200])
|
||||
plt.ylim([2.72, 3.8])
|
||||
plt.xlabel('Norm')
|
||||
#plt.ylabel(r'Misfit($\frac{\chi^2}{N}$)')
|
||||
plt.ylabel(r'Misfit($\chi^2/N$)')
|
||||
#plt.title('Tradeoff curve Misfit vs Norm. Numbers in plot show smoothing values.')
|
||||
plt.legend()
|
||||
#plt.show()
|
||||
plt.savefig('/data/AlpArray_Data/sciebo/AlpArray_home/pictures/paper_II/tradeoff.pdf', dpi=300)
|
||||
|
63
pylot/tomography/fmtomo_tools/visualize_frechet_on_vgrid.py
Executable file
63
pylot/tomography/fmtomo_tools/visualize_frechet_on_vgrid.py
Executable file
@ -0,0 +1,63 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import numpy as np
|
||||
import json
|
||||
|
||||
from pylot.tomography.fmtomo_tools.fmtomo_grid_utils import read_vgrid, write_vtk, calculate_differences_grid, write_vgrid
|
||||
|
||||
def visualize_frechet_derivative(fname_vgrid, fname_frechet, fname_out_vtk=None, fname_out_json=None,
|
||||
fname_out_vgrid=None, diff_model=None, fname_vgrid_ref=None):
|
||||
vgrid, gridN, gridDelta, gridStart = read_vgrid(fname_vgrid, inv_index_frechet=True)
|
||||
if diff_model and fname_vgrid_ref:
|
||||
raise OverflowError('Cannot have both parameters set, diff_model and fname_vgrid_ref')
|
||||
if diff_model:
|
||||
vgrid = calculate_differences_grid(vgrid, earth_model=diff_model)
|
||||
if fname_vgrid_ref:
|
||||
vgrid_ref, gridN_ref, gridDelta_ref, gridStart_ref = read_vgrid(fname_vgrid_ref, inv_index_frechet=False)
|
||||
grid_check = (gridN == gridN_ref,
|
||||
compare_tuple(gridDelta, gridDelta_ref),
|
||||
compare_tuple(gridStart, gridStart_ref))
|
||||
assert(all(grid_check), 'Missmatch ref grid size')
|
||||
vps = np.array(vgrid['vps'])
|
||||
vps_ref = np.array(vgrid_ref['vps'])
|
||||
vps_rel = (vps - vps_ref) / vps_ref * 100.
|
||||
print('Min/Max change {}/{}%'.format(min(vps_rel), max(vps_rel)))
|
||||
vgrid['vps'] = list(vps_rel)
|
||||
|
||||
add_frechet(vgrid, fname_frechet)
|
||||
|
||||
if fname_out_vgrid:
|
||||
write_vgrid(vgrid, gridN, gridDelta, gridStart, fname_out_vgrid)
|
||||
if fname_out_vtk:
|
||||
write_vtk(vgrid, fname_out_vtk, ['vps', 'frechs', 'grid_indices', 'hit_count'])
|
||||
if fname_out_json:
|
||||
with open(fname_out_vtk, 'w') as outfile:
|
||||
json.dump(vgrid, outfile)
|
||||
|
||||
|
||||
def add_frechet(vgrid, fname_frechet):
|
||||
vgrid['frechs'] = list(np.zeros(len(vgrid['xs'])))
|
||||
vgrid['hit_count'] = list(np.zeros(len(vgrid['xs'])))
|
||||
with open(fname_frechet, 'r') as infile:
|
||||
while True:
|
||||
try:
|
||||
n, source_id, m, k, n_pdev = [int(item) for item in infile.readline().split()]
|
||||
except:
|
||||
break
|
||||
#print(n, source_ids, m, k, n_pdev)
|
||||
for _ in range(n_pdev):
|
||||
pdev_index, pdev = infile.readline().split()
|
||||
pdev_index = int(pdev_index)
|
||||
pdev = float(pdev)
|
||||
vgrid_index = vgrid['inv_index'][pdev_index]
|
||||
vgrid['frechs'][vgrid_index] += pdev
|
||||
# hit by ray count
|
||||
vgrid['hit_count'][vgrid_index] += 1
|
||||
|
||||
|
||||
def compare_tuple(t1, t2, epsilon=1e-6):
|
||||
for item1, item2 in zip(t1, t2):
|
||||
if abs(item1 - item2) > epsilon:
|
||||
return False
|
||||
return True
|
1323
pylot/tomography/fmtomo_utils.py
Normal file
1323
pylot/tomography/fmtomo_utils.py
Normal file
File diff suppressed because it is too large
Load Diff
160
pylot/tomography/map_utils.py
Normal file
160
pylot/tomography/map_utils.py
Normal file
@ -0,0 +1,160 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import glob
|
||||
import os
|
||||
import json
|
||||
import numpy as np
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
import cartopy
|
||||
import cartopy.crs as ccrs
|
||||
from cartopy.io.shapereader import Reader
|
||||
from matplotlib.patches import Patch
|
||||
|
||||
#from cmcrameri import cm
|
||||
|
||||
from obspy.geodetics import gps2dist_azimuth
|
||||
|
||||
TRANSFORM = ccrs.PlateCarree()
|
||||
|
||||
|
||||
def draw_schmid_faults(ax, fnin='schmidfaults.shp'):
|
||||
reader = Reader(fnin)
|
||||
lines = []
|
||||
linestyles = []
|
||||
for record in reader.records():
|
||||
info = record.attributes
|
||||
if info["fault_type"] == 1 or info["fault_type"] == 3:
|
||||
linestyles.append('solid')
|
||||
else:
|
||||
linestyles.append('dashed')
|
||||
line_arr = np.array(record.geometry.coords)
|
||||
lines.append(line_arr)
|
||||
# ax.add_collection(LineCollection(lines, linewidths=1.2, linestyles=linemarkers, colors='black', transform=ccrs.PlateCarree()))
|
||||
for line, style in zip(lines, linestyles):
|
||||
ax.plot(line[:, 0], line[:, 1], transform=TRANSFORM, ls=style, c='k', lw=1.2)
|
||||
|
||||
|
||||
def draw_alcapadi_faults(ax, fnin='faults_alcapadi', color='black'):
|
||||
reader = Reader(fnin)
|
||||
lines = []
|
||||
linestyles = []
|
||||
linewidths = []
|
||||
for record in reader.records():
|
||||
info = record.attributes
|
||||
if info["fault_type"] == 1:
|
||||
linestyles.append('solid')
|
||||
linewidths.append(.8)
|
||||
elif info["fault_type"] == 2:
|
||||
linestyles.append('solid')
|
||||
linewidths.append(0.4)
|
||||
else:
|
||||
linestyles.append('dashed')
|
||||
linewidths.append(.8)
|
||||
line_arr = np.array(record.geometry.coords)
|
||||
lines.append(line_arr)
|
||||
for line, style, lwidth in zip(lines, linestyles, linewidths):
|
||||
ax.plot(line[:, 0], line[:, 1], transform=TRANSFORM, ls=style, c='k', lw=lwidth)
|
||||
|
||||
|
||||
def draw_alcapadi_model(ax, fnin='tect_units_alcapadi', alpha=0.2, add_legend=True):
|
||||
reader = Reader(fnin)
|
||||
color_dict = {"Adria accreted": (0.8, 0.7, 0.45, alpha),
|
||||
"Adria autochton": (0.52, 0.32, 0.2, alpha),
|
||||
"Europe accreted": (0.42, 0.67, 0.88, alpha),
|
||||
"Flexural foredeep and graben fill": (0.6, 0.6, 0.6, alpha), # (1.0, 1.0, 220/255., alpha),
|
||||
"Alpine Tethys": (0., 0.65, 0.3, alpha),
|
||||
"Neotethys": (0.5, 0.8, 0.32, alpha)}
|
||||
|
||||
patches_legend = [Patch(color=value, label=key.capitalize()) for key, value in color_dict.items()]
|
||||
|
||||
for record in reader.records():
|
||||
info = record.attributes
|
||||
shape = record.geometry
|
||||
color = color_dict.get(info['tect_unit'])
|
||||
if not color:
|
||||
color = (1.0, 1.0, 1.0, alpha)
|
||||
ax.add_geometries(shape, crs=TRANSFORM, facecolor=color)
|
||||
|
||||
if add_legend:
|
||||
ax.legend(handles=patches_legend, ncol=3, bbox_to_anchor=(0.5, -0.075), loc='center')
|
||||
|
||||
|
||||
def init_cartopy_map(fig=None, draw_mapbound=True, fill_continents=False,
|
||||
continents_color=None, mapbound_color=None, lakes_color=None, clon=None, clat=None):
|
||||
if not fig:
|
||||
fig = plt.figure()
|
||||
#projection = ccrs.LambertConformal(central_longitude=clon, central_latitude=clat)
|
||||
projection = ccrs.PlateCarree(central_longitude=clon)
|
||||
ax = fig.add_subplot(111, projection=projection)
|
||||
|
||||
if fill_continents:
|
||||
ax.add_feature(cartopy.feature.LAND, color=continents_color)
|
||||
ax.add_feature(cartopy.feature.LAKES, color=lakes_color)
|
||||
if draw_mapbound:
|
||||
#ax.add_feature(cartopy.feature.OCEAN, color='w', linewidth=0.1)
|
||||
ax.add_feature(cartopy.feature.BORDERS, linewidth=0.2, color='0.3')
|
||||
ax.add_feature(cartopy.feature.COASTLINE, linewidth=0.3, color='0.3')
|
||||
return ax
|
||||
|
||||
|
||||
def make_map(fig=None, draw_model=False, model_legends=True, draw_faults=False, width=20,
|
||||
height=14, clon=11, clat=46., draw_grid=True, continents='0.8', lakes='0.85', no_content=False,
|
||||
no_fill=True, alpha_model=0.2, faults_color='k', station_file=None):
|
||||
|
||||
ax = init_cartopy_map(fig=fig, draw_mapbound=not no_content, fill_continents=not no_fill,
|
||||
continents_color=continents, lakes_color=lakes, clon=clon, clat=clat)
|
||||
|
||||
if station_file:
|
||||
with open(station_file, 'r') as fid:
|
||||
stations = json.load(fid)
|
||||
|
||||
lons, lats = zip(*[(sta['longitude'], sta['latitude']) for sta in stations.values()])
|
||||
ax.scatter(lons, lats, c='0.3', s=1, transform=ccrs.PlateCarree(), zorder=5, edgecolors='none', alpha=0.5)
|
||||
|
||||
# if draw_topo:
|
||||
# draw_topo_model(basemap)
|
||||
if draw_model:
|
||||
fnin = '/home/marcel/sciebo/AlpArray_home/tectonic_maps_4dmb_2020_09_17/shape_files/tect_units_alcapadi'
|
||||
draw_alcapadi_model(ax, fnin, add_legend=model_legends, alpha=alpha_model)
|
||||
if draw_faults:
|
||||
fnin = '/home/marcel/sciebo/AlpArray_home/tectonic_maps_4dmb_2020_09_17/shape_files/faults_alcapadi'
|
||||
draw_alcapadi_faults(ax, fnin, faults_color)
|
||||
|
||||
# if not no_content:
|
||||
# basemap.drawcountries(color=line_color, linewidth=0.2)
|
||||
# basemap.drawcoastlines(color=line_color, linewidth=0.3)
|
||||
|
||||
if draw_grid:
|
||||
gl = ax.gridlines(crs=TRANSFORM, draw_labels=True,
|
||||
linewidth=0.5, color='gray', alpha=0.5, linestyle=':')
|
||||
# dashes = [3, 6]
|
||||
# parallels = list(np.arange(-90, 90, lgrid))
|
||||
# parallels_small = [item for item in np.arange(-90, 90, sgrid) if not item in parallels]
|
||||
# basemap.drawparallels(parallels_small, dashes=dashes, color=line_color, linewidth=0.1, zorder=7)
|
||||
# basemap.drawparallels(parallels, dashes=[], color=line_color, linewidth=0.2, zorder=7, labels=[1, 1, 0, 0])
|
||||
# meridians = list(np.arange(-180, 180, lgrid))
|
||||
# meridians_small = [item for item in np.arange(-180, 180, sgrid) if not item in meridians]
|
||||
# basemap.drawmeridians(meridians_small, dashes=dashes, color=line_color, linewidth=0.1, zorder=7)
|
||||
# basemap.drawmeridians(meridians, dashes=[], color=line_color, linewidth=0.2, zorder=7, labels=[0, 0, 1, 1])
|
||||
|
||||
ax.set_extent([clon - width/2, clon + width/2, clat - height/2, clat + height/2])
|
||||
|
||||
return ax
|
||||
|
||||
|
||||
def angle_marker(a1, a2, delta=1.):
|
||||
a1 = np.deg2rad(a1)
|
||||
a2 = np.deg2rad(a2)
|
||||
delta = np.deg2rad(delta)
|
||||
x_vals = [np.sin(angle) for angle in np.arange(a1, a2 + delta, delta)]
|
||||
y_vals = [np.cos(angle) for angle in np.arange(a1, a2 + delta, delta)]
|
||||
xy = zip(x_vals, y_vals)
|
||||
#x1 = np.sin(a1)
|
||||
#y1 = np.cos(a1)
|
||||
#x2 = np.sin(a2)
|
||||
#y2 = np.cos(a2)
|
||||
marker = [(0, 0), *xy, (0, 0)]
|
||||
return marker
|
176
pylot/tomography/utils.py
Normal file
176
pylot/tomography/utils.py
Normal file
@ -0,0 +1,176 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
import glob
|
||||
import numpy as np
|
||||
import os
|
||||
import json
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
from obspy import Catalog, read_events
|
||||
|
||||
from pylot.core.util.dataprocessing import Metadata
|
||||
|
||||
|
||||
def get_metadata(eventdir):
|
||||
metadata_path = os.path.join(eventdir, 'resp')
|
||||
metadata = Metadata(inventory=metadata_path, verbosity=0)
|
||||
return metadata
|
||||
|
||||
|
||||
|
||||
def set_rc_params(textsize=7.):
|
||||
plt.style.use('/home/marcel/solid_earth.mplstyle')
|
||||
#plt.rcParams.update({'font.size': textsize,
|
||||
# 'font.family': 'sans-serif'})
|
||||
|
||||
|
||||
def normed_figure_ratio_width(width_cm, ratio):
|
||||
width_inch = width_cm / 2.54
|
||||
height_inch = width_inch / ratio
|
||||
return width_inch, height_inch
|
||||
|
||||
|
||||
def normed_figure_ratio_height(height_cm, ratio):
|
||||
height_inch = height_cm / 2.54
|
||||
width_inch = height_inch * ratio
|
||||
return width_inch, height_inch
|
||||
|
||||
|
||||
def normed_figure(width_cm=None, ratio=1.777):
|
||||
#assert ((width_cm and not height_cm) or (height_cm and not width_cm)), 'Choose either of width or height!'
|
||||
set_rc_params()
|
||||
if width_cm:
|
||||
fig = plt.figure(figsize=normed_figure_ratio_width(width_cm, ratio))
|
||||
return fig
|
||||
#elif height_cm:
|
||||
# fig = plt.figure(figsize=normed_figure_ratio_height(height_cm, ratio))
|
||||
return
|
||||
|
||||
|
||||
def pol2cart(lat, lon, r):
|
||||
x = r * np.cos(np.deg2rad(lat)) * np.cos(np.deg2rad(lon))
|
||||
y = r * np.cos(np.deg2rad(lat)) * np.sin(np.deg2rad(lon))
|
||||
z = r * np.sin(np.deg2rad(lat))
|
||||
return x, y, z
|
||||
|
||||
|
||||
def cart2pol(x, y, z):
|
||||
r = np.sqrt(x**2 + y**2 + z**2)
|
||||
theta = np.rad2deg(np.arccos(z/r))
|
||||
phi = np.rad2deg(np.arctan2(y, x))
|
||||
lat = 90. - theta
|
||||
lon = phi
|
||||
return lat, lon, r
|
||||
|
||||
|
||||
def pol2cart_vector(lat, lon, north, east, r_comp):
|
||||
if any(val is None for val in [north, east, r_comp]):
|
||||
return None, None, None
|
||||
phi = np.deg2rad(lon)
|
||||
# change north components to common spherical coordinate convention
|
||||
theta = np.deg2rad(90. - lat)
|
||||
north *= -1
|
||||
x = (np.sin(theta) * np.cos(phi) * r_comp +
|
||||
np.cos(theta) * np.cos(phi) * north -
|
||||
np.sin(phi) * east)
|
||||
y = (np.sin(theta) * np.sin(phi) * r_comp +
|
||||
np.cos(theta) * np.sin(phi) * north +
|
||||
np.cos(phi) * east)
|
||||
z = (np.cos(theta) * r_comp -
|
||||
np.sin(theta) * north)
|
||||
return x, y, z
|
||||
|
||||
|
||||
def read_cat_obspy_dmt_database(databasedir, filemask):
|
||||
infiles = glob.glob(os.path.join(databasedir, '*.a', filemask))
|
||||
cat = Catalog()
|
||||
nPicks = 0
|
||||
for index, infile in enumerate(infiles):
|
||||
print(f'Working on: {infile} ({index + 1}/{len(infiles)})')
|
||||
event = read_events(infile)[0]
|
||||
nPicks += len(event.picks)
|
||||
cat += event
|
||||
|
||||
nEvents = len(cat)
|
||||
print('Number of events: {} (filemask: {})'.format(nEvents, filemask))
|
||||
print('Total # picks: {} ({:.2f} per event)'.format(nPicks, float(nPicks)/nEvents))
|
||||
return cat
|
||||
|
||||
|
||||
def get_event(cat, eventid):
|
||||
for event in cat.events:
|
||||
if event.resource_id.id.split('/')[-1] == eventid:
|
||||
return event
|
||||
|
||||
|
||||
def get_pick4station(picks, network_code, station_code, method='auto'):
|
||||
for pick in picks:
|
||||
if pick.waveform_id.network_code == network_code:
|
||||
if pick.waveform_id.station_code == station_code:
|
||||
if pick.method_id.id.endswith(method):
|
||||
return pick
|
||||
|
||||
|
||||
def delete_picks(picks, nwst_ids_delete):
|
||||
''' Delete picks from list in picks containing ObsPy pick objects'''
|
||||
for index, pick in list(reversed(list(enumerate(picks)))):
|
||||
seed_id = pick.waveform_id.get_seed_string()
|
||||
network, station = seed_id.split('.')[:2]
|
||||
nwst_id = '{}.{}'.format(network, station)
|
||||
if nwst_id in nwst_ids_delete:
|
||||
picks.pop(index)
|
||||
print('Removed pick: ', nwst_id)
|
||||
return picks
|
||||
|
||||
|
||||
def save_all_station_coordinates_dmt_database(dmt_database, fn_out):
|
||||
'''
|
||||
Get all station coordinates from dmt_database and write them (unique) to json outputfile
|
||||
:param dmt_database:
|
||||
:param fn_out:
|
||||
:return:
|
||||
'''
|
||||
stations_dict = {}
|
||||
eventdirs = glob.glob(os.path.join(dmt_database, '*.?'))
|
||||
nEvents = len(eventdirs)
|
||||
for index, eventdir in enumerate(eventdirs):
|
||||
print('Working on event {} ({}/{})'.format(eventdir, index+1, nEvents))
|
||||
metadata = get_metadata(eventdir)
|
||||
current_stations_dict = metadata.get_all_coordinates()
|
||||
for nwst_id, coords in current_stations_dict.items():
|
||||
if not nwst_id in stations_dict.keys():
|
||||
stations_dict[nwst_id] = coords
|
||||
|
||||
with open(fn_out, 'w') as outfile:
|
||||
json.dump(stations_dict, outfile)
|
||||
|
||||
|
||||
def get_metadata(eventdir):
|
||||
metadata_path = os.path.join(eventdir, 'resp')
|
||||
metadata = Metadata(inventory=metadata_path, verbosity=0)
|
||||
return metadata
|
||||
|
||||
|
||||
def get_coordinate_from_dist_baz(station_tmp, dist, baz, mode='deg'):
|
||||
''' function copied from Andre'''
|
||||
# station_tmp: [lon, lat]
|
||||
if mode!='deg' and mode!='rad':
|
||||
print('mode hast to be ether deg or rad!')
|
||||
return None
|
||||
else:
|
||||
station = np.deg2rad(station_tmp)
|
||||
epi_tmp=[0.,0.]
|
||||
if mode=='deg':
|
||||
dist = np.deg2rad(dist)
|
||||
baz = np.deg2rad(baz)
|
||||
az = baz - np.pi
|
||||
if az < 0:
|
||||
az += 2 * np.pi
|
||||
epi_tmp[1] = np.arcsin(np.sin(station[1]) * np.cos(dist) - np.cos(station[1]) * np.sin(dist) * np.cos(az))
|
||||
if (np.cos(dist) - np.sin(station[1]) * np.sin(epi_tmp[1])) / (np.cos(station[1]) * np.cos(epi_tmp[1])) >= 0.:
|
||||
epi_tmp[0] = station[0] - np.arcsin(np.sin(dist) * np.sin(az) / np.cos(epi_tmp[1]))
|
||||
else:
|
||||
epi_tmp[0] = station[0] - np.pi + np.arcsin(np.sin(dist) * np.sin(az) / np.cos(epi_tmp[1]))
|
||||
epi=np.rad2deg(epi_tmp)
|
||||
return epi
|
Loading…
x
Reference in New Issue
Block a user