def get_pierce_points(st, phase, depth, h5_out): model = TauPyModel(model="prem") try: f = h5py.File( '/home/samhaug/anaconda2/lib/python2.7/site-packages/seispy' + h5_out, 'w') except IOError: os.rmdir('/home/samhaug/anaconda2/lib/python2.7/site-packages/seispy' + h5_out) print "Had to remove existing file. Try again" evla = st[0].stats.sac['evla'] evlo = st[0].stats.sac['evlo'] evdp = st[0].stats.sac['evdp'] pierce_list = [] for idx, tr in enumerate(st): print tr a = model.get_pierce_points(evdp, tr.stats.sac['gcarc'], phase_list=[phase]) b = a[0] depth_index = np.argmin(np.abs(b.pierce['depth'] - depth)) distance = b.pierce['dist'][depth_index] pierce_list.append((distance, tr.stats.sac['az'])) pierce_array = np.array(pierce_list) f.create_dataset('pierce', data=pierce_array) f.close() return pierce_array
def migrate(self,plot=False): import geopy from geopy.distance import VincentyDistance ''' This is a rewritten function that combines the functions find_pierce_coor and migrate_1d so that it's more efficient. Still in testing stages. RM 2/6/16 ''' depth_range = np.arange(50,800,5) #set range of pierce points value = np.zeros((len(depth_range))) #geodetic info bearing = self.az lon_s = self.ses3d_seismogram.sy lat_s = 90.0-self.ses3d_seismogram.sx lon_r = self.ses3d_seismogram.ry lat_r = 90.0-self.ses3d_seismogram.rx origin = geopy.Point(lat_s, lon_s) #find how far away the pierce point is model = TauPyModel(model='pyrolite_5km') for i in range(0,len(depth_range)): phase = 'P'+str(depth_range[i])+'s' pierce = model.get_pierce_points(self.eq_depth,self.delta_deg,phase_list=[phase]) tt = model.get_travel_times(self.eq_depth,self.delta_deg,phase_list=['P',phase]) #in case there's duplicate phase arrivals for j in range(0,len(tt)): if tt[j].name == 'P': p_arr = tt[j].time elif tt[j].name == phase: phase_arr = tt[j].time #determine value Pds_time = phase_arr - p_arr i_start = int((0.0 - self.window_start)/self.ses3d_seismogram.dt) i_t = int(Pds_time/self.ses3d_seismogram.dt) + i_start value[i] = self.prf[i_t] points = pierce[0].pierce for j in range(0,len(points)): if points[j]['depth'] == depth_range[i] and points[j]['dist']*(180.0/np.pi) > 20.0: prc_dist = points[j]['dist']*(180.0/np.pi) d_km = prc_dist * ((2*np.pi*6371.0/360.0)) destination = VincentyDistance(kilometers=d_km).destination(origin,bearing) lat = destination[0] lon = destination[1] row = {'depth':depth_range[i],'dist':prc_dist,'lat':lat,'lon':lon,'value':value[i]} self.pierce_dict.append(row) if plot == True: plt.plot(value,depth_range) plt.gca().invert_yaxis() plt.show() return value,depth_range
def get_taupy_points( center_lat, center_lon, ev_lat, ev_lon, ev_depth, stime, etime, mini, maxi, ev_otime, phase_shift, sll, slm ): distance = locations2degrees(center_lat, center_lon, ev_lat, ev_lon) # print(distance) model = TauPyModel(model="ak135") arrivals = model.get_pierce_points(ev_depth, distance) # arrivals = earthmodel.get_pierce_points(ev_depth,distance,phase_list=('PP','P^410P')) # compute the vespagram window start_vespa = stime - mini end_vespa = etime - maxi # compare the arrival times with the time window count = 0 k = 0 phase_name_info = [] phase_slowness_info = [] phase_time_info = [] for i_elem in arrivals: # print(i_elem) dummy_phase = arrivals[count] # print(dummy_phase) # phase time in seconds taup_phase_time = dummy_phase.time # print(taup_phase_time) # slowness of the phase taup_phase_slowness = dummy_phase.ray_param_sec_degree # compute the UTC travel phase time taup_phase_time2 = ev_otime + taup_phase_time + phase_shift # print(start_vespa) # print(end_vespa) # print(taup_phase_time2) if start_vespa <= taup_phase_time2 <= end_vespa: # time window if sll <= taup_phase_slowness <= slm: # slowness window # seconds inside the vespagram taup_mark = taup_phase_time2 - start_vespa # store the information phase_name_info.append(dummy_phase.name) phase_slowness_info.append(dummy_phase.ray_param_sec_degree) phase_time_info.append(taup_mark) # print(phases_info[k]) k += 1 count += 1 # print(phase_name_info) phase_slowness_info = np.array(phase_slowness_info) phase_time_info = np.array(phase_time_info) return phase_name_info, phase_slowness_info, phase_time_info
def get_taupy_points(center_lat, center_lon, ev_lat, ev_lon, ev_depth, stime, etime, mini, maxi, ev_otime, phase_shift, sll, slm): distance = locations2degrees(center_lat, center_lon, ev_lat, ev_lon) #print(distance) model = TauPyModel(model="ak135") arrivals = model.get_pierce_points(ev_depth, distance) #arrivals = earthmodel.get_pierce_points(ev_depth,distance,phase_list=('PP','P^410P')) # compute the vespagram window start_vespa = stime - mini end_vespa = etime - maxi # compare the arrival times with the time window count = 0 k = 0 phase_name_info = [] phase_slowness_info = [] phase_time_info = [] for i_elem in arrivals: #print(i_elem) dummy_phase = arrivals[count] #print(dummy_phase) # phase time in seconds taup_phase_time = dummy_phase.time #print(taup_phase_time) # slowness of the phase taup_phase_slowness = dummy_phase.ray_param_sec_degree # compute the UTC travel phase time taup_phase_time2 = ev_otime + taup_phase_time + phase_shift # print(start_vespa) # print(end_vespa) # print(taup_phase_time2) if start_vespa <= taup_phase_time2 <= end_vespa: # time window if sll <= taup_phase_slowness <= slm: # slowness window # seconds inside the vespagram taup_mark = taup_phase_time2 - start_vespa # store the information phase_name_info.append(dummy_phase.name) phase_slowness_info.append(dummy_phase.ray_param_sec_degree) phase_time_info.append(taup_mark) #print(phases_info[k]) k += 1 count += 1 #print(phase_name_info) phase_slowness_info = np.array(phase_slowness_info) phase_time_info = np.array(phase_time_info) return phase_name_info, phase_slowness_info, phase_time_info
def calculate_depths(i): #start = time.time() lhs = np.ones(( len(discon),)) model = TauPyModel(model="iasp91") #for i in range(f): for j in range(len(discon)): phase = 'S'+ str(discon[j]) + 'p' arrivals = model.get_ray_paths(source_depth_in_km=source_info[i,1], distance_in_degree=source_info[i,0] , phase_list=[phase]) piercing_points = model.get_pierce_points(source_depth_in_km=source_info[i,1], distance_in_degree=source_info[i,0] , phase_list=[phase]) piercing_points_S = model.get_pierce_points(source_depth_in_km=source_info[i,1], distance_in_degree=source_info[i,0] , phase_list=['S']) if piercing_points: t_sp = piercing_points[0].pierce[-1][1] t_s = piercing_points_S[0].pierce[-1][1] lhs[j] = t_s - t_sp file_name = file_info[i] return (lhs, file_name)
def test_pierce_add_depth(self): """ Test pierce points requested at a specific depth. """ model = TauPyModel("iasp91") depth = 1000.0 arrivals = model.get_pierce_points(300.0, 50.0, ["PcP"], add_depth=[depth]) pierce = arrivals[0].pierce assert pierce[3]['depth'] == depth assert abs(pierce[3]['dist'] - 0.051) < 2e-3
def find_pierce_coor(self,plot='False'): import geopy from geopy.distance import VincentyDistance ''' given an instance of the receiver function class this function returns latitude and longitude of all receiver side pierce points of Pds in a given depth range (the default range is 50 - 800 km) NOTE: be careful that ses3d typically uses colatitude, while this function returns latitude ''' depth_range = np.arange(50,800,5) #set range of pierce points #geodetic info bearing = self.az lon_s = self.ses3d_seismogram.sy lat_s = 90.0-self.ses3d_seismogram.sx lon_r = self.ses3d_seismogram.ry lat_r = 90.0-self.ses3d_seismogram.rx origin = geopy.Point(lat_s, lon_s) #find how far away the pierce point is model = TauPyModel(model='pyrolite_5km') for i in range(0,len(depth_range)): phase = 'P'+str(depth_range[i])+'s' pierce = model.get_pierce_points(self.eq_depth,self.delta_deg,phase_list=[phase]) points = pierce[0].pierce for j in range(0,len(points)): if points[j]['depth'] == depth_range[i] and points[j]['dist']*(180.0/np.pi) > 25.0: prc_dist = points[j]['dist']*(180.0/np.pi) d_km = prc_dist * ((2*np.pi*6371.0/360.0)) destination = VincentyDistance(kilometers=d_km).destination(origin,bearing) lat = destination[0] lon = destination[1] value = 0 row = {'depth':depth_range[i],'dist':prc_dist,'lat':lat,'lon':lon,'value':value} self.pierce_dict.append(row) if plot=='True': m = Basemap(projection='hammer',lon_0=0) m.drawmapboundary() m.drawcoastlines() m.drawgreatcircle(lon_s,lat_s,lon_r,lat_r,linewidth=1,color='b',alpha=0.5) for i in range(len(self.pierce_dict)): x,y = m(self.pierce_dict[i]['lon'],self.pierce_dict[i]['lat']) m.scatter(x,y,5,marker='o',color='r') plt.show()
def test_pierce_all_phases(self): """ Tests pierce points against those calculated in TauP. """ filename = os.path.join(DATA, "java_taup_pierce_h10_deg35_ttall") expected = collections.defaultdict(list) with open(filename, "rt") as fh: for line in fh: line = line.strip() if not line: continue if line.startswith(">"): current_phase = line[1:].strip().split()[0] continue dist, depth, time = list(map(float, line.split())) expected[current_phase].append((dist, depth, time)) expected_phases = sorted(set(expected.keys())) m = TauPyModel(model="iasp91") arrivals = m.get_pierce_points(source_depth_in_km=10.0, distance_in_degree=35.0, phase_list=["ttall"]) # Make sure the same stuff is available. arrival_phases = sorted(set([_i.name for _i in arrivals])) self.assertEqual(expected_phases, arrival_phases) actual = collections.defaultdict(list) for arr in arrivals: for p in arr.pierce: actual[arr.name].append(( round(np.degrees(p['dist']), 2), round(p['depth'], 1), round(p['time'], 1))) self.assertEqual(sorted(actual.keys()), sorted(expected.keys())) for key in actual.keys(): actual_values = sorted(actual[key]) expected_values = sorted(expected[key]) self.assertEqual(actual_values, expected_values)
def test_pierce_all_phases(self): """ Tests pierce points against those calculated in TauP. """ filename = os.path.join(DATA, "java_taup_pierce_h10_deg35_ttall") expected = collections.defaultdict(list) with open(filename, "rt") as fh: for line in fh: line = line.strip() if not line: continue if line.startswith(">"): current_phase = line[1:].strip().split()[0] continue dist, depth, time = list(map(float, line.split())) expected[current_phase].append((dist, depth, time)) expected_phases = sorted(set(expected.keys())) m = TauPyModel(model="iasp91") arrivals = m.get_pierce_points(source_depth_in_km=10.0, distance_in_degree=35.0, phase_list=["ttall"]) # Make sure the same stuff is available. arrival_phases = sorted(set([_i.name for _i in arrivals])) self.assertEqual(expected_phases, arrival_phases) actual = collections.defaultdict(list) for arr in arrivals: for p in arr.pierce: actual[arr.name].append(( round(np.degrees(p['dist']), 2), round(p['depth'], 1), round(p['time'], 1))) self.assertEqual(sorted(actual.keys()), sorted(expected.keys())) for key in actual.keys(): actual_values = sorted(actual[key]) expected_values = sorted(expected[key]) self.assertEqual(actual_values, expected_values)
def test_pierce_p_iasp91(self): """ Test single pierce point against output from TauP. """ m = TauPyModel(model="iasp91") arrivals = m.get_pierce_points(source_depth_in_km=10.0, distance_in_degree=35.0, phase_list=["P"]) self.assertEqual(len(arrivals), 1) p_arr = arrivals[0] # Open test file. filename = os.path.join(DATA, "taup_pierce_-h_10_-ph_P_-deg_35") expected = np.genfromtxt(filename, skip_header=1) np.testing.assert_almost_equal(expected[:, 0], np.degrees(p_arr.pierce['dist']), 2) np.testing.assert_almost_equal(expected[:, 1], p_arr.pierce['depth'], 1) np.testing.assert_almost_equal(expected[:, 2], p_arr.pierce['time'], 1)
def test_pierce_p_iasp91(self): """ Test single pierce point against output from TauP. """ m = TauPyModel(model="iasp91") arrivals = m.get_pierce_points(source_depth_in_km=10.0, distance_in_degree=35.0, phase_list=["P"]) self.assertEqual(len(arrivals), 1) p_arr = arrivals[0] # Open test file. filename = os.path.join(DATA, "taup_pierce_-h_10_-ph_P_-deg_35") expected = np.genfromtxt(filename, skip_header=1) np.testing.assert_almost_equal(expected[:, 0], np.degrees(p_arr.pierce['dist']), 2) np.testing.assert_almost_equal(expected[:, 1], p_arr.pierce['depth'], 1) np.testing.assert_almost_equal(expected[:, 2], p_arr.pierce['time'], 1)
def get_pierce_points(st,phase,depth,h5_out): model = TauPyModel(model="prem_50") try: f = h5py.File('/home/samhaug/anaconda2/lib/python2.7/site-packages/seispy'+ h5_out,'w') except IOError: os.rmdir('/home/samhaug/anaconda2/lib/python2.7/site-packages/seispy'+ h5_out) print "Had to remove existing file. Try again" evla = st[0].stats.sac['evla'] evlo = st[0].stats.sac['evlo'] evdp = st[0].stats.sac['evdp'] pierce_list = [] for idx,tr in enumerate(st): print tr a = model.get_pierce_points(evdp,tr.stats.sac['gcarc'],phase_list=[phase]) b = a[0] depth_index = np.argmin(np.abs(b.pierce['depth']-depth)) distance = b.pierce['dist'][depth_index] pierce_list.append((distance,tr.stats.sac['az'])) pierce_array = np.array(pierce_list) f.create_dataset('pierce',data=pierce_array) f.close() return pierce_array
def migrate(self, plot=False): import geopy from geopy.distance import VincentyDistance ''' This is a rewritten function that combines the functions find_pierce_coor and migrate_1d so that it's more efficient. Still in testing stages. RM 2/6/16 ''' depth_range = np.arange(50, 800, 5) #set range of pierce points value = np.zeros((len(depth_range))) #geodetic info bearing = self.az lon_s = self.ses3d_seismogram.sy lat_s = 90.0 - self.ses3d_seismogram.sx lon_r = self.ses3d_seismogram.ry lat_r = 90.0 - self.ses3d_seismogram.rx origin = geopy.Point(lat_s, lon_s) #find how far away the pierce point is model = TauPyModel(model='pyrolite_5km') for i in range(0, len(depth_range)): phase = 'P' + str(depth_range[i]) + 's' pierce = model.get_pierce_points(self.eq_depth, self.delta_deg, phase_list=[phase]) tt = model.get_travel_times(self.eq_depth, self.delta_deg, phase_list=['P', phase]) #in case there's duplicate phase arrivals for j in range(0, len(tt)): if tt[j].name == 'P': p_arr = tt[j].time elif tt[j].name == phase: phase_arr = tt[j].time #determine value Pds_time = phase_arr - p_arr i_start = int((0.0 - self.window_start) / self.ses3d_seismogram.dt) i_t = int(Pds_time / self.ses3d_seismogram.dt) + i_start value[i] = self.prf[i_t] points = pierce[0].pierce for j in range(0, len(points)): if points[j]['depth'] == depth_range[ i] and points[j]['dist'] * (180.0 / np.pi) > 20.0: prc_dist = points[j]['dist'] * (180.0 / np.pi) d_km = prc_dist * ((2 * np.pi * 6371.0 / 360.0)) destination = VincentyDistance( kilometers=d_km).destination(origin, bearing) lat = destination[0] lon = destination[1] row = { 'depth': depth_range[i], 'dist': prc_dist, 'lat': lat, 'lon': lon, 'value': value[i] } self.pierce_dict.append(row) if plot == True: plt.plot(value, depth_range) plt.gca().invert_yaxis() plt.show() return value, depth_range
class receiver_function(object): ####################################################################################### ''' The receiver funciton class contains an obspy stream with the three components of the receiver function. The three trace objects in the stream are, in order: rf_st[0] : transverse component receiver function rf_st[1] : radial component receiver function rf_st[2] : z component receiver function (not usually used) ''' #################################################################################### def __init__(self,tr_e,tr_n,tr_z,**kwargs): #################################################################################### ''' Initialize receiver function params: tr_e :obspy trace object, BHE channel tr_n :obspy trace object, BHN channel tr_z :obspy trace object, BHZ channel **kwargs: taup_model: TauPyModel instance. Passing a pre-existing model speeds things up since it isn't necessary to initialize the the model when creating the receiver function object. taup_model_name: If taup_model = 'none', you can tell it which model it use. The default is prem_5km. window: A tuple describing the time window of the receiver function (times given relative to P). ''' #inherit taup model to avoid initializing the model for every receiver function taup_model = kwargs.get('taup_model','none') taup_model_name = kwargs.get('taup_model_name','ak135') #prem_5km if doing migrations if taup_model == 'none': self.model = TauPyModel(model=taup_model_name) else: self.model = taup_model #cut window centered on P phase self.window = kwargs.get('window',[-10,150]) self.tr_e = phase_window(tr_e,phases=['P'],window_tuple=self.window,taup_model=self.model) self.tr_n = phase_window(tr_n,phases=['P'],window_tuple=self.window,taup_model=self.model) self.tr_z = phase_window(tr_z,phases=['P'],window_tuple=self.window,taup_model=self.model) self.time = np.linspace(self.window[0],self.window[1],len(self.tr_e.data)) self.dt = 1.0/self.tr_e.stats.sampling_rate #make start time zero NOT SURE IF THIS WORKS!!! RM/ 4/13/16 self.tr_e.starttime = 0 self.tr_n.starttime = 0 self.tr_z.starttime = 0 #initialize obspy stream self.rf_st = obspy.Stream(self.tr_e) self.rf_st += self.tr_n self.rf_st += self.tr_z self.gcarc = self.tr_e.stats.sac['gcarc'] self.evdp = self.tr_e.stats.sac['evdp'] self.pierce = [] #read slowness table for moveout correction self.slowness_table = np.loadtxt('/geo/work10/romaguir/seismology/seis_tools/seispy/slowness_table.dat') #get slowness and predicted P410s, P660s arrival times tt = self.model.get_travel_times(source_depth_in_km = self.evdp, distance_in_degree = self.gcarc, phase_list=['P','P410s','P660s']) #just in case there's more than one phase arrival, loop through tt list for i in range(0,len(tt)): if tt[i].name == 'P': self.predicted_p_arr = tt[i].time #ray parameter (horizontal slowness) of incident plane wave self.ray_param = tt[i].ray_param_sec_degree elif tt[i].name == 'P410s': self.predicted_p410s_arr = tt[i].time if tt[i].name == 'P660s': self.predicted_p660s_arr = tt[i].time #event information self.evla = self.tr_e.stats.sac['evla'] self.evlo = self.tr_e.stats.sac['evlo'] self.stla = self.tr_e.stats.sac['stla'] self.stlo = self.tr_e.stats.sac['stlo'] self.gcarc = self.tr_e.stats.sac['gcarc'] self.evdp = self.tr_e.stats.sac['evdp'] #################################################################################### def plot(self): #################################################################################### #make axes----------------------------------------------------------------------- fig,axes = plt.subplots(3,sharex=True,figsize=(12,6)) font = {'family': 'serif', 'color': 'black', 'weight': 'normal', 'size': 16, } #plot data----------------------------------------------------------------------- axes[0].plot(self.time,self.rf_st[0].data,'k') axes[0].grid() axes[0].ticklabel_format(style='sci',scilimits=(0,1),axis='y') axes[1].plot(self.time,self.rf_st[1].data,'k') axes[1].grid() axes[1].ticklabel_format(style='sci',scilimits=(0,1),axis='y') axes[2].plot(self.time,self.rf_st[2].data,'k') axes[2].grid() axes[2].ticklabel_format(style='sci',scilimits=(0,1),axis='y') #plot expected P410s and P660s arrivals t410 = self.predicted_p410s_arr - self.predicted_p_arr t660 = self.predicted_p660s_arr - self.predicted_p_arr axes[0].axvline(t410, color='r') axes[0].axvline(t660, color='r') axes[1].axvline(t410, color='r') axes[1].axvline(t660, color='r') axes[2].axvline(t410, color='r') axes[2].axvline(t660, color='r') #labels-------------------------------------------------------------------------- axes[0].set_ylabel('amplitude',fontdict=font) axes[0].text(0.05,0.85,self.rf_st[0].stats.channel, horizontalalignment='center', verticalalignment='center', transform=axes[0].transAxes, fontdict=font) axes[1].set_ylabel('amplitude',fontdict=font) axes[1].text(0.05,0.85,self.rf_st[1].stats.channel, horizontalalignment='center', verticalalignment='center', transform=axes[1].transAxes, fontdict=font) axes[2].set_ylabel('amplitude',fontdict=font) axes[2].set_xlabel('time after P (s)',fontdict=font) axes[2].text(0.05,0.85,self.rf_st[2].stats.channel, horizontalalignment='center', verticalalignment='center', transform=axes[2].transAxes, fontdict=font) plt.show() #################################################################################### def rotate(self,rotation_method='RTZ'): #################################################################################### #rotate------------------------------------------------------------------------- for i in range(0,len(self.rf_st)): self.rf_st[i].stats.back_azimuth = self.tr_e.stats.sac['baz'] self.rf_st.rotate(method='NE->RT') if rotation_method == 'LQT': r_amp = np.amax(np.amax(self.rf_st[1].data)) z_amp = np.amax(np.amax(self.rf_st[2].data)) incidence_angle = np.arctan(r_amp/z_amp) * (180.0/np.pi) for i in range(0,len(self.rf_st)): self.rf_st[i].stats.inclination = incidence_angle self.rf_st.rotate(method='RT->NE') self.rf_st.rotate(method='ZNE->LQT') #################################################################################### def get_prf(self,decon_type='water_level',wl=0.1,damping=5.0,rotation_method='RTZ'): #################################################################################### #rotate------------------------------------------------------------------------- for tr in self.rf_st: tr.stats.back_azimuth = tr.stats.sac['baz'] for tr in self.rf_st: tr.stats.starttime = 0 self.rf_st.rotate(method='NE->RT') if rotation_method == 'LQT': r_amp = np.amax(np.amax(self.rf_st[1].data)) z_amp = np.amax(np.amax(self.rf_st[2].data)) incidence_angle = np.arctan(r_amp/z_amp) * (180.0/np.pi) print 'using LQT rotation' for i in range(0,len(self.rf_st)): self.rf_st[i].stats.inclination = incidence_angle self.rf_st.rotate(method='RT->NE') self.rf_st.rotate(method='ZNE->LQT') #deconvolve--------------------------------------------------------------------- #divisor should be the L or Z component (depending on rotation method) div = self.rf_st[2].data if decon_type == 'water_level': #self.rf_st[0].data = water_level(self.rf_st[0].data,div,alpha=wl) self.rf_st[1].data = water_level(self.rf_st[1].data,div,alpha=wl) #self.rf_st[2].data = water_level(self.rf_st[2].data,div,alpha=wl) elif decon_type == 'damped_lstsq': #self.rf_st[0].data = damped_lstsq(self.rf_st[0].data,div,damping=damp) #self.rf_st[1].data = damped_lstsq(self.rf_st[1].data,div,damping=damping) self.rf_st[1].data = damped_lstsq(div,self.rf_st[1].data,damping=damping) #self.rf_st[2].data = damped_lstsq(self.rf_st[2].data,div,damping=damp) #TESTING FASTER DECON METHODS 5/24/16 #elif decon_type == 'rf_time_decon': # self.rf_st[1].data = rf_time_decon(self.rf_st[1].data,div) #elif decon_type == 'solve_toeplitz': # self.rf_st[1].data = solve_toeplitz(div,self.rf_st[1].data) elif decon_type == 'use_lsrn': self.rf_st[1].data = use_lsrn(self.rf_st[1].data,div) #center on P-------------------------------------------------------------------- spike = np.exp((-1.0*(self.time)**2)/0.1) spike_omega = np.fft.fft(spike) for i in range(0,len(self.rf_st)): data_omega = np.fft.fft(self.rf_st[i].data) shifted = spike_omega*data_omega self.rf_st[i].data = np.real(np.fft.ifft(shifted)) #normalize on maximum of L component peak_amp = np.max(self.rf_st[2].data) for rf in self.rf_st: rf.data /= peak_amp #TODO write a function to reconvolve and compare misfit #################################################################################### def check_decon(self): #################################################################################### recon = np.convolve(self.rf_st[1],self.tr_z) plt.plot(self.time,recon) plt.plot(self.time,self.tr_e) #################################################################################### def moveout_correction(self): #################################################################################### ''' Moveout correction relative to a reference ray parameter of 6.4 s/deg. This stretches the time axis for smaller ray parameters (larger epicentral distances), and shrinks the time axis for larger ray parameters (smaller epicentral distances). #NOTE 3-16-16, Moveout correction doesn't work properly... The time axis seems to be stretching in the opposite way that it should. ''' p = self.slowness_table[:,0] s = self.slowness_table[:,1] #interpolate with np.interp. make sure values in the first vector are increasing scale = np.interp(self.ray_param,p[::-1],s[::-1]) #print "ray parameter, scale = ",self.ray_param,scale #scale the receiver function and interpolate new time axis new_time = self.time * scale f = interp1d(new_time,self.rf_st[0].data,bounds_error=False,fill_value=0) self.rf_st[0].data = f(self.time) f = interp1d(new_time,self.rf_st[1].data,bounds_error=False,fill_value=0) self.rf_st[1].data = f(self.time) f = interp1d(new_time,self.rf_st[2].data,bounds_error=False,fill_value=0) self.rf_st[2].data = f(self.time) #################################################################################### def migrate_1d(self,**kwargs): #################################################################################### depth = kwargs.get('depth_range',np.arange(50,1005,5)) amp = np.zeros((len(depth))) origin = geopy.Point(self.evla,self.evlo) bearing = self.rf_st[0].stats.sac['az'] ii = 0 for d in depth: phase = 'P'+str(d)+'s' pierce = self.model.get_pierce_points(self.evdp,self.gcarc,phase_list=[phase]) arrs = self.model.get_travel_times(self.evdp,self.gcarc,phase_list=['P',phase]) #in case there's duplicate phase arrivals for arr in arrs: if arr.name == 'P': p_arr = arr.time elif arr.name == phase: pds_arr = arr.time #determine amplitude at each depth window_start = self.window[0] pds_minus_p = pds_arr - p_arr i_start = int((0.0 - window_start)/self.dt) i_t = int(pds_minus_p/self.dt) + i_start amp[ii] = self.rf_st[1].data[i_t] #find pierce points and create pierce dictionary points = pierce[0].pierce for p in points: if p['depth'] == d and np.degrees(p['dist']) > 20.0: prc_dist = np.degrees(p['dist']) d_km = prc_dist * ((2*np.pi*6371.0/360.0)) destination = VincentyDistance(kilometers=d_km).destination(origin,bearing) lat = destination[0] lon = destination[1] row = {'depth':d,'dist':prc_dist,'lat':lat,'lon':lon,'amplitude':amp[ii]} self.pierce.append(row) ii += 1 #################################################################################### def plot_pierce_points(self,depth=410.0,ax='None',**kwargs): #################################################################################### ''' Plots pierce points for a given depth. If an axes object is supplied as an argument it will use it for the plot. Otherwise, a new axes object is created. kwargs: depth: pierce point depth proj: map projection. if none given, a default basemap axis is made, defined by the coordinates of the corners. if 'ortho' is given, a globe centered on Lat_0 and Lon_0 is made. ll_corner_lat: latitude of lower left corner of map ll_corner_lon: longitude of lower left corner of map ur_corner_lat: latitude of upper right corner of map Lat_0: latitude center of ortho Lon_0: logitude center of ortho return_ax: whether or you return the basemap axis object. default False ''' proj=kwargs.get('proj','default') ll_lat=kwargs.get('ll_corner_lat',-35.0) ll_lon=kwargs.get('ll_corner_lon',0.0) ur_lat=kwargs.get('ur_corner_lat',35.0) ur_lon=kwargs.get('ur_corner_lon',120.0) Lat_0=kwargs.get('Lat_0',0.0) Lon_0=kwargs.get('Lon_0',0.0) return_ax =kwargs.get('return_ax',False) if ax == 'None': if proj == 'default': m = Basemap(llcrnrlon=ll_lon,llcrnrlat=ll_lat,urcrnrlon=ur_lon,urcrnrlat=ur_lat) elif proj == 'ortho': m = Basemap(projection='ortho',lat_0=Lat_0,lon_0=Lon_0) m.drawmapboundary() m.drawcoastlines() m.fillcontinents() else: m = ax found_points = False for ii in self.pierce: if ii['depth'] == depth: x,y = m(ii['lon'],ii['lat']) found_points = True if found_points == True: m.scatter(x,y,100,marker='+',color='r',zorder=99) else: print "no pierce points found for the given depth" ############################################################################### def shift(self,phase,ref_deg=64.0): ############################################################################### ''' Shifts the time axis to account for moveout of a given phase params--------------------------------------------------------------------- ref_deg: float, reference epicentral distance phase: string, phase name for which to apply moveout correction ''' t_ref = self.model.ge_travel_times(source_depth_in_km=self.evdp, distance_in_degree=ref_deg, phase_list=["P",phase]) t_arr = self.model.get_travel_times(source_depth_in_km=self.evdp, distance_in_degree=self.gcarc, phase_list=["P",phase]) for arr in t_ref: if arr.name=='P': p_arrival_ref = arr.time elif arr.name==phase: pds_arrival_ref = arr.time for arr in t_arr: if arr.name=='P': p_arrival = arr.time elif arr.name==phase: pds_arrival = arr.time time_shift = (pds_arrival_ref-p_arrival_ref)-(pds_arrival-p_arrival) int_shift = int(time_shift/self.dt) self.rf_st[0].data = np.roll(self.rf_st[0].data,int_shift) self.rf_st[1].data = np.roll(self.rf_st[1].data,int_shift) self.rf_st[2].data = np.roll(self.rf_st[2].data,int_shift) ############################################################################## def zero_pP(self,**kwargs): ############################################################################## ''' Finds predicted pP arrival and zeros a window centered on the arrival kwargs--------------------------------------------------------------------- window_half_dur : half duration of zero window (default = 2.5 s) ''' window_half_dur = kwargs.get('window_half_dur',2.5) arrs = self.model.get_travel_times(source_depth_in_km=self.evdp, distance_in_degree=self.gcarc, phase_list=['P','pP']) P_arr = 'none' pP_arr = 'none' for arr in arrs: if arr.name == 'P': P_arr = arr elif arr.name == 'pP': pP_arr = arr if P_arr == 'none' or pP_arr == 'none': raise ValueError('problem occured in function "zero_pP", no matching arrivals found') else: P_time = P_arr.time pP_time = pP_arr.time delay_time = pP_time - P_time zero_window_center = -1.0*self.window[0] + delay_time zero_window_start = zero_window_center - window_half_dur zero_window_end = zero_window_center + window_half_dur zero_window_start_index = int(zero_window_start/self.dt) zero_window_end_index = int(zero_window_end/self.dt) #case 1: entire window is in range if zero_window_start_index >= 0 and zero_window_end_index <= len(self.rf_st[1].data): self.rf_st[1].data[zero_window_start_index:zero_window_end_index] = 0.0 #case 2: end of window is out of range if zero_window_start_index >= 0 and zero_window_end_index >= len(self.rf_st[1].data): self.rf_st[1].data[zero_window_start_index:] = 0.0 #case 3: entire window is out of range if zero_window_start_index >= len(self.rf_st[1].data): print "pP arrives outside the receiver function window" ############################################################################## def zero_PP(self,**kwargs): ############################################################################## ''' Finds predicted PP arrival and zeros a window centered on the arrival kwargs--------------------------------------------------------------------- window_half_dur : half duration of zero window (default = 2.5 s) ''' window_half_dur = kwargs.get('window_half_dur',2.5) arrs = self.model.get_travel_times(source_depth_in_km=self.evdp, distance_in_degree=self.gcarc, phase_list=['P','PP']) P_arr = 'none' PP_arr = 'none' for arr in arrs: if arr.name == 'P': P_arr = arr elif arr.name == 'PP': PP_arr = arr if P_arr == 'none' or PP_arr == 'none': raise ValueError('problem occured in function "zero_PP", no matching arrivals found') else: P_time = P_arr.time PP_time = PP_arr.time delay_time = PP_time - P_time zero_window_center = -1.0*self.window[0] + delay_time zero_window_start = zero_window_center - window_half_dur zero_window_end = zero_window_center + window_half_dur zero_window_start_index = int(zero_window_start/self.dt) zero_window_end_index = int(zero_window_end/self.dt) #case 1: entire window is in range if zero_window_start_index >= 0 and zero_window_end_index <= len(self.rf_st[1].data): self.rf_st[1].data[zero_window_start_index:zero_window_end_index] = 0.0 #case 2: end of window is out of range if zero_window_start_index >= 0 and zero_window_end_index >= len(self.rf_st[1].data): self.rf_st[1].data[zero_window_start_index:] = 0.0 #case 3: entire window is out of range if zero_window_start_index >= len(self.rf_st[1].data): print "PP arrives outside the receiver function window"
def migrate_1d(rf_trace,**kwargs): #################################################################################### ''' takes an rf trace and returns a dictionary with pierce points and associated reciever function ampltiudes *note it's best to pass a TauPyModel instance (eg, prem_5km), to avoid having to initiate a new model every time you call this function ''' #get kwargs depth = kwargs.get('depth_range',np.arange(50,805,5)) taup_model = kwargs.get('taup_model','None') format = kwargs.get('format','rfh5') window = kwargs.get('window',[-10,100]) #geographical information if format == 'rfh5': gcarc = rf_trace.stats.gcarc dt = rf_trace.stats.delta evla = rf_trace.stats.evla evlo = rf_trace.stats.evlo evdp = rf_trace.stats.evdp stla = rf_trace.stats.stla stlo = rf_trace.stats.stlo az = rf_trace.stats.az o = rf_trace.stats.o #initializations amp = np.zeros((len(depth))) origin = geopy.Point(evla,evlo) bearing = az pierce_dict = [] if taup_model == 'None': taup_model = TauPyModel('prem_5km') ii = 0 for d in depth: phase = 'P'+str(d)+'s' pierce = taup_model.get_pierce_points(evdp,gcarc,phase_list=[phase]) arrs = taup_model.get_travel_times(evdp,gcarc,phase_list=['P',phase]) #in case there's duplicate phase arrivals P_arrs = [] Pds_arrs = [] for arr in arrs: if arr.name == 'P': P_arrs.append(arr) #p_arr = arr.time elif arr.name == phase: #pds_arr = arr.time Pds_arrs.append(arr) p_arr = P_arrs[0].time pds_arr = Pds_arrs[0].time #determine amplitude at each depth #window_start = o #TODO update writeh5py_dict so that win_start win_end are written pds_minus_p = pds_arr - p_arr i_start = int((0.0 - window[0])/dt) i_t = int(pds_minus_p/dt) + i_start amp[ii] = rf_trace.data[i_t] #find pierce points and create pierce dictionary points = pierce[0].pierce for p in points: if p['depth'] == d and np.degrees(p['dist']) > 20.0: prc_dist = np.degrees(p['dist']) d_km = prc_dist * ((2*np.pi*6371.0/360.0)) destination = VincentyDistance(kilometers=d_km).destination(origin,bearing) lat = destination[0] lon = destination[1] row = {'depth':d,'dist':prc_dist,'lat':lat,'lon':lon,'amplitude':amp[ii]} pierce_dict.append(row) ii += 1 return pierce_dict
class receiver_function(object): ####################################################################################### ''' The receiver funciton class contains an obspy stream with the three components of the receiver function. The three trace objects in the stream are, in order: rf_st[0] : transverse component receiver function rf_st[1] : radial component receiver function rf_st[2] : z component receiver function (not usually used) ''' #################################################################################### def __init__(self,tr_e,tr_n,tr_z,**kwargs): #################################################################################### ''' Initialize receiver function params: tr_e :obspy trace object, BHE channel tr_n :obspy trace object, BHN channel tr_z :obspy trace object, BHZ channel **kwargs: taup_model: TauPyModel instance. Passing a pre-existing model speeds things up since it isn't necessary to initialize the the model when creating the receiver function object. taup_model_name: If taup_model = 'none', you can tell it which model it use. The default is prem_5km. window: A tuple describing the time window of the receiver function (times given relative to P). ''' #inherit taup model to avoid initializing the model for every receiver function taup_model = kwargs.get('taup_model','none') taup_model_name = kwargs.get('taup_model_name','ak135') #prem_5km if doing migrations if taup_model == 'none': self.model = TauPyModel(model=taup_model_name) else: self.model = taup_model #cut window centered on P phase self.window = kwargs.get('window',[-10,150]) self.tr_e = phase_window(tr_e,phases=['P'],window_tuple=self.window,taup_model=self.model) self.tr_n = phase_window(tr_n,phases=['P'],window_tuple=self.window,taup_model=self.model) self.tr_z = phase_window(tr_z,phases=['P'],window_tuple=self.window,taup_model=self.model) self.time = np.linspace(self.window[0],self.window[1],len(self.tr_e.data)) self.dt = 1.0/self.tr_e.stats.sampling_rate #make start time zero NOT SURE IF THIS WORKS!!! RM/ 4/13/16 self.tr_e.starttime = 0 self.tr_n.starttime = 0 self.tr_z.starttime = 0 #initialize obspy stream self.rf_st = obspy.Stream(self.tr_e) self.rf_st += self.tr_n self.rf_st += self.tr_z self.gcarc = self.tr_e.stats.sac['gcarc'] self.evdp = self.tr_e.stats.sac['evdp'] self.pierce = [] #read slowness table for moveout correction self.slowness_table = np.loadtxt('/geo/work10/romaguir/seismology/seis_tools/seispy/slowness_table.dat') #get slowness and predicted P410s, P660s arrival times tt = self.model.get_travel_times(source_depth_in_km = self.evdp, distance_in_degree = self.gcarc, phase_list=['P','P410s','P660s']) #just in case there's more than one phase arrival, loop through tt list for i in range(0,len(tt)): if tt[i].name == 'P': self.predicted_p_arr = tt[i].time #ray parameter (horizontal slowness) of incident plane wave self.ray_param = tt[i].ray_param_sec_degree elif tt[i].name == 'P410s': self.predicted_p410s_arr = tt[i].time if tt[i].name == 'P660s': self.predicted_p660s_arr = tt[i].time #event information self.evla = self.tr_e.stats.sac['evla'] self.evlo = self.tr_e.stats.sac['evlo'] self.stla = self.tr_e.stats.sac['stla'] self.stlo = self.tr_e.stats.sac['stlo'] self.gcarc = self.tr_e.stats.sac['gcarc'] self.evdp = self.tr_e.stats.sac['evdp'] #################################################################################### def plot(self): #################################################################################### #make axes----------------------------------------------------------------------- fig,axes = plt.subplots(3,sharex=True,figsize=(12,6)) font = {'family': 'serif', 'color': 'black', 'weight': 'normal', 'size': 16, } #plot data----------------------------------------------------------------------- axes[0].plot(self.time,self.rf_st[0].data,'k') axes[0].grid() axes[0].ticklabel_format(style='sci',scilimits=(0,1),axis='y') axes[1].plot(self.time,self.rf_st[1].data,'k') axes[1].grid() axes[1].ticklabel_format(style='sci',scilimits=(0,1),axis='y') axes[2].plot(self.time,self.rf_st[2].data,'k') axes[2].grid() axes[2].ticklabel_format(style='sci',scilimits=(0,1),axis='y') #plot expected P410s and P660s arrivals t410 = self.predicted_p410s_arr - self.predicted_p_arr t660 = self.predicted_p660s_arr - self.predicted_p_arr axes[0].axvline(t410, color='r') axes[0].axvline(t660, color='r') axes[1].axvline(t410, color='r') axes[1].axvline(t660, color='r') axes[2].axvline(t410, color='r') axes[2].axvline(t660, color='r') #labels-------------------------------------------------------------------------- axes[0].set_ylabel('amplitude',fontdict=font) axes[0].text(0.05,0.85,self.rf_st[0].stats.channel, horizontalalignment='center', verticalalignment='center', transform=axes[0].transAxes, fontdict=font) axes[1].set_ylabel('amplitude',fontdict=font) axes[1].text(0.05,0.85,self.rf_st[1].stats.channel, horizontalalignment='center', verticalalignment='center', transform=axes[1].transAxes, fontdict=font) axes[2].set_ylabel('amplitude',fontdict=font) axes[2].set_xlabel('time after P (s)',fontdict=font) axes[2].text(0.05,0.85,self.rf_st[2].stats.channel, horizontalalignment='center', verticalalignment='center', transform=axes[2].transAxes, fontdict=font) plt.show() #################################################################################### def rotate(self,rotation_method='RTZ'): #################################################################################### #rotate------------------------------------------------------------------------- for i in range(0,len(self.rf_st)): self.rf_st[i].stats.back_azimuth = self.tr_e.stats.sac['baz'] self.rf_st.rotate(method='NE->RT') if rotation_method == 'LQT': r_amp = np.amax(np.amax(self.rf_st[1].data)) z_amp = np.amax(np.amax(self.rf_st[2].data)) incidence_angle = np.arctan(r_amp/z_amp) * (180.0/np.pi) for i in range(0,len(self.rf_st)): self.rf_st[i].stats.inclination = incidence_angle self.rf_st.rotate(method='RT->NE') self.rf_st.rotate(method='ZNE->LQT') #################################################################################### def get_prf(self,decon_type='water_level',wl=0.1,damping=5.0,rotation_method='RTZ'): #################################################################################### #rotate------------------------------------------------------------------------- for tr in self.rf_st: tr.stats.back_azimuth = tr.stats.sac['baz'] for tr in self.rf_st: tr.stats.starttime = 0 self.rf_st.rotate(method='NE->RT') if rotation_method == 'LQT': r_amp = np.amax(np.amax(self.rf_st[1].data)) z_amp = np.amax(np.amax(self.rf_st[2].data)) incidence_angle = np.arctan(r_amp/z_amp) * (180.0/np.pi) print 'using LQT rotation' for i in range(0,len(self.rf_st)): self.rf_st[i].stats.inclination = incidence_angle self.rf_st.rotate(method='RT->NE') self.rf_st.rotate(method='ZNE->LQT') #deconvolve--------------------------------------------------------------------- #divisor should be the L or Z component (depending on rotation method) div = self.rf_st[2].data if decon_type == 'water_level': #self.rf_st[0].data = water_level(self.rf_st[0].data,div,alpha=wl) self.rf_st[1].data = water_level(self.rf_st[1].data,div,alpha=wl) #self.rf_st[2].data = water_level(self.rf_st[2].data,div,alpha=wl) elif decon_type == 'damped_lstsq': #self.rf_st[0].data = damped_lstsq(self.rf_st[0].data,div,damping=damp) #self.rf_st[1].data = damped_lstsq(self.rf_st[1].data,div,damping=damping) self.rf_st[1].data = damped_lstsq(div,self.rf_st[1].data,damping=damping) #self.rf_st[2].data = damped_lstsq(self.rf_st[2].data,div,damping=damp) #TESTING FASTER DECON METHODS 5/24/16 #elif decon_type == 'rf_time_decon': # self.rf_st[1].data = rf_time_decon(self.rf_st[1].data,div) #elif decon_type == 'solve_toeplitz': # self.rf_st[1].data = solve_toeplitz(div,self.rf_st[1].data) #elif decon_type == 'use_lsrn': #self.rf_st[1].data = use_lsrn(self.rf_st[1].data,div) #center on P-------------------------------------------------------------------- spike = np.exp((-1.0*(self.time)**2)/0.1) spike_omega = np.fft.fft(spike) for i in range(0,len(self.rf_st)): data_omega = np.fft.fft(self.rf_st[i].data) shifted = spike_omega*data_omega self.rf_st[i].data = np.real(np.fft.ifft(shifted)) #normalize on maximum of L component peak_amp = np.max(self.rf_st[2].data) for rf in self.rf_st: rf.data /= peak_amp #TODO write a function to reconvolve and compare misfit #################################################################################### def check_decon(self): #################################################################################### recon = np.convolve(self.rf_st[1],self.tr_z) plt.plot(self.time,recon) plt.plot(self.time,self.tr_e) #################################################################################### def moveout_correction(self): #################################################################################### ''' Moveout correction relative to a reference ray parameter of 6.4 s/deg. This stretches the time axis for smaller ray parameters (larger epicentral distances), and shrinks the time axis for larger ray parameters (smaller epicentral distances). #NOTE 3-16-16, Moveout correction doesn't work properly... The time axis seems to be stretching in the opposite way that it should. ''' p = self.slowness_table[:,0] s = self.slowness_table[:,1] #interpolate with np.interp. make sure values in the first vector are increasing scale = np.interp(self.ray_param,p[::-1],s[::-1]) #print "ray parameter, scale = ",self.ray_param,scale #scale the receiver function and interpolate new time axis new_time = self.time * scale f = interp1d(new_time,self.rf_st[0].data,bounds_error=False,fill_value=0) self.rf_st[0].data = f(self.time) f = interp1d(new_time,self.rf_st[1].data,bounds_error=False,fill_value=0) self.rf_st[1].data = f(self.time) f = interp1d(new_time,self.rf_st[2].data,bounds_error=False,fill_value=0) self.rf_st[2].data = f(self.time) #################################################################################### def migrate_1d(self,**kwargs): #################################################################################### depth = kwargs.get('depth_range',np.arange(50,1005,5)) amp = np.zeros((len(depth))) origin = geopy.Point(self.evla,self.evlo) bearing = self.rf_st[0].stats.sac['az'] ii = 0 for d in depth: phase = 'P'+str(d)+'s' pierce = self.model.get_pierce_points(self.evdp,self.gcarc,phase_list=[phase]) arrs = self.model.get_travel_times(self.evdp,self.gcarc,phase_list=['P',phase]) #in case there's duplicate phase arrivals for arr in arrs: if arr.name == 'P': p_arr = arr.time elif arr.name == phase: pds_arr = arr.time #determine amplitude at each depth window_start = self.window[0] pds_minus_p = pds_arr - p_arr i_start = int((0.0 - window_start)/self.dt) i_t = int(pds_minus_p/self.dt) + i_start amp[ii] = self.rf_st[1].data[i_t] #find pierce points and create pierce dictionary points = pierce[0].pierce for p in points: if p['depth'] == d and np.degrees(p['dist']) > 20.0: prc_dist = np.degrees(p['dist']) d_km = prc_dist * ((2*np.pi*6371.0/360.0)) destination = VincentyDistance(kilometers=d_km).destination(origin,bearing) lat = destination[0] lon = destination[1] row = {'depth':d,'dist':prc_dist,'lat':lat,'lon':lon,'amplitude':amp[ii]} self.pierce.append(row) ii += 1 #################################################################################### def plot_pierce_points(self,depth=410.0,ax='None',**kwargs): #################################################################################### ''' Plots pierce points for a given depth. If an axes object is supplied as an argument it will use it for the plot. Otherwise, a new axes object is created. kwargs: depth: pierce point depth proj: map projection. if none given, a default basemap axis is made, defined by the coordinates of the corners. if 'ortho' is given, a globe centered on Lat_0 and Lon_0 is made. ll_corner_lat: latitude of lower left corner of map ll_corner_lon: longitude of lower left corner of map ur_corner_lat: latitude of upper right corner of map Lat_0: latitude center of ortho Lon_0: logitude center of ortho return_ax: whether or you return the basemap axis object. default False ''' proj=kwargs.get('proj','default') ll_lat=kwargs.get('ll_corner_lat',-35.0) ll_lon=kwargs.get('ll_corner_lon',0.0) ur_lat=kwargs.get('ur_corner_lat',35.0) ur_lon=kwargs.get('ur_corner_lon',120.0) Lat_0=kwargs.get('Lat_0',0.0) Lon_0=kwargs.get('Lon_0',0.0) return_ax =kwargs.get('return_ax',False) if ax == 'None': if proj == 'default': m = Basemap(llcrnrlon=ll_lon,llcrnrlat=ll_lat,urcrnrlon=ur_lon,urcrnrlat=ur_lat) elif proj == 'ortho': m = Basemap(projection='ortho',lat_0=Lat_0,lon_0=Lon_0) m.drawmapboundary() m.drawcoastlines() m.fillcontinents() else: m = ax found_points = False for ii in self.pierce: if ii['depth'] == depth: x,y = m(ii['lon'],ii['lat']) found_points = True if found_points == True: m.scatter(x,y,100,marker='+',color='r',zorder=99) else: print "no pierce points found for the given depth" ############################################################################### def shift(self,phase,ref_deg=64.0): ############################################################################### ''' Shifts the time axis to account for moveout of a given phase params--------------------------------------------------------------------- ref_deg: float, reference epicentral distance phase: string, phase name for which to apply moveout correction ''' t_ref = self.model.ge_travel_times(source_depth_in_km=self.evdp, distance_in_degree=ref_deg, phase_list=["P",phase]) t_arr = self.model.get_travel_times(source_depth_in_km=self.evdp, distance_in_degree=self.gcarc, phase_list=["P",phase]) for arr in t_ref: if arr.name=='P': p_arrival_ref = arr.time elif arr.name==phase: pds_arrival_ref = arr.time for arr in t_arr: if arr.name=='P': p_arrival = arr.time elif arr.name==phase: pds_arrival = arr.time time_shift = (pds_arrival_ref-p_arrival_ref)-(pds_arrival-p_arrival) int_shift = int(time_shift/self.dt) self.rf_st[0].data = np.roll(self.rf_st[0].data,int_shift) self.rf_st[1].data = np.roll(self.rf_st[1].data,int_shift) self.rf_st[2].data = np.roll(self.rf_st[2].data,int_shift) ############################################################################## def zero_pP(self,**kwargs): ############################################################################## ''' Finds predicted pP arrival and zeros a window centered on the arrival kwargs--------------------------------------------------------------------- window_half_dur : half duration of zero window (default = 2.5 s) ''' window_half_dur = kwargs.get('window_half_dur',2.5) arrs = self.model.get_travel_times(source_depth_in_km=self.evdp, distance_in_degree=self.gcarc, phase_list=['P','pP']) P_arr = 'none' pP_arr = 'none' for arr in arrs: if arr.name == 'P': P_arr = arr elif arr.name == 'pP': pP_arr = arr if P_arr == 'none' or pP_arr == 'none': raise ValueError('problem occured in function "zero_pP", no matching arrivals found') else: P_time = P_arr.time pP_time = pP_arr.time delay_time = pP_time - P_time zero_window_center = -1.0*self.window[0] + delay_time zero_window_start = zero_window_center - window_half_dur zero_window_end = zero_window_center + window_half_dur zero_window_start_index = int(zero_window_start/self.dt) zero_window_end_index = int(zero_window_end/self.dt) #case 1: entire window is in range if zero_window_start_index >= 0 and zero_window_end_index <= len(self.rf_st[1].data): self.rf_st[1].data[zero_window_start_index:zero_window_end_index] = 0.0 #case 2: end of window is out of range if zero_window_start_index >= 0 and zero_window_end_index >= len(self.rf_st[1].data): self.rf_st[1].data[zero_window_start_index:] = 0.0 #case 3: entire window is out of range if zero_window_start_index >= len(self.rf_st[1].data): print "pP arrives outside the receiver function window" ############################################################################## def zero_PP(self,**kwargs): ############################################################################## ''' Finds predicted PP arrival and zeros a window centered on the arrival kwargs--------------------------------------------------------------------- window_half_dur : half duration of zero window (default = 2.5 s) ''' window_half_dur = kwargs.get('window_half_dur',2.5) arrs = self.model.get_travel_times(source_depth_in_km=self.evdp, distance_in_degree=self.gcarc, phase_list=['P','PP']) P_arr = 'none' PP_arr = 'none' for arr in arrs: if arr.name == 'P': P_arr = arr elif arr.name == 'PP': PP_arr = arr if P_arr == 'none' or PP_arr == 'none': raise ValueError('problem occured in function "zero_PP", no matching arrivals found') else: P_time = P_arr.time PP_time = PP_arr.time delay_time = PP_time - P_time zero_window_center = -1.0*self.window[0] + delay_time zero_window_start = zero_window_center - window_half_dur zero_window_end = zero_window_center + window_half_dur zero_window_start_index = int(zero_window_start/self.dt) zero_window_end_index = int(zero_window_end/self.dt) #case 1: entire window is in range if zero_window_start_index >= 0 and zero_window_end_index <= len(self.rf_st[1].data): self.rf_st[1].data[zero_window_start_index:zero_window_end_index] = 0.0 #case 2: end of window is out of range if zero_window_start_index >= 0 and zero_window_end_index >= len(self.rf_st[1].data): self.rf_st[1].data[zero_window_start_index:] = 0.0 #case 3: entire window is out of range if zero_window_start_index >= len(self.rf_st[1].data): print "PP arrives outside the receiver function window"
def migrate_1d(rf_trace,**kwargs): #################################################################################### ''' takes an rf trace and returns a dictionary with pierce points and associated reciever function ampltiudes *note it's best to pass a TauPyModel instance (eg, prem_5km), to avoid having to initiate a new model every time you call this function ''' #get kwargs depth = kwargs.get('depth_range',np.arange(50,805,5)) taup_model = kwargs.get('taup_model','None') form = kwargs.get('format','rfh5') window = kwargs.get('window',[-10,120]) #geographical information if form == 'rfh5': gcarc = rf_trace.stats.gcarc dt = rf_trace.stats.delta evla = rf_trace.stats.evla evlo = rf_trace.stats.evlo evdp = rf_trace.stats.evdp stla = rf_trace.stats.stla stlo = rf_trace.stats.stlo az = rf_trace.stats.az o = rf_trace.stats.o #initializations amp = np.zeros((len(depth))) origin = geopy.Point(evla,evlo) bearing = az pierce_dict = [] if taup_model == 'None': taup_model = TauPyModel('prem_5km') ii = 0 for d in depth: phase = 'P'+str(d)+'s' pierce = taup_model.get_pierce_points(evdp,gcarc,phase_list=[phase]) arrs = taup_model.get_travel_times(evdp,gcarc,phase_list=['P',phase]) #in case there's duplicate phase arrivals P_arrs = [] Pds_arrs = [] for arr in arrs: if arr.name == 'P': P_arrs.append(arr) #p_arr = arr.time elif arr.name == phase: #pds_arr = arr.time Pds_arrs.append(arr) p_arr = P_arrs[0].time pds_arr = Pds_arrs[0].time #determine amplitude at each depth #window_start = o #TODO update writeh5py_dict so that win_start win_end are written pds_minus_p = pds_arr - p_arr i_start = int((0.0 - window[0])/dt) i_t = int(pds_minus_p/dt) + i_start amp[ii] = rf_trace.data[i_t] #find pierce points and create pierce dictionary points = pierce[0].pierce for p in points: if p['depth'] == d and np.degrees(p['dist']) > 20.0: prc_dist = np.degrees(p['dist']) d_km = prc_dist * ((2*np.pi*6371.0/360.0)) destination = VincentyDistance(kilometers=d_km).destination(origin,bearing) lat = destination[0] lon = destination[1] row = {'depth':d,'dist':prc_dist,'lat':lat,'lon':lon,'amplitude':amp[ii]} pierce_dict.append(row) ii += 1 return pierce_dict
def calculate_ray_coverage(earthquake_list,stations_list,depth_range,phase='S',**kwargs): ''' args: earthquake_list: earthquakes file (same format as used in synth tomo) stations_list: stations file (same format as used in synth tomo) depth_range: tuple (mindepth,maxdepth) kwargs: savefig: True or False figname: str, name of figure (defaults to fig.pdf) plot_title: str, title at the top of the plot (default no title) ''' #the earthquake list format is: eq num, eq lon, eq lat, eq dep #the stations list format is: st lon, st lat savefig = kwargs.get('savefig',True) fig_name = kwargs.get('fig_name','fig.pdf') plot_title = kwargs.get('plot_title','None') fout_name = kwargs.get('fout_name','None') prem = TauPyModel('prem_50km') stations_file = np.loadtxt(stations_list) quakes_file = np.loadtxt(earthquake_list) n_quakes = len(quakes_file) n_stats = len(stations_file) st_lons = stations_file[:,0] st_lats = stations_file[:,1] eq_lons = quakes_file[:,1] eq_lats = quakes_file[:,2] eq_deps = quakes_file[:,3] if phase=='S' or phase=='P': delta_min = 30.0 delta_max = 100.0 elif phase == 'SKS': delta_min = 70.0 delta_max = 140.0 m = Basemap(projection='hammer',lon_0=204) fout = open(fout_name,'w') for i in range(0,n_quakes): #print 'working on earthquake', i for j in range(0,n_stats): geodet = gps2dist_azimuth(eq_lats[i], eq_lons[i], st_lats[j], st_lons[j]) dist_m = geodet[0] dist_deg = kilometer2degrees((dist_m/1000.)) if dist_deg < delta_min: continue elif dist_deg > delta_max: continue az = geodet[1] #print 'eq_lat, eq_lon, st_lat, st_lon, dist_deg', eq_lats[i],eq_lons[i],st_lats[j],st_lons[j],dist_deg arrs = prem.get_pierce_points(source_depth_in_km=eq_deps[i], distance_in_degree=dist_deg, phase_list=[phase]) #print arrs arr = arrs[0] pierce_dict = arr.pierce #items in pierce_dict: 'p' (slowness), 'time' (time in s), 'dist', (distance in rad), 'depth' (depth in km) origin = geopy.Point(eq_lats[i],eq_lons[i]) bearing = az geo_path = [] cross_pt1 = 0 cross_pt2 = 0 dist_max = pierce_dict['dist'][::-1][0] for ds in pierce_dict: #only add points that are past turning depth dist_here = ds[2] if dist_here >= dist_max / 2: time_here = ds[1] depth_here = ds[3] if depth_here == depth_range[1]: dist_deg = np.degrees(ds[2]) dist_km = dist_deg * ((2*np.pi*6371.0/360.0)) geo_pt = VincentyDistance(kilometers=dist_km).destination(origin,bearing) lat_pt = geo_pt[0] lon_pt = geo_pt[1] cross_pt1 = (lon_pt,lat_pt) if depth_here == depth_range[0]: dist_deg = np.degrees(ds[2]) dist_km = dist_deg * ((2*np.pi*6371.0/360.0)) geo_pt = VincentyDistance(kilometers=dist_km).destination(origin,bearing) lat_pt = geo_pt[0] lon_pt = geo_pt[1] cross_pt2 = (lon_pt,lat_pt) if cross_pt1 != 0 and cross_pt2 != 0: m.drawgreatcircle(cross_pt1[0],cross_pt1[1],cross_pt2[0],cross_pt2[1],linewidth=1,alpha=0.15,color='k') fout.write('{} {} {} {}'.format(cross_pt1[0],cross_pt1[1],cross_pt2[0],cross_pt2[1])+'\n') m.drawcoastlines() m.fillcontinents(color='lightgrey') #m.drawparallels(np.arange(-90.,120.,30.)) #m.drawmeridians(np.arange(0.,360.,60.)) if plot_title != 'None': plt.title(plot_title) if savefig: plt.savefig(fig_name) plt.clf() else: plt.show()
def find_pierce_coor(self, plot='False'): import geopy from geopy.distance import VincentyDistance ''' given an instance of the receiver function class this function returns latitude and longitude of all receiver side pierce points of Pds in a given depth range (the default range is 50 - 800 km) NOTE: be careful that ses3d typically uses colatitude, while this function returns latitude ''' depth_range = np.arange(50, 800, 5) #set range of pierce points #geodetic info bearing = self.az lon_s = self.ses3d_seismogram.sy lat_s = 90.0 - self.ses3d_seismogram.sx lon_r = self.ses3d_seismogram.ry lat_r = 90.0 - self.ses3d_seismogram.rx origin = geopy.Point(lat_s, lon_s) #find how far away the pierce point is model = TauPyModel(model='pyrolite_5km') for i in range(0, len(depth_range)): phase = 'P' + str(depth_range[i]) + 's' pierce = model.get_pierce_points(self.eq_depth, self.delta_deg, phase_list=[phase]) points = pierce[0].pierce for j in range(0, len(points)): if points[j]['depth'] == depth_range[ i] and points[j]['dist'] * (180.0 / np.pi) > 25.0: prc_dist = points[j]['dist'] * (180.0 / np.pi) d_km = prc_dist * ((2 * np.pi * 6371.0 / 360.0)) destination = VincentyDistance( kilometers=d_km).destination(origin, bearing) lat = destination[0] lon = destination[1] value = 0 row = { 'depth': depth_range[i], 'dist': prc_dist, 'lat': lat, 'lon': lon, 'value': value } self.pierce_dict.append(row) if plot == 'True': m = Basemap(projection='hammer', lon_0=0) m.drawmapboundary() m.drawcoastlines() m.drawgreatcircle(lon_s, lat_s, lon_r, lat_r, linewidth=1, color='b', alpha=0.5) for i in range(len(self.pierce_dict)): x, y = m(self.pierce_dict[i]['lon'], self.pierce_dict[i]['lat']) m.scatter(x, y, 5, marker='o', color='r') plt.show()