# print tr.stats['station'],tr.stats['channel'],'-1,-1,-1' continue if tr.stats.distance > 20: # only use stations within 20 degrees continue else: pass # numobs = tr.count() # print 'count', numobs # print 'seedid', seedid # print 'sample rate', tr.stats.sampling_rate sample_rate = tr.stats.sampling_rate try: tr.stats.great_circle_distance, azf, azb = gps2DistAzimuth( tr.stats.coordinates.latitude, tr.stats.coordinates.longitude, lat, lon) travel_times = vel_model.get_travel_times( dep, tr.stats.distance, phase_list=['P', 'p', 'Pn', 'S', 'Sn'] ) #,model="iasp91") # need to make more efficient in order # to only calculate once per station #print travel_times, type(travel_times) #try: #for arrival in travel_times: # print arrival.phase.name # arrivals= (item for item in travel_times if item.phase.name=='S' or item.phase.name=='Sn').next() #except StopIteration: # print "WARNING: reference station ",tr.stats['station']," does not have S or Sn phases" P_time = None
def sachdr2assoc(header, pickmap=None): """ Takes a sac header dictionary, and produces a list of up to 10 Assoc instances. Header->phase mappings follow SAC2000, i.e.: * t0: P * t1: Pn * t2: Pg * t3: S * t4: Sn * t5: Sg * t6: Lg * t7: LR * t8: Rg * t9: pP An alternate mapping for some or all picks can be supplied, however, as a dictionary of strings in the above form. Note: arid values will not be filled in, so do: >>> for assoc in kbio.tables['assoc']: assoc.arid = lastarid+1 lastarid += 1 """ pick2phase = { 't0': 'P', 't1': 'Pn', 't2': 'Pg', 't3': 'S', 't4': 'Sn', 't5': 'Sg', 't6': 'Lg', 't7': 'LR', 't8': 'Rg', 't9': 'pP' } #overwrite defaults with supplied map if pickmap: pick2phase.update(pickmap) #geographic relations # obspy.read tries to calculate these values if lcalca is True and needed #header info is there, so we only need to try to if lcalca is False. #XXX: I just calculate it if no values are currently filled in. sac_assoc = [('az', 'esaz'), ('baz', 'seaz'), ('gcarc', 'delta')] assocdict = AttribDict() for hdr, col in sac_assoc: val = header.get(hdr, None) assocdict[col] = val if val != SACDEFAULT[hdr] else None #overwrite if any are None if not assocdict: try: delta = geod.locations2degrees(header['stla'], header['stlo'], header['evla'], header['evlo']) m, seaz, esaz = geod.gps2DistAzimuth(header['stla'], header['stlo'], header['evla'], header['evlo']) assocdict['esaz'] = esaz assocdict['seaz'] = seaz assocdict['delta'] = delta except (ValueError, TypeError): #some sac header values are None pass if header.get('kstnm', None): assocdict['sta'] = header['kstnm'] orid = header.get('norid', None) assocdict['orid'] = orid if orid != SACDEFAULT['norid'] else None #now, do the phase arrival mappings #for each pick in hdr, make a separate dictionary containing assocdict plus #the new phase info. assocs = [] for key in pick2phase: kkey = 'k' + key #if there's a value in t[0-9] if header.get(key, None) not in (SACDEFAULT[key], None): #if the phase name kt[0-9] is null if header[kkey] == SACDEFAULT[kkey]: #take it from the map iassoc = {'phase': pick2phase[key]} else: #take it directly iassoc = {'phase': header[kkey]} iassoc.update(assocdict) assocs.append(iassoc) return assocs
def distaz_query(records, deg=None, km=None, swath=None): """ Out-of-database subset based on distances and/or azimuths. Parameters ---------- records : iterable of objects with lat, lon attribute floats Target of the subset. deg : list or tuple of numbers, optional (centerlat, centerlon, minr, maxr) minr, maxr in degrees or None for unconstrained. km : list or tuple of numbers, optional (centerlat, centerlon, minr, maxr) minr, maxr in km or None for unconstrained. swath : list or tuple of numbers, optional (lat, lon, azimuth, tolerance) Azimuth (from North) +/-tolerance from lat,lon point in degrees. Returns ------- list Subset of supplied records. """ #initial True array to propagate through multiple logical AND comparisons mask0 = np.ones(len(records), dtype=np.bool) if deg: dgen = (geod.locations2degrees(irec.lat, irec.lon, deg[0], deg[1]) \ for irec in records) degrees = np.fromiter(dgen, dtype=float) if deg[2] is not None: mask0 = np.logical_and(mask0, deg[2] <= degrees) if deg[3] is not None: mask0 = np.logical_and(mask0, deg[3] >= degrees) #mask0 = np.logical_and(mask0, mask) if km: #???: this may be backwards mgen = (geod.gps2DistAzimuth(irec.lat, irec.lon, km[0], km[1])[0] \ for irec in records) kilometers = np.fromiter(mgen, dtype=float) / 1e3 #meters, azs, bazs = zip(*valgen) #kilometers = np.array(meters)/1e3 if km[2] is not None: mask0 = np.logical_and(mask0, km[2] <= kilometers) if km[3] is not None: mask0 = np.logical_and(mask0, km[3] >= kilometers) #mask0 = np.logical_and(mask0, mask) if swath is not None: minaz = swath[2] - swath[3] maxaz = swath[2] + swath[3] #???: this may be backwards azgen = (geod.gps2DistAzimuth(irec.lat, irec.lon, km[0], km[1])[1] \ for irec in records) azimuths = np.fromiter(azgen, dtype=float) mask0 = np.logical_and(mask0, azimuths >= minaz) mask0 = np.logical_and(mask0, azimuths <= maxaz) #idx = np.nonzero(np.atleast_1d(mask0))[0] ??? idx = np.nonzero(mask0)[0] recs = [records[i] for i in idx] return recs
def distaz_query(records, deg=None, km=None, swath=None): """ Out-of-database subset based on distances and/or azimuths. Parameters ---------- records : iterable of objects with lat, lon attribute floats Target of the subset. deg : list or tuple of numbers, optional (centerlat, centerlon, minr, maxr) minr, maxr in degrees or None for unconstrained. km : list or tuple of numbers, optional (centerlat, centerlon, minr, maxr) minr, maxr in km or None for unconstrained. swath : list or tuple of numbers, optional (lat, lon, azimuth, tolerance) Azimuth (from North) +/-tolerance from lat,lon point in degrees. Returns ------- list Subset of supplied records. """ #initial True array to propagate through multiple logical AND comparisons mask0 = np.ones(len(records), dtype=np.bool) if deg: dgen = (geod.locations2degrees(irec.lat, irec.lon, deg[0], deg[1]) \ for irec in records) degrees = np.fromiter(dgen, dtype=float) if deg[2] is not None: mask0 = np.logical_and(mask0, deg[2] <= degrees) if deg[3] is not None: mask0 = np.logical_and(mask0, deg[3] >= degrees) #mask0 = np.logical_and(mask0, mask) if km: #???: this may be backwards mgen = (geod.gps2DistAzimuth(irec.lat, irec.lon, km[0], km[1])[0] \ for irec in records) kilometers = np.fromiter(mgen, dtype=float)/1e3 #meters, azs, bazs = zip(*valgen) #kilometers = np.array(meters)/1e3 if km[2] is not None: mask0 = np.logical_and(mask0, km[2] <= kilometers) if km[3] is not None: mask0 = np.logical_and(mask0, km[3] >= kilometers) #mask0 = np.logical_and(mask0, mask) if swath is not None: minaz = swath[2] - swath[3] maxaz = swath[2] + swath[3] #???: this may be backwards azgen = (geod.gps2DistAzimuth(irec.lat, irec.lon, km[0], km[1])[1] \ for irec in records) azimuths = np.fromiter(azgen, dtype=float) mask0 = np.logical_and(mask0, azimuths >= minaz) mask0 = np.logical_and(mask0, azimuths <= maxaz) #idx = np.nonzero(np.atleast_1d(mask0))[0] ??? idx = np.nonzero(mask0)[0] recs = [records[i] for i in idx] return recs
def sachdr2assoc(header, pickmap=None): """ Takes a sac header dictionary, and produces a list of up to 10 Assoc instances. Header->phase mappings follow SAC2000, i.e.: * t0: P * t1: Pn * t2: Pg * t3: S * t4: Sn * t5: Sg * t6: Lg * t7: LR * t8: Rg * t9: pP An alternate mapping for some or all picks can be supplied, however, as a dictionary of strings in the above form. Note: arid values will not be filled in, so do: >>> for assoc in kbio.tables['assoc']: assoc.arid = lastarid+1 lastarid += 1 """ pick2phase = {'t0': 'P', 't1': 'Pn', 't2': 'Pg', 't3': 'S', 't4': 'Sn', 't5': 'Sg', 't6': 'Lg', 't7': 'LR', 't8': 'Rg', 't9': 'pP'} # overwrite defaults with supplied map if pickmap: pick2phase.update(pickmap) # geographic relations # obspy.read tries to calculate these values if lcalca is True and needed # header info is there, so we only need to try to if lcalca is False. # XXX: I just calculate it if no values are currently filled in. sac_assoc = [('az', 'esaz'), ('baz', 'seaz'), ('gcarc', 'delta')] assocdict = AttribDict() for hdr, col in sac_assoc: val = header.get(hdr, None) assocdict[col] = val if val != SACDEFAULT[hdr] else None # overwrite if any are None if not assocdict: try: delta = geod.locations2degrees(header['stla'], header['stlo'], header['evla'], header['evlo']) m, seaz, esaz = geod.gps2DistAzimuth(header['stla'], header['stlo'], header['evla'], header['evlo']) assocdict['esaz'] = esaz assocdict['seaz'] = seaz assocdict['delta'] = delta except (ValueError, TypeError): # some sac header values are None pass if header.get('kstnm', None): assocdict['sta'] = header['kstnm'] orid = header.get('norid', None) assocdict['orid'] = orid if orid != SACDEFAULT['norid'] else None # now, do the phase arrival mappings # for each pick in hdr, make a separate dictionary containing assocdict plus # the new phase info. assocs = [] for key in pick2phase: kkey = 'k' + key # if there's a value in t[0-9] if header.get(key, None) not in (SACDEFAULT[key], None): # if the phase name kt[0-9] is null if header[kkey] == SACDEFAULT[kkey]: # take it from the map iassoc = {'phase': pick2phase[key]} else: # take it directly iassoc = {'phase': header[kkey]} iassoc.update(assocdict) assocs.append(iassoc) return assocs
def plotXcorrEvent(st, stn, stack, maxlag, acausal=False, figurename=None): eventtime = UTCDateTime(1998, 7, 15, 4, 53, 21, 0) # event near MLAC # station locations latP, lonP = 35.41, -120.55 # station PHL latM, lonM = 37.63, -118.84 # station MLAC latE, lonE = 37.55, -118.809 # event 1998 # calculate distance between stations dist = gps2DistAzimuth(latP, lonP, latM, lonM)[0] # between PHL and MLAC distE = gps2DistAzimuth(latP, lonP, latE, lonE)[0] # between event and PHL # # CROSSCORRELATION # reverse stack to plot acausal part (= negative times of correlation) if acausal: stack = stack[::-1] # find center of stack c = int(np.ceil(len(stack) / 2.) + 1) #cut stack to maxlag stack = stack[c - maxlag * int(np.ceil(stn[0].stats.sampling_rate)):c + maxlag * int(np.ceil(stn[0].stats.sampling_rate))] # find new center of stack c2 = int(np.ceil(len(stack) / 2.) + 1) # define time vector for cross correlation limit = (len(stack) / 2.) * stn[0].stats.delta timevec = np.arange(-limit, limit, stn[0].stats.delta) # define timevector: dist / t timevecDist = dist / timevec # EVENT ste = st.copy() st_PHL_e = ste.select(station='PHL') # cut down event trace to 'maxlag' seconds dt = len(stack[c2:]) / stn[0].stats.sampling_rate #xcorrlength st_PHL_e[0].trim(eventtime, eventtime + dt) # create time vector for event signal # extreme values: limit = st_PHL_e[0].stats.npts * st_PHL_e[0].stats.delta timevecSig = np.arange(0, limit, st_PHL_e[0].stats.delta) # PLOTTING fig = plt.figure(figsize=(12.0, 6.0)) ax1 = fig.add_subplot(2, 1, 1) ax2 = fig.add_subplot(2, 1, 2) # plot noise correlation ax1.plot(timevecDist[c2:], stack[c2:], 'k') ax1.set_title('Noise correlation between MLAC and PHL') # plot event near MLAC measured at PHL ax2.plot(distE / timevecSig, st_PHL_e[0].data / np.max(np.abs(st_PHL_e[0].data)), 'r') ax2.set_title('Event near MLAC observed at PHL') ax2.set_xlim((0, 8000)) ax1.set_xlim((0, 8000)) ax2.set_xlabel("group velocity [m/s]") if figurename is not None: fig.savefig(figurename, format="pdf") else: plt.show()
def process_grid(pfile, ofile, atime): tolerance = 0.5 # Tolerance (consider alert correct if within this range; typical value is 0.5) mmi_thresholds = [2.0, 3.0, 4.0, 5.0, 6.0, 7.0] # MMI thresholds true_pos_rate = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] # True positive rate for certain MMI false_pos_rate = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] # False positive rate for certain MMI true_pos_rate2 = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] # True positive rate for certain MMI (timeliness) false_pos_rate2 = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] # False positive rate for certain MMI (timeliness) s_wave_velocity = 3.0 # S-wave velocity (to estimate peak shaking arrival time) pred_grid_data = [] # Predicted grid 2D array (read in from file) obs_grid_data = [] # Observed grid 2D array (read in from file) distance_grid = [] # grid to hold the distances between obs and pred # Data structure to count each type of positive or negative result # when calculating the ROC curve. # https://en.wikipedia.org/wiki/Receiver_operating_characteristic num_true_pos = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] num_false_pos = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] num_true_neg = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] num_false_neg = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] # when considering timeliness (time from eq to station alert) num_true_pos_timeliness = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] num_false_pos_timeliness = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] num_true_neg_timeliness = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] num_false_neg_timeliness = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] with open(pfile, 'r') as predicted_file: pred_data = predicted_file.read() with open(ofile, 'r') as observed_file: obs_data = observed_file.read() # predicted data values pred_header = pred_data.split('\n', 1)[0] pred_grid = pred_data.split('\n') pred_alert_time = atime # for now it is the time passed into the function. Should get from Analysis results pred_eq_lat = pred_header.split(' ')[2] pred_eq_lon = pred_header.split(' ')[3] # observed data values obs_header = obs_data.split('\n', 1)[0] obs_grid = obs_data.split('\n') obs_alert_time = atime # for now it is the time passed into the function. Should get from Shakemap obs_eq_lat = obs_header.split(' ')[2] obs_eq_lon = obs_header.split(' ')[3] # populate pred_grid_data with grid.xyz from predicted shakemap for i, g in enumerate(pred_grid): if i != 0: pred_grid_data.append(g.split(' ')) # populate obs_grid_data with grid.xyz from observed shakemap for i, g in enumerate(obs_grid): if i != 0: obs_grid_data.append(g.split(' ')) pred_grid_size = len(pred_grid_data) obs_grid_size = len(obs_grid_data) # # compute distances grids # for i, pred_data in enumerate(pred_grid_data[:-1]): meters, az, baz = gps.gps2DistAzimuth(float(obs_eq_lat), float(obs_eq_lon), float(pred_grid_data[i][1]), float(pred_grid_data[i][0])) distance_grid.append(meters) # # comparing the two grids not taking into account timeliness # ignoring the timeliness calculations # for j, mmi in enumerate(mmi_thresholds): # j, mmi values = 0 - 4 / 2 - 7 for i, obs_data in enumerate( obs_grid_data[:-1] ): # i = 0 thru obs_grid_size - 1. Last entry is a emtpy string. if float(obs_data[4]) > mmi_thresholds[j]: if float(pred_grid_data[i][4]) + tolerance > mmi_thresholds[j]: num_true_pos[j] = num_true_pos[j] + 1.0 else: num_false_neg[j] = num_false_neg[j] + 1.0 else: if float(pred_grid_data[i][4]) - tolerance > mmi_thresholds[j]: num_false_pos[j] = num_false_pos[j] + 1.0 else: num_true_neg[j] = num_true_neg[j] + 1.0 if num_true_pos[j] + num_false_neg[j] != 0.0: true_pos_rate[j] = num_true_pos[j] / (num_true_pos[j] + num_false_neg[j]) false_pos_rate[j] = num_false_pos[j] / (num_true_pos[j] + num_false_neg[j]) # # Timeliness calculations # for j, mmi in enumerate(mmi_thresholds): # values = 0 - 4 / 2 - 7 for i, obs_data in enumerate( obs_grid_data[:-1] ): # i = 0 thru obs_grid_size - 1. Last entry is emtpy string. dist_km = distance_grid[i] / 1000.0 ground_motion_time = dist_km / s_wave_velocity if float(obs_grid_data[i][4]) > mmi_thresholds[j]: if float(pred_grid_data[i][4]) + tolerance > mmi_thresholds[j]: if float(obs_alert_time) <= float( ground_motion_time ): # obs_alert_time might need to be type cast to float. num_true_pos_timeliness[ j] = num_true_pos_timeliness[j] + 1.0 else: num_false_neg_timeliness[ j] = num_false_neg_timeliness[j] + 1.0 else: num_false_neg_timeliness[ j] = num_false_neg_timeliness[j] + 1.0 else: if float(pred_grid_data[i][4]) - tolerance > mmi_thresholds[j]: num_false_pos_timeliness[ j] = num_false_pos_timeliness[j] + 1.0 else: num_true_neg_timeliness[ j] = num_true_neg_timeliness[j] + 1.0 if num_true_pos_timeliness[j] + num_false_neg_timeliness[j] != 0.0: true_pos_rate2[j] = num_true_pos_timeliness[j] / ( num_true_pos_timeliness[j] + num_false_neg_timeliness[j]) false_pos_rate2[j] = num_false_pos_timeliness[j] / ( num_true_pos_timeliness[j] + num_false_neg_timeliness[j]) print "Non-timeliness" print "true pos rate {}".format(true_pos_rate) print "false pos rate {}".format(false_pos_rate) print "\n" print "Timeliness" print "true pos rate (timeliness) {}".format(true_pos_rate2) print "false pos rate (timeliness) {}".format(false_pos_rate2)