def main(): #pathname = os.path.dirname(sys.argv[0]) #print('path =', pathname) #print('full path =', os.path.abspath(pathname)) exs = Examples() outputs = exs.run_example(3) # if outputs is not None: # for out in outputs: # print(out) exit() write_outputs_to_file(outfile, outputs) for i, out in enumerate(outputs): p1 = NodalPlane(strike=out['strike'], dip=out['dip'], rake=out['rake']) s, d, r = aux_plane(out['strike'], out['dip'], out['rake']) p2 = NodalPlane(strike=s, dip=d, rake=r) fc = FocalMechanism( nodal_planes=NodalPlanes(nodal_plane_1=p1, nodal_plane_2=p2), azimuthal_gap=out['azim_gap'], station_polarity_count=out['station_polarity_count'], station_distribution_ratio=out['stdr'], misfit=out['misfit'], evaluation_mode='automatic', evaluation_status='preliminary', comments=[Comment(text="HASH v1.2 Quality=[%s]" % out['quality'])])
def fill_tensor_from_components(mrr, mtt, mpp, mrt, mrp, mtp, source='unknown', mtype='unknown'): """Fill in moment tensor parameters from moment tensor components. Args: strike (float): Strike from (assumed) first nodal plane (degrees). dip (float): Dip from (assumed) first nodal plane (degrees). rake (float): Rake from (assumed) first nodal plane (degrees). magnitude (float): Magnitude for moment tensor (not required if using moment tensor for angular comparisons.) Returns: dict: Fully descriptive moment tensor dictionary, including fields: - mrr,mtt,mpp,mrt,mrp,mtp Moment tensor components. - T T-axis values: - azimuth (degrees) - plunge (degrees) - N N-axis values: - azimuth (degrees) - plunge (degrees) - P P-axis values: - azimuth (degrees) - plunge (degrees) - NP1 First nodal plane values: - strike (degrees) - dip (degrees) - rake (degrees) - NP2 Second nodal plane values: - strike (degrees) - dip (degrees) - rake (degrees) """ tensor_params = { 'mrr': mrr, 'mtt': mtt, 'mpp': mpp, 'mrt': mrt, 'mrp': mrp, 'mtp': mtp } tensor_params['source'] = source tensor_params['type'] = mtype mt = MomentTensor(mrr, mtt, mpp, mrt, mrp, mtp, 1) tnp1 = mt2plane(mt) np1 = {'strike': tnp1.strike, 'dip': tnp1.dip, 'rake': tnp1.rake} tensor_params['NP1'] = np1.copy() strike2, dip2, rake2 = aux_plane(np1['strike'], np1['dip'], np1['rake']) np2 = {'strike': strike2, 'dip': dip2, 'rake': rake2} tensor_params['NP2'] = np2.copy() T, N, P = mt2axes(mt) tensor_params['T'] = {'azimuth': T.strike, 'value': T.val, 'plunge': T.dip} tensor_params['N'] = {'azimuth': N.strike, 'value': N.val, 'plunge': N.dip} tensor_params['P'] = {'azimuth': P.strike, 'value': P.val, 'plunge': P.dip} return tensor_params
def faulting_style(strike, dip, rake): ''' Assign most physically plausible faulting style given the angles defining one of the nodal planes. Method: find euclidian distances from both nodal planes to the planes of each of the canonical faulting styles, and choose the minimum. Returns ======= faulting_style: str most physically plausible faulting style strike, dip, rake: tuple of float angles defining most physically plausible fault plane ''' try: rake = wrap(rake) if 0 <= wrap(dip) <= 90: candidates = [ focal_mech(dip, rake), focal_mech(*(aux_plane(strike, dip, rake)[1:])) ] return next((faulting_style for faulting_style in candidates if faulting_style in ['normal', 'reverse']), 'strike-slip') return 'undefined' except ValueError: return [faulting_style(s, d, r) for s, d, r in zip(strike, dip, rake)]
def test_aux_plane_735(self): """ Test aux_plane precision issue #735 """ s, d, r = aux_plane(164, 90, -32) self.assertAlmostEqual(s, 254.) self.assertAlmostEqual(d, 58.) self.assertAlmostEqual(r, -180.)
def test_aux_plane_735(self): """ Test aux_plane precision issue #735 """ s, d, r = aux_plane(164, 90, -32) assert round(abs(s - 254.), 7) == 0 assert round(abs(d - 58.), 7) == 0 assert round(abs(r - -180.), 7) == 0
def plot_polarities(event): import warnings import matplotlib.pyplot as plt import mplstereonet from obspy.imaging.beachball import aux_plane fig = plt.figure(figsize=(10, 10)) ax = fig.add_subplot(111, projection='stereonet') up_toa, up_baz, down_toa, down_baz = ([], [], [], []) for pick in event.picks: try: polarity = pick.polarity except AttributeError: continue if polarity == "undecidable": continue origin = event.preferred_origin() or event.origins[-1] # We want the take-off angle and azimuth toa, baz = (None, None) for arrival in origin.arrivals: with warnings.catch_warnings(): warnings.simplefilter("ignore") _pick = arrival.pick_id.get_referred_object() if _pick is not None and _pick == pick: toa = arrival.takeoff_angle baz = arrival.azimuth if not (toa and baz): continue if 0. <= toa < 90.: toa = 90. - toa # complement for downward angles elif 90. <= toa <= 180.: toa = 270. - toa # project upward angles baz -= 90 # Calculate strike azi from direct (dip-pointing) azi if polarity == "positive": up_toa.append(toa) up_baz.append(baz) elif polarity == "negative": down_toa.append(toa) down_baz.append(baz) ax.rake(up_baz, up_toa, 90, "ro", label="Compressional") ax.rake(down_baz, down_toa, 90, "bo", label="Dilatational") for fm in event.focal_mechanisms: if fm.nodal_planes: if fm.nodal_planes.nodal_plane_1: _np = fm.nodal_planes.nodal_plane_1 ax.plane(_np.strike, _np.dip, "k") if fm.nodal_planes.nodal_plane_2: _np = fm.nodal_planes.nodal_plane_2 ax.plane(_np.strike, _np.dip, "k") elif fm.nodal_planes.nodal_plane_1: # Calculate the aux plane _np = fm.nodal_planes.nodal_plane_1 _str, _dip, _rake = aux_plane(_np.strike, _np.dip, _np.rake) ax.plane(_str, _dip, "k") ax.legend() return fig
def test_aux_plane(self): """ Test aux_plane function - all values are taken from MatLab. """ # https://en.wikipedia.org/wiki/File:USGS_sumatra_mts.gif s1 = 132.18005257215460 d1 = 84.240987194376590 r1 = 98.963372641038790 (s2, d2, r2) = aux_plane(s1, d1, r1) assert round(abs(s2 - 254.64386091007400), 7) == 0 assert round(abs(d2 - 10.641291652406172), 7) == 0 assert round(abs(r2 - 32.915578422454380), 7) == 0 # s1 = 160.55 d1 = 76.00 r1 = -46.78 (s2, d2, r2) = aux_plane(s1, d1, r1) assert round(abs(s2 - 264.98676854650216), 7) == 0 assert round(abs(d2 - 45.001906942415623), 7) == 0 assert round(abs(r2 - -159.99404307049076), 7) == 0
def test_aux_plane(self): """ Test aux_plane function - all values are taken from MatLab. """ # https://en.wikipedia.org/wiki/File:USGS_sumatra_mts.gif s1 = 132.18005257215460 d1 = 84.240987194376590 r1 = 98.963372641038790 (s2, d2, r2) = aux_plane(s1, d1, r1) self.assertAlmostEqual(s2, 254.64386091007400) self.assertAlmostEqual(d2, 10.641291652406172) self.assertAlmostEqual(r2, 32.915578422454380) # s1 = 160.55 d1 = 76.00 r1 = -46.78 (s2, d2, r2) = aux_plane(s1, d1, r1) self.assertAlmostEqual(s2, 264.98676854650216) self.assertAlmostEqual(d2, 45.001906942415623) self.assertAlmostEqual(r2, -159.99404307049076)
def convert_to_antelope(M, cenloc): """ Convert Roberto's wphase output to antelope's moment tensor format. Plus calculate a few values based on the moment tensor. :param M: Moment tensor :param cenloc: The centroid location, (cenlat,cenlon,cendep) :return: antelope's moment tensor info in a dictionary. """ M2 = np.asarray(M)**2 m0 = np.sqrt(0.5*(M2[0]+M2[1]+M2[2])+M2[3]+M2[4]+M2[5]) mag = 2./3.*(np.log10(m0)-9.10) np1 = mt2plane(MomentTensor(M, 0)) np2 = aux_plane(np1.strike, np1.dip, np1.rake) results = model.AntelopeMomentTensor( tmpp=M[2], tmrp=M[4], tmrr=M[0], tmrt=M[3], tmtp=M[5], tmtt=M[1], scm=m0, drmag=mag, drmagt='Mww', drlat=cenloc[0], drlon=cenloc[1], drdepth=cenloc[2], str1=np1.strike, dip1=np1.dip, rake1=np1.rake, str2=np2[0], dip2=np2[1], rake2=np2[2], auth=settings.AUTHORITY, ) try: results.dc, results.clvd = decomposeMT(M) except Exception: import traceback logger.warning("Error computing DC/CLVD decomposition: %s", traceback.format_exc()) return results
def mt2parameters(MTx,M,gfscale): #Iso Mo MoIso = (MTx[0][0] + MTx[1][1] + MTx[2][2])/3 c = MomentTensor(MTx[2][2],MTx[0][0],MTx[1][1],MTx[0][2],-MTx[1][2],-MTx[0][1],gfscale) # Principal axes (T, N, P) = mt2axes(c) # Nodal planes np0 = mt2plane(c) np2 = aux_plane(np0.strike,np0.dip,np0.rake) # Convention rake: up-down if (np0.rake>180): np0.rake= np0.rake-360 if (np0.rake<-180): np0.rake= np0.rake+360 np1 = np.zeros(3.) np1 = [np0.strike,np0.dip,np0.rake] # Compute Eigenvectors and Eigenvalues # Seismic Moment and Moment Magnitude (EigVal, EigVec) = np.linalg.eig(MTx) b = copy.deepcopy(EigVal) b.sort() Mo = (abs(b[0]) + abs(b[2]))/2. Mw = math.log10(Mo)/1.5-10.73 # Compute double-couple, CLVD & iso d = copy.deepcopy(EigVal) d[0]=abs(d[0]) d[1]=abs(d[1]) d[2]=abs(d[2]) d.sort() eps=abs(d[0])/abs(d[2]) pcdc=100.0*(1.0-2.0*eps) pcclvd=200.0*eps pcdc=pcdc/100.0 pcclvd=pcclvd/100.0 pciso=abs(MoIso)/Mo pcsum=pcdc+pcclvd+pciso pcdc=100.0*pcdc/pcsum pcclvd=100.0*pcclvd/pcsum pciso=100.0*pciso/pcsum Pdc = pcdc Pclvd = pcclvd return (Mo, Mw, Pdc, Pclvd, EigVal, EigVec, T, N, P, np1, np2)
def test_stereo(azimuths, takeoffs, polarities, sdr=[], event_info=None): ''' Plots points with given azimuths, takeoff angles, and polarities on a stereonet. Will also plot both planes of a double-couple given a strike/dip/rake ''' fig = plt.figure() ax = fig.add_subplot(111, projection='stereonet') up = polarities > 0 dn = polarities < 0 plot_upper_hemisphere = True # This assumes takeoffs are measured wrt vertical up # so that i=0 (vertical) up has plunge=90: # ax.line(plunge, trend) - where plunge=90 plots at center and # plunge=0 at edge h_rk = ax.line(90. - takeoffs[up], azimuths[up], 'bo') # compressional # first arrivals h_rk = ax.line(90. - takeoffs[dn], azimuths[dn], 'go', fillstyle='none') if sdr: s1, d1, r1 = sdr[0], sdr[1], sdr[2] s2, d2, r2 = aux_plane(*sdr) if plot_upper_hemisphere: s1 += 180. s2 += 180. h_rk = ax.plane(s1, d1, 'g') ax.pole(s1, d1, 'gs', markersize=7) h_rk = ax.plane(s2, d2, 'b') ax.pole(s2, d2, 'bs', markersize=7) ax.grid(True) plt.title("upper hemisphere") title = event_info + " (s,d,r)=(%.1f, %.1f, %.1f)" % (s1, d1, r1) if title: plt.suptitle(title) # plt.show() return plt.gcf()
def test_stereo(azimuths, takeoffs, polarities, sdr=[]): ''' Plots points with given azimuths, takeoff angles, and polarities on a stereonet. Will also plot both planes of a double-couple given a strike/dip/rake ''' import matplotlib.pyplot as plt import mplstereonet from obspy.imaging.beachball import aux_plane print("azimuths:") print(azimuths) print("takeoffs:") print(takeoffs) print("polarities:") print(polarities) fig = plt.figure() ax = fig.add_subplot(111, projection='stereonet') up = polarities > 0 dn = polarities < 0 #h_rk = ax.rake(azimuths[up]-90.,takeoffs[up],90, 'ro') # MTH: this seems to put the observations in the right location # We're plotting a lower-hemisphere focal mech, and we have to convert # the up-going rays to the right az/dip quadrants: h_rk = ax.rake(azimuths[up] - 90. + 180., 90. - takeoffs[up], 90, 'ro') h_rk = ax.rake(azimuths[dn] - 90. + 180., 90. - takeoffs[dn], 90, 'b+') #ax.rake(strike-90., 90.-dip, rake, 'ro', markersize=14) #h_rk = ax.rake(azimuths[dn]-90.,takeoffs[dn],90, 'b+') print("HERE we ARE") if sdr: s2, d2, r2 = aux_plane(*sdr) h_rk = ax.plane(sdr[0], sdr[1], 'g') h_rk = ax.rake(sdr[0], sdr[1], -sdr[2], 'go') h_rk = ax.plane(s2, d2, 'g') plt.show()
hash_solns = read_hash_solutions("example1.out") hash_focal = rad2deg(hash_solns[event]) mechs = genfromtxt("3146815_realizations.out")[:,:3] polarity_data = read_demo("north1.phase", "scsn.reverse", reverse=True) fig = plt.figure(facecolor="white") ax = fig.add_subplot(111, projection='stereonet') for mech in mechs[:10]: strike, dip, rake = mech ax.plane(strike-90, dip, '-r', linewidth=2) strike, dip, rake = aux_plane(*mech) ax.plane(strike-90, dip, '-r', linewidth=2) strike, dip, rake = hash_focal ax.plane(strike-90, dip, '-b', linewidth=2) strike, dip, rake = aux_plane(*hash_focal) ax.plane(strike-90, dip, '-b', linewidth=2) azi = rad2deg(polarity_data[event][:,0]) toa = rad2deg(polarity_data[event][:,1]) polarity = polarity_data[event][:,2] for a, t, p in zip(azi, toa, polarity): if p > 0: ax.pole(a, t,'o', markeredgecolor='red', markerfacecolor='red')
def calc(cat, settings): """ Prepare input arrays needed to calculate focal mechanisms and pass these into hashwrap.hashwrapper Return list of obspy focalmechanisms & list of matplotlib figs :param cat: obspy.core.event.Catalog :type list: list of obspy.core.event.Events or microquake.core.event.Events :param settings:hash settings :type settings dictionary :returns: obsy_focal_mechanisms, matplotlib_figures :rtype: list, list """ fname = 'calc_focal_mechanism' plot_focal_mechs = settings.plot_focal_mechs sname = [] p_pol = [] p_qual = [] qdist = [] qazi = [] qthe = [] sazi = [] sthe = [] events = [] for event in cat: event_dict = {} origin = event.preferred_origin() event_dict['event_info'] = origin.time.datetime.strftime('%Y-%m-%d ' '%H:%M:%S') event_dict['event'] = {} event_dict['event']['qdep'] = origin.loc[2] event_dict['event']['sez'] = 10. event_dict['event']['icusp'] = 1234567 arrivals = [ arr for arr in event.preferred_origin().arrivals if arr.phase == 'P' ] for arr in arrivals: if not arr.get_pick(): logger.warning(f"Missing pick for arrival {arr.resource_id} on" f" event {event.resource_id}") continue if arr.get_pick().snr is None: logger.warning( "%s P arr pulse_snr == NONE !!!" % arr.pick_id.get_referred_object().waveform_id.station_code) continue if arr.polarity is None: continue sname.append( arr.pick_id.get_referred_object().waveform_id.station_code) p_pol.append(arr.polarity) qdist.append(arr.distance) qazi.append(arr.azimuth) # MTH: both HASH and test_stereo expect takeoff theta measured wrt # vertical Up! qthe.append(180. - arr.takeoff_angle) sazi.append(2.) sthe.append(10.) if arr.get_pick().snr <= 6: qual = 0 else: qual = 1 p_qual.append(qual) event_dict['sname'] = sname event_dict['p_pol'] = p_pol event_dict['p_qual'] = p_qual event_dict['qdist'] = qdist event_dict['qazi'] = qazi event_dict['qthe'] = qthe event_dict['sazi'] = sazi event_dict['sthe'] = sthe events.append(event_dict) outputs = calc_focal_mechanisms(events, settings, phase_format='FPFIT') focal_mechanisms = [] plot_figures = [] for i, out in enumerate(outputs): logger.info("%s.%s: Process Focal Mech i=%d" % (__name__, fname, i)) p1 = NodalPlane(strike=out['strike'], dip=out['dip'], rake=out['rake']) s, d, r = aux_plane(out['strike'], out['dip'], out['rake']) p2 = NodalPlane(strike=s, dip=d, rake=r) fc = FocalMechanism( nodal_planes=NodalPlanes(nodal_plane_1=p1, nodal_plane_2=p2), azimuthal_gap=out['azim_gap'], station_polarity_count=out['station_polarity_count'], station_distribution_ratio=out['stdr'], misfit=out['misfit'], evaluation_mode='automatic', evaluation_status='preliminary', comments=[Comment(text="HASH v1.2 Quality=[%s]" % out['quality'])]) focal_mechanisms.append(fc) event = events[i] title = "%s (s,d,r)_1=(%.1f,%.1f,%.1f) _2=(%.1f,%.1f,%.1f)" % \ (event['event_info'], p1.strike, p1.dip, p1.rake, p2.strike, p2.dip, p2.rake) if plot_focal_mechs: gcf = test_stereo(np.array(event['qazi']), np.array(event['qthe']), np.array(event['p_pol']), sdr=[p1.strike, p1.dip, p1.rake], event_info=event['event_info']) # sdr=[p1.strike,p1.dip,p1.rake], title=title) plot_figures.append(gcf) return focal_mechanisms, plot_figures
pth6 = c.collections[0].get_paths()[1].vertices pth6 = rad2deg(pth6) hash_focal3 = rad2deg(hash_solns[event3]) fig = plt.figure(facecolor="white", figsize=(10, 20)) ax = fig.add_subplot(221, projection='stereonet') ax.rake(pth1[:, 0], pth1[:, 1] + 90.0, 90.0, ':', color='red', linewidth=3) ax.rake(pth2[:, 0], pth2[:, 1] + 90.0, 90.0, ':', color='red', linewidth=3) strike, dip, rake = svm_soln ax.plane(strike, dip, '-r', linewidth=2) strike, dip, rake = aux_plane(*svm_soln) ax.plane(strike, dip, '-r', linewidth=2) strike, dip, rake = hash_focal ax.plane(strike - 90, dip, 'g-', linewidth=2) strike, dip, rake = aux_plane(*hash_focal) ax.plane(strike - 90, dip, 'g-', linewidth=2) azi = rad2deg(polarity_data[event][:, 0]) toa = rad2deg(polarity_data[event][:, 1]) polarity = polarity_data[event][:, 2] for a, t, p in zip(azi, toa, polarity): if p > 0: ax.pole(a, t, 'o', markeredgecolor='red', markerfacecolor='red')
hash_focal_nr = rad2deg(hash_solns_nr[event]) fig = plt.figure(facecolor="white") ax = fig.add_subplot(111, projection='stereonet') indx = mech > 0 # reflect the points in the lower half space #ax.pole(rad2deg(longi[indx]), rad2deg(lati[indx])) #ax.rake(pth1[:,0], pth1[:,1] +90.0, 90.0, '-', color='red', linewidth=3) #ax.rake(pth2[:,0], pth2[:,1] +90.0, 90.0, '-', color='red', linewidth=3) strike, dip, rake = svm_soln ax.plane(round(strike), dip, '-r', linewidth=2) strike, dip, rake = aux_plane(*svm_soln) ax.plane(strike, dip, '-r', linewidth=2) strike, dip, rake = svm_soln_nr ax.plane(round(strike), dip, '--r', linewidth=2) strike, dip, rake = aux_plane(*svm_soln_nr) ax.plane(strike, dip, '--r', linewidth=2) strike, dip, rake = hash_focal ax.plane(strike - 90, dip, 'g-', linewidth=2) strike, dip, rake = aux_plane(*hash_focal) ax.plane(strike - 90, dip, 'g-', linewidth=2) strike, dip, rake = hash_focal_nr
def write_source_models(version=0, full=False, use_recomputed=False, prefix='nt2012'): ''' Writes all source models. ''' # compute some filenames if use_recomputed: smoothed_data_path = RECOMPUTED_DATA_PATH smoothed_prefix = 'recomputed' else: smoothed_data_path = ORIGINAL_DATA_PATH smoothed_prefix = prefix layers_df = pd.read_csv(LAYERS_FORMAT % version, index_col='layerid') # load electronic supplement for areal zones df_erroneous = pd.read_csv(StringIO('''\ zoneid layerid strike dip rake 14 1 228 69 330 914 1 192 46 124 '''), sep=r'\s+', index_col='zoneid') print('Reading areal polygons and seismicity statistics for each layer') areal_dfs = [] for layer_id in layers_df.index: # read seismicity and polygons and join them seismicity_file = os.path.join(ORIGINAL_DATA_PATH, SEISMICITY_FORMAT % layer_id) print('Reading: ' + os.path.abspath(seismicity_file)) seismicity_df = pd.read_csv(seismicity_file) seismicity_df.set_index('zoneid', inplace=True, verify_integrity=True) seismicity_df.rename(columns=SEISMICITY_ALIASES, inplace=True) # preserve errors in electonic supplement in version v0 if int(version) == 0: if layer_id == 4: (seismicity_df.loc[169], seismicity_df.loc[170]) = \ (seismicity_df.loc[170].copy(), seismicity_df.loc[169].copy()) print('Swapped seismicity parameters for zones 169 and 170.') for zoneid, row in df_erroneous[df_erroneous.layerid == layer_id].iterrows(): row = row.drop('layerid') for column in row.keys(): seismicity_df.loc[zoneid, column] = row[column] print('Restored zone %d erroneous %s: %s' % (zoneid, row.keys().values, row.values)) polygon_file = os.path.join(ORIGINAL_DATA_PATH, POLYGON_FORMAT % layer_id) print('Reading: ' + os.path.abspath(polygon_file)) polygon_df = read_polygons(polygon_file) polygon_df.set_index('zoneid', inplace=True, verify_integrity=True) df = seismicity_df.join(polygon_df, how='outer') # add layer info df.insert(0, 'layerid', layer_id) areal_dfs.append(df) # put it all together columns = list( unique_everseen([column for column in df.columns for df in areal_dfs])) areal_df = pd.concat(areal_dfs, sort=True)[columns].sort_index() # auxiliary information aux_file = AUX_FORMAT % int(version) print('\nReading: ' + os.path.abspath(aux_file)) aux_df = pd.read_csv(aux_file, index_col='zoneid').sort_index() assert (areal_df.index == aux_df.index).all() if 'layerid' in aux_df: aux_df.drop(columns='layerid', inplace=True) areal_df = areal_df.join(aux_df) # assign undefined focal mechanisms as reverse faulting - shouldn't matter undefined = areal_df['dip'] == -1 areal_df.loc[undefined, 'rake'] = 90 areal_df.loc[undefined, 'dip'] = 45 areal_df.loc[undefined, 'strike'] = 0 # augment areal zone description tables areal_df = areal_df.join(layers_df, on='layerid') areal_df['rake'] = wrap(areal_df['rake']) areal_df['mechanism'] = focal_mech(areal_df['dip'], areal_df['rake']) areal_df['new style'] = faulting_style(areal_df['strike'], areal_df['dip'], areal_df['rake']) areal_df['strike2'], areal_df['dip2'], areal_df['rake2'] = zip(*[ aux_plane(strike, dip, rake) for strike, dip, rake in zip( areal_df['strike'], areal_df['dip'], areal_df['rake']) ]) areal_df['mechanism2'] = focal_mech(areal_df['dip2'], areal_df['rake2']) areal_df['mmin'] = MIN_MAGS[0] areal_df['strike2'] = areal_df['strike2'].round(1) areal_df['dip2'] = areal_df['dip2'].round(1) areal_df['rake2'] = areal_df['rake2'].apply(wrap) areal_df['rake2'] = areal_df['rake2'].round(1) areal_df['rake2'] = areal_df['rake2'].apply(wrap) swap = areal_df['focal plane'] == 'secondary' print('Treating %d focal planes as secondary: %s' % (sum(swap), ', '.join(str(item) for item in areal_df.index[swap]))) for column in ['strike', 'dip', 'rake', 'mechanism']: areal_df.loc[swap, [column, column + '2']] = \ areal_df.loc[swap, [column + '2', column]].values # grab mmax and bvalue from zone above if mmax zero for this zone check_keys = ['mmax', 'b'] none_found = True for i, area_series in areal_df[(areal_df[check_keys] == 0).any( axis=1)].iterrows(): alternate_zone = int(area_series.name / 10) for key in check_keys: if area_series['a'] != 0 and area_series[key] == 0: print('For zone %d taking %s from zone %d' % (area_series.name, key, alternate_zone)) areal_df.at[i, key] = areal_df.at[alternate_zone, key] none_found = False if none_found: print('SUCCESS: All zones already have mmax & b defined.') # write areal CSV areal_source_model_base = AREAL_MODEL_FORMAT % (prefix, int(version)) areal2csv(areal_df, areal_source_model_base) # write areal NRML mark = time() df2nrml(areal_df, areal_source_model_base) print('Finished writing areal model to NRML: %s\n' % pd.to_timedelta(time() - mark, unit='s')) # read logic tree description table source_tree_tsv = SOURCE_TREE_FORMAT % int(version) print('Logic tree before collapse:') source_tree_symbolic_df = read_tree_tsv(source_tree_tsv) print(source_tree_symbolic_df) # compute collapsed rates areal_collapsed_df, collapsed_tree_df, _, _ = \ collapse_sources(areal_df, source_tree_symbolic_df) print('Logic tree after collapse:') print(collapsed_tree_df) # write areal sources to NRML mark = time() areal_collapsed_model_base = areal_source_model_base + ' collapsed' df2nrml(areal_collapsed_df, areal_collapsed_model_base) print('Finished writing collapsed areal model to NRML: %s\n' % pd.to_timedelta(time() - mark, unit='s')) # completeness tables print('Reading completeness tables.') completeness_df = pd.read_csv( '../Data/thingbaijam2011seismogenic/Table1.csv', header=[0, 1], index_col=[0, 1]) completeness_df.columns = [ ' '.join(col).strip() for col in completeness_df.columns.values ] # electronic supplement for smoothed-gridded model print('Reading smoothed seismicity data ...') smoothed_data_format = os.path.join(smoothed_data_path, SMOOTHED_FORMAT) mark = time() smoothed_df_list = [] for i, min_mag in enumerate(MIN_MAGS): layer_smoothed_df_list = [] for layer_id, layer in layers_df.join(completeness_df, on=['zmin', 'zmax']).iterrows(): layer_smoothed_df = pd.read_csv(smoothed_data_format % (layer_id, min_mag)) nu_mag = 'nu%s' % str(min_mag).replace('.', '_') rename_cols = {nu_mag: 'nu', 'lat': 'latitude', 'lon': 'longitude'} layer_smoothed_df.rename(columns=rename_cols, inplace=True) layer_smoothed_df['layerid'] = layer_id layer_smoothed_df['mmin model'] = min_mag layer_smoothed_df['mmin'] = min_mag layer_smoothed_df['duration'] = (layer[str(min_mag) + ' end'] - layer[str(min_mag) + ' start'] + 1) if use_recomputed: layer_smoothed_df['lambda'] = layer_smoothed_df['nu'] layer_smoothed_df['nu'] = (layer_smoothed_df['lambda'] * layer_smoothed_df['duration']) else: layer_smoothed_df['lambda'] = (layer_smoothed_df['nu'] / layer_smoothed_df['duration']) layer_smoothed_df_list.append(layer_smoothed_df) layer_smoothed_df = pd.concat(layer_smoothed_df_list, ignore_index=True) smoothed_df_list.append(layer_smoothed_df) smoothed_df = pd.concat(smoothed_df_list, ignore_index=True) len_smoothed = smoothed_df.shape[0] smoothed_df.sort_values(['layerid', 'mmin model', 'longitude', 'latitude']) smoothed_df['geometry'] = [ Point(longitude, latitude) for longitude, latitude in zip( smoothed_df['longitude'], smoothed_df['latitude']) ] smoothed_df = gpd.GeoDataFrame(smoothed_df, crs='WGS84') print('Read %d point sources from %d files: %s\n' % (len(smoothed_df), len(MIN_MAGS) * len(layers_df), pd.to_timedelta(time() - mark, unit='s'))) # associate smoothed-gridded points with zones # we are only interested in active zones active_areal_df = areal_df[areal_df['a'] != 0].reset_index() print('Associate point sources in areal zones with those zones ...') # quick, requires no transformations mark = time() smoothed_df['distance'] = np.inf smoothed_dfs = [] for layer_id in layers_df.index: smoothed_layer_df = smoothed_df[smoothed_df['layerid'] == layer_id] areal_layer_df = gpd.GeoDataFrame( active_areal_df[active_areal_df['layerid'] == layer_id], crs='WGS84') smoothed_layer_df = gpd.sjoin( smoothed_layer_df, areal_layer_df[['zoneid', 'a', 'geometry']], how='left', op='within') smoothed_dfs.append(smoothed_layer_df) smoothed_df = pd.concat(smoothed_dfs) smoothed_df.drop(columns='index_right', inplace=True) smoothed_df['in zoneid'] = smoothed_df['zoneid'].copy() assigned = (~np.isnan(smoothed_df['in zoneid'])) & (smoothed_df['a'] != 0) smoothed_df.loc[assigned, 'distance'] = 0 print('Spatial join accounted for %.2f%% of sources: %s\n' % (100 * len(smoothed_df[assigned]) / len(smoothed_df), pd.to_timedelta(time() - mark, unit='s'))) unassigned_zones = ( set(active_areal_df.zoneid.unique()) - set(smoothed_df[pd.notnull(smoothed_df.zoneid)].zoneid.unique())) if list(unassigned_zones): raise RuntimeError('Zones not assigned to any point: ' + str(sorted(list(unassigned_zones)))) else: print('SUCCESS: All active areal zones assigned to at least one point') # no point should be associated with multiple zones id_columns = ['latitude', 'longitude', 'layerid', 'mmin'] duplicated_df = smoothed_df[smoothed_df.duplicated( subset=id_columns, keep=False)].sort_values(id_columns + ['zoneid']) if duplicated_df.empty: print('SUCCESS: No grid point fell in multiple areal zones') else: duplicated_df.to_csv('smoothed_duplicated.csv') point_a = duplicated_df.iloc[0] point_b = duplicated_df.iloc[1] zone_a = active_areal_df.at[int(point_a.zoneid), 'geometry'] zone_b = active_areal_df.at[int(point_b.zoneid), 'geometry'] _, ax = plt.subplots() ax.add_patch(PolygonPatch(zone_a, alpha=0.5)) ax.add_patch(PolygonPatch(zone_b, alpha=0.5)) ax.scatter(duplicated_df['longitude'], duplicated_df['latitude']) ax.set_xlim((point_a.longitude - 5, point_a.longitude + 5)) ax.set_ylim((point_a.latitude - 5, point_a.latitude + 5)) ax.set_aspect(1) print(int(point_a.zoneid), point_a.layerid, dumps(zone_a, rounding_precision=2)) print(int(point_b.zoneid), point_a.layerid, dumps(zone_b, rounding_precision=2)) print(point_a.longitude, point_a.latitude) raise RuntimeError('Points assigned to multiple zones.') # associate points nearest to zones print('Find nearest areal zones for remaining points ...') mark = time() active_areal_df['polygon'] = [ MyPolygon([ geo.point.Point(lat, lon) for lat, lon in zip(*zone.geometry.exterior.coords.xy) ]) for _, zone in active_areal_df.iterrows() ] unassigned_df = smoothed_df.loc[~assigned].copy() distances = np.full((len(unassigned_df), len(active_areal_df)), np.inf) for i, area_series in active_areal_df.iterrows(): in_layer = (unassigned_df['layerid'] == area_series['layerid']).values mesh = geo.mesh.Mesh(unassigned_df.loc[in_layer, 'longitude'].values, unassigned_df.loc[in_layer, 'latitude'].values) distances[in_layer, i] = area_series['polygon'].distances(mesh) unassigned_df.loc[:, 'zoneid'] = active_areal_df.loc[ np.argmin(distances, axis=1), 'zoneid'].values unassigned_df.loc[:, 'distance'] = np.amin(distances, axis=1) print('Nearest zone required for %.0f%% of sources: %s\n' % (100 * len(unassigned_df) / len(smoothed_df), pd.to_timedelta(time() - mark, unit='s'))) smoothed_df = pd.concat((smoothed_df[assigned], unassigned_df)) # copy parameters of nearest areal zone print('For each point source, copy parameters of nearest areal zone') columns_to_copy = [ 'zoneid', 'zmax', 'zmin', 'hypo_depth', 'tectonic subregion', 'a', 'b', 'stdb', 'mmax', 'stdmmax', 'rake', 'dip', 'strike', 'aspect ratio', 'msr' ] smoothed_df.drop(columns=['a'], inplace=True) smoothed_df = smoothed_df.merge(active_areal_df[columns_to_copy], on='zoneid') smoothed_df['a'] = (np.log10(smoothed_df['lambda']) + smoothed_df['b'] * smoothed_df['mmin model']) assert len_smoothed == smoothed_df.shape[0] # check for unassigned parameters display_drop = [ 'zmax', 'zmin', 'aspect ratio', 'msr', 'rake', 'dip', 'strike', 'stdb', 'stdmmax' ] no_zoneid_df = smoothed_df[smoothed_df['zoneid'].isnull()] no_mmax_df = smoothed_df[smoothed_df['mmax'] == 0] no_b_df = smoothed_df[smoothed_df['b'] == 0] if not no_zoneid_df.empty: print(no_zoneid_df.drop(display_drop, axis=1).head()) RuntimeError("Leftover points with no assigned zone id") if not no_mmax_df.empty: print(no_mmax_df.drop(display_drop, axis=1).head()) RuntimeError("Leftover points with no assigned mmax") if not no_b_df.empty: print(no_b_df.drop(display_drop, axis=1).head()) RuntimeError("Leftover points with no assigned b") if (no_mmax_df.empty and no_b_df.empty and no_zoneid_df.empty): print("SUCCESS: No points with unassigned MFD or zone") else: raise RuntimeError('Unassigned parameters remain.') # Thinning of models allows quick testing and git archiving of a sample res_deg = 1 thinned_df = smoothed_df.loc[ np.isclose(np.remainder(smoothed_df['latitude'], res_deg), 0) & np.isclose(np.remainder(smoothed_df['longitude'], res_deg), 0)].copy( ) print('Thinning to %g° spacing reduces number of points from %d to %d.\n' % (res_deg, len(smoothed_df), len(thinned_df))) # write thinned models mark = time() thinned_base = (SMOOTHED_MODEL_FORMAT.replace('v%d', '') + 'thinned ' + 'v%d') % (smoothed_prefix, int(version)) points2csv(thinned_df, thinned_base) points2nrml(thinned_df, thinned_base) print('Wrote %d thinned smoothed-gridded sources to CSV & NRML: %s\n' % (len(thinned_df), pd.to_timedelta(time() - mark, unit='s'))) thinned_collapsed_df, collapsed_tree_df, _, _ = \ collapse_sources(thinned_df, source_tree_symbolic_df) points2nrml(thinned_collapsed_df, thinned_base + ' collapsed') print( 'Wrote %d collapsed thinned sources to CSV & NRML: %s\n' % (len(thinned_collapsed_df), pd.to_timedelta(time() - mark, unit='s'))) # write full smoothed-gridded models (~10 minutes) if full: mark = time() smoothed_model_base = SMOOTHED_MODEL_FORMAT % (smoothed_prefix, int(version)) points2csv(smoothed_df, smoothed_model_base) points2nrml(smoothed_df, smoothed_model_base, by='mmin model') print('Wrote %d full smoothed-gridded sources to CSV & NRML: %s\n' % (len(smoothed_df), pd.to_timedelta(time() - mark, unit='s'))) # write collapsed smoothed-gridded sources to NRML (~10 minutes) mark = time() smoothed_collapsed_df, collapsed_tree_df, _, _ = \ collapse_sources(smoothed_df, source_tree_symbolic_df) points2nrml(smoothed_collapsed_df, smoothed_model_base + ' collapsed') print( 'Wrote %d collapsed smoothed-gridded sources to CSV & NRML: %s\n' % (len(smoothed_collapsed_df), pd.to_timedelta(time() - mark, unit='s')))
def main(): parser = argparse.ArgumentParser( description= 'Draws a beachball of an earthquake focal mechanism given strike, dip, rake or the 6 components of the moment tensor' ) parser.add_argument( '--fm', help= 'Nodal plane (strike dip rake) or moment tensor (Mrr Mtt Mpp Mrt Mrp Mtp)', type=float, nargs='+') parser.add_argument('--dc', help='superpose the double couple component', action='store_true') parser.add_argument( '-c', '--color', help= 'Choose a color for the quadrant of tension (r,b,k,g,y...). Default color is red', default='r', type=str) parser.add_argument( '-o', '--output', help= 'Name of the output file (could be a png, ps, pdf,eps, and svg). Default is None', default=None, type=str) args = parser.parse_args() bb = BB(outputname=args.output, facecolor=args.color) NP = None if args.fm: if len(args.fm) == 6: print "Draw beachball from moment tensor" Mrr = args.fm[0] Mtt = args.fm[1] Mpp = args.fm[2] Mrt = args.fm[3] Mrp = args.fm[4] Mtp = args.fm[5] M = MomentTensor((Mrr, Mtt, Mpp, Mrt, Mrp, Mtp), 0) nod_planes = mt2plane(M) if args.dc: NP = (nod_planes.strike, nod_planes.dip, nod_planes.rake) s2, d2, r2 = aux_plane(nod_planes.strike, nod_planes.dip, nod_planes.rake) print "Moment tensor:", \ "\n Mrr :",Mrr, \ "\n Mtt :",Mtt, \ "\n Mpp :",Mpp, \ "\n Mrt :",Mrt, \ "\n Mrp :",Mrp, \ "\n Mtp :",Mtp print "NP1:", round(nod_planes.strike), round( nod_planes.dip), round(nod_planes.rake) print "NP2:", round(s2), round(d2), round(r2) elif len(args.fm) == 3: print "Draw beachball from nodal plane" # compute auxiliary plane s2, d2, r2 = aux_plane(args.fm[0], args.fm[1], args.fm[2]) print "NP1:", round(args.fm[0]), round(args.fm[1]), round( args.fm[2]) print "NP2:", round(s2), round(d2), round(r2) bb.draw(args.fm, NP=NP)
def __init__(self, az, toa, pol, stn=None, event_id=None, strike=None, dip=None, rake=None): """ is a container for event az = list type of azimuths toa = list type of take of angles pol = first motion polarity (up = 1, down = -1) stn = list of stations that should be same length of az, toa, and pol event_id = unique event id """ # if len(az) != len(toa): # print('toa & az inputs are not same length') # if len(toa) != len(pol): # print('toa & polarity inputs are not same length') trend_lst = [] plunge_lst = [] for i, t in enumerate(toa): if t >= 90: if az[i] <= 180: trend = az[i] + 180 plunge = toa[i] - 90 # pol[i] = -pol[i] else: trend = az[i] - 180 plunge = toa[i] - 90 # pol[i] = -pol[i] else: trend = az[i] plunge = 90 - toa[i] trend_lst.append(trend) plunge_lst.append(plunge) if type(pol[0]) == str: b = pol == 'u' pol = b.astype(int) #Instances of Class self.az = np.array(az) self.toa = np.array(toa) self.pol = np.array(pol) self.stn = np.array(stn) self.event_id = event_id self.strike = np.array(strike) self.dip = np.array(dip) self.rake = np.array(rake) # calculated instances self.aux_plane = aux_plane(self.strike, self.dip, self.rake) self.trend = np.asarray(trend_lst) self.plunge = np.asarray(plunge_lst)
eigvals, eigvcts = np.linalg.eigh(np.array(mt.mt * 10**mt.exp, dtype=float)) print(eigvals) #[-8.45266194 7.57685043 0.87581151] print(eigvcts) #[[-0.40938256 0.90576372 -0.1095354 ] # [ 0.80215416 0.30012763 -0.51620937] # [ 0.43468912 0.29919139 0.84942915]] in columns t, b, p = princax(mt) print(t, b, p) print(bb.mt2axes(mt)[0].__dict__) # {'val': 7.58, 'strike': 315, 'dip': 64.9} print(bb.mt2axes(mt)[1].__dict__) # {'val': 0.88, 'strike': 58.7, 'dip': 6.29} print(bb.mt2axes(mt) [2].__dict__) # {'val': -8.45, 'strike': 151.55, 'dip': 24.2} print( bb.mt2plane(mt).__dict__) # {'strike': 56.34, 'dip': 69.45, 'rake': 83.28} print(bb.aux_plane(56.344996989653225, 69.45184548172541, 83.28228402625453)) #(254.8956832264013, 21.573156870706903, 107.33165895106156) strike, dip, slip aux plane M0 = sqrt(np.sum(eigvals**2) / 2) print('M0: ', M0) # 8.050565259657235 * 10 ** 24 Scalar Moment M_W = 2 / 3 * log(M0, 10) - 10.7 print(M_W) # 5.903884249895878 T, B, P = eigvals f_CLVD = abs(B) / max(abs(T), abs(P)) print(f_CLVD) # 0.10361369212705232 print(mt.fclvd)
epilog='exemple : ./np1_np2TNP.py 321 69 -173') parser.add_argument('np', help='Give nodal plane angle (strike,dip,slip)', nargs=3, type=int, metavar=("strike", "dip", "slip")) args = parser.parse_args() NP1 = args.np s1 = NP1[0] d1 = NP1[1] r1 = NP1[2] # Strike,dip and rake of the second plane s2, d2, r2 = aux_plane(s1, d1, r1) # degre to radian strike = ((s1 * pi) / 180.) dip = ((d1 * pi) / 180.) rake = ((r1 * pi) / 180.) # moment tensor (r=up, t=south and p=east) is2 = 1 / sqrt(2.0) mrr = is2 * (sin(2 * dip) * sin(rake)) mtt = -is2 * (sin(dip) * cos(rake) * sin(2 * strike) + sin(2 * dip) * sin(rake) * sin(strike)**2) mpp = is2 * (sin(dip) * cos(rake) * sin(2 * strike) -
def trace_density(self, filepath, savename, directory, skiprows, column_names, show=True): dir = directory + '/%s' % (savename.strip('.yaml')) if not os.path.exists(dir): os.makedirs(dir) if filepath.endswith('.yaml') == True: with open(filepath, 'r') as stream: data_file = yaml.load(stream) stream.close() data = data_file['data'] # parameters = data_file['parameters'] else: # parameters = open(filepath, "r").readlines()[:33] data = np.loadtxt(filepath, delimiter=',', skiprows=skiprows) length = len(data[0]) - (len(column_names) + 3) L_length = 4 #int(data[0][-1]) R_length = 4 #int(data[0][-2]) for i in range(R_length): column_names = np.append(column_names, "R_%i" % (i + 1)) for i in range(L_length): column_names = np.append(column_names, "L_%i" % (i + 1)) column_names = np.append(column_names, "Accepted") # column_names = np.append(column_names,"Rayleigh_length") # column_names = np.append(column_names,"Love_length") params = { 'legend.fontsize': 'x-large', 'figure.figsize': (15, 15), 'axes.labelsize': 25, 'axes.titlesize': 'x-large', 'xtick.labelsize': 25, 'ytick.labelsize': 25 } pylab.rcParams.update(params) # df = pd.DataFrame(data, columns=column_names) df_select = df[["Epi", "Depth", "Strike", "Dip", "Rake"]] par = Get_Paramters() REAL = par.get_unkown() real_v = np.array([ REAL['epi'], REAL['depth_s'], REAL['strike'], REAL['dip'], REAL['rake'] ]) # real_v=np.array([35.2068855191,16848.1405882,99.88000245897199, 77.02994881296266,68.80551508147109]) # real_v=np.array([73.8059395565,26247.7456326,158.92744919732135, 47.46909146946276,-45.69139707143311]) strike, dip, rake = aux_plane(REAL['strike'], REAL['dip'], REAL['rake']) # strike,dip,rake = aux_plane(99.88000245897199,77.02994881296266,68.80551508147109) # strike,dip,rake = aux_plane(158.92744919732135, 47.46909146946276,-45.69139707143311) fig = plt.figure(figsize=(20, 20)) row = 0 for i in df_select: ax1 = plt.subplot2grid((5, 2), (row, 0)) ax1.plot(df_select[i], label=i) # ax1.axhline(y=REAL[df_select], linewidth=0.3, linestyle=':') ax1.set_title("Trace %s" % i, color='b', fontsize=20) ax1.set_xlabel("Iteration") # ax1.set_ylabel("Epicentral ") plt.tight_layout() ax2 = plt.subplot2grid((5, 2), (row, 1)) plt.hist(df_select[i], bins=100) ymin, ymax = ax2.get_ylim() plt.vlines(df_select[i][1], ymin=ymin, ymax=ymax, colors='r', linewidth=3) plt.vlines(real_v[row], ymin=ymin, ymax=ymax, colors='k', linewidth=3) if i == 'Strike': plt.vlines(strike, ymin=ymin, ymax=ymax, colors='g', linewidth=3) if i == 'Dip': plt.vlines(dip, ymin=ymin, ymax=ymax, colors='g', linewidth=3) if i == 'Rake': plt.vlines(rake, ymin=ymin, ymax=ymax, colors='g', linewidth=3) # y, binEdges = np.histogram(df_select[i], bins=100) # bincenters = 0.5 * (binEdges[1:] + binEdges[:-1]) # pylab.plot(bincenters, y, '-',label = "%s" % i) ax2.set_title("Density %s" % i, color='b', fontsize=20) ax2.set_xlabel("N=%i" % (len(df_select[i]))) plt.tight_layout() row += 1 plt.savefig(dir + '/Trace_density.pdf')
event = 3146815 hash_solns = read_hash_solutions("example1.out") hash_focal = rad2deg(hash_solns[event]) mechs = genfromtxt("3146815_realizations.out")[:, :3] polarity_data = read_demo("north1.phase", "scsn.reverse", reverse=True) fig = plt.figure(facecolor="white") ax = fig.add_subplot(111, projection='stereonet') for mech in mechs[:10]: strike, dip, rake = mech ax.plane(strike - 90, dip, '-r', linewidth=2) strike, dip, rake = aux_plane(*mech) ax.plane(strike - 90, dip, '-r', linewidth=2) strike, dip, rake = hash_focal ax.plane(strike - 90, dip, '-b', linewidth=2) strike, dip, rake = aux_plane(*hash_focal) ax.plane(strike - 90, dip, '-b', linewidth=2) azi = rad2deg(polarity_data[event][:, 0]) toa = rad2deg(polarity_data[event][:, 1]) polarity = polarity_data[event][:, 2] for a, t, p in zip(azi, toa, polarity): if p > 0: ax.pole(a, t, 'o', markeredgecolor='red', markerfacecolor='red')
pth6 = c.collections[0].get_paths()[1].vertices pth6 = rad2deg(pth6) hash_focal3 = rad2deg(hash_solns[event3]) fig = plt.figure(facecolor="white", figsize=(10,20)) ax = fig.add_subplot(221, projection='stereonet') ax.rake(pth1[:,0], pth1[:,1] +90.0, 90.0, ':', color='red', linewidth=3) ax.rake(pth2[:,0], pth2[:,1] +90.0, 90.0, ':', color='red', linewidth=3) strike, dip, rake = svm_soln ax.plane(strike, dip, '-r', linewidth=2) strike, dip, rake = aux_plane(*svm_soln) ax.plane(strike, dip, '-r', linewidth=2) strike, dip, rake = hash_focal ax.plane(strike-90, dip, 'g-', linewidth=2) strike, dip, rake = aux_plane(*hash_focal) ax.plane(strike-90, dip,'g-', linewidth=2) azi = rad2deg(polarity_data[event][:,0]) toa = rad2deg(polarity_data[event][:,1]) polarity = polarity_data[event][:,2] for a, t, p in zip(azi, toa, polarity): if p > 0: ax.pole(a, t,'o', markeredgecolor='red', markerfacecolor='red')
def fill_tensor_from_components(mrr, mtt, mpp, mrt, mrp, mtp, source='unknown', mtype='unknown'): """Fill in moment tensor parameters from moment tensor components. Args: strike (float): Strike from (assumed) first nodal plane (degrees). dip (float): Dip from (assumed) first nodal plane (degrees). rake (float): Rake from (assumed) first nodal plane (degrees). magnitude (float): Magnitude for moment tensor (not required if using moment tensor for angular comparisons.) Returns: dict: Fully descriptive moment tensor dictionary, including fields: - mrr,mtt,mpp,mrt,mrp,mtp Moment tensor components. - T T-axis values: - azimuth (degrees) - plunge (degrees) - N N-axis values: - azimuth (degrees) - plunge (degrees) - P P-axis values: - azimuth (degrees) - plunge (degrees) - NP1 First nodal plane values: - strike (degrees) - dip (degrees) - rake (degrees) - NP2 Second nodal plane values: - strike (degrees) - dip (degrees) - rake (degrees) """ tensor_params = {'mrr': mrr, 'mtt': mtt, 'mpp': mpp, 'mrt': mrt, 'mrp': mrp, 'mtp': mtp} tensor_params['source'] = source tensor_params['type'] = mtype mt = MomentTensor(mrr, mtt, mpp, mrt, mrp, mtp, 1) tnp1 = mt2plane(mt) np1 = {'strike': tnp1.strike, 'dip': tnp1.dip, 'rake': tnp1.rake} tensor_params['NP1'] = np1.copy() strike2, dip2, rake2 = aux_plane(np1['strike'], np1['dip'], np1['rake']) np2 = {'strike': strike2, 'dip': dip2, 'rake': rake2} tensor_params['NP2'] = np2.copy() T, N, P = mt2axes(mt) tensor_params['T'] = {'azimuth': T.strike, 'value': T.val, 'plunge': T.dip} tensor_params['N'] = {'azimuth': N.strike, 'value': N.val, 'plunge': N.dip} tensor_params['P'] = {'azimuth': P.strike, 'value': P.val, 'plunge': P.dip} return tensor_params