diff --git a/BIN/bin2sac b/BIN/bin2sac new file mode 100755 index 0000000..c151776 Binary files /dev/null and b/BIN/bin2sac differ diff --git a/BIN/decon b/BIN/decon new file mode 100755 index 0000000..de97b1b Binary files /dev/null and b/BIN/decon differ diff --git a/BIN/fpsol.py b/BIN/fpsol.py new file mode 100644 index 0000000..2753e68 --- /dev/null +++ b/BIN/fpsol.py @@ -0,0 +1,69 @@ +#function to determine the strike, rake and dip from a dc moment tensor +#(full and deviatoric moment tenors need to be decomposed first + +import math +import numpy as np + +def fpsol(nu,u): + + """ + reads the vector normal and slip vector returning strike, rake, dip + """ + + + dip=np.arccos(-1*nu[2]) + if nu[0] ==0. and nu[1] == 0.: + str=0. + else: + str=np.arctan2(-1*nu[0],nu[1]) + + + + sstr=np.sin(str) + cstr=np.cos(str) + sdip=np.sin(dip) + cdip=np.cos(dip) + + + if abs(sdip) > 0.: + rake=np.arcsin(-1*u[2]/np.sin(dip)); + else: + arg1=1. + arg2=u[2] + arg=np.sign(arg2) + if arg < 0.: + rake=np.pi + else: + rake=0. + + slambda=np.sin(rake) + cdsl=cdip*slambda + + if abs(sstr) > abs(cstr): + clambda=(u[1]+cdsl*cstr)/sstr + else: + clambda=(u[0]-cdsl*sstr)/cstr + + if slambda == 0. and clambda == 0.: + slip=0. + else: + slip=np.arctan2(slambda,clambda) + + + if dip > np.pi/2: + dip=np.pi-dip + str=str+np.pi + slip=2*np.pi-slip + + if str < 0.: + str=str+2*np.pi + + if slip >= np.pi: + slip=slip-2*np.pi; + + + str=str*180/np.pi + rake=slip*180/np.pi + dip=dip*180/np.pi + + return str, rake, dip diff --git a/BIN/fromHelm b/BIN/fromHelm new file mode 100755 index 0000000..1be4772 Binary files /dev/null and b/BIN/fromHelm differ diff --git a/BIN/mkHelm b/BIN/mkHelm new file mode 100755 index 0000000..12bcf3e Binary files /dev/null and b/BIN/mkHelm differ diff --git a/BIN/mopad.py b/BIN/mopad.py new file mode 100644 index 0000000..0b13d3d --- /dev/null +++ b/BIN/mopad.py @@ -0,0 +1,4922 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +from io import StringIO +import optparse +import math +import numpy as np +import os +import os.path +import sys +import re + +MOPAD_VERSION = 1.0 + + +# constants: +dynecm = 1e-7 +pi = np.pi + +epsilon = 1e-13 + +rad2deg = 180. / pi + + +def wrap(text, line_length=80): + '''Paragraph and list-aware wrapping of text.''' + + text = text.strip('\n') + at_lineend = re.compile(r' *\n') + at_para = re.compile(r'((^|(\n\s*)?\n)(\s+[*] )|\n\s*\n)') + + paragraphs = at_para.split(text)[::5] + listindents = at_para.split(text)[4::5] + newlist = at_para.split(text)[3::5] + + listindents[0:0] = [None] + listindents.append(True) + newlist.append(None) + + det_indent = re.compile(r'^ *') + + outlines = [] + for ip, p in enumerate(paragraphs): + if not p: + continue + + if listindents[ip] is None: + _indent = det_indent.findall(p)[0] + findent = _indent + else: + findent = listindents[ip] + _indent = ' ' * len(findent) + + ll = line_length - len(_indent) + llf = ll + + oldlines = [s.strip() for s in at_lineend.split(p.rstrip())] + p1 = ' '.join(oldlines) + possible = re.compile(r'(^.{1,%i}|.{1,%i})( |$)' % (llf, ll)) + for imatch, match in enumerate(possible.finditer(p1)): + parout = match.group(1) + if imatch == 0: + outlines.append(findent + parout) + else: + outlines.append(_indent + parout) + + if ip != len(paragraphs) - 1 and ( + listindents[ip] is None or + newlist[ip] is not None or + listindents[ip + 1] is None): + + outlines.append('') + + return outlines + + +def basis_switcher(in_system, out_system): + from_ned = { + 'NED': np.matrix([[1., 0., 0.], [0., 1., 0.], [0., 0., 1.]], + dtype=np.float), + 'USE': np.matrix([[0., -1., 0.], [0., 0., 1.], [-1., 0., 0.]], + dtype=np.float).I, + 'XYZ': np.matrix([[0., 1., 0.], [1., 0., 0.], [0., 0., -1.]], + dtype=np.float).I, + 'NWU': np.matrix([[1., 0., 0.], [0., -1., 0.], [0., 0., -1.]], + dtype=np.float).I} + + return from_ned[in_system].I * from_ned[out_system] + + +def basis_transform_matrix(m, in_system, out_system): + r = basis_switcher(in_system, out_system) + return np.dot(r, np.dot(m, r.I)) + + +def basis_transform_vector(v, in_system, out_system): + r = basis_switcher(in_system, out_system) + return np.dot(r, v) + + +class MopadHelpFormatter(optparse.IndentedHelpFormatter): + + def format_option(self, option): + '''From IndentedHelpFormatter but using a different wrap method.''' + + result = [] + opts = self.option_strings[option] + opt_width = self.help_position - self.current_indent - 2 + if len(opts) > opt_width: + opts = "%*s%s\n" % (self.current_indent, "", opts) + indent_first = self.help_position + else: # start help on same line as opts + opts = "%*s%-*s " % (self.current_indent, "", opt_width, opts) + indent_first = 0 + result.append(opts) + if option.help: + help_text = self.expand_default(option) + help_lines = wrap(help_text, self.help_width) + if len(help_lines) > 1: + help_lines.append('') + result.append("%*s%s\n" % (indent_first, "", help_lines[0])) + result.extend(["%*s%s\n" % (self.help_position, "", line) + for line in help_lines[1:]]) + elif opts[-1] != "\n": + result.append("\n") + return "".join(result) + + def format_description(self, description): + if not description: + return "" + desc_width = self.width - self.current_indent + return '\n'.join(wrap(description, desc_width)) + "\n" + + +class MTError(Exception): + pass + + +def euler_to_matrix(alpha, beta, gamma): + '''Given the euler angles alpha,beta,gamma, create rotation matrix + + Given coordinate system (x,y,z) and rotated system (xs,ys,zs) + the line of nodes is the intersection between the x-y and the xs-ys + planes. + alpha is the angle between the z-axis and the zs-axis. + beta is the angle between the x-axis and the line of nodes. + gamma is the angle between the line of nodes and the xs-axis. + + Usage for moment tensors: + m_unrot = numpy.matrix([[0,0,-1],[0,0,0],[-1,0,0]]) + rotmat = euler_to_matrix(dip,strike,-rake) + m = rotmat.T * m_unrot * rotmat''' + + ca = math.cos(alpha) + cb = math.cos(beta) + cg = math.cos(gamma) + sa = math.sin(alpha) + sb = math.sin(beta) + sg = math.sin(gamma) + + mat = np.matrix( + [[cb * cg - ca * sb * sg, sb * cg + ca * cb * sg, sa * sg], + [-cb * sg - ca * sb * cg, -sb * + sg + ca * cb * cg, sa * cg], + [sa * sb, -sa * cb, ca]], dtype=np.float) + return mat + + +class MomentTensor: + + _m_unrot = np.matrix( + [[0., 0., -1.], [0., 0., 0.], [-1., 0., 0.]], dtype=np.float) + + def __init__(self, M=None, in_system='NED', out_system='NED', debug=0): + + """ + Creates a moment tensor object on the basis of a provided mechanism M. + + If M is a non symmetric 3x3-matrix, the upper right triangle + of the matrix is taken as reference. M is symmetrisised + w.r.t. these entries. If M is provided as a 3-,4-,6-,7-tuple + or array, it is converted into a matrix internally according + to standard conventions (Aki & Richards). + + 'system' may be chosen as 'NED','USE','NWU', or 'XYZ'. + + 'debug' enables output on the shell at the intermediate steps. + """ + + self._original_M = M[:] + + self._input_basis = in_system.upper() + self._output_basis = out_system.upper() + + # bring M to symmetric matrix form + self._M = self._setup_M(M, self._input_basis) + + # decomposition: + self._decomposition_key = 1 + + # eigenvector / principal-axes system: + self._eigenvalues = None + self._eigenvectors = None + self._null_axis = None + self._t_axis = None + self._p_axis = None + self._rotation_matrix = None + + # optional - maybe set afterwards by external application - for later + # plotting: + self._best_faultplane = None + self._auxiliary_plane = None + + # initialise decomposition components + self._DC = None + self._DC_percentage = None + self._DC2 = None + self._DC2_percentage = None + self._DC3 = None + self._DC3_percentage = None + + self._iso = None + self._iso_percentage = None + self._devi = None + self._devi_percentage = None + self._CLVD = None + self._CLVD_percentage = None + + self._isotropic = None + self._deviatoric = None + self._seismic_moment = None + self._moment_magnitude = None + + self._decomp_attrib_map_keys = ('in', 'out', 'type', + 'full', + 'iso', 'iso_perc', + 'dev', 'devi', 'devi_perc', + 'dc', 'dc_perc', + 'dc2', 'dc2_perc', + 'dc3', 'dc3_perc', + 'clvd', 'clvd_perc', + 'mom', 'mag', + 'eigvals', 'eigvecs', + 't', 'n', 'p') + + self._decomp_attrib_map = dict(zip(self._decomp_attrib_map_keys, + ('input_system', 'output_system', + 'decomp_type', 'M', + 'iso', 'iso_percentage', + 'devi', 'devi', 'devi_percentage', + 'DC', 'DC_percentage', + 'DC2', 'DC2_percentage', + 'DC3', 'DC3_percentage', + 'CLVD', 'CLVD_percentage', + 'moment', 'mag', + 'eigvals', 'eigvecs', + 't_axis', 'null_axis', 'p_axis') + )) + + # carry out the MT decomposition - results are in basis NED + self._decompose_M() + + # set the appropriate principal axis system: + self._M_to_principal_axis_system() + + def _setup_M(self, mech, input_basis): + """ + Brings the provided mechanism into symmetric 3x3 matrix form. + + The source mechanism may be provided in different forms: + + * as 3x3 matrix - symmetry is checked - one basis system has to be + chosen, or NED as default is taken + * as 3-element tuple or array - interpreted as strike, dip, slip-rake + angles in degree + * as 4-element tuple or array - interpreted as strike, dip, slip-rake + angles in degree + seismic scalar moment in Nm + * as 6-element tuple or array - interpreted as the 6 independent + entries of the moment tensor + * as 7-element tuple or array - interpreted as the 6 independent + entries of the moment tensor + seismic scalar moment in Nm + * as 9-element tuple or array - interpreted as the 9 entries of the + moment tensor - checked for symmetry + * as a nesting of one of the upper types (e.g. a list of n-tuples); + first element of outer nesting is taken + """ + # set source mechanism to matrix form + + if mech is None: + raise MTError('Please provide a mechanism') + + # if some stupid nesting occurs + if len(mech) == 1: + mech = mech[0] + + # all 9 elements are given + if np.prod(np.shape(mech)) == 9: + if np.shape(mech)[0] == 3: + # assure symmetry: + mech[1, 0] = mech[0, 1] + mech[2, 0] = mech[0, 2] + mech[2, 1] = mech[1, 2] + new_M = mech + else: + new_M = np.array(mech).reshape(3, 3).copy() + new_M[1, 0] = new_M[0, 1] + new_M[2, 0] = new_M[0, 2] + new_M[2, 1] = new_M[1, 2] + + # mechanism given as 6- or 7-tuple, list or array + elif len(mech) == 6 or len(mech) == 7: + M = mech + new_M = np.matrix( + np.array([M[0], M[3], M[4], + M[3], M[1], M[5], + M[4], M[5], M[2]]).reshape(3, 3)) + + if len(mech) == 7: + new_M = M[6] * new_M + + # if given as strike, dip, rake, conventions from Jost & Herrmann hold + # - resulting matrix is in NED-basis: + elif len(mech) == 3 or len(mech) == 4: + strike, dip, rake = mech[:3] + scalar_moment = 1.0 + if len(mech) == 4: + scalar_moment = mech[3] + + rotmat1 = euler_to_matrix( + dip / rad2deg, strike / rad2deg, -rake / rad2deg) + new_M = rotmat1.T * MomentTensor._m_unrot * rotmat1 * scalar_moment + + # to assure right basis system - others are meaningless, provided + # these angles + input_basis = 'NED' + + return basis_transform_matrix(np.matrix(new_M), input_basis, 'NED') + + def _decompose_M(self): + """ + Running the decomposition of the moment tensor object. + + the standard decompositions M = Isotropic + DC + (CLVD or 2nd DC) are + supported (C.f. Jost & Herrmann, Aki & Richards) + """ + k = self._decomposition_key + d = MomentTensor.decomp_dict + if k in d: + d[k][1](self) + + else: + raise MTError('Invalid decomposition key: %i' % k) + + def print_decomposition(self): + for arg in self._decomp_attrib_map_keys: + getter = getattr(self, 'get_' + self._decomp_attrib_map[arg]) + print(getter(style='y', system=self._output_basis)) + + def _standard_decomposition(self): + """ + Decomposition according Aki & Richards and Jost & Herrmann into + + isotropic + deviatoric + = isotropic + DC + CLVD + + parts of the input moment tensor. + + results are given as attributes, callable via the get_* function: + + DC, CLVD, DC_percentage, seismic_moment, moment_magnitude + """ + + M = self._M + + # isotropic part + M_iso = np.diag(np.array([1. / 3 * np.trace(M), + 1. / 3 * np.trace(M), + 1. / 3 * np.trace(M)])) + + M0_iso = abs(1. / 3 * np.trace(M)) + + # deviatoric part + M_devi = M - M_iso + + self._isotropic = M_iso + self._deviatoric = M_devi + + #eigenvalues and -vectors + eigenwtot, eigenvtot = np.linalg.eig(M_devi) + + # eigenvalues and -vectors of the deviatoric part + eigenw1, eigenv1 = np.linalg.eig(M_devi) + + # eigenvalues in ascending order: + eigenw = np.real(np.take(eigenw1, np.argsort(abs(eigenwtot)))) + eigenv = np.real(np.take(eigenv1, np.argsort(abs(eigenwtot)), 1)) + + # eigenvalues in ascending order in absolute value!!: + eigenw_devi = np.real(np.take(eigenw1, np.argsort(abs(eigenw1)))) + #eigenv_devi = np.real(np.take(eigenv1, np.argsort(abs(eigenw1)), 1)) + + M0_devi = max(abs(eigenw_devi)) + + # named according to Jost & Herrmann: + #a1 = eigenv[:, 0] + a2 = eigenv[:, 1] + a3 = eigenv[:, 2] + + # if only isotropic part exists: + if M0_devi < epsilon: + F = 0.5 + else: + F = -eigenw_devi[0] / eigenw_devi[2] + + M_DC = np.matrix(np.zeros((9), float)).reshape(3, 3) + M_CLVD = np.matrix(np.zeros((9), float)).reshape(3, 3) + + M_DC = eigenw[2] * (1 - 2 * F) * (np.outer(a3, a3) - np.outer(a2, a2)) + M_CLVD = M_devi - M_DC + + # according to Bowers & Hudson: + M0 = M0_iso + M0_devi + + M_iso_percentage = int(round(M0_iso / M0 * 100, 6)) + self._iso_percentage = M_iso_percentage + + M_DC_percentage = int(round((1 - 2 * abs(F)) * + (1 - M_iso_percentage / 100.) * 100, 6)) + + self._DC = M_DC + self._CLVD = M_CLVD + self._DC_percentage = M_DC_percentage + + self._seismic_moment = M0 + self._moment_magnitude = np.log10( + self._seismic_moment * 1.0e7) / 1.5 - 10.7 + + def _decomposition_w_2DC(self): + """ + Decomposition according Aki & Richards and Jost & Herrmann into + + isotropic + deviatoric + = isotropic + DC + DC2 + + parts of the input moment tensor. + + results are given as attributes, callable via the get_* function: + + DC1, DC2, DC_percentage, seismic_moment, moment_magnitude + """ + M = self._M + + # isotropic part + M_iso = np.diag(np.array([1. / 3 * np.trace(M), + 1. / 3 * np.trace(M), + 1. / 3 * np.trace(M)])) + + M0_iso = abs(1. / 3 * np.trace(M)) + + # deviatoric part + M_devi = M - M_iso + + self._isotropic = M_iso + self._deviatoric = M_devi + + # eigenvalues and -vectors of the deviatoric part + eigenw1, eigenv1 = np.linalg.eig(M_devi) + + # eigenvalues in ascending order of their absolute values: + eigenw = np.real( + np.take(eigenw1, np.argsort(abs(eigenw1)))) + eigenv = np.real( + np.take(eigenv1, np.argsort(abs(eigenw1)), 1)) + + M0_devi = max(abs(eigenw)) + + # named according to Jost & Herrmann: + a1 = eigenv[:, 0] + a2 = eigenv[:, 1] + a3 = eigenv[:, 2] + + M_DC = np.matrix(np.zeros((9), float)).reshape(3, 3) + M_DC2 = np.matrix(np.zeros((9), float)).reshape(3, 3) + + M_DC = eigenw[2] * (np.outer(a3, a3) - np.outer(a2, a2)) + M_DC2 = eigenw[0] * (np.outer(a1, a1) - np.outer(a2, a2)) + + M_DC_percentage = abs(eigenw[2] / (abs(eigenw[2]) + abs(eigenw[0]))) + + self._DC = M_DC + self._DC2 = M_DC2 + self._DC_percentage = M_DC_percentage + + # according to Bowers & Hudson: + M0 = M0_iso + M0_devi + + M_iso_percentage = int(M0_iso / M0 * 100) + self._iso_percentage = M_iso_percentage + + #self._seismic_moment = np.sqrt(1./2*np.sum(eigenw**2) ) + self._seismic_moment = M0 + self._moment_magnitude = np.log10( + self._seismic_moment * 1.0e7) / 1.5 - 10.7 + + def _decomposition_w_CLVD_2DC(self): + """ + Decomposition according to Dahm (1993) into + + - isotropic + - CLVD + - strike-slip + - dip-slip + + parts of the input moment tensor. + + results are given as attributes, callable via the get_* function: + + iso, CLVD, DC1, DC2, iso_percentage, DC_percentage, DC1_percentage, + DC2_percentage, CLVD_percentage, seismic_moment, moment_magnitude + """ + M = self._M + + # isotropic part + M_iso = np.diag( + np.array([1. / 3 * np.trace(M), + 1. / 3 * np.trace(M), + 1. / 3 * np.trace(M)])) + + #M0_iso = abs(1. / 3 * np.trace(M)) + + # deviatoric part + M_devi = M - M_iso + + self._isotropic = M_iso + self._deviatoric = M_devi + + M_DC1 = np.matrix(np.zeros((9), float)).reshape(3, 3) + M_DC2 = np.matrix(np.zeros((9), float)).reshape(3, 3) + M_CLVD = np.matrix(np.zeros((9), float)).reshape(3, 3) + + M_DC1[0, 0] = -0.5 * (M[1, 1] - M[0, 0]) + M_DC1[1, 1] = 0.5 * (M[1, 1] - M[0, 0]) + M_DC1[0, 1] = M_DC1[1, 0] = M[0, 1] + + M_DC2[0, 2] = M_DC2[2, 0] = M[0, 2] + M_DC2[1, 2] = M_DC2[2, 1] = M[1, 2] + + M_CLVD = 1. / 3. * \ + (0.5 * (M[1, 1] + M[0, 0]) - M[2, 2]) * \ + np.diag(np.array([1., 1., -2.])) + + M_DC = M_DC1 + M_DC2 + + self._DC = M_DC + self._DC1 = M_DC1 + self._DC2 = M_DC2 + + # according to Bowers & Hudson: + eigvals_M, dummy_vecs = np.linalg.eig(M) + eigvals_M_devi, dummy_vecs = np.linalg.eig(M_devi) + eigvals_M_iso, dummy_iso = np.linalg.eig(M_iso) + eigvals_M_clvd, dummy_vecs = np.linalg.eig(M_CLVD) + eigvals_M_dc1, dummy_vecs = np.linalg.eig(M_DC1) + eigvals_M_dc2, dummy_vecs = np.linalg.eig(M_DC2) + + #M0_M = np.max(np.abs(eigvals_M - 1./3*np.sum(eigvals_M) )) + M0_M_iso = np.max( + np.abs(eigvals_M_iso - 1. / 3 * np.sum(eigvals_M))) + M0_M_clvd = np.max( + np.abs(eigvals_M_clvd - 1. / 3 * np.sum(eigvals_M))) + M0_M_dc1 = np.max( + np.abs(eigvals_M_dc1 - 1. / 3 * np.sum(eigvals_M))) + M0_M_dc2 = np.max( + np.abs(eigvals_M_dc2 - 1. / 3 * np.sum(eigvals_M))) + + M0_M_dc = M0_M_dc1 + M0_M_dc2 + M0_M_devi = M0_M_clvd + M0_M_dc + M0_M = M0_M_iso + M0_M_devi + + self._iso_percentage = int(M0_M_iso / M0_M * 100) + self._DC_percentage = int(M0_M_dc / M0_M * 100) + self._DC1_percentage = int(M0_M_dc1 / M0_M * 100) + self._DC2_percentage = int(M0_M_dc2 / M0_M * 100) + + #self._seismic_moment = np.sqrt(1./2*np.sum(eigenw**2) ) + self._seismic_moment = M0_M + self._moment_magnitude = np.log10( + self._seismic_moment * 1.0e7) / 1.5 - 10.7 + + def _decomposition_w_3DC(self): + """ + Decomposition according Aki & Richards and Jost & Herrmann into + + - isotropic + - deviatoric + - 3 DC + + parts of the input moment tensor. + + results are given as attributes, callable via the get_* function: + + DC1, DC2, DC3, DC_percentage, seismic_moment, moment_magnitude + """ + M = self._M + + # isotropic part + M_iso = np.diag(np.array([1. / 3 * np.trace(M), + 1. / 3 * np.trace(M), + 1. / 3 * np.trace(M)])) + M0_iso = abs(1. / 3 * np.trace(M)) + + # deviatoric part + M_devi = M - M_iso + + self._isotropic = M_iso + self._deviatoric = M_devi + + # eigenvalues and -vectors of the deviatoric part + eigenw1, eigenv1 = np.linalg.eig(M_devi) + M0_devi = max(abs(eigenw1)) + + # eigenvalues and -vectors of the full M !!!!!!!! + eigenw1, eigenv1 = np.linalg.eig(M) + + # eigenvalues in ascending order of their absolute values: + eigenw = np.real( + np.take(eigenw1, np.argsort(abs(eigenw1)))) + eigenv = np.real( + np.take(eigenv1, np.argsort(abs(eigenw1)), 1)) + + # named according to Jost & Herrmann: + a1 = eigenv[:, 0] + a2 = eigenv[:, 1] + a3 = eigenv[:, 2] + + M_DC1 = np.matrix(np.zeros((9), float)).reshape(3, 3) + M_DC2 = np.matrix(np.zeros((9), float)).reshape(3, 3) + M_DC3 = np.matrix(np.zeros((9), float)).reshape(3, 3) + + M_DC1 = 1. / 3. * \ + (eigenw[0] - eigenw[1]) * (np.outer(a1, a1) - np.outer(a2, a2)) + M_DC2 = 1. / 3. * \ + (eigenw[1] - eigenw[2]) * (np.outer(a2, a2) - np.outer(a3, a3)) + M_DC3 = 1. / 3. * \ + (eigenw[2] - eigenw[0]) * (np.outer(a3, a3) - np.outer(a1, a1)) + + M_DC1_perc = int(100 * abs((eigenw[0] - eigenw[1])) / + (abs((eigenw[1] - eigenw[2])) + + abs((eigenw[1] - eigenw[2])) + + abs((eigenw[2] - eigenw[0])))) + M_DC2_perc = int(100 * abs((eigenw[1] - eigenw[2])) / + (abs((eigenw[1] - eigenw[2])) + + abs((eigenw[1] - eigenw[2])) + + abs((eigenw[2] - eigenw[0])))) + + self._DC = M_DC1 + self._DC2 = M_DC2 + self._DC3 = M_DC3 + + self._DC_percentage = M_DC1_perc + self._DC2_percentage = M_DC2_perc + + # according to Bowers & Hudson: + M0 = M0_iso + M0_devi + + M_iso_percentage = int(M0_iso / M0 * 100) + self._iso_percentage = M_iso_percentage + + #self._seismic_moment = np.sqrt(1./2*np.sum(eigenw**2) ) + self._seismic_moment = M0 + self._moment_magnitude = np.log10( + self._seismic_moment * 1.0e7) / 1.5 - 10.7 + + def _M_to_principal_axis_system(self): + """ + Read in Matrix M and set up eigenvalues (EW) and eigenvectors + (EV) for setting up the principal axis system. + + The internal convention is the 'HNS'-system: H is the + eigenvector for the smallest absolute eigenvalue, S is the + eigenvector for the largest absolute eigenvalue, N is the null + axis. + + Naming due to the geometry: a CLVD is + Symmetric to the S-axis, + Null-axis is common sense, and the third (auxiliary) axis + Helps to construct the R³. + + Additionally builds matrix for basis transformation back to NED system. + + The eigensystem setup defines the colouring order for a later + plotting in the BeachBall class. This order is set by the + '_plot_clr_order' attribute. + """ + + M = self._M + M_devi = self._deviatoric + + # working in framework of 3 principal axes: + # eigenvalues (EW) are in order from high to low + # - neutral axis N, belongs to middle EW + # - symmetry axis S ('sigma') belongs to EW with largest absolute value + # (P- or T-axis) + # - auxiliary axis H ('help') belongs to remaining EW (T- or P-axis) + # EW sorting from lowest to highest value + EW_devi, EV_devi = np.linalg.eigh(M_devi) + EW_order = np.argsort(EW_devi) + + # print 'order',EW_order + + if 1: # self._plot_isotropic_part: + trace_M = np.trace(M) + if abs(trace_M) < epsilon: + trace_M = 0 + EW, EV = np.linalg.eigh(M) + for i, ew in enumerate(EW): + if abs(EW[i]) < epsilon: + EW[i] = 0 + else: + trace_M = np.trace(M_devi) + if abs(trace_M) < epsilon: + trace_M = 0 + + EW, EV = np.linalg.eigh(M_devi) + for i, ew in enumerate(EW): + if abs(EW[i]) < epsilon: + EW[i] = 0 + + EW1_devi = EW_devi[EW_order[0]] + EW2_devi = EW_devi[EW_order[1]] + EW3_devi = EW_devi[EW_order[2]] + EV1_devi = EV_devi[:, EW_order[0]] + EV2_devi = EV_devi[:, EW_order[1]] + EV3_devi = EV_devi[:, EW_order[2]] + + EW1 = EW[EW_order[0]] + EW2 = EW[EW_order[1]] + EW3 = EW[EW_order[2]] + EV1 = EV[:, EW_order[0]] + EV2 = EV[:, EW_order[1]] + EV3 = EV[:, EW_order[2]] + + chng_basis_tmp = np.asmatrix(np.zeros((3, 3))) + chng_basis_tmp[:, 0] = EV1_devi + chng_basis_tmp[:, 1] = EV2_devi + chng_basis_tmp[:, 2] = EV3_devi + + symmetry_around_tension = 1 + clr = 1 + + if abs(EW2_devi) < epsilon: + EW2_devi = 0 + + # implosion + if EW1 < 0 and EW2 < 0 and EW3 < 0: + symmetry_around_tension = 0 + # logger.debug( 'IMPLOSION - symmetry around pressure axis \n\n') + clr = 1 + # explosion + elif EW1 > 0 and EW2 > 0 and EW3 > 0: + symmetry_around_tension = 1 + if abs(EW1_devi) > abs(EW3_devi): + symmetry_around_tension = 0 + # logger.debug( 'EXPLOSION - symmetry around tension axis \n\n') + clr = -1 + # net-implosion + elif EW2 < 0 and sum([EW1, EW2, EW3]) < 0: + if abs(EW1_devi) < abs(EW3_devi): + symmetry_around_tension = 1 + clr = 1 + else: + symmetry_around_tension = 1 + clr = 1 + # net-implosion + elif EW2_devi >= 0 and sum([EW1, EW2, EW3]) < 0: + symmetry_around_tension = 0 + clr = -1 + if abs(EW1_devi) < abs(EW3_devi): + symmetry_around_tension = 1 + clr = 1 + # net-explosion + elif EW2_devi < 0 and sum([EW1, EW2, EW3]) > 0: + symmetry_around_tension = 1 + clr = 1 + if abs(EW1_devi) > abs(EW3_devi): + symmetry_around_tension = 0 + clr = -1 + # net-explosion + elif EW2_devi >= 0 and sum([EW1, EW2, EW3]) > 0: + symmetry_around_tension = 0 + clr = -1 + else: + # TODO check: this point should never be reached !! + pass + + if abs(EW1_devi) < abs(EW3_devi): + symmetry_around_tension = 1 + clr = 1 + if 0: # EW2 > 0 :#or (EW2 > 0 and EW2_devi > 0) : + symmetry_around_tension = 0 + clr = -1 + + if abs(EW1_devi) >= abs(EW3_devi): + symmetry_around_tension = 0 + clr = -1 + if 0: # EW2 < 0 : + symmetry_around_tension = 1 + clr = 1 + if (EW3 < 0 and np.trace(self._M) >= 0): + # reaching this point means, we have a serious problem, likely of + # numerical nature + print('Houston, we have had a problem - check M !!!!!! \n' + \ + '( Trace(M) > 0, but largest eigenvalue is still negative)') + raise MTError(' !! ') + + if trace_M == 0: + # print 'pure deviatoric' + if EW2 == 0: + # print 'pure shear' + symmetry_around_tension = 1 + clr = 1 + + elif 2 * abs(EW2) == abs(EW1) or 2 * abs(EW2) == abs(EW3): + # print 'pure clvd' + if abs(EW1) < EW3: + # print 'CLVD: symmetry around tension' + symmetry_around_tension = 1 + clr = 1 + else: + # print 'CLVD: symmetry around pressure' + symmetry_around_tension = 0 + clr = -1 + else: + # print 'mix of DC and CLVD' + if abs(EW1) < EW3: + # print 'symmetry around tension' + symmetry_around_tension = 1 + clr = 1 + else: + # print 'symmetry around pressure' + symmetry_around_tension = 0 + clr = -1 + + # define order of eigenvectors and values according to symmetry axis + if symmetry_around_tension == 1: + EWs = EW3.copy() + EVs = EV3.copy() + EWh = EW1.copy() + EVh = EV1.copy() + + else: + EWs = EW1.copy() + EVs = EV1.copy() + EWh = EW3.copy() + EVh = EV3.copy() + + EWn = EW2 + EVn = EV2 + + # build the basis system change matrix: + chng_basis = np.asmatrix(np.zeros((3, 3))) + + # order of eigenvector's basis: (H,N,S) + chng_basis[:, 0] = EVh + chng_basis[:, 1] = EVn + chng_basis[:, 2] = EVs + + # matrix for basis transformation + self._rotation_matrix = chng_basis + + # collections of eigenvectors and eigenvalues + self._eigenvectors = [EVh, EVn, EVs] + self._eigenvalues = [EWh, EWn, EWs] + + # principal axes + self._null_axis = EVn + self._t_axis = EV1 + self._p_axis = EV3 + + # plotting order flag - important for plot in BeachBall class + self._plot_clr_order = clr + + # collection of the faultplanes, given in strike, dip, slip-rake + self._faultplanes = self._find_faultplanes() + + def _find_faultplanes(self): + """ + Sets the two angle-triples, describing the faultplanes of the + Double Couple, defined by the eigenvectors P and T of the + moment tensor object. + + Define a reference Double Couple with strike = dip = + slip-rake = 0, the moment tensor object's DC is transformed + (rotated) w.r.t. this orientation. The respective rotation + matrix yields the first fault plane angles as the Euler + angles. After flipping the first reference plane by + multiplying the appropriate flip-matrix, one gets the second fault + plane's geometry. + + All output angles are in degree + + ( + to check: + using Sebastian's conventions: + + rotationsmatrix1 = + EV Matrix of M, but in order TNP (not as here PNT!!!) + + reference-DC with strike, dip, rake = 0,0,0 + in NED - form: M = 0,0,0,0,-1,0 + + the eigenvectors of this into a Matrix: + + trafo-matrix2 = EV Matrix of Reference-DC in order TNP + + effective Rotation matrix = (rotation_matrix1 * trafo-matrix2.T).T + + by checking for det <0, make sure, if Matrix must be multiplied by -1 + + flip_matrix = 0,0,-1,0,-1,0,-1,0,0 + + other DC orientation obtained by flip * effective Rotation matrix + + both matrices in matrix_2_euler + ) + """ + # reference Double Couple (in NED basis) + # it has strike, dip, slip-rake = 0,0,0 + refDC = np.matrix([[0., 0., -1.], [0., 0., 0.], [-1., 0., 0.]], + dtype=np.float) + refDC_evals, refDC_evecs = np.linalg.eigh(refDC) + + # matrix which is turning from one fault plane to the other + flip_dc = np.matrix([[0., 0., -1.], [0., -1., 0.], [-1., 0., 0.]], + dtype=np.float) + + # euler-tools need matrices of EV sorted in PNT: + pnt_sorted_EV_matrix = self._rotation_matrix.copy() + + # resort only necessary, if abs(p) <= abs(t) + # print self._plot_clr_order + if self._plot_clr_order < 0: + pnt_sorted_EV_matrix[:, 0] = self._rotation_matrix[:, 2] + pnt_sorted_EV_matrix[:, 2] = self._rotation_matrix[:, 0] + + # rotation matrix, describing the rotation of the eigenvector + # system of the input moment tensor into the eigenvector + # system of the reference Double Couple + rot_matrix_fp1 = (np.dot(pnt_sorted_EV_matrix, refDC_evecs.T)).T + + # check, if rotation has right orientation + if np.linalg.det(rot_matrix_fp1) < 0.: + rot_matrix_fp1 *= -1. + + # adding a rotation into the ambiguous system of the second fault plane + rot_matrix_fp2 = np.dot(flip_dc, rot_matrix_fp1) + + fp1 = self._find_strike_dip_rake(rot_matrix_fp1) + fp2 = self._find_strike_dip_rake(rot_matrix_fp2) + + return [fp1, fp2] + + def _find_strike_dip_rake(self, rotation_matrix): + """ + Returns angles strike, dip, slip-rake in degrees, describing the fault + plane. + """ + (alpha, beta, gamma) = self._matrix_to_euler(rotation_matrix) + return (beta * rad2deg, alpha * rad2deg, -gamma * rad2deg) + + def _cvec(self, x, y, z): + """ + Builds a column vector (matrix type) from a 3 tuple. + """ + return np.matrix([[x, y, z]], dtype=np.float).T + + def _matrix_to_euler(self, rotmat): + """ + Returns three Euler angles alpha, beta, gamma (in radians) from a + rotation matrix. + """ + ex = self._cvec(1., 0., 0.) + ez = self._cvec(0., 0., 1.) + exs = rotmat.T * ex + ezs = rotmat.T * ez + enodes = np.cross(ez.T, ezs.T).T + if np.linalg.norm(enodes) < 1e-10: + enodes = exs + enodess = rotmat * enodes + cos_alpha = float((ez.T * ezs)) + if cos_alpha > 1.: + cos_alpha = 1. + if cos_alpha < -1.: + cos_alpha = -1. + alpha = np.arccos(cos_alpha) + beta = np.mod(np.arctan2(enodes[1, 0], enodes[0, 0]), np.pi * 2.) + gamma = np.mod(-np.arctan2(enodess[1, 0], enodess[0, 0]), np.pi * 2.) + return self._unique_euler(alpha, beta, gamma) + + def _unique_euler(self, alpha, beta, gamma): + """ + Uniquify euler angle triplet. + + Puts euler angles into ranges compatible with (dip,strike,-rake) in + seismology: + + alpha (dip) : [0, pi/2] + beta (strike) : [0, 2*pi) + gamma (-rake) : [-pi, pi) + + If alpha is near to zero, beta is replaced by beta+gamma and gamma is + set to zero, to prevent that additional ambiguity. + + If alpha is near to pi/2, beta is put into the range [0,pi). + """ + alpha = np.mod(alpha, 2.0 * pi) + + if 0.5 * pi < alpha and alpha <= pi: + alpha = pi - alpha + beta = beta + pi + gamma = 2.0 * pi - gamma + elif pi < alpha and alpha <= 1.5 * pi: + alpha = alpha - pi + gamma = pi - gamma + elif 1.5 * pi < alpha and alpha <= 2.0 * pi: + alpha = 2.0 * pi - alpha + beta = beta + pi + gamma = pi + gamma + + alpha = np.mod(alpha, 2.0 * pi) + beta = np.mod(beta, 2.0 * pi) + gamma = np.mod(gamma + pi, 2.0 * pi) - pi + + # If dip is exactly 90 degrees, one is still + # free to choose between looking at the plane from either side. + # Choose to look at such that beta is in the range [0,180) + + # This should prevent some problems, when dip is close to 90 degrees: + if abs(alpha - 0.5 * pi) < 1e-10: + alpha = 0.5 * pi + if abs(beta - pi) < 1e-10: + beta = pi + if abs(beta - 2. * pi) < 1e-10: + beta = 0. + if abs(beta) < 1e-10: + beta = 0. + + if alpha == 0.5 * pi and beta >= pi: + gamma = -gamma + beta = np.mod(beta - pi, 2.0 * pi) + gamma = np.mod(gamma + pi, 2.0 * pi) - pi + assert 0. <= beta < pi + assert -pi <= gamma < pi + + if alpha < 1e-7: + beta = np.mod(beta + gamma, 2.0 * pi) + gamma = 0. + + return (alpha, beta, gamma) + + def _matrix_w_style_and_system(self, M2return, system, style): + """ + Transform matrix into the given basis system 'system'. + + If the argument 'style' is set to 'fancy', a 'print' of the return + value yields a nice shell output of the matrix for better + visual control. + """ + + M2return = basis_transform_matrix(M2return, 'NED', system.upper()) + + if style.lower() in ['f', 'fan', 'fancy', 'y']: + return fancy_matrix(M2return) + else: + return M2return + + def _vector_w_style_and_system(self, vectors, system, style='n'): + """ + Transform vector(s) into the given basis system 'system'. + + If the argument 'style' is set to 'fancy', a 'print' of the return + value yields a nice shell output of the vector(s) for better + visual control. + + 'vectors' can be either a single array, tuple, matrix or a collection + in form of a list, array or matrix. + If it's a list, each entry will be checked, if it's 3D - if not, an + exception is raised. + If it's a matrix or array with column-length 3, the columns are + interpreted as vectors, otherwise, its transposed is used. + """ + + fancy = style.lower() in ['f', 'fan', 'fancy', 'y'] + + lo_vectors = [] + + # if list of vectors + if type(vectors) == list: + lo_vectors = vectors + + else: + assert 3 in vectors.shape + + if np.shape(vectors)[0] == 3: + for ii in np.arange(np.shape(vectors)[1]): + lo_vectors.append(vectors[:, ii]) + else: + for ii in np.arange(np.shape(vectors)[0]): + lo_vectors.append(vectors[:, ii].transpose()) + + lo_vecs_to_show = [] + for vec in lo_vectors: + + t_vec = basis_transform_vector(vec, 'NED', system.upper()) + + if fancy: + lo_vecs_to_show.append(fancy_vector(t_vec)) + else: + lo_vecs_to_show.append(t_vec) + + if len(lo_vecs_to_show) == 1: + return lo_vecs_to_show[0] + + else: + if fancy: + return ''.join(lo_vecs_to_show) + else: + return lo_vecs_to_show + + def get_M(self, system='NED', style='n'): + """ + Returns the moment tensor in matrix representation. + + Call with arguments to set ouput in other basis system or in fancy + style (to be viewed with 'print') + """ + return self._matrix_w_style_and_system(self._M, system, style) + + def get_decomposition(self, in_system='NED', out_system='NED', style='n'): + """ + Returns a tuple of the decomposition results. + + Order: + - 1 - basis of the provided input (string) + - 2 - basis of the representation (string) + - 3 - chosen decomposition type (integer) + + - 4 - full moment tensor (matrix) + + - 5 - isotropic part (matrix) + - 6 - isotropic percentage (float) + - 7 - deviatoric part (matrix) + - 8 - deviatoric percentage (float) + + - 9 - DC part (matrix) + -10 - DC percentage (float) + -11 - DC2 part (matrix) + -12 - DC2 percentage (float) + -13 - DC3 part (matrix) + -14 - DC3 percentage (float) + + -15 - CLVD part (matrix) + -16 - CLVD percentage (matrix) + + -17 - seismic moment (float) + -18 - moment magnitude (float) + + -19 - eigenvectors (3-array) + -20 - eigenvalues (list) + -21 - p-axis (3-array) + -22 - neutral axis (3-array) + -23 - t-axis (3-array) + -24 - faultplanes (list of two 3-arrays) + """ + return [in_system, out_system, self.get_decomp_type(), + self.get_M(system=out_system), + self.get_iso(system=out_system), self.get_iso_percentage(), + self.get_devi(system=out_system), self.get_devi_percentage(), + self.get_DC(system=out_system), self.get_DC_percentage(), + self.get_DC2(system=out_system), self.get_DC2_percentage(), + self.get_DC3(system=out_system), self.get_DC3_percentage(), + self.get_CLVD(system=out_system), self.get_CLVD_percentage(), + self.get_moment(), self.get_mag(), + self.get_eigvecs(system=out_system), + self.get_eigvals(system=out_system), + self.get_p_axis(system=out_system), + self.get_null_axis(system=out_system), + self.get_t_axis(system=out_system), + self.get_fps()] + + def __str__(self): + """ + Nice compilation of decomposition result to be viewed in the shell + (call with 'print'). + """ + + mexp = pow(10, np.ceil(np.log10(np.max(np.abs(self._M))))) + + m = basis_transform_matrix(self._M / mexp, 'NED', self._output_basis) + + def b(i, j): + x = self._output_basis.lower() + return x[i] + x[j] + + s = '\nScalar Moment: M0 = %g Nm (Mw = %3.1f)\n' + s += 'Moment Tensor: M%s = %6.3f, M%s = %6.3f, M%s = %6.3f,\n' + s += ' M%s = %6.3f, M%s = %6.3f, M%s = %6.3f' + s += ' [ x %g ]\n\n' + s = s % (self._seismic_moment, self._moment_magnitude, + b(0, 0), m[0, 0], + b(1, 1), m[1, 1], + b(2, 2), m[2, 2], + b(0, 1), m[0, 1], + b(0, 2), m[0, 2], + b(1, 2), m[1, 2], + mexp) + + s += self._fault_planes_as_str() + return s + + def _fault_planes_as_str(self): + """ + Internal setup of a nice string, containing information about the fault + planes. + """ + s = '\n' + for i, sdr in enumerate(self.get_fps()): + s += 'Fault plane %i: ' % (i + 1) + s += 'strike = %3.0f°, dip = %3.0f°, slip-rake = %4.0f°\n' % \ + (sdr[0], sdr[1], sdr[2]) + return s + + def get_input_system(self, style='n', **kwargs): + """ + Returns the basis system of the input. + """ + return self._input_basis + + def get_output_system(self, style='n', **kwargs): + """ + Returns the basis system of the output. + """ + return self._output_basis + + def get_decomp_type(self, style='n', **kwargs): + """ + Returns the decomposition type. + """ + + if style == 'y': + return MomentTensor.decomp_dict[self._decomposition_key][0] + + return self._decomposition_key + + def get_iso(self, system='NED', style='n'): + """ + Returns the isotropic part of the moment tensor in matrix + representation. + + Call with arguments to set ouput in other basis system or in fancy + style (to be viewed with 'print') + """ + return self._matrix_w_style_and_system(self._isotropic, system, style) + + def get_devi(self, system='NED', style='n'): + """ + Returns the deviatoric part of the moment tensor in matrix + representation. + + Call with arguments to set ouput in other basis system or in fancy + style (to be viewed with 'print') + """ + return self._matrix_w_style_and_system(self._deviatoric, system, style) + + def get_DC(self, system='NED', style='n'): + """ + Returns the Double Couple part of the moment tensor in matrix + representation. + + Call with arguments to set ouput in other basis system or in fancy + style (to be viewed with 'print') + """ + return self._matrix_w_style_and_system(self._DC, system, style) + + def get_DC2(self, system='NED', style='n'): + """ + Returns the second Double Couple part of the moment tensor in matrix + representation. + + Call with arguments to set ouput in other basis system or in fancy + style (to be viewed with 'print') + """ + + if self._DC2 is None: + if style == 'y': + return 'not available in this decomposition type' + else: + return None + + return self._matrix_w_style_and_system(self._DC2, system, style) + + def get_DC3(self, system='NED', style='n'): + """ + Returns the third Double Couple part of the moment tensor in matrix + representation. + + Call with arguments to set ouput in other basis system or in fancy + style (to be viewed with 'print') + """ + + if self._DC3 is None: + if style == 'y': + return 'not available in this decomposition type' + else: + return None + + return self._matrix_w_style_and_system(self._DC3, system, style) + + def get_CLVD(self, system='NED', style='n'): + """ + Returns the CLVD part of the moment tensor in matrix representation. + + Call with arguments to set ouput in other basis system or in fancy + style (to be viewed with 'print') + """ + if self._CLVD is None: + if style == 'y': + return 'not available in this decomposition type' + else: + return None + + return self._matrix_w_style_and_system(self._CLVD, system, style) + + def get_DC_percentage(self, system='NED', style='n'): + """ + Returns the percentage of the DC part of the moment tensor in matrix + representation. + """ + + return self._DC_percentage + + def get_CLVD_percentage(self, system='NED', style='n'): + """ + Returns the percentage of the DC part of the moment tensor in matrix + representation. + """ + + if self._CLVD is None: + if style == 'y': + return 'not available in this decomposition type' + else: + return None + + return int(100 - self._iso_percentage - self._DC_percentage) + + def get_DC2_percentage(self, system='NED', style='n'): + """ + Returns the percentage of the second DC part of the moment tensor in + matrix representation. + """ + + if self._DC2 is None: + if style == 'y': + return 'not available in this decomposition type' + else: + return None + + return self._DC2_percentage + + def get_DC3_percentage(self, system='NED', style='n'): + """ + Returns the percentage of the third DC part of the moment tensor in + matrix representation. + """ + + if self._DC3 is None: + if style == 'y': + return 'not available in this decomposition type' + else: + return None + + return int(100 - self._DC2_percentage - self._DC_percentage) + + def get_iso_percentage(self, system='NED', style='n'): + """ + Returns the percentage of the isotropic part of the moment tensor in + matrix representation. + """ + return self._iso_percentage + + def get_devi_percentage(self, system='NED', style='n'): + """ + Returns the percentage of the deviatoric part of the moment tensor in + matrix representation. + """ + return int(100 - self._iso_percentage) + + def get_moment(self, system='NED', style='n'): + """ + Returns the seismic moment (in Nm) of the moment tensor. + """ + return self._seismic_moment + + def get_mag(self, system='NED', style='n'): + """ + Returns the moment magnitude M_w of the moment tensor. + """ + return self._moment_magnitude + + def get_decomposition_key(self, system='NED', style='n'): + """ + 10 = standard decomposition (Jost & Herrmann) + """ + return self._decomposition_key + + def get_eigvals(self, system='NED', style='n', **kwargs): + """ + Returns a list of the eigenvalues of the moment tensor. + """ + if style == 'y': + return '%g, %g, %g' % tuple(self._eigenvalues) + + # in the order HNS: + return self._eigenvalues + + def get_eigvecs(self, system='NED', style='n'): + """ + Returns the eigenvectors of the moment tensor. + + Call with arguments to set ouput in other basis system or in fancy + style (to be viewed with 'print') + """ + return self._vector_w_style_and_system(self._eigenvectors, system, + style) + + def get_null_axis(self, system='NED', style='n'): + """ + Returns the neutral axis of the moment tensor. + + Call with arguments to set ouput in other basis system or in fancy + style (to be viewed with 'print') + """ + + return self._vector_w_style_and_system(self._null_axis, system, style) + + def get_t_axis(self, system='NED', style='n'): + """ + Returns the tension axis of the moment tensor. + + Call with arguments to set ouput in other basis system or in fancy + style (to be viewed with 'print') + """ + return self._vector_w_style_and_system(self._t_axis, system, style) + + def get_p_axis(self, system='NED', style='n'): + """ + Returns the pressure axis of the moment tensor. + + Call with arguments to set ouput in other basis system or in fancy + style (to be viewed with 'print') + """ + return self._vector_w_style_and_system(self._p_axis, system, style) + + def get_transform_matrix(self, system='NED', style='n'): + """ + Returns the transformation matrix (input system to principal axis + system. + + Call with arguments to set ouput in other basis system or in fancy + style (to be viewed with 'print') + """ + return self._matrix_w_style_and_system(self._rotation_matrix, system, + style) + + def get_fps(self, **kwargs): + """ + Returns a list of the two faultplane 3-tuples, each showing strike, + dip, slip-rake. + """ + fancy_key = kwargs.get('style', '0') + if fancy_key[0].lower() == 'y': + return self._fault_planes_as_str() + else: + return self._faultplanes + + def get_colour_order(self, **kwargs): + """ + Returns the value of the plotting order (only important in BeachBall + instances). + """ + return self._plot_clr_order + +MomentTensor.decomp_dict = { + 1: ('ISO + DC + CLVD', + MomentTensor._standard_decomposition), + 2: ('ISO + major DC + minor DC', + MomentTensor._decomposition_w_2DC), + 3: ('ISO + DC1 + DC2 + DC3', + MomentTensor._decomposition_w_3DC), + 4: ('ISO + strike DC + dip DC + CLVD', + MomentTensor._decomposition_w_CLVD_2DC), +} + + +def fancy_matrix(m_in): + m = m_in.copy() + + norm_factor = round(max(abs(np.array(m).flatten())), 5) + + try: + if (norm_factor < 0.1) or (norm_factor >= 10): + if not abs(norm_factor) == 0: + m = m / norm_factor + out = "\n / %5.2F %5.2F %5.2F \\\n" % \ + (m[0, 0], m[0, 1], m[0, 2]) + out += " | %5.2F %5.2F %5.2F | x %F\n" % \ + (m[1, 0], m[1, 1], m[1, 2], norm_factor) + out += " \\ %5.2F %5.2F %5.2F /\n" % \ + (m[2, 0], m[2, 1], m[2, 2]) + return out + except: + pass + + return "\n / %5.2F %5.2F %5.2F \\\n" % (m[0, 0], m[0, 1], m[0, 2]) + \ + " | %5.2F %5.2F %5.2F | \n" % (m[1, 0], m[1, 1], m[1, 2]) + \ + " \\ %5.2F %5.2F %5.2F /\n" % (m[2, 0], m[2, 1], m[2, 2]) + + +def fancy_vector(v): + """ + Returns a given 3-vector or array in a cute way on the shell, if you + use 'print' on the return value. + """ + return "\n / %5.2F \\\n" % (v[0]) + \ + " | %5.2F |\n" % (v[1]) + \ + " \\ %5.2F /\n" % (v[2]) + + +class BeachBall: + + """ + Class for generating a beachball projection for a provided moment tensor + object. + + Input for instance generation: MomentTensor object [,keywords dictionary] + + Output can be plots of + - the eigensystem + - the complete sphere + - the projection to a unit sphere + ... either lower (standard) or upper half + + Beside the plots, the unit sphere projection may be saved in a given file. + + Alternatively, only the file can be provided without showing anything + directly. + """ + def __init__(self, MT=MomentTensor, kwargs_dict={}, npoints=360): + self.MT = MT + self._M = MT._M + self._set_standard_attributes() + self._update_attributes(kwargs_dict) + + self._plot_n_points = npoints + self._nodallines_in_NED_system() + self.arange_1 = np.arange(3 * npoints) - 1 + # self._identify_faultplanes() + + def ploBB(self, kwargs, ax=None): + """ + Plots the projection of the beachball onto a unit sphere. + """ + self._update_attributes(kwargs) + self._setup_BB() + self._plot_US(ax=ax) + + def save_BB(self, kwargs): + """ + Method for saving the 2D projection of the beachball without showing + the plot. + + :param outfile: name of outfile, addressing w.r.t. current directory + :param format: if no implicit valid format is provided within the + filename, add file format + """ + self._update_attributes(kwargs) + self._setup_BB() + self._just_save_bb() + + def _just_save_bb(self): + """ + Internal method for saving the beachball unit sphere plot into a given + file. + + This method tries to setup the approprite backend according to the + requested file format first. 'AGG' is used in most cases. + """ + import matplotlib + + if self._plot_outfile_format == 'svg': + try: + matplotlib.use('SVG') + except: + matplotlib.use('Agg') + elif self._plot_outfile_format == 'pdf': + try: + matplotlib.use('PDF') + except: + matplotlib.use('Agg') + pass + elif self._plot_outfile_format == 'ps': + try: + matplotlib.use('PS') + except: + matplotlib.use('Agg') + pass + elif self._plot_outfile_format == 'eps': + try: + matplotlib.use('Agg') + except: + matplotlib.use('PS') + pass + elif self._plot_outfile_format == 'png': + try: + matplotlib.use('AGG') + except: + mp_out = matplotlib.use('GTKCairo') + if mp_out: + mp_out2 = matplotlib.use('Cairo') + if mp_out2: + matplotlib.use('GDK') + + # finally generating the actual plot + import pylab as P + + plotfig = self._setup_plot_US(P) + + outfile_format = self._plot_outfile_format + outfile_name = self._plot_outfile + + outfile_abs_name = os.path.realpath( + os.path.abspath(os.path.join(os.curdir, outfile_name))) + + # save plot into file + try: + plotfig.savefig(outfile_abs_name, dpi=self._plot_dpi, + transparent=True, format=outfile_format) + except: + print('ERROR!! -- Saving of plot not possible') + return + P.close(667) + del P + del matplotlib + + def get_psxy(self, kwargs): + """ + Method returning one single string, which can be piped into the psxy + method of the GMT package. + + :param GMT_type: fill/lines/EVs (select type of string), + default is 'fill' + :param GMT_scaling: scale the beachball - default radius is 1.0 + :param GMT_tension_colour: tension area of BB - colour flag for -Z in + psxy, default is 1 + :param GMT_pressure_colour: pressure area of BB - colour flag for -Z in + psxy, default is 0 + :param GMT_show_2FPs: flag, if both faultplanes are to be shown, + default is 0 + :param GMT_show_1FP: flag, if one faultplane is to be shown, default + is 1 + :param GMT_FP_index: 1 or 2, default is 2 + """ + self._GMT_type = 'fill' + self._GMT_2fps = False + self._GMT_1fp = 0 + + self._GMT_psxy_fill = None + self._GMT_psxy_nodals = None + self._GMT_psxy_EVs = None + self._GMT_scaling = 1. + + self._GMT_tension_colour = 1 + self._GMT_pressure_colour = 0 + + self._update_attributes(kwargs) + + self._setup_BB() + + self._set_GMT_attributes() + + if self._GMT_type == 'fill': + self._GMT_psxy_fill.seek(0) + GMT_string = self._GMT_psxy_fill.getvalue() + elif self._GMT_type == 'lines': + self._GMT_psxy_nodals.seek(0) + GMT_string = self._GMT_psxy_nodals.getvalue() + else: + GMT_string = self._GMT_psxy_EVs.getvalue() + + return GMT_string + + def _add_2_GMT_string(self, FH_string, curve, colour): + """ + Writes coordinate pair list of given curve as string into temporal + file handler. + """ + colour_Z = colour + wstring = '> -Z%i\n' % (colour_Z) + FH_string.write(wstring) + np.savetxt(FH_string, self._GMT_scaling * curve.transpose()) + + def _set_GMT_attributes(self): + """ + Set the beachball lines and nodals as strings into a file handler. + """ + neg_nodalline = self._nodalline_negative_final_US + pos_nodalline = self._nodalline_positive_final_US + FP1_2_plot = self._FP1_final_US + FP2_2_plot = self._FP2_final_US + EV_2_plot = self._all_EV_2D_US[:, :2].transpose() + US = self._unit_sphere + + tension_colour = self._GMT_tension_colour + pressure_colour = self._GMT_pressure_colour + + # build strings for possible GMT-output, used by 'psxy' + GMT_string_FH = StringIO() + GMT_linestring_FH = StringIO() + GMT_EVs_FH = StringIO() + + self._add_2_GMT_string(GMT_EVs_FH, EV_2_plot, tension_colour) + GMT_EVs_FH.flush() + + if self._plot_clr_order > 0: + self._add_2_GMT_string(GMT_string_FH, US, pressure_colour) + self._add_2_GMT_string(GMT_string_FH, neg_nodalline, + tension_colour) + self._add_2_GMT_string(GMT_string_FH, pos_nodalline, + tension_colour) + GMT_string_FH.flush() + + if self._plot_curve_in_curve != 0: + self._add_2_GMT_string(GMT_string_FH, US, tension_colour) + + if self._plot_curve_in_curve < 1: + self._add_2_GMT_string(GMT_string_FH, neg_nodalline, + pressure_colour) + self._add_2_GMT_string(GMT_string_FH, pos_nodalline, + tension_colour) + GMT_string_FH.flush() + else: + self._add_2_GMT_string(GMT_string_FH, pos_nodalline, + pressure_colour) + self._add_2_GMT_string(GMT_string_FH, neg_nodalline, + tension_colour) + GMT_string_FH.flush() + else: + self._add_2_GMT_string(GMT_string_FH, US, tension_colour) + self._add_2_GMT_string(GMT_string_FH, neg_nodalline, + pressure_colour) + self._add_2_GMT_string(GMT_string_FH, pos_nodalline, + pressure_colour) + GMT_string_FH.flush() + + if self._plot_curve_in_curve != 0: + self._add_2_GMT_string(GMT_string_FH, US, pressure_colour) + if self._plot_curve_in_curve < 1: + self._add_2_GMT_string(GMT_string_FH, neg_nodalline, + tension_colour) + self._add_2_GMT_string(GMT_string_FH, pos_nodalline, + pressure_colour) + GMT_string_FH.flush() + else: + self._add_2_GMT_string(GMT_string_FH, pos_nodalline, + tension_colour) + self._add_2_GMT_string(GMT_string_FH, neg_nodalline, + pressure_colour) + + GMT_string_FH.flush() + + # set all nodallines and faultplanes for plotting: + self._add_2_GMT_string(GMT_linestring_FH, neg_nodalline, + tension_colour) + self._add_2_GMT_string(GMT_linestring_FH, pos_nodalline, + tension_colour) + + if self._GMT_2fps: + self._add_2_GMT_string(GMT_linestring_FH, FP1_2_plot, + tension_colour) + self._add_2_GMT_string(GMT_linestring_FH, FP2_2_plot, + tension_colour) + + elif self._GMT_1fp: + if not int(self._GMT_1fp) in [1, 2]: + print('no fault plane specified for being plotted...continue') + print('without fault plane(s)') + pass + else: + if int(self._GMT_1fp) == 1: + self._add_2_GMT_string(GMT_linestring_FH, FP1_2_plot, + tension_colour) + else: + self._add_2_GMT_string(GMT_linestring_FH, FP2_2_plot, + tension_colour) + + self._add_2_GMT_string(GMT_linestring_FH, US, tension_colour) + + GMT_linestring_FH.flush() + + setattr(self, '_GMT_psxy_nodals', GMT_linestring_FH) + setattr(self, '_GMT_psxy_fill', GMT_string_FH) + setattr(self, '_GMT_psxy_EVs', GMT_EVs_FH) + + def get_MT(self): + """ + Returns the original moment tensor object, handed over to the class at + generating this instance. + """ + return self.MT + + def full_sphere_plot(self, kwargs): + """ + Plot of the full beachball, projected on a circle with a radius 2. + """ + self._update_attributes(kwargs) + self._setup_BB() + self._aux_plot() + + def _aux_plot(self): + """ + Generates the final plot of the total sphere (according to the chosen + 2D-projection. + """ + from matplotlib import interactive + import pylab as P + + P.close('all') + plotfig = P.figure(665, figsize=(self._plot_aux_plot_size, + self._plot_aux_plot_size)) + + plotfig.subplots_adjust(left=0, bottom=0, right=1, top=1) + ax = plotfig.add_subplot(111, aspect='equal') + # P.axis([-1.1,1.1,-1.1,1.1],'equal') + ax.axison = False + + EV_2_plot = getattr(self, '_all_EV' + '_final') + BV_2_plot = getattr(self, '_all_BV' + '_final').transpose() + curve_pos_2_plot = getattr(self, '_nodalline_positive' + '_final') + curve_neg_2_plot = getattr(self, '_nodalline_negative' + '_final') + FP1_2_plot = getattr(self, '_FP1' + '_final') + FP2_2_plot = getattr(self, '_FP2' + '_final') + + tension_colour = self._plot_tension_colour + pressure_colour = self._plot_pressure_colour + + if self._plot_clr_order > 0: + if self._plot_fill_flag: + + alpha = self._plot_fill_alpha * self._plot_total_alpha + ax.fill(self._outer_circle[0, :], self._outer_circle[1, :], + fc=pressure_colour, alpha=alpha) + ax.fill(curve_pos_2_plot[0, :], curve_pos_2_plot[1, :], + fc=tension_colour, alpha=alpha) + ax.fill(curve_neg_2_plot[0, :], curve_neg_2_plot[1, :], + fc=tension_colour, alpha=alpha) + + if self._plot_curve_in_curve != 0: + ax.fill(self._outer_circle[0, :], self._outer_circle[1, :], + fc=tension_colour, alpha=alpha) + if self._plot_curve_in_curve < 1: + ax.fill(curve_neg_2_plot[0, :], curve_neg_2_plot[1, :], + fc=pressure_colour, alpha=alpha) + ax.fill(curve_pos_2_plot[0, :], curve_pos_2_plot[1, :], + fc=tension_colour, alpha=alpha) + else: + ax.fill(curve_pos_2_plot[0, :], curve_pos_2_plot[1, :], + fc=pressure_colour, alpha=alpha) + ax.fill(curve_neg_2_plot[0, :], curve_neg_2_plot[1, :], + fc=tension_colour, alpha=alpha) + + if self._plot_show_princ_axes: + alpha = self._plot_princ_axes_alpha * self._plot_total_alpha + ax.plot([EV_2_plot[0, 0]], [EV_2_plot[1, 0]], 'm^', + ms=self._plot_princ_axes_symsize, + lw=self._plot_princ_axes_lw, alpha=alpha) + ax.plot([EV_2_plot[0, 3]], [EV_2_plot[1, 3]], 'mv', + ms=self._plot_princ_axes_symsize, + lw=self._plot_princ_axes_lw, alpha=alpha) + ax.plot([EV_2_plot[0, 1]], [EV_2_plot[1, 1]], 'b^', + ms=self._plot_princ_axes_symsize, + lw=self._plot_princ_axes_lw, alpha=alpha) + ax.plot([EV_2_plot[0, 4]], [EV_2_plot[1, 4]], 'bv', + ms=self._plot_princ_axes_symsize, + lw=self._plot_princ_axes_lw, alpha=alpha) + ax.plot([EV_2_plot[0, 2]], [EV_2_plot[1, 2]], 'g^', + ms=self._plot_princ_axes_symsize, + lw=self._plot_princ_axes_lw, alpha=alpha) + ax.plot([EV_2_plot[0, 5]], [EV_2_plot[1, 5]], 'gv', + ms=self._plot_princ_axes_symsize, + lw=self._plot_princ_axes_lw, alpha=alpha) + else: + if self._plot_fill_flag: + alpha = self._plot_fill_alpha * self._plot_total_alpha + ax.fill(self._outer_circle[0, :], self._outer_circle[1, :], + fc=tension_colour, alpha=alpha) + ax.fill(curve_pos_2_plot[0, :], curve_pos_2_plot[1, :], + fc=pressure_colour, alpha=alpha) + ax.fill(curve_neg_2_plot[0, :], curve_neg_2_plot[1, :], + fc=pressure_colour, alpha=alpha) + + if self._plot_curve_in_curve != 0: + ax.fill(self._outer_circle[0, :], self._outer_circle[1, :], + fc=pressure_colour, alpha=alpha) + if self._plot_curve_in_curve < 0: + ax.fill(curve_neg_2_plot[0, :], curve_neg_2_plot[1, :], + fc=tension_colour, alpha=alpha) + ax.fill(curve_pos_2_plot[0, :], curve_pos_2_plot[1, :], + fc=pressure_colour, alpha=alpha) + pass + else: + ax.fill(curve_pos_2_plot[0, :], curve_pos_2_plot[1, :], + fc=tension_colour, alpha=alpha) + ax.fill(curve_neg_2_plot[0, :], curve_neg_2_plot[1, :], + fc=pressure_colour, alpha=alpha) + pass + + if self._plot_show_princ_axes: + alpha = self._plot_princ_axes_alpha * self._plot_total_alpha + ax.plot([EV_2_plot[0, 0]], [EV_2_plot[1, 0]], 'g^', + ms=self._plot_princ_axes_symsize, + lw=self._plot_princ_axes_lw, alpha=alpha) + ax.plot([EV_2_plot[0, 3]], [EV_2_plot[1, 3]], 'gv', + ms=self._plot_princ_axes_symsize, + lw=self._plot_princ_axes_lw, alpha=alpha) + ax.plot([EV_2_plot[0, 1]], [EV_2_plot[1, 1]], 'b^', + ms=self._plot_princ_axes_symsize, + lw=self._plot_princ_axes_lw, alpha=alpha) + ax.plot([EV_2_plot[0, 4]], [EV_2_plot[1, 4]], 'bv', + ms=self._plot_princ_axes_symsize, + lw=self._plot_princ_axes_lw, alpha=alpha) + ax.plot([EV_2_plot[0, 2]], [EV_2_plot[1, 2]], 'm^', + ms=self._plot_princ_axes_symsize, + lw=self._plot_princ_axes_lw, alpha=alpha) + ax.plot([EV_2_plot[0, 5]], [EV_2_plot[1, 5]], 'mv', + ms=self._plot_princ_axes_symsize, + lw=self._plot_princ_axes_lw, alpha=alpha) + + self._plot_nodalline_colour = 'y' + + ax.plot(curve_neg_2_plot[0, :], curve_neg_2_plot[1, :], 'o', + c=self._plot_nodalline_colour, lw=self._plot_nodalline_width, + alpha=self._plot_nodalline_alpha * self._plot_total_alpha, + ms=3) + + self._plot_nodalline_colour = 'b' + + ax.plot(curve_pos_2_plot[0, :], curve_pos_2_plot[1, :], 'D', + c=self._plot_nodalline_colour, lw=self._plot_nodalline_width, + alpha=self._plot_nodalline_alpha * self._plot_total_alpha, + ms=3) + + if self._plot_show_1faultplane: + if self._plot_show_FP_index == 1: + ax.plot(FP1_2_plot[0, :], FP1_2_plot[1, :], '+', + c=self._plot_faultplane_colour, + lw=self._plot_faultplane_width, + alpha=self._plot_faultplane_alpha * + self._plot_total_alpha, ms=5) + elif self._plot_show_FP_index == 2: + ax.plot(FP2_2_plot[0, :], FP2_2_plot[1, :], '+', + c=self._plot_faultplane_colour, + lw=self._plot_faultplane_width, + alpha=self._plot_faultplane_alpha * + self._plot_total_alpha, ms=5) + + elif self._plot_show_faultplanes: + ax.plot(FP1_2_plot[0, :], FP1_2_plot[1, :], '+', + c=self._plot_faultplane_colour, + lw=self._plot_faultplane_width, + alpha=self._plot_faultplane_alpha * self._plot_total_alpha, + ms=4) + ax.plot(FP2_2_plot[0, :], FP2_2_plot[1, :], '+', + c=self._plot_faultplane_colour, + lw=self._plot_faultplane_width, + alpha=self._plot_faultplane_alpha * self._plot_total_alpha, + ms=4) + else: + pass + + # if isotropic part shall be displayed, fill the circle completely with + # the appropriate colour + if self._pure_isotropic: + if abs(np.trace(self._M)) > epsilon: + if self._plot_clr_order < 0: + ax.fill(self._outer_circle[0, :], self._outer_circle[1, :], + fc=tension_colour, alpha=1, zorder=100) + else: + ax.fill(self._outer_circle[0, :], self._outer_circle[1, :], + fc=pressure_colour, alpha=1, zorder=100) + + # plot NED basis vectors + if self._plot_show_basis_axes: + plot_size_in_points = self._plot_size * 2.54 * 72 + points_per_unit = plot_size_in_points / 2. + + fontsize = plot_size_in_points / 66. + symsize = plot_size_in_points / 77. + + direction_letters = list('NSEWDU') + for idx, val in enumerate(BV_2_plot): + x_coord = val[0] + y_coord = val[1] + np_letter = direction_letters[idx] + + rot_angle = -np.arctan2(y_coord, x_coord) + pi / 2. + original_rho = np.sqrt(x_coord ** 2 + y_coord ** 2) + + marker_x = (original_rho - (3 * symsize / points_per_unit)) * \ + np.sin(rot_angle) + marker_y = (original_rho - (3 * symsize / points_per_unit)) * \ + np.cos(rot_angle) + annot_x = (original_rho - (8.5 * fontsize / points_per_unit)) \ + * np.sin(rot_angle) + annot_y = (original_rho - (8.5 * fontsize / points_per_unit)) \ + * np.cos(rot_angle) + + ax.text(annot_x, annot_y, np_letter, + horizontalalignment='center', size=fontsize, + weight='bold', verticalalignment='center', + bbox=dict(edgecolor='white', facecolor='white', + alpha=1)) + + if original_rho > epsilon: + ax.scatter([marker_x], [marker_y], + marker=(3, 0, rot_angle), s=symsize ** 2, c='k', + facecolor='k', zorder=300) + else: + ax.scatter([x_coord], [y_coord], marker=(4, 1, rot_angle), + s=symsize ** 2, c='k', facecolor='k', + zorder=300) + + # plot both circle lines (radius 1 and 2) + ax.plot(self._unit_sphere[0, :], self._unit_sphere[1, :], + c=self._plot_outerline_colour, lw=self._plot_outerline_width, + alpha=self._plot_outerline_alpha * self._plot_total_alpha) + ax.plot(self._outer_circle[0, :], self._outer_circle[1, :], + c=self._plot_outerline_colour, lw=self._plot_outerline_width, + alpha=self._plot_outerline_alpha * self._plot_total_alpha) + + # dummy points for setting plot plot size more accurately + ax.plot([0, 2.1, 0, -2.1], [2.1, 0, -2.1, 0], ',', alpha=0.) + + ax.autoscale_view(tight=True, scalex=True, scaley=True) + interactive(True) + + if self._plot_save_plot: + try: + plotfig.savefig(self._plot_outfile + '.' + + self._plot_outfile_format, dpi=self._plot_dpi, + transparent=True, + format=self._plot_outfile_format) + except: + print('saving of plot not possible') + + P.show() + + def pa_plot(self, kwargs): + """ + Plot of the solution in the principal axes system. + """ + import pylab as P + + self._update_attributes(kwargs) + + r_hor = self._r_hor_for_pa_plot + r_hor_FP = self._r_hor_FP_for_pa_plot + + P.rc('grid', color='#316931', linewidth=0.5, linestyle='-.') + P.rc('xtick', labelsize=12) + P.rc('ytick', labelsize=10) + + width, height = P.rcParams['figure.figsize'] + size = min(width, height) + + fig = P.figure(34, figsize=(size, size)) + P.clf() + ax = fig.add_axes([0.1, 0.1, 0.8, 0.8], polar=True, axisbg='#d5de9c') + + r_steps = [0.000001] + for i in (np.arange(4) + 1) * 0.2: + r_steps.append(i) + r_labels = ['S'] + for ii in range(len(r_steps)): + if (ii + 1) % 2 == 0: + r_labels.append(str(r_steps[ii])) + else: + r_labels.append(' ') + + t_angles = np.arange(0., 360., 90) + t_labels = [' N ', ' H ', ' - N', ' - H'] + + P.thetagrids(t_angles, labels=t_labels) + + ax.plot(self._phi_curve, r_hor, color='r', lw=3) + ax.plot(self._phi_curve, r_hor_FP, color='b', lw=1.5) + ax.set_rmax(1.0) + P.grid(True) + + P.rgrids((r_steps), labels=r_labels) + + ax.set_title("beachball in eigenvector system", fontsize=15) + + if self._plot_save_plot: + try: + fig.savefig(self._plot_outfile + '.' + + self._plot_outfile_format, dpi=self._plot_dpi, + transparent=True, + format=self._plot_outfile_format) + except: + print('saving of plot not possible') + P.show() + + def _set_standard_attributes(self): + """ + Sets default values of mandatory arguments. + """ + # plot basis system and view point: + self._plot_basis = 'NED' + self._plot_projection = 'lambert' + self._plot_viewpoint = [0., 0., 0.] + self._plot_basis_change = None + + # flag, if upper hemisphere is seen instead + self._plot_show_upper_hemis = False + + # flag, if isotropic part shall be considered + self._plot_isotropic_part = False + self._pure_isotropic = False + + # number of minimum points per line and full circle (number/360 is + # minimum of points per degree at rounded lines) + self._plot_n_points = 360 + + # nodal line of pressure and tension regimes: + self._plot_nodalline_width = 2 + self._plot_nodalline_colour = 'k' + self._plot_nodalline_alpha = 1. + + # outer circle line + self._plot_outerline_width = 2 + self._plot_outerline_colour = 'k' + self._plot_outerline_alpha = 1. + + # faultplane(s) + self._plot_faultplane_width = 4 + self._plot_faultplane_colour = 'b' + self._plot_faultplane_alpha = 1. + + self._plot_show_faultplanes = False + self._plot_show_1faultplane = False + self._plot_show_FP_index = 1 + + # principal axes: + self._plot_show_princ_axes = False + self._plot_princ_axes_symsize = 10 + self._plot_princ_axes_lw = 3 + self._plot_princ_axes_alpha = 0.5 + + # NED basis: + self._plot_show_basis_axes = False + + # filling of the area: + self._plot_clr_order = self.MT.get_colour_order() + self._plot_curve_in_curve = 0 + self._plot_fill_flag = True + self._plot_tension_colour = 'r' + self._plot_pressure_colour = 'w' + self._plot_fill_alpha = 1. + + # general plot options + self._plot_size = 5 + self._plot_aux_plot_size = 5 + self._plot_dpi = 200 + + self._plot_total_alpha = 1. + + # possibility to add external data (e.g. measured polariations) + self._plot_external_data = False + self._external_data = None + + # if, howto, whereto save the plot + self._plot_save_plot = False + self._plot_outfile = './BB_plot_example' + self._plot_outfile_format = 'svg' + + def _update_attributes(self, kwargs): + """ + Makes an internal update of the object's attributes with the + provided list of keyword arguments. + + If the keyword (extended by a leading _ ) is in the dict of + the object, the value is updated. Otherwise, the keyword is + ignored. + """ + for key in kwargs.keys(): + if key[0] == '_': + kw = key[1:] + else: + kw = key + if '_' + kw in dir(self): + setattr(self, '_' + kw, kwargs[key]) + + if kwargs.get('plot_only_lines', False): + setattr(self, '_plot_fill_flag', False) + + def _setup_BB(self, unit_circle=True): + """ + Setup of the beachball, when a plotting method is evoked. + + Contains all the technical stuff for generating the final view of the + beachball: + + - Finding a rotation matrix, describing the given viewpoint onto the + beachball projection + - Rotating all elements (lines, points) w.r.t. the given viewpoint + - Projecting the 3D sphere into the 2D plane + - Building circle lines in radius r=1 and r=2 + - Correct the order of line points, yielding a consecutive set of + points for drawing lines + - Smoothing of all curves, avoiding nasty sectioning connection lines + - Checking, if the two nodalline curves are laying completely within + each other ( cahnges plotting order of overlay plot construction) + - Projection of final smooth solution onto the standard unit sphere + """ + self._find_basis_change_2_new_viewpoint() + self._rotate_all_objects_2_new_view() + self._vertical_2D_projection() + + if unit_circle: + self._build_circles() + + if not self.MT._iso_percentage == 100: + self._correct_curves() + self._smooth_curves() + self._check_curve_in_curve() + + self._projection_2_unit_sphere() + + if self.MT._iso_percentage == 100: + if np.trace(self.MT.get_M()) < 0: + self._plot_clr_order = 1 + else: + self._plot_clr_order = -1 + + def _correct_curves(self): + """ + Correcting potentially wrong curves. + + Checks, if the order of the given coordinates of the lines must be + re-arranged, allowing for an automatical line plotting. + """ + list_of_curves_2_correct = ['nodalline_negative', 'nodalline_positive', + 'FP1', 'FP2'] + n_curve_points = self._plot_n_points + + for obj in list_of_curves_2_correct: + obj2cor_name = '_' + obj + '_2D' + obj2cor = getattr(self, obj2cor_name) + + obj2cor_in_right_order = self._sort_curve_points(obj2cor) + + # logger.debug( 'curve: ', str(obj)) + # check, if curve closed !!!!!! + start_r = np.sqrt(obj2cor_in_right_order[0, 0] ** 2 + + obj2cor_in_right_order[1, 0] ** 2) + r_last_point = np.sqrt(obj2cor_in_right_order[0, -1] ** 2 + + obj2cor_in_right_order[1, -1] ** 2) + dist_last_first_point = \ + np.sqrt((obj2cor_in_right_order[0, -1] - + obj2cor_in_right_order[0, 0]) ** 2 + + (obj2cor_in_right_order[1, -1] - + obj2cor_in_right_order[1, 0]) ** 2) + + # check, if distance between last and first point is smaller than + # the distance between last point and the edge (at radius=2) + if dist_last_first_point > (2 - r_last_point): + # add points on edge to polygon, if it is an open curve + # logger.debug( str(obj)+' not closed - closing over edge... ') + phi_end = np.arctan2(obj2cor_in_right_order[0, -1], + obj2cor_in_right_order[1, -1]) % (2 * pi) + R_end = r_last_point + phi_start = np.arctan2(obj2cor_in_right_order[0, 0], + obj2cor_in_right_order[1, 0]) % (2 * pi) + R_start = start_r + + # add one point on the edge every fraction of degree given by + # input parameter, increase the radius linearily + phi_end_larger = np.sign(phi_end - phi_start) + angle_smaller_pi = np.sign(pi - np.abs(phi_end - phi_start)) + + if phi_end_larger * angle_smaller_pi > 0: + go_ccw = True + openangle = (phi_end - phi_start) % (2 * pi) + else: + go_ccw = False + openangle = (phi_start - phi_end) % (2 * pi) + + radius_interval = R_start - R_end # closing from end to start + + n_edgepoints = int(openangle * rad2deg * + n_curve_points / 360.) - 1 + # logger.debug( 'open angle %.2f degrees - filling with %i + # points on the edge\n'%(openangle/pi*180,n_edgepoints)) + if go_ccw: + obj2cor_in_right_order = \ + list(obj2cor_in_right_order.transpose()) + for kk in range(n_edgepoints + 1): + current_phi = phi_end - kk * openangle / \ + (n_edgepoints + 1) + current_radius = R_end + kk * radius_interval / \ + (n_edgepoints + 1) + temp = [current_radius * math.sin(current_phi), + current_radius * np.cos(current_phi)] + obj2cor_in_right_order.append(temp) + obj2cor_in_right_order = \ + np.array(obj2cor_in_right_order).transpose() + else: + obj2cor_in_right_order = \ + list(obj2cor_in_right_order.transpose()) + for kk in range(n_edgepoints + 1): + current_phi = phi_end + kk * openangle / \ + (n_edgepoints + 1) + current_radius = R_end + kk * radius_interval / \ + (n_edgepoints + 1) + temp = [current_radius * math.sin(current_phi), + current_radius * np.cos(current_phi)] + obj2cor_in_right_order.append(temp) + obj2cor_in_right_order = \ + np.array(obj2cor_in_right_order).transpose() + setattr(self, '_' + obj + '_in_order', obj2cor_in_right_order) + return 1 + + def _nodallines_in_NED_system(self): + """ + The two nodal lines between the areas on a beachball are given by the + points, where tan²(alpha) = (-EWs/(EWN*cos(phi)**2 + EWh*sin(phi)**2)) + is fulfilled. + + This solution is gained in the principal axes system and then expressed + in terms of the NED basis system + + output: + - set of points, building the first nodal line, coordinates in the + input basis system (standard NED) + - set of points, building the second nodal line, coordinates in the + input basis system (standard NED) + - array with 6 points, describing positive and negative part of 3 + principal axes + - array with partition of full circle (angle values in degrees) + fraction is given by parametre n_curve_points + """ + # build the nodallines of positive/negative areas in the principal axes + # system + n_curve_points = self._plot_n_points + + # phi is the angle between neutral axis and horizontal projection + # of the curve point to the surface, spanned by H- and + # N-axis. Running mathematically negative (clockwise) around the + # SIGMA-axis. Stepsize is given by the parametre for number of + # curve points + phi = (np.arange(n_curve_points) / float(n_curve_points) + + 1. / n_curve_points) * 2 * pi + self._phi_curve = phi + + # analytical/geometrical solution for separatrix curve - alpha is + # opening angle between principal axis SIGMA and point of curve. (alpha + # is 0, if curve lies directly on the SIGMA axis) + + # CASE: including isotropic part + # sigma axis flippes, if EWn flippes sign + + EWh_devi = self.MT.get_eigvals()[0] - 1. / 3 * np.trace(self._M) + EWn_devi = self.MT.get_eigvals()[1] - 1. / 3 * np.trace(self._M) + EWs_devi = self.MT.get_eigvals()[2] - 1. / 3 * np.trace(self._M) + + if not self._plot_isotropic_part: + EWh = EWh_devi + EWn = EWn_devi + EWs = EWs_devi + else: + EWh_tmp = self.MT.get_eigvals()[0] + EWn_tmp = self.MT.get_eigvals()[1] + EWs_tmp = self.MT.get_eigvals()[2] + + trace_m = np.sum(self.MT.get_eigvals()) + EWh = EWh_tmp.copy() + EWs = EWs_tmp.copy() + + if trace_m != 0: + if (self._plot_clr_order > 0 and EWn_tmp >= 0 and + abs(EWs_tmp) > abs(EWh_tmp)) or \ + (self._plot_clr_order < 0 and + EWn_tmp <= 0 and abs(EWs_tmp) > abs(EWh_tmp)): + + EWs = EWh_tmp.copy() + EWh = EWs_tmp.copy() + print('changed order!!\n') + EVs_tmp = self.MT._rotation_matrix[:, 2].copy() + EVh_tmp = self.MT._rotation_matrix[:, 0].copy() + + self.MT._rotation_matrix[:, 0] = EVs_tmp + self.MT._rotation_matrix[:, 2] = EVh_tmp + self._plot_clr_order *= -1 + + EWn = EWn_tmp.copy() + + if abs(EWn) < epsilon: + EWn = 0 + norm_factor = max(np.abs([EWh, EWn, EWs])) + + [EWh, EWn, EWs] = [xx / norm_factor for xx in [EWh, EWn, EWs]] + + RHS = -EWs / (EWn * np.cos(phi) ** 2 + EWh * np.sin(phi) ** 2) + + if np.all([np.sign(xx) >= 0 for xx in RHS]): + alpha = np.arctan(np.sqrt(RHS)) * rad2deg + else: + alpha = phi.copy() + alpha[:] = 90 + self._pure_isotropic = 1 + + # fault planes: + RHS_FP = 1. / (np.sin(phi) ** 2) + alpha_FP = np.arctan(np.sqrt(RHS_FP)) * rad2deg + + # horizontal coordinates of curves + r_hor = np.sin(alpha / rad2deg) + r_hor_FP = np.sin(alpha_FP / rad2deg) + + self._r_hor_for_pa_plot = r_hor + self._r_hor_FP_for_pa_plot = r_hor_FP + + H_values = np.sin(phi) * r_hor + N_values = np.cos(phi) * r_hor + H_values_FP = np.sin(phi) * r_hor_FP + N_values_FP = np.cos(phi) * r_hor_FP + + # set vertical value of curve point coordinates - two symmetric curves + # exist + S_values_positive = np.cos(alpha / rad2deg) + S_values_negative = -np.cos(alpha / rad2deg) + S_values_positive_FP = np.cos(alpha_FP / rad2deg) + S_values_negative_FP = -np.cos(alpha_FP / rad2deg) + + # change basis back to original input reference system + chng_basis = self.MT._rotation_matrix + + line_tuple_pos = np.zeros((3, n_curve_points)) + line_tuple_neg = np.zeros((3, n_curve_points)) + + for ii in range(n_curve_points): + pos_vec_in_EV_basis = np.array([H_values[ii], N_values[ii], + S_values_positive[ii]]).transpose() + neg_vec_in_EV_basis = np.array([H_values[ii], N_values[ii], + S_values_negative[ii]]).transpose() + line_tuple_pos[:, ii] = np.dot(chng_basis, pos_vec_in_EV_basis) + line_tuple_neg[:, ii] = np.dot(chng_basis, neg_vec_in_EV_basis) + + EVh = self.MT.get_eigvecs()[0] + EVn = self.MT.get_eigvecs()[1] + EVs = self.MT.get_eigvecs()[2] + + all_EV = np.zeros((3, 6)) + + all_EV[:, 0] = EVh.transpose() + all_EV[:, 1] = EVn.transpose() + all_EV[:, 2] = EVs.transpose() + all_EV[:, 3] = -EVh.transpose() + all_EV[:, 4] = -EVn.transpose() + all_EV[:, 5] = -EVs.transpose() + + # basis vectors: + all_BV = np.zeros((3, 6)) + all_BV[:, 0] = np.array((1, 0, 0)) + all_BV[:, 1] = np.array((-1, 0, 0)) + all_BV[:, 2] = np.array((0, 1, 0)) + all_BV[:, 3] = np.array((0, -1, 0)) + all_BV[:, 4] = np.array((0, 0, 1)) + all_BV[:, 5] = np.array((0, 0, -1)) + + # re-sort the two 90 degree nodal lines to 2 fault planes - cut each at + # halves and merge pairs + # additionally change basis system to NED reference system + + midpoint_idx = int(n_curve_points / 2.) + + FP1 = np.zeros((3, n_curve_points)) + FP2 = np.zeros((3, n_curve_points)) + + for ii in range(midpoint_idx): + FP1_vec = np.array([H_values_FP[ii], N_values_FP[ii], + S_values_positive_FP[ii]]).transpose() + FP2_vec = np.array([H_values_FP[ii], N_values_FP[ii], + S_values_negative_FP[ii]]).transpose() + FP1[:, ii] = np.dot(chng_basis, FP1_vec) + FP2[:, ii] = np.dot(chng_basis, FP2_vec) + + for jj in range(midpoint_idx): + ii = n_curve_points - jj - 1 + + FP1_vec = np.array([H_values_FP[ii], N_values_FP[ii], + S_values_negative_FP[ii]]).transpose() + FP2_vec = np.array([H_values_FP[ii], N_values_FP[ii], + S_values_positive_FP[ii]]).transpose() + FP1[:, ii] = np.dot(chng_basis, FP1_vec) + FP2[:, ii] = np.dot(chng_basis, FP2_vec) + + # identify with faultplane index, gotten from 'get_fps': + self._FP1 = FP1 + self._FP2 = FP2 + + self._all_EV = all_EV + self._all_BV = all_BV + self._nodalline_negative = line_tuple_neg + self._nodalline_positive = line_tuple_pos + + def _identify_faultplanes(self): + """ + See, if the 2 faultplanes, given as attribute of the moment + tensor object, handed to this instance, are consistent with + the faultplane lines, obtained from the basis solution. If + not, interchange the indices of the newly found ones. + """ + # TODO !!!!!! + pass + + def _find_basis_change_2_new_viewpoint(self): + """ + Finding the Eulerian angles, if you want to rotate an object. + + Your original view point is the position (0,0,0). Input are the + coordinates of the new point of view, equivalent to geographical + coordinates. + + Example: + + Original view onto the Earth is from right above lat=0, lon=0 with + north=upper edge, south=lower edge. Now you want to see the Earth + from a position somewhere near Baku. So lat=45, + lon=45, azimuth=0. + + The Earth must be rotated around some axis, not to be determined. + The rotation matrixx is the matrix for the change of basis to the + new local orthonormal system. + + input: + - latitude in degrees from -90 (south) to 90 (north) + - longitude in degrees from -180 (west) to 180 (east) + - azimuth in degrees from 0 (heading north) to 360 (north again) + """ + + new_latitude = self._plot_viewpoint[0] + new_longitude = self._plot_viewpoint[1] + new_azimuth = self._plot_viewpoint[2] + + s_lat = np.sin(new_latitude / rad2deg) + if abs(s_lat) < epsilon: + s_lat = 0 + c_lat = np.cos(new_latitude / rad2deg) + if abs(c_lat) < epsilon: + c_lat = 0 + s_lon = np.sin(new_longitude / rad2deg) + if abs(s_lon) < epsilon: + s_lon = 0 + c_lon = np.cos(new_longitude / rad2deg) + if abs(c_lon) < epsilon: + c_lon = 0 + + # assume input basis as NED!!! + + # original point of view therein is (0,0,-1) + # new point at lat=latitude, lon=longitude, az=0, given in old + # NED-coordinates: + # (cos(latitude), sin(latitude)*sin(longitude), + # sin(latitude)*cos(longitude) ) + # + # new " down' " is given by the negative position vector, so pointing + # inwards to the centre point + # down_prime = - ( np.array( ( s_lat, c_lat*c_lon, -c_lat*s_lon ) ) ) + down_prime = -(np.array((s_lat, c_lat * s_lon, -c_lat * c_lon))) + + #normalise: + down_prime /= np.sqrt(np.dot(down_prime, down_prime)) + + # get second local basis vector " north' " by orthogonalising + # (Gram-Schmidt method) the original north w.r.t. the new " down' " + north_prime_not_normalised = np.array((1., 0., 0.)) - \ + (np.dot(down_prime, np.array((1., 0., 0.))) / + (np.dot(down_prime, down_prime)) * down_prime) + + len_north_prime_not_normalised = \ + np.sqrt(np.dot(north_prime_not_normalised, + north_prime_not_normalised)) + # check for poles: + if np.abs(len_north_prime_not_normalised) < epsilon: + # case: north pole + if s_lat > 0: + north_prime = np.array((0., 0., 1.)) + # case: south pole + else: + north_prime = np.array((0., 0., -1.)) + else: + north_prime = \ + north_prime_not_normalised / len_north_prime_not_normalised + + # third basis vector is obtained by a cross product of the first two + east_prime = np.cross(down_prime, north_prime) + + # normalise: + east_prime /= np.sqrt(np.dot(east_prime, east_prime)) + + rotmat_pos_raw = np.zeros((3, 3)) + rotmat_pos_raw[:, 0] = north_prime + rotmat_pos_raw[:, 1] = east_prime + rotmat_pos_raw[:, 2] = down_prime + + rotmat_pos = np.asmatrix(rotmat_pos_raw).T + # this matrix gives the coordinates of a given point in the old + # coordinates w.r.t. the new system + + # up to here, only the position has changed, the angle of view + # (azimuth) has to be added by an additional rotation around the + # down'-axis (in the frame of the new coordinates) + # set up the local rotation around the new down'-axis by the given + # angle 'azimuth'. Positive values turn view counterclockwise from the + # new north' + only_rotation = np.zeros((3, 3)) + s_az = np.sin(new_azimuth / rad2deg) + if abs(s_az) < epsilon: + s_az = 0. + c_az = np.cos(new_azimuth / rad2deg) + if abs(c_az) < epsilon: + c_az = 0. + + only_rotation[2, 2] = 1 + only_rotation[0, 0] = c_az + only_rotation[1, 1] = c_az + only_rotation[0, 1] = -s_az + only_rotation[1, 0] = s_az + + local_rotation = np.asmatrix(only_rotation) + + # apply rotation from left!! + total_rotation_matrix = np.dot(local_rotation, rotmat_pos) + + # yields the complete matrix for representing the old coordinates in + # the new (rotated) frame: + self._plot_basis_change = total_rotation_matrix + + def _rotate_all_objects_2_new_view(self): + """ + Rotate all relevant parts of the solution - namely the + eigenvector-projections, the 2 nodallines, and the faultplanes + - so that they are seen from the new viewpoint. + """ + objects_2_rotate = ['all_EV', 'all_BV', 'nodalline_negative', + 'nodalline_positive', 'FP1', 'FP2'] + + for obj in objects_2_rotate: + object2rotate = getattr(self, '_' + obj).transpose() + + rotated_thing = object2rotate.copy() + for i in range(len(object2rotate)): + rotated_thing[i] = np.dot(self._plot_basis_change, + object2rotate[i]) + + rotated_object = rotated_thing.copy() + setattr(self, '_' + obj + '_rotated', rotated_object.transpose()) + + def _vertical_2D_projection(self): + """ + Start the vertical projection of the 3D beachball onto the 2D plane. + The projection is chosen according to the attribute '_plot_projection' + """ + list_of_possible_projections = ['stereo', 'ortho', 'lambert', 'gnom'] + + if not self._plot_projection in list_of_possible_projections: + print('desired projection not possible - choose from:\n ') + print(list_of_possible_projections) + raise MTError(' !! ') + + if self._plot_projection == 'stereo': + if not self._stereo_vertical(): + print('ERROR in stereo_vertical') + raise MTError(' !! ') + elif self._plot_projection == 'ortho': + if not self._orthographic_vertical(): + print('ERROR in stereo_vertical') + raise MTError(' !! ') + elif self._plot_projection == 'lambert': + if not self._lambert_vertical(): + print('ERROR in stereo_vertical') + raise MTError(' !! ') + elif self._plot_projection == 'gnom': + if not self._gnomonic_vertical(): + print('ERROR in stereo_vertical') + raise MTError(' !! ') + + def _stereo_vertical(self): + """ + Stereographic/azimuthal conformal 2D projection onto a plane, tangent + to the lowest point (0,0,1). + + Keeps the angles constant! + + The parts in the lower hemisphere are projected to the unit + sphere, the upper half to an annular region between radii r=1 + and r=2. If the attribute '_show_upper_hemis' is set, the + projection is reversed. + """ + objects_2_project = ['all_EV', 'all_BV', 'nodalline_negative', + 'nodalline_positive', 'FP1', 'FP2'] + + available_coord_systems = ['NED'] + + if not self._plot_basis in available_coord_systems: + print('desired plotting projection not possible - choose from :\n') + print(available_coord_systems) + raise MTError(' !! ') + + plot_upper_hem = self._plot_show_upper_hemis + + for obj in objects_2_project: + obj_name = '_' + obj + '_rotated' + o2proj = getattr(self, obj_name) + coords = o2proj.copy() + + n_points = len(o2proj[0, :]) + stereo_coords = np.zeros((2, n_points)) + + for ll in range(n_points): + # second component is EAST + co_x = coords[1, ll] + # first component is NORTH + co_y = coords[0, ll] + # z given in DOWN + co_z = -coords[2, ll] + + rho_hor = np.sqrt(co_x ** 2 + co_y ** 2) + + if rho_hor == 0: + new_y = 0 + new_x = 0 + if plot_upper_hem: + if co_z < 0: + new_x = 2 + else: + if co_z > 0: + new_x = 2 + else: + if co_z < 0: + new_rho = rho_hor / (1. - co_z) + if plot_upper_hem: + new_rho = 2 - (rho_hor / (1. - co_z)) + + new_x = co_x / rho_hor * new_rho + new_y = co_y / rho_hor * new_rho + else: + new_rho = 2 - (rho_hor / (1. + co_z)) + if plot_upper_hem: + new_rho = rho_hor / (1. + co_z) + + new_x = co_x / rho_hor * new_rho + new_y = co_y / rho_hor * new_rho + + stereo_coords[0, ll] = new_x + stereo_coords[1, ll] = new_y + + setattr(self, '_' + obj + '_2D', stereo_coords) + setattr(self, '_' + obj + '_final', stereo_coords) + + return 1 + + def _orthographic_vertical(self): + """ + Orthographic 2D projection onto a plane, tangent to the lowest + point (0,0,1). + + Shows the natural view on a 2D sphere from large distances (assuming + parallel projection) + + The parts in the lower hemisphere are projected to the unit + sphere, the upper half to an annular region between radii r=1 + and r=2. If the attribute '_show_upper_hemis' is set, the + projection is reversed. + """ + + objects_2_project = ['all_EV', 'all_BV', 'nodalline_negative', + 'nodalline_positive', 'FP1', 'FP2'] + + available_coord_systems = ['NED'] + + if not self._plot_basis in available_coord_systems: + print('desired plotting projection not possible - choose from :\n') + print(available_coord_systems) + raise MTError(' !! ') + + plot_upper_hem = self._plot_show_upper_hemis + + for obj in objects_2_project: + obj_name = '_' + obj + '_rotated' + o2proj = getattr(self, obj_name) + coords = o2proj.copy() + + n_points = len(o2proj[0, :]) + coords2D = np.zeros((2, n_points)) + + for ll in range(n_points): + # second component is EAST + co_x = coords[1, ll] + # first component is NORTH + co_y = coords[0, ll] + # z given in DOWN + co_z = -coords[2, ll] + + rho_hor = np.sqrt(co_x ** 2 + co_y ** 2) + + if rho_hor == 0: + new_y = 0 + new_x = 0 + if plot_upper_hem: + if co_z < 0: + new_x = 2 + else: + if co_z > 0: + new_x = 2 + else: + if co_z < 0: + new_rho = rho_hor + if plot_upper_hem: + new_rho = 2 - rho_hor + + new_x = co_x / rho_hor * new_rho + new_y = co_y / rho_hor * new_rho + else: + new_rho = 2 - rho_hor + if plot_upper_hem: + new_rho = rho_hor + + new_x = co_x / rho_hor * new_rho + new_y = co_y / rho_hor * new_rho + + coords2D[0, ll] = new_x + coords2D[1, ll] = new_y + + setattr(self, '_' + obj + '_2D', coords2D) + setattr(self, '_' + obj + '_final', coords2D) + + return 1 + + def _lambert_vertical(self): + """ + Lambert azimuthal equal-area 2D projection onto a plane, tangent to the + lowest point (0,0,1). + + Keeps the area constant! + + The parts in the lower hemisphere are projected to the unit + sphere (only here the area is kept constant), the upper half to an + annular region between radii r=1 and r=2. If the attribute + '_show_upper_hemis' is set, the projection is reversed. + """ + objects_2_project = ['all_EV', 'all_BV', 'nodalline_negative', + 'nodalline_positive', 'FP1', 'FP2'] + + available_coord_systems = ['NED'] + + if not self._plot_basis in available_coord_systems: + print('desired plotting projection not possible - choose from :\n') + print(available_coord_systems) + raise MTError(' !! ') + + plot_upper_hem = self._plot_show_upper_hemis + + for obj in objects_2_project: + obj_name = '_' + obj + '_rotated' + o2proj = getattr(self, obj_name) + coords = o2proj.copy() + + n_points = len(o2proj[0, :]) + coords2D = np.zeros((2, n_points)) + + for ll in range(n_points): + # second component is EAST + co_x = coords[1, ll] + # first component is NORTH + co_y = coords[0, ll] + # z given in DOWN + co_z = -coords[2, ll] + + rho_hor = np.sqrt(co_x ** 2 + co_y ** 2) + + if rho_hor == 0: + new_y = 0 + new_x = 0 + if plot_upper_hem: + if co_z < 0: + new_x = 2 + else: + if co_z > 0: + new_x = 2 + else: + if co_z < 0: + new_rho = rho_hor / np.sqrt(1. - co_z) + + if plot_upper_hem: + new_rho = 2 - (rho_hor / np.sqrt(1. - co_z)) + + new_x = co_x / rho_hor * new_rho + new_y = co_y / rho_hor * new_rho + + else: + new_rho = 2 - (rho_hor / np.sqrt(1. + co_z)) + + if plot_upper_hem: + new_rho = rho_hor / np.sqrt(1. + co_z) + + new_x = co_x / rho_hor * new_rho + new_y = co_y / rho_hor * new_rho + + coords2D[0, ll] = new_x + coords2D[1, ll] = new_y + + setattr(self, '_' + obj + '_2D', coords2D) + setattr(self, '_' + obj + '_final', coords2D) + + return 1 + + def _gnomonic_vertical(self): + """ + Gnomonic 2D projection onto a plane, tangent to the lowest + point (0,0,1). + + Keeps the great circles as straight lines (geodetics constant) ! + + The parts in the lower hemisphere are projected to the unit + sphere, the upper half to an annular region between radii r=1 + and r=2. If the attribute '_show_upper_hemis' is set, the + projection is reversed. + """ + + objects_2_project = ['all_EV', 'all_BV', 'nodalline_negative', + 'nodalline_positive', 'FP1', 'FP2'] + + available_coord_systems = ['NED'] + + if not self._plot_basis in available_coord_systems: + print('desired plotting projection not possible - choose from :\n') + print(available_coord_systems) + raise MTError(' !! ') + + plot_upper_hem = self._plot_show_upper_hemis + + for obj in objects_2_project: + obj_name = '_' + obj + '_rotated' + o2proj = getattr(self, obj_name) + coords = o2proj.copy() + + n_points = len(o2proj[0, :]) + coords2D = np.zeros((2, n_points)) + + for ll in range(n_points): + # second component is EAST + co_x = coords[1, ll] + # first component is NORTH + co_y = coords[0, ll] + # z given in DOWN + co_z = -coords[2, ll] + + rho_hor = np.sqrt(co_x ** 2 + co_y ** 2) + + if rho_hor == 0: + new_y = 0 + new_x = 0 + if co_z > 0: + new_x = 2 + if plot_upper_hem: + new_x = 0 + else: + if co_z < 0: + new_rho = np.cos(np.arcsin(rho_hor)) * \ + np.tan(np.arcsin(rho_hor)) + + if plot_upper_hem: + new_rho = 2 - (np.cos(np.arcsin(rho_hor)) * + np.tan(np.arcsin(rho_hor))) + + new_x = co_x / rho_hor * new_rho + new_y = co_y / rho_hor * new_rho + + else: + new_rho = 2 - (np.cos(np.arcsin(rho_hor)) * + np.tan(np.arcsin(rho_hor))) + + if plot_upper_hem: + new_rho = np.cos(np.arcsin(rho_hor)) * \ + np.tan(np.arcsin(rho_hor)) + + new_x = co_x / rho_hor * new_rho + new_y = co_y / rho_hor * new_rho + + coords2D[0, ll] = new_x + coords2D[1, ll] = new_y + + setattr(self, '_' + obj + '_2D', coords2D) + setattr(self, '_' + obj + '_final', coords2D) + + return 1 + + def _build_circles(self): + """ + Sets two sets of points, describing the unit sphere and the outer + circle with r=2. + + Added as attributes '_unit_sphere' and '_outer_circle'. + """ + phi = self._phi_curve + + UnitSphere = np.zeros((2, len(phi))) + UnitSphere[0, :] = np.cos(phi) + UnitSphere[1, :] = np.sin(phi) + + # outer circle ( radius for stereographic projection is set to 2 ) + outer_circle_points = 2 * UnitSphere + + self._unit_sphere = UnitSphere + self._outer_circle = outer_circle_points + + def _sort_curve_points(self, curve): + """ + Checks, if curve points are in right order for line plotting. + + If not, a re-arranging is carried out. + """ + sorted_curve = np.zeros((2, len(curve[0, :]))) + # in polar coordinates + r_phi_curve = np.zeros((len(curve[0, :]), 2)) + for ii in range(curve.shape[1]): + r_phi_curve[ii, 0] = \ + math.sqrt(curve[0, ii] ** 2 + curve[1, ii] ** 2) + r_phi_curve[ii, 1] = \ + math.atan2(curve[0, ii], curve[1, ii]) % (2 * pi) + # find index with highest r + largest_r_idx = np.argmax(r_phi_curve[:, 0]) + + # check, if perhaps more values with same r - if so, take point with + # lowest phi + other_idces = \ + list(np.where(r_phi_curve[:, 0] == r_phi_curve[largest_r_idx, 0])) + if len(other_idces) > 1: + best_idx = np.argmin(r_phi_curve[other_idces, 1]) + start_idx_curve = other_idces[best_idx] + else: + start_idx_curve = largest_r_idx + + if not start_idx_curve == 0: + pass + + # check orientation - want to go inwards + start_r = r_phi_curve[start_idx_curve, 0] + next_idx = (start_idx_curve + 1) % len(r_phi_curve[:, 0]) + prep_idx = (start_idx_curve - 1) % len(r_phi_curve[:, 0]) + next_r = r_phi_curve[next_idx, 0] + + keep_direction = True + if next_r <= start_r: + # check, if next R is on other side of area - look at total + # distance - if yes, reverse direction + dist_first_next = \ + (curve[0, next_idx] - curve[0, start_idx_curve]) ** 2 + \ + (curve[1, next_idx] - curve[1, start_idx_curve]) ** 2 + dist_first_other = \ + (curve[0, prep_idx] - curve[0, start_idx_curve]) ** 2 + \ + (curve[1, prep_idx] - curve[1, start_idx_curve]) ** 2 + + if dist_first_next > dist_first_other: + keep_direction = False + + if keep_direction: + # direction is kept + for jj in range(curve.shape[1]): + running_idx = (start_idx_curve + jj) % len(curve[0, :]) + sorted_curve[0, jj] = curve[0, running_idx] + sorted_curve[1, jj] = curve[1, running_idx] + else: + # direction is reversed + for jj in range(curve.shape[1]): + running_idx = (start_idx_curve - jj) % len(curve[0, :]) + sorted_curve[0, jj] = curve[0, running_idx] + sorted_curve[1, jj] = curve[1, running_idx] + + # check if step of first to second point does not have large angle + # step (problem caused by projection of point (pole) onto whole + # edge - if this first angle step is larger than the one between + # points 2 and three, correct position of first point: keep R, but + # take angle with same difference as point 2 to point 3 + + angle_point_1 = (math.atan2(sorted_curve[0, 0], + sorted_curve[1, 0]) % (2 * pi)) + angle_point_2 = (math.atan2(sorted_curve[0, 1], + sorted_curve[1, 1]) % (2 * pi)) + angle_point_3 = (math.atan2(sorted_curve[0, 2], + sorted_curve[1, 2]) % (2 * pi)) + + angle_diff_23 = (angle_point_3 - angle_point_2) + if angle_diff_23 > pi: + angle_diff_23 = (-angle_diff_23) % (2 * pi) + + angle_diff_12 = (angle_point_2 - angle_point_1) + if angle_diff_12 > pi: + angle_diff_12 = (-angle_diff_12) % (2 * pi) + + if abs(angle_diff_12) > abs(angle_diff_23): + r_old = \ + math.sqrt(sorted_curve[0, 0] ** 2 + sorted_curve[1, 0] ** 2) + new_angle = (angle_point_2 - angle_diff_23) % (2 * pi) + sorted_curve[0, 0] = r_old * math.sin(new_angle) + sorted_curve[1, 0] = r_old * math.cos(new_angle) + + return sorted_curve + + def _smooth_curves(self): + """ + Corrects curves for potential large gaps, resulting in strange + intersection lines on nodals of round and irreagularly shaped + areas. + + At least one coordinte point on each degree on the circle is assured. + """ + list_of_curves_2_smooth = ['nodalline_negative', 'nodalline_positive', + 'FP1', 'FP2'] + + points_per_degree = self._plot_n_points / 360. + + for curve2smooth in list_of_curves_2_smooth: + obj_name = curve2smooth + '_in_order' + obj = getattr(self, '_' + obj_name).transpose() + + smoothed_array = np.zeros((1, 2)) + smoothed_array[0, :] = obj[0] + smoothed_list = [smoothed_array] + + # now in shape (n_points,2) + for idx, val in enumerate(obj[:-1]): + r1 = math.sqrt(val[0] ** 2 + val[1] ** 2) + r2 = math.sqrt(obj[idx + 1][0] ** 2 + obj[idx + 1][1] ** 2) + phi1 = math.atan2(val[0], val[1]) + phi2 = math.atan2(obj[idx + 1][0], obj[idx + 1][1]) + + phi2_larger = np.sign(phi2 - phi1) + angle_smaller_pi = np.sign(pi - abs(phi2 - phi1)) + + if phi2_larger * angle_smaller_pi > 0: + go_cw = True + openangle = (phi2 - phi1) % (2 * pi) + else: + go_cw = False + openangle = (phi1 - phi2) % (2 * pi) + + openangle_deg = openangle * rad2deg + radius_diff = r2 - r1 + + if openangle_deg > 1. / points_per_degree: + + n_fillpoints = int(openangle_deg * points_per_degree) + fill_array = np.zeros((n_fillpoints, 2)) + if go_cw: + angles = ((np.arange(n_fillpoints) + 1) * openangle / + (n_fillpoints + 1) + phi1) % (2 * pi) + else: + angles = (phi1 - (np.arange(n_fillpoints) + 1) * + openangle / (n_fillpoints + 1)) % (2 * pi) + + radii = (np.arange(n_fillpoints) + 1) * \ + radius_diff / (n_fillpoints + 1) + r1 + + fill_array[:, 0] = radii * np.sin(angles) + fill_array[:, 1] = radii * np.cos(angles) + + smoothed_list.append(fill_array) + + smoothed_list.append([obj[idx + 1]]) + + smoothed_array = np.vstack(smoothed_list) + setattr(self, '_' + curve2smooth + '_final', + smoothed_array.transpose()) + + def _check_curve_in_curve(self): + """ + Checks, if one of the two nodallines contains the other one + completely. If so, the order of colours is re-adapted, + assuring the correct order when doing the overlay plotting. + """ + lo_points_in_pos_curve = \ + list(self._nodalline_positive_final.transpose()) + lo_points_in_pos_curve_array = \ + self._nodalline_positive_final.transpose() + lo_points_in_neg_curve = \ + list(self._nodalline_negative_final.transpose()) + lo_points_in_neg_curve_array = \ + self._nodalline_negative_final.transpose() + + # check, if negative curve completely within positive curve + mask_neg_in_pos = 0 + for neg_point in lo_points_in_neg_curve: + mask_neg_in_pos += self._pnpoly(lo_points_in_pos_curve_array, + neg_point[:2]) + if mask_neg_in_pos > len(lo_points_in_neg_curve) - 3: + self._plot_curve_in_curve = 1 + + # check, if positive curve completely within negative curve + mask_pos_in_neg = 0 + for pos_point in lo_points_in_pos_curve: + mask_pos_in_neg += self._pnpoly(lo_points_in_neg_curve_array, + pos_point[:2]) + if mask_pos_in_neg > len(lo_points_in_pos_curve) - 3: + self._plot_curve_in_curve = -1 + + # correct for ONE special case: double couple with its + # eigensystem = NED basis system: + testarray = [1., 0, 0, 0, 1, 0, 0, 0, 1] + if np.prod(self.MT._rotation_matrix.A1 == testarray) and \ + (self.MT._eigenvalues[1] == 0): + self._plot_curve_in_curve = -1 + self._plot_clr_order = 1 + + def _point_inside_polygon(self, x, y, poly): + """ + Determine if a point is inside a given polygon or not. + + Polygon is a list of (x,y) pairs. + """ + n = len(poly) + inside = False + + p1x, p1y = poly[0] + for i in range(n + 1): + p2x, p2y = poly[i % n] + if y > min(p1y, p2y): + if y <= max(p1y, p2y): + if x <= max(p1x, p2x): + if p1y != p2y: + xinters = \ + (y - p1y) * (p2x - p1x) / (p2y - p1y) + p1x + if p1x == p2x or x <= xinters: + inside = not inside + p1x, p1y = p2x, p2y + + return inside + + def _pnpoly(self, verts, point): + """ + Check whether point is in the polygon defined by verts. + + verts - 2xN array + point - (2,) array + + See + http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html + """ + # using take instead of getitem, about ten times faster, see + # http://wesmckinney.com/blog/?p=215 + verts = np.require(verts, dtype=np.float64) + x, y = point + + xpi = verts[:, 0] + ypi = verts[:, 1] + # shift + xpj = xpi.take(self.arange_1[:xpi.size]) + ypj = ypi.take(self.arange_1[:ypi.size]) + + possible_crossings = \ + ((ypi <= y) & (y < ypj)) | ((ypj <= y) & (y < ypi)) + + xpi = xpi[possible_crossings] + ypi = ypi[possible_crossings] + xpj = xpj[possible_crossings] + ypj = ypj[possible_crossings] + + crossings = x < (xpj - xpi) * (y - ypi) / (ypj - ypi) + xpi + + return crossings.sum() % 2 + + def _projection_2_unit_sphere(self): + """ + Brings the complete solution (from stereographic projection) + onto the unit sphere by just shrinking the maximum radius of + all points to 1. + + This keeps the area definitions, so the colouring is not affected. + """ + list_of_objects_2_project = ['nodalline_positive_final', + 'nodalline_negative_final'] + lo_fps = ['FP1_final', 'FP2_final'] + + for obj2proj in list_of_objects_2_project: + obj = getattr(self, '_' + obj2proj).transpose().copy() + for idx, val in enumerate(obj): + old_radius = np.sqrt(val[0] ** 2 + val[1] ** 2) + if old_radius > 1: + obj[idx, 0] = val[0] / old_radius + obj[idx, 1] = val[1] / old_radius + + setattr(self, '_' + obj2proj + '_US', obj.transpose()) + + for fp in lo_fps: + obj = getattr(self, '_' + fp).transpose().copy() + + tmp_obj = [] + for idx, val in enumerate(obj): + old_radius = np.sqrt(val[0] ** 2 + val[1] ** 2) + if old_radius <= 1 + epsilon: + tmp_obj.append(val) + tmp_obj2 = np.array(tmp_obj).transpose() + tmp_obj3 = self._sort_curve_points(tmp_obj2) + + setattr(self, '_' + fp + '_US', tmp_obj3) + + lo_visible_EV = [] + + for idx, val in enumerate(self._all_EV_2D.transpose()): + r_ev = np.sqrt(val[0] ** 2 + val[1] ** 2) + if r_ev <= 1: + lo_visible_EV.append([val[0], val[1], idx]) + visible_EVs = np.array(lo_visible_EV) + + self._all_EV_2D_US = visible_EVs + + lo_visible_BV = [] + dummy_list1 = [] + direction_letters = list('NSEWDU') + + for idx, val in enumerate(self._all_BV_2D.transpose()): + r_bv = math.sqrt(val[0] ** 2 + val[1] ** 2) + if r_bv <= 1: + if idx == 1 and 'N' in dummy_list1: + continue + elif idx == 3 and 'E' in dummy_list1: + continue + elif idx == 5 and 'D' in dummy_list1: + continue + else: + lo_visible_BV.append([val[0], val[1], idx]) + dummy_list1.append(direction_letters[idx]) + + visible_BVs = np.array(lo_visible_BV) + + self._all_BV_2D_US = visible_BVs + + def _plot_US(self, ax=None): + """ + Generates the final plot of the beachball projection on the unit + sphere. + + Additionally, the plot can be saved in a file on the fly. + """ + import pylab as P + + plotfig = self._setup_plot_US(P, ax=ax) + + if self._plot_save_plot: + try: + plotfig.savefig(self._plot_outfile + '.' + + self._plot_outfile_format, dpi=self._plot_dpi, + transparent=True, + format=self._plot_outfile_format) + except: + print('saving of plot not possible') + P.show() + P.close('all') + + def _setup_plot_US(self, P, ax=None): + """ + Setting up the figure with the final plot of the unit sphere. + + Either called by _plot_US or by _just_save_bb + """ + P.close(667) + if ax is None: + plotfig = P.figure(667, figsize=(self._plot_size, self._plot_size)) + plotfig.subplots_adjust(left=0, bottom=0, right=1, top=1) + ax = plotfig.add_subplot(111, aspect='equal') + + ax.axison = False + + neg_nodalline = self._nodalline_negative_final_US + pos_nodalline = self._nodalline_positive_final_US + FP1_2_plot = self._FP1_final_US + FP2_2_plot = self._FP2_final_US + + US = self._unit_sphere + + tension_colour = self._plot_tension_colour + pressure_colour = self._plot_pressure_colour + + if self._plot_fill_flag: + if self._plot_clr_order > 0: + alpha = self._plot_fill_alpha * self._plot_total_alpha + + ax.fill(US[0, :], US[1, :], fc=pressure_colour, alpha=alpha) + ax.fill(neg_nodalline[0, :], neg_nodalline[1, :], + fc=tension_colour, alpha=alpha) + ax.fill(pos_nodalline[0, :], pos_nodalline[1, :], + fc=tension_colour, alpha=alpha) + + if self._plot_curve_in_curve != 0: + ax.fill(US[0, :], US[1, :], fc=tension_colour, + alpha=alpha) + + if self._plot_curve_in_curve < 1: + ax.fill(neg_nodalline[0, :], neg_nodalline[1, :], + fc=pressure_colour, alpha=alpha) + ax.fill(pos_nodalline[0, :], pos_nodalline[1, :], + fc=tension_colour, alpha=alpha) + pass + else: + ax.fill(pos_nodalline[0, :], pos_nodalline[1, :], + fc=pressure_colour, alpha=alpha) + ax.fill(neg_nodalline[0, :], neg_nodalline[1, :], + fc=tension_colour, alpha=alpha) + pass + + EV_sym = ['m^', 'b^', 'g^', 'mv', 'bv', 'gv'] + + if self._plot_show_princ_axes: + alpha = \ + self._plot_princ_axes_alpha * self._plot_total_alpha + + for val in self._all_EV_2D_US: + ax.plot([val[0]], [val[1]], EV_sym[int(val[2])], + ms=self._plot_princ_axes_symsize, + lw=self._plot_princ_axes_lw, alpha=alpha) + + else: + alpha = self._plot_fill_alpha * self._plot_total_alpha + + ax.fill(US[0, :], US[1, :], fc=tension_colour, alpha=alpha) + ax.fill(neg_nodalline[0, :], neg_nodalline[1, :], + fc=pressure_colour, alpha=alpha) + ax.fill(pos_nodalline[0, :], pos_nodalline[1, :], + fc=pressure_colour, alpha=alpha) + + if self._plot_curve_in_curve != 0: + ax.fill(US[0, :], US[1, :], fc=pressure_colour, + alpha=alpha) + + if self._plot_curve_in_curve < 1: + ax.fill(neg_nodalline[0, :], neg_nodalline[1, :], + fc=tension_colour, alpha=alpha) + ax.fill(pos_nodalline[0, :], pos_nodalline[1, :], + fc=pressure_colour, alpha=alpha) + pass + else: + ax.fill(pos_nodalline[0, :], pos_nodalline[1, :], + fc=tension_colour, alpha=alpha) + ax.fill(neg_nodalline[0, :], neg_nodalline[1, :], + fc=pressure_colour, alpha=alpha) + pass + + EV_sym = ['g^', 'b^', 'm^', 'gv', 'bv', 'mv'] + if self._plot_show_princ_axes: + alpha = self._plot_princ_axes_alpha * self._plot_total_alpha + + for val in self._all_EV_2D_US: + ax.plot([val[0]], [val[1]], EV_sym[int(val[2])], + ms=self._plot_princ_axes_symsize, + lw=self._plot_princ_axes_lw, alpha=alpha) + + # + # set all nodallines and faultplanes for plotting: + # + + ax.plot(neg_nodalline[0, :], neg_nodalline[1, :], + c=self._plot_nodalline_colour, ls='-', + lw=self._plot_nodalline_width, + alpha=self._plot_nodalline_alpha * self._plot_total_alpha) + #ax.plot( neg_nodalline[0,:] ,neg_nodalline[1,:],'go') + + ax.plot(pos_nodalline[0, :], pos_nodalline[1, :], + c=self._plot_nodalline_colour, ls='-', + lw=self._plot_nodalline_width, + alpha=self._plot_nodalline_alpha * self._plot_total_alpha) + + if self._plot_show_faultplanes: + ax.plot(FP1_2_plot[0, :], FP1_2_plot[1, :], + c=self._plot_faultplane_colour, ls='-', + lw=self._plot_faultplane_width, + alpha=self._plot_faultplane_alpha * self._plot_total_alpha) + ax.plot(FP2_2_plot[0, :], FP2_2_plot[1, :], + c=self._plot_faultplane_colour, ls='-', + lw=self._plot_faultplane_width, + alpha=self._plot_faultplane_alpha * self._plot_total_alpha) + + elif self._plot_show_1faultplane: + if not self._plot_show_FP_index in [1, 2]: + print('no fault plane specified for being plotted... ') + print('continue without faultplane') + pass + else: + alpha = self._plot_faultplane_alpha * self._plot_total_alpha + if self._plot_show_FP_index == 1: + ax.plot(FP1_2_plot[0, :], FP1_2_plot[1, :], + c=self._plot_faultplane_colour, ls='-', + lw=self._plot_faultplane_width, alpha=alpha) + else: + ax.plot(FP2_2_plot[0, :], FP2_2_plot[1, :], + c=self._plot_faultplane_colour, ls='-', + lw=self._plot_faultplane_width, alpha=alpha) + + # if isotropic part shall be displayed, fill the circle completely with + # the appropriate colour + + if self._pure_isotropic: + # f abs( np.trace( self._M )) > epsilon: + if self._plot_clr_order < 0: + ax.fill(US[0, :], US[1, :], fc=tension_colour, alpha=1, + zorder=100) + else: + ax.fill(US[0, :], US[1, :], fc=pressure_colour, alpha=1, + zorder=100) + + # plot outer circle line of US + ax.plot(US[0, :], US[1, :], c=self._plot_outerline_colour, ls='-', + lw=self._plot_outerline_width, + alpha=self._plot_outerline_alpha * self._plot_total_alpha) + + # plot NED basis vectors + if self._plot_show_basis_axes: + plot_size_in_points = self._plot_size * 2.54 * 72 + points_per_unit = plot_size_in_points / 2. + + fontsize = plot_size_in_points / 40. + symsize = plot_size_in_points / 61. + + direction_letters = list('NSEWDU') + + for val in self._all_BV_2D_US: + x_coord = val[0] + y_coord = val[1] + np_letter = direction_letters[int(val[2])] + + rot_angle = -np.arctan2(y_coord, x_coord) + pi / 2. + original_rho = np.sqrt(x_coord ** 2 + y_coord ** 2) + + marker_x = (original_rho - (1.5 * symsize / points_per_unit)) \ + * np.sin(rot_angle) + marker_y = (original_rho - (1.5 * symsize / points_per_unit)) \ + * np.cos(rot_angle) + annot_x = (original_rho - (4.5 * fontsize / points_per_unit)) \ + * np.sin(rot_angle) + annot_y = (original_rho - (4.5 * fontsize / points_per_unit)) \ + * np.cos(rot_angle) + + ax.text(annot_x, annot_y, np_letter, + horizontalalignment='center', size=fontsize, + weight='bold', verticalalignment='center', + bbox=dict(edgecolor='white', facecolor='white', + alpha=1)) + + if original_rho > epsilon: + ax.scatter([marker_x], [marker_y], + marker=(3, 0, rot_angle), s=symsize ** 2, + c='k', facecolor='k', zorder=300) + else: + ax.scatter([x_coord], [y_coord], marker=(4, 1, rot_angle), + s=symsize ** 2, c='k', facecolor='k', + zorder=300) + + # plot 4 fake points, guaranteeing full visibilty of the sphere + ax.plot([0, 1.05, 0, -1.05], [1.05, 0, -1.05, 0], ',', alpha=0.) + # scaling behaviour + ax.autoscale_view(tight=True, scalex=True, scaley=True) + + return plotfig + + +if __name__ == "__main__": + + from optparse import OptionParser, OptionGroup + + decomp_attrib_map_keys = ('in', 'out', 'type', + 'full', + 'iso', 'iso_perc', + 'dev', 'devi', 'devi_perc', + 'dc', 'dc_perc', + 'dc2', 'dc2_perc', + 'dc3', 'dc3_perc', + 'clvd', 'clvd_perc', + 'mom', 'mag', + 'eigvals', 'eigvecs', + 't', 'n', 'p') + + decomp_attrib_map = dict(zip(decomp_attrib_map_keys, + ('input_system', 'output_system', + 'decomp_type', 'M', + 'iso', 'iso_percentage', + 'devi', 'devi', 'devi_percentage', + 'DC', 'DC_percentage', + 'DC2', 'DC2_percentage', + 'DC3', 'DC3_percentage', + 'CLVD', 'CLVD_percentage', + 'moment', 'mag', + 'eigvals', 'eigvecs', + 't_axis', 'null_axis', 'p_axis') + )) + + lo_allowed_systems = ['NED', 'USE', 'XYZ', 'NWU'] + + def _handle_input(call, M_in, call_args, optparser): + """ + take the original method and its arguments, the source mechanism, + and the dictionary with proper parsers for each call, + """ + + # construct a dict with consistent keywordargs suited for the current + # call + kwargs = _parse_arguments(call, call_args, optparser) + + # set the fitting input basis system + in_system = kwargs.get('in_system', 'NED') + out_system = kwargs.get('out_system', 'NED') + + # build the moment tensor object + mt = MomentTensor( + M=M_in, in_system=in_system, out_system=out_system) + + # if only parts of M are to be plotted, M must be reduced to this part + # already here! + if call == 'plot' and kwargs['plot_part_of_m']: + if kwargs['plot_part_of_m'] == 'iso': + mt = MomentTensor( + M=mt.get_iso(), in_system=in_system, out_system=out_system) + + if kwargs['plot_part_of_m'] == 'devi': + mt = MomentTensor( + M=mt.get_devi(), in_system=in_system, + out_system=out_system) + + if kwargs['plot_part_of_m'] == 'dc': + mt = MomentTensor( + M=mt.get_DC(), in_system=in_system, out_system=out_system) + + if kwargs['plot_part_of_m'] == 'clvd': + mt = MomentTensor( + M=mt.get_CLVD(), in_system=in_system, + out_system=out_system) + + # call the main routine to handle the moment tensor + return _call_main(mt, call, kwargs) + + def _call_main(MT, main_call, kwargs_dict): + + if main_call == 'plot': + return _call_plot(MT, kwargs_dict) + + elif main_call == 'gmt': + return _call_gmt(MT, kwargs_dict) + + elif main_call == 'decompose': + return _call_decompose(MT, kwargs_dict) + + elif main_call == 'describe': + return _call_describe(MT, kwargs_dict) + + def _call_plot(MT, kwargs_dict): + + bb2plot = BeachBall(MT, kwargs_dict) + + if kwargs_dict['plot_save_plot']: + bb2plot.save_BB(kwargs_dict) + return + + if kwargs_dict['plot_pa_plot']: + bb2plot.pa_plot(kwargs_dict) + return + + if kwargs_dict['plot_full_sphere']: + bb2plot.full_sphere_plot(kwargs_dict) + return + + bb2plot.ploBB(kwargs_dict) + + return + + def _call_gmt(MT, kwargs_dict): + bb = BeachBall(MT, kwargs_dict) + return bb.get_psxy(kwargs_dict) + + def _call_decompose(MT, kwargs_dict): + + MT._isotropic = None + MT._deviatoric = None + MT._DC = None + MT._iso_percentage = None + MT._DC_percentage = None + MT._DC2 = None + MT._DC3 = None + MT._DC2_percentage = None + MT._CLVD = None + MT._seismic_moment = None + MT._moment_magnitude = None + + out_system = kwargs_dict['out_system'] + MT._output_basis = out_system + MT._decomposition_key = kwargs_dict['decomposition_key'] + + MT._decompose_M() + + print + + # build argument for local call within MT object: + lo_args = kwargs_dict['decomp_out_part'] + + if not lo_args: + lo_args = decomp_attrib_map_keys + + labels = '''Basis system of the input +Basis system of the output +Decomposition type +Full moment tensor +Isotropic part +Isotropic percentage +Deviatoric part +Deviatoric part +Deviatoric percentage +Double Couple part +Double Couple percentage +Second Double Couple part +Second Double Couple's percentage +Third Double Couple part +Third Double Couple's percentage +CLVD part in +CLVD percentage +Seismic moment (in Nm) +Moment magnitude ) +Eigenvalues +Eigenvectors +Tension-axis +Null-axis +Pressure-axis'''.splitlines() + + # for list of elements: + for label, arg in zip(labels, lo_args): + getter = getattr(MT, 'get_' + decomp_attrib_map[arg]) + x = getter(style='y', system=out_system) + print('%s: %s' % (label, x)) + + def _call_describe(MT, kwargs_dict): + print(MT) + + def _build_gmt_dict(options, optparser): + """ + """ + consistent_kwargs_dict = {} + temp_dict = {} + lo_allowed_options = ['GMT_string_type', 'GMT_scaling', + 'GMT_tension_colour', 'GMT_pressure_colour', + 'GMT_show_2FP2', 'GMT_show_1FP', + 'plot_viewpoint', 'GMT_plot_isotropic_part', + 'GMT_projection'] + + # check for allowed options: + for ao in lo_allowed_options: + if hasattr(options, ao): + temp_dict[ao] = getattr(options, ao) + + if temp_dict['GMT_show_1FP']: + try: + if int(float(temp_dict['GMT_show_1FP'])) in [1, 2]: + consistent_kwargs_dict['_GMT_1fp'] = \ + int(float(temp_dict['GMT_show_1FP'])) + except: + pass + + if temp_dict['GMT_show_2FP2']: + temp_dict['GMT_show_1FP'] = 0 + + consistent_kwargs_dict['_GMT_2fps'] = True + consistent_kwargs_dict['_GMT_1fp'] = 0 + + if temp_dict['GMT_string_type'][0].lower() not in ['f', 'l', 'e']: + print('type of desired string not known - taking "fill" instead') + consistent_kwargs_dict['_GMT_type'] = 'fill' + + else: + if temp_dict['GMT_string_type'][0] == 'f': + consistent_kwargs_dict['_GMT_type'] = 'fill' + elif temp_dict['GMT_string_type'][0] == 'l': + consistent_kwargs_dict['_GMT_type'] = 'lines' + else: + consistent_kwargs_dict['_GMT_type'] = 'EVs' + + if float(temp_dict['GMT_scaling']) < epsilon: + print('GMT scaling factor must be a factor larger than') + print('%f - set to 1, due to obviously stupid input value' % \ + (epsilon)) + temp_dict['GMT_scaling'] = 1 + + if temp_dict['plot_viewpoint']: + try: + vp = temp_dict['plot_viewpoint'].split(',') + if not len(vp) == 3: + raise + if not -90 <= float(vp[0]) <= 90: + raise + if not -180 <= float(vp[1]) <= 180: + raise + if not 0 <= float(vp[2]) % 360 <= 360: + raise + consistent_kwargs_dict['plot_viewpoint'] = \ + [float(vp[0]), float(vp[1]), float(vp[2])] + except: + pass + + if temp_dict['GMT_projection']: + lo_allowed_projections = ['stereo', 'ortho', 'lambert'] # ,'gnom'] + do_allowed_projections = dict(zip(('s', 'o', 'l', 'g'), + ('stereo', 'ortho', + 'lambert', 'gnom'))) + try: + gmtp = temp_dict['GMT_projection'].lower() + if gmtp in lo_allowed_projections: + consistent_kwargs_dict['plot_projection'] = gmtp + elif gmtp in do_allowed_projections.keys(): + consistent_kwargs_dict['plot_projection'] = \ + do_allowed_projections[gmtp] + else: + consistent_kwargs_dict['plot_projection'] = 'stereo' + except: + pass + + consistent_kwargs_dict['_GMT_scaling'] = \ + temp_dict['GMT_scaling'] + consistent_kwargs_dict['_GMT_tension_colour'] = \ + temp_dict['GMT_tension_colour'] + consistent_kwargs_dict['_GMT_pressure_colour'] = \ + temp_dict['GMT_pressure_colour'] + consistent_kwargs_dict['_plot_isotropic_part'] = \ + temp_dict['GMT_plot_isotropic_part'] + + return consistent_kwargs_dict + + def _build_decompose_dict(options, optparser): + + consistent_kwargs_dict = {} + temp_dict = {} + lo_allowed_options = ['decomp_out_complete', 'decomp_out_fancy', + 'decomp_out_part', 'in_system', 'out_system', + 'decomp_key'] + + # check for allowed options: + for ao in lo_allowed_options: + if hasattr(options, ao): + temp_dict[ao] = getattr(options, ao) + + for k in 'in_system', 'out_system': + s = getattr(options, k).upper() + if s not in lo_allowed_systems: + sys.exit('Unavailable coordinate system: %s' % s) + + consistent_kwargs_dict[k] = s + + consistent_kwargs_dict['decomposition_key'] = int( + temp_dict['decomp_key']) + + if temp_dict['decomp_out_part'] is None: + consistent_kwargs_dict['decomp_out_part'] = None + else: + parts = [x.strip().lower() + for x in temp_dict['decomp_out_part'].split(',')] + for part in parts: + if part not in decomp_attrib_map_keys: + sys.exit('Unavailable decomposition part: %s' % part) + + consistent_kwargs_dict['decomp_out_part'] = parts + + consistent_kwargs_dict['style'] = 'y' + + return consistent_kwargs_dict + + def _build_plot_dict(options, optparser): + consistent_kwargs_dict = {} + temp_dict = {} + + lo_allowed_options = [ + 'plot_outfile', 'plot_pa_plot', + 'plot_full_sphere', 'plot_part_of_m', 'plot_viewpoint', + 'plot_projection', 'plot_show_upper_hemis', 'plot_n_points', + 'plot_size', 'plot_tension_colour', 'plot_pressure_colour', + 'plot_total_alpha', 'plot_show_faultplanes', + 'plot_show_1faultplane', 'plot_show_princ_axes', + 'plot_show_basis_axes', 'plot_outerline', 'plot_nodalline', + 'plot_dpi', 'plot_only_lines', 'plot_input_system', + 'plot_isotropic_part'] + + # check for allowed options: + for ao in lo_allowed_options: + if hasattr(options, ao): + temp_dict[ao] = getattr(options, ao) + + consistent_kwargs_dict['plot_save_plot'] = False + if temp_dict['plot_outfile']: + consistent_kwargs_dict['plot_save_plot'] = True + lo_possible_formats = ['svg', 'png', 'eps', 'pdf', 'ps'] + + try: + (filepath, filename) = os.path.split(temp_dict['plot_outfile']) + if not filename: + filename = 'dummy_filename.svg' + (shortname, extension) = os.path.splitext(filename) + if not shortname: + shortname = 'dummy_shortname' + + if extension[1:].lower() in lo_possible_formats: + consistent_kwargs_dict['plot_outfile_format'] = \ + extension[1:].lower() + + if shortname.endswith('.'): + consistent_kwargs_dict['plot_outfile'] = \ + os.path.realpath(os.path.abspath(os.path.join( + os.curdir, filepath, + shortname + extension[1:].lower()))) + else: + consistent_kwargs_dict['plot_outfile'] = \ + os.path.realpath(os.path.abspath(os.path.join( + os.curdir, filepath, shortname + '.' + + extension[1:].lower()))) + else: + if filename.endswith('.'): + consistent_kwargs_dict['plot_outfile'] = \ + os.path.realpath(os.path.abspath(os.path.join( + os.curdir, filepath, + filename + lo_possible_formats[0]))) + else: + consistent_kwargs_dict['plot_outfile'] = \ + os.path.realpath(os.path.abspath(os.path.join( + os.curdir, filepath, filename + '.' + + lo_possible_formats[0]))) + consistent_kwargs_dict['plot_outfile_format'] = \ + lo_possible_formats[0] + + except: + msg = 'please provide valid filename: . !!\n' + msg += ' must be svg, png, eps, pdf, or ps ' + exit(msg) + + if temp_dict['plot_pa_plot']: + consistent_kwargs_dict['plot_pa_plot'] = True + else: + consistent_kwargs_dict['plot_pa_plot'] = False + + if temp_dict['plot_full_sphere']: + consistent_kwargs_dict['plot_full_sphere'] = True + consistent_kwargs_dict['plot_pa_plot'] = False + else: + consistent_kwargs_dict['plot_full_sphere'] = False + + if temp_dict['plot_part_of_m']: + try: + plottable_part_raw = temp_dict['plot_part_of_m'].lower()[:2] + if plottable_part_raw == 'is': + plottable_part = 'iso' + elif plottable_part_raw == 'de': + plottable_part = 'devi' + elif plottable_part_raw == 'dc': + plottable_part = 'dc' + elif plottable_part_raw == 'cl': + plottable_part = 'clvd' + else: + plottable_part = False + + consistent_kwargs_dict['plot_part_of_m'] = plottable_part + + except: + consistent_kwargs_dict['plot_part_of_m'] = False + + else: + consistent_kwargs_dict['plot_part_of_m'] = False + + if temp_dict['plot_viewpoint']: + try: + vp = temp_dict['plot_viewpoint'].split(',') + if not len(vp) == 3: + raise + if not -90 <= float(vp[0]) <= 90: + raise + if not -180 <= float(vp[1]) <= 180: + raise + if not 0 <= float(vp[2]) % 360 <= 360: + raise + consistent_kwargs_dict['plot_viewpoint'] = \ + [float(vp[0]), float(vp[1]), float(vp[2])] + except: + pass + + if temp_dict['plot_projection']: + lo_allowed_projections = ['stereo', 'ortho', 'lambert'] # ,'gnom'] + do_allowed_projections = dict(zip(('s', 'o', 'l', 'g'), + ('stereo', 'ortho', + 'lambert', 'gnom'))) + try: + ppl = temp_dict['plot_projection'].lower() + if ppl in lo_allowed_projections: + consistent_kwargs_dict['plot_projection'] = ppl + elif ppl in do_allowed_projections.keys(): + consistent_kwargs_dict['plot_projection'] = \ + do_allowed_projections[ppl] + else: + consistent_kwargs_dict['plot_projection'] = 'stereo' + except: + pass + + if temp_dict['plot_show_upper_hemis']: + consistent_kwargs_dict['plot_show_upper_hemis'] = True + + if temp_dict['plot_n_points']: + try: + if temp_dict['plot_n_points'] > 360: + consistent_kwargs_dict['plot_n_points'] = \ + int(temp_dict['plot_n_points']) + except: + pass + + if temp_dict['plot_size']: + try: + if 0.01 < temp_dict['plot_size'] <= 1: + consistent_kwargs_dict['plot_size'] = \ + temp_dict['plot_size'] * 10 / 2.54 + elif 1 < temp_dict['plot_size'] < 45: + consistent_kwargs_dict['plot_size'] = \ + temp_dict['plot_size'] / 2.54 + else: + consistent_kwargs_dict['plot_size'] = 5 + consistent_kwargs_dict['plot_aux_plot_size'] = \ + consistent_kwargs_dict['plot_size'] + except: + pass + + if temp_dict['plot_pressure_colour']: + try: + sec_colour_raw = temp_dict['plot_pressure_colour'].split(',') + if len(sec_colour_raw) == 1: + if sec_colour_raw[0].lower()[0] in list('bgrcmykw'): + consistent_kwargs_dict['plot_pressure_colour'] = \ + sec_colour_raw[0].lower()[0] + else: + raise + elif len(sec_colour_raw) == 3: + for sc in sec_colour_raw: + if not 0 <= (int(sc)) <= 255: + raise + consistent_kwargs_dict['plot_pressure_colour'] = \ + (float(sec_colour_raw[0]) / 255., + float(sec_colour_raw[1]) / 255., + float(sec_colour_raw[2]) / 255.) + else: + raise + except: + pass + + if temp_dict['plot_tension_colour']: + try: + sec_colour_raw = temp_dict['plot_tension_colour'].split(',') + if len(sec_colour_raw) == 1: + if sec_colour_raw[0].lower()[0] in list('bgrcmykw'): + consistent_kwargs_dict['plot_tension_colour'] = \ + sec_colour_raw[0].lower()[0] + else: + raise + elif len(sec_colour_raw) == 3: + for sc in sec_colour_raw: + if not 0 <= (int(float(sc))) <= 255: + raise + consistent_kwargs_dict['plot_tension_colour'] = \ + (float(sec_colour_raw[0]) / 255., + float(sec_colour_raw[1]) / 255., + float(sec_colour_raw[2]) / 255.) + else: + raise + except: + pass + + if temp_dict['plot_total_alpha']: + try: + if not 0 <= float(temp_dict['plot_total_alpha']) <= 1: + consistent_kwargs_dict['plot_total_alpha'] = 1 + else: + consistent_kwargs_dict['plot_total_alpha'] = \ + float(temp_dict['plot_total_alpha']) + except: + pass + + if temp_dict['plot_show_1faultplane']: + consistent_kwargs_dict['plot_show_1faultplane'] = True + try: + fp_args = temp_dict['plot_show_1faultplane'] + + if not int(fp_args[0]) in [1, 2]: + consistent_kwargs_dict['plot_show_FP_index'] = 1 + else: + consistent_kwargs_dict['plot_show_FP_index'] = \ + int(fp_args[0]) + + if not 0 < float(fp_args[1]) <= 20: + consistent_kwargs_dict['plot_faultplane_width'] = 2 + else: + consistent_kwargs_dict['plot_faultplane_width'] = \ + float(fp_args[1]) + + try: + sec_colour_raw = fp_args[2].split(',') + if len(sec_colour_raw) == 1: + if sec_colour_raw[0].lower()[0] in list('bgrcmykw'): + consistent_kwargs_dict['plot_faultplane_colour'] =\ + sec_colour_raw[0].lower()[0] + else: + raise + elif len(sec_colour_raw) == 3: + for sc in sec_colour_raw: + if not 0 <= (int(sc)) <= 255: + raise + consistent_kwargs_dict['plot_faultplane_colour'] = \ + (float(sec_colour_raw[0]) / 255., + float(sec_colour_raw[1]) / 255., + float(sec_colour_raw[2]) / 255.) + else: + raise + except: + consistent_kwargs_dict['plot_faultplane_colour'] = 'k' + + try: + if 0 <= float(fp_args[3]) <= 1: + consistent_kwargs_dict['plot_faultplane_alpha'] = \ + float(fp_args[3]) + except: + consistent_kwargs_dict['plot_faultplane_alpha'] = 1 + except: + pass + + if temp_dict['plot_show_faultplanes']: + consistent_kwargs_dict['plot_show_faultplanes'] = True + consistent_kwargs_dict['plot_show_1faultplane'] = False + + if temp_dict['plot_dpi']: + try: + if 200 <= int(temp_dict['plot_dpi']) <= 2000: + consistent_kwargs_dict['plot_dpi'] = \ + int(temp_dict['plot_dpi']) + else: + raise + except: + pass + + if temp_dict['plot_only_lines']: + consistent_kwargs_dict['plot_fill_flag'] = False + + if temp_dict['plot_outerline']: + consistent_kwargs_dict['plot_outerline'] = True + try: + fp_args = temp_dict['plot_outerline'] + if not 0 < float(fp_args[0]) <= 20: + consistent_kwargs_dict['plot_outerline_width'] = 2 + else: + consistent_kwargs_dict['plot_outerline_width'] = \ + float(fp_args[0]) + try: + sec_colour_raw = fp_args[1].split(',') + if len(sec_colour_raw) == 1: + if sec_colour_raw[0].lower()[0] in list('bgrcmykw'): + consistent_kwargs_dict['plot_outerline_colour'] = \ + sec_colour_raw[0].lower()[0] + else: + raise + elif len(sec_colour_raw) == 3: + for sc in sec_colour_raw: + if not 0 <= (int(sc)) <= 255: + raise + consistent_kwargs_dict['plot_outerline_colour'] = \ + (float(sec_colour_raw[0]) / 255., + float(sec_colour_raw[1]) / 255., + float(sec_colour_raw[2]) / 255.) + else: + raise + except: + consistent_kwargs_dict['plot_outerline_colour'] = 'k' + + try: + if 0 <= float(fp_args[2]) <= 1: + consistent_kwargs_dict['plot_outerline_alpha'] = \ + float(fp_args[2]) + except: + consistent_kwargs_dict['plot_outerline_alpha'] = 1 + except: + pass + + if temp_dict['plot_nodalline']: + consistent_kwargs_dict['plot_nodalline'] = True + try: + fp_args = temp_dict['plot_nodalline'] + + if not 0 < float(fp_args[0]) <= 20: + consistent_kwargs_dict['plot_nodalline_width'] = 2 + else: + consistent_kwargs_dict['plot_nodalline_width'] = \ + float(fp_args[0]) + try: + sec_colour_raw = fp_args[1].split(',') + if len(sec_colour_raw) == 1: + if sec_colour_raw[0].lower()[0] in list('bgrcmykw'): + consistent_kwargs_dict['plot_nodalline_colour'] = \ + sec_colour_raw[0].lower()[0] + else: + raise + elif len(sec_colour_raw) == 3: + for sc in sec_colour_raw: + if not 0 <= (int(sc)) <= 255: + raise + consistent_kwargs_dict['plot_nodalline_colour'] = \ + (float(sec_colour_raw[0]) / 255., + float(sec_colour_raw[1]) / 255., + float(sec_colour_raw[2]) / 255.) + else: + raise + except: + consistent_kwargs_dict['plot_nodalline_colour'] = 'k' + try: + if 0 <= float(fp_args[2]) <= 1: + consistent_kwargs_dict['plot_nodalline_alpha'] = \ + float(fp_args[2]) + except: + consistent_kwargs_dict['plot_nodalline_alpha'] = 1 + except: + pass + + if temp_dict['plot_show_princ_axes']: + consistent_kwargs_dict['plot_show_princ_axes'] = True + try: + fp_args = temp_dict['plot_show_princ_axes'] + + if not 0 < float(fp_args[0]) <= 40: + consistent_kwargs_dict['plot_princ_axes_symsize'] = 10 + else: + consistent_kwargs_dict['plot_princ_axes_symsize'] = \ + float(fp_args[0]) + + if not 0 < float(fp_args[1]) <= 20: + consistent_kwargs_dict['plot_princ_axes_lw '] = 3 + else: + consistent_kwargs_dict['plot_princ_axes_lw '] = \ + float(fp_args[1]) + try: + if 0 <= float(fp_args[2]) <= 1: + consistent_kwargs_dict['plot_princ_axes_alpha'] = \ + float(fp_args[2]) + except: + consistent_kwargs_dict['plot_princ_axes_alpha'] = 1 + except: + pass + + if temp_dict['plot_show_basis_axes']: + consistent_kwargs_dict['plot_show_basis_axes'] = True + + if temp_dict['plot_input_system']: + lo_allowed_systems = ['XYZ', 'NED', 'USE', 'NWU'] + try: + tpis = temp_dict['plot_input_system'][:3].upper() + if tpis in lo_allowed_systems: + consistent_kwargs_dict['in_system'] = tpis + else: + raise + except: + pass + + if temp_dict['plot_isotropic_part']: + consistent_kwargs_dict['plot_isotropic_part'] = \ + temp_dict['plot_isotropic_part'] + + return consistent_kwargs_dict + + def _build_describe_dict(options, optparser): + consistent_kwargs_dict = {} + + for k in 'in_system', 'out_system': + s = getattr(options, k).upper() + if s not in lo_allowed_systems: + sys.exit('Unavailable coordinate system: %s' % s) + + consistent_kwargs_dict[k] = s + + return consistent_kwargs_dict + + def _parse_arguments(main_call, its_arguments, optparser): + """ + """ + # todo: + # print '\n', main_call,its_arguments,'\n' + (options, args) = optparser.parse_args(its_arguments) + + # todo + # check, if arguments do not start with "-" - if so, there is a lack of + # arguments for the previous option + for val2check in options.__dict__.values(): + if str(val2check).startswith('-'): + try: + val2check_split = val2check.split(',') + for ii in val2check_split: + float(ii) + except: + sys.exit('ERROR - check carefully number of arguments ' + + 'for all options\n') + + if main_call == 'plot': + consistent_kwargs_dict = _build_plot_dict(options, optparser) + + elif main_call == 'gmt': + consistent_kwargs_dict = _build_gmt_dict(options, optparser) + + elif main_call == 'decompose': + consistent_kwargs_dict = _build_decompose_dict(options, optparser) + + elif main_call == 'describe': + consistent_kwargs_dict = _build_describe_dict(options, optparser) + + return consistent_kwargs_dict + + def _add_group_system(parent): + group_system = OptionGroup(parent, 'Basis systems') + group_system.add_option('-i', '--input-system', + action="store", + dest='in_system', + metavar='', + default='NED', + help=''' +Define the coordinate system of the source mechanism [Default: NED]. + +Available coordinate systems: + + * NED: North, East, Down + * USE: Up, South, East (Global CMT) + * XYZ: East, North, Up (Jost and Herrmann) + * NWU: North, West, Up (Stein and Wysession) +'''.lstrip()) + + group_system.add_option('-o', '--output-system', + action="store", + dest='out_system', + metavar='', + default='NED', + help=''' +Define the coordinate system of the output. See '--input-system' for a list of +available coordinate systems [Default: NED].'''.lstrip()) + + parent.add_option_group(group_system) + + def _build_optparsers(): + + _do_parsers = {} + + desc = """ +Generate a beachball representation which can be plotted with GMT. + +This tool produces output which can be fed into the GMT command `psxy`. The +output consists of coordinates which describe the lines of the beachball in +standard cartesian coordinates, centered at zero. + +In order to generate a beachball diagram, this tool has to be called twice with +different arguments of the --type option. First to define the colored areas +(--type=fill) and second for the nodal and border lines (--type=lines). + +Example: + + mopad gmt 30,60,90 --type=fill | \ +psxy -Jx4/4 -R-2/2/-2/2 -P -Cpsxy_fill.cpt -M -L -K > out.ps + + mopad gmt 30,60,90 --type=lines | \ +psxy -Jx4/4 -R-2/2/-2/2 -P -Cpsxy_lines.cpt -W2p -P -M -O >> out.ps + +""" + + parser_gmt = OptionParser( + usage='mopad.py gmt [options]', + description=desc, formatter=MopadHelpFormatter()) + + group_type = OptionGroup(parser_gmt, 'Output') + group_show = OptionGroup(parser_gmt, 'Appearance') + group_geo = OptionGroup(parser_gmt, 'Geometry') + + group_type.add_option('-t', '--type', + type='string', + dest='GMT_string_type', + action='store', + default='fill', + help='Chosing the respective psxy data set: ' + 'area to fill (fill), nodal lines (lines), ' + 'or eigenvector positions (ev) [Default: fill]', + metavar='') + + group_show.add_option('-s', '--scaling', + dest='GMT_scaling', + action='store', + default='1', + type='float', + metavar='', + help='Spatial scaling factor of the beachball ' + '[Default: 1]') + group_show.add_option('-r', '--colour1', + dest='GMT_tension_colour', + type='int', + action='store', + metavar='', + default='1', + help="-Z option's key (see help for 'psxy') " + 'for the tension colour of the beachball - ' + 'type: integer [Default: 1]') + group_show.add_option('-w', '--colour2', + dest='GMT_pressure_colour', + type='int', + action='store', + metavar='', + default='0', + help="-Z option's key (see help for 'psxy') " + 'for the pressure colour of the beachball - ' + 'type: integer [Default: 0]') + group_show.add_option('-D', '--faultplanes', + dest='GMT_show_2FP2', + action='store_true', + default=False, + help='Key, if 2 faultplanes shall be shown ' + '[Default: deactivated]') + group_show.add_option('-d', '--show_1fp', + type='choice', + dest='GMT_show_1FP', + choices=['1', '2'], + metavar='', + action='store', + default=False, + help='Key for plotting 1 specific faultplane - ' + 'value: 1,2 [Default: None]') + group_geo.add_option('-v', '--viewpoint', + action='store', + dest='plot_viewpoint', + metavar='', + default=None, + help='Coordinates (in degrees) of the viewpoint ' + 'onto the projection - type: comma separated ' + '3-tuple [Default: None]') + group_geo.add_option('-p', '--projection', + action='store', + dest='GMT_projection', + metavar='', + default=None, + help='Two-dimensional projection of the sphere - ' + 'value: (s)tereographic, (l)ambert, ' + '(o)rthographic [Default: (s)tereographic]') + group_show.add_option('-I', '--show_isotropic_part', + dest='GMT_plot_isotropic_part', + action='store_true', + default=False, + help='Key for considering the isotropic part ' + 'for plotting [Default: deactivated]') + + parser_gmt.add_option_group(group_type) + parser_gmt.add_option_group(group_show) + parser_gmt.add_option_group(group_geo) + + _do_parsers['gmt'] = parser_gmt + + # plot + desc_plot = """ + Plot a beachball diagram of the provided mechanism. + + Several styles and configurations are available. Also saving + on the fly can be enabled. + ONLY THE DEVIATORIC COMPONENT WILL BE PLOTTED by default; + for including the isotropic part, use the '--show_isotropic_part' + option! + """ + parser_plot = OptionParser( + usage="mopad.py plot [options]", + description=desc_plot, formatter=MopadHelpFormatter()) + + group_save = OptionGroup(parser_plot, 'Saving') + group_type = OptionGroup(parser_plot, 'Type of plot') + group_quality = OptionGroup(parser_plot, 'Quality') + group_colours = OptionGroup(parser_plot, 'Colours') + group_misc = OptionGroup(parser_plot, 'Miscellaneous') + group_dc = OptionGroup(parser_plot, 'Fault planes') + group_geo = OptionGroup(parser_plot, 'Geometry') + group_app = OptionGroup(parser_plot, 'Appearance') + + group_save.add_option('-f', '--output_file', + action="store", + dest='plot_outfile', + metavar='', + default=None, + nargs=1, + help='(Absolute) filename for saving ' + '[Default: None]') + + group_type.add_option('-E', '--eigen_system', + action="store_true", + dest='plot_pa_plot', + default=False, + help='Key for plotting principal axis ' + 'system/eigensystem [Default: deactivated]') + + group_type.add_option('-O', '--full_sphere', + action="store_true", + dest='plot_full_sphere', + default=False, + help='Key for plotting the full sphere ' + '[Default: deactivated]') + + group_type.add_option('-P', '--partial', + action="store", + dest='plot_part_of_m', + metavar='', + default=None, + help='Key for plotting only a specific part of ' + 'M - values: iso,devi,dc,clvd [Default: None] ') + + group_geo.add_option('-v', '--viewpoint', + action="store", + dest='plot_viewpoint', + metavar='', + default=None, + help='Coordinates (in degrees) of the viewpoint ' + 'onto the projection - type: comma separated ' + '3-tuple [Default: None]') + + group_geo.add_option('-p', '--projection', + action="store", + dest='plot_projection', + metavar='', + default=None, + help='Two-dimensional projection of the sphere ' + '- value: (s)tereographic, (l)ambert, ' + '(o)rthographic [Default: (s)tereographic]') + + group_type.add_option('-U', '--upper', + action="store_true", + dest='plot_show_upper_hemis', + default=False, + help='Key for plotting the upper hemisphere ' + '[Default: deactivated]') + + group_quality.add_option('-N', '--points', + action="store", + metavar='', + dest='plot_n_points', + type="int", + default=None, + help='Minimum number of points, used for ' + 'nodal lines [Default: None]') + + group_app.add_option('-s', '--size', + action="store", + dest='plot_size', + metavar='', + type="float", + default=None, + help='Size of plot (diameter) in cm ' + '[Default: None]') + + group_colours.add_option('-w', '--pressure_colour', + action="store", + dest='plot_pressure_colour', + metavar='', + default=None, + help='Colour of the tension area - values: ' + 'comma separated RGB 3-tuples OR MATLAB ' + 'conform colour names [Default: None]') + + group_colours.add_option('-r', '--tension_colour', + action="store", + dest='plot_tension_colour', + metavar='', + default=None, + help='Colour of the pressure area values: ' + 'comma separated RGB 3-tuples OR MATLAB ' + 'conform colour names [Default: None]') + + group_app.add_option('-a', '--alpha', + action="store", + dest='plot_total_alpha', + metavar='', + type='float', + default=None, + help='Alpha value for the total plot - value: ' + 'float between 1=opaque to 0=transparent ' + '[Default: None]') + + group_dc.add_option('-D', '--dc', + action="store_true", + dest='plot_show_faultplanes', + default=False, + help='Key for plotting both double couple ' + 'faultplanes (blue) [Default: deactivated]') + + group_dc.add_option('-d', '--show1fp', + action="store", + metavar=' ', + dest='plot_show_1faultplane', + default=None, + nargs=4, + help='Key for plotting 1 specific faultplane - ' + '4 arguments as space separated list - ' + 'index values: 1,2, linewidth value: float, ' + 'line colour value: string or RGB-3-tuple, alpha ' + 'value: float between 0 and 1 [Default: None] ') + + group_misc.add_option('-e', '--eigenvectors', + action="store", + dest='plot_show_princ_axes', + metavar=' ', + default=None, + nargs=3, + help='Key for showing eigenvectors - ' + '3 arguments as space separated list - symbol ' + 'size value: float, symbol linewidth value: ' + 'float, symbol alpha value: float between 0 ' + 'and 1 [Default: None]') + + group_misc.add_option('-b', '--basis_vectors', + action="store_true", + dest='plot_show_basis_axes', + default=False, + help='Key for showing NED basis axes in plot ' + '[Default: deactivated]') + + group_app.add_option('-l', '--lines', + action="store", + dest='plot_outerline', + metavar=' ', + nargs=3, + default=None, + help='Define the style of the outer line - ' + '3 arguments as space separated list - ' + 'linewidth value: float, line colour value: ' + 'string or RGB-3-tuple), alpha value: float ' + 'between 0 and 1 [Default: None]') + + group_app.add_option('-n', '--nodals', + action="store", + dest='plot_nodalline', + metavar=' ', + default=None, + nargs=3, + help='Define the style of the nodal lines - 3 ' + 'arguments as space separated list - linewidth ' + 'value: float, line colour value: string or ' + 'RGB-3-tuple), alpha value: float between 0 and ' + '1 [Default: None]') + + group_quality.add_option('-Q', '--quality', + action="store", + dest='plot_dpi', + metavar='', + type="int", + default=None, + help='Set the quality for the plot in ' + 'terms of dpi (minimum=200) [Default: None] ') + + group_type.add_option('-L', '--lines_only', + action="store_true", + dest='plot_only_lines', + default=False, + help='Key for plotting lines only (no filling ' + '- this overwrites all "fill"-related options) ' + '[Default: deactivated] ') + + group_misc.add_option('-i', '--input-system', + action="store", + dest='plot_input_system', + metavar='', + default=False, + help='Define the coordinate system of the ' + 'source mechanism - value: NED,USE,XYZ,NWU ' + '[Default: NED] ') + + group_type.add_option('-I', '--show_isotropic_part', + dest='plot_isotropic_part', + action='store_true', + default=False, + help='Key for considering the isotropic part ' + 'for plotting [Default: deactivated]') + + parser_plot.add_option_group(group_save) + parser_plot.add_option_group(group_type) + parser_plot.add_option_group(group_quality) + parser_plot.add_option_group(group_colours) + parser_plot.add_option_group(group_misc) + parser_plot.add_option_group(group_dc) + parser_plot.add_option_group(group_geo) + parser_plot.add_option_group(group_app) + + _do_parsers['plot'] = parser_plot + + desc_decomp = """ +Decompose moment tensor into additive contributions. + +This method implements four different decompositions following the conventions +given by Jost & Herrmann (1998), and Dahm (1997). The type of decomposition can +be selected with the '--type' option. Use the '--partial' option, if only parts +of the full decomposition are required. + +By default, the decomposition results are printed in the following order: + + * 01 - basis of the provided input (string) + * 02 - basis of the representation (string) + * 03 - chosen decomposition type (integer) + + * 04 - full moment tensor (matrix) + + * 05 - isotropic part (matrix) + * 06 - isotropic percentage (float) + * 07 - deviatoric part (matrix) + * 08 - deviatoric percentage (float) + + * 09 - DC part (matrix) + * 10 - DC percentage (float) + * 11 - DC2 part (matrix) + * 12 - DC2 percentage (float) + * 13 - DC3 part (matrix) + * 14 - DC3 percentage (float) + + * 15 - CLVD part (matrix) + * 16 - CLVD percentage (matrix) + + * 17 - seismic moment (float) + * 18 - moment magnitude (float) + + * 19 - eigenvectors (3-array) + * 20 - eigenvalues (list) + * 21 - p-axis (3-array) + * 22 - neutral axis (3-array) + * 23 - t-axis (3-array) +""" + + parser_decompose = OptionParser( + usage="mopad decompose [options]", + description=desc_decomp, formatter=MopadHelpFormatter()) + + group_type = OptionGroup( + parser_decompose, 'Type of decomposition') + group_part = OptionGroup( + parser_decompose, 'Output selection') + + group_part.add_option('-p', '--partial', + action="store", + dest='decomp_out_part', + default=None, + metavar='', + help=''' +Print a subset of the decomposition results. + +Give a comma separated list of what parts of the results should be +printed [Default: None]. The following parts are available: + + %s +''' % ', '.join(decomp_attrib_map_keys)) + + group_type.add_option('-t', '--type', + action="store", + dest='decomp_key', + metavar='', + default=1, + type='int', + help=''' +Choose type of decomposition - values 1,2,3,4 \n[Default: 1]: + +%s +''' % '\n'.join([' * %s - %s' % (k, v[0]) for (k, v) + in MomentTensor.decomp_dict.items()])) + + parser_decompose.add_option_group(group_type) + parser_decompose.add_option_group(group_part) + _add_group_system(parser_decompose) + + _do_parsers['decompose'] = parser_decompose + + parser_describe = OptionParser( + usage="mopad describe [options]", + description=''' +Print the detailed description of a source mechanism + + +For a given source mechanism, orientations of the fault planes, moment, +magnitude, and moment tensor are printed. Input and output coordinate basis +systems can be specified.'''.lstrip(), + formatter=MopadHelpFormatter()) + + _add_group_system(parser_describe) + + _do_parsers['describe'] = parser_describe + + return _do_parsers + + if len(sys.argv) < 2: + call = 'help' + + else: + + call = sys.argv[1].lower() + abbrev = dict(zip(('p', 'g', 'd', 'i', '--help', '-h'), ( + 'plot', 'gmt', 'decompose', 'describe', 'help', 'help'))) + + if call in abbrev: + call = abbrev[call] + + if call not in abbrev.values(): + sys.exit('no such method: %s' % call) + + if call == 'help': + helpstring = """ + +Usage: mopad [options] + + +Type 'mopad --help' for help on a specific method. + + +MoPaD (version %.1f) - Moment Tensor Plotting and Decomposition Tool + +MoPaD is a tool to plot and decompose moment tensor representations of seismic +sources which are commonly used in seismology. This tool is completely +controlled via command line parameters, which consist of a and a + argument and zero or more options. The argument +tells MoPaD what to do and the argument specifies an input +moment tensor source in one of the formats described below. + +Available methods: + + * plot: plot a beachball representation of a source mechanism + * describe: print detailed description of a source mechanism + * decompose: decompose a source mechanism according to various conventions + * gmt: output beachball representation in a format suitable for + plotting with GMT + +The source-mechanism is given as a comma separated list (NO BLANK SPACES!) of +values which is interpreted differently, according to the number of values in +the list. The following source-mechanism representations are available: + + * strike,dip,rake + * strike,dip,rake,moment + * M11,M22,M33,M12,M13,M23 + * M11,M22,M33,M12,M13,M23,moment + * M11,M12,M13,M21,M22,M23,M31,M32,M33 + +Angles are given in degrees, moment tensor components and scalar moment are +given in [Nm] for a coordinate system with axes pointing North, East, and Down +by default. +_______________________________________________________________________________ + +EXAMPLES +-------- + +'plot' : +-- +To generate the "beachball" representation of a pure normal faulting event with +a strike angle of 0 degrees and a dip of 45 degrees, use either of the +following commands: + + mopad plot 0,45,-90 + + mopad plot 0,1,-1,0,0,0 + + +'describe': +-- +To see the seismic moment tensor entries (in GlobalCMT's USE basis) and the +orientation of the auxilliary plane for a shear crack with the +(strike,dip,slip-rake) tuple (90,45,45) use: + + mopad describe 90,45,45 -o USE + + +'decompose': +-- +Get the deviatoric part of a seismic moment tensor M=(1,2,3,4,5,6) together +with the respective double-couple- and CLVD-components by using: + + mopad decompose 1,2,3,4,5,6 -p devi,dc,clvd + + +""" % (MOPAD_VERSION) + + print(helpstring) + + sys.exit() + + try: + M_raw = [float(xx) for xx in sys.argv[2].split(',')] + except: + dummy_list = [] + dummy_list.append(sys.argv[0]) + dummy_list.append(sys.argv[1]) + dummy_list.append('0,0,0') + dummy_list.append('-h') + + sys.argv = dummy_list + M_raw = [float(xx) for xx in sys.argv[2].split(',')] + + if not len(M_raw) in [3, 4, 6, 7, 9]: + print('\nERROR!! Provide proper source mechanism\n\n') + sys.exit() + if len(M_raw) in [4, 6, 7, 9] and len(np.array(M_raw).nonzero()[0]) == 0: + print('\nERROR!! Provide proper source mechanism\n\n') + sys.exit() + + aa = _handle_input(call, M_raw, sys.argv[3:], _build_optparsers()[call]) + if aa is not None: + print(aa) diff --git a/BIN/mopad_wrapper.py b/BIN/mopad_wrapper.py new file mode 100644 index 0000000..aabf07d --- /dev/null +++ b/BIN/mopad_wrapper.py @@ -0,0 +1,325 @@ +# -*- coding: utf-8 -*- +# ----------------------------------------------- +# Filename: mopad_wrapper.py +# Purpose: Wrapper for mopad +# Author: Tobias Megies, Moritz Beyreuther +# Email: megies@geophysik.uni-muenchen.de +# +# Copyright (C) 2008-2012 ObsPy Development Team +# +# Notes +# Modified by Andrea Chiang for option to display isotropic part, last update on May 22, 2018 +# ----------------------------------------------- +""" +ObsPy wrapper to the *Moment tensor Plotting and Decomposition tool* (MoPaD) +written by Lars Krieger and Sebastian Heimann. + +.. seealso:: [Krieger2012]_ + +.. warning:: The MoPaD wrapper does not yet provide the full functionality of + MoPaD. Please consider using the command line script ``obspy-mopad`` for + now if you need the full power of MoPaD. + +:copyright: + The ObsPy Development Team (devs@obspy.org) +:license: + GNU Lesser General Public License, Version 3 + (https://www.gnu.org/copyleft/lesser.html) +""" +from __future__ import (absolute_import, division, print_function, + unicode_literals) +from future.builtins import * # NOQA @UnusedWildImport + +import numpy as np +import matplotlib.collections as mpl_collections +from matplotlib import patches, transforms + +from obspy.imaging.beachball import xy2patch +from mopad import BeachBall as mopad_BeachBall +from mopad import MomentTensor as mopad_MomentTensor +from mopad import epsilon + + +# seems the base system we (gmt) are using is called "USE" in mopad +KWARG_MAP = { + 'size': ['plot_size', 'plot_aux_plot_size'], + 'linewidth': ['plot_nodalline_width', 'plot_outerline_width'], + 'facecolor': ['plot_tension_colour'], + 'edgecolor': ['plot_outerline_colour'], + 'bgcolor': [], + 'alpha': ['plot_total_alpha'], + 'width': [], + 'outfile': ['plot_outfile'], + 'format': ['plot_outfile_format'], + 'nofill': ['plot_only_lines'] +} + + +def beach(fm, linewidth=2, facecolor='b', bgcolor='w', edgecolor='k', + alpha=1.0, xy=(0, 0), width=200, size=100, nofill=False, + zorder=100, mopad_basis='USE', axes=None, show_iso=False): + """ + Return a beach ball as a collection which can be connected to an + current matplotlib axes instance (ax.add_collection). Based on MoPaD. + + S1, D1, and R1, the strike, dip and rake of one of the focal planes, can + be vectors of multiple focal mechanisms. + + :param fm: Focal mechanism that is either number of mechanisms (NM) by 3 + (strike, dip, and rake) or NM x 6 (M11, M22, M33, M12, M13, M23 - the + six independent components of the moment tensor, where the coordinate + system is 1,2,3 = Up,South,East which equals r,theta,phi - + Harvard/Global CMT convention). The relation to Aki and Richards + x,y,z equals North,East,Down convention is as follows: Mrr=Mzz, + Mtt=Mxx, Mpp=Myy, Mrt=Mxz, Mrp=-Myz, Mtp=-Mxy. + The strike is of the first plane, clockwise relative to north. + The dip is of the first plane, defined clockwise and perpendicular to + strike, relative to horizontal such that 0 is horizontal and 90 is + vertical. The rake is of the first focal plane solution. 90 moves the + hanging wall up-dip (thrust), 0 moves it in the strike direction + (left-lateral), -90 moves it down-dip (normal), and 180 moves it + opposite to strike (right-lateral). + :param facecolor: Color to use for quadrants of tension; can be a string, + e.g. ``'r'``, ``'b'`` or three component color vector, [R G B]. + Defaults to ``'b'`` (blue). + :param bgcolor: The background color. Defaults to ``'w'`` (white). + :param edgecolor: Color of the edges. Defaults to ``'k'`` (black). + :param alpha: The alpha level of the beach ball. Defaults to ``1.0`` + (opaque). + :param xy: Origin position of the beach ball as tuple. Defaults to + ``(0, 0)``. + :type width: int + :param width: Symbol size of beach ball. Defaults to ``200``. + :param size: Controls the number of interpolation points for the + curves. Minimum is automatically set to ``100``. + :param nofill: Do not fill the beach ball, but only plot the planes. + :param zorder: Set zorder. Artists with lower zorder values are drawn + first. + :param mopad_basis: The basis system. Defaults to ``'USE'``. See the + `Supported Basis Systems`_ section below for a full list of supported + systems. + :type axes: :class:`matplotlib.axes.Axes` + :param axes: Used to make beach balls circular on non-scaled axes. Also + maintains the aspect ratio when resizing the figure. Will not add + the returned collection to the axes instance. + + .. rubric:: _`Supported Basis Systems` + + ========= =================== ============================================= + Short Basis vectors Usage + ========= =================== ============================================= + ``'NED'`` North, East, Down Jost and Herrmann 1989 + ``'USE'`` Up, South, East Global CMT Catalog, Larson et al. 2010 + ``'XYZ'`` East, North, Up General formulation, Jost and Herrmann 1989 + ``'RT'`` Radial, Transverse, psmeca (GMT), Wessel and Smith 1999 + Tangential + ``'NWU'`` North, West, Up Stein and Wysession 2003 + ========= =================== ============================================= + """ + # initialize beachball + mt = mopad_MomentTensor(fm, in_system=mopad_basis) + bb = mopad_BeachBall(mt, npoints=size) + + ## Snippets added by A. Chiang + if show_iso: + bb._plot_isotropic_part = True + bb._nodallines_in_NED_system() + + ## + + bb._setup_BB(unit_circle=False) + + # extract the coordinates and colors of the lines + radius = width / 2.0 + neg_nodalline = bb._nodalline_negative_final_US + pos_nodalline = bb._nodalline_positive_final_US + tension_colour = facecolor + pressure_colour = bgcolor + + if nofill: + tension_colour = 'none' + pressure_colour = 'none' + + # based on mopads _setup_plot_US() function + # collect patches for the selection + coll = [None, None, None] + coll[0] = patches.Circle(xy, radius=radius) + coll[1] = xy2patch(neg_nodalline[0, :], neg_nodalline[1, :], radius, xy) + coll[2] = xy2patch(pos_nodalline[0, :], pos_nodalline[1, :], radius, xy) + + # set the color of the three parts + fc = [None, None, None] + if bb._plot_clr_order > 0: + fc[0] = pressure_colour + fc[1] = tension_colour + fc[2] = tension_colour + if bb._plot_curve_in_curve != 0: + fc[0] = tension_colour + if bb._plot_curve_in_curve < 1: + fc[1] = pressure_colour + fc[2] = tension_colour + else: + coll = [coll[i] for i in (0, 2, 1)] + fc[1] = pressure_colour + fc[2] = tension_colour + else: + fc[0] = tension_colour + fc[1] = pressure_colour + fc[2] = pressure_colour + if bb._plot_curve_in_curve != 0: + fc[0] = pressure_colour + if bb._plot_curve_in_curve < 1: + fc[1] = tension_colour + fc[2] = pressure_colour + else: + coll = [coll[i] for i in (0, 2, 1)] + fc[1] = tension_colour + fc[2] = pressure_colour + + if bb._pure_isotropic: + if abs(np.trace(bb._M)) > epsilon: + # use the circle as the most upper layer + coll = [coll[0]] + if bb._plot_clr_order < 0: + fc = [tension_colour] + else: + fc = [pressure_colour] + + # transform the patches to a path collection and set + # the appropriate attributes + collection = mpl_collections.PatchCollection(coll, match_original=False) + collection.set_facecolors(fc) + # Use the given axes to maintain the aspect ratio of beachballs on figure + # resize. + if axes is not None: + # This is what holds the aspect ratio (but breaks the positioning) + collection.set_transform(transforms.IdentityTransform()) + # Next is a dirty hack to fix the positioning: + # 1. Need to bring the all patches to the origin (0, 0). + for p in collection._paths: + p.vertices -= xy + # 2. Then use the offset property of the collection to position the + # patches + collection.set_offsets(xy) + collection._transOffset = axes.transData + collection.set_edgecolors(edgecolor) + collection.set_alpha(alpha) + collection.set_linewidth(linewidth) + collection.set_zorder(zorder) + return collection + + +def beachball(fm, linewidth=2, facecolor='b', bgcolor='w', edgecolor='k', + alpha=1.0, xy=(0, 0), width=200, size=100, nofill=False, + zorder=100, mopad_basis='USE', outfile=None, format=None, + fig=None): + """ + Draws a beach ball diagram of an earthquake focal mechanism. Based on + MoPaD. + + S1, D1, and R1, the strike, dip and rake of one of the focal planes, can + be vectors of multiple focal mechanisms. + + :param fm: Focal mechanism that is either number of mechanisms (NM) by 3 + (strike, dip, and rake) or NM x 6 (M11, M22, M33, M12, M13, M23 - the + six independent components of the moment tensor, where the coordinate + system is 1,2,3 = Up,South,East which equals r,theta,phi). The strike + is of the first plane, clockwise relative to north. + The dip is of the first plane, defined clockwise and perpendicular to + strike, relative to horizontal such that 0 is horizontal and 90 is + vertical. The rake is of the first focal plane solution. 90 moves the + hanging wall up-dip (thrust), 0 moves it in the strike direction + (left-lateral), -90 moves it down-dip (normal), and 180 moves it + opposite to strike (right-lateral). + :param facecolor: Color to use for quadrants of tension; can be a string, + e.g. ``'r'``, ``'b'`` or three component color vector, [R G B]. + Defaults to ``'b'`` (blue). + :param bgcolor: The background color. Defaults to ``'w'`` (white). + :param edgecolor: Color of the edges. Defaults to ``'k'`` (black). + :param alpha: The alpha level of the beach ball. Defaults to ``1.0`` + (opaque). + :param xy: Origin position of the beach ball as tuple. Defaults to + ``(0, 0)``. + :type width: int + :param width: Symbol size of beach ball. Defaults to ``200``. + :param size: Controls the number of interpolation points for the + curves. Minimum is automatically set to ``100``. + :param nofill: Do not fill the beach ball, but only plot the planes. + :param zorder: Set zorder. Artists with lower zorder values are drawn + first. + :param mopad_basis: The basis system. Defaults to ``'USE'``. See the + `Supported Basis Systems`_ section below for a full list of supported + systems. + :param outfile: Output file string. Also used to automatically + determine the output format. Supported file formats depend on your + matplotlib backend. Most backends support png, pdf, ps, eps and + svg. Defaults to ``None``. + :param format: Format of the graph picture. If no format is given the + outfile parameter will be used to try to automatically determine + the output format. If no format is found it defaults to png output. + If no outfile is specified but a format is, than a binary + imagestring will be returned. + Defaults to ``None``. + :param fig: Give an existing figure instance to plot into. New Figure if + set to ``None``. + + .. rubric:: _`Supported Basis Systems` + + ========= =================== ============================================= + Short Basis vectors Usage + ========= =================== ============================================= + ``'NED'`` North, East, Down Jost and Herrmann 1989 + ``'USE'`` Up, South, East Global CMT Catalog, Larson et al. 2010 + ``'XYZ'`` East, North, Up General formulation, Jost and Herrmann 1989 + ``'RT'`` Radial, Transverse, psmeca (GMT), Wessel and Smith 1999 + Tangential + ``'NWU'`` North, West, Up Stein and Wysession 2003 + ========= =================== ============================================= + + .. rubric:: Examples + + (1) Using basis system ``'NED'``. + + >>> from obspy.imaging.mopad_wrapper import beachball + >>> mt = [1, 2, 3, -4, -5, -10] + >>> beachball(mt, mopad_basis='NED') #doctest: +SKIP + + .. plot:: + + from obspy.imaging.mopad_wrapper import beachball + mt = [1, 2, 3, -4, -5, -10] + beachball(mt, mopad_basis='NED') + """ + mopad_kwargs = {} + loc = locals() + # map to kwargs used in mopad + for key in KWARG_MAP: + value = loc[key] + for mopad_key in KWARG_MAP[key]: + mopad_kwargs[mopad_key] = value + # convert from points to size in cm + for key in ['plot_aux_plot_size', 'plot_size']: + # 100.0 is matplotlib's default DPI for savefig + mopad_kwargs[key] = mopad_kwargs[key] / 100.0 * 2.54 + # use nofill kwarg + + mt = mopad_MomentTensor(fm, system=mopad_basis) + bb = mopad_BeachBall(mt, npoints=size) + + # show plot in a window + if outfile is None: + bb.ploBB(mopad_kwargs) + # save plot to file + else: + # no format specified, parse it from outfile name + if mopad_kwargs['plot_outfile_format'] is None: + mopad_kwargs['plot_outfile_format'] = \ + mopad_kwargs['plot_outfile'].split(".")[-1] + else: + # append file format if not already at end of outfile + if not mopad_kwargs['plot_outfile'].endswith( + mopad_kwargs['plot_outfile_format']): + mopad_kwargs['plot_outfile'] += "." + \ + mopad_kwargs['plot_outfile_format'] + bb.save_BB(mopad_kwargs) + diff --git a/BIN/mtdecomp_v2.ipynb b/BIN/mtdecomp_v2.ipynb new file mode 100644 index 0000000..e85d66f --- /dev/null +++ b/BIN/mtdecomp_v2.ipynb @@ -0,0 +1,501 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Moment Tensor Decomposition Tool\n", + "\n", + "This workbook is a port of the matlab mtdecomp.m tool for decomposing a moment tensor solution.\n", + "\n", + "Written by Douglas Dreger July 26, 2018\n", + "\n", + "Updated: Dreger September 2, 2020" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Background\n", + "\n", + "### This notebook takes an input moment tensor in Aki & Richard convention and applies a source type decomposition. The moment tensor is decomposed into an isotropic tensor and a deviatoric tensor. The deviatoric tensor is further decomposed to a double-couple and a CLVD that share the same principle eigenvector (either compression or dilation). An excellent description of this is given in Bob Herrmann's 'A Student's Guide to the Moment Tensor' (Jost and Herrmann, 1989). It also plots the moment tensor in the Tape & Tape (2014) source-type Lune, a geometrical representation of the moment tensor. Source-type plots are quite useful for interpreting moment tensor results particularly for atypical seismic events (underground cavity collapse, volcanic/geothermal seismicity, and explosions). The example given is for the DPRK 2017 nuclear test from Chiang et al. (2018) using the source-type inversion method (Ford et al., 2010; Nayak and Dreger (2015).\n", + "\n", + "### Some Good Reading\n", + "\n", + "1. Jost, M. L. and R. B. Herrmann (1989), A student's guide to and review of moment tensors, Seism. Res. Letters, 60, 37-57.\n", + "2. Tape, W., and C. Tape, (2012), A geometric setting for moment tensors, Geophys. J. Int., 190(1), 476-498, doi: 10.1111/j.1365-246X.2012.05491.x. \n", + "3. Chiang, A. , D. S. Dreger, S. R. Ford, G. A. Ichinose, E. Matzel, S. Myers, and W. R. Walter (2018). Moment tensor source-type analysis for the Democratic People’s Republic of Korea declared nuclear explosions (2006-2017) and 03-SEP2017 collapse event,Seism. Res. Lett., Vol. 89, No. 6, 2152-2165, doi: 10/1785/0220180130.\n", + "4. Ford, S. R., D. S. Dreger and W. R. Walter (2010). Network sensitivity solutions for regional moment tensor inversions, Bull. Seism. Soc. Am., 100, 1962-1970.\n", + "5. Nayak, A. and D. S. Dreger (2015). Source-type Specific Inversion of Moment Tensors, Bull. Seism. Soc. Am. 105:2987-3000; doi:10.1785/0120140334\n", + "\n", + "### This notebook uses the mopad functions of obspy and a wrapper modified by Andrea Chiang to correctly the full moment tensor." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "#Initialization of packages\n", + "import math\n", + "\n", + "from numpy import linalg as la\n", + "import matplotlib\n", + "import matplotlib.pyplot as plt\n", + "import pandas as pd\n", + "\n", + "#Note that Andrea Chiang's modified mopad.py and mopad_wrapper.py need to be in the same directory as the notebook\n", + "import sys \n", + "sys.path.append('./')\n", + "from mopad_wrapper import beach\n", + "import fpsol\n", + "\n", + "import numpy as np" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "#Enter moment tensor parameters here\n", + "#It is assumed that the moment tensor units are SI, x1.0e13 dyne cm\n", + "\n", + "#mxx mxy mxz myy myz mzz\n", + "#-9.270e+24 -5.539e+24 -4.062e+24 1.012e+25 5.179e+23 -8.502e+23\n", + "\n", + "Mxx=-5584.449\n", + "Mxy=1894.188\n", + "Mxz=-3589.742\n", + "Myy=7696.749\n", + "Myz=2095.005\n", + "Mzz=-2112.300\n", + "\n", + "#DPRK2017 Nuclear Test\n", + "\"\"\"\n", + "Mxx=6630.\n", + "Myy=6530.\n", + "Mzz=7030.\n", + "Mxy=-1030.\n", + "Mxz=200.\n", + "Myz=500.\n", + "\n", + "\n", + "#NAPA Earthquake BSL Solution\n", + "Mxx=-99951.\n", + "Myy=84789.\n", + "Mzz=15163.\n", + "Mxy=-50989.\n", + "Mxz=-15864.\n", + "Myz=32531.\n", + "\n", + "\n", + "\n", + "Mxx=-81152.791587\n", + "Myy=108463.432748\n", + "Mzz=39239.655126\n", + "Mxy=-53703.675745\n", + "Myz=56.308974\n", + "Mxz=-21246.865195\n", + "\"\"\"\n", + "moscale=1.0e13 #Units: Nm #scale value for moment estimates and Mw\n", + " #note if applied to tensors and passed to \n", + " #mopad the very large values leads to plotting errors can result\n", + "\n", + "nssplotflag=0 #1 - read/plot 0 - don't read/plot\n", + "#nssfilename='chiang_etal_2018_dprk_nss_mtfm.txt' #nss file for DPRK2017\n", + "nssfilename='napa_nss.txt' #nss file for Napa2014" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "Mfull=np.array([[Mxx,Mxy,Mxz],[Mxy,Myy,Myz],[Mxz,Myz,Mzz]]) #Construct Moment Tensor Matrix\n", + "L, V = la.eig(Mfull)\n", + "\n", + "if L[0]==L[1] and L[0]==L[2]:\n", + " print('Pure Isotropic') #deal with this perfect isotropic case\n", + " Mxx=Mxx+Mxx*0.0001\n", + " Mfull[0,]=Mfull[0,]+Mfull[0,]*0.0001\n", + "\n", + "Moiso=(Mxx+Myy+Mzz)/3 #Esimate the Scalar Moment\n", + "\n", + "Mdev=Mfull - np.identity(3)*Moiso #Compute the Deviatoric Moment Tensor\n", + "\n", + "w, v = la.eig(Mdev) #Calculate eigenvalues(w) and eigenvectors(v)\n", + "\n", + "Motot=(abs(Moiso) + max(abs(w)))*moscale #Compute Bower and Hudson Total Moment and the \n", + "Mw=(np.log10(Motot)-9.1)/1.5 #Moment Magnitude\n", + "\n", + "Moiso=Moiso*moscale #Now scale Moiso and Modev for plotting later\n", + "Modev=max(abs(w))*moscale #Modev is maximum deviatoric eigenvalue in absolute sense\n", + " #It is used to scale deviatoric tensor into DC and CLVD components\n", + "\n", + "\n", + "#Order the eigenvalues and eigenvectors\n", + "indx=np.argsort(abs(w)) #Sort by absolute value of w\n", + "m3=w[indx[2]] \n", + "m2=w[indx[1]]\n", + "m1=w[indx[0]]\n", + "eig3=v[:,indx[2]]\n", + "eig2=v[:,indx[1]]\n", + "eig1=v[:,indx[0]]\n", + "\n", + "#Order eigenvalues for Tape & Tape Lune\n", + "indx=np.argsort(L) #Sort retaining sign\n", + "l1=L[indx[2]]\n", + "l2=L[indx[1]]\n", + "l3=L[indx[0]]\n", + "\n", + "#Calculate Tape & Tape gamma and beta parameters testing for pure isotropic singularity\n", + "#These parameters, gamma, beta and delta are used later to plot the source-type in the Tape and Tape Lune perspective\n", + "if l1 == l2 and l1 == l3 and l1 > 0.:\n", + " gamma=0.\n", + " beta=0.\n", + " delta=90. - beta\n", + "elif l1 == l2 and l1 == l3 and l1 < 0.:\n", + " gamma=0.\n", + " beta=0.\n", + " delta=beta - 90.\n", + "else:\n", + " gamma=math.atan((-l1+2*l2-l3)/(np.sqrt(3)*(l1-l3)))*180/math.pi\n", + " beta=math.acos((l1+l2+l3)/(np.sqrt(3)*np.sqrt(L.dot(L))))*180/math.pi\n", + " delta=90. - beta\n", + " \n", + "#Construct Dyadics\n", + "#Dyadics represent fundamental vector-dipole tensors from which double-couples, CLVDs, tensile-cracks, etc. are constructed\n", + "#See Jost and Herrman for details\n", + "a3=np.array((eig3, eig3, eig3)).transpose()\n", + "a2=np.array((eig2, eig2, eig2)).transpose()\n", + "a1=np.array((eig1, eig1, eig1)).transpose()\n", + "a3a3=a3*a3.transpose()\n", + "a2a2=a2*a2.transpose()\n", + "a1a1=a1*a1.transpose()\n", + "\n", + "#Perform DC-CLVD Decomposition\n", + "F=-1*m1/m3\n", + "Mdc=m3*(1-2*F)*(a3a3-a2a2) #Double-Couple Moment Tensor\n", + "Mclvd=m3*F*(2*a3a3-a2a2-a1a1) #CLVD Moment Tensor\n", + "Modc=abs(m3*(1-2*F))*moscale #Double-Couple Moment\n", + "Moclvd=abs(2*m3*F)*moscale #CLVD Moment - to be consistent with Hudson decomp\n", + "kappa=Moiso/Motot #Hudson Plot kappa\n", + "T=(2*m1)/abs(m3) #Hudson Plot T\n", + "periso=abs(Moiso/Motot)\n", + "perdc=abs(Modc/Modev)\n", + "perclvd=abs(Moclvd/Modev)\n", + "\n", + "#Determine Strike, Rake, Dip\n", + "if Modc != 0.:\n", + " w, v = la.eig(Mdc)\n", + " indx=np.argsort(w) #Sort by absolute value of w\n", + " eig3=v[:,indx[2]]\n", + " eig2=v[:,indx[1]]\n", + " eig1=v[:,indx[0]]\n", + " nu1=(1/np.sqrt(2))*(eig3-eig1) #fault normal vector\n", + " u1=(1/np.sqrt(2))*(eig1+eig3) #slip vector\n", + " [strike1, rake1, dip1]=fpsol.fpsol(nu1,u1)\n", + " nu2=(1/np.sqrt(2))*(eig1+eig3) #conjugate fault normal vector\n", + " u2=(1/np.sqrt(2))*(eig3-eig1) #conjugate slip vector\n", + " [strike2, rake2, dip2]=fpsol.fpsol(nu2,u2)\n", + "\n", + "#Construct Moment Tensor arrays for plotting\n", + "fm=np.array((Mxx,Myy,Mzz,Mxy,Mxz,Myz))\n", + "devm=np.array((Mdev[0,0], Mdev[1,1], Mdev[2,2], Mdev[0,1], Mdev[0,2], Mdev[1,2]))\n", + "dcm=np.array((Mdc[0,0], Mdc[1,1], Mdc[2,2], Mdc[0,1], Mdc[0,2], Mdc[1,2]))\n", + "clvdm=np.array((Mclvd[0,0], Mclvd[1,1], Mclvd[2,2], Mclvd[0,1], Mclvd[0,2], Mclvd[1,2]))\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Solution Parameters:\n", + "\n", + "Mw=5.21\n", + " Total (Bowers) Moment=8.30e+16 Nm\n", + " Isotropic Moment=0.00e+00Nm\n", + " Deviatoric Moment=8.30e+16Nm\n", + "\n", + "Percentage Decomposition (Deviatoric Part)\n", + " Isotropic=0.0\n", + " Double-Couple=98.3(98.3)\n", + " CLVD=1.7 (1.7)\n", + "\n", + "DC Moment=8.159e+16\n", + "Strike Rake Dip\n", + "131.5 -162.5 60.2 degrees\n", + "32.6 -31.0 74.9 degrees\n" + ] + } + ], + "source": [ + "#Make Plots and write solution parameters\n", + "\n", + "#Scaled by total moment\n", + "# First one plots the full mt\n", + "# The conditional statements were added because mopad returns an error for identially zero-valued moment tensors\n", + "# i.e. pure double-couple solution would have a zero-valued clvd moment tensor\n", + "fig=plt.figure(figsize=(8,8))\n", + "threshold=0.; #initialize threshold\n", + "if Moiso != 0.0:\n", + " beach1 = beach(fm,xy=(0.5,0.5),width=0.95,mopad_basis='NED',show_iso=True)\n", + " ax2 = fig.add_subplot(2,2,1)\n", + " ax2.add_collection(beach1)\n", + " ax2.set_aspect(\"equal\")\n", + " ax2.set_axis_off()\n", + " buf=\"Full MT {0:.2e}\".format(Motot)\n", + " ax2.set(title=buf)\n", + " threshold=Moiso*0.00001 #Set Modev threshold to a small value of Mosio if there is a Moiso\n", + "\n", + "# Second one plots deviatoric mt\n", + "if Modev != 0.0 and Modev/Motot > 0.001: #plot only significant deviatoric parts\n", + " beach1 = beach(devm,xy=(0.5,0.5),width=0.95*Modev/Motot,mopad_basis='NED')\n", + " ax3 = fig.add_subplot(2,2,2)\n", + " ax3.add_collection(beach1)\n", + " ax3.set_aspect(\"equal\")\n", + " ax3.set_axis_off()\n", + " buf=\"Dev MT {0:.2e}\".format(Modev)\n", + " ax3.set(title=buf)\n", + "\n", + "# Third one plots dc\n", + "if Modc != 0.0 and Modc/Motot > 0.001: #plot only significant double-couple parts\n", + " beach1 = beach(dcm,xy=(0.5,0.5),width=0.95*Modc/Modev,mopad_basis='NED')\n", + " ax3 = fig.add_subplot(2,2,3)\n", + " ax3.add_collection(beach1)\n", + " ax3.set_aspect(\"equal\")\n", + " ax3.set_axis_off()\n", + " buf=\"DC MT {0:.2e}\".format(Modc)\n", + " ax3.set(title=buf)\n", + "\n", + "# Forth one plots dc\n", + "if Moclvd != 0.0 and Moclvd/Motot > 0.001: #plot only signicant clvd parts\n", + " beach1 = beach(clvdm,xy=(0.5,0.5),width=0.95*Moclvd/Modev,mopad_basis='NED')\n", + " ax3 = fig.add_subplot(2,2,4)\n", + " ax3.add_collection(beach1)\n", + " ax3.set_aspect(\"equal\")\n", + " ax3.set_axis_off()\n", + " buf=\"CLVD MT {0:.2e}\".format(Moclvd)\n", + " ax3.set(title=buf)\n", + "\n", + "#f.savefig(\"mtdecomp_mt_plt.png\")\n", + "fig.savefig(\"mtdecomp_mt_plt.png\")\n", + "plt.show()\n", + "\n", + "#Write the solution parameters\n", + "print(f'Solution Parameters:\\n')\n", + "print(f'Mw={Mw:.2f}\\n Total (Bowers) Moment={Motot:.2e} Nm\\n Isotropic Moment={Moiso:.2e}Nm\\n Deviatoric Moment={Modev:.2e}Nm\\n')\n", + "print(f'Percentage Decomposition (Deviatoric Part)\\n Isotropic={periso*100:.1f}\\n Double-Couple={perdc*(1-periso)*100:.1f}({perdc*100:.1f})\\n CLVD={perclvd*(1-periso)*100:.1f} ({perclvd*100:.1f})\\n')\n", + "if Modc != 0.:\n", + " print(f'DC Moment={Modc:.3e}')\n", + " print(f'Strike Rake Dip')\n", + " print(f'{strike1:3.1f} {rake1:3.1f} {dip1:3.1f} degrees')\n", + " print(f'{strike2:3.1f} {rake2:3.1f} {dip2:3.1f} degrees')" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "#Compute Tape and Tape parameters for plotting the NSS\n", + "#Read the NSS output for plotting solution space on Tape and Tape Lune\n", + "if (nssplotflag == 1):\n", + " data=pd.read_csv(nssfilename, sep='\\s+', header=None) \n", + "\n", + " d=np.array(data)\n", + " lam=np.array((d[:,0],d[:,1],d[:,2])).transpose() #eigenvalues are column ordered each row is a individual tuple\n", + " lam.sort(axis=1) #sort eigenvalue rows lam1=d[:,2], lam2=d[:,1], lam3=d[:,0]\n", + " vr=d[:,3]\n", + "\n", + " l1=lam[:,2]\n", + " l2=lam[:,1]\n", + " l3=lam[:,0]\n", + " L=np.sqrt(l1**2 + l2**2 + l3**2)\n", + "\n", + "#Test for pure isotropic singularity and compute gamma, beta and delta\n", + " n=len(l1)\n", + " GAMMA=np.zeros(n)\n", + " BETA=np.zeros(n)\n", + " DELTA=np.zeros(n)\n", + " for i in range(0,n,1): \n", + " if l1[i] == l2[i] and l1[i] == l3[i] and l1[i] > 0.:\n", + " GAMMA[i]=0.\n", + " BETA[i]=0.\n", + " DELTA[i]=90. - BETA[i]\n", + " elif l1[i] == l2[i] and l1[i] == l3[i] and l1[i] < 0.:\n", + " GAMMA[i]=0.\n", + " BETA[i]=0.\n", + " DELTA[i]=BETA[i] - 90.\n", + " else:\n", + " GAMMA[i]=np.arctan((-l1[i]+2*l2[i]-l3[i])/(np.sqrt(3)*(l1[i]-l3[i])))*180/np.pi\n", + " BETA[i]=np.arccos((l1[i]+l2[i]+l3[i])/(np.sqrt(3)*L[i]))*180/np.pi\n", + " DELTA[i]=90. - BETA[i]\n", + " " + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAY0AAAM+CAYAAADmZl0jAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjQuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/MnkTPAAAACXBIWXMAAAsTAAALEwEAmpwYAADuSUlEQVR4nOydd5hkVZn/v++tHLs6VXWYmZ6cEzMwMEMYkihRdBGRoLiCkV0VVlbUXVl13Z95XXVd17AoKLsSzKAiMMAgGQGZgcmxw3Tuqq6uXOf3R/UtqnOFe86tuvd8nqee7q6uOudWd9X5njceYoxBIpFIJJJiUPS+AIlEIpHUDlI0JBKJRFI0UjQkEolEUjRSNCQSiURSNFI0JBKJRFI0UjQkEolEUjRSNCSSKoeI7iCiL1Q4xn8R0T9pdU0S82LV+wIkEiNBRIcBhABkCu6+gzF2kz5XlIMx9kE955cYBykaEkkBRLQMQGCGXycBvMLmroi9lDH2J00vTCKpEqRoSCTjNDc0fOwUt/srGy0WgsUyRRiOpFLotNkeIKK3M8Yy040xE0T0XQDNjLErxn/+EoCTAZwPYDuAuwD8J4CbAYwC+DRj7KczjHUjgH8E0ABgJ4APMsa6iIgAfB3ANQAcAI4AuJox9ioR3QHgOGPsM7ONMf47BuBDAG4B0ATgZwBuKkIsJSZAiobE9BARNTc3f+WU00573/0f+IDVefXVQDwOpFJvPMhmA7Na8ak3v/lNfY8++jsieitjLFHCNLcAeImIrgdwAMD7AGxkjLHcWo8W5BbodgCnAXiAiJ5njO2ZdK3nAvg3ABcA2AXgqwD+F8BZ4/edBWA5gBEAKwEMT/N6ZxtD5RIApwDwA3gBwG8A/L6E1ysxKowxeZM3094AWJqbm392/fXXj6TTacYYY2z/fsY+8hHG/H7GFCX39SMfyd3PGPv3f//3WHNz83MA/NOMdxg5S2G44Hbj+O+2ABhEzgJ4V8FzzgaQBuApuO/nAP5p/Ps7AHxh/PsfAvhyweO8AFIAFgI4F8Be5ERHmXRdRY0x/jMDcMaka/mk3v8reauOm8yekpgWInIEg8EHb7zxxkt/9KMf+S0WS+4XS5YA3/42MDICZDK5r9/+du5+AB/96Eed3/rWtzY0Nzc/S0TN0wx9OWMsUHD7PgAwxp4FcBAAIbcQFzLEGIsW/HwEQNs0Y7eN/w7jY44CGADQzhh7BMC3AXwHwAki+m8i8pcyRsFjegq+H0NOWCQSKRoSc0JEnubm5h233nrrGTfddJM3nU6DseJc9slkEm9605ts3//+95cFg8FniWhekXN+BLlYQxeAWyf9up6IPAU/Lxh/3GS6AHQUvg4AjQA6AYAx9h+Msc0A1iDnpvpEqWNIJLMhRUNiOogo0Nzc/OcvfelLm7Zv3+5KJpN46aWXMDAwgGeeeQbJZBK7d+8GABw+fBhHjhzB8PAwnnvuOYyOjuKpp55CNptFfX29cs8993SEQqGniWjpHHMuB/AFANcCuA7ArUS0cdLD/oWI7ER0JnIxhXumGepnAN5LRBuJyAHgiwCeYYwdJqJTiOhUIrIBiAKIY2Lq75xjFPUHlJgaKRoSU5GLeTc/81//9V+rzzjjDLvdbofL5QIAWCwWWCwWRCIRpMaD4IcPHwZjDIODg6irq8PAwABaWlowPDyMjo4OuN1u+v3vf9/e1tb2BBGtHZ/mN0Q0WnD7BXLZUV9ijL3MGNsH4FMA7hxftIGcO2gIOSvgp8hlM70++foZYw8D+CcA9wHoBrAEwFXjv/YD+P74OEeQczl9tcQxJJJZoWJNcomk1iGilubm5j/feeedHRs3blQOHDiALVu2YOfOndiyZQv27duH1tZWjI6OwmKxoKOjAzt27MCGDRtw+PBhrFq1Cs899xy2bduGJ554Aqeeeir27dsHl8uFbDaL8847r7ezs/MtjLG/lHhdZwO4izFWlJtLItETaWlITAERzQsGg0///Oc/79i2bZvy2muvYfPmzTh06BDa2tpgt9sxODiI5uZmjIyMoK6uLv9cv9+PcDgMp9MJu92OaDSKVatWYffu3Vi7di36+voQCATw2GOPBefPn/8HIjpVx5cqkXBFiobE8BDRwmAw+NT999+/4PTTT1deeOEFnHTSSchms+js7MSSJUvQ1dWFtrY2EBHC4TB8Pl/++WpWVTabRUdHB44cOYJQKIR0Oo3BwUFs3rwZr732GoLBIHbu3Nm8cOHC31qt1jP0er0SCU+kaEgMDREtDIVCO3/729/O27ZtG7344otYvnw56urqsGvXLqxatQoWiwVHjx7FggULkMnk4sb59NtxfD4fwuEwgsEg+vv7kclksHbtWuzatQs2mw0nnXQSXnjhBbS2tuLJJ59s6ujo+IXVaj2zmGtkjO2QrilJrSBFQ2JYiGhRKBTa+bvf/a79lFNOwd69e+H1etHW1oaBgQFkMhmEQiFEIhFYrVa4XC5EIpEJVoZKXV0dRkZGoCgKWlpa0N3dDY/Hg2AwiMOHD6Ourg5Lly7FSy+9hNbWVjzxxBNNHR0d9zscju06vHSJhBtSNCSGhIgWtbS0PPG73/2uffPmzejt7UV/fz9WrVoFxhh27dqFNWvWAEDeygAwJZ6hoooGAMyfPx/Hjh0DACxbtgxHjhxBMpnEvHnz4HA4cPDgQbS1tWHnzp1N8+bNu08Kh8RISNGQGA4i6giFQnnBiMVi2LVrFzZv3gxFUXDs2DHU19fD6/WCMYbe3l6EQiEAs4tGOBwGAHi9XmQyGcRiMVitVixZsgR79+4FAKxduxbd3d0YGBhAa2srdu7c2Thv3rx7rVbr6eL+AhIJP6RoSAwFEc0LhUJP/PrXv27ftGkTstksXnjhBaxfvx5OpxPpdBoHDhzAihUrAAB9fX1obGzMxzDC4fC0omGz2ZBKpfJV4/Pnz8fx48fz3w8ODmJ0dBSKomDz5s145ZVXEI/H0draiscff7xp3rx5v5RZVRIjIEVDYhiIqDUYDO6877775m3ZsgUA8Oqrr6K1tRWNjY0AgP3796OjowN2ux1AzjU1f/58ALnmnalUCjabbdrx3W43xsbGAABtbW3o7OwEYwxEhDVr1mDXrl0AAJfLhTVr1uDFF19ENptFe3s7Hn/88aYFCxb8hog2cf0jSCSckaIhMQRE1NTc3Pzk//3f/80//fTTCQCOHz+ORCKBxYsXAwBisRh6enqwcOFCAEAqlcLo6CgCgUD+9263e8Y5Cl1UNpsNPp8Pw8PDAIDGxkYoioK+vj4AQDAYRGNjI15/PVfUvWDBAjz66KPN8+bN+z0RrdP8DyCRCEKKhqTmIaK65ubmnXfeeWfH2WefrQDA6Ogo9u/fj40bN2L8vArs3r0bq1atgqLk3vadnZ1ob2/P/36meIaK3+/PB8OBnFvq6NGj+Z9Xr16N1157Le/CWr58OUZGRtDb2wsAWLx4MR5++OHmtra2P46fECiR1BxSNCQ1zXi32se/+93vLnnzm9+sALkivL/85S/YsGFD3tU0MjKCRCKBYDCYf+7x48cxb94b5REjIyPw+6frJJ6jMIMKAJqbmzE4OIhsNgsA8Hg8aGhoyMc6iAibNm3Crl27EI/HAeSE5IEHHmgJhUKPEtECrf4OEokopGhIahYicjQ3Nz/y5S9/ecXf/M3f5E+h3L17N9ra2lBfX4/C+1avXp23KsbGxqAoSr5ZIZALgs8mGm63G7FYrHB+NDc348SJE/n7li9fjv379+eLBB0ORz6+oVogGzZswC9+8Yu2YDD4OBGFKv07SCQikaIhqUmIyNLc3PybT3/60+uuv/56tVMsenp6MDo6mo9jALkMKZvNlo9dAFOtDCAnJB6PBzNBRLBYLEin0/n7CrOoAMBut2PevHk4dOhQ/r5gMIhAIIB9+/bl79u6dSv97Gc/m9/c3Pw4Ec3sE5NIqgwpGpKag4goGAzeeeONN2796Ec/mjcVYrEYXnvtNZx00kl5i4Ixhtdeew2rVq3KP58xhq6uLrS2tubvS6fTUBQl/7yZUNuJqPj9fkSj0XwrdSAXuzh27NiE+1auXIne3l4MDg7m7zvvvPOU//zP/1zc3Nz8KBE5y/lbSCSikaIhqTmam5u/fumll178hS98IX8EKWMML774ItatWweHI294oKurC4FAYIIFEQ6H4fF4JqTWztQ+ZDJ+vx+RSCT/MxGhra0N3d3d+fssFgsWL148wbJQFAWbNm3Cyy+/jGQymb//iiuusH7uc59bHQwGHySivItNIqlWpGhIaorGxsZ/OO2009773//93/5Cq2Dfvn1oaGhAU1NT/r5sNot9+/blC/lUpnNNzRXPUJmcQQUA8+bNm+CiAnJuq76+vgkxELfbjRUrVuDll1+ecLTsBz/4QcdHPvKRU4LB4N00l6kjkeiMFA1JzeD3+69avnz5p+699946NW0WAIaHh3HixIkp4nDkyBG0tLRMsDwmtw1RKUU0Ci0NICcG2Wx2gkAoioLly5fn24uotLW1wWKxTBGZf/qnf/K89a1vvaC5ufkrc16ERKIjUjQkNYHVaj2rra3tO3/4wx/q1WpuAMhkMnjppZewceNGFApJJpPB4cOHsWTJkgnjDAwMoL6+fsJjgeJFw263I5lMYvKJl+3t7ejq6ppwX0tLC8LhMKLR6IT7161bhwMHDuSry4Gcm+u73/2uf/Pmze9rbGz8uzkvRCLRCSkakqqHiNa0tbXd+8gjjzRMXth37dqFjo6OKfGIw4cPY968eVNagqgFfZNJJpMTLJLZcDgcSCQSE+5rbW2dENcYv26sWLECe/bsmXC/zWbDunXr8Je//GWC+FgsFvziF78ILFq06HaPx/O2oi5GIhGMFA1JVUNEraFQ6A9/+MMfmtva2ib8rre3F2NjY/m2ICrpdBpHjx7FokWLJtyfzWYxODiY70OlkkwmUWi9zMV0Liqn0wlFUSZYD0CuADAWi015fGNjI+rr67F///4p4zz00EMNra2t3yeiU4q+KIlEEFI0JFULEXmbm5sf/d///d/WwpRZILfQ79q1a0KbEJWDBw+io6MDVuvEZKT+/n40NDRMcU0Vmzml4vP5pogAkItXTHZRERFWrlyZ70FVyMqVK9Hd3T0lsF5fX48//elPjW1tbb8mooVFX5hEIgApGpKqZLx474Gvfe1ri9R+UoW8/PLLWLlyJZzOieUNqVQKnZ2d6OjomDJmZ2fnlKwpAFPOBJ+LybUaKtOJBpCzKtLp9BRxUBQFJ510El566aV8BbnKwoUL8ctf/rIlGAw+QkSBoi9OIuGMFA1JVRIMBn/4gQ98YNN11103xW/U2dkJi8UyoThPZf/+/Vi8ePGUM76z2SyGh4fR0NAw5TmRSKSoILjKTJaG3W6HzWbD6OjolN/NZG34fD7MmzdvStwDAE455RR873vfm9/c3PwnIpq+X7tEIhgpGpKqo7Gx8eYzzzzz8s997nNTenokEgns3bsXa9eunfK8ZDKJEydO5M/HKKSvrw9NTU3TVnyX6p6y2WxIp9NTMqiA6bOogJzLiTGWb6VeyOLFizE4OIihoaEpv7v88sutt9xyy+pgMHiXrOGQVANSNCRVhdPpfEtHR8dnfvrTn9ZNt0a+8sorWLVq1bSB64MHD2LhwoVTYhZArjJ8ciBdpdRA+Ph1TsmgAnJptj09PdM+Z7pMKiAX99i4cSNeeeWVKW4qALj11ltdb3rTm97c1NT0qZIuUiLhgBQNSdVARKtaWlp+8oc//KF+uvRX1S3V0tIy5XepVArd3d1YsGBqt/HZXFPlCAYwt4tqcm0GkLM2stnslNgGkDt3fCY3FRHhf/7nf+oWL158i9frvbzki5VINESKhqQqIKLGUCj0hwceeKC5ubl5yu9nc0sBs1sZ/f39aGxs1MQ1pTKTaACY0ouqkJmsDWB2N5XNZsPvf//7+mAw+H0imv6PIJEIQIqGRHeIyBYMBn///e9/v2316tVTfs8Ym9UtlUql0NXVNa2VAQDd3d3TBs0BPqLR0tIyo2g0NDQgnU5Pm301l5uqvr4ev/vd75pCodCDRNQ45QESiQCkaEh0JxgMfv/v/u7vVl966aWW6X7f3d0NRVGmdUsBwKFDh9DR0TElYwrICc50BX0q5YqG1+udNksKyFWMK4oyoRdVIbNZG16vF+3t7VN6VqmsWrUKP/zhD1uDweAfZEaVRA+kaEh0paGh4SNbt269/NOf/rR7ut+nUins2bNnRrdUOp2esS4DmLnXlEokEoHX6532d7Oh9qCaiZlqNoBc3UYymZzRUlm8eDH6+vqmtUYA4OKLL7Z89KMfXRUMBn9Y8oVLJBUiRUOiG1ardWtra+vn7r777mkzpYBcb6mlS5fO2BfqyJEjmDdv3rRWBjC7awrIxUqK7Tk1mdmEo7W1dcYsKgBYtmzZlBYiKoqiYMOGDVNaqBdy2223ubdu3XppfX39B0u/comkfKRoSHSBiFqDweB9DzzwQEPhOd2F9Pf3IxaLTVvFDeSyoo4ePTql95QKYwz9/f2YLrAO5KwUq9U652l9MzGbi8rpdCKbzc4oKs3NzYhEIjO6sOrq6tDQ0DDh2NhCiAg/+9nPAqFQ6AtEtKWsFyCRlIEUDYlwiMje3Nz8h7vuuis0k1spk8ng1Vdfxfr162dc1I8fP46WlpYpnWxV1PYgM7mmRkdHy3JNqcwWDAdmr9kgIixdunRGawPIxT6OHj06o7C43W48+OCDjS0tLb8kotC0D5JINEaKhkQ4wWDwB7fccsvSc889d8b33969ezFv3rwJx7QWwhjDwYMHsXjx4hnn6e7unjF4DlQuGrNZGsDsWVRAzoU1MDAwbZEgAFitVqxevRqvvPLKjG6qRYsW4cc//nGoubn59zIwLhGBFA2JUAKBwHu3bNly6a233jq9Two5C6Gvr29OQWhsbJw1HnHixIkpJ/QVUm4QXGUu0fD5fIjFYkin09P+noiwaNGiGV1QABAMBmGz2WYMqgPABRdcoNx0003LgsHgt4u/eomkPKRoSIRBROubmpq+cvfddwdmcjkxxvDXv/4V69evn9GtxBjDgQMHppzKV8jY2Fi+OnsmRkdHy0q3VXG5XDO6jlSam5vR19c34+/nz5+P7u5upFKpGR+zdu1a7N27d9bHfOYzn/GsW7fuHV6v96q5r1wiKR8pGhIhEFFdMBj87W9/+9vG2Xb3x48fh8/nQyAQmPExAwMD8Hg8cLunzdIFMLdrCgCi0eisY8wFEYGIkM1mZ3zMdCf6FaIoChYsWICjR4/O+Bi73Y7FixfPWNuhjnPvvffWNzU1fYuIVsz4QImkQqRoSLhDRBQMBu//5je/2bJy5coZH5dKpbB//35MPnBpMvv375/VygCAnp6eWUVDjRHMZM0Ui8fjmbbPlEp9fT1GRkZmjEkAyIvGbOKzYMECDA8Pz1i7AQCBQAC//vWvm4LB4ANENH0wSCKpECkaEu40Njb+46WXXnryVVddNWug9rXXXsPSpUtndSlFIhEwxlBXVzfjY1KpFDKZDGZK5QWAWCw26++LZa64BhEhEAhM209KxWazIRgMzmqREBHWrVs3a1AcANavX4/Pf/7z7cFg8M7iXoFEUhpSNCRcIaJTW1paPvGd73xn1lOOhoeHEYlEZqzJUJkrlgHkzg4PBoOzPqbSzCkVj8czq2gAs6feqixevBgHDhyYVRDq6upQV1eHY8eOzTrWjTfe6DjzzDPPCQQCH5j1gRJJGUjRkHCDiBpaWlru+81vftMwW5aTGvxet27drIV28XgcIyMjMxbrqfT09MyaNQXkRGOmdN5S8Hq9s7qngLmD4UAuqO7xeDAwMDDr41auXIkDBw7M2sKEiPDjH/840NTU9K9EtG7WASWSEpGiIeHCeBzjvv/8z/8MLVq0aNbHHjlyBA0NDXMeuXro0CEsWrRoVmFhjCEcDs8aSAdyQXAtLI253FNArt7CbrdjbGxs1sctXboUBw4cmPUxNpsNy5Ytw2uvvTbr4zweD3796183BoPBX8n4hkRLpGhIuFBfX3/LpZdeuultb3ubdbbHpVIpHDp0CMuXL591vHQ6jZ6eHrS3t8/6uMHBQdTX18/ZGkQr95R69OtchEKhOV1UdXV1yGQys1aZA7kjZUdHR6c9zKmQ1atX4/Of/3xbKBS6Y84LlEiKRIqGRHOI6KRQKPTJb3/727ObDgBef/11LFmyZNbgN5BLxW1ra5uxMaFKMa4poLJGhZOxWq2z1lAAubjGiRMn5hxryZIlOHjw4KyPISKsWbMGr7766qwxECAX39iyZcv5dXV1755zcomkCKRoSDSFiLzBYPAXv/zlLxudTuesj41EIhgeHsb8+fNnfRxjDEeOHJmxMWEhszUoVMlkMvkaCy0oJhjudruRTCbntEqCwSCGhobmFKFAIACPxzNrxhWQE5i77rorEAgEvkZEs2cQSCRFIEVDoinBYPCOL37xi60rVsxeX8YYw6uvvoo1a9bMuXj39/fD7/fPaRnEYjHYbDZYrbN6xDA2NqZJEFylmGA4ADQ1NaG/v3/WxxARFixYgCNHjsw53qpVq7Bnz55pT/krxO/34957720KBoO/lv2pJJUiRUOiGV6v96pTTjnlvL/927+deibrJE6cOAGHw4GGhoY5x52rMWHhmMW4pqLRqKaiMVeBn0owGCzKRTV//nwcO3ZsTteTw+HAggULZu2Uq3LKKafgpptuWhgMBr8y54MlklmQoiHRBCKa39DQ8M277rprxr5SKplMBq+//jqmOw98MtFoFOl0etZiPpVi6jPUMfUQjcbGRgwODs4pBjabDU1NTXMGzoFcl9vu7u45e2ABwKc+9Sl3R0fHdVardfucD5ZIZkCKhqRiiMgSDAZ/fddddzXPleoK5CyHtrY2zBXzAN5Is52LbDZbdBqtVplTKsWKhqIoRcU/gJwYzBUQV8dcvXo1du/ePedjLRYL7r///obm5uafEVH9nE+QSKZBioakYhobGz9z3XXXLT3rrLPmjCwnEgkcP358zqpuIJdm29fXN2fjQSDXxLChoaGo4LbWlobVap0zrqASDAbR29s75+O8Xi8sFsucabXqmMlkctZWJSrz5s3DN7/5zWAoFPppURcskUxCioakIohoQzAY/LsvfvGLRW3d9+7di2XLls2ZOgsAx44dw7x584pqKtjb21tUPAMAkskk7PY5wy4lUUzaLZCr1ygmrgHkWoscPny4qMeuWbMGu3fvntP1BQBXXnml9fTTT9/q8/muK2pwiaQAKRqSsiEiZzAYvP++++5rLGYRHh0dxfDw8JwFekAuu+ro0aNYsGBBUdfS39+PpqamOR+XzWY1TbdVKdZF5XK5kEqliioIbG5uLir9FshlSHk8nqLiIADwox/9SE3DnT3fWSKZhBQNSdmEQqFvvec97+lwu90YHh6e00Wze/durFq1qqgFe3BwEF6vt6gCvHg8DqvVOmeqLZBLt63kDI2ZKFY0gDcC4nNBRJg3b96cDQpVVq5ciT179szaYp0xhkgkgkgkgs997nPNLS0tvyYiuQ5IimbuT5lEMg1Wq/WszZs3/82tt95qCYfDOHjw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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "#Plot Tape and Tape Lune\n", + "#Initial code from 'Ajean' https://stackoverflow.com/questions/32209496/matplotlib-basemap-fundamental-lune\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import cartopy.crs as ccrs\n", + "import matplotlib.path as mpath\n", + "from scipy.interpolate import griddata\n", + "\n", + "# Mollweide projection\n", + "fig = plt.figure(figsize=(15,15))\n", + "ax = fig.add_subplot(111, projection=ccrs.LambertAzimuthalEqualArea()) #This seems best\n", + "ax.set_extent([-30, 30, -90, 90])\n", + "xstep=30/5 #20% lines\n", + "ystep=90/5 #20% lines\n", + "xgrds=np.arange(-30.0, 31.0, xstep)\n", + "ygrds=np.arange(-90.0, 91.0, ystep)\n", + "ax.gridlines(xlocs=xgrds,ylocs=ygrds)\n", + "\n", + "# Here I define a matplotlib Path object to use as the boundary\n", + "outlinex = np.concatenate([[-30],np.tile(-30,180), np.tile(30,180),[-30]])\n", + "outliney = np.concatenate([[-90],np.arange(-90,90),np.arange(89,-91,-1),[-90]])\n", + "outlinecodes = np.array([mpath.Path.MOVETO]+[mpath.Path.LINETO]*360+[mpath.Path.MOVETO])\n", + "outlinepath = mpath.Path(np.column_stack([outlinex[::-1], outliney[::-1]]), outlinecodes[::-1])\n", + "ax.set_boundary(outlinepath, transform=ccrs.Geodetic())\n", + "\n", + "\n", + "\n", + "#Fundamental Source-Types\n", + "ax.plot(0, 90., 'ro', markersize=10, transform=ccrs.Geodetic()) #Explosion\n", + "ax.text(30,87,'Explosion',fontsize=12,transform=ccrs.Geodetic())\n", + "ax.plot(0, -90., 'ro', markersize=10, transform=ccrs.Geodetic()) #Implosion\n", + "ax.text(70,-88,'Implosion',fontsize=12,transform=ccrs.Geodetic())\n", + "ax.plot(0, 0, 'ro', markersize=10, transform=ccrs.Geodetic()) #Double-Couple\n", + "ax.text(0,2,'DC',fontsize=12,transform=ccrs.Geodetic())\n", + "ax.plot(30, 0, 'ro', markersize=10, transform=ccrs.Geodetic()) #Negative CLVD\n", + "ax.text(31,0,'-CLVD',fontsize=12,transform=ccrs.Geodetic())\n", + "ax.plot(-30, 0, 'ro', markersize=10, transform=ccrs.Geodetic()) #Positive CLVD\n", + "ax.text(-39,0,'+CLVD',fontsize=12,transform=ccrs.Geodetic())\n", + "LAM=np.array([3,1,1])\n", + "x=math.atan((-LAM[0]+2*LAM[1]-LAM[2])/(np.sqrt(3)*(LAM[0]-LAM[2])))*180/math.pi\n", + "y=math.acos((LAM[0]+LAM[1]+LAM[2])/(np.sqrt(3)*np.sqrt(LAM.dot(LAM))))*180/math.pi\n", + "y=90. - y\n", + "ax.plot(x, y, 'ro', markersize=10, transform=ccrs.Geodetic()) #Tensile Crack\n", + "ax.text(x-15,y-2,'+Crack',fontsize=12,transform=ccrs.Geodetic())\n", + "LAM=np.array([-1,-1, -3]) #note ordering is due to sign considered ordering\n", + "x=math.atan((-LAM[0]+2*LAM[1]-LAM[2])/(np.sqrt(3)*(LAM[0]-LAM[2])))*180/math.pi\n", + "y=math.acos((LAM[0]+LAM[1]+LAM[2])/(np.sqrt(3)*np.sqrt(LAM.dot(LAM))))*180/math.pi\n", + "y=90. - y\n", + "ax.plot(x, y, 'ro', markersize=10, transform=ccrs.Geodetic()) #Closing Crack\n", + "ax.text(x+3,y-1,'-Crack',fontsize=12,transform=ccrs.Geodetic())\n", + "LAM=np.array([1,0,0])\n", + "x=math.atan((-LAM[0]+2*LAM[1]-LAM[2])/(np.sqrt(3)*(LAM[0]-LAM[2])))*180/math.pi\n", + "y=math.acos((LAM[0]+LAM[1]+LAM[2])/(np.sqrt(3)*np.sqrt(LAM.dot(LAM))))*180/math.pi\n", + "y=90. - y\n", + "ax.plot(x, y, 'ro', markersize=10, transform=ccrs.Geodetic()) #LVD\n", + "ax.text(x-10,y-2,'+LVD',fontsize=12,transform=ccrs.Geodetic())\n", + "LAM=np.array([0,0,-1])\n", + "x=math.atan((-LAM[0]+2*LAM[1]-LAM[2])/(np.sqrt(3)*(LAM[0]-LAM[2])))*180/math.pi\n", + "y=math.acos((LAM[0]+LAM[1]+LAM[2])/(np.sqrt(3)*np.sqrt(LAM.dot(LAM))))*180/math.pi\n", + "y=90. - y\n", + "ax.plot(x, y, 'ro', markersize=10, transform=ccrs.Geodetic()) #LVD\n", + "ax.text(x+3,y-0,'-LVD',fontsize=12,transform=ccrs.Geodetic())\n", + "\n", + "\n", + "\n", + "# Plot Source-Type Solution Space\n", + "# Comment out if not available\n", + "if (nssplotflag == 1):\n", + " c = plt.cm.plasma(np.arange(0.,100.,10.)/100)\n", + " x=np.arange(-30.,31,5) #The third argument, the step controls smoothing\n", + " y=np.arange(-90,90,5)\n", + " X, Y= np.meshgrid(x, y)\n", + " idx=np.nonzero(vr >= 10.)\n", + " Z = griddata((GAMMA[idx],DELTA[idx]),vr[idx],(X,Y), method='cubic')\n", + " cb=ax.contourf(X, Y, Z, 20, transform=ccrs.PlateCarree(),cmap='Blues')\n", + "\n", + "#Plot MT Solution\n", + "ax.plot(gamma, delta, 'ks', markersize=14, transform=ccrs.Geodetic())\n", + "\n", + "# Add colorbar, make sure to specify tick locations to match desired ticklabels\n", + "#ax.set_title('Source-Type Lune')\n", + "if (nssplotflag == 1):\n", + " position=fig.add_axes([0.70,0.3,0.025,0.4]) ## the parameters are the specified position you set\n", + " cbar=plt.colorbar(cb, cax=position, orientation='vertical',ticks=[10, 20, 30, 40, 50, 60, 70, 80, 90, 100], spacing='uniform',shrink=0.5)\n", + " cbar.set_label('Variance Reduction (%)', rotation=90, size=14)\n", + "\n", + "fig.savefig(\"mtdecomp_lune_plt.png\")\n", + "fig.savefig(\"mtdecomp_lune_plt.png\")\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.7" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/BIN/mtmanip b/BIN/mtmanip new file mode 100755 index 0000000..a47adaf Binary files /dev/null and b/BIN/mtmanip differ diff --git a/BIN/plot_mtwaveform2.py b/BIN/plot_mtwaveform2.py new file mode 100644 index 0000000..e0e0c42 --- /dev/null +++ b/BIN/plot_mtwaveform2.py @@ -0,0 +1,706 @@ +# plot_mtwaveform.py +# Script to plot waveform fit +# Yuexin Li and Gabriel Rogow-Patt +# First version: April, 2019 +# Second version: 2021 + +import os +import glob +import numpy as np +import matplotlib.pyplot as plt +import matplotlib.transforms as transforms +#from obspy.imaging.beachball import beach,beachball +import math +import fpsol +import pandas as pd +#import sys +#sys.path.append('./') +from mopad_wrapper import beach +from numpy import linalg as la +import argparse +from matplotlib.backends.backend_pdf import PdfPages + + +parser = argparse.ArgumentParser() +parser.add_argument('--nodisplaywindow', action='store_true', help='prevents the plot(s) from opening any display window(s)') +parser.add_argument('--nosavefig', action='store_true', help='prevents the plot(s) from saving to any file(s)') +parser.add_argument('--noplotazimuth', action='store_true', help='prevents the station names from plotting on the beachball(s)') +parser.add_argument('--plotseparatebeachball', action='store_true', help='saves extra file(s) with the beachball diagram(s)') +parser.add_argument('--plottape', action='store_true', help='saves extra file(s) with the Tape Plot diagram(s)') +parser.add_argument("mtinv_file", action='extend', nargs='+') +args = parser.parse_args() + +# Python script to substitute "tdmt_plot_gmt5.perl mtinvout" + + +moscale=1 #Units: Nm #scale value for moment estimates and Mw + +# Plotting while reading +for mtinput in args.mtinv_file: + fig=plt.figure(figsize=(9,6)) + mark=0 + index=-1 + ncol=4 + DT=[];DR=[];DZ=[] #List for data + ST=[];SR=[];SZ=[] + filenameazlist=[] + f=open(mtinput,'r') + linelist = f.readlines() + if len(linelist[-1])!=0: + linelist.append('') + #for line in f.readlines(): + with PdfPages('fm_waveform(' + mtinput + ').pdf') as pdf: + for line in linelist: + line=line.strip() + if len(line)==0: + mark=0 + index=index+1 + if index>0: + index=index-1 + nstascale=nsta/4 + ax3 = fig.add_subplot(nsta,ncol,1+(index%8)*ncol) + trans = transforms.blended_transform_factory(ax3.transData, ax3.transAxes) + #plt.subplot(nsta,4,1+(index-1)*4) + plt.plot(DT,'k',linewidth=.75) + #plt.plot(ST,'r--',linewidth=.75, scaley=False) + plt.axis('off') + plt.text(-.1,.5,filename,fontsize=8,transform=ax3.transAxes,horizontalalignment='right') + if (index%8)==0: + plt.title('Tangential') + text_str='Distance = %d km Azimuth = %d Max Amp = %.2e cm Zcorr = %d VR = %d' % (dist, Az, np.max(np.abs([DT,DR,DZ])), Zcor, VR) + plt.text(0,-.15, text_str,fontsize=8,transform=trans) + + + ax6 = fig.add_subplot(nsta,ncol,2+(index%8)*ncol,sharey=ax3) + #plt.subplot(nsta,ncol,2+(index-1)*ncol) + plt.plot(DR,'k',linewidth=.75) + #plt.plot(SR,'r--',linewidth=.75, scaley=False) + plt.axis('off') + if (index%8)==0: + plt.title('Radial') + + + ax2 = fig.add_subplot(nsta,ncol,3+(index%8)*ncol,sharey=ax3) + #plt.subplot(nsta,ncol,3+(index-1)*ncol) + plt.plot(DZ,'k',linewidth=.75) + #plt.plot(SZ,'r--',linewidth=.75, scaley=False) + plt.axis('off') + if (index%8)==0: + plt.title('Vertical') + + trans2 = transforms.blended_transform_factory(ax2.transData, ax2.transAxes) + if DZ.index(min(DZ))2: + ax5 = fig.add_subplot(nsta,ncol,nsta*ncol) + if nsta>4: + ax5 = fig.add_subplot(4,ncol,4*ncol) + if nsta1==True: + ax5 = fig.add_subplot(nsta,ncol,nsta*ncol-2) + ax5.add_collection(beach1) + ax5.set_aspect("equal") + ax5.set_axis_off() + + #plots the filenames on the beachball + if args.noplotazimuth==False: + for filenameaz in filenameazlist: + Az = (90 - (float((filenameaz.split())[0]))) + if Az < 0: + Az+=360 + filename = (filenameaz.split())[1] + if Az>0 and Az<=90: + ax5.text((.5+0.475*math.cos(np.deg2rad(Az))), (.5+0.475*math.sin(np.deg2rad(Az))), filename, ha='left', va='bottom') + if Az>90 and Az<=180: + ax5.text((.5+0.475*math.cos(np.deg2rad(Az))), (.5+0.475*math.sin(np.deg2rad(Az))), filename, ha='right', va='bottom') + if Az>180 and Az<=270: + ax5.text((.5+0.475*math.cos(np.deg2rad(Az))), (.5+0.475*math.sin(np.deg2rad(Az))), filename, ha='right', va='top') + if Az>270 and Az<=360 or Az==0: + ax5.text((.5+0.475*math.cos(np.deg2rad(Az))), (.5+0.475*math.sin(np.deg2rad(Az))), filename, ha='left', va='top') + + #plots and saves the current plot and makes a new page if there's more than 8 stations + if index==truensta or index==8: + if args.nosavefig==False: + pdf.savefig() + if args.nodisplaywindow==False: + fig.canvas.manager.set_window_title(mtinput) + plt.show() + plt.close() + if truensta>index: + fig=plt.figure(figsize=(9,6)) + if (truensta-index)<8: + nsta=truensta-index + if nsta==1: + nsta=2 + nsta1=True + else: + nsta=8 + + + + + +# """ +# if index==nsta-1: +# # Plot beachball +# # In Obspy: M11, M22, M33, M12, M13, M23 +# ax2=plt.subplot(nsta,ncol,4+index*ncol) +# ###plt.axis('equal') +# mt=[mxx,myy,mzz,mxy,mxz,myz] +# beach=beach(mt, xy=(0.5, 0.5), width=0.6) +# ax2.add_collection(beach) +# """ + + #clear data list + DT=[];DR=[];DZ=[] + ST=[];SR=[];SZ=[] + continue + else: + if mark==1: + depth=float((line.split())[0]) + variance=float((line.split())[1]) + VRtot=float((line.split())[2]) + nsta=int((line.split())[3]) + npages=math.ceil(nsta/8) + truensta=nsta + if nsta>8: + nsta=8 + nsta1=False + if nsta==1: + nsta=2 + nsta1=True + mark=0 #reset + continue + elif mark==2: + mxx=float((line.split())[0]) + mxy=float((line.split())[1]) + mxz=float((line.split())[2]) + myy=float((line.split())[3]) + myz=float((line.split())[4]) + mzz=float((line.split())[5]) + #note if applied to tensors and passed to + #mopad the very large values leads to plotting errors can result + Mfull=np.array([[mxx,mxy,mxz],[mxy,myy,myz],[mxz,myz,mzz]]) #Construct Moment Tensor Matrix + L, V = la.eig(Mfull) + + if L[0]==L[1] and L[0]==L[2]: + print('Pure Isotropic') #deal with this perfect isotropic case + mxx=mxx+mxx*0.0001 + Mfull[0,]=Mfull[0,]+Mfull[0,]*0.0001 + + Moiso=(mxx+myy+mzz)/3 #Esimate the Scalar Moment + Mdev=Mfull - np.identity(3)*Moiso #Compute the Deviatoric Moment Tensor + + w, v = la.eig(Mdev) #Calculate eigenvalues(w) and eigenvectors(v) + + Motot=(abs(Moiso) + max(abs(w)))*moscale #Compute Bower and Hudson Total Moment and the + Mw=(np.log10(Motot)-16.1)/1.5 #Moment Magnitude + + Moiso=Moiso*moscale #Now scale Moiso and Modev for plotting later + Modev=max(abs(w))*moscale #Modev is maximum deviatoric eigenvalue in absolute sense #It is used to scale deviatoric tensor into DC and CLVD components + + + #Order the eigenvalues and eigenvectors + indx=np.argsort(abs(w)) #Sort by absolute value of w + m3=w[indx[2]] + m2=w[indx[1]] + m1=w[indx[0]] + eig3=v[:,indx[2]] + eig2=v[:,indx[1]] + eig1=v[:,indx[0]] + + #Order eigenvalues for Tape & Tape Lune + indx=np.argsort(L) #Sort retaining sign + l1=L[indx[2]] + l2=L[indx[1]] + l3=L[indx[0]] + + #Calculate Tape & Tape gamma and beta parameters testing for pure isotropic singularity + #These parameters, gamma, beta and delta are used later to plot the source-type in the Tape and Tape Lune perspective + if l1 == l2 and l1 == l3 and l1 > 0.: + gamma=0. + beta=0. + delta=90. - beta + elif l1 == l2 and l1 == l3 and l1 < 0.: + gamma=0. + beta=0. + delta=beta - 90. + else: + gamma=math.atan((-l1+2*l2-l3)/(np.sqrt(3)*(l1-l3)))*180/math.pi + beta=math.acos((l1+l2+l3)/(np.sqrt(3)*np.sqrt(L.dot(L))))*180/math.pi + delta=90. - beta + + #Construct Dyadics + #Dyadics represent fundamental vector-dipole tensors from which double-couples, CLVDs, tensile-cracks, etc. are constructed + #See Jost and Herrman for details + a3=np.array((eig3, eig3, eig3)).transpose() + a2=np.array((eig2, eig2, eig2)).transpose() + a1=np.array((eig1, eig1, eig1)).transpose() + a3a3=a3*a3.transpose() + a2a2=a2*a2.transpose() + a1a1=a1*a1.transpose() + + #Perform DC-CLVD Decomposition + F=-1*m1/m3 + Mdc=m3*(1-2*F)*(a3a3-a2a2) #Double-Couple Moment Tensor + Mclvd=m3*F*(2*a3a3-a2a2-a1a1) #CLVD Moment Tensor + Modc=abs(m3*(1-2*F))*moscale #Double-Couple Moment + Moclvd=abs(2*m3*F)*moscale #CLVD Moment - to be consistent with Hudson decomp + kappa=Moiso/Motot #Hudson Plot kappa + T=(2*m1)/abs(m3) #Hudson Plot T + periso=abs(Moiso/Motot) + perdc=abs(Modc/Modev) + perclvd=abs(Moclvd/Modev) + + #Determine Strike, Rake, Dip + if Modc != 0.: + w, v = la.eig(Mdc) + indx=np.argsort(w) #Sort by absolute value of w + eig3=v[:,indx[2]] + eig2=v[:,indx[1]] + eig1=v[:,indx[0]] + nu1=(1/np.sqrt(2))*(eig3-eig1) #fault normal vector + u1=(1/np.sqrt(2))*(eig1+eig3) #slip vector + [strike1, rake1, dip1]=fpsol.fpsol(nu1,u1) + nu2=(1/np.sqrt(2))*(eig1+eig3) #conjugate fault normal vector + u2=(1/np.sqrt(2))*(eig3-eig1) #conjugate slip vector + [strike2, rake2, dip2]=fpsol.fpsol(nu2,u2) + + mark=0 + continue + elif mark==3: + st1=float((line.split())[0]) + rk1=float((line.split())[1]) + dp1=float((line.split())[2]) + st2=float((line.split())[3]) + rk2=float((line.split())[4]) + dp2=float((line.split())[5]) + mark=0 + continue + elif mark==4: + pdc=float((line.split())[0]) + pclvd=float((line.split())[1]) + piso=float((line.split())[2]) + mark=0 #reset + continue + elif mark==5: + filename=line + #strips anything after the first '_' or '.' in the filename + terminator = filename.find('_') + if terminator==-1: + terminator = filename.find('.') + if terminator!=-1: + filename = filename[:terminator] + mark=0 + continue + elif mark==6: + dt=float((line.split())[0]) + npts=int((line.split())[1]) + dist=float((line.split())[2]) + Az=float((line.split())[3]) + filenameazlist.append(str(Az) + " " + filename) + Zcor=int((line.split())[4]) + VR=float((line.split())[5]) + mark=0 + continue + elif mark==7: + DT.append(float((line.split())[0])) + DR.append(float((line.split())[1])) + DZ.append(float((line.split())[2])) + ST.append(float((line.split())[3])) + SR.append(float((line.split())[4])) + SZ.append(float((line.split())[5])) + + # Locate yourself... + if line.startswith('#depth'): + mark=1; + elif line.startswith('#mxx'): + mark=2 + elif line.startswith('#st1'): + mark=3 + elif line.startswith('#pdc'): + mark=4 + elif line.startswith('#filename'): + mark=5 + elif line.startswith('#dt'): + mark=6 + elif line.startswith('#data'): + mark=7 + + f.close() + + + if args.plotseparatebeachball==True: + fig=plt.figure(figsize=(30,30)) + mt=np.array((mxx,myy,mzz,mxy,mxz,myz)) + #ax2=beachball(mt,facecolor='k',outfile='fm_beachball.pdf') + beach1 = beach(mt,xy=(0.5,0.5),width=0.95,mopad_basis='NED',show_iso=True,facecolor='black') + ax2 = fig.add_subplot(1,1,1) + ax2.add_collection(beach1) + ax2.set_aspect("equal") + ax2.set_axis_off() + if args.nosavefig==False: + if args.noplotazimuth==False: + for filenameaz in filenameazlist: + Az = 90 - float((filenameaz.split())[0]) + if Az < 0: + Az+=360 + filename = (filenameaz.split())[1] + if Az>0 and Az<=90: + ax2.text((.5+0.475*math.cos(np.deg2rad(Az))), (.5+0.475*math.sin(np.deg2rad(Az))), filename, ha='left', va='bottom', fontsize=70) + if Az>90 and Az<=180: + ax2.text((.5+0.475*math.cos(np.deg2rad(Az))), (.5+0.475*math.sin(np.deg2rad(Az))), filename, ha='right', va='bottom', fontsize=70) + if Az>180 and Az<=270: + ax2.text((.5+0.475*math.cos(np.deg2rad(Az))), (.5+0.475*math.sin(np.deg2rad(Az))), filename, ha='right', va='top', fontsize=70) + if Az>270 and Az<=360 or Az==0: + ax2.text((.5+0.475*math.cos(np.deg2rad(Az))), (.5+0.475*math.sin(np.deg2rad(Az))), filename, ha='left', va='top', fontsize=70) + fig.savefig('fm_beachball(' + mtinput + ').pdf') + plt.close() + + + + if args.plottape==True: + nssfilename='' + nssplotflag=0 + + Mfull=np.array([[mxx,mxy,mxz],[mxy,myy,myz],[mxz,myz,mzz]]) #Construct Moment Tensor Matrix + L, V = la.eig(Mfull) + + if L[0]==L[1] and L[0]==L[2]: + print('Pure Isotropic') #deal with this perfect isotropic case + mxx=mxx+mxx*0.0001 + Mfull[0,]=Mfull[0,]+Mfull[0,]*0.0001 + + Moiso=(mxx+myy+mzz)/3 #Esimate the Scalar Moment + + Mdev=Mfull - np.identity(3)*Moiso #Compute the Deviatoric Moment Tensor + + w, v = la.eig(Mdev) #Calculate eigenvalues(w) and eigenvectors(v) + + Motot=(abs(Moiso) + max(abs(w)))*moscale #Compute Bower and Hudson Total Moment and the + Mw=(np.log10(Motot)-9.1)/1.5 #Moment Magnitude + + Moiso=Moiso*moscale #Now scale Moiso and Modev for plotting later + Modev=max(abs(w))*moscale #Modev is maximum deviatoric eigenvalue in absolute sense + #It is used to scale deviatoric tensor into DC and CLVD components + + + #Order the eigenvalues and eigenvectors + indx=np.argsort(abs(w)) #Sort by absolute value of w + m3=w[indx[2]] + m2=w[indx[1]] + m1=w[indx[0]] + eig3=v[:,indx[2]] + eig2=v[:,indx[1]] + eig1=v[:,indx[0]] + + #Order eigenvalues for Tape & Tape Lune + indx=np.argsort(L) #Sort retaining sign + l1=L[indx[2]] + l2=L[indx[1]] + l3=L[indx[0]] + + #Calculate Tape & Tape gamma and beta parameters testing for pure isotropic singularity + #These parameters, gamma, beta and delta are used later to plot the source-type in the Tape and Tape Lune perspective + if l1 == l2 and l1 == l3 and l1 > 0.: + gamma=0. + beta=0. + delta=90. - beta + elif l1 == l2 and l1 == l3 and l1 < 0.: + gamma=0. + beta=0. + delta=beta - 90. + else: + gamma=math.atan((-l1+2*l2-l3)/(np.sqrt(3)*(l1-l3)))*180/math.pi + beta=math.acos((l1+l2+l3)/(np.sqrt(3)*np.sqrt(L.dot(L))))*180/math.pi + delta=90. - beta + + #Construct Dyadics + #Dyadics represent fundamental vector-dipole tensors from which double-couples, CLVDs, tensile-cracks, etc. are constructed + #See Jost and Herrman for details + a3=np.array((eig3, eig3, eig3)).transpose() + a2=np.array((eig2, eig2, eig2)).transpose() + a1=np.array((eig1, eig1, eig1)).transpose() + a3a3=a3*a3.transpose() + a2a2=a2*a2.transpose() + a1a1=a1*a1.transpose() + + #Perform DC-CLVD Decomposition + F=-1*m1/m3 + Mdc=m3*(1-2*F)*(a3a3-a2a2) #Double-Couple Moment Tensor + Mclvd=m3*F*(2*a3a3-a2a2-a1a1) #CLVD Moment Tensor + Modc=abs(m3*(1-2*F))*moscale #Double-Couple Moment + Moclvd=abs(2*m3*F)*moscale #CLVD Moment - to be consistent with Hudson decomp + kappa=Moiso/Motot #Hudson Plot kappa + T=(2*m1)/abs(m3) #Hudson Plot T + periso=abs(Moiso/Motot) + perdc=abs(Modc/Modev) + perclvd=abs(Moclvd/Modev) + + #Determine Strike, Rake, Dip + if Modc != 0.: + w, v = la.eig(Mdc) + indx=np.argsort(w) #Sort by absolute value of w + eig3=v[:,indx[2]] + eig2=v[:,indx[1]] + eig1=v[:,indx[0]] + nu1=(1/np.sqrt(2))*(eig3-eig1) #fault normal vector + u1=(1/np.sqrt(2))*(eig1+eig3) #slip vector + [strike1, rake1, dip1]=fpsol.fpsol(nu1,u1) + nu2=(1/np.sqrt(2))*(eig1+eig3) #conjugate fault normal vector + u2=(1/np.sqrt(2))*(eig3-eig1) #conjugate slip vector + [strike2, rake2, dip2]=fpsol.fpsol(nu2,u2) + + #Construct Moment Tensor arrays for plotting + fm=np.array((mxx,myy,mzz,mxy,mxz,myz)) + devm=np.array((Mdev[0,0], Mdev[1,1], Mdev[2,2], Mdev[0,1], Mdev[0,2], Mdev[1,2])) + dcm=np.array((Mdc[0,0], Mdc[1,1], Mdc[2,2], Mdc[0,1], Mdc[0,2], Mdc[1,2])) + clvdm=np.array((Mclvd[0,0], Mclvd[1,1], Mclvd[2,2], Mclvd[0,1], Mclvd[0,2], Mclvd[1,2])) + + #Compute Tape and Tape parameters for plotting the NSS + #Read the NSS output for plotting solution space on Tape and Tape Lune + if (nssplotflag == 1): + data=pd.read_csv(nssfilename, sep='\s+', header=None) + + d=np.array(data) + lam=np.array((d[:,0],d[:,1],d[:,2])).transpose() #eigenvalues are column ordered each row is a individual tuple + lam.sort(axis=1) #sort eigenvalue rows lam1=d[:,2], lam2=d[:,1], lam3=d[:,0] + vr=d[:,3] + + l1=lam[:,2] + l2=lam[:,1] + l3=lam[:,0] + L=np.sqrt(l1**2 + l2**2 + l3**2) + + #Test for pure isotropic singularity and compute gamma, beta and delta + n=len(l1) + GAMMA=np.zeros(n) + BETA=np.zeros(n) + DELTA=np.zeros(n) + for i in range(0,n,1): + if l1[i] == l2[i] and l1[i] == l3[i] and l1[i] > 0.: + GAMMA[i]=0. + BETA[i]=0. + DELTA[i]=90. - BETA[i] + elif l1[i] == l2[i] and l1[i] == l3[i] and l1[i] < 0.: + GAMMA[i]=0. + BETA[i]=0. + DELTA[i]=BETA[i] - 90. + else: + GAMMA[i]=np.arctan((-l1[i]+2*l2[i]-l3[i])/(np.sqrt(3)*(l1[i]-l3[i])))*180/np.pi + BETA[i]=np.arccos((l1[i]+l2[i]+l3[i])/(np.sqrt(3)*L[i]))*180/np.pi + DELTA[i]=90. - BETA[i] + #Plot Tape and Tape Lune + #Initial code from 'Ajean' https://stackoverflow.com/questions/32209496/matplotlib-basemap-fundamental-lune + + import cartopy.crs as ccrs + import matplotlib.path as mpath + from scipy.interpolate import griddata + + # Mollweide projection + fig = plt.figure(figsize=(15,15)) + ax = fig.add_subplot(111, projection=ccrs.LambertAzimuthalEqualArea()) #This seems best + ax.set_extent([-30, 30, -90, 90]) + xstep=30/5 #20% lines + ystep=90/5 #20% lines + xgrds=np.arange(-30.0, 31.0, xstep) + ygrds=np.arange(-90.0, 91.0, ystep) + ax.gridlines(xlocs=xgrds,ylocs=ygrds) + + # Here I define a matplotlib Path object to use as the boundary + outlinex = np.concatenate([[-30],np.tile(-30,180), np.tile(30,180),[-30]]) + outliney = np.concatenate([[-90],np.arange(-90,90),np.arange(89,-91,-1),[-90]]) + outlinecodes = np.array([mpath.Path.MOVETO]+[mpath.Path.LINETO]*360+[mpath.Path.MOVETO]) + outlinepath = mpath.Path(np.column_stack([outlinex[::-1], outliney[::-1]]), outlinecodes[::-1]) + ax.set_boundary(outlinepath, transform=ccrs.Geodetic()) + + + + #Fundamental Source-Types + ax.plot(0, 90., 'ro', markersize=10, transform=ccrs.Geodetic()) #Explosion + ax.text(30,87,'Explosion',fontsize=12,transform=ccrs.Geodetic()) + ax.plot(0, -90., 'ro', markersize=10, transform=ccrs.Geodetic()) #Implosion + ax.text(70,-88,'Implosion',fontsize=12,transform=ccrs.Geodetic()) + ax.plot(0, 0, 'ro', markersize=10, transform=ccrs.Geodetic()) #Double-Couple + ax.text(0,2,'DC',fontsize=12,transform=ccrs.Geodetic()) + ax.plot(30, 0, 'ro', markersize=10, transform=ccrs.Geodetic()) #Negative CLVD + ax.text(31,0,'-CLVD',fontsize=12,transform=ccrs.Geodetic()) + ax.plot(-30, 0, 'ro', markersize=10, transform=ccrs.Geodetic()) #Positive CLVD + ax.text(-39,0,'+CLVD',fontsize=12,transform=ccrs.Geodetic()) + LAM=np.array([3,1,1]) + x=math.atan((-LAM[0]+2*LAM[1]-LAM[2])/(np.sqrt(3)*(LAM[0]-LAM[2])))*180/math.pi + y=math.acos((LAM[0]+LAM[1]+LAM[2])/(np.sqrt(3)*np.sqrt(LAM.dot(LAM))))*180/math.pi + y=90. - y + ax.plot(x, y, 'ro', markersize=10, transform=ccrs.Geodetic()) #Tensile Crack + ax.text(x-15,y-2,'+Crack',fontsize=12,transform=ccrs.Geodetic()) + LAM=np.array([-1,-1, -3]) #note ordering is due to sign considered ordering + x=math.atan((-LAM[0]+2*LAM[1]-LAM[2])/(np.sqrt(3)*(LAM[0]-LAM[2])))*180/math.pi + y=math.acos((LAM[0]+LAM[1]+LAM[2])/(np.sqrt(3)*np.sqrt(LAM.dot(LAM))))*180/math.pi + y=90. - y + ax.plot(x, y, 'ro', markersize=10, transform=ccrs.Geodetic()) #Closing Crack + ax.text(x+3,y-1,'-Crack',fontsize=12,transform=ccrs.Geodetic()) + LAM=np.array([1,0,0]) + x=math.atan((-LAM[0]+2*LAM[1]-LAM[2])/(np.sqrt(3)*(LAM[0]-LAM[2])))*180/math.pi + y=math.acos((LAM[0]+LAM[1]+LAM[2])/(np.sqrt(3)*np.sqrt(LAM.dot(LAM))))*180/math.pi + y=90. - y + ax.plot(x, y, 'ro', markersize=10, transform=ccrs.Geodetic()) #LVD + ax.text(x-10,y-2,'+LVD',fontsize=12,transform=ccrs.Geodetic()) + LAM=np.array([0,0,-1]) + x=math.atan((-LAM[0]+2*LAM[1]-LAM[2])/(np.sqrt(3)*(LAM[0]-LAM[2])))*180/math.pi + y=math.acos((LAM[0]+LAM[1]+LAM[2])/(np.sqrt(3)*np.sqrt(LAM.dot(LAM))))*180/math.pi + y=90. - y + ax.plot(x, y, 'ro', markersize=10, transform=ccrs.Geodetic()) #LVD + ax.text(x+3,y-0,'-LVD',fontsize=12,transform=ccrs.Geodetic()) + + + + # Plot Source-Type Solution Space + # Comment out if not available + if (nssplotflag == 1): + c = plt.cm.plasma(np.arange(0.,100.,10.)/100) + x=np.arange(-30.,31,5) #The third argument, the step controls smoothing + y=np.arange(-90,90,5) + X, Y= np.meshgrid(x, y) + idx=np.nonzero(vr >= 10.) + Z = griddata((GAMMA[idx],DELTA[idx]),vr[idx],(X,Y), method='cubic') + cb=ax.contourf(X, Y, Z, 20, transform=ccrs.PlateCarree(),cmap='Blues') + + #Plot MT Solution + ax.plot(gamma, delta, 'ks', markersize=14, transform=ccrs.Geodetic()) + + # Add colorbar, make sure to specify tick locations to match desired ticklabels + #ax.set_title('Source-Type Lune') + if (nssplotflag == 1): + position=fig.add_axes([0.70,0.3,0.025,0.4]) ## the parameters are the specified position you set + cbar=plt.colorbar(cb, cax=position, orientation='vertical',ticks=[10, 20, 30, 40, 50, 60, 70, 80, 90, 100], spacing='uniform',shrink=0.5) + cbar.set_label('Variance Reduction (%)', rotation=90, size=14) + + fig.savefig('fm_tape(' + mtinput +').pdf') + + + #Beginning of third section of code from Moment Tensor Decomposition Tool + + #Construct Moment Tensor arrays for plotting + mt=np.array((mxx,myy,mzz,mxy,mxz,myz)) + devm=np.array((Mdev[0,0], Mdev[1,1], Mdev[2,2], Mdev[0,1], Mdev[0,2], Mdev[1,2])) + dcm=np.array((Mdc[0,0], Mdc[1,1], Mdc[2,2], Mdc[0,1], Mdc[0,2], Mdc[1,2])) + clvdm=np.array((Mclvd[0,0], Mclvd[1,1], Mclvd[2,2], Mclvd[0,1], Mclvd[0,2], Mclvd[1,2])) + + fig=plt.figure(figsize=(8,8)) + threshold=0.; #initialize threshold + if Moiso != 0.0: + beach1 = beach(mt,xy=(0.5,0.5),width=0.95,mopad_basis='NED',show_iso=True,facecolor='black') + ax2 = fig.add_subplot(2,2,1) + ax2.add_collection(beach1) + ax2.set_aspect("equal") + ax2.set_axis_off() + buf="Full MT {0:.2e}".format(Motot) + ax2.set(title=buf) + threshold=Moiso*0.00001 #Set Modev threshold to a small value of Mosio if there is a Moiso + + # Second one plots deviatoric mt + if Modev != 0.0 and Modev/(Motot) > 0.001: #plot only significant deviatoric parts + beach1 = beach(devm,xy=(0.5,0.5),width=0.95*Modev/Motot,mopad_basis='NED',facecolor='black') + ax3 = fig.add_subplot(2,2,2) + ax3.add_collection(beach1) + ax3.set_aspect("equal") + ax3.set_axis_off() + buf="Dev MT {0:.2e}".format(Modev) + ax3.set(title=buf) + + # Third one plots dc + if Modc != 0.0 and Modc/Motot > 0.001: #plot only significant double-couple parts + beach1 = beach(dcm,xy=(0.5,0.5),width=0.95*Modc/Modev,mopad_basis='NED',facecolor='black') + ax3 = fig.add_subplot(2,2,3) + ax3.add_collection(beach1) + ax3.set_aspect("equal") + ax3.set_axis_off() + buf="DC MT {0:.2e}".format(Modc) + ax3.set(title=buf) + + # Forth one plots dc + if Moclvd != 0.0 and Moclvd/Motot > 0.001: #plot only signicant clvd parts + beach1 = beach(clvdm,xy=(0.5,0.5),width=0.95*Moclvd/Modev,mopad_basis='NED',facecolor='black') + ax3 = fig.add_subplot(2,2,4) + ax3.add_collection(beach1) + ax3.set_aspect("equal") + ax3.set_axis_off() + buf="CLVD MT {0:.2e}".format(Moclvd) + ax3.set(title=buf) + + if args.nosavefig==False: + fig.savefig('fm_beachball_components(' + mtinput + ').pdf') + #plt.show() + plt.close() diff --git a/BIN/putmech b/BIN/putmech new file mode 100755 index 0000000..7bfbbed Binary files /dev/null and b/BIN/putmech differ diff --git a/BIN/putmt b/BIN/putmt new file mode 100755 index 0000000..d561a2a Binary files /dev/null and b/BIN/putmt differ diff --git a/BIN/sac b/BIN/sac new file mode 100755 index 0000000..1ec1a2f Binary files /dev/null and b/BIN/sac differ diff --git a/BIN/sac2bin b/BIN/sac2bin new file mode 100755 index 0000000..5b06db4 Binary files /dev/null and b/BIN/sac2bin differ diff --git a/BIN/sac2columns b/BIN/sac2columns new file mode 100755 index 0000000..b1c2e01 Binary files /dev/null and b/BIN/sac2columns differ diff --git a/BIN/sac2columns_mt b/BIN/sac2columns_mt new file mode 100755 index 0000000..9b4cfcb Binary files /dev/null and b/BIN/sac2columns_mt differ diff --git a/BIN/sac2helm b/BIN/sac2helm new file mode 100755 index 0000000..62f6b6b Binary files /dev/null and b/BIN/sac2helm differ diff --git a/BIN/tdmt_invc_iso b/BIN/tdmt_invc_iso new file mode 100755 index 0000000..cd67f63 Binary files /dev/null and b/BIN/tdmt_invc_iso differ diff --git a/BIN/tdmt_invc_iso_fwd b/BIN/tdmt_invc_iso_fwd new file mode 100755 index 0000000..562db6b Binary files /dev/null and b/BIN/tdmt_invc_iso_fwd differ diff --git a/BIN/tdmt_invc_iso_out b/BIN/tdmt_invc_iso_out new file mode 100755 index 0000000..f0b13ec Binary files /dev/null and b/BIN/tdmt_invc_iso_out differ diff --git a/BIN/tdmt_invc_iso_outputmtline b/BIN/tdmt_invc_iso_outputmtline new file mode 100755 index 0000000..e867849 Binary files /dev/null and b/BIN/tdmt_invc_iso_outputmtline differ diff --git a/BIN/tdmt_invc_iso_zcor b/BIN/tdmt_invc_iso_zcor new file mode 100755 index 0000000..a4694d2 Binary files /dev/null and b/BIN/tdmt_invc_iso_zcor differ diff --git a/BIN/window b/BIN/window new file mode 100755 index 0000000..bd6de0f Binary files /dev/null and b/BIN/window differ diff --git a/SCRIPTS/fpsol.py b/SCRIPTS/fpsol.py new file mode 100644 index 0000000..2753e68 --- /dev/null +++ b/SCRIPTS/fpsol.py @@ -0,0 +1,69 @@ +#function to determine the strike, rake and dip from a dc moment tensor +#(full and deviatoric moment tenors need to be decomposed first + +import math +import numpy as np + +def fpsol(nu,u): + + """ + reads the vector normal and slip vector returning strike, rake, dip + """ + + + dip=np.arccos(-1*nu[2]) + if nu[0] ==0. and nu[1] == 0.: + str=0. + else: + str=np.arctan2(-1*nu[0],nu[1]) + + + + sstr=np.sin(str) + cstr=np.cos(str) + sdip=np.sin(dip) + cdip=np.cos(dip) + + + if abs(sdip) > 0.: + rake=np.arcsin(-1*u[2]/np.sin(dip)); + else: + arg1=1. + arg2=u[2] + arg=np.sign(arg2) + if arg < 0.: + rake=np.pi + else: + rake=0. + + slambda=np.sin(rake) + cdsl=cdip*slambda + + if abs(sstr) > abs(cstr): + clambda=(u[1]+cdsl*cstr)/sstr + else: + clambda=(u[0]-cdsl*sstr)/cstr + + if slambda == 0. and clambda == 0.: + slip=0. + else: + slip=np.arctan2(slambda,clambda) + + + if dip > np.pi/2: + dip=np.pi-dip + str=str+np.pi + slip=2*np.pi-slip + + if str < 0.: + str=str+2*np.pi + + if slip >= np.pi: + slip=slip-2*np.pi; + + + str=str*180/np.pi + rake=slip*180/np.pi + dip=dip*180/np.pi + + return str, rake, dip diff --git a/SCRIPTS/mopad.py b/SCRIPTS/mopad.py new file mode 100644 index 0000000..0b13d3d --- /dev/null +++ b/SCRIPTS/mopad.py @@ -0,0 +1,4922 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +from io import StringIO +import optparse +import math +import numpy as np +import os +import os.path +import sys +import re + +MOPAD_VERSION = 1.0 + + +# constants: +dynecm = 1e-7 +pi = np.pi + +epsilon = 1e-13 + +rad2deg = 180. / pi + + +def wrap(text, line_length=80): + '''Paragraph and list-aware wrapping of text.''' + + text = text.strip('\n') + at_lineend = re.compile(r' *\n') + at_para = re.compile(r'((^|(\n\s*)?\n)(\s+[*] )|\n\s*\n)') + + paragraphs = at_para.split(text)[::5] + listindents = at_para.split(text)[4::5] + newlist = at_para.split(text)[3::5] + + listindents[0:0] = [None] + listindents.append(True) + newlist.append(None) + + det_indent = re.compile(r'^ *') + + outlines = [] + for ip, p in enumerate(paragraphs): + if not p: + continue + + if listindents[ip] is None: + _indent = det_indent.findall(p)[0] + findent = _indent + else: + findent = listindents[ip] + _indent = ' ' * len(findent) + + ll = line_length - len(_indent) + llf = ll + + oldlines = [s.strip() for s in at_lineend.split(p.rstrip())] + p1 = ' '.join(oldlines) + possible = re.compile(r'(^.{1,%i}|.{1,%i})( |$)' % (llf, ll)) + for imatch, match in enumerate(possible.finditer(p1)): + parout = match.group(1) + if imatch == 0: + outlines.append(findent + parout) + else: + outlines.append(_indent + parout) + + if ip != len(paragraphs) - 1 and ( + listindents[ip] is None or + newlist[ip] is not None or + listindents[ip + 1] is None): + + outlines.append('') + + return outlines + + +def basis_switcher(in_system, out_system): + from_ned = { + 'NED': np.matrix([[1., 0., 0.], [0., 1., 0.], [0., 0., 1.]], + dtype=np.float), + 'USE': np.matrix([[0., -1., 0.], [0., 0., 1.], [-1., 0., 0.]], + dtype=np.float).I, + 'XYZ': np.matrix([[0., 1., 0.], [1., 0., 0.], [0., 0., -1.]], + dtype=np.float).I, + 'NWU': np.matrix([[1., 0., 0.], [0., -1., 0.], [0., 0., -1.]], + dtype=np.float).I} + + return from_ned[in_system].I * from_ned[out_system] + + +def basis_transform_matrix(m, in_system, out_system): + r = basis_switcher(in_system, out_system) + return np.dot(r, np.dot(m, r.I)) + + +def basis_transform_vector(v, in_system, out_system): + r = basis_switcher(in_system, out_system) + return np.dot(r, v) + + +class MopadHelpFormatter(optparse.IndentedHelpFormatter): + + def format_option(self, option): + '''From IndentedHelpFormatter but using a different wrap method.''' + + result = [] + opts = self.option_strings[option] + opt_width = self.help_position - self.current_indent - 2 + if len(opts) > opt_width: + opts = "%*s%s\n" % (self.current_indent, "", opts) + indent_first = self.help_position + else: # start help on same line as opts + opts = "%*s%-*s " % (self.current_indent, "", opt_width, opts) + indent_first = 0 + result.append(opts) + if option.help: + help_text = self.expand_default(option) + help_lines = wrap(help_text, self.help_width) + if len(help_lines) > 1: + help_lines.append('') + result.append("%*s%s\n" % (indent_first, "", help_lines[0])) + result.extend(["%*s%s\n" % (self.help_position, "", line) + for line in help_lines[1:]]) + elif opts[-1] != "\n": + result.append("\n") + return "".join(result) + + def format_description(self, description): + if not description: + return "" + desc_width = self.width - self.current_indent + return '\n'.join(wrap(description, desc_width)) + "\n" + + +class MTError(Exception): + pass + + +def euler_to_matrix(alpha, beta, gamma): + '''Given the euler angles alpha,beta,gamma, create rotation matrix + + Given coordinate system (x,y,z) and rotated system (xs,ys,zs) + the line of nodes is the intersection between the x-y and the xs-ys + planes. + alpha is the angle between the z-axis and the zs-axis. + beta is the angle between the x-axis and the line of nodes. + gamma is the angle between the line of nodes and the xs-axis. + + Usage for moment tensors: + m_unrot = numpy.matrix([[0,0,-1],[0,0,0],[-1,0,0]]) + rotmat = euler_to_matrix(dip,strike,-rake) + m = rotmat.T * m_unrot * rotmat''' + + ca = math.cos(alpha) + cb = math.cos(beta) + cg = math.cos(gamma) + sa = math.sin(alpha) + sb = math.sin(beta) + sg = math.sin(gamma) + + mat = np.matrix( + [[cb * cg - ca * sb * sg, sb * cg + ca * cb * sg, sa * sg], + [-cb * sg - ca * sb * cg, -sb * + sg + ca * cb * cg, sa * cg], + [sa * sb, -sa * cb, ca]], dtype=np.float) + return mat + + +class MomentTensor: + + _m_unrot = np.matrix( + [[0., 0., -1.], [0., 0., 0.], [-1., 0., 0.]], dtype=np.float) + + def __init__(self, M=None, in_system='NED', out_system='NED', debug=0): + + """ + Creates a moment tensor object on the basis of a provided mechanism M. + + If M is a non symmetric 3x3-matrix, the upper right triangle + of the matrix is taken as reference. M is symmetrisised + w.r.t. these entries. If M is provided as a 3-,4-,6-,7-tuple + or array, it is converted into a matrix internally according + to standard conventions (Aki & Richards). + + 'system' may be chosen as 'NED','USE','NWU', or 'XYZ'. + + 'debug' enables output on the shell at the intermediate steps. + """ + + self._original_M = M[:] + + self._input_basis = in_system.upper() + self._output_basis = out_system.upper() + + # bring M to symmetric matrix form + self._M = self._setup_M(M, self._input_basis) + + # decomposition: + self._decomposition_key = 1 + + # eigenvector / principal-axes system: + self._eigenvalues = None + self._eigenvectors = None + self._null_axis = None + self._t_axis = None + self._p_axis = None + self._rotation_matrix = None + + # optional - maybe set afterwards by external application - for later + # plotting: + self._best_faultplane = None + self._auxiliary_plane = None + + # initialise decomposition components + self._DC = None + self._DC_percentage = None + self._DC2 = None + self._DC2_percentage = None + self._DC3 = None + self._DC3_percentage = None + + self._iso = None + self._iso_percentage = None + self._devi = None + self._devi_percentage = None + self._CLVD = None + self._CLVD_percentage = None + + self._isotropic = None + self._deviatoric = None + self._seismic_moment = None + self._moment_magnitude = None + + self._decomp_attrib_map_keys = ('in', 'out', 'type', + 'full', + 'iso', 'iso_perc', + 'dev', 'devi', 'devi_perc', + 'dc', 'dc_perc', + 'dc2', 'dc2_perc', + 'dc3', 'dc3_perc', + 'clvd', 'clvd_perc', + 'mom', 'mag', + 'eigvals', 'eigvecs', + 't', 'n', 'p') + + self._decomp_attrib_map = dict(zip(self._decomp_attrib_map_keys, + ('input_system', 'output_system', + 'decomp_type', 'M', + 'iso', 'iso_percentage', + 'devi', 'devi', 'devi_percentage', + 'DC', 'DC_percentage', + 'DC2', 'DC2_percentage', + 'DC3', 'DC3_percentage', + 'CLVD', 'CLVD_percentage', + 'moment', 'mag', + 'eigvals', 'eigvecs', + 't_axis', 'null_axis', 'p_axis') + )) + + # carry out the MT decomposition - results are in basis NED + self._decompose_M() + + # set the appropriate principal axis system: + self._M_to_principal_axis_system() + + def _setup_M(self, mech, input_basis): + """ + Brings the provided mechanism into symmetric 3x3 matrix form. + + The source mechanism may be provided in different forms: + + * as 3x3 matrix - symmetry is checked - one basis system has to be + chosen, or NED as default is taken + * as 3-element tuple or array - interpreted as strike, dip, slip-rake + angles in degree + * as 4-element tuple or array - interpreted as strike, dip, slip-rake + angles in degree + seismic scalar moment in Nm + * as 6-element tuple or array - interpreted as the 6 independent + entries of the moment tensor + * as 7-element tuple or array - interpreted as the 6 independent + entries of the moment tensor + seismic scalar moment in Nm + * as 9-element tuple or array - interpreted as the 9 entries of the + moment tensor - checked for symmetry + * as a nesting of one of the upper types (e.g. a list of n-tuples); + first element of outer nesting is taken + """ + # set source mechanism to matrix form + + if mech is None: + raise MTError('Please provide a mechanism') + + # if some stupid nesting occurs + if len(mech) == 1: + mech = mech[0] + + # all 9 elements are given + if np.prod(np.shape(mech)) == 9: + if np.shape(mech)[0] == 3: + # assure symmetry: + mech[1, 0] = mech[0, 1] + mech[2, 0] = mech[0, 2] + mech[2, 1] = mech[1, 2] + new_M = mech + else: + new_M = np.array(mech).reshape(3, 3).copy() + new_M[1, 0] = new_M[0, 1] + new_M[2, 0] = new_M[0, 2] + new_M[2, 1] = new_M[1, 2] + + # mechanism given as 6- or 7-tuple, list or array + elif len(mech) == 6 or len(mech) == 7: + M = mech + new_M = np.matrix( + np.array([M[0], M[3], M[4], + M[3], M[1], M[5], + M[4], M[5], M[2]]).reshape(3, 3)) + + if len(mech) == 7: + new_M = M[6] * new_M + + # if given as strike, dip, rake, conventions from Jost & Herrmann hold + # - resulting matrix is in NED-basis: + elif len(mech) == 3 or len(mech) == 4: + strike, dip, rake = mech[:3] + scalar_moment = 1.0 + if len(mech) == 4: + scalar_moment = mech[3] + + rotmat1 = euler_to_matrix( + dip / rad2deg, strike / rad2deg, -rake / rad2deg) + new_M = rotmat1.T * MomentTensor._m_unrot * rotmat1 * scalar_moment + + # to assure right basis system - others are meaningless, provided + # these angles + input_basis = 'NED' + + return basis_transform_matrix(np.matrix(new_M), input_basis, 'NED') + + def _decompose_M(self): + """ + Running the decomposition of the moment tensor object. + + the standard decompositions M = Isotropic + DC + (CLVD or 2nd DC) are + supported (C.f. Jost & Herrmann, Aki & Richards) + """ + k = self._decomposition_key + d = MomentTensor.decomp_dict + if k in d: + d[k][1](self) + + else: + raise MTError('Invalid decomposition key: %i' % k) + + def print_decomposition(self): + for arg in self._decomp_attrib_map_keys: + getter = getattr(self, 'get_' + self._decomp_attrib_map[arg]) + print(getter(style='y', system=self._output_basis)) + + def _standard_decomposition(self): + """ + Decomposition according Aki & Richards and Jost & Herrmann into + + isotropic + deviatoric + = isotropic + DC + CLVD + + parts of the input moment tensor. + + results are given as attributes, callable via the get_* function: + + DC, CLVD, DC_percentage, seismic_moment, moment_magnitude + """ + + M = self._M + + # isotropic part + M_iso = np.diag(np.array([1. / 3 * np.trace(M), + 1. / 3 * np.trace(M), + 1. / 3 * np.trace(M)])) + + M0_iso = abs(1. / 3 * np.trace(M)) + + # deviatoric part + M_devi = M - M_iso + + self._isotropic = M_iso + self._deviatoric = M_devi + + #eigenvalues and -vectors + eigenwtot, eigenvtot = np.linalg.eig(M_devi) + + # eigenvalues and -vectors of the deviatoric part + eigenw1, eigenv1 = np.linalg.eig(M_devi) + + # eigenvalues in ascending order: + eigenw = np.real(np.take(eigenw1, np.argsort(abs(eigenwtot)))) + eigenv = np.real(np.take(eigenv1, np.argsort(abs(eigenwtot)), 1)) + + # eigenvalues in ascending order in absolute value!!: + eigenw_devi = np.real(np.take(eigenw1, np.argsort(abs(eigenw1)))) + #eigenv_devi = np.real(np.take(eigenv1, np.argsort(abs(eigenw1)), 1)) + + M0_devi = max(abs(eigenw_devi)) + + # named according to Jost & Herrmann: + #a1 = eigenv[:, 0] + a2 = eigenv[:, 1] + a3 = eigenv[:, 2] + + # if only isotropic part exists: + if M0_devi < epsilon: + F = 0.5 + else: + F = -eigenw_devi[0] / eigenw_devi[2] + + M_DC = np.matrix(np.zeros((9), float)).reshape(3, 3) + M_CLVD = np.matrix(np.zeros((9), float)).reshape(3, 3) + + M_DC = eigenw[2] * (1 - 2 * F) * (np.outer(a3, a3) - np.outer(a2, a2)) + M_CLVD = M_devi - M_DC + + # according to Bowers & Hudson: + M0 = M0_iso + M0_devi + + M_iso_percentage = int(round(M0_iso / M0 * 100, 6)) + self._iso_percentage = M_iso_percentage + + M_DC_percentage = int(round((1 - 2 * abs(F)) * + (1 - M_iso_percentage / 100.) * 100, 6)) + + self._DC = M_DC + self._CLVD = M_CLVD + self._DC_percentage = M_DC_percentage + + self._seismic_moment = M0 + self._moment_magnitude = np.log10( + self._seismic_moment * 1.0e7) / 1.5 - 10.7 + + def _decomposition_w_2DC(self): + """ + Decomposition according Aki & Richards and Jost & Herrmann into + + isotropic + deviatoric + = isotropic + DC + DC2 + + parts of the input moment tensor. + + results are given as attributes, callable via the get_* function: + + DC1, DC2, DC_percentage, seismic_moment, moment_magnitude + """ + M = self._M + + # isotropic part + M_iso = np.diag(np.array([1. / 3 * np.trace(M), + 1. / 3 * np.trace(M), + 1. / 3 * np.trace(M)])) + + M0_iso = abs(1. / 3 * np.trace(M)) + + # deviatoric part + M_devi = M - M_iso + + self._isotropic = M_iso + self._deviatoric = M_devi + + # eigenvalues and -vectors of the deviatoric part + eigenw1, eigenv1 = np.linalg.eig(M_devi) + + # eigenvalues in ascending order of their absolute values: + eigenw = np.real( + np.take(eigenw1, np.argsort(abs(eigenw1)))) + eigenv = np.real( + np.take(eigenv1, np.argsort(abs(eigenw1)), 1)) + + M0_devi = max(abs(eigenw)) + + # named according to Jost & Herrmann: + a1 = eigenv[:, 0] + a2 = eigenv[:, 1] + a3 = eigenv[:, 2] + + M_DC = np.matrix(np.zeros((9), float)).reshape(3, 3) + M_DC2 = np.matrix(np.zeros((9), float)).reshape(3, 3) + + M_DC = eigenw[2] * (np.outer(a3, a3) - np.outer(a2, a2)) + M_DC2 = eigenw[0] * (np.outer(a1, a1) - np.outer(a2, a2)) + + M_DC_percentage = abs(eigenw[2] / (abs(eigenw[2]) + abs(eigenw[0]))) + + self._DC = M_DC + self._DC2 = M_DC2 + self._DC_percentage = M_DC_percentage + + # according to Bowers & Hudson: + M0 = M0_iso + M0_devi + + M_iso_percentage = int(M0_iso / M0 * 100) + self._iso_percentage = M_iso_percentage + + #self._seismic_moment = np.sqrt(1./2*np.sum(eigenw**2) ) + self._seismic_moment = M0 + self._moment_magnitude = np.log10( + self._seismic_moment * 1.0e7) / 1.5 - 10.7 + + def _decomposition_w_CLVD_2DC(self): + """ + Decomposition according to Dahm (1993) into + + - isotropic + - CLVD + - strike-slip + - dip-slip + + parts of the input moment tensor. + + results are given as attributes, callable via the get_* function: + + iso, CLVD, DC1, DC2, iso_percentage, DC_percentage, DC1_percentage, + DC2_percentage, CLVD_percentage, seismic_moment, moment_magnitude + """ + M = self._M + + # isotropic part + M_iso = np.diag( + np.array([1. / 3 * np.trace(M), + 1. / 3 * np.trace(M), + 1. / 3 * np.trace(M)])) + + #M0_iso = abs(1. / 3 * np.trace(M)) + + # deviatoric part + M_devi = M - M_iso + + self._isotropic = M_iso + self._deviatoric = M_devi + + M_DC1 = np.matrix(np.zeros((9), float)).reshape(3, 3) + M_DC2 = np.matrix(np.zeros((9), float)).reshape(3, 3) + M_CLVD = np.matrix(np.zeros((9), float)).reshape(3, 3) + + M_DC1[0, 0] = -0.5 * (M[1, 1] - M[0, 0]) + M_DC1[1, 1] = 0.5 * (M[1, 1] - M[0, 0]) + M_DC1[0, 1] = M_DC1[1, 0] = M[0, 1] + + M_DC2[0, 2] = M_DC2[2, 0] = M[0, 2] + M_DC2[1, 2] = M_DC2[2, 1] = M[1, 2] + + M_CLVD = 1. / 3. * \ + (0.5 * (M[1, 1] + M[0, 0]) - M[2, 2]) * \ + np.diag(np.array([1., 1., -2.])) + + M_DC = M_DC1 + M_DC2 + + self._DC = M_DC + self._DC1 = M_DC1 + self._DC2 = M_DC2 + + # according to Bowers & Hudson: + eigvals_M, dummy_vecs = np.linalg.eig(M) + eigvals_M_devi, dummy_vecs = np.linalg.eig(M_devi) + eigvals_M_iso, dummy_iso = np.linalg.eig(M_iso) + eigvals_M_clvd, dummy_vecs = np.linalg.eig(M_CLVD) + eigvals_M_dc1, dummy_vecs = np.linalg.eig(M_DC1) + eigvals_M_dc2, dummy_vecs = np.linalg.eig(M_DC2) + + #M0_M = np.max(np.abs(eigvals_M - 1./3*np.sum(eigvals_M) )) + M0_M_iso = np.max( + np.abs(eigvals_M_iso - 1. / 3 * np.sum(eigvals_M))) + M0_M_clvd = np.max( + np.abs(eigvals_M_clvd - 1. / 3 * np.sum(eigvals_M))) + M0_M_dc1 = np.max( + np.abs(eigvals_M_dc1 - 1. / 3 * np.sum(eigvals_M))) + M0_M_dc2 = np.max( + np.abs(eigvals_M_dc2 - 1. / 3 * np.sum(eigvals_M))) + + M0_M_dc = M0_M_dc1 + M0_M_dc2 + M0_M_devi = M0_M_clvd + M0_M_dc + M0_M = M0_M_iso + M0_M_devi + + self._iso_percentage = int(M0_M_iso / M0_M * 100) + self._DC_percentage = int(M0_M_dc / M0_M * 100) + self._DC1_percentage = int(M0_M_dc1 / M0_M * 100) + self._DC2_percentage = int(M0_M_dc2 / M0_M * 100) + + #self._seismic_moment = np.sqrt(1./2*np.sum(eigenw**2) ) + self._seismic_moment = M0_M + self._moment_magnitude = np.log10( + self._seismic_moment * 1.0e7) / 1.5 - 10.7 + + def _decomposition_w_3DC(self): + """ + Decomposition according Aki & Richards and Jost & Herrmann into + + - isotropic + - deviatoric + - 3 DC + + parts of the input moment tensor. + + results are given as attributes, callable via the get_* function: + + DC1, DC2, DC3, DC_percentage, seismic_moment, moment_magnitude + """ + M = self._M + + # isotropic part + M_iso = np.diag(np.array([1. / 3 * np.trace(M), + 1. / 3 * np.trace(M), + 1. / 3 * np.trace(M)])) + M0_iso = abs(1. / 3 * np.trace(M)) + + # deviatoric part + M_devi = M - M_iso + + self._isotropic = M_iso + self._deviatoric = M_devi + + # eigenvalues and -vectors of the deviatoric part + eigenw1, eigenv1 = np.linalg.eig(M_devi) + M0_devi = max(abs(eigenw1)) + + # eigenvalues and -vectors of the full M !!!!!!!! + eigenw1, eigenv1 = np.linalg.eig(M) + + # eigenvalues in ascending order of their absolute values: + eigenw = np.real( + np.take(eigenw1, np.argsort(abs(eigenw1)))) + eigenv = np.real( + np.take(eigenv1, np.argsort(abs(eigenw1)), 1)) + + # named according to Jost & Herrmann: + a1 = eigenv[:, 0] + a2 = eigenv[:, 1] + a3 = eigenv[:, 2] + + M_DC1 = np.matrix(np.zeros((9), float)).reshape(3, 3) + M_DC2 = np.matrix(np.zeros((9), float)).reshape(3, 3) + M_DC3 = np.matrix(np.zeros((9), float)).reshape(3, 3) + + M_DC1 = 1. / 3. * \ + (eigenw[0] - eigenw[1]) * (np.outer(a1, a1) - np.outer(a2, a2)) + M_DC2 = 1. / 3. * \ + (eigenw[1] - eigenw[2]) * (np.outer(a2, a2) - np.outer(a3, a3)) + M_DC3 = 1. / 3. * \ + (eigenw[2] - eigenw[0]) * (np.outer(a3, a3) - np.outer(a1, a1)) + + M_DC1_perc = int(100 * abs((eigenw[0] - eigenw[1])) / + (abs((eigenw[1] - eigenw[2])) + + abs((eigenw[1] - eigenw[2])) + + abs((eigenw[2] - eigenw[0])))) + M_DC2_perc = int(100 * abs((eigenw[1] - eigenw[2])) / + (abs((eigenw[1] - eigenw[2])) + + abs((eigenw[1] - eigenw[2])) + + abs((eigenw[2] - eigenw[0])))) + + self._DC = M_DC1 + self._DC2 = M_DC2 + self._DC3 = M_DC3 + + self._DC_percentage = M_DC1_perc + self._DC2_percentage = M_DC2_perc + + # according to Bowers & Hudson: + M0 = M0_iso + M0_devi + + M_iso_percentage = int(M0_iso / M0 * 100) + self._iso_percentage = M_iso_percentage + + #self._seismic_moment = np.sqrt(1./2*np.sum(eigenw**2) ) + self._seismic_moment = M0 + self._moment_magnitude = np.log10( + self._seismic_moment * 1.0e7) / 1.5 - 10.7 + + def _M_to_principal_axis_system(self): + """ + Read in Matrix M and set up eigenvalues (EW) and eigenvectors + (EV) for setting up the principal axis system. + + The internal convention is the 'HNS'-system: H is the + eigenvector for the smallest absolute eigenvalue, S is the + eigenvector for the largest absolute eigenvalue, N is the null + axis. + + Naming due to the geometry: a CLVD is + Symmetric to the S-axis, + Null-axis is common sense, and the third (auxiliary) axis + Helps to construct the R³. + + Additionally builds matrix for basis transformation back to NED system. + + The eigensystem setup defines the colouring order for a later + plotting in the BeachBall class. This order is set by the + '_plot_clr_order' attribute. + """ + + M = self._M + M_devi = self._deviatoric + + # working in framework of 3 principal axes: + # eigenvalues (EW) are in order from high to low + # - neutral axis N, belongs to middle EW + # - symmetry axis S ('sigma') belongs to EW with largest absolute value + # (P- or T-axis) + # - auxiliary axis H ('help') belongs to remaining EW (T- or P-axis) + # EW sorting from lowest to highest value + EW_devi, EV_devi = np.linalg.eigh(M_devi) + EW_order = np.argsort(EW_devi) + + # print 'order',EW_order + + if 1: # self._plot_isotropic_part: + trace_M = np.trace(M) + if abs(trace_M) < epsilon: + trace_M = 0 + EW, EV = np.linalg.eigh(M) + for i, ew in enumerate(EW): + if abs(EW[i]) < epsilon: + EW[i] = 0 + else: + trace_M = np.trace(M_devi) + if abs(trace_M) < epsilon: + trace_M = 0 + + EW, EV = np.linalg.eigh(M_devi) + for i, ew in enumerate(EW): + if abs(EW[i]) < epsilon: + EW[i] = 0 + + EW1_devi = EW_devi[EW_order[0]] + EW2_devi = EW_devi[EW_order[1]] + EW3_devi = EW_devi[EW_order[2]] + EV1_devi = EV_devi[:, EW_order[0]] + EV2_devi = EV_devi[:, EW_order[1]] + EV3_devi = EV_devi[:, EW_order[2]] + + EW1 = EW[EW_order[0]] + EW2 = EW[EW_order[1]] + EW3 = EW[EW_order[2]] + EV1 = EV[:, EW_order[0]] + EV2 = EV[:, EW_order[1]] + EV3 = EV[:, EW_order[2]] + + chng_basis_tmp = np.asmatrix(np.zeros((3, 3))) + chng_basis_tmp[:, 0] = EV1_devi + chng_basis_tmp[:, 1] = EV2_devi + chng_basis_tmp[:, 2] = EV3_devi + + symmetry_around_tension = 1 + clr = 1 + + if abs(EW2_devi) < epsilon: + EW2_devi = 0 + + # implosion + if EW1 < 0 and EW2 < 0 and EW3 < 0: + symmetry_around_tension = 0 + # logger.debug( 'IMPLOSION - symmetry around pressure axis \n\n') + clr = 1 + # explosion + elif EW1 > 0 and EW2 > 0 and EW3 > 0: + symmetry_around_tension = 1 + if abs(EW1_devi) > abs(EW3_devi): + symmetry_around_tension = 0 + # logger.debug( 'EXPLOSION - symmetry around tension axis \n\n') + clr = -1 + # net-implosion + elif EW2 < 0 and sum([EW1, EW2, EW3]) < 0: + if abs(EW1_devi) < abs(EW3_devi): + symmetry_around_tension = 1 + clr = 1 + else: + symmetry_around_tension = 1 + clr = 1 + # net-implosion + elif EW2_devi >= 0 and sum([EW1, EW2, EW3]) < 0: + symmetry_around_tension = 0 + clr = -1 + if abs(EW1_devi) < abs(EW3_devi): + symmetry_around_tension = 1 + clr = 1 + # net-explosion + elif EW2_devi < 0 and sum([EW1, EW2, EW3]) > 0: + symmetry_around_tension = 1 + clr = 1 + if abs(EW1_devi) > abs(EW3_devi): + symmetry_around_tension = 0 + clr = -1 + # net-explosion + elif EW2_devi >= 0 and sum([EW1, EW2, EW3]) > 0: + symmetry_around_tension = 0 + clr = -1 + else: + # TODO check: this point should never be reached !! + pass + + if abs(EW1_devi) < abs(EW3_devi): + symmetry_around_tension = 1 + clr = 1 + if 0: # EW2 > 0 :#or (EW2 > 0 and EW2_devi > 0) : + symmetry_around_tension = 0 + clr = -1 + + if abs(EW1_devi) >= abs(EW3_devi): + symmetry_around_tension = 0 + clr = -1 + if 0: # EW2 < 0 : + symmetry_around_tension = 1 + clr = 1 + if (EW3 < 0 and np.trace(self._M) >= 0): + # reaching this point means, we have a serious problem, likely of + # numerical nature + print('Houston, we have had a problem - check M !!!!!! \n' + \ + '( Trace(M) > 0, but largest eigenvalue is still negative)') + raise MTError(' !! ') + + if trace_M == 0: + # print 'pure deviatoric' + if EW2 == 0: + # print 'pure shear' + symmetry_around_tension = 1 + clr = 1 + + elif 2 * abs(EW2) == abs(EW1) or 2 * abs(EW2) == abs(EW3): + # print 'pure clvd' + if abs(EW1) < EW3: + # print 'CLVD: symmetry around tension' + symmetry_around_tension = 1 + clr = 1 + else: + # print 'CLVD: symmetry around pressure' + symmetry_around_tension = 0 + clr = -1 + else: + # print 'mix of DC and CLVD' + if abs(EW1) < EW3: + # print 'symmetry around tension' + symmetry_around_tension = 1 + clr = 1 + else: + # print 'symmetry around pressure' + symmetry_around_tension = 0 + clr = -1 + + # define order of eigenvectors and values according to symmetry axis + if symmetry_around_tension == 1: + EWs = EW3.copy() + EVs = EV3.copy() + EWh = EW1.copy() + EVh = EV1.copy() + + else: + EWs = EW1.copy() + EVs = EV1.copy() + EWh = EW3.copy() + EVh = EV3.copy() + + EWn = EW2 + EVn = EV2 + + # build the basis system change matrix: + chng_basis = np.asmatrix(np.zeros((3, 3))) + + # order of eigenvector's basis: (H,N,S) + chng_basis[:, 0] = EVh + chng_basis[:, 1] = EVn + chng_basis[:, 2] = EVs + + # matrix for basis transformation + self._rotation_matrix = chng_basis + + # collections of eigenvectors and eigenvalues + self._eigenvectors = [EVh, EVn, EVs] + self._eigenvalues = [EWh, EWn, EWs] + + # principal axes + self._null_axis = EVn + self._t_axis = EV1 + self._p_axis = EV3 + + # plotting order flag - important for plot in BeachBall class + self._plot_clr_order = clr + + # collection of the faultplanes, given in strike, dip, slip-rake + self._faultplanes = self._find_faultplanes() + + def _find_faultplanes(self): + """ + Sets the two angle-triples, describing the faultplanes of the + Double Couple, defined by the eigenvectors P and T of the + moment tensor object. + + Define a reference Double Couple with strike = dip = + slip-rake = 0, the moment tensor object's DC is transformed + (rotated) w.r.t. this orientation. The respective rotation + matrix yields the first fault plane angles as the Euler + angles. After flipping the first reference plane by + multiplying the appropriate flip-matrix, one gets the second fault + plane's geometry. + + All output angles are in degree + + ( + to check: + using Sebastian's conventions: + + rotationsmatrix1 = + EV Matrix of M, but in order TNP (not as here PNT!!!) + + reference-DC with strike, dip, rake = 0,0,0 + in NED - form: M = 0,0,0,0,-1,0 + + the eigenvectors of this into a Matrix: + + trafo-matrix2 = EV Matrix of Reference-DC in order TNP + + effective Rotation matrix = (rotation_matrix1 * trafo-matrix2.T).T + + by checking for det <0, make sure, if Matrix must be multiplied by -1 + + flip_matrix = 0,0,-1,0,-1,0,-1,0,0 + + other DC orientation obtained by flip * effective Rotation matrix + + both matrices in matrix_2_euler + ) + """ + # reference Double Couple (in NED basis) + # it has strike, dip, slip-rake = 0,0,0 + refDC = np.matrix([[0., 0., -1.], [0., 0., 0.], [-1., 0., 0.]], + dtype=np.float) + refDC_evals, refDC_evecs = np.linalg.eigh(refDC) + + # matrix which is turning from one fault plane to the other + flip_dc = np.matrix([[0., 0., -1.], [0., -1., 0.], [-1., 0., 0.]], + dtype=np.float) + + # euler-tools need matrices of EV sorted in PNT: + pnt_sorted_EV_matrix = self._rotation_matrix.copy() + + # resort only necessary, if abs(p) <= abs(t) + # print self._plot_clr_order + if self._plot_clr_order < 0: + pnt_sorted_EV_matrix[:, 0] = self._rotation_matrix[:, 2] + pnt_sorted_EV_matrix[:, 2] = self._rotation_matrix[:, 0] + + # rotation matrix, describing the rotation of the eigenvector + # system of the input moment tensor into the eigenvector + # system of the reference Double Couple + rot_matrix_fp1 = (np.dot(pnt_sorted_EV_matrix, refDC_evecs.T)).T + + # check, if rotation has right orientation + if np.linalg.det(rot_matrix_fp1) < 0.: + rot_matrix_fp1 *= -1. + + # adding a rotation into the ambiguous system of the second fault plane + rot_matrix_fp2 = np.dot(flip_dc, rot_matrix_fp1) + + fp1 = self._find_strike_dip_rake(rot_matrix_fp1) + fp2 = self._find_strike_dip_rake(rot_matrix_fp2) + + return [fp1, fp2] + + def _find_strike_dip_rake(self, rotation_matrix): + """ + Returns angles strike, dip, slip-rake in degrees, describing the fault + plane. + """ + (alpha, beta, gamma) = self._matrix_to_euler(rotation_matrix) + return (beta * rad2deg, alpha * rad2deg, -gamma * rad2deg) + + def _cvec(self, x, y, z): + """ + Builds a column vector (matrix type) from a 3 tuple. + """ + return np.matrix([[x, y, z]], dtype=np.float).T + + def _matrix_to_euler(self, rotmat): + """ + Returns three Euler angles alpha, beta, gamma (in radians) from a + rotation matrix. + """ + ex = self._cvec(1., 0., 0.) + ez = self._cvec(0., 0., 1.) + exs = rotmat.T * ex + ezs = rotmat.T * ez + enodes = np.cross(ez.T, ezs.T).T + if np.linalg.norm(enodes) < 1e-10: + enodes = exs + enodess = rotmat * enodes + cos_alpha = float((ez.T * ezs)) + if cos_alpha > 1.: + cos_alpha = 1. + if cos_alpha < -1.: + cos_alpha = -1. + alpha = np.arccos(cos_alpha) + beta = np.mod(np.arctan2(enodes[1, 0], enodes[0, 0]), np.pi * 2.) + gamma = np.mod(-np.arctan2(enodess[1, 0], enodess[0, 0]), np.pi * 2.) + return self._unique_euler(alpha, beta, gamma) + + def _unique_euler(self, alpha, beta, gamma): + """ + Uniquify euler angle triplet. + + Puts euler angles into ranges compatible with (dip,strike,-rake) in + seismology: + + alpha (dip) : [0, pi/2] + beta (strike) : [0, 2*pi) + gamma (-rake) : [-pi, pi) + + If alpha is near to zero, beta is replaced by beta+gamma and gamma is + set to zero, to prevent that additional ambiguity. + + If alpha is near to pi/2, beta is put into the range [0,pi). + """ + alpha = np.mod(alpha, 2.0 * pi) + + if 0.5 * pi < alpha and alpha <= pi: + alpha = pi - alpha + beta = beta + pi + gamma = 2.0 * pi - gamma + elif pi < alpha and alpha <= 1.5 * pi: + alpha = alpha - pi + gamma = pi - gamma + elif 1.5 * pi < alpha and alpha <= 2.0 * pi: + alpha = 2.0 * pi - alpha + beta = beta + pi + gamma = pi + gamma + + alpha = np.mod(alpha, 2.0 * pi) + beta = np.mod(beta, 2.0 * pi) + gamma = np.mod(gamma + pi, 2.0 * pi) - pi + + # If dip is exactly 90 degrees, one is still + # free to choose between looking at the plane from either side. + # Choose to look at such that beta is in the range [0,180) + + # This should prevent some problems, when dip is close to 90 degrees: + if abs(alpha - 0.5 * pi) < 1e-10: + alpha = 0.5 * pi + if abs(beta - pi) < 1e-10: + beta = pi + if abs(beta - 2. * pi) < 1e-10: + beta = 0. + if abs(beta) < 1e-10: + beta = 0. + + if alpha == 0.5 * pi and beta >= pi: + gamma = -gamma + beta = np.mod(beta - pi, 2.0 * pi) + gamma = np.mod(gamma + pi, 2.0 * pi) - pi + assert 0. <= beta < pi + assert -pi <= gamma < pi + + if alpha < 1e-7: + beta = np.mod(beta + gamma, 2.0 * pi) + gamma = 0. + + return (alpha, beta, gamma) + + def _matrix_w_style_and_system(self, M2return, system, style): + """ + Transform matrix into the given basis system 'system'. + + If the argument 'style' is set to 'fancy', a 'print' of the return + value yields a nice shell output of the matrix for better + visual control. + """ + + M2return = basis_transform_matrix(M2return, 'NED', system.upper()) + + if style.lower() in ['f', 'fan', 'fancy', 'y']: + return fancy_matrix(M2return) + else: + return M2return + + def _vector_w_style_and_system(self, vectors, system, style='n'): + """ + Transform vector(s) into the given basis system 'system'. + + If the argument 'style' is set to 'fancy', a 'print' of the return + value yields a nice shell output of the vector(s) for better + visual control. + + 'vectors' can be either a single array, tuple, matrix or a collection + in form of a list, array or matrix. + If it's a list, each entry will be checked, if it's 3D - if not, an + exception is raised. + If it's a matrix or array with column-length 3, the columns are + interpreted as vectors, otherwise, its transposed is used. + """ + + fancy = style.lower() in ['f', 'fan', 'fancy', 'y'] + + lo_vectors = [] + + # if list of vectors + if type(vectors) == list: + lo_vectors = vectors + + else: + assert 3 in vectors.shape + + if np.shape(vectors)[0] == 3: + for ii in np.arange(np.shape(vectors)[1]): + lo_vectors.append(vectors[:, ii]) + else: + for ii in np.arange(np.shape(vectors)[0]): + lo_vectors.append(vectors[:, ii].transpose()) + + lo_vecs_to_show = [] + for vec in lo_vectors: + + t_vec = basis_transform_vector(vec, 'NED', system.upper()) + + if fancy: + lo_vecs_to_show.append(fancy_vector(t_vec)) + else: + lo_vecs_to_show.append(t_vec) + + if len(lo_vecs_to_show) == 1: + return lo_vecs_to_show[0] + + else: + if fancy: + return ''.join(lo_vecs_to_show) + else: + return lo_vecs_to_show + + def get_M(self, system='NED', style='n'): + """ + Returns the moment tensor in matrix representation. + + Call with arguments to set ouput in other basis system or in fancy + style (to be viewed with 'print') + """ + return self._matrix_w_style_and_system(self._M, system, style) + + def get_decomposition(self, in_system='NED', out_system='NED', style='n'): + """ + Returns a tuple of the decomposition results. + + Order: + - 1 - basis of the provided input (string) + - 2 - basis of the representation (string) + - 3 - chosen decomposition type (integer) + + - 4 - full moment tensor (matrix) + + - 5 - isotropic part (matrix) + - 6 - isotropic percentage (float) + - 7 - deviatoric part (matrix) + - 8 - deviatoric percentage (float) + + - 9 - DC part (matrix) + -10 - DC percentage (float) + -11 - DC2 part (matrix) + -12 - DC2 percentage (float) + -13 - DC3 part (matrix) + -14 - DC3 percentage (float) + + -15 - CLVD part (matrix) + -16 - CLVD percentage (matrix) + + -17 - seismic moment (float) + -18 - moment magnitude (float) + + -19 - eigenvectors (3-array) + -20 - eigenvalues (list) + -21 - p-axis (3-array) + -22 - neutral axis (3-array) + -23 - t-axis (3-array) + -24 - faultplanes (list of two 3-arrays) + """ + return [in_system, out_system, self.get_decomp_type(), + self.get_M(system=out_system), + self.get_iso(system=out_system), self.get_iso_percentage(), + self.get_devi(system=out_system), self.get_devi_percentage(), + self.get_DC(system=out_system), self.get_DC_percentage(), + self.get_DC2(system=out_system), self.get_DC2_percentage(), + self.get_DC3(system=out_system), self.get_DC3_percentage(), + self.get_CLVD(system=out_system), self.get_CLVD_percentage(), + self.get_moment(), self.get_mag(), + self.get_eigvecs(system=out_system), + self.get_eigvals(system=out_system), + self.get_p_axis(system=out_system), + self.get_null_axis(system=out_system), + self.get_t_axis(system=out_system), + self.get_fps()] + + def __str__(self): + """ + Nice compilation of decomposition result to be viewed in the shell + (call with 'print'). + """ + + mexp = pow(10, np.ceil(np.log10(np.max(np.abs(self._M))))) + + m = basis_transform_matrix(self._M / mexp, 'NED', self._output_basis) + + def b(i, j): + x = self._output_basis.lower() + return x[i] + x[j] + + s = '\nScalar Moment: M0 = %g Nm (Mw = %3.1f)\n' + s += 'Moment Tensor: M%s = %6.3f, M%s = %6.3f, M%s = %6.3f,\n' + s += ' M%s = %6.3f, M%s = %6.3f, M%s = %6.3f' + s += ' [ x %g ]\n\n' + s = s % (self._seismic_moment, self._moment_magnitude, + b(0, 0), m[0, 0], + b(1, 1), m[1, 1], + b(2, 2), m[2, 2], + b(0, 1), m[0, 1], + b(0, 2), m[0, 2], + b(1, 2), m[1, 2], + mexp) + + s += self._fault_planes_as_str() + return s + + def _fault_planes_as_str(self): + """ + Internal setup of a nice string, containing information about the fault + planes. + """ + s = '\n' + for i, sdr in enumerate(self.get_fps()): + s += 'Fault plane %i: ' % (i + 1) + s += 'strike = %3.0f°, dip = %3.0f°, slip-rake = %4.0f°\n' % \ + (sdr[0], sdr[1], sdr[2]) + return s + + def get_input_system(self, style='n', **kwargs): + """ + Returns the basis system of the input. + """ + return self._input_basis + + def get_output_system(self, style='n', **kwargs): + """ + Returns the basis system of the output. + """ + return self._output_basis + + def get_decomp_type(self, style='n', **kwargs): + """ + Returns the decomposition type. + """ + + if style == 'y': + return MomentTensor.decomp_dict[self._decomposition_key][0] + + return self._decomposition_key + + def get_iso(self, system='NED', style='n'): + """ + Returns the isotropic part of the moment tensor in matrix + representation. + + Call with arguments to set ouput in other basis system or in fancy + style (to be viewed with 'print') + """ + return self._matrix_w_style_and_system(self._isotropic, system, style) + + def get_devi(self, system='NED', style='n'): + """ + Returns the deviatoric part of the moment tensor in matrix + representation. + + Call with arguments to set ouput in other basis system or in fancy + style (to be viewed with 'print') + """ + return self._matrix_w_style_and_system(self._deviatoric, system, style) + + def get_DC(self, system='NED', style='n'): + """ + Returns the Double Couple part of the moment tensor in matrix + representation. + + Call with arguments to set ouput in other basis system or in fancy + style (to be viewed with 'print') + """ + return self._matrix_w_style_and_system(self._DC, system, style) + + def get_DC2(self, system='NED', style='n'): + """ + Returns the second Double Couple part of the moment tensor in matrix + representation. + + Call with arguments to set ouput in other basis system or in fancy + style (to be viewed with 'print') + """ + + if self._DC2 is None: + if style == 'y': + return 'not available in this decomposition type' + else: + return None + + return self._matrix_w_style_and_system(self._DC2, system, style) + + def get_DC3(self, system='NED', style='n'): + """ + Returns the third Double Couple part of the moment tensor in matrix + representation. + + Call with arguments to set ouput in other basis system or in fancy + style (to be viewed with 'print') + """ + + if self._DC3 is None: + if style == 'y': + return 'not available in this decomposition type' + else: + return None + + return self._matrix_w_style_and_system(self._DC3, system, style) + + def get_CLVD(self, system='NED', style='n'): + """ + Returns the CLVD part of the moment tensor in matrix representation. + + Call with arguments to set ouput in other basis system or in fancy + style (to be viewed with 'print') + """ + if self._CLVD is None: + if style == 'y': + return 'not available in this decomposition type' + else: + return None + + return self._matrix_w_style_and_system(self._CLVD, system, style) + + def get_DC_percentage(self, system='NED', style='n'): + """ + Returns the percentage of the DC part of the moment tensor in matrix + representation. + """ + + return self._DC_percentage + + def get_CLVD_percentage(self, system='NED', style='n'): + """ + Returns the percentage of the DC part of the moment tensor in matrix + representation. + """ + + if self._CLVD is None: + if style == 'y': + return 'not available in this decomposition type' + else: + return None + + return int(100 - self._iso_percentage - self._DC_percentage) + + def get_DC2_percentage(self, system='NED', style='n'): + """ + Returns the percentage of the second DC part of the moment tensor in + matrix representation. + """ + + if self._DC2 is None: + if style == 'y': + return 'not available in this decomposition type' + else: + return None + + return self._DC2_percentage + + def get_DC3_percentage(self, system='NED', style='n'): + """ + Returns the percentage of the third DC part of the moment tensor in + matrix representation. + """ + + if self._DC3 is None: + if style == 'y': + return 'not available in this decomposition type' + else: + return None + + return int(100 - self._DC2_percentage - self._DC_percentage) + + def get_iso_percentage(self, system='NED', style='n'): + """ + Returns the percentage of the isotropic part of the moment tensor in + matrix representation. + """ + return self._iso_percentage + + def get_devi_percentage(self, system='NED', style='n'): + """ + Returns the percentage of the deviatoric part of the moment tensor in + matrix representation. + """ + return int(100 - self._iso_percentage) + + def get_moment(self, system='NED', style='n'): + """ + Returns the seismic moment (in Nm) of the moment tensor. + """ + return self._seismic_moment + + def get_mag(self, system='NED', style='n'): + """ + Returns the moment magnitude M_w of the moment tensor. + """ + return self._moment_magnitude + + def get_decomposition_key(self, system='NED', style='n'): + """ + 10 = standard decomposition (Jost & Herrmann) + """ + return self._decomposition_key + + def get_eigvals(self, system='NED', style='n', **kwargs): + """ + Returns a list of the eigenvalues of the moment tensor. + """ + if style == 'y': + return '%g, %g, %g' % tuple(self._eigenvalues) + + # in the order HNS: + return self._eigenvalues + + def get_eigvecs(self, system='NED', style='n'): + """ + Returns the eigenvectors of the moment tensor. + + Call with arguments to set ouput in other basis system or in fancy + style (to be viewed with 'print') + """ + return self._vector_w_style_and_system(self._eigenvectors, system, + style) + + def get_null_axis(self, system='NED', style='n'): + """ + Returns the neutral axis of the moment tensor. + + Call with arguments to set ouput in other basis system or in fancy + style (to be viewed with 'print') + """ + + return self._vector_w_style_and_system(self._null_axis, system, style) + + def get_t_axis(self, system='NED', style='n'): + """ + Returns the tension axis of the moment tensor. + + Call with arguments to set ouput in other basis system or in fancy + style (to be viewed with 'print') + """ + return self._vector_w_style_and_system(self._t_axis, system, style) + + def get_p_axis(self, system='NED', style='n'): + """ + Returns the pressure axis of the moment tensor. + + Call with arguments to set ouput in other basis system or in fancy + style (to be viewed with 'print') + """ + return self._vector_w_style_and_system(self._p_axis, system, style) + + def get_transform_matrix(self, system='NED', style='n'): + """ + Returns the transformation matrix (input system to principal axis + system. + + Call with arguments to set ouput in other basis system or in fancy + style (to be viewed with 'print') + """ + return self._matrix_w_style_and_system(self._rotation_matrix, system, + style) + + def get_fps(self, **kwargs): + """ + Returns a list of the two faultplane 3-tuples, each showing strike, + dip, slip-rake. + """ + fancy_key = kwargs.get('style', '0') + if fancy_key[0].lower() == 'y': + return self._fault_planes_as_str() + else: + return self._faultplanes + + def get_colour_order(self, **kwargs): + """ + Returns the value of the plotting order (only important in BeachBall + instances). + """ + return self._plot_clr_order + +MomentTensor.decomp_dict = { + 1: ('ISO + DC + CLVD', + MomentTensor._standard_decomposition), + 2: ('ISO + major DC + minor DC', + MomentTensor._decomposition_w_2DC), + 3: ('ISO + DC1 + DC2 + DC3', + MomentTensor._decomposition_w_3DC), + 4: ('ISO + strike DC + dip DC + CLVD', + MomentTensor._decomposition_w_CLVD_2DC), +} + + +def fancy_matrix(m_in): + m = m_in.copy() + + norm_factor = round(max(abs(np.array(m).flatten())), 5) + + try: + if (norm_factor < 0.1) or (norm_factor >= 10): + if not abs(norm_factor) == 0: + m = m / norm_factor + out = "\n / %5.2F %5.2F %5.2F \\\n" % \ + (m[0, 0], m[0, 1], m[0, 2]) + out += " | %5.2F %5.2F %5.2F | x %F\n" % \ + (m[1, 0], m[1, 1], m[1, 2], norm_factor) + out += " \\ %5.2F %5.2F %5.2F /\n" % \ + (m[2, 0], m[2, 1], m[2, 2]) + return out + except: + pass + + return "\n / %5.2F %5.2F %5.2F \\\n" % (m[0, 0], m[0, 1], m[0, 2]) + \ + " | %5.2F %5.2F %5.2F | \n" % (m[1, 0], m[1, 1], m[1, 2]) + \ + " \\ %5.2F %5.2F %5.2F /\n" % (m[2, 0], m[2, 1], m[2, 2]) + + +def fancy_vector(v): + """ + Returns a given 3-vector or array in a cute way on the shell, if you + use 'print' on the return value. + """ + return "\n / %5.2F \\\n" % (v[0]) + \ + " | %5.2F |\n" % (v[1]) + \ + " \\ %5.2F /\n" % (v[2]) + + +class BeachBall: + + """ + Class for generating a beachball projection for a provided moment tensor + object. + + Input for instance generation: MomentTensor object [,keywords dictionary] + + Output can be plots of + - the eigensystem + - the complete sphere + - the projection to a unit sphere + ... either lower (standard) or upper half + + Beside the plots, the unit sphere projection may be saved in a given file. + + Alternatively, only the file can be provided without showing anything + directly. + """ + def __init__(self, MT=MomentTensor, kwargs_dict={}, npoints=360): + self.MT = MT + self._M = MT._M + self._set_standard_attributes() + self._update_attributes(kwargs_dict) + + self._plot_n_points = npoints + self._nodallines_in_NED_system() + self.arange_1 = np.arange(3 * npoints) - 1 + # self._identify_faultplanes() + + def ploBB(self, kwargs, ax=None): + """ + Plots the projection of the beachball onto a unit sphere. + """ + self._update_attributes(kwargs) + self._setup_BB() + self._plot_US(ax=ax) + + def save_BB(self, kwargs): + """ + Method for saving the 2D projection of the beachball without showing + the plot. + + :param outfile: name of outfile, addressing w.r.t. current directory + :param format: if no implicit valid format is provided within the + filename, add file format + """ + self._update_attributes(kwargs) + self._setup_BB() + self._just_save_bb() + + def _just_save_bb(self): + """ + Internal method for saving the beachball unit sphere plot into a given + file. + + This method tries to setup the approprite backend according to the + requested file format first. 'AGG' is used in most cases. + """ + import matplotlib + + if self._plot_outfile_format == 'svg': + try: + matplotlib.use('SVG') + except: + matplotlib.use('Agg') + elif self._plot_outfile_format == 'pdf': + try: + matplotlib.use('PDF') + except: + matplotlib.use('Agg') + pass + elif self._plot_outfile_format == 'ps': + try: + matplotlib.use('PS') + except: + matplotlib.use('Agg') + pass + elif self._plot_outfile_format == 'eps': + try: + matplotlib.use('Agg') + except: + matplotlib.use('PS') + pass + elif self._plot_outfile_format == 'png': + try: + matplotlib.use('AGG') + except: + mp_out = matplotlib.use('GTKCairo') + if mp_out: + mp_out2 = matplotlib.use('Cairo') + if mp_out2: + matplotlib.use('GDK') + + # finally generating the actual plot + import pylab as P + + plotfig = self._setup_plot_US(P) + + outfile_format = self._plot_outfile_format + outfile_name = self._plot_outfile + + outfile_abs_name = os.path.realpath( + os.path.abspath(os.path.join(os.curdir, outfile_name))) + + # save plot into file + try: + plotfig.savefig(outfile_abs_name, dpi=self._plot_dpi, + transparent=True, format=outfile_format) + except: + print('ERROR!! -- Saving of plot not possible') + return + P.close(667) + del P + del matplotlib + + def get_psxy(self, kwargs): + """ + Method returning one single string, which can be piped into the psxy + method of the GMT package. + + :param GMT_type: fill/lines/EVs (select type of string), + default is 'fill' + :param GMT_scaling: scale the beachball - default radius is 1.0 + :param GMT_tension_colour: tension area of BB - colour flag for -Z in + psxy, default is 1 + :param GMT_pressure_colour: pressure area of BB - colour flag for -Z in + psxy, default is 0 + :param GMT_show_2FPs: flag, if both faultplanes are to be shown, + default is 0 + :param GMT_show_1FP: flag, if one faultplane is to be shown, default + is 1 + :param GMT_FP_index: 1 or 2, default is 2 + """ + self._GMT_type = 'fill' + self._GMT_2fps = False + self._GMT_1fp = 0 + + self._GMT_psxy_fill = None + self._GMT_psxy_nodals = None + self._GMT_psxy_EVs = None + self._GMT_scaling = 1. + + self._GMT_tension_colour = 1 + self._GMT_pressure_colour = 0 + + self._update_attributes(kwargs) + + self._setup_BB() + + self._set_GMT_attributes() + + if self._GMT_type == 'fill': + self._GMT_psxy_fill.seek(0) + GMT_string = self._GMT_psxy_fill.getvalue() + elif self._GMT_type == 'lines': + self._GMT_psxy_nodals.seek(0) + GMT_string = self._GMT_psxy_nodals.getvalue() + else: + GMT_string = self._GMT_psxy_EVs.getvalue() + + return GMT_string + + def _add_2_GMT_string(self, FH_string, curve, colour): + """ + Writes coordinate pair list of given curve as string into temporal + file handler. + """ + colour_Z = colour + wstring = '> -Z%i\n' % (colour_Z) + FH_string.write(wstring) + np.savetxt(FH_string, self._GMT_scaling * curve.transpose()) + + def _set_GMT_attributes(self): + """ + Set the beachball lines and nodals as strings into a file handler. + """ + neg_nodalline = self._nodalline_negative_final_US + pos_nodalline = self._nodalline_positive_final_US + FP1_2_plot = self._FP1_final_US + FP2_2_plot = self._FP2_final_US + EV_2_plot = self._all_EV_2D_US[:, :2].transpose() + US = self._unit_sphere + + tension_colour = self._GMT_tension_colour + pressure_colour = self._GMT_pressure_colour + + # build strings for possible GMT-output, used by 'psxy' + GMT_string_FH = StringIO() + GMT_linestring_FH = StringIO() + GMT_EVs_FH = StringIO() + + self._add_2_GMT_string(GMT_EVs_FH, EV_2_plot, tension_colour) + GMT_EVs_FH.flush() + + if self._plot_clr_order > 0: + self._add_2_GMT_string(GMT_string_FH, US, pressure_colour) + self._add_2_GMT_string(GMT_string_FH, neg_nodalline, + tension_colour) + self._add_2_GMT_string(GMT_string_FH, pos_nodalline, + tension_colour) + GMT_string_FH.flush() + + if self._plot_curve_in_curve != 0: + self._add_2_GMT_string(GMT_string_FH, US, tension_colour) + + if self._plot_curve_in_curve < 1: + self._add_2_GMT_string(GMT_string_FH, neg_nodalline, + pressure_colour) + self._add_2_GMT_string(GMT_string_FH, pos_nodalline, + tension_colour) + GMT_string_FH.flush() + else: + self._add_2_GMT_string(GMT_string_FH, pos_nodalline, + pressure_colour) + self._add_2_GMT_string(GMT_string_FH, neg_nodalline, + tension_colour) + GMT_string_FH.flush() + else: + self._add_2_GMT_string(GMT_string_FH, US, tension_colour) + self._add_2_GMT_string(GMT_string_FH, neg_nodalline, + pressure_colour) + self._add_2_GMT_string(GMT_string_FH, pos_nodalline, + pressure_colour) + GMT_string_FH.flush() + + if self._plot_curve_in_curve != 0: + self._add_2_GMT_string(GMT_string_FH, US, pressure_colour) + if self._plot_curve_in_curve < 1: + self._add_2_GMT_string(GMT_string_FH, neg_nodalline, + tension_colour) + self._add_2_GMT_string(GMT_string_FH, pos_nodalline, + pressure_colour) + GMT_string_FH.flush() + else: + self._add_2_GMT_string(GMT_string_FH, pos_nodalline, + tension_colour) + self._add_2_GMT_string(GMT_string_FH, neg_nodalline, + pressure_colour) + + GMT_string_FH.flush() + + # set all nodallines and faultplanes for plotting: + self._add_2_GMT_string(GMT_linestring_FH, neg_nodalline, + tension_colour) + self._add_2_GMT_string(GMT_linestring_FH, pos_nodalline, + tension_colour) + + if self._GMT_2fps: + self._add_2_GMT_string(GMT_linestring_FH, FP1_2_plot, + tension_colour) + self._add_2_GMT_string(GMT_linestring_FH, FP2_2_plot, + tension_colour) + + elif self._GMT_1fp: + if not int(self._GMT_1fp) in [1, 2]: + print('no fault plane specified for being plotted...continue') + print('without fault plane(s)') + pass + else: + if int(self._GMT_1fp) == 1: + self._add_2_GMT_string(GMT_linestring_FH, FP1_2_plot, + tension_colour) + else: + self._add_2_GMT_string(GMT_linestring_FH, FP2_2_plot, + tension_colour) + + self._add_2_GMT_string(GMT_linestring_FH, US, tension_colour) + + GMT_linestring_FH.flush() + + setattr(self, '_GMT_psxy_nodals', GMT_linestring_FH) + setattr(self, '_GMT_psxy_fill', GMT_string_FH) + setattr(self, '_GMT_psxy_EVs', GMT_EVs_FH) + + def get_MT(self): + """ + Returns the original moment tensor object, handed over to the class at + generating this instance. + """ + return self.MT + + def full_sphere_plot(self, kwargs): + """ + Plot of the full beachball, projected on a circle with a radius 2. + """ + self._update_attributes(kwargs) + self._setup_BB() + self._aux_plot() + + def _aux_plot(self): + """ + Generates the final plot of the total sphere (according to the chosen + 2D-projection. + """ + from matplotlib import interactive + import pylab as P + + P.close('all') + plotfig = P.figure(665, figsize=(self._plot_aux_plot_size, + self._plot_aux_plot_size)) + + plotfig.subplots_adjust(left=0, bottom=0, right=1, top=1) + ax = plotfig.add_subplot(111, aspect='equal') + # P.axis([-1.1,1.1,-1.1,1.1],'equal') + ax.axison = False + + EV_2_plot = getattr(self, '_all_EV' + '_final') + BV_2_plot = getattr(self, '_all_BV' + '_final').transpose() + curve_pos_2_plot = getattr(self, '_nodalline_positive' + '_final') + curve_neg_2_plot = getattr(self, '_nodalline_negative' + '_final') + FP1_2_plot = getattr(self, '_FP1' + '_final') + FP2_2_plot = getattr(self, '_FP2' + '_final') + + tension_colour = self._plot_tension_colour + pressure_colour = self._plot_pressure_colour + + if self._plot_clr_order > 0: + if self._plot_fill_flag: + + alpha = self._plot_fill_alpha * self._plot_total_alpha + ax.fill(self._outer_circle[0, :], self._outer_circle[1, :], + fc=pressure_colour, alpha=alpha) + ax.fill(curve_pos_2_plot[0, :], curve_pos_2_plot[1, :], + fc=tension_colour, alpha=alpha) + ax.fill(curve_neg_2_plot[0, :], curve_neg_2_plot[1, :], + fc=tension_colour, alpha=alpha) + + if self._plot_curve_in_curve != 0: + ax.fill(self._outer_circle[0, :], self._outer_circle[1, :], + fc=tension_colour, alpha=alpha) + if self._plot_curve_in_curve < 1: + ax.fill(curve_neg_2_plot[0, :], curve_neg_2_plot[1, :], + fc=pressure_colour, alpha=alpha) + ax.fill(curve_pos_2_plot[0, :], curve_pos_2_plot[1, :], + fc=tension_colour, alpha=alpha) + else: + ax.fill(curve_pos_2_plot[0, :], curve_pos_2_plot[1, :], + fc=pressure_colour, alpha=alpha) + ax.fill(curve_neg_2_plot[0, :], curve_neg_2_plot[1, :], + fc=tension_colour, alpha=alpha) + + if self._plot_show_princ_axes: + alpha = self._plot_princ_axes_alpha * self._plot_total_alpha + ax.plot([EV_2_plot[0, 0]], [EV_2_plot[1, 0]], 'm^', + ms=self._plot_princ_axes_symsize, + lw=self._plot_princ_axes_lw, alpha=alpha) + ax.plot([EV_2_plot[0, 3]], [EV_2_plot[1, 3]], 'mv', + ms=self._plot_princ_axes_symsize, + lw=self._plot_princ_axes_lw, alpha=alpha) + ax.plot([EV_2_plot[0, 1]], [EV_2_plot[1, 1]], 'b^', + ms=self._plot_princ_axes_symsize, + lw=self._plot_princ_axes_lw, alpha=alpha) + ax.plot([EV_2_plot[0, 4]], [EV_2_plot[1, 4]], 'bv', + ms=self._plot_princ_axes_symsize, + lw=self._plot_princ_axes_lw, alpha=alpha) + ax.plot([EV_2_plot[0, 2]], [EV_2_plot[1, 2]], 'g^', + ms=self._plot_princ_axes_symsize, + lw=self._plot_princ_axes_lw, alpha=alpha) + ax.plot([EV_2_plot[0, 5]], [EV_2_plot[1, 5]], 'gv', + ms=self._plot_princ_axes_symsize, + lw=self._plot_princ_axes_lw, alpha=alpha) + else: + if self._plot_fill_flag: + alpha = self._plot_fill_alpha * self._plot_total_alpha + ax.fill(self._outer_circle[0, :], self._outer_circle[1, :], + fc=tension_colour, alpha=alpha) + ax.fill(curve_pos_2_plot[0, :], curve_pos_2_plot[1, :], + fc=pressure_colour, alpha=alpha) + ax.fill(curve_neg_2_plot[0, :], curve_neg_2_plot[1, :], + fc=pressure_colour, alpha=alpha) + + if self._plot_curve_in_curve != 0: + ax.fill(self._outer_circle[0, :], self._outer_circle[1, :], + fc=pressure_colour, alpha=alpha) + if self._plot_curve_in_curve < 0: + ax.fill(curve_neg_2_plot[0, :], curve_neg_2_plot[1, :], + fc=tension_colour, alpha=alpha) + ax.fill(curve_pos_2_plot[0, :], curve_pos_2_plot[1, :], + fc=pressure_colour, alpha=alpha) + pass + else: + ax.fill(curve_pos_2_plot[0, :], curve_pos_2_plot[1, :], + fc=tension_colour, alpha=alpha) + ax.fill(curve_neg_2_plot[0, :], curve_neg_2_plot[1, :], + fc=pressure_colour, alpha=alpha) + pass + + if self._plot_show_princ_axes: + alpha = self._plot_princ_axes_alpha * self._plot_total_alpha + ax.plot([EV_2_plot[0, 0]], [EV_2_plot[1, 0]], 'g^', + ms=self._plot_princ_axes_symsize, + lw=self._plot_princ_axes_lw, alpha=alpha) + ax.plot([EV_2_plot[0, 3]], [EV_2_plot[1, 3]], 'gv', + ms=self._plot_princ_axes_symsize, + lw=self._plot_princ_axes_lw, alpha=alpha) + ax.plot([EV_2_plot[0, 1]], [EV_2_plot[1, 1]], 'b^', + ms=self._plot_princ_axes_symsize, + lw=self._plot_princ_axes_lw, alpha=alpha) + ax.plot([EV_2_plot[0, 4]], [EV_2_plot[1, 4]], 'bv', + ms=self._plot_princ_axes_symsize, + lw=self._plot_princ_axes_lw, alpha=alpha) + ax.plot([EV_2_plot[0, 2]], [EV_2_plot[1, 2]], 'm^', + ms=self._plot_princ_axes_symsize, + lw=self._plot_princ_axes_lw, alpha=alpha) + ax.plot([EV_2_plot[0, 5]], [EV_2_plot[1, 5]], 'mv', + ms=self._plot_princ_axes_symsize, + lw=self._plot_princ_axes_lw, alpha=alpha) + + self._plot_nodalline_colour = 'y' + + ax.plot(curve_neg_2_plot[0, :], curve_neg_2_plot[1, :], 'o', + c=self._plot_nodalline_colour, lw=self._plot_nodalline_width, + alpha=self._plot_nodalline_alpha * self._plot_total_alpha, + ms=3) + + self._plot_nodalline_colour = 'b' + + ax.plot(curve_pos_2_plot[0, :], curve_pos_2_plot[1, :], 'D', + c=self._plot_nodalline_colour, lw=self._plot_nodalline_width, + alpha=self._plot_nodalline_alpha * self._plot_total_alpha, + ms=3) + + if self._plot_show_1faultplane: + if self._plot_show_FP_index == 1: + ax.plot(FP1_2_plot[0, :], FP1_2_plot[1, :], '+', + c=self._plot_faultplane_colour, + lw=self._plot_faultplane_width, + alpha=self._plot_faultplane_alpha * + self._plot_total_alpha, ms=5) + elif self._plot_show_FP_index == 2: + ax.plot(FP2_2_plot[0, :], FP2_2_plot[1, :], '+', + c=self._plot_faultplane_colour, + lw=self._plot_faultplane_width, + alpha=self._plot_faultplane_alpha * + self._plot_total_alpha, ms=5) + + elif self._plot_show_faultplanes: + ax.plot(FP1_2_plot[0, :], FP1_2_plot[1, :], '+', + c=self._plot_faultplane_colour, + lw=self._plot_faultplane_width, + alpha=self._plot_faultplane_alpha * self._plot_total_alpha, + ms=4) + ax.plot(FP2_2_plot[0, :], FP2_2_plot[1, :], '+', + c=self._plot_faultplane_colour, + lw=self._plot_faultplane_width, + alpha=self._plot_faultplane_alpha * self._plot_total_alpha, + ms=4) + else: + pass + + # if isotropic part shall be displayed, fill the circle completely with + # the appropriate colour + if self._pure_isotropic: + if abs(np.trace(self._M)) > epsilon: + if self._plot_clr_order < 0: + ax.fill(self._outer_circle[0, :], self._outer_circle[1, :], + fc=tension_colour, alpha=1, zorder=100) + else: + ax.fill(self._outer_circle[0, :], self._outer_circle[1, :], + fc=pressure_colour, alpha=1, zorder=100) + + # plot NED basis vectors + if self._plot_show_basis_axes: + plot_size_in_points = self._plot_size * 2.54 * 72 + points_per_unit = plot_size_in_points / 2. + + fontsize = plot_size_in_points / 66. + symsize = plot_size_in_points / 77. + + direction_letters = list('NSEWDU') + for idx, val in enumerate(BV_2_plot): + x_coord = val[0] + y_coord = val[1] + np_letter = direction_letters[idx] + + rot_angle = -np.arctan2(y_coord, x_coord) + pi / 2. + original_rho = np.sqrt(x_coord ** 2 + y_coord ** 2) + + marker_x = (original_rho - (3 * symsize / points_per_unit)) * \ + np.sin(rot_angle) + marker_y = (original_rho - (3 * symsize / points_per_unit)) * \ + np.cos(rot_angle) + annot_x = (original_rho - (8.5 * fontsize / points_per_unit)) \ + * np.sin(rot_angle) + annot_y = (original_rho - (8.5 * fontsize / points_per_unit)) \ + * np.cos(rot_angle) + + ax.text(annot_x, annot_y, np_letter, + horizontalalignment='center', size=fontsize, + weight='bold', verticalalignment='center', + bbox=dict(edgecolor='white', facecolor='white', + alpha=1)) + + if original_rho > epsilon: + ax.scatter([marker_x], [marker_y], + marker=(3, 0, rot_angle), s=symsize ** 2, c='k', + facecolor='k', zorder=300) + else: + ax.scatter([x_coord], [y_coord], marker=(4, 1, rot_angle), + s=symsize ** 2, c='k', facecolor='k', + zorder=300) + + # plot both circle lines (radius 1 and 2) + ax.plot(self._unit_sphere[0, :], self._unit_sphere[1, :], + c=self._plot_outerline_colour, lw=self._plot_outerline_width, + alpha=self._plot_outerline_alpha * self._plot_total_alpha) + ax.plot(self._outer_circle[0, :], self._outer_circle[1, :], + c=self._plot_outerline_colour, lw=self._plot_outerline_width, + alpha=self._plot_outerline_alpha * self._plot_total_alpha) + + # dummy points for setting plot plot size more accurately + ax.plot([0, 2.1, 0, -2.1], [2.1, 0, -2.1, 0], ',', alpha=0.) + + ax.autoscale_view(tight=True, scalex=True, scaley=True) + interactive(True) + + if self._plot_save_plot: + try: + plotfig.savefig(self._plot_outfile + '.' + + self._plot_outfile_format, dpi=self._plot_dpi, + transparent=True, + format=self._plot_outfile_format) + except: + print('saving of plot not possible') + + P.show() + + def pa_plot(self, kwargs): + """ + Plot of the solution in the principal axes system. + """ + import pylab as P + + self._update_attributes(kwargs) + + r_hor = self._r_hor_for_pa_plot + r_hor_FP = self._r_hor_FP_for_pa_plot + + P.rc('grid', color='#316931', linewidth=0.5, linestyle='-.') + P.rc('xtick', labelsize=12) + P.rc('ytick', labelsize=10) + + width, height = P.rcParams['figure.figsize'] + size = min(width, height) + + fig = P.figure(34, figsize=(size, size)) + P.clf() + ax = fig.add_axes([0.1, 0.1, 0.8, 0.8], polar=True, axisbg='#d5de9c') + + r_steps = [0.000001] + for i in (np.arange(4) + 1) * 0.2: + r_steps.append(i) + r_labels = ['S'] + for ii in range(len(r_steps)): + if (ii + 1) % 2 == 0: + r_labels.append(str(r_steps[ii])) + else: + r_labels.append(' ') + + t_angles = np.arange(0., 360., 90) + t_labels = [' N ', ' H ', ' - N', ' - H'] + + P.thetagrids(t_angles, labels=t_labels) + + ax.plot(self._phi_curve, r_hor, color='r', lw=3) + ax.plot(self._phi_curve, r_hor_FP, color='b', lw=1.5) + ax.set_rmax(1.0) + P.grid(True) + + P.rgrids((r_steps), labels=r_labels) + + ax.set_title("beachball in eigenvector system", fontsize=15) + + if self._plot_save_plot: + try: + fig.savefig(self._plot_outfile + '.' + + self._plot_outfile_format, dpi=self._plot_dpi, + transparent=True, + format=self._plot_outfile_format) + except: + print('saving of plot not possible') + P.show() + + def _set_standard_attributes(self): + """ + Sets default values of mandatory arguments. + """ + # plot basis system and view point: + self._plot_basis = 'NED' + self._plot_projection = 'lambert' + self._plot_viewpoint = [0., 0., 0.] + self._plot_basis_change = None + + # flag, if upper hemisphere is seen instead + self._plot_show_upper_hemis = False + + # flag, if isotropic part shall be considered + self._plot_isotropic_part = False + self._pure_isotropic = False + + # number of minimum points per line and full circle (number/360 is + # minimum of points per degree at rounded lines) + self._plot_n_points = 360 + + # nodal line of pressure and tension regimes: + self._plot_nodalline_width = 2 + self._plot_nodalline_colour = 'k' + self._plot_nodalline_alpha = 1. + + # outer circle line + self._plot_outerline_width = 2 + self._plot_outerline_colour = 'k' + self._plot_outerline_alpha = 1. + + # faultplane(s) + self._plot_faultplane_width = 4 + self._plot_faultplane_colour = 'b' + self._plot_faultplane_alpha = 1. + + self._plot_show_faultplanes = False + self._plot_show_1faultplane = False + self._plot_show_FP_index = 1 + + # principal axes: + self._plot_show_princ_axes = False + self._plot_princ_axes_symsize = 10 + self._plot_princ_axes_lw = 3 + self._plot_princ_axes_alpha = 0.5 + + # NED basis: + self._plot_show_basis_axes = False + + # filling of the area: + self._plot_clr_order = self.MT.get_colour_order() + self._plot_curve_in_curve = 0 + self._plot_fill_flag = True + self._plot_tension_colour = 'r' + self._plot_pressure_colour = 'w' + self._plot_fill_alpha = 1. + + # general plot options + self._plot_size = 5 + self._plot_aux_plot_size = 5 + self._plot_dpi = 200 + + self._plot_total_alpha = 1. + + # possibility to add external data (e.g. measured polariations) + self._plot_external_data = False + self._external_data = None + + # if, howto, whereto save the plot + self._plot_save_plot = False + self._plot_outfile = './BB_plot_example' + self._plot_outfile_format = 'svg' + + def _update_attributes(self, kwargs): + """ + Makes an internal update of the object's attributes with the + provided list of keyword arguments. + + If the keyword (extended by a leading _ ) is in the dict of + the object, the value is updated. Otherwise, the keyword is + ignored. + """ + for key in kwargs.keys(): + if key[0] == '_': + kw = key[1:] + else: + kw = key + if '_' + kw in dir(self): + setattr(self, '_' + kw, kwargs[key]) + + if kwargs.get('plot_only_lines', False): + setattr(self, '_plot_fill_flag', False) + + def _setup_BB(self, unit_circle=True): + """ + Setup of the beachball, when a plotting method is evoked. + + Contains all the technical stuff for generating the final view of the + beachball: + + - Finding a rotation matrix, describing the given viewpoint onto the + beachball projection + - Rotating all elements (lines, points) w.r.t. the given viewpoint + - Projecting the 3D sphere into the 2D plane + - Building circle lines in radius r=1 and r=2 + - Correct the order of line points, yielding a consecutive set of + points for drawing lines + - Smoothing of all curves, avoiding nasty sectioning connection lines + - Checking, if the two nodalline curves are laying completely within + each other ( cahnges plotting order of overlay plot construction) + - Projection of final smooth solution onto the standard unit sphere + """ + self._find_basis_change_2_new_viewpoint() + self._rotate_all_objects_2_new_view() + self._vertical_2D_projection() + + if unit_circle: + self._build_circles() + + if not self.MT._iso_percentage == 100: + self._correct_curves() + self._smooth_curves() + self._check_curve_in_curve() + + self._projection_2_unit_sphere() + + if self.MT._iso_percentage == 100: + if np.trace(self.MT.get_M()) < 0: + self._plot_clr_order = 1 + else: + self._plot_clr_order = -1 + + def _correct_curves(self): + """ + Correcting potentially wrong curves. + + Checks, if the order of the given coordinates of the lines must be + re-arranged, allowing for an automatical line plotting. + """ + list_of_curves_2_correct = ['nodalline_negative', 'nodalline_positive', + 'FP1', 'FP2'] + n_curve_points = self._plot_n_points + + for obj in list_of_curves_2_correct: + obj2cor_name = '_' + obj + '_2D' + obj2cor = getattr(self, obj2cor_name) + + obj2cor_in_right_order = self._sort_curve_points(obj2cor) + + # logger.debug( 'curve: ', str(obj)) + # check, if curve closed !!!!!! + start_r = np.sqrt(obj2cor_in_right_order[0, 0] ** 2 + + obj2cor_in_right_order[1, 0] ** 2) + r_last_point = np.sqrt(obj2cor_in_right_order[0, -1] ** 2 + + obj2cor_in_right_order[1, -1] ** 2) + dist_last_first_point = \ + np.sqrt((obj2cor_in_right_order[0, -1] - + obj2cor_in_right_order[0, 0]) ** 2 + + (obj2cor_in_right_order[1, -1] - + obj2cor_in_right_order[1, 0]) ** 2) + + # check, if distance between last and first point is smaller than + # the distance between last point and the edge (at radius=2) + if dist_last_first_point > (2 - r_last_point): + # add points on edge to polygon, if it is an open curve + # logger.debug( str(obj)+' not closed - closing over edge... ') + phi_end = np.arctan2(obj2cor_in_right_order[0, -1], + obj2cor_in_right_order[1, -1]) % (2 * pi) + R_end = r_last_point + phi_start = np.arctan2(obj2cor_in_right_order[0, 0], + obj2cor_in_right_order[1, 0]) % (2 * pi) + R_start = start_r + + # add one point on the edge every fraction of degree given by + # input parameter, increase the radius linearily + phi_end_larger = np.sign(phi_end - phi_start) + angle_smaller_pi = np.sign(pi - np.abs(phi_end - phi_start)) + + if phi_end_larger * angle_smaller_pi > 0: + go_ccw = True + openangle = (phi_end - phi_start) % (2 * pi) + else: + go_ccw = False + openangle = (phi_start - phi_end) % (2 * pi) + + radius_interval = R_start - R_end # closing from end to start + + n_edgepoints = int(openangle * rad2deg * + n_curve_points / 360.) - 1 + # logger.debug( 'open angle %.2f degrees - filling with %i + # points on the edge\n'%(openangle/pi*180,n_edgepoints)) + if go_ccw: + obj2cor_in_right_order = \ + list(obj2cor_in_right_order.transpose()) + for kk in range(n_edgepoints + 1): + current_phi = phi_end - kk * openangle / \ + (n_edgepoints + 1) + current_radius = R_end + kk * radius_interval / \ + (n_edgepoints + 1) + temp = [current_radius * math.sin(current_phi), + current_radius * np.cos(current_phi)] + obj2cor_in_right_order.append(temp) + obj2cor_in_right_order = \ + np.array(obj2cor_in_right_order).transpose() + else: + obj2cor_in_right_order = \ + list(obj2cor_in_right_order.transpose()) + for kk in range(n_edgepoints + 1): + current_phi = phi_end + kk * openangle / \ + (n_edgepoints + 1) + current_radius = R_end + kk * radius_interval / \ + (n_edgepoints + 1) + temp = [current_radius * math.sin(current_phi), + current_radius * np.cos(current_phi)] + obj2cor_in_right_order.append(temp) + obj2cor_in_right_order = \ + np.array(obj2cor_in_right_order).transpose() + setattr(self, '_' + obj + '_in_order', obj2cor_in_right_order) + return 1 + + def _nodallines_in_NED_system(self): + """ + The two nodal lines between the areas on a beachball are given by the + points, where tan²(alpha) = (-EWs/(EWN*cos(phi)**2 + EWh*sin(phi)**2)) + is fulfilled. + + This solution is gained in the principal axes system and then expressed + in terms of the NED basis system + + output: + - set of points, building the first nodal line, coordinates in the + input basis system (standard NED) + - set of points, building the second nodal line, coordinates in the + input basis system (standard NED) + - array with 6 points, describing positive and negative part of 3 + principal axes + - array with partition of full circle (angle values in degrees) + fraction is given by parametre n_curve_points + """ + # build the nodallines of positive/negative areas in the principal axes + # system + n_curve_points = self._plot_n_points + + # phi is the angle between neutral axis and horizontal projection + # of the curve point to the surface, spanned by H- and + # N-axis. Running mathematically negative (clockwise) around the + # SIGMA-axis. Stepsize is given by the parametre for number of + # curve points + phi = (np.arange(n_curve_points) / float(n_curve_points) + + 1. / n_curve_points) * 2 * pi + self._phi_curve = phi + + # analytical/geometrical solution for separatrix curve - alpha is + # opening angle between principal axis SIGMA and point of curve. (alpha + # is 0, if curve lies directly on the SIGMA axis) + + # CASE: including isotropic part + # sigma axis flippes, if EWn flippes sign + + EWh_devi = self.MT.get_eigvals()[0] - 1. / 3 * np.trace(self._M) + EWn_devi = self.MT.get_eigvals()[1] - 1. / 3 * np.trace(self._M) + EWs_devi = self.MT.get_eigvals()[2] - 1. / 3 * np.trace(self._M) + + if not self._plot_isotropic_part: + EWh = EWh_devi + EWn = EWn_devi + EWs = EWs_devi + else: + EWh_tmp = self.MT.get_eigvals()[0] + EWn_tmp = self.MT.get_eigvals()[1] + EWs_tmp = self.MT.get_eigvals()[2] + + trace_m = np.sum(self.MT.get_eigvals()) + EWh = EWh_tmp.copy() + EWs = EWs_tmp.copy() + + if trace_m != 0: + if (self._plot_clr_order > 0 and EWn_tmp >= 0 and + abs(EWs_tmp) > abs(EWh_tmp)) or \ + (self._plot_clr_order < 0 and + EWn_tmp <= 0 and abs(EWs_tmp) > abs(EWh_tmp)): + + EWs = EWh_tmp.copy() + EWh = EWs_tmp.copy() + print('changed order!!\n') + EVs_tmp = self.MT._rotation_matrix[:, 2].copy() + EVh_tmp = self.MT._rotation_matrix[:, 0].copy() + + self.MT._rotation_matrix[:, 0] = EVs_tmp + self.MT._rotation_matrix[:, 2] = EVh_tmp + self._plot_clr_order *= -1 + + EWn = EWn_tmp.copy() + + if abs(EWn) < epsilon: + EWn = 0 + norm_factor = max(np.abs([EWh, EWn, EWs])) + + [EWh, EWn, EWs] = [xx / norm_factor for xx in [EWh, EWn, EWs]] + + RHS = -EWs / (EWn * np.cos(phi) ** 2 + EWh * np.sin(phi) ** 2) + + if np.all([np.sign(xx) >= 0 for xx in RHS]): + alpha = np.arctan(np.sqrt(RHS)) * rad2deg + else: + alpha = phi.copy() + alpha[:] = 90 + self._pure_isotropic = 1 + + # fault planes: + RHS_FP = 1. / (np.sin(phi) ** 2) + alpha_FP = np.arctan(np.sqrt(RHS_FP)) * rad2deg + + # horizontal coordinates of curves + r_hor = np.sin(alpha / rad2deg) + r_hor_FP = np.sin(alpha_FP / rad2deg) + + self._r_hor_for_pa_plot = r_hor + self._r_hor_FP_for_pa_plot = r_hor_FP + + H_values = np.sin(phi) * r_hor + N_values = np.cos(phi) * r_hor + H_values_FP = np.sin(phi) * r_hor_FP + N_values_FP = np.cos(phi) * r_hor_FP + + # set vertical value of curve point coordinates - two symmetric curves + # exist + S_values_positive = np.cos(alpha / rad2deg) + S_values_negative = -np.cos(alpha / rad2deg) + S_values_positive_FP = np.cos(alpha_FP / rad2deg) + S_values_negative_FP = -np.cos(alpha_FP / rad2deg) + + # change basis back to original input reference system + chng_basis = self.MT._rotation_matrix + + line_tuple_pos = np.zeros((3, n_curve_points)) + line_tuple_neg = np.zeros((3, n_curve_points)) + + for ii in range(n_curve_points): + pos_vec_in_EV_basis = np.array([H_values[ii], N_values[ii], + S_values_positive[ii]]).transpose() + neg_vec_in_EV_basis = np.array([H_values[ii], N_values[ii], + S_values_negative[ii]]).transpose() + line_tuple_pos[:, ii] = np.dot(chng_basis, pos_vec_in_EV_basis) + line_tuple_neg[:, ii] = np.dot(chng_basis, neg_vec_in_EV_basis) + + EVh = self.MT.get_eigvecs()[0] + EVn = self.MT.get_eigvecs()[1] + EVs = self.MT.get_eigvecs()[2] + + all_EV = np.zeros((3, 6)) + + all_EV[:, 0] = EVh.transpose() + all_EV[:, 1] = EVn.transpose() + all_EV[:, 2] = EVs.transpose() + all_EV[:, 3] = -EVh.transpose() + all_EV[:, 4] = -EVn.transpose() + all_EV[:, 5] = -EVs.transpose() + + # basis vectors: + all_BV = np.zeros((3, 6)) + all_BV[:, 0] = np.array((1, 0, 0)) + all_BV[:, 1] = np.array((-1, 0, 0)) + all_BV[:, 2] = np.array((0, 1, 0)) + all_BV[:, 3] = np.array((0, -1, 0)) + all_BV[:, 4] = np.array((0, 0, 1)) + all_BV[:, 5] = np.array((0, 0, -1)) + + # re-sort the two 90 degree nodal lines to 2 fault planes - cut each at + # halves and merge pairs + # additionally change basis system to NED reference system + + midpoint_idx = int(n_curve_points / 2.) + + FP1 = np.zeros((3, n_curve_points)) + FP2 = np.zeros((3, n_curve_points)) + + for ii in range(midpoint_idx): + FP1_vec = np.array([H_values_FP[ii], N_values_FP[ii], + S_values_positive_FP[ii]]).transpose() + FP2_vec = np.array([H_values_FP[ii], N_values_FP[ii], + S_values_negative_FP[ii]]).transpose() + FP1[:, ii] = np.dot(chng_basis, FP1_vec) + FP2[:, ii] = np.dot(chng_basis, FP2_vec) + + for jj in range(midpoint_idx): + ii = n_curve_points - jj - 1 + + FP1_vec = np.array([H_values_FP[ii], N_values_FP[ii], + S_values_negative_FP[ii]]).transpose() + FP2_vec = np.array([H_values_FP[ii], N_values_FP[ii], + S_values_positive_FP[ii]]).transpose() + FP1[:, ii] = np.dot(chng_basis, FP1_vec) + FP2[:, ii] = np.dot(chng_basis, FP2_vec) + + # identify with faultplane index, gotten from 'get_fps': + self._FP1 = FP1 + self._FP2 = FP2 + + self._all_EV = all_EV + self._all_BV = all_BV + self._nodalline_negative = line_tuple_neg + self._nodalline_positive = line_tuple_pos + + def _identify_faultplanes(self): + """ + See, if the 2 faultplanes, given as attribute of the moment + tensor object, handed to this instance, are consistent with + the faultplane lines, obtained from the basis solution. If + not, interchange the indices of the newly found ones. + """ + # TODO !!!!!! + pass + + def _find_basis_change_2_new_viewpoint(self): + """ + Finding the Eulerian angles, if you want to rotate an object. + + Your original view point is the position (0,0,0). Input are the + coordinates of the new point of view, equivalent to geographical + coordinates. + + Example: + + Original view onto the Earth is from right above lat=0, lon=0 with + north=upper edge, south=lower edge. Now you want to see the Earth + from a position somewhere near Baku. So lat=45, + lon=45, azimuth=0. + + The Earth must be rotated around some axis, not to be determined. + The rotation matrixx is the matrix for the change of basis to the + new local orthonormal system. + + input: + - latitude in degrees from -90 (south) to 90 (north) + - longitude in degrees from -180 (west) to 180 (east) + - azimuth in degrees from 0 (heading north) to 360 (north again) + """ + + new_latitude = self._plot_viewpoint[0] + new_longitude = self._plot_viewpoint[1] + new_azimuth = self._plot_viewpoint[2] + + s_lat = np.sin(new_latitude / rad2deg) + if abs(s_lat) < epsilon: + s_lat = 0 + c_lat = np.cos(new_latitude / rad2deg) + if abs(c_lat) < epsilon: + c_lat = 0 + s_lon = np.sin(new_longitude / rad2deg) + if abs(s_lon) < epsilon: + s_lon = 0 + c_lon = np.cos(new_longitude / rad2deg) + if abs(c_lon) < epsilon: + c_lon = 0 + + # assume input basis as NED!!! + + # original point of view therein is (0,0,-1) + # new point at lat=latitude, lon=longitude, az=0, given in old + # NED-coordinates: + # (cos(latitude), sin(latitude)*sin(longitude), + # sin(latitude)*cos(longitude) ) + # + # new " down' " is given by the negative position vector, so pointing + # inwards to the centre point + # down_prime = - ( np.array( ( s_lat, c_lat*c_lon, -c_lat*s_lon ) ) ) + down_prime = -(np.array((s_lat, c_lat * s_lon, -c_lat * c_lon))) + + #normalise: + down_prime /= np.sqrt(np.dot(down_prime, down_prime)) + + # get second local basis vector " north' " by orthogonalising + # (Gram-Schmidt method) the original north w.r.t. the new " down' " + north_prime_not_normalised = np.array((1., 0., 0.)) - \ + (np.dot(down_prime, np.array((1., 0., 0.))) / + (np.dot(down_prime, down_prime)) * down_prime) + + len_north_prime_not_normalised = \ + np.sqrt(np.dot(north_prime_not_normalised, + north_prime_not_normalised)) + # check for poles: + if np.abs(len_north_prime_not_normalised) < epsilon: + # case: north pole + if s_lat > 0: + north_prime = np.array((0., 0., 1.)) + # case: south pole + else: + north_prime = np.array((0., 0., -1.)) + else: + north_prime = \ + north_prime_not_normalised / len_north_prime_not_normalised + + # third basis vector is obtained by a cross product of the first two + east_prime = np.cross(down_prime, north_prime) + + # normalise: + east_prime /= np.sqrt(np.dot(east_prime, east_prime)) + + rotmat_pos_raw = np.zeros((3, 3)) + rotmat_pos_raw[:, 0] = north_prime + rotmat_pos_raw[:, 1] = east_prime + rotmat_pos_raw[:, 2] = down_prime + + rotmat_pos = np.asmatrix(rotmat_pos_raw).T + # this matrix gives the coordinates of a given point in the old + # coordinates w.r.t. the new system + + # up to here, only the position has changed, the angle of view + # (azimuth) has to be added by an additional rotation around the + # down'-axis (in the frame of the new coordinates) + # set up the local rotation around the new down'-axis by the given + # angle 'azimuth'. Positive values turn view counterclockwise from the + # new north' + only_rotation = np.zeros((3, 3)) + s_az = np.sin(new_azimuth / rad2deg) + if abs(s_az) < epsilon: + s_az = 0. + c_az = np.cos(new_azimuth / rad2deg) + if abs(c_az) < epsilon: + c_az = 0. + + only_rotation[2, 2] = 1 + only_rotation[0, 0] = c_az + only_rotation[1, 1] = c_az + only_rotation[0, 1] = -s_az + only_rotation[1, 0] = s_az + + local_rotation = np.asmatrix(only_rotation) + + # apply rotation from left!! + total_rotation_matrix = np.dot(local_rotation, rotmat_pos) + + # yields the complete matrix for representing the old coordinates in + # the new (rotated) frame: + self._plot_basis_change = total_rotation_matrix + + def _rotate_all_objects_2_new_view(self): + """ + Rotate all relevant parts of the solution - namely the + eigenvector-projections, the 2 nodallines, and the faultplanes + - so that they are seen from the new viewpoint. + """ + objects_2_rotate = ['all_EV', 'all_BV', 'nodalline_negative', + 'nodalline_positive', 'FP1', 'FP2'] + + for obj in objects_2_rotate: + object2rotate = getattr(self, '_' + obj).transpose() + + rotated_thing = object2rotate.copy() + for i in range(len(object2rotate)): + rotated_thing[i] = np.dot(self._plot_basis_change, + object2rotate[i]) + + rotated_object = rotated_thing.copy() + setattr(self, '_' + obj + '_rotated', rotated_object.transpose()) + + def _vertical_2D_projection(self): + """ + Start the vertical projection of the 3D beachball onto the 2D plane. + The projection is chosen according to the attribute '_plot_projection' + """ + list_of_possible_projections = ['stereo', 'ortho', 'lambert', 'gnom'] + + if not self._plot_projection in list_of_possible_projections: + print('desired projection not possible - choose from:\n ') + print(list_of_possible_projections) + raise MTError(' !! ') + + if self._plot_projection == 'stereo': + if not self._stereo_vertical(): + print('ERROR in stereo_vertical') + raise MTError(' !! ') + elif self._plot_projection == 'ortho': + if not self._orthographic_vertical(): + print('ERROR in stereo_vertical') + raise MTError(' !! ') + elif self._plot_projection == 'lambert': + if not self._lambert_vertical(): + print('ERROR in stereo_vertical') + raise MTError(' !! ') + elif self._plot_projection == 'gnom': + if not self._gnomonic_vertical(): + print('ERROR in stereo_vertical') + raise MTError(' !! ') + + def _stereo_vertical(self): + """ + Stereographic/azimuthal conformal 2D projection onto a plane, tangent + to the lowest point (0,0,1). + + Keeps the angles constant! + + The parts in the lower hemisphere are projected to the unit + sphere, the upper half to an annular region between radii r=1 + and r=2. If the attribute '_show_upper_hemis' is set, the + projection is reversed. + """ + objects_2_project = ['all_EV', 'all_BV', 'nodalline_negative', + 'nodalline_positive', 'FP1', 'FP2'] + + available_coord_systems = ['NED'] + + if not self._plot_basis in available_coord_systems: + print('desired plotting projection not possible - choose from :\n') + print(available_coord_systems) + raise MTError(' !! ') + + plot_upper_hem = self._plot_show_upper_hemis + + for obj in objects_2_project: + obj_name = '_' + obj + '_rotated' + o2proj = getattr(self, obj_name) + coords = o2proj.copy() + + n_points = len(o2proj[0, :]) + stereo_coords = np.zeros((2, n_points)) + + for ll in range(n_points): + # second component is EAST + co_x = coords[1, ll] + # first component is NORTH + co_y = coords[0, ll] + # z given in DOWN + co_z = -coords[2, ll] + + rho_hor = np.sqrt(co_x ** 2 + co_y ** 2) + + if rho_hor == 0: + new_y = 0 + new_x = 0 + if plot_upper_hem: + if co_z < 0: + new_x = 2 + else: + if co_z > 0: + new_x = 2 + else: + if co_z < 0: + new_rho = rho_hor / (1. - co_z) + if plot_upper_hem: + new_rho = 2 - (rho_hor / (1. - co_z)) + + new_x = co_x / rho_hor * new_rho + new_y = co_y / rho_hor * new_rho + else: + new_rho = 2 - (rho_hor / (1. + co_z)) + if plot_upper_hem: + new_rho = rho_hor / (1. + co_z) + + new_x = co_x / rho_hor * new_rho + new_y = co_y / rho_hor * new_rho + + stereo_coords[0, ll] = new_x + stereo_coords[1, ll] = new_y + + setattr(self, '_' + obj + '_2D', stereo_coords) + setattr(self, '_' + obj + '_final', stereo_coords) + + return 1 + + def _orthographic_vertical(self): + """ + Orthographic 2D projection onto a plane, tangent to the lowest + point (0,0,1). + + Shows the natural view on a 2D sphere from large distances (assuming + parallel projection) + + The parts in the lower hemisphere are projected to the unit + sphere, the upper half to an annular region between radii r=1 + and r=2. If the attribute '_show_upper_hemis' is set, the + projection is reversed. + """ + + objects_2_project = ['all_EV', 'all_BV', 'nodalline_negative', + 'nodalline_positive', 'FP1', 'FP2'] + + available_coord_systems = ['NED'] + + if not self._plot_basis in available_coord_systems: + print('desired plotting projection not possible - choose from :\n') + print(available_coord_systems) + raise MTError(' !! ') + + plot_upper_hem = self._plot_show_upper_hemis + + for obj in objects_2_project: + obj_name = '_' + obj + '_rotated' + o2proj = getattr(self, obj_name) + coords = o2proj.copy() + + n_points = len(o2proj[0, :]) + coords2D = np.zeros((2, n_points)) + + for ll in range(n_points): + # second component is EAST + co_x = coords[1, ll] + # first component is NORTH + co_y = coords[0, ll] + # z given in DOWN + co_z = -coords[2, ll] + + rho_hor = np.sqrt(co_x ** 2 + co_y ** 2) + + if rho_hor == 0: + new_y = 0 + new_x = 0 + if plot_upper_hem: + if co_z < 0: + new_x = 2 + else: + if co_z > 0: + new_x = 2 + else: + if co_z < 0: + new_rho = rho_hor + if plot_upper_hem: + new_rho = 2 - rho_hor + + new_x = co_x / rho_hor * new_rho + new_y = co_y / rho_hor * new_rho + else: + new_rho = 2 - rho_hor + if plot_upper_hem: + new_rho = rho_hor + + new_x = co_x / rho_hor * new_rho + new_y = co_y / rho_hor * new_rho + + coords2D[0, ll] = new_x + coords2D[1, ll] = new_y + + setattr(self, '_' + obj + '_2D', coords2D) + setattr(self, '_' + obj + '_final', coords2D) + + return 1 + + def _lambert_vertical(self): + """ + Lambert azimuthal equal-area 2D projection onto a plane, tangent to the + lowest point (0,0,1). + + Keeps the area constant! + + The parts in the lower hemisphere are projected to the unit + sphere (only here the area is kept constant), the upper half to an + annular region between radii r=1 and r=2. If the attribute + '_show_upper_hemis' is set, the projection is reversed. + """ + objects_2_project = ['all_EV', 'all_BV', 'nodalline_negative', + 'nodalline_positive', 'FP1', 'FP2'] + + available_coord_systems = ['NED'] + + if not self._plot_basis in available_coord_systems: + print('desired plotting projection not possible - choose from :\n') + print(available_coord_systems) + raise MTError(' !! ') + + plot_upper_hem = self._plot_show_upper_hemis + + for obj in objects_2_project: + obj_name = '_' + obj + '_rotated' + o2proj = getattr(self, obj_name) + coords = o2proj.copy() + + n_points = len(o2proj[0, :]) + coords2D = np.zeros((2, n_points)) + + for ll in range(n_points): + # second component is EAST + co_x = coords[1, ll] + # first component is NORTH + co_y = coords[0, ll] + # z given in DOWN + co_z = -coords[2, ll] + + rho_hor = np.sqrt(co_x ** 2 + co_y ** 2) + + if rho_hor == 0: + new_y = 0 + new_x = 0 + if plot_upper_hem: + if co_z < 0: + new_x = 2 + else: + if co_z > 0: + new_x = 2 + else: + if co_z < 0: + new_rho = rho_hor / np.sqrt(1. - co_z) + + if plot_upper_hem: + new_rho = 2 - (rho_hor / np.sqrt(1. - co_z)) + + new_x = co_x / rho_hor * new_rho + new_y = co_y / rho_hor * new_rho + + else: + new_rho = 2 - (rho_hor / np.sqrt(1. + co_z)) + + if plot_upper_hem: + new_rho = rho_hor / np.sqrt(1. + co_z) + + new_x = co_x / rho_hor * new_rho + new_y = co_y / rho_hor * new_rho + + coords2D[0, ll] = new_x + coords2D[1, ll] = new_y + + setattr(self, '_' + obj + '_2D', coords2D) + setattr(self, '_' + obj + '_final', coords2D) + + return 1 + + def _gnomonic_vertical(self): + """ + Gnomonic 2D projection onto a plane, tangent to the lowest + point (0,0,1). + + Keeps the great circles as straight lines (geodetics constant) ! + + The parts in the lower hemisphere are projected to the unit + sphere, the upper half to an annular region between radii r=1 + and r=2. If the attribute '_show_upper_hemis' is set, the + projection is reversed. + """ + + objects_2_project = ['all_EV', 'all_BV', 'nodalline_negative', + 'nodalline_positive', 'FP1', 'FP2'] + + available_coord_systems = ['NED'] + + if not self._plot_basis in available_coord_systems: + print('desired plotting projection not possible - choose from :\n') + print(available_coord_systems) + raise MTError(' !! ') + + plot_upper_hem = self._plot_show_upper_hemis + + for obj in objects_2_project: + obj_name = '_' + obj + '_rotated' + o2proj = getattr(self, obj_name) + coords = o2proj.copy() + + n_points = len(o2proj[0, :]) + coords2D = np.zeros((2, n_points)) + + for ll in range(n_points): + # second component is EAST + co_x = coords[1, ll] + # first component is NORTH + co_y = coords[0, ll] + # z given in DOWN + co_z = -coords[2, ll] + + rho_hor = np.sqrt(co_x ** 2 + co_y ** 2) + + if rho_hor == 0: + new_y = 0 + new_x = 0 + if co_z > 0: + new_x = 2 + if plot_upper_hem: + new_x = 0 + else: + if co_z < 0: + new_rho = np.cos(np.arcsin(rho_hor)) * \ + np.tan(np.arcsin(rho_hor)) + + if plot_upper_hem: + new_rho = 2 - (np.cos(np.arcsin(rho_hor)) * + np.tan(np.arcsin(rho_hor))) + + new_x = co_x / rho_hor * new_rho + new_y = co_y / rho_hor * new_rho + + else: + new_rho = 2 - (np.cos(np.arcsin(rho_hor)) * + np.tan(np.arcsin(rho_hor))) + + if plot_upper_hem: + new_rho = np.cos(np.arcsin(rho_hor)) * \ + np.tan(np.arcsin(rho_hor)) + + new_x = co_x / rho_hor * new_rho + new_y = co_y / rho_hor * new_rho + + coords2D[0, ll] = new_x + coords2D[1, ll] = new_y + + setattr(self, '_' + obj + '_2D', coords2D) + setattr(self, '_' + obj + '_final', coords2D) + + return 1 + + def _build_circles(self): + """ + Sets two sets of points, describing the unit sphere and the outer + circle with r=2. + + Added as attributes '_unit_sphere' and '_outer_circle'. + """ + phi = self._phi_curve + + UnitSphere = np.zeros((2, len(phi))) + UnitSphere[0, :] = np.cos(phi) + UnitSphere[1, :] = np.sin(phi) + + # outer circle ( radius for stereographic projection is set to 2 ) + outer_circle_points = 2 * UnitSphere + + self._unit_sphere = UnitSphere + self._outer_circle = outer_circle_points + + def _sort_curve_points(self, curve): + """ + Checks, if curve points are in right order for line plotting. + + If not, a re-arranging is carried out. + """ + sorted_curve = np.zeros((2, len(curve[0, :]))) + # in polar coordinates + r_phi_curve = np.zeros((len(curve[0, :]), 2)) + for ii in range(curve.shape[1]): + r_phi_curve[ii, 0] = \ + math.sqrt(curve[0, ii] ** 2 + curve[1, ii] ** 2) + r_phi_curve[ii, 1] = \ + math.atan2(curve[0, ii], curve[1, ii]) % (2 * pi) + # find index with highest r + largest_r_idx = np.argmax(r_phi_curve[:, 0]) + + # check, if perhaps more values with same r - if so, take point with + # lowest phi + other_idces = \ + list(np.where(r_phi_curve[:, 0] == r_phi_curve[largest_r_idx, 0])) + if len(other_idces) > 1: + best_idx = np.argmin(r_phi_curve[other_idces, 1]) + start_idx_curve = other_idces[best_idx] + else: + start_idx_curve = largest_r_idx + + if not start_idx_curve == 0: + pass + + # check orientation - want to go inwards + start_r = r_phi_curve[start_idx_curve, 0] + next_idx = (start_idx_curve + 1) % len(r_phi_curve[:, 0]) + prep_idx = (start_idx_curve - 1) % len(r_phi_curve[:, 0]) + next_r = r_phi_curve[next_idx, 0] + + keep_direction = True + if next_r <= start_r: + # check, if next R is on other side of area - look at total + # distance - if yes, reverse direction + dist_first_next = \ + (curve[0, next_idx] - curve[0, start_idx_curve]) ** 2 + \ + (curve[1, next_idx] - curve[1, start_idx_curve]) ** 2 + dist_first_other = \ + (curve[0, prep_idx] - curve[0, start_idx_curve]) ** 2 + \ + (curve[1, prep_idx] - curve[1, start_idx_curve]) ** 2 + + if dist_first_next > dist_first_other: + keep_direction = False + + if keep_direction: + # direction is kept + for jj in range(curve.shape[1]): + running_idx = (start_idx_curve + jj) % len(curve[0, :]) + sorted_curve[0, jj] = curve[0, running_idx] + sorted_curve[1, jj] = curve[1, running_idx] + else: + # direction is reversed + for jj in range(curve.shape[1]): + running_idx = (start_idx_curve - jj) % len(curve[0, :]) + sorted_curve[0, jj] = curve[0, running_idx] + sorted_curve[1, jj] = curve[1, running_idx] + + # check if step of first to second point does not have large angle + # step (problem caused by projection of point (pole) onto whole + # edge - if this first angle step is larger than the one between + # points 2 and three, correct position of first point: keep R, but + # take angle with same difference as point 2 to point 3 + + angle_point_1 = (math.atan2(sorted_curve[0, 0], + sorted_curve[1, 0]) % (2 * pi)) + angle_point_2 = (math.atan2(sorted_curve[0, 1], + sorted_curve[1, 1]) % (2 * pi)) + angle_point_3 = (math.atan2(sorted_curve[0, 2], + sorted_curve[1, 2]) % (2 * pi)) + + angle_diff_23 = (angle_point_3 - angle_point_2) + if angle_diff_23 > pi: + angle_diff_23 = (-angle_diff_23) % (2 * pi) + + angle_diff_12 = (angle_point_2 - angle_point_1) + if angle_diff_12 > pi: + angle_diff_12 = (-angle_diff_12) % (2 * pi) + + if abs(angle_diff_12) > abs(angle_diff_23): + r_old = \ + math.sqrt(sorted_curve[0, 0] ** 2 + sorted_curve[1, 0] ** 2) + new_angle = (angle_point_2 - angle_diff_23) % (2 * pi) + sorted_curve[0, 0] = r_old * math.sin(new_angle) + sorted_curve[1, 0] = r_old * math.cos(new_angle) + + return sorted_curve + + def _smooth_curves(self): + """ + Corrects curves for potential large gaps, resulting in strange + intersection lines on nodals of round and irreagularly shaped + areas. + + At least one coordinte point on each degree on the circle is assured. + """ + list_of_curves_2_smooth = ['nodalline_negative', 'nodalline_positive', + 'FP1', 'FP2'] + + points_per_degree = self._plot_n_points / 360. + + for curve2smooth in list_of_curves_2_smooth: + obj_name = curve2smooth + '_in_order' + obj = getattr(self, '_' + obj_name).transpose() + + smoothed_array = np.zeros((1, 2)) + smoothed_array[0, :] = obj[0] + smoothed_list = [smoothed_array] + + # now in shape (n_points,2) + for idx, val in enumerate(obj[:-1]): + r1 = math.sqrt(val[0] ** 2 + val[1] ** 2) + r2 = math.sqrt(obj[idx + 1][0] ** 2 + obj[idx + 1][1] ** 2) + phi1 = math.atan2(val[0], val[1]) + phi2 = math.atan2(obj[idx + 1][0], obj[idx + 1][1]) + + phi2_larger = np.sign(phi2 - phi1) + angle_smaller_pi = np.sign(pi - abs(phi2 - phi1)) + + if phi2_larger * angle_smaller_pi > 0: + go_cw = True + openangle = (phi2 - phi1) % (2 * pi) + else: + go_cw = False + openangle = (phi1 - phi2) % (2 * pi) + + openangle_deg = openangle * rad2deg + radius_diff = r2 - r1 + + if openangle_deg > 1. / points_per_degree: + + n_fillpoints = int(openangle_deg * points_per_degree) + fill_array = np.zeros((n_fillpoints, 2)) + if go_cw: + angles = ((np.arange(n_fillpoints) + 1) * openangle / + (n_fillpoints + 1) + phi1) % (2 * pi) + else: + angles = (phi1 - (np.arange(n_fillpoints) + 1) * + openangle / (n_fillpoints + 1)) % (2 * pi) + + radii = (np.arange(n_fillpoints) + 1) * \ + radius_diff / (n_fillpoints + 1) + r1 + + fill_array[:, 0] = radii * np.sin(angles) + fill_array[:, 1] = radii * np.cos(angles) + + smoothed_list.append(fill_array) + + smoothed_list.append([obj[idx + 1]]) + + smoothed_array = np.vstack(smoothed_list) + setattr(self, '_' + curve2smooth + '_final', + smoothed_array.transpose()) + + def _check_curve_in_curve(self): + """ + Checks, if one of the two nodallines contains the other one + completely. If so, the order of colours is re-adapted, + assuring the correct order when doing the overlay plotting. + """ + lo_points_in_pos_curve = \ + list(self._nodalline_positive_final.transpose()) + lo_points_in_pos_curve_array = \ + self._nodalline_positive_final.transpose() + lo_points_in_neg_curve = \ + list(self._nodalline_negative_final.transpose()) + lo_points_in_neg_curve_array = \ + self._nodalline_negative_final.transpose() + + # check, if negative curve completely within positive curve + mask_neg_in_pos = 0 + for neg_point in lo_points_in_neg_curve: + mask_neg_in_pos += self._pnpoly(lo_points_in_pos_curve_array, + neg_point[:2]) + if mask_neg_in_pos > len(lo_points_in_neg_curve) - 3: + self._plot_curve_in_curve = 1 + + # check, if positive curve completely within negative curve + mask_pos_in_neg = 0 + for pos_point in lo_points_in_pos_curve: + mask_pos_in_neg += self._pnpoly(lo_points_in_neg_curve_array, + pos_point[:2]) + if mask_pos_in_neg > len(lo_points_in_pos_curve) - 3: + self._plot_curve_in_curve = -1 + + # correct for ONE special case: double couple with its + # eigensystem = NED basis system: + testarray = [1., 0, 0, 0, 1, 0, 0, 0, 1] + if np.prod(self.MT._rotation_matrix.A1 == testarray) and \ + (self.MT._eigenvalues[1] == 0): + self._plot_curve_in_curve = -1 + self._plot_clr_order = 1 + + def _point_inside_polygon(self, x, y, poly): + """ + Determine if a point is inside a given polygon or not. + + Polygon is a list of (x,y) pairs. + """ + n = len(poly) + inside = False + + p1x, p1y = poly[0] + for i in range(n + 1): + p2x, p2y = poly[i % n] + if y > min(p1y, p2y): + if y <= max(p1y, p2y): + if x <= max(p1x, p2x): + if p1y != p2y: + xinters = \ + (y - p1y) * (p2x - p1x) / (p2y - p1y) + p1x + if p1x == p2x or x <= xinters: + inside = not inside + p1x, p1y = p2x, p2y + + return inside + + def _pnpoly(self, verts, point): + """ + Check whether point is in the polygon defined by verts. + + verts - 2xN array + point - (2,) array + + See + http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html + """ + # using take instead of getitem, about ten times faster, see + # http://wesmckinney.com/blog/?p=215 + verts = np.require(verts, dtype=np.float64) + x, y = point + + xpi = verts[:, 0] + ypi = verts[:, 1] + # shift + xpj = xpi.take(self.arange_1[:xpi.size]) + ypj = ypi.take(self.arange_1[:ypi.size]) + + possible_crossings = \ + ((ypi <= y) & (y < ypj)) | ((ypj <= y) & (y < ypi)) + + xpi = xpi[possible_crossings] + ypi = ypi[possible_crossings] + xpj = xpj[possible_crossings] + ypj = ypj[possible_crossings] + + crossings = x < (xpj - xpi) * (y - ypi) / (ypj - ypi) + xpi + + return crossings.sum() % 2 + + def _projection_2_unit_sphere(self): + """ + Brings the complete solution (from stereographic projection) + onto the unit sphere by just shrinking the maximum radius of + all points to 1. + + This keeps the area definitions, so the colouring is not affected. + """ + list_of_objects_2_project = ['nodalline_positive_final', + 'nodalline_negative_final'] + lo_fps = ['FP1_final', 'FP2_final'] + + for obj2proj in list_of_objects_2_project: + obj = getattr(self, '_' + obj2proj).transpose().copy() + for idx, val in enumerate(obj): + old_radius = np.sqrt(val[0] ** 2 + val[1] ** 2) + if old_radius > 1: + obj[idx, 0] = val[0] / old_radius + obj[idx, 1] = val[1] / old_radius + + setattr(self, '_' + obj2proj + '_US', obj.transpose()) + + for fp in lo_fps: + obj = getattr(self, '_' + fp).transpose().copy() + + tmp_obj = [] + for idx, val in enumerate(obj): + old_radius = np.sqrt(val[0] ** 2 + val[1] ** 2) + if old_radius <= 1 + epsilon: + tmp_obj.append(val) + tmp_obj2 = np.array(tmp_obj).transpose() + tmp_obj3 = self._sort_curve_points(tmp_obj2) + + setattr(self, '_' + fp + '_US', tmp_obj3) + + lo_visible_EV = [] + + for idx, val in enumerate(self._all_EV_2D.transpose()): + r_ev = np.sqrt(val[0] ** 2 + val[1] ** 2) + if r_ev <= 1: + lo_visible_EV.append([val[0], val[1], idx]) + visible_EVs = np.array(lo_visible_EV) + + self._all_EV_2D_US = visible_EVs + + lo_visible_BV = [] + dummy_list1 = [] + direction_letters = list('NSEWDU') + + for idx, val in enumerate(self._all_BV_2D.transpose()): + r_bv = math.sqrt(val[0] ** 2 + val[1] ** 2) + if r_bv <= 1: + if idx == 1 and 'N' in dummy_list1: + continue + elif idx == 3 and 'E' in dummy_list1: + continue + elif idx == 5 and 'D' in dummy_list1: + continue + else: + lo_visible_BV.append([val[0], val[1], idx]) + dummy_list1.append(direction_letters[idx]) + + visible_BVs = np.array(lo_visible_BV) + + self._all_BV_2D_US = visible_BVs + + def _plot_US(self, ax=None): + """ + Generates the final plot of the beachball projection on the unit + sphere. + + Additionally, the plot can be saved in a file on the fly. + """ + import pylab as P + + plotfig = self._setup_plot_US(P, ax=ax) + + if self._plot_save_plot: + try: + plotfig.savefig(self._plot_outfile + '.' + + self._plot_outfile_format, dpi=self._plot_dpi, + transparent=True, + format=self._plot_outfile_format) + except: + print('saving of plot not possible') + P.show() + P.close('all') + + def _setup_plot_US(self, P, ax=None): + """ + Setting up the figure with the final plot of the unit sphere. + + Either called by _plot_US or by _just_save_bb + """ + P.close(667) + if ax is None: + plotfig = P.figure(667, figsize=(self._plot_size, self._plot_size)) + plotfig.subplots_adjust(left=0, bottom=0, right=1, top=1) + ax = plotfig.add_subplot(111, aspect='equal') + + ax.axison = False + + neg_nodalline = self._nodalline_negative_final_US + pos_nodalline = self._nodalline_positive_final_US + FP1_2_plot = self._FP1_final_US + FP2_2_plot = self._FP2_final_US + + US = self._unit_sphere + + tension_colour = self._plot_tension_colour + pressure_colour = self._plot_pressure_colour + + if self._plot_fill_flag: + if self._plot_clr_order > 0: + alpha = self._plot_fill_alpha * self._plot_total_alpha + + ax.fill(US[0, :], US[1, :], fc=pressure_colour, alpha=alpha) + ax.fill(neg_nodalline[0, :], neg_nodalline[1, :], + fc=tension_colour, alpha=alpha) + ax.fill(pos_nodalline[0, :], pos_nodalline[1, :], + fc=tension_colour, alpha=alpha) + + if self._plot_curve_in_curve != 0: + ax.fill(US[0, :], US[1, :], fc=tension_colour, + alpha=alpha) + + if self._plot_curve_in_curve < 1: + ax.fill(neg_nodalline[0, :], neg_nodalline[1, :], + fc=pressure_colour, alpha=alpha) + ax.fill(pos_nodalline[0, :], pos_nodalline[1, :], + fc=tension_colour, alpha=alpha) + pass + else: + ax.fill(pos_nodalline[0, :], pos_nodalline[1, :], + fc=pressure_colour, alpha=alpha) + ax.fill(neg_nodalline[0, :], neg_nodalline[1, :], + fc=tension_colour, alpha=alpha) + pass + + EV_sym = ['m^', 'b^', 'g^', 'mv', 'bv', 'gv'] + + if self._plot_show_princ_axes: + alpha = \ + self._plot_princ_axes_alpha * self._plot_total_alpha + + for val in self._all_EV_2D_US: + ax.plot([val[0]], [val[1]], EV_sym[int(val[2])], + ms=self._plot_princ_axes_symsize, + lw=self._plot_princ_axes_lw, alpha=alpha) + + else: + alpha = self._plot_fill_alpha * self._plot_total_alpha + + ax.fill(US[0, :], US[1, :], fc=tension_colour, alpha=alpha) + ax.fill(neg_nodalline[0, :], neg_nodalline[1, :], + fc=pressure_colour, alpha=alpha) + ax.fill(pos_nodalline[0, :], pos_nodalline[1, :], + fc=pressure_colour, alpha=alpha) + + if self._plot_curve_in_curve != 0: + ax.fill(US[0, :], US[1, :], fc=pressure_colour, + alpha=alpha) + + if self._plot_curve_in_curve < 1: + ax.fill(neg_nodalline[0, :], neg_nodalline[1, :], + fc=tension_colour, alpha=alpha) + ax.fill(pos_nodalline[0, :], pos_nodalline[1, :], + fc=pressure_colour, alpha=alpha) + pass + else: + ax.fill(pos_nodalline[0, :], pos_nodalline[1, :], + fc=tension_colour, alpha=alpha) + ax.fill(neg_nodalline[0, :], neg_nodalline[1, :], + fc=pressure_colour, alpha=alpha) + pass + + EV_sym = ['g^', 'b^', 'm^', 'gv', 'bv', 'mv'] + if self._plot_show_princ_axes: + alpha = self._plot_princ_axes_alpha * self._plot_total_alpha + + for val in self._all_EV_2D_US: + ax.plot([val[0]], [val[1]], EV_sym[int(val[2])], + ms=self._plot_princ_axes_symsize, + lw=self._plot_princ_axes_lw, alpha=alpha) + + # + # set all nodallines and faultplanes for plotting: + # + + ax.plot(neg_nodalline[0, :], neg_nodalline[1, :], + c=self._plot_nodalline_colour, ls='-', + lw=self._plot_nodalline_width, + alpha=self._plot_nodalline_alpha * self._plot_total_alpha) + #ax.plot( neg_nodalline[0,:] ,neg_nodalline[1,:],'go') + + ax.plot(pos_nodalline[0, :], pos_nodalline[1, :], + c=self._plot_nodalline_colour, ls='-', + lw=self._plot_nodalline_width, + alpha=self._plot_nodalline_alpha * self._plot_total_alpha) + + if self._plot_show_faultplanes: + ax.plot(FP1_2_plot[0, :], FP1_2_plot[1, :], + c=self._plot_faultplane_colour, ls='-', + lw=self._plot_faultplane_width, + alpha=self._plot_faultplane_alpha * self._plot_total_alpha) + ax.plot(FP2_2_plot[0, :], FP2_2_plot[1, :], + c=self._plot_faultplane_colour, ls='-', + lw=self._plot_faultplane_width, + alpha=self._plot_faultplane_alpha * self._plot_total_alpha) + + elif self._plot_show_1faultplane: + if not self._plot_show_FP_index in [1, 2]: + print('no fault plane specified for being plotted... ') + print('continue without faultplane') + pass + else: + alpha = self._plot_faultplane_alpha * self._plot_total_alpha + if self._plot_show_FP_index == 1: + ax.plot(FP1_2_plot[0, :], FP1_2_plot[1, :], + c=self._plot_faultplane_colour, ls='-', + lw=self._plot_faultplane_width, alpha=alpha) + else: + ax.plot(FP2_2_plot[0, :], FP2_2_plot[1, :], + c=self._plot_faultplane_colour, ls='-', + lw=self._plot_faultplane_width, alpha=alpha) + + # if isotropic part shall be displayed, fill the circle completely with + # the appropriate colour + + if self._pure_isotropic: + # f abs( np.trace( self._M )) > epsilon: + if self._plot_clr_order < 0: + ax.fill(US[0, :], US[1, :], fc=tension_colour, alpha=1, + zorder=100) + else: + ax.fill(US[0, :], US[1, :], fc=pressure_colour, alpha=1, + zorder=100) + + # plot outer circle line of US + ax.plot(US[0, :], US[1, :], c=self._plot_outerline_colour, ls='-', + lw=self._plot_outerline_width, + alpha=self._plot_outerline_alpha * self._plot_total_alpha) + + # plot NED basis vectors + if self._plot_show_basis_axes: + plot_size_in_points = self._plot_size * 2.54 * 72 + points_per_unit = plot_size_in_points / 2. + + fontsize = plot_size_in_points / 40. + symsize = plot_size_in_points / 61. + + direction_letters = list('NSEWDU') + + for val in self._all_BV_2D_US: + x_coord = val[0] + y_coord = val[1] + np_letter = direction_letters[int(val[2])] + + rot_angle = -np.arctan2(y_coord, x_coord) + pi / 2. + original_rho = np.sqrt(x_coord ** 2 + y_coord ** 2) + + marker_x = (original_rho - (1.5 * symsize / points_per_unit)) \ + * np.sin(rot_angle) + marker_y = (original_rho - (1.5 * symsize / points_per_unit)) \ + * np.cos(rot_angle) + annot_x = (original_rho - (4.5 * fontsize / points_per_unit)) \ + * np.sin(rot_angle) + annot_y = (original_rho - (4.5 * fontsize / points_per_unit)) \ + * np.cos(rot_angle) + + ax.text(annot_x, annot_y, np_letter, + horizontalalignment='center', size=fontsize, + weight='bold', verticalalignment='center', + bbox=dict(edgecolor='white', facecolor='white', + alpha=1)) + + if original_rho > epsilon: + ax.scatter([marker_x], [marker_y], + marker=(3, 0, rot_angle), s=symsize ** 2, + c='k', facecolor='k', zorder=300) + else: + ax.scatter([x_coord], [y_coord], marker=(4, 1, rot_angle), + s=symsize ** 2, c='k', facecolor='k', + zorder=300) + + # plot 4 fake points, guaranteeing full visibilty of the sphere + ax.plot([0, 1.05, 0, -1.05], [1.05, 0, -1.05, 0], ',', alpha=0.) + # scaling behaviour + ax.autoscale_view(tight=True, scalex=True, scaley=True) + + return plotfig + + +if __name__ == "__main__": + + from optparse import OptionParser, OptionGroup + + decomp_attrib_map_keys = ('in', 'out', 'type', + 'full', + 'iso', 'iso_perc', + 'dev', 'devi', 'devi_perc', + 'dc', 'dc_perc', + 'dc2', 'dc2_perc', + 'dc3', 'dc3_perc', + 'clvd', 'clvd_perc', + 'mom', 'mag', + 'eigvals', 'eigvecs', + 't', 'n', 'p') + + decomp_attrib_map = dict(zip(decomp_attrib_map_keys, + ('input_system', 'output_system', + 'decomp_type', 'M', + 'iso', 'iso_percentage', + 'devi', 'devi', 'devi_percentage', + 'DC', 'DC_percentage', + 'DC2', 'DC2_percentage', + 'DC3', 'DC3_percentage', + 'CLVD', 'CLVD_percentage', + 'moment', 'mag', + 'eigvals', 'eigvecs', + 't_axis', 'null_axis', 'p_axis') + )) + + lo_allowed_systems = ['NED', 'USE', 'XYZ', 'NWU'] + + def _handle_input(call, M_in, call_args, optparser): + """ + take the original method and its arguments, the source mechanism, + and the dictionary with proper parsers for each call, + """ + + # construct a dict with consistent keywordargs suited for the current + # call + kwargs = _parse_arguments(call, call_args, optparser) + + # set the fitting input basis system + in_system = kwargs.get('in_system', 'NED') + out_system = kwargs.get('out_system', 'NED') + + # build the moment tensor object + mt = MomentTensor( + M=M_in, in_system=in_system, out_system=out_system) + + # if only parts of M are to be plotted, M must be reduced to this part + # already here! + if call == 'plot' and kwargs['plot_part_of_m']: + if kwargs['plot_part_of_m'] == 'iso': + mt = MomentTensor( + M=mt.get_iso(), in_system=in_system, out_system=out_system) + + if kwargs['plot_part_of_m'] == 'devi': + mt = MomentTensor( + M=mt.get_devi(), in_system=in_system, + out_system=out_system) + + if kwargs['plot_part_of_m'] == 'dc': + mt = MomentTensor( + M=mt.get_DC(), in_system=in_system, out_system=out_system) + + if kwargs['plot_part_of_m'] == 'clvd': + mt = MomentTensor( + M=mt.get_CLVD(), in_system=in_system, + out_system=out_system) + + # call the main routine to handle the moment tensor + return _call_main(mt, call, kwargs) + + def _call_main(MT, main_call, kwargs_dict): + + if main_call == 'plot': + return _call_plot(MT, kwargs_dict) + + elif main_call == 'gmt': + return _call_gmt(MT, kwargs_dict) + + elif main_call == 'decompose': + return _call_decompose(MT, kwargs_dict) + + elif main_call == 'describe': + return _call_describe(MT, kwargs_dict) + + def _call_plot(MT, kwargs_dict): + + bb2plot = BeachBall(MT, kwargs_dict) + + if kwargs_dict['plot_save_plot']: + bb2plot.save_BB(kwargs_dict) + return + + if kwargs_dict['plot_pa_plot']: + bb2plot.pa_plot(kwargs_dict) + return + + if kwargs_dict['plot_full_sphere']: + bb2plot.full_sphere_plot(kwargs_dict) + return + + bb2plot.ploBB(kwargs_dict) + + return + + def _call_gmt(MT, kwargs_dict): + bb = BeachBall(MT, kwargs_dict) + return bb.get_psxy(kwargs_dict) + + def _call_decompose(MT, kwargs_dict): + + MT._isotropic = None + MT._deviatoric = None + MT._DC = None + MT._iso_percentage = None + MT._DC_percentage = None + MT._DC2 = None + MT._DC3 = None + MT._DC2_percentage = None + MT._CLVD = None + MT._seismic_moment = None + MT._moment_magnitude = None + + out_system = kwargs_dict['out_system'] + MT._output_basis = out_system + MT._decomposition_key = kwargs_dict['decomposition_key'] + + MT._decompose_M() + + print + + # build argument for local call within MT object: + lo_args = kwargs_dict['decomp_out_part'] + + if not lo_args: + lo_args = decomp_attrib_map_keys + + labels = '''Basis system of the input +Basis system of the output +Decomposition type +Full moment tensor +Isotropic part +Isotropic percentage +Deviatoric part +Deviatoric part +Deviatoric percentage +Double Couple part +Double Couple percentage +Second Double Couple part +Second Double Couple's percentage +Third Double Couple part +Third Double Couple's percentage +CLVD part in +CLVD percentage +Seismic moment (in Nm) +Moment magnitude ) +Eigenvalues +Eigenvectors +Tension-axis +Null-axis +Pressure-axis'''.splitlines() + + # for list of elements: + for label, arg in zip(labels, lo_args): + getter = getattr(MT, 'get_' + decomp_attrib_map[arg]) + x = getter(style='y', system=out_system) + print('%s: %s' % (label, x)) + + def _call_describe(MT, kwargs_dict): + print(MT) + + def _build_gmt_dict(options, optparser): + """ + """ + consistent_kwargs_dict = {} + temp_dict = {} + lo_allowed_options = ['GMT_string_type', 'GMT_scaling', + 'GMT_tension_colour', 'GMT_pressure_colour', + 'GMT_show_2FP2', 'GMT_show_1FP', + 'plot_viewpoint', 'GMT_plot_isotropic_part', + 'GMT_projection'] + + # check for allowed options: + for ao in lo_allowed_options: + if hasattr(options, ao): + temp_dict[ao] = getattr(options, ao) + + if temp_dict['GMT_show_1FP']: + try: + if int(float(temp_dict['GMT_show_1FP'])) in [1, 2]: + consistent_kwargs_dict['_GMT_1fp'] = \ + int(float(temp_dict['GMT_show_1FP'])) + except: + pass + + if temp_dict['GMT_show_2FP2']: + temp_dict['GMT_show_1FP'] = 0 + + consistent_kwargs_dict['_GMT_2fps'] = True + consistent_kwargs_dict['_GMT_1fp'] = 0 + + if temp_dict['GMT_string_type'][0].lower() not in ['f', 'l', 'e']: + print('type of desired string not known - taking "fill" instead') + consistent_kwargs_dict['_GMT_type'] = 'fill' + + else: + if temp_dict['GMT_string_type'][0] == 'f': + consistent_kwargs_dict['_GMT_type'] = 'fill' + elif temp_dict['GMT_string_type'][0] == 'l': + consistent_kwargs_dict['_GMT_type'] = 'lines' + else: + consistent_kwargs_dict['_GMT_type'] = 'EVs' + + if float(temp_dict['GMT_scaling']) < epsilon: + print('GMT scaling factor must be a factor larger than') + print('%f - set to 1, due to obviously stupid input value' % \ + (epsilon)) + temp_dict['GMT_scaling'] = 1 + + if temp_dict['plot_viewpoint']: + try: + vp = temp_dict['plot_viewpoint'].split(',') + if not len(vp) == 3: + raise + if not -90 <= float(vp[0]) <= 90: + raise + if not -180 <= float(vp[1]) <= 180: + raise + if not 0 <= float(vp[2]) % 360 <= 360: + raise + consistent_kwargs_dict['plot_viewpoint'] = \ + [float(vp[0]), float(vp[1]), float(vp[2])] + except: + pass + + if temp_dict['GMT_projection']: + lo_allowed_projections = ['stereo', 'ortho', 'lambert'] # ,'gnom'] + do_allowed_projections = dict(zip(('s', 'o', 'l', 'g'), + ('stereo', 'ortho', + 'lambert', 'gnom'))) + try: + gmtp = temp_dict['GMT_projection'].lower() + if gmtp in lo_allowed_projections: + consistent_kwargs_dict['plot_projection'] = gmtp + elif gmtp in do_allowed_projections.keys(): + consistent_kwargs_dict['plot_projection'] = \ + do_allowed_projections[gmtp] + else: + consistent_kwargs_dict['plot_projection'] = 'stereo' + except: + pass + + consistent_kwargs_dict['_GMT_scaling'] = \ + temp_dict['GMT_scaling'] + consistent_kwargs_dict['_GMT_tension_colour'] = \ + temp_dict['GMT_tension_colour'] + consistent_kwargs_dict['_GMT_pressure_colour'] = \ + temp_dict['GMT_pressure_colour'] + consistent_kwargs_dict['_plot_isotropic_part'] = \ + temp_dict['GMT_plot_isotropic_part'] + + return consistent_kwargs_dict + + def _build_decompose_dict(options, optparser): + + consistent_kwargs_dict = {} + temp_dict = {} + lo_allowed_options = ['decomp_out_complete', 'decomp_out_fancy', + 'decomp_out_part', 'in_system', 'out_system', + 'decomp_key'] + + # check for allowed options: + for ao in lo_allowed_options: + if hasattr(options, ao): + temp_dict[ao] = getattr(options, ao) + + for k in 'in_system', 'out_system': + s = getattr(options, k).upper() + if s not in lo_allowed_systems: + sys.exit('Unavailable coordinate system: %s' % s) + + consistent_kwargs_dict[k] = s + + consistent_kwargs_dict['decomposition_key'] = int( + temp_dict['decomp_key']) + + if temp_dict['decomp_out_part'] is None: + consistent_kwargs_dict['decomp_out_part'] = None + else: + parts = [x.strip().lower() + for x in temp_dict['decomp_out_part'].split(',')] + for part in parts: + if part not in decomp_attrib_map_keys: + sys.exit('Unavailable decomposition part: %s' % part) + + consistent_kwargs_dict['decomp_out_part'] = parts + + consistent_kwargs_dict['style'] = 'y' + + return consistent_kwargs_dict + + def _build_plot_dict(options, optparser): + consistent_kwargs_dict = {} + temp_dict = {} + + lo_allowed_options = [ + 'plot_outfile', 'plot_pa_plot', + 'plot_full_sphere', 'plot_part_of_m', 'plot_viewpoint', + 'plot_projection', 'plot_show_upper_hemis', 'plot_n_points', + 'plot_size', 'plot_tension_colour', 'plot_pressure_colour', + 'plot_total_alpha', 'plot_show_faultplanes', + 'plot_show_1faultplane', 'plot_show_princ_axes', + 'plot_show_basis_axes', 'plot_outerline', 'plot_nodalline', + 'plot_dpi', 'plot_only_lines', 'plot_input_system', + 'plot_isotropic_part'] + + # check for allowed options: + for ao in lo_allowed_options: + if hasattr(options, ao): + temp_dict[ao] = getattr(options, ao) + + consistent_kwargs_dict['plot_save_plot'] = False + if temp_dict['plot_outfile']: + consistent_kwargs_dict['plot_save_plot'] = True + lo_possible_formats = ['svg', 'png', 'eps', 'pdf', 'ps'] + + try: + (filepath, filename) = os.path.split(temp_dict['plot_outfile']) + if not filename: + filename = 'dummy_filename.svg' + (shortname, extension) = os.path.splitext(filename) + if not shortname: + shortname = 'dummy_shortname' + + if extension[1:].lower() in lo_possible_formats: + consistent_kwargs_dict['plot_outfile_format'] = \ + extension[1:].lower() + + if shortname.endswith('.'): + consistent_kwargs_dict['plot_outfile'] = \ + os.path.realpath(os.path.abspath(os.path.join( + os.curdir, filepath, + shortname + extension[1:].lower()))) + else: + consistent_kwargs_dict['plot_outfile'] = \ + os.path.realpath(os.path.abspath(os.path.join( + os.curdir, filepath, shortname + '.' + + extension[1:].lower()))) + else: + if filename.endswith('.'): + consistent_kwargs_dict['plot_outfile'] = \ + os.path.realpath(os.path.abspath(os.path.join( + os.curdir, filepath, + filename + lo_possible_formats[0]))) + else: + consistent_kwargs_dict['plot_outfile'] = \ + os.path.realpath(os.path.abspath(os.path.join( + os.curdir, filepath, filename + '.' + + lo_possible_formats[0]))) + consistent_kwargs_dict['plot_outfile_format'] = \ + lo_possible_formats[0] + + except: + msg = 'please provide valid filename: . !!\n' + msg += ' must be svg, png, eps, pdf, or ps ' + exit(msg) + + if temp_dict['plot_pa_plot']: + consistent_kwargs_dict['plot_pa_plot'] = True + else: + consistent_kwargs_dict['plot_pa_plot'] = False + + if temp_dict['plot_full_sphere']: + consistent_kwargs_dict['plot_full_sphere'] = True + consistent_kwargs_dict['plot_pa_plot'] = False + else: + consistent_kwargs_dict['plot_full_sphere'] = False + + if temp_dict['plot_part_of_m']: + try: + plottable_part_raw = temp_dict['plot_part_of_m'].lower()[:2] + if plottable_part_raw == 'is': + plottable_part = 'iso' + elif plottable_part_raw == 'de': + plottable_part = 'devi' + elif plottable_part_raw == 'dc': + plottable_part = 'dc' + elif plottable_part_raw == 'cl': + plottable_part = 'clvd' + else: + plottable_part = False + + consistent_kwargs_dict['plot_part_of_m'] = plottable_part + + except: + consistent_kwargs_dict['plot_part_of_m'] = False + + else: + consistent_kwargs_dict['plot_part_of_m'] = False + + if temp_dict['plot_viewpoint']: + try: + vp = temp_dict['plot_viewpoint'].split(',') + if not len(vp) == 3: + raise + if not -90 <= float(vp[0]) <= 90: + raise + if not -180 <= float(vp[1]) <= 180: + raise + if not 0 <= float(vp[2]) % 360 <= 360: + raise + consistent_kwargs_dict['plot_viewpoint'] = \ + [float(vp[0]), float(vp[1]), float(vp[2])] + except: + pass + + if temp_dict['plot_projection']: + lo_allowed_projections = ['stereo', 'ortho', 'lambert'] # ,'gnom'] + do_allowed_projections = dict(zip(('s', 'o', 'l', 'g'), + ('stereo', 'ortho', + 'lambert', 'gnom'))) + try: + ppl = temp_dict['plot_projection'].lower() + if ppl in lo_allowed_projections: + consistent_kwargs_dict['plot_projection'] = ppl + elif ppl in do_allowed_projections.keys(): + consistent_kwargs_dict['plot_projection'] = \ + do_allowed_projections[ppl] + else: + consistent_kwargs_dict['plot_projection'] = 'stereo' + except: + pass + + if temp_dict['plot_show_upper_hemis']: + consistent_kwargs_dict['plot_show_upper_hemis'] = True + + if temp_dict['plot_n_points']: + try: + if temp_dict['plot_n_points'] > 360: + consistent_kwargs_dict['plot_n_points'] = \ + int(temp_dict['plot_n_points']) + except: + pass + + if temp_dict['plot_size']: + try: + if 0.01 < temp_dict['plot_size'] <= 1: + consistent_kwargs_dict['plot_size'] = \ + temp_dict['plot_size'] * 10 / 2.54 + elif 1 < temp_dict['plot_size'] < 45: + consistent_kwargs_dict['plot_size'] = \ + temp_dict['plot_size'] / 2.54 + else: + consistent_kwargs_dict['plot_size'] = 5 + consistent_kwargs_dict['plot_aux_plot_size'] = \ + consistent_kwargs_dict['plot_size'] + except: + pass + + if temp_dict['plot_pressure_colour']: + try: + sec_colour_raw = temp_dict['plot_pressure_colour'].split(',') + if len(sec_colour_raw) == 1: + if sec_colour_raw[0].lower()[0] in list('bgrcmykw'): + consistent_kwargs_dict['plot_pressure_colour'] = \ + sec_colour_raw[0].lower()[0] + else: + raise + elif len(sec_colour_raw) == 3: + for sc in sec_colour_raw: + if not 0 <= (int(sc)) <= 255: + raise + consistent_kwargs_dict['plot_pressure_colour'] = \ + (float(sec_colour_raw[0]) / 255., + float(sec_colour_raw[1]) / 255., + float(sec_colour_raw[2]) / 255.) + else: + raise + except: + pass + + if temp_dict['plot_tension_colour']: + try: + sec_colour_raw = temp_dict['plot_tension_colour'].split(',') + if len(sec_colour_raw) == 1: + if sec_colour_raw[0].lower()[0] in list('bgrcmykw'): + consistent_kwargs_dict['plot_tension_colour'] = \ + sec_colour_raw[0].lower()[0] + else: + raise + elif len(sec_colour_raw) == 3: + for sc in sec_colour_raw: + if not 0 <= (int(float(sc))) <= 255: + raise + consistent_kwargs_dict['plot_tension_colour'] = \ + (float(sec_colour_raw[0]) / 255., + float(sec_colour_raw[1]) / 255., + float(sec_colour_raw[2]) / 255.) + else: + raise + except: + pass + + if temp_dict['plot_total_alpha']: + try: + if not 0 <= float(temp_dict['plot_total_alpha']) <= 1: + consistent_kwargs_dict['plot_total_alpha'] = 1 + else: + consistent_kwargs_dict['plot_total_alpha'] = \ + float(temp_dict['plot_total_alpha']) + except: + pass + + if temp_dict['plot_show_1faultplane']: + consistent_kwargs_dict['plot_show_1faultplane'] = True + try: + fp_args = temp_dict['plot_show_1faultplane'] + + if not int(fp_args[0]) in [1, 2]: + consistent_kwargs_dict['plot_show_FP_index'] = 1 + else: + consistent_kwargs_dict['plot_show_FP_index'] = \ + int(fp_args[0]) + + if not 0 < float(fp_args[1]) <= 20: + consistent_kwargs_dict['plot_faultplane_width'] = 2 + else: + consistent_kwargs_dict['plot_faultplane_width'] = \ + float(fp_args[1]) + + try: + sec_colour_raw = fp_args[2].split(',') + if len(sec_colour_raw) == 1: + if sec_colour_raw[0].lower()[0] in list('bgrcmykw'): + consistent_kwargs_dict['plot_faultplane_colour'] =\ + sec_colour_raw[0].lower()[0] + else: + raise + elif len(sec_colour_raw) == 3: + for sc in sec_colour_raw: + if not 0 <= (int(sc)) <= 255: + raise + consistent_kwargs_dict['plot_faultplane_colour'] = \ + (float(sec_colour_raw[0]) / 255., + float(sec_colour_raw[1]) / 255., + float(sec_colour_raw[2]) / 255.) + else: + raise + except: + consistent_kwargs_dict['plot_faultplane_colour'] = 'k' + + try: + if 0 <= float(fp_args[3]) <= 1: + consistent_kwargs_dict['plot_faultplane_alpha'] = \ + float(fp_args[3]) + except: + consistent_kwargs_dict['plot_faultplane_alpha'] = 1 + except: + pass + + if temp_dict['plot_show_faultplanes']: + consistent_kwargs_dict['plot_show_faultplanes'] = True + consistent_kwargs_dict['plot_show_1faultplane'] = False + + if temp_dict['plot_dpi']: + try: + if 200 <= int(temp_dict['plot_dpi']) <= 2000: + consistent_kwargs_dict['plot_dpi'] = \ + int(temp_dict['plot_dpi']) + else: + raise + except: + pass + + if temp_dict['plot_only_lines']: + consistent_kwargs_dict['plot_fill_flag'] = False + + if temp_dict['plot_outerline']: + consistent_kwargs_dict['plot_outerline'] = True + try: + fp_args = temp_dict['plot_outerline'] + if not 0 < float(fp_args[0]) <= 20: + consistent_kwargs_dict['plot_outerline_width'] = 2 + else: + consistent_kwargs_dict['plot_outerline_width'] = \ + float(fp_args[0]) + try: + sec_colour_raw = fp_args[1].split(',') + if len(sec_colour_raw) == 1: + if sec_colour_raw[0].lower()[0] in list('bgrcmykw'): + consistent_kwargs_dict['plot_outerline_colour'] = \ + sec_colour_raw[0].lower()[0] + else: + raise + elif len(sec_colour_raw) == 3: + for sc in sec_colour_raw: + if not 0 <= (int(sc)) <= 255: + raise + consistent_kwargs_dict['plot_outerline_colour'] = \ + (float(sec_colour_raw[0]) / 255., + float(sec_colour_raw[1]) / 255., + float(sec_colour_raw[2]) / 255.) + else: + raise + except: + consistent_kwargs_dict['plot_outerline_colour'] = 'k' + + try: + if 0 <= float(fp_args[2]) <= 1: + consistent_kwargs_dict['plot_outerline_alpha'] = \ + float(fp_args[2]) + except: + consistent_kwargs_dict['plot_outerline_alpha'] = 1 + except: + pass + + if temp_dict['plot_nodalline']: + consistent_kwargs_dict['plot_nodalline'] = True + try: + fp_args = temp_dict['plot_nodalline'] + + if not 0 < float(fp_args[0]) <= 20: + consistent_kwargs_dict['plot_nodalline_width'] = 2 + else: + consistent_kwargs_dict['plot_nodalline_width'] = \ + float(fp_args[0]) + try: + sec_colour_raw = fp_args[1].split(',') + if len(sec_colour_raw) == 1: + if sec_colour_raw[0].lower()[0] in list('bgrcmykw'): + consistent_kwargs_dict['plot_nodalline_colour'] = \ + sec_colour_raw[0].lower()[0] + else: + raise + elif len(sec_colour_raw) == 3: + for sc in sec_colour_raw: + if not 0 <= (int(sc)) <= 255: + raise + consistent_kwargs_dict['plot_nodalline_colour'] = \ + (float(sec_colour_raw[0]) / 255., + float(sec_colour_raw[1]) / 255., + float(sec_colour_raw[2]) / 255.) + else: + raise + except: + consistent_kwargs_dict['plot_nodalline_colour'] = 'k' + try: + if 0 <= float(fp_args[2]) <= 1: + consistent_kwargs_dict['plot_nodalline_alpha'] = \ + float(fp_args[2]) + except: + consistent_kwargs_dict['plot_nodalline_alpha'] = 1 + except: + pass + + if temp_dict['plot_show_princ_axes']: + consistent_kwargs_dict['plot_show_princ_axes'] = True + try: + fp_args = temp_dict['plot_show_princ_axes'] + + if not 0 < float(fp_args[0]) <= 40: + consistent_kwargs_dict['plot_princ_axes_symsize'] = 10 + else: + consistent_kwargs_dict['plot_princ_axes_symsize'] = \ + float(fp_args[0]) + + if not 0 < float(fp_args[1]) <= 20: + consistent_kwargs_dict['plot_princ_axes_lw '] = 3 + else: + consistent_kwargs_dict['plot_princ_axes_lw '] = \ + float(fp_args[1]) + try: + if 0 <= float(fp_args[2]) <= 1: + consistent_kwargs_dict['plot_princ_axes_alpha'] = \ + float(fp_args[2]) + except: + consistent_kwargs_dict['plot_princ_axes_alpha'] = 1 + except: + pass + + if temp_dict['plot_show_basis_axes']: + consistent_kwargs_dict['plot_show_basis_axes'] = True + + if temp_dict['plot_input_system']: + lo_allowed_systems = ['XYZ', 'NED', 'USE', 'NWU'] + try: + tpis = temp_dict['plot_input_system'][:3].upper() + if tpis in lo_allowed_systems: + consistent_kwargs_dict['in_system'] = tpis + else: + raise + except: + pass + + if temp_dict['plot_isotropic_part']: + consistent_kwargs_dict['plot_isotropic_part'] = \ + temp_dict['plot_isotropic_part'] + + return consistent_kwargs_dict + + def _build_describe_dict(options, optparser): + consistent_kwargs_dict = {} + + for k in 'in_system', 'out_system': + s = getattr(options, k).upper() + if s not in lo_allowed_systems: + sys.exit('Unavailable coordinate system: %s' % s) + + consistent_kwargs_dict[k] = s + + return consistent_kwargs_dict + + def _parse_arguments(main_call, its_arguments, optparser): + """ + """ + # todo: + # print '\n', main_call,its_arguments,'\n' + (options, args) = optparser.parse_args(its_arguments) + + # todo + # check, if arguments do not start with "-" - if so, there is a lack of + # arguments for the previous option + for val2check in options.__dict__.values(): + if str(val2check).startswith('-'): + try: + val2check_split = val2check.split(',') + for ii in val2check_split: + float(ii) + except: + sys.exit('ERROR - check carefully number of arguments ' + + 'for all options\n') + + if main_call == 'plot': + consistent_kwargs_dict = _build_plot_dict(options, optparser) + + elif main_call == 'gmt': + consistent_kwargs_dict = _build_gmt_dict(options, optparser) + + elif main_call == 'decompose': + consistent_kwargs_dict = _build_decompose_dict(options, optparser) + + elif main_call == 'describe': + consistent_kwargs_dict = _build_describe_dict(options, optparser) + + return consistent_kwargs_dict + + def _add_group_system(parent): + group_system = OptionGroup(parent, 'Basis systems') + group_system.add_option('-i', '--input-system', + action="store", + dest='in_system', + metavar='', + default='NED', + help=''' +Define the coordinate system of the source mechanism [Default: NED]. + +Available coordinate systems: + + * NED: North, East, Down + * USE: Up, South, East (Global CMT) + * XYZ: East, North, Up (Jost and Herrmann) + * NWU: North, West, Up (Stein and Wysession) +'''.lstrip()) + + group_system.add_option('-o', '--output-system', + action="store", + dest='out_system', + metavar='', + default='NED', + help=''' +Define the coordinate system of the output. See '--input-system' for a list of +available coordinate systems [Default: NED].'''.lstrip()) + + parent.add_option_group(group_system) + + def _build_optparsers(): + + _do_parsers = {} + + desc = """ +Generate a beachball representation which can be plotted with GMT. + +This tool produces output which can be fed into the GMT command `psxy`. The +output consists of coordinates which describe the lines of the beachball in +standard cartesian coordinates, centered at zero. + +In order to generate a beachball diagram, this tool has to be called twice with +different arguments of the --type option. First to define the colored areas +(--type=fill) and second for the nodal and border lines (--type=lines). + +Example: + + mopad gmt 30,60,90 --type=fill | \ +psxy -Jx4/4 -R-2/2/-2/2 -P -Cpsxy_fill.cpt -M -L -K > out.ps + + mopad gmt 30,60,90 --type=lines | \ +psxy -Jx4/4 -R-2/2/-2/2 -P -Cpsxy_lines.cpt -W2p -P -M -O >> out.ps + +""" + + parser_gmt = OptionParser( + usage='mopad.py gmt [options]', + description=desc, formatter=MopadHelpFormatter()) + + group_type = OptionGroup(parser_gmt, 'Output') + group_show = OptionGroup(parser_gmt, 'Appearance') + group_geo = OptionGroup(parser_gmt, 'Geometry') + + group_type.add_option('-t', '--type', + type='string', + dest='GMT_string_type', + action='store', + default='fill', + help='Chosing the respective psxy data set: ' + 'area to fill (fill), nodal lines (lines), ' + 'or eigenvector positions (ev) [Default: fill]', + metavar='') + + group_show.add_option('-s', '--scaling', + dest='GMT_scaling', + action='store', + default='1', + type='float', + metavar='', + help='Spatial scaling factor of the beachball ' + '[Default: 1]') + group_show.add_option('-r', '--colour1', + dest='GMT_tension_colour', + type='int', + action='store', + metavar='', + default='1', + help="-Z option's key (see help for 'psxy') " + 'for the tension colour of the beachball - ' + 'type: integer [Default: 1]') + group_show.add_option('-w', '--colour2', + dest='GMT_pressure_colour', + type='int', + action='store', + metavar='', + default='0', + help="-Z option's key (see help for 'psxy') " + 'for the pressure colour of the beachball - ' + 'type: integer [Default: 0]') + group_show.add_option('-D', '--faultplanes', + dest='GMT_show_2FP2', + action='store_true', + default=False, + help='Key, if 2 faultplanes shall be shown ' + '[Default: deactivated]') + group_show.add_option('-d', '--show_1fp', + type='choice', + dest='GMT_show_1FP', + choices=['1', '2'], + metavar='', + action='store', + default=False, + help='Key for plotting 1 specific faultplane - ' + 'value: 1,2 [Default: None]') + group_geo.add_option('-v', '--viewpoint', + action='store', + dest='plot_viewpoint', + metavar='', + default=None, + help='Coordinates (in degrees) of the viewpoint ' + 'onto the projection - type: comma separated ' + '3-tuple [Default: None]') + group_geo.add_option('-p', '--projection', + action='store', + dest='GMT_projection', + metavar='', + default=None, + help='Two-dimensional projection of the sphere - ' + 'value: (s)tereographic, (l)ambert, ' + '(o)rthographic [Default: (s)tereographic]') + group_show.add_option('-I', '--show_isotropic_part', + dest='GMT_plot_isotropic_part', + action='store_true', + default=False, + help='Key for considering the isotropic part ' + 'for plotting [Default: deactivated]') + + parser_gmt.add_option_group(group_type) + parser_gmt.add_option_group(group_show) + parser_gmt.add_option_group(group_geo) + + _do_parsers['gmt'] = parser_gmt + + # plot + desc_plot = """ + Plot a beachball diagram of the provided mechanism. + + Several styles and configurations are available. Also saving + on the fly can be enabled. + ONLY THE DEVIATORIC COMPONENT WILL BE PLOTTED by default; + for including the isotropic part, use the '--show_isotropic_part' + option! + """ + parser_plot = OptionParser( + usage="mopad.py plot [options]", + description=desc_plot, formatter=MopadHelpFormatter()) + + group_save = OptionGroup(parser_plot, 'Saving') + group_type = OptionGroup(parser_plot, 'Type of plot') + group_quality = OptionGroup(parser_plot, 'Quality') + group_colours = OptionGroup(parser_plot, 'Colours') + group_misc = OptionGroup(parser_plot, 'Miscellaneous') + group_dc = OptionGroup(parser_plot, 'Fault planes') + group_geo = OptionGroup(parser_plot, 'Geometry') + group_app = OptionGroup(parser_plot, 'Appearance') + + group_save.add_option('-f', '--output_file', + action="store", + dest='plot_outfile', + metavar='', + default=None, + nargs=1, + help='(Absolute) filename for saving ' + '[Default: None]') + + group_type.add_option('-E', '--eigen_system', + action="store_true", + dest='plot_pa_plot', + default=False, + help='Key for plotting principal axis ' + 'system/eigensystem [Default: deactivated]') + + group_type.add_option('-O', '--full_sphere', + action="store_true", + dest='plot_full_sphere', + default=False, + help='Key for plotting the full sphere ' + '[Default: deactivated]') + + group_type.add_option('-P', '--partial', + action="store", + dest='plot_part_of_m', + metavar='', + default=None, + help='Key for plotting only a specific part of ' + 'M - values: iso,devi,dc,clvd [Default: None] ') + + group_geo.add_option('-v', '--viewpoint', + action="store", + dest='plot_viewpoint', + metavar='', + default=None, + help='Coordinates (in degrees) of the viewpoint ' + 'onto the projection - type: comma separated ' + '3-tuple [Default: None]') + + group_geo.add_option('-p', '--projection', + action="store", + dest='plot_projection', + metavar='', + default=None, + help='Two-dimensional projection of the sphere ' + '- value: (s)tereographic, (l)ambert, ' + '(o)rthographic [Default: (s)tereographic]') + + group_type.add_option('-U', '--upper', + action="store_true", + dest='plot_show_upper_hemis', + default=False, + help='Key for plotting the upper hemisphere ' + '[Default: deactivated]') + + group_quality.add_option('-N', '--points', + action="store", + metavar='', + dest='plot_n_points', + type="int", + default=None, + help='Minimum number of points, used for ' + 'nodal lines [Default: None]') + + group_app.add_option('-s', '--size', + action="store", + dest='plot_size', + metavar='', + type="float", + default=None, + help='Size of plot (diameter) in cm ' + '[Default: None]') + + group_colours.add_option('-w', '--pressure_colour', + action="store", + dest='plot_pressure_colour', + metavar='', + default=None, + help='Colour of the tension area - values: ' + 'comma separated RGB 3-tuples OR MATLAB ' + 'conform colour names [Default: None]') + + group_colours.add_option('-r', '--tension_colour', + action="store", + dest='plot_tension_colour', + metavar='', + default=None, + help='Colour of the pressure area values: ' + 'comma separated RGB 3-tuples OR MATLAB ' + 'conform colour names [Default: None]') + + group_app.add_option('-a', '--alpha', + action="store", + dest='plot_total_alpha', + metavar='', + type='float', + default=None, + help='Alpha value for the total plot - value: ' + 'float between 1=opaque to 0=transparent ' + '[Default: None]') + + group_dc.add_option('-D', '--dc', + action="store_true", + dest='plot_show_faultplanes', + default=False, + help='Key for plotting both double couple ' + 'faultplanes (blue) [Default: deactivated]') + + group_dc.add_option('-d', '--show1fp', + action="store", + metavar=' ', + dest='plot_show_1faultplane', + default=None, + nargs=4, + help='Key for plotting 1 specific faultplane - ' + '4 arguments as space separated list - ' + 'index values: 1,2, linewidth value: float, ' + 'line colour value: string or RGB-3-tuple, alpha ' + 'value: float between 0 and 1 [Default: None] ') + + group_misc.add_option('-e', '--eigenvectors', + action="store", + dest='plot_show_princ_axes', + metavar=' ', + default=None, + nargs=3, + help='Key for showing eigenvectors - ' + '3 arguments as space separated list - symbol ' + 'size value: float, symbol linewidth value: ' + 'float, symbol alpha value: float between 0 ' + 'and 1 [Default: None]') + + group_misc.add_option('-b', '--basis_vectors', + action="store_true", + dest='plot_show_basis_axes', + default=False, + help='Key for showing NED basis axes in plot ' + '[Default: deactivated]') + + group_app.add_option('-l', '--lines', + action="store", + dest='plot_outerline', + metavar=' ', + nargs=3, + default=None, + help='Define the style of the outer line - ' + '3 arguments as space separated list - ' + 'linewidth value: float, line colour value: ' + 'string or RGB-3-tuple), alpha value: float ' + 'between 0 and 1 [Default: None]') + + group_app.add_option('-n', '--nodals', + action="store", + dest='plot_nodalline', + metavar=' ', + default=None, + nargs=3, + help='Define the style of the nodal lines - 3 ' + 'arguments as space separated list - linewidth ' + 'value: float, line colour value: string or ' + 'RGB-3-tuple), alpha value: float between 0 and ' + '1 [Default: None]') + + group_quality.add_option('-Q', '--quality', + action="store", + dest='plot_dpi', + metavar='', + type="int", + default=None, + help='Set the quality for the plot in ' + 'terms of dpi (minimum=200) [Default: None] ') + + group_type.add_option('-L', '--lines_only', + action="store_true", + dest='plot_only_lines', + default=False, + help='Key for plotting lines only (no filling ' + '- this overwrites all "fill"-related options) ' + '[Default: deactivated] ') + + group_misc.add_option('-i', '--input-system', + action="store", + dest='plot_input_system', + metavar='', + default=False, + help='Define the coordinate system of the ' + 'source mechanism - value: NED,USE,XYZ,NWU ' + '[Default: NED] ') + + group_type.add_option('-I', '--show_isotropic_part', + dest='plot_isotropic_part', + action='store_true', + default=False, + help='Key for considering the isotropic part ' + 'for plotting [Default: deactivated]') + + parser_plot.add_option_group(group_save) + parser_plot.add_option_group(group_type) + parser_plot.add_option_group(group_quality) + parser_plot.add_option_group(group_colours) + parser_plot.add_option_group(group_misc) + parser_plot.add_option_group(group_dc) + parser_plot.add_option_group(group_geo) + parser_plot.add_option_group(group_app) + + _do_parsers['plot'] = parser_plot + + desc_decomp = """ +Decompose moment tensor into additive contributions. + +This method implements four different decompositions following the conventions +given by Jost & Herrmann (1998), and Dahm (1997). The type of decomposition can +be selected with the '--type' option. Use the '--partial' option, if only parts +of the full decomposition are required. + +By default, the decomposition results are printed in the following order: + + * 01 - basis of the provided input (string) + * 02 - basis of the representation (string) + * 03 - chosen decomposition type (integer) + + * 04 - full moment tensor (matrix) + + * 05 - isotropic part (matrix) + * 06 - isotropic percentage (float) + * 07 - deviatoric part (matrix) + * 08 - deviatoric percentage (float) + + * 09 - DC part (matrix) + * 10 - DC percentage (float) + * 11 - DC2 part (matrix) + * 12 - DC2 percentage (float) + * 13 - DC3 part (matrix) + * 14 - DC3 percentage (float) + + * 15 - CLVD part (matrix) + * 16 - CLVD percentage (matrix) + + * 17 - seismic moment (float) + * 18 - moment magnitude (float) + + * 19 - eigenvectors (3-array) + * 20 - eigenvalues (list) + * 21 - p-axis (3-array) + * 22 - neutral axis (3-array) + * 23 - t-axis (3-array) +""" + + parser_decompose = OptionParser( + usage="mopad decompose [options]", + description=desc_decomp, formatter=MopadHelpFormatter()) + + group_type = OptionGroup( + parser_decompose, 'Type of decomposition') + group_part = OptionGroup( + parser_decompose, 'Output selection') + + group_part.add_option('-p', '--partial', + action="store", + dest='decomp_out_part', + default=None, + metavar='', + help=''' +Print a subset of the decomposition results. + +Give a comma separated list of what parts of the results should be +printed [Default: None]. The following parts are available: + + %s +''' % ', '.join(decomp_attrib_map_keys)) + + group_type.add_option('-t', '--type', + action="store", + dest='decomp_key', + metavar='', + default=1, + type='int', + help=''' +Choose type of decomposition - values 1,2,3,4 \n[Default: 1]: + +%s +''' % '\n'.join([' * %s - %s' % (k, v[0]) for (k, v) + in MomentTensor.decomp_dict.items()])) + + parser_decompose.add_option_group(group_type) + parser_decompose.add_option_group(group_part) + _add_group_system(parser_decompose) + + _do_parsers['decompose'] = parser_decompose + + parser_describe = OptionParser( + usage="mopad describe [options]", + description=''' +Print the detailed description of a source mechanism + + +For a given source mechanism, orientations of the fault planes, moment, +magnitude, and moment tensor are printed. Input and output coordinate basis +systems can be specified.'''.lstrip(), + formatter=MopadHelpFormatter()) + + _add_group_system(parser_describe) + + _do_parsers['describe'] = parser_describe + + return _do_parsers + + if len(sys.argv) < 2: + call = 'help' + + else: + + call = sys.argv[1].lower() + abbrev = dict(zip(('p', 'g', 'd', 'i', '--help', '-h'), ( + 'plot', 'gmt', 'decompose', 'describe', 'help', 'help'))) + + if call in abbrev: + call = abbrev[call] + + if call not in abbrev.values(): + sys.exit('no such method: %s' % call) + + if call == 'help': + helpstring = """ + +Usage: mopad [options] + + +Type 'mopad --help' for help on a specific method. + + +MoPaD (version %.1f) - Moment Tensor Plotting and Decomposition Tool + +MoPaD is a tool to plot and decompose moment tensor representations of seismic +sources which are commonly used in seismology. This tool is completely +controlled via command line parameters, which consist of a and a + argument and zero or more options. The argument +tells MoPaD what to do and the argument specifies an input +moment tensor source in one of the formats described below. + +Available methods: + + * plot: plot a beachball representation of a source mechanism + * describe: print detailed description of a source mechanism + * decompose: decompose a source mechanism according to various conventions + * gmt: output beachball representation in a format suitable for + plotting with GMT + +The source-mechanism is given as a comma separated list (NO BLANK SPACES!) of +values which is interpreted differently, according to the number of values in +the list. The following source-mechanism representations are available: + + * strike,dip,rake + * strike,dip,rake,moment + * M11,M22,M33,M12,M13,M23 + * M11,M22,M33,M12,M13,M23,moment + * M11,M12,M13,M21,M22,M23,M31,M32,M33 + +Angles are given in degrees, moment tensor components and scalar moment are +given in [Nm] for a coordinate system with axes pointing North, East, and Down +by default. +_______________________________________________________________________________ + +EXAMPLES +-------- + +'plot' : +-- +To generate the "beachball" representation of a pure normal faulting event with +a strike angle of 0 degrees and a dip of 45 degrees, use either of the +following commands: + + mopad plot 0,45,-90 + + mopad plot 0,1,-1,0,0,0 + + +'describe': +-- +To see the seismic moment tensor entries (in GlobalCMT's USE basis) and the +orientation of the auxilliary plane for a shear crack with the +(strike,dip,slip-rake) tuple (90,45,45) use: + + mopad describe 90,45,45 -o USE + + +'decompose': +-- +Get the deviatoric part of a seismic moment tensor M=(1,2,3,4,5,6) together +with the respective double-couple- and CLVD-components by using: + + mopad decompose 1,2,3,4,5,6 -p devi,dc,clvd + + +""" % (MOPAD_VERSION) + + print(helpstring) + + sys.exit() + + try: + M_raw = [float(xx) for xx in sys.argv[2].split(',')] + except: + dummy_list = [] + dummy_list.append(sys.argv[0]) + dummy_list.append(sys.argv[1]) + dummy_list.append('0,0,0') + dummy_list.append('-h') + + sys.argv = dummy_list + M_raw = [float(xx) for xx in sys.argv[2].split(',')] + + if not len(M_raw) in [3, 4, 6, 7, 9]: + print('\nERROR!! Provide proper source mechanism\n\n') + sys.exit() + if len(M_raw) in [4, 6, 7, 9] and len(np.array(M_raw).nonzero()[0]) == 0: + print('\nERROR!! Provide proper source mechanism\n\n') + sys.exit() + + aa = _handle_input(call, M_raw, sys.argv[3:], _build_optparsers()[call]) + if aa is not None: + print(aa) diff --git a/SCRIPTS/mopad_wrapper.py b/SCRIPTS/mopad_wrapper.py new file mode 100644 index 0000000..aabf07d --- /dev/null +++ b/SCRIPTS/mopad_wrapper.py @@ -0,0 +1,325 @@ +# -*- coding: utf-8 -*- +# ----------------------------------------------- +# Filename: mopad_wrapper.py +# Purpose: Wrapper for mopad +# Author: Tobias Megies, Moritz Beyreuther +# Email: megies@geophysik.uni-muenchen.de +# +# Copyright (C) 2008-2012 ObsPy Development Team +# +# Notes +# Modified by Andrea Chiang for option to display isotropic part, last update on May 22, 2018 +# ----------------------------------------------- +""" +ObsPy wrapper to the *Moment tensor Plotting and Decomposition tool* (MoPaD) +written by Lars Krieger and Sebastian Heimann. + +.. seealso:: [Krieger2012]_ + +.. warning:: The MoPaD wrapper does not yet provide the full functionality of + MoPaD. Please consider using the command line script ``obspy-mopad`` for + now if you need the full power of MoPaD. + +:copyright: + The ObsPy Development Team (devs@obspy.org) +:license: + GNU Lesser General Public License, Version 3 + (https://www.gnu.org/copyleft/lesser.html) +""" +from __future__ import (absolute_import, division, print_function, + unicode_literals) +from future.builtins import * # NOQA @UnusedWildImport + +import numpy as np +import matplotlib.collections as mpl_collections +from matplotlib import patches, transforms + +from obspy.imaging.beachball import xy2patch +from mopad import BeachBall as mopad_BeachBall +from mopad import MomentTensor as mopad_MomentTensor +from mopad import epsilon + + +# seems the base system we (gmt) are using is called "USE" in mopad +KWARG_MAP = { + 'size': ['plot_size', 'plot_aux_plot_size'], + 'linewidth': ['plot_nodalline_width', 'plot_outerline_width'], + 'facecolor': ['plot_tension_colour'], + 'edgecolor': ['plot_outerline_colour'], + 'bgcolor': [], + 'alpha': ['plot_total_alpha'], + 'width': [], + 'outfile': ['plot_outfile'], + 'format': ['plot_outfile_format'], + 'nofill': ['plot_only_lines'] +} + + +def beach(fm, linewidth=2, facecolor='b', bgcolor='w', edgecolor='k', + alpha=1.0, xy=(0, 0), width=200, size=100, nofill=False, + zorder=100, mopad_basis='USE', axes=None, show_iso=False): + """ + Return a beach ball as a collection which can be connected to an + current matplotlib axes instance (ax.add_collection). Based on MoPaD. + + S1, D1, and R1, the strike, dip and rake of one of the focal planes, can + be vectors of multiple focal mechanisms. + + :param fm: Focal mechanism that is either number of mechanisms (NM) by 3 + (strike, dip, and rake) or NM x 6 (M11, M22, M33, M12, M13, M23 - the + six independent components of the moment tensor, where the coordinate + system is 1,2,3 = Up,South,East which equals r,theta,phi - + Harvard/Global CMT convention). The relation to Aki and Richards + x,y,z equals North,East,Down convention is as follows: Mrr=Mzz, + Mtt=Mxx, Mpp=Myy, Mrt=Mxz, Mrp=-Myz, Mtp=-Mxy. + The strike is of the first plane, clockwise relative to north. + The dip is of the first plane, defined clockwise and perpendicular to + strike, relative to horizontal such that 0 is horizontal and 90 is + vertical. The rake is of the first focal plane solution. 90 moves the + hanging wall up-dip (thrust), 0 moves it in the strike direction + (left-lateral), -90 moves it down-dip (normal), and 180 moves it + opposite to strike (right-lateral). + :param facecolor: Color to use for quadrants of tension; can be a string, + e.g. ``'r'``, ``'b'`` or three component color vector, [R G B]. + Defaults to ``'b'`` (blue). + :param bgcolor: The background color. Defaults to ``'w'`` (white). + :param edgecolor: Color of the edges. Defaults to ``'k'`` (black). + :param alpha: The alpha level of the beach ball. Defaults to ``1.0`` + (opaque). + :param xy: Origin position of the beach ball as tuple. Defaults to + ``(0, 0)``. + :type width: int + :param width: Symbol size of beach ball. Defaults to ``200``. + :param size: Controls the number of interpolation points for the + curves. Minimum is automatically set to ``100``. + :param nofill: Do not fill the beach ball, but only plot the planes. + :param zorder: Set zorder. Artists with lower zorder values are drawn + first. + :param mopad_basis: The basis system. Defaults to ``'USE'``. See the + `Supported Basis Systems`_ section below for a full list of supported + systems. + :type axes: :class:`matplotlib.axes.Axes` + :param axes: Used to make beach balls circular on non-scaled axes. Also + maintains the aspect ratio when resizing the figure. Will not add + the returned collection to the axes instance. + + .. rubric:: _`Supported Basis Systems` + + ========= =================== ============================================= + Short Basis vectors Usage + ========= =================== ============================================= + ``'NED'`` North, East, Down Jost and Herrmann 1989 + ``'USE'`` Up, South, East Global CMT Catalog, Larson et al. 2010 + ``'XYZ'`` East, North, Up General formulation, Jost and Herrmann 1989 + ``'RT'`` Radial, Transverse, psmeca (GMT), Wessel and Smith 1999 + Tangential + ``'NWU'`` North, West, Up Stein and Wysession 2003 + ========= =================== ============================================= + """ + # initialize beachball + mt = mopad_MomentTensor(fm, in_system=mopad_basis) + bb = mopad_BeachBall(mt, npoints=size) + + ## Snippets added by A. Chiang + if show_iso: + bb._plot_isotropic_part = True + bb._nodallines_in_NED_system() + + ## + + bb._setup_BB(unit_circle=False) + + # extract the coordinates and colors of the lines + radius = width / 2.0 + neg_nodalline = bb._nodalline_negative_final_US + pos_nodalline = bb._nodalline_positive_final_US + tension_colour = facecolor + pressure_colour = bgcolor + + if nofill: + tension_colour = 'none' + pressure_colour = 'none' + + # based on mopads _setup_plot_US() function + # collect patches for the selection + coll = [None, None, None] + coll[0] = patches.Circle(xy, radius=radius) + coll[1] = xy2patch(neg_nodalline[0, :], neg_nodalline[1, :], radius, xy) + coll[2] = xy2patch(pos_nodalline[0, :], pos_nodalline[1, :], radius, xy) + + # set the color of the three parts + fc = [None, None, None] + if bb._plot_clr_order > 0: + fc[0] = pressure_colour + fc[1] = tension_colour + fc[2] = tension_colour + if bb._plot_curve_in_curve != 0: + fc[0] = tension_colour + if bb._plot_curve_in_curve < 1: + fc[1] = pressure_colour + fc[2] = tension_colour + else: + coll = [coll[i] for i in (0, 2, 1)] + fc[1] = pressure_colour + fc[2] = tension_colour + else: + fc[0] = tension_colour + fc[1] = pressure_colour + fc[2] = pressure_colour + if bb._plot_curve_in_curve != 0: + fc[0] = pressure_colour + if bb._plot_curve_in_curve < 1: + fc[1] = tension_colour + fc[2] = pressure_colour + else: + coll = [coll[i] for i in (0, 2, 1)] + fc[1] = tension_colour + fc[2] = pressure_colour + + if bb._pure_isotropic: + if abs(np.trace(bb._M)) > epsilon: + # use the circle as the most upper layer + coll = [coll[0]] + if bb._plot_clr_order < 0: + fc = [tension_colour] + else: + fc = [pressure_colour] + + # transform the patches to a path collection and set + # the appropriate attributes + collection = mpl_collections.PatchCollection(coll, match_original=False) + collection.set_facecolors(fc) + # Use the given axes to maintain the aspect ratio of beachballs on figure + # resize. + if axes is not None: + # This is what holds the aspect ratio (but breaks the positioning) + collection.set_transform(transforms.IdentityTransform()) + # Next is a dirty hack to fix the positioning: + # 1. Need to bring the all patches to the origin (0, 0). + for p in collection._paths: + p.vertices -= xy + # 2. Then use the offset property of the collection to position the + # patches + collection.set_offsets(xy) + collection._transOffset = axes.transData + collection.set_edgecolors(edgecolor) + collection.set_alpha(alpha) + collection.set_linewidth(linewidth) + collection.set_zorder(zorder) + return collection + + +def beachball(fm, linewidth=2, facecolor='b', bgcolor='w', edgecolor='k', + alpha=1.0, xy=(0, 0), width=200, size=100, nofill=False, + zorder=100, mopad_basis='USE', outfile=None, format=None, + fig=None): + """ + Draws a beach ball diagram of an earthquake focal mechanism. Based on + MoPaD. + + S1, D1, and R1, the strike, dip and rake of one of the focal planes, can + be vectors of multiple focal mechanisms. + + :param fm: Focal mechanism that is either number of mechanisms (NM) by 3 + (strike, dip, and rake) or NM x 6 (M11, M22, M33, M12, M13, M23 - the + six independent components of the moment tensor, where the coordinate + system is 1,2,3 = Up,South,East which equals r,theta,phi). The strike + is of the first plane, clockwise relative to north. + The dip is of the first plane, defined clockwise and perpendicular to + strike, relative to horizontal such that 0 is horizontal and 90 is + vertical. The rake is of the first focal plane solution. 90 moves the + hanging wall up-dip (thrust), 0 moves it in the strike direction + (left-lateral), -90 moves it down-dip (normal), and 180 moves it + opposite to strike (right-lateral). + :param facecolor: Color to use for quadrants of tension; can be a string, + e.g. ``'r'``, ``'b'`` or three component color vector, [R G B]. + Defaults to ``'b'`` (blue). + :param bgcolor: The background color. Defaults to ``'w'`` (white). + :param edgecolor: Color of the edges. Defaults to ``'k'`` (black). + :param alpha: The alpha level of the beach ball. Defaults to ``1.0`` + (opaque). + :param xy: Origin position of the beach ball as tuple. Defaults to + ``(0, 0)``. + :type width: int + :param width: Symbol size of beach ball. Defaults to ``200``. + :param size: Controls the number of interpolation points for the + curves. Minimum is automatically set to ``100``. + :param nofill: Do not fill the beach ball, but only plot the planes. + :param zorder: Set zorder. Artists with lower zorder values are drawn + first. + :param mopad_basis: The basis system. Defaults to ``'USE'``. See the + `Supported Basis Systems`_ section below for a full list of supported + systems. + :param outfile: Output file string. Also used to automatically + determine the output format. Supported file formats depend on your + matplotlib backend. Most backends support png, pdf, ps, eps and + svg. Defaults to ``None``. + :param format: Format of the graph picture. If no format is given the + outfile parameter will be used to try to automatically determine + the output format. If no format is found it defaults to png output. + If no outfile is specified but a format is, than a binary + imagestring will be returned. + Defaults to ``None``. + :param fig: Give an existing figure instance to plot into. New Figure if + set to ``None``. + + .. rubric:: _`Supported Basis Systems` + + ========= =================== ============================================= + Short Basis vectors Usage + ========= =================== ============================================= + ``'NED'`` North, East, Down Jost and Herrmann 1989 + ``'USE'`` Up, South, East Global CMT Catalog, Larson et al. 2010 + ``'XYZ'`` East, North, Up General formulation, Jost and Herrmann 1989 + ``'RT'`` Radial, Transverse, psmeca (GMT), Wessel and Smith 1999 + Tangential + ``'NWU'`` North, West, Up Stein and Wysession 2003 + ========= =================== ============================================= + + .. rubric:: Examples + + (1) Using basis system ``'NED'``. + + >>> from obspy.imaging.mopad_wrapper import beachball + >>> mt = [1, 2, 3, -4, -5, -10] + >>> beachball(mt, mopad_basis='NED') #doctest: +SKIP + + .. plot:: + + from obspy.imaging.mopad_wrapper import beachball + mt = [1, 2, 3, -4, -5, -10] + beachball(mt, mopad_basis='NED') + """ + mopad_kwargs = {} + loc = locals() + # map to kwargs used in mopad + for key in KWARG_MAP: + value = loc[key] + for mopad_key in KWARG_MAP[key]: + mopad_kwargs[mopad_key] = value + # convert from points to size in cm + for key in ['plot_aux_plot_size', 'plot_size']: + # 100.0 is matplotlib's default DPI for savefig + mopad_kwargs[key] = mopad_kwargs[key] / 100.0 * 2.54 + # use nofill kwarg + + mt = mopad_MomentTensor(fm, system=mopad_basis) + bb = mopad_BeachBall(mt, npoints=size) + + # show plot in a window + if outfile is None: + bb.ploBB(mopad_kwargs) + # save plot to file + else: + # no format specified, parse it from outfile name + if mopad_kwargs['plot_outfile_format'] is None: + mopad_kwargs['plot_outfile_format'] = \ + mopad_kwargs['plot_outfile'].split(".")[-1] + else: + # append file format if not already at end of outfile + if not mopad_kwargs['plot_outfile'].endswith( + mopad_kwargs['plot_outfile_format']): + mopad_kwargs['plot_outfile'] += "." + \ + mopad_kwargs['plot_outfile_format'] + bb.save_BB(mopad_kwargs) + diff --git a/SCRIPTS/plot_mtwaveform2.py b/SCRIPTS/plot_mtwaveform2.py new file mode 100644 index 0000000..e0e0c42 --- /dev/null +++ b/SCRIPTS/plot_mtwaveform2.py @@ -0,0 +1,706 @@ +# plot_mtwaveform.py +# Script to plot waveform fit +# Yuexin Li and Gabriel Rogow-Patt +# First version: April, 2019 +# Second version: 2021 + +import os +import glob +import numpy as np +import matplotlib.pyplot as plt +import matplotlib.transforms as transforms +#from obspy.imaging.beachball import beach,beachball +import math +import fpsol +import pandas as pd +#import sys +#sys.path.append('./') +from mopad_wrapper import beach +from numpy import linalg as la +import argparse +from matplotlib.backends.backend_pdf import PdfPages + + +parser = argparse.ArgumentParser() +parser.add_argument('--nodisplaywindow', action='store_true', help='prevents the plot(s) from opening any display window(s)') +parser.add_argument('--nosavefig', action='store_true', help='prevents the plot(s) from saving to any file(s)') +parser.add_argument('--noplotazimuth', action='store_true', help='prevents the station names from plotting on the beachball(s)') +parser.add_argument('--plotseparatebeachball', action='store_true', help='saves extra file(s) with the beachball diagram(s)') +parser.add_argument('--plottape', action='store_true', help='saves extra file(s) with the Tape Plot diagram(s)') +parser.add_argument("mtinv_file", action='extend', nargs='+') +args = parser.parse_args() + +# Python script to substitute "tdmt_plot_gmt5.perl mtinvout" + + +moscale=1 #Units: Nm #scale value for moment estimates and Mw + +# Plotting while reading +for mtinput in args.mtinv_file: + fig=plt.figure(figsize=(9,6)) + mark=0 + index=-1 + ncol=4 + DT=[];DR=[];DZ=[] #List for data + ST=[];SR=[];SZ=[] + filenameazlist=[] + f=open(mtinput,'r') + linelist = f.readlines() + if len(linelist[-1])!=0: + linelist.append('') + #for line in f.readlines(): + with PdfPages('fm_waveform(' + mtinput + ').pdf') as pdf: + for line in linelist: + line=line.strip() + if len(line)==0: + mark=0 + index=index+1 + if index>0: + index=index-1 + nstascale=nsta/4 + ax3 = fig.add_subplot(nsta,ncol,1+(index%8)*ncol) + trans = transforms.blended_transform_factory(ax3.transData, ax3.transAxes) + #plt.subplot(nsta,4,1+(index-1)*4) + plt.plot(DT,'k',linewidth=.75) + #plt.plot(ST,'r--',linewidth=.75, scaley=False) + plt.axis('off') + plt.text(-.1,.5,filename,fontsize=8,transform=ax3.transAxes,horizontalalignment='right') + if (index%8)==0: + plt.title('Tangential') + text_str='Distance = %d km Azimuth = %d Max Amp = %.2e cm Zcorr = %d VR = %d' % (dist, Az, np.max(np.abs([DT,DR,DZ])), Zcor, VR) + plt.text(0,-.15, text_str,fontsize=8,transform=trans) + + + ax6 = fig.add_subplot(nsta,ncol,2+(index%8)*ncol,sharey=ax3) + #plt.subplot(nsta,ncol,2+(index-1)*ncol) + plt.plot(DR,'k',linewidth=.75) + #plt.plot(SR,'r--',linewidth=.75, scaley=False) + plt.axis('off') + if (index%8)==0: + plt.title('Radial') + + + ax2 = fig.add_subplot(nsta,ncol,3+(index%8)*ncol,sharey=ax3) + #plt.subplot(nsta,ncol,3+(index-1)*ncol) + plt.plot(DZ,'k',linewidth=.75) + #plt.plot(SZ,'r--',linewidth=.75, scaley=False) + plt.axis('off') + if (index%8)==0: + plt.title('Vertical') + + trans2 = transforms.blended_transform_factory(ax2.transData, ax2.transAxes) + if DZ.index(min(DZ))2: + ax5 = fig.add_subplot(nsta,ncol,nsta*ncol) + if nsta>4: + ax5 = fig.add_subplot(4,ncol,4*ncol) + if nsta1==True: + ax5 = fig.add_subplot(nsta,ncol,nsta*ncol-2) + ax5.add_collection(beach1) + ax5.set_aspect("equal") + ax5.set_axis_off() + + #plots the filenames on the beachball + if args.noplotazimuth==False: + for filenameaz in filenameazlist: + Az = (90 - (float((filenameaz.split())[0]))) + if Az < 0: + Az+=360 + filename = (filenameaz.split())[1] + if Az>0 and Az<=90: + ax5.text((.5+0.475*math.cos(np.deg2rad(Az))), (.5+0.475*math.sin(np.deg2rad(Az))), filename, ha='left', va='bottom') + if Az>90 and Az<=180: + ax5.text((.5+0.475*math.cos(np.deg2rad(Az))), (.5+0.475*math.sin(np.deg2rad(Az))), filename, ha='right', va='bottom') + if Az>180 and Az<=270: + ax5.text((.5+0.475*math.cos(np.deg2rad(Az))), (.5+0.475*math.sin(np.deg2rad(Az))), filename, ha='right', va='top') + if Az>270 and Az<=360 or Az==0: + ax5.text((.5+0.475*math.cos(np.deg2rad(Az))), (.5+0.475*math.sin(np.deg2rad(Az))), filename, ha='left', va='top') + + #plots and saves the current plot and makes a new page if there's more than 8 stations + if index==truensta or index==8: + if args.nosavefig==False: + pdf.savefig() + if args.nodisplaywindow==False: + fig.canvas.manager.set_window_title(mtinput) + plt.show() + plt.close() + if truensta>index: + fig=plt.figure(figsize=(9,6)) + if (truensta-index)<8: + nsta=truensta-index + if nsta==1: + nsta=2 + nsta1=True + else: + nsta=8 + + + + + +# """ +# if index==nsta-1: +# # Plot beachball +# # In Obspy: M11, M22, M33, M12, M13, M23 +# ax2=plt.subplot(nsta,ncol,4+index*ncol) +# ###plt.axis('equal') +# mt=[mxx,myy,mzz,mxy,mxz,myz] +# beach=beach(mt, xy=(0.5, 0.5), width=0.6) +# ax2.add_collection(beach) +# """ + + #clear data list + DT=[];DR=[];DZ=[] + ST=[];SR=[];SZ=[] + continue + else: + if mark==1: + depth=float((line.split())[0]) + variance=float((line.split())[1]) + VRtot=float((line.split())[2]) + nsta=int((line.split())[3]) + npages=math.ceil(nsta/8) + truensta=nsta + if nsta>8: + nsta=8 + nsta1=False + if nsta==1: + nsta=2 + nsta1=True + mark=0 #reset + continue + elif mark==2: + mxx=float((line.split())[0]) + mxy=float((line.split())[1]) + mxz=float((line.split())[2]) + myy=float((line.split())[3]) + myz=float((line.split())[4]) + mzz=float((line.split())[5]) + #note if applied to tensors and passed to + #mopad the very large values leads to plotting errors can result + Mfull=np.array([[mxx,mxy,mxz],[mxy,myy,myz],[mxz,myz,mzz]]) #Construct Moment Tensor Matrix + L, V = la.eig(Mfull) + + if L[0]==L[1] and L[0]==L[2]: + print('Pure Isotropic') #deal with this perfect isotropic case + mxx=mxx+mxx*0.0001 + Mfull[0,]=Mfull[0,]+Mfull[0,]*0.0001 + + Moiso=(mxx+myy+mzz)/3 #Esimate the Scalar Moment + Mdev=Mfull - np.identity(3)*Moiso #Compute the Deviatoric Moment Tensor + + w, v = la.eig(Mdev) #Calculate eigenvalues(w) and eigenvectors(v) + + Motot=(abs(Moiso) + max(abs(w)))*moscale #Compute Bower and Hudson Total Moment and the + Mw=(np.log10(Motot)-16.1)/1.5 #Moment Magnitude + + Moiso=Moiso*moscale #Now scale Moiso and Modev for plotting later + Modev=max(abs(w))*moscale #Modev is maximum deviatoric eigenvalue in absolute sense #It is used to scale deviatoric tensor into DC and CLVD components + + + #Order the eigenvalues and eigenvectors + indx=np.argsort(abs(w)) #Sort by absolute value of w + m3=w[indx[2]] + m2=w[indx[1]] + m1=w[indx[0]] + eig3=v[:,indx[2]] + eig2=v[:,indx[1]] + eig1=v[:,indx[0]] + + #Order eigenvalues for Tape & Tape Lune + indx=np.argsort(L) #Sort retaining sign + l1=L[indx[2]] + l2=L[indx[1]] + l3=L[indx[0]] + + #Calculate Tape & Tape gamma and beta parameters testing for pure isotropic singularity + #These parameters, gamma, beta and delta are used later to plot the source-type in the Tape and Tape Lune perspective + if l1 == l2 and l1 == l3 and l1 > 0.: + gamma=0. + beta=0. + delta=90. - beta + elif l1 == l2 and l1 == l3 and l1 < 0.: + gamma=0. + beta=0. + delta=beta - 90. + else: + gamma=math.atan((-l1+2*l2-l3)/(np.sqrt(3)*(l1-l3)))*180/math.pi + beta=math.acos((l1+l2+l3)/(np.sqrt(3)*np.sqrt(L.dot(L))))*180/math.pi + delta=90. - beta + + #Construct Dyadics + #Dyadics represent fundamental vector-dipole tensors from which double-couples, CLVDs, tensile-cracks, etc. are constructed + #See Jost and Herrman for details + a3=np.array((eig3, eig3, eig3)).transpose() + a2=np.array((eig2, eig2, eig2)).transpose() + a1=np.array((eig1, eig1, eig1)).transpose() + a3a3=a3*a3.transpose() + a2a2=a2*a2.transpose() + a1a1=a1*a1.transpose() + + #Perform DC-CLVD Decomposition + F=-1*m1/m3 + Mdc=m3*(1-2*F)*(a3a3-a2a2) #Double-Couple Moment Tensor + Mclvd=m3*F*(2*a3a3-a2a2-a1a1) #CLVD Moment Tensor + Modc=abs(m3*(1-2*F))*moscale #Double-Couple Moment + Moclvd=abs(2*m3*F)*moscale #CLVD Moment - to be consistent with Hudson decomp + kappa=Moiso/Motot #Hudson Plot kappa + T=(2*m1)/abs(m3) #Hudson Plot T + periso=abs(Moiso/Motot) + perdc=abs(Modc/Modev) + perclvd=abs(Moclvd/Modev) + + #Determine Strike, Rake, Dip + if Modc != 0.: + w, v = la.eig(Mdc) + indx=np.argsort(w) #Sort by absolute value of w + eig3=v[:,indx[2]] + eig2=v[:,indx[1]] + eig1=v[:,indx[0]] + nu1=(1/np.sqrt(2))*(eig3-eig1) #fault normal vector + u1=(1/np.sqrt(2))*(eig1+eig3) #slip vector + [strike1, rake1, dip1]=fpsol.fpsol(nu1,u1) + nu2=(1/np.sqrt(2))*(eig1+eig3) #conjugate fault normal vector + u2=(1/np.sqrt(2))*(eig3-eig1) #conjugate slip vector + [strike2, rake2, dip2]=fpsol.fpsol(nu2,u2) + + mark=0 + continue + elif mark==3: + st1=float((line.split())[0]) + rk1=float((line.split())[1]) + dp1=float((line.split())[2]) + st2=float((line.split())[3]) + rk2=float((line.split())[4]) + dp2=float((line.split())[5]) + mark=0 + continue + elif mark==4: + pdc=float((line.split())[0]) + pclvd=float((line.split())[1]) + piso=float((line.split())[2]) + mark=0 #reset + continue + elif mark==5: + filename=line + #strips anything after the first '_' or '.' in the filename + terminator = filename.find('_') + if terminator==-1: + terminator = filename.find('.') + if terminator!=-1: + filename = filename[:terminator] + mark=0 + continue + elif mark==6: + dt=float((line.split())[0]) + npts=int((line.split())[1]) + dist=float((line.split())[2]) + Az=float((line.split())[3]) + filenameazlist.append(str(Az) + " " + filename) + Zcor=int((line.split())[4]) + VR=float((line.split())[5]) + mark=0 + continue + elif mark==7: + DT.append(float((line.split())[0])) + DR.append(float((line.split())[1])) + DZ.append(float((line.split())[2])) + ST.append(float((line.split())[3])) + SR.append(float((line.split())[4])) + SZ.append(float((line.split())[5])) + + # Locate yourself... + if line.startswith('#depth'): + mark=1; + elif line.startswith('#mxx'): + mark=2 + elif line.startswith('#st1'): + mark=3 + elif line.startswith('#pdc'): + mark=4 + elif line.startswith('#filename'): + mark=5 + elif line.startswith('#dt'): + mark=6 + elif line.startswith('#data'): + mark=7 + + f.close() + + + if args.plotseparatebeachball==True: + fig=plt.figure(figsize=(30,30)) + mt=np.array((mxx,myy,mzz,mxy,mxz,myz)) + #ax2=beachball(mt,facecolor='k',outfile='fm_beachball.pdf') + beach1 = beach(mt,xy=(0.5,0.5),width=0.95,mopad_basis='NED',show_iso=True,facecolor='black') + ax2 = fig.add_subplot(1,1,1) + ax2.add_collection(beach1) + ax2.set_aspect("equal") + ax2.set_axis_off() + if args.nosavefig==False: + if args.noplotazimuth==False: + for filenameaz in filenameazlist: + Az = 90 - float((filenameaz.split())[0]) + if Az < 0: + Az+=360 + filename = (filenameaz.split())[1] + if Az>0 and Az<=90: + ax2.text((.5+0.475*math.cos(np.deg2rad(Az))), (.5+0.475*math.sin(np.deg2rad(Az))), filename, ha='left', va='bottom', fontsize=70) + if Az>90 and Az<=180: + ax2.text((.5+0.475*math.cos(np.deg2rad(Az))), (.5+0.475*math.sin(np.deg2rad(Az))), filename, ha='right', va='bottom', fontsize=70) + if Az>180 and Az<=270: + ax2.text((.5+0.475*math.cos(np.deg2rad(Az))), (.5+0.475*math.sin(np.deg2rad(Az))), filename, ha='right', va='top', fontsize=70) + if Az>270 and Az<=360 or Az==0: + ax2.text((.5+0.475*math.cos(np.deg2rad(Az))), (.5+0.475*math.sin(np.deg2rad(Az))), filename, ha='left', va='top', fontsize=70) + fig.savefig('fm_beachball(' + mtinput + ').pdf') + plt.close() + + + + if args.plottape==True: + nssfilename='' + nssplotflag=0 + + Mfull=np.array([[mxx,mxy,mxz],[mxy,myy,myz],[mxz,myz,mzz]]) #Construct Moment Tensor Matrix + L, V = la.eig(Mfull) + + if L[0]==L[1] and L[0]==L[2]: + print('Pure Isotropic') #deal with this perfect isotropic case + mxx=mxx+mxx*0.0001 + Mfull[0,]=Mfull[0,]+Mfull[0,]*0.0001 + + Moiso=(mxx+myy+mzz)/3 #Esimate the Scalar Moment + + Mdev=Mfull - np.identity(3)*Moiso #Compute the Deviatoric Moment Tensor + + w, v = la.eig(Mdev) #Calculate eigenvalues(w) and eigenvectors(v) + + Motot=(abs(Moiso) + max(abs(w)))*moscale #Compute Bower and Hudson Total Moment and the + Mw=(np.log10(Motot)-9.1)/1.5 #Moment Magnitude + + Moiso=Moiso*moscale #Now scale Moiso and Modev for plotting later + Modev=max(abs(w))*moscale #Modev is maximum deviatoric eigenvalue in absolute sense + #It is used to scale deviatoric tensor into DC and CLVD components + + + #Order the eigenvalues and eigenvectors + indx=np.argsort(abs(w)) #Sort by absolute value of w + m3=w[indx[2]] + m2=w[indx[1]] + m1=w[indx[0]] + eig3=v[:,indx[2]] + eig2=v[:,indx[1]] + eig1=v[:,indx[0]] + + #Order eigenvalues for Tape & Tape Lune + indx=np.argsort(L) #Sort retaining sign + l1=L[indx[2]] + l2=L[indx[1]] + l3=L[indx[0]] + + #Calculate Tape & Tape gamma and beta parameters testing for pure isotropic singularity + #These parameters, gamma, beta and delta are used later to plot the source-type in the Tape and Tape Lune perspective + if l1 == l2 and l1 == l3 and l1 > 0.: + gamma=0. + beta=0. + delta=90. - beta + elif l1 == l2 and l1 == l3 and l1 < 0.: + gamma=0. + beta=0. + delta=beta - 90. + else: + gamma=math.atan((-l1+2*l2-l3)/(np.sqrt(3)*(l1-l3)))*180/math.pi + beta=math.acos((l1+l2+l3)/(np.sqrt(3)*np.sqrt(L.dot(L))))*180/math.pi + delta=90. - beta + + #Construct Dyadics + #Dyadics represent fundamental vector-dipole tensors from which double-couples, CLVDs, tensile-cracks, etc. are constructed + #See Jost and Herrman for details + a3=np.array((eig3, eig3, eig3)).transpose() + a2=np.array((eig2, eig2, eig2)).transpose() + a1=np.array((eig1, eig1, eig1)).transpose() + a3a3=a3*a3.transpose() + a2a2=a2*a2.transpose() + a1a1=a1*a1.transpose() + + #Perform DC-CLVD Decomposition + F=-1*m1/m3 + Mdc=m3*(1-2*F)*(a3a3-a2a2) #Double-Couple Moment Tensor + Mclvd=m3*F*(2*a3a3-a2a2-a1a1) #CLVD Moment Tensor + Modc=abs(m3*(1-2*F))*moscale #Double-Couple Moment + Moclvd=abs(2*m3*F)*moscale #CLVD Moment - to be consistent with Hudson decomp + kappa=Moiso/Motot #Hudson Plot kappa + T=(2*m1)/abs(m3) #Hudson Plot T + periso=abs(Moiso/Motot) + perdc=abs(Modc/Modev) + perclvd=abs(Moclvd/Modev) + + #Determine Strike, Rake, Dip + if Modc != 0.: + w, v = la.eig(Mdc) + indx=np.argsort(w) #Sort by absolute value of w + eig3=v[:,indx[2]] + eig2=v[:,indx[1]] + eig1=v[:,indx[0]] + nu1=(1/np.sqrt(2))*(eig3-eig1) #fault normal vector + u1=(1/np.sqrt(2))*(eig1+eig3) #slip vector + [strike1, rake1, dip1]=fpsol.fpsol(nu1,u1) + nu2=(1/np.sqrt(2))*(eig1+eig3) #conjugate fault normal vector + u2=(1/np.sqrt(2))*(eig3-eig1) #conjugate slip vector + [strike2, rake2, dip2]=fpsol.fpsol(nu2,u2) + + #Construct Moment Tensor arrays for plotting + fm=np.array((mxx,myy,mzz,mxy,mxz,myz)) + devm=np.array((Mdev[0,0], Mdev[1,1], Mdev[2,2], Mdev[0,1], Mdev[0,2], Mdev[1,2])) + dcm=np.array((Mdc[0,0], Mdc[1,1], Mdc[2,2], Mdc[0,1], Mdc[0,2], Mdc[1,2])) + clvdm=np.array((Mclvd[0,0], Mclvd[1,1], Mclvd[2,2], Mclvd[0,1], Mclvd[0,2], Mclvd[1,2])) + + #Compute Tape and Tape parameters for plotting the NSS + #Read the NSS output for plotting solution space on Tape and Tape Lune + if (nssplotflag == 1): + data=pd.read_csv(nssfilename, sep='\s+', header=None) + + d=np.array(data) + lam=np.array((d[:,0],d[:,1],d[:,2])).transpose() #eigenvalues are column ordered each row is a individual tuple + lam.sort(axis=1) #sort eigenvalue rows lam1=d[:,2], lam2=d[:,1], lam3=d[:,0] + vr=d[:,3] + + l1=lam[:,2] + l2=lam[:,1] + l3=lam[:,0] + L=np.sqrt(l1**2 + l2**2 + l3**2) + + #Test for pure isotropic singularity and compute gamma, beta and delta + n=len(l1) + GAMMA=np.zeros(n) + BETA=np.zeros(n) + DELTA=np.zeros(n) + for i in range(0,n,1): + if l1[i] == l2[i] and l1[i] == l3[i] and l1[i] > 0.: + GAMMA[i]=0. + BETA[i]=0. + DELTA[i]=90. - BETA[i] + elif l1[i] == l2[i] and l1[i] == l3[i] and l1[i] < 0.: + GAMMA[i]=0. + BETA[i]=0. + DELTA[i]=BETA[i] - 90. + else: + GAMMA[i]=np.arctan((-l1[i]+2*l2[i]-l3[i])/(np.sqrt(3)*(l1[i]-l3[i])))*180/np.pi + BETA[i]=np.arccos((l1[i]+l2[i]+l3[i])/(np.sqrt(3)*L[i]))*180/np.pi + DELTA[i]=90. - BETA[i] + #Plot Tape and Tape Lune + #Initial code from 'Ajean' https://stackoverflow.com/questions/32209496/matplotlib-basemap-fundamental-lune + + import cartopy.crs as ccrs + import matplotlib.path as mpath + from scipy.interpolate import griddata + + # Mollweide projection + fig = plt.figure(figsize=(15,15)) + ax = fig.add_subplot(111, projection=ccrs.LambertAzimuthalEqualArea()) #This seems best + ax.set_extent([-30, 30, -90, 90]) + xstep=30/5 #20% lines + ystep=90/5 #20% lines + xgrds=np.arange(-30.0, 31.0, xstep) + ygrds=np.arange(-90.0, 91.0, ystep) + ax.gridlines(xlocs=xgrds,ylocs=ygrds) + + # Here I define a matplotlib Path object to use as the boundary + outlinex = np.concatenate([[-30],np.tile(-30,180), np.tile(30,180),[-30]]) + outliney = np.concatenate([[-90],np.arange(-90,90),np.arange(89,-91,-1),[-90]]) + outlinecodes = np.array([mpath.Path.MOVETO]+[mpath.Path.LINETO]*360+[mpath.Path.MOVETO]) + outlinepath = mpath.Path(np.column_stack([outlinex[::-1], outliney[::-1]]), outlinecodes[::-1]) + ax.set_boundary(outlinepath, transform=ccrs.Geodetic()) + + + + #Fundamental Source-Types + ax.plot(0, 90., 'ro', markersize=10, transform=ccrs.Geodetic()) #Explosion + ax.text(30,87,'Explosion',fontsize=12,transform=ccrs.Geodetic()) + ax.plot(0, -90., 'ro', markersize=10, transform=ccrs.Geodetic()) #Implosion + ax.text(70,-88,'Implosion',fontsize=12,transform=ccrs.Geodetic()) + ax.plot(0, 0, 'ro', markersize=10, transform=ccrs.Geodetic()) #Double-Couple + ax.text(0,2,'DC',fontsize=12,transform=ccrs.Geodetic()) + ax.plot(30, 0, 'ro', markersize=10, transform=ccrs.Geodetic()) #Negative CLVD + ax.text(31,0,'-CLVD',fontsize=12,transform=ccrs.Geodetic()) + ax.plot(-30, 0, 'ro', markersize=10, transform=ccrs.Geodetic()) #Positive CLVD + ax.text(-39,0,'+CLVD',fontsize=12,transform=ccrs.Geodetic()) + LAM=np.array([3,1,1]) + x=math.atan((-LAM[0]+2*LAM[1]-LAM[2])/(np.sqrt(3)*(LAM[0]-LAM[2])))*180/math.pi + y=math.acos((LAM[0]+LAM[1]+LAM[2])/(np.sqrt(3)*np.sqrt(LAM.dot(LAM))))*180/math.pi + y=90. - y + ax.plot(x, y, 'ro', markersize=10, transform=ccrs.Geodetic()) #Tensile Crack + ax.text(x-15,y-2,'+Crack',fontsize=12,transform=ccrs.Geodetic()) + LAM=np.array([-1,-1, -3]) #note ordering is due to sign considered ordering + x=math.atan((-LAM[0]+2*LAM[1]-LAM[2])/(np.sqrt(3)*(LAM[0]-LAM[2])))*180/math.pi + y=math.acos((LAM[0]+LAM[1]+LAM[2])/(np.sqrt(3)*np.sqrt(LAM.dot(LAM))))*180/math.pi + y=90. - y + ax.plot(x, y, 'ro', markersize=10, transform=ccrs.Geodetic()) #Closing Crack + ax.text(x+3,y-1,'-Crack',fontsize=12,transform=ccrs.Geodetic()) + LAM=np.array([1,0,0]) + x=math.atan((-LAM[0]+2*LAM[1]-LAM[2])/(np.sqrt(3)*(LAM[0]-LAM[2])))*180/math.pi + y=math.acos((LAM[0]+LAM[1]+LAM[2])/(np.sqrt(3)*np.sqrt(LAM.dot(LAM))))*180/math.pi + y=90. - y + ax.plot(x, y, 'ro', markersize=10, transform=ccrs.Geodetic()) #LVD + ax.text(x-10,y-2,'+LVD',fontsize=12,transform=ccrs.Geodetic()) + LAM=np.array([0,0,-1]) + x=math.atan((-LAM[0]+2*LAM[1]-LAM[2])/(np.sqrt(3)*(LAM[0]-LAM[2])))*180/math.pi + y=math.acos((LAM[0]+LAM[1]+LAM[2])/(np.sqrt(3)*np.sqrt(LAM.dot(LAM))))*180/math.pi + y=90. - y + ax.plot(x, y, 'ro', markersize=10, transform=ccrs.Geodetic()) #LVD + ax.text(x+3,y-0,'-LVD',fontsize=12,transform=ccrs.Geodetic()) + + + + # Plot Source-Type Solution Space + # Comment out if not available + if (nssplotflag == 1): + c = plt.cm.plasma(np.arange(0.,100.,10.)/100) + x=np.arange(-30.,31,5) #The third argument, the step controls smoothing + y=np.arange(-90,90,5) + X, Y= np.meshgrid(x, y) + idx=np.nonzero(vr >= 10.) + Z = griddata((GAMMA[idx],DELTA[idx]),vr[idx],(X,Y), method='cubic') + cb=ax.contourf(X, Y, Z, 20, transform=ccrs.PlateCarree(),cmap='Blues') + + #Plot MT Solution + ax.plot(gamma, delta, 'ks', markersize=14, transform=ccrs.Geodetic()) + + # Add colorbar, make sure to specify tick locations to match desired ticklabels + #ax.set_title('Source-Type Lune') + if (nssplotflag == 1): + position=fig.add_axes([0.70,0.3,0.025,0.4]) ## the parameters are the specified position you set + cbar=plt.colorbar(cb, cax=position, orientation='vertical',ticks=[10, 20, 30, 40, 50, 60, 70, 80, 90, 100], spacing='uniform',shrink=0.5) + cbar.set_label('Variance Reduction (%)', rotation=90, size=14) + + fig.savefig('fm_tape(' + mtinput +').pdf') + + + #Beginning of third section of code from Moment Tensor Decomposition Tool + + #Construct Moment Tensor arrays for plotting + mt=np.array((mxx,myy,mzz,mxy,mxz,myz)) + devm=np.array((Mdev[0,0], Mdev[1,1], Mdev[2,2], Mdev[0,1], Mdev[0,2], Mdev[1,2])) + dcm=np.array((Mdc[0,0], Mdc[1,1], Mdc[2,2], Mdc[0,1], Mdc[0,2], Mdc[1,2])) + clvdm=np.array((Mclvd[0,0], Mclvd[1,1], Mclvd[2,2], Mclvd[0,1], Mclvd[0,2], Mclvd[1,2])) + + fig=plt.figure(figsize=(8,8)) + threshold=0.; #initialize threshold + if Moiso != 0.0: + beach1 = beach(mt,xy=(0.5,0.5),width=0.95,mopad_basis='NED',show_iso=True,facecolor='black') + ax2 = fig.add_subplot(2,2,1) + ax2.add_collection(beach1) + ax2.set_aspect("equal") + ax2.set_axis_off() + buf="Full MT {0:.2e}".format(Motot) + ax2.set(title=buf) + threshold=Moiso*0.00001 #Set Modev threshold to a small value of Mosio if there is a Moiso + + # Second one plots deviatoric mt + if Modev != 0.0 and Modev/(Motot) > 0.001: #plot only significant deviatoric parts + beach1 = beach(devm,xy=(0.5,0.5),width=0.95*Modev/Motot,mopad_basis='NED',facecolor='black') + ax3 = fig.add_subplot(2,2,2) + ax3.add_collection(beach1) + ax3.set_aspect("equal") + ax3.set_axis_off() + buf="Dev MT {0:.2e}".format(Modev) + ax3.set(title=buf) + + # Third one plots dc + if Modc != 0.0 and Modc/Motot > 0.001: #plot only significant double-couple parts + beach1 = beach(dcm,xy=(0.5,0.5),width=0.95*Modc/Modev,mopad_basis='NED',facecolor='black') + ax3 = fig.add_subplot(2,2,3) + ax3.add_collection(beach1) + ax3.set_aspect("equal") + ax3.set_axis_off() + buf="DC MT {0:.2e}".format(Modc) + ax3.set(title=buf) + + # Forth one plots dc + if Moclvd != 0.0 and Moclvd/Motot > 0.001: #plot only signicant clvd parts + beach1 = beach(clvdm,xy=(0.5,0.5),width=0.95*Moclvd/Modev,mopad_basis='NED',facecolor='black') + ax3 = fig.add_subplot(2,2,4) + ax3.add_collection(beach1) + ax3.set_aspect("equal") + ax3.set_axis_off() + buf="CLVD MT {0:.2e}".format(Moclvd) + ax3.set(title=buf) + + if args.nosavefig==False: + fig.savefig('fm_beachball_components(' + mtinput + ').pdf') + #plt.show() + plt.close() diff --git a/TDMT_GMT/.DS_Store b/TDMT_GMT/.DS_Store new file mode 100755 index 0000000..5008ddf Binary files /dev/null and b/TDMT_GMT/.DS_Store differ diff --git a/TDMT_GMT/README_mtmaniptest b/TDMT_GMT/README_mtmaniptest new file mode 100644 index 0000000..45a595a --- /dev/null +++ b/TDMT_GMT/README_mtmaniptest @@ -0,0 +1 @@ +mtmanip -k 1.0 -6.714e+23 7.812e+23 -7.675e+22 4.578e+22 -6.889e+23 6.257e+23 -W -K diff --git a/TDMT_GMT/correl2b.c b/TDMT_GMT/correl2b.c new file mode 100755 index 0000000..cd520ee --- /dev/null +++ b/TDMT_GMT/correl2b.c @@ -0,0 +1,367 @@ +/*Copyright (c) Douglas Dreger +Berkeley Seismological Laboratory +University of California, Berkeley */ +#include +#include"tdmtinv_iso.h" + +int correlate(ss,gg,i,np2) + int i, np2; + struct DATA *ss; + struct GREEN *gg; +{ + int j, Zcor[8], zvalue; + float cormax[8], maximum=0.0; + float *data1, *data2, *ans; + + + data1=vector(1,np2); + data2=vector(1,np2); + ans =vector(1,2*np2); + + for(j=0; j < 8; j++) /*Initialize cormax for each component*/ + { + Zcor[j] =0; + cormax[j]=0.0; + } + + for(j=0; j < np2; j++) /*Load Tangential Data*/ + if(j >= (ss[i].np)) + data1[j+1]=0.0; + else + data1[j+1]=ss[i].t[j]; + + for(j=0; j < np2; j++) /*Load TSS*/ + if(j >= (gg[i].np)) + data2[j+1]=0.0; + else + data2[j+1]=gg[i].u1[j]; + + correl(data1,data2,np2,ans); + + for(j=0; j < np2/2; j++) + if(cormax[0] < fabs(ans[j+1])) + { + cormax[0] = fabs(ans[j+1]); + Zcor[0] = j; + } + if(maximum < cormax[0]) + { + zvalue=Zcor[0]; + maximum=cormax[0]; + } + + for(j=0; j < np2; j++) /*Load TDS*/ + if(j >= (gg[i].np)) + data2[j+1]=0.0; + else + data2[j+1]=gg[i].u2[j]; + + correl(data1,data2,np2,ans); + + for(j=0; j < np2/2; j++) + if(cormax[1] < fabs(ans[j+1])) + { + cormax[1] = fabs(ans[j+1]); + Zcor[1] = j; + } + if(maximum < cormax[1]) + { + zvalue=Zcor[1]; + maximum=cormax[1]; + } + + for(j=0; j < np2; j++) /*Load Radial Data*/ + if(j >= (ss[i].np)) + data1[j+1]=0.0; + else + data1[j+1]=ss[i].r[j]; + + for(j=0; j < np2; j++) /*Load RSS*/ + if(j >= (gg[i].np)) + data2[j+1]=0.0; + else + data2[j+1]=gg[i].u3[j]; + + correl(data1,data2,np2,ans); + + for(j=0; j < np2/2; j++) + if(cormax[2] < fabs(ans[j+1])) + { + cormax[2] = fabs(ans[j+1]); + Zcor[2] = j; + } + if(maximum < cormax[2]) + { + zvalue=Zcor[2]; + maximum=cormax[2]; + } + + for(j=0; j < np2; j++) /*Load RDS*/ + if(j >= (gg[i].np)) + data2[j+1]=0.0; + else + data2[j+1]=gg[i].u4[j]; + + correl(data1,data2,np2,ans); + + for(j=0; j < np2/2; j++) + if(cormax[3] < fabs(ans[j+1])) + { + cormax[3] = fabs(ans[j+1]); + Zcor[3] = j; + } + if(maximum < cormax[3]) + { + zvalue=Zcor[3]; + maximum=cormax[3]; + } + + for(j=0; j < np2; j++) /*Load RDD*/ + if(j >= (gg[i].np)) + data2[j+1]=0.0; + else + data2[j+1]=gg[i].u5[j]; + + correl(data1,data2,np2,ans); + + for(j=0; j < np2/2; j++) + if(cormax[4] < fabs(ans[j+1])) + { + cormax[4] = fabs(ans[j+1]); + Zcor[4] = j; + } + if(maximum < cormax[4]) + { + zvalue=Zcor[4]; + maximum=cormax[4]; + } + + for(j=0; j < np2; j++) /*Load Vertical Data*/ + if(j >= (ss[i].np)) + data1[j+1]=0.0; + else + data1[j+1]=ss[i].z[j]; + + for(j=0; j < np2; j++) /*Load ZSS*/ + if(j >= (gg[i].np)) + data2[j+1]=0.0; + else + data2[j+1]=gg[i].u6[j]; + + correl(data1,data2,np2,ans); + + for(j=0; j < np2/2; j++) + if(cormax[5] < fabs(ans[j+1])) + { + cormax[5] = fabs(ans[j+1]); + Zcor[5] = j; + } + if(maximum < cormax[5]) + { + zvalue=Zcor[5]; + maximum=cormax[5]; + } + + for(j=0; j < np2; j++) /*Load ZDS*/ + if(j >= (gg[i].np)) + data2[j+1]=0.0; + else + data2[j+1]=gg[i].u7[j]; + + correl(data1,data2,np2,ans); + + for(j=0; j < np2/2; j++) + if(cormax[6] < fabs(ans[j+1])) + { + cormax[6] = fabs(ans[j+1]); + Zcor[6] = j; + } + if(maximum < cormax[6]) + { + zvalue=Zcor[6]; + maximum=cormax[6]; + } + + for(j=0; j < np2; j++) /*Load ZDD*/ + if(j >= (gg[i].np)) + data2[j+1]=0.0; + else + data2[j+1]=gg[i].u8[j]; + + correl(data1,data2,np2,ans); + + for(j=0; j < np2/2; j++) + if(cormax[7] < fabs(ans[j+1])) + { + cormax[7] = fabs(ans[j+1]); + Zcor[7] = j; + } + if(maximum < cormax[7]) + { + zvalue=Zcor[7]; + maximum=cormax[7]; + } + + + free_vector(data1,1,np2); + free_vector(data2,1,np2); + free_vector(ans,1,2*np2); + + return(zvalue); + +}/*END CORRELATE*/ + + + + + +/*Collection of Numerical Recipe Routines to perform correlations*/ +void correl(data1,data2,n,ans) + float data1[],data2[],ans[]; + int n; +{ + int no2,i; + float dum,*fft,*vector(); + void twofft(),realft(),free_vector(); + + fft=vector(1,2*n); + twofft(data1,data2,fft,ans,n); + no2=n/2; + for (i=2;i<=n+2;i+=2) { + ans[i-1]=(fft[i-1]*(dum=ans[i-1])+fft[i]*ans[i])/no2; + ans[i]=(fft[i]*dum-fft[i-1]*ans[i])/no2; + } + ans[2]=ans[n+1]; + realft(ans,no2,-1); + free_vector(fft,1,2*n); +} + +void realft(data,n,isign) + float data[]; + int n,isign; +{ + int i,i1,i2,i3,i4,n2p3; + float c1=0.5,c2,h1r,h1i,h2r,h2i; + double wr,wi,wpr,wpi,wtemp,theta; + void four1(); + + theta=3.141592653589793/(double) n; + if (isign == 1) { + c2 = -0.5; + four1(data,n,1); + } else { + c2=0.5; + theta = -theta; + } + wtemp=sin(0.5*theta); + wpr = -2.0*wtemp*wtemp; + wpi=sin(theta); + wr=1.0+wpr; + wi=wpi; + n2p3=2*n+3; + for (i=2;i<=n/2;i++) { + i4=1+(i3=n2p3-(i2=1+(i1=i+i-1))); + h1r=c1*(data[i1]+data[i3]); + h1i=c1*(data[i2]-data[i4]); + h2r = -c2*(data[i2]+data[i4]); + h2i=c2*(data[i1]-data[i3]); + data[i1]=h1r+wr*h2r-wi*h2i; + data[i2]=h1i+wr*h2i+wi*h2r; + data[i3]=h1r-wr*h2r+wi*h2i; + data[i4] = -h1i+wr*h2i+wi*h2r; + wr=(wtemp=wr)*wpr-wi*wpi+wr; + wi=wi*wpr+wtemp*wpi+wi; + } + if (isign == 1) { + data[1] = (h1r=data[1])+data[2]; + data[2] = h1r-data[2]; + } else { + data[1]=c1*((h1r=data[1])+data[2]); + data[2]=c1*(h1r-data[2]); + four1(data,n,-1); + } +} + +void twofft(data1,data2,fft1,fft2,n) + float data1[],data2[],fft1[],fft2[]; + int n; +{ + int nn3,nn2,jj,j; + float rep,rem,aip,aim; + void four1(); + + nn3=1+(nn2=2+n+n); + for (j=1,jj=2;j<=n;j++,jj+=2) { + fft1[jj-1]=data1[j]; + fft1[jj]=data2[j]; + } + four1(fft1,n,1); + fft2[1]=fft1[2]; + fft1[2]=fft2[2]=0.0; + for (j=3;j<=n+1;j+=2) { + rep=0.5*(fft1[j]+fft1[nn2-j]); + rem=0.5*(fft1[j]-fft1[nn2-j]); + aip=0.5*(fft1[j+1]+fft1[nn3-j]); + aim=0.5*(fft1[j+1]-fft1[nn3-j]); + fft1[j]=rep; + fft1[j+1]=aim; + fft1[nn2-j]=rep; + fft1[nn3-j] = -aim; + fft2[j]=aip; + fft2[j+1] = -rem; + fft2[nn2-j]=aip; + fft2[nn3-j]=rem; + } +} + +#define SWAP(a,b) tempr=(a);(a)=(b);(b)=tempr + +void four1(data,nn,isign) + float data[]; + int nn,isign; +{ + int n,mmax,m,j,istep,i; + double wtemp,wr,wpr,wpi,wi,theta; + float tempr,tempi; + + n=nn << 1; + j=1; + for (i=1;i i) { + SWAP(data[j],data[i]); + SWAP(data[j+1],data[i+1]); + } + m=n >> 1; + while (m >= 2 && j > m) { + j -= m; + m >>= 1; + } + j += m; + } + mmax=2; + while (n > mmax) { + istep=2*mmax; + theta=6.28318530717959/(isign*mmax); + wtemp=sin(0.5*theta); + wpr = -2.0*wtemp*wtemp; + wpi=sin(theta); + wr=1.0; + wi=0.0; + for (m=1;margname,alptr->argval); + } + gp_close_dump(file); + } + FLAGS |= addflags; + } + +void gp_add_entry(name,value) /* add an entry to arglist, expanding memory */ +register char *name, *value; /* if necessary */ + { + struct arglist *alptr; + int len; + register char *ptr; + + /* check arglist memory */ + if(NLIST >= LISTMAX) + { + LISTMAX += LISTINC; + if(ARGLIST == NULL) + ARGLIST= (AL *)malloc(LISTMAX * sizeof(AL)); + else ARGLIST= (AL *)realloc(ARGLIST,LISTMAX * sizeof(AL)); + } + /* check argbuf memory */ + len= strlen(name) + strlen(value) + 2; /* +2 for terminating nulls */ + if(NBUF+len >= BUFMAX) + { + BUFMAX += BUFINC; + if(ARGBUF == NULL) + ARGBUF= (char *)malloc(BUFMAX); + else ARGBUF= (char *)realloc(ARGBUF,BUFMAX); + } + if(ARGBUF == NULL || ARGLIST == NULL) + gp_getpar_err("setpar","cannot allocate memory",0,0,0,0); + + /* add name */ + alptr= ARGLIST + NLIST; + alptr->hash= gp_compute_hash(name); + ptr= alptr->argname= ARGBUF + NBUF; + do *ptr++ = *name; while(*name++); + + /* add value */ + NBUF += len; + alptr->argval= ptr; + do *ptr++ = *value; while(*value++); + NLIST++; + } + +void ENDPAR() /* free arglist & argbuf memory, & process STOP command */ + { + if(ARGLIST != NULL) free(ARGLIST); + if(ARGBUF != NULL) free(ARGBUF); + ARGBUF= NULL; + ARGLIST= NULL; + if(FLAGS & STOP) + { + fprintf(stderr,"%s[endpar]: stop due to STOP in input\n", + PROGNAME); + exit(GETPAR_STOP); + } + FLAGS= END_PAR; /* this stops further getpar calls */ + } + +int mstpar(name,type,val) +char *name, *type; +int *val; + { + int cnt; + char *typemess; + + if( (cnt= GETPAR(name,type,val)) > 0) return(cnt); + + /* The following line corrects a common input error */ + if(type[1]=='v') { type[1]= type[0]; type[0]='v'; } + + switch(*type) + { + case 'd': typemess= "an integer"; break; + case 'f': typemess= "a float"; break; + case 'F': typemess= "a double"; break; + case 's': typemess= "a string"; break; + case 'b': typemess= "a boolean"; break; + case 'v': switch(type[1]) + { + case 'd': typemess= "an integer vector"; break; + case 'f': typemess= "a float vector"; break; + case 'F': typemess= "a double vector"; break; + default : typemess= "unknow vectorn (error)"; + break; + } + return(0); + break; + default : typemess= "unknown (error)"; return(0); break; + } + gp_getpar_err("mstpar","must specify value for '%s', expecting %s", + name,typemess,0,0,0); + return(0); + } + +int getpar(name,type,val) +char *name, *type; +int *val; + { + register char *sptr; + register struct arglist *alptr; + register int i; + double atof(), *dbl; + float *flt; + int h, hno, hyes, found; + char line[MAXLINE], *str, *noname; + + if(FLAGS & END_PAR) + gp_getpar_err("getpar","called after endpar"); + if( (FLAGS & INIT) == 0) + gp_getpar_err("getpar","not initialized with setpar"); + if(FLAGS & VERBOSE) + fprintf(stderr,"getpar: looking for %s\n",name); + + /* The following line corrects a common input error */ + if(type[1]=='v') { type[1]= type[0]; type[0]='v'; } + + + if(*type == 'b') goto boolean; + + h= gp_compute_hash(name); + found=0; + /* search list backwards, stopping at first find */ + for(alptr= ARGLIST +(NLIST-1); alptr >= ARGHEAD; alptr--) + { + if(alptr->hash != h) continue; + if(strcmp(alptr->argname,name) != 0) continue; + str= alptr->argval; + switch(*type) + { + case 'd': + *val= atoi(str); + found=1; + break; + case 'f': + flt= (float *) val; + *flt= atof(str); + found=1; + break; + case 'F': + dbl= (double *) val; + *dbl= atof(str); + found=1; + break; + case 's': + sptr= (char *) val; + while(*str) *sptr++ = *str++; + *sptr= '\0'; + found=1; + break; + case 'v': + found= gp_getvector(str,type,val); + break; + default: + gp_getpar_err("getpar", + "unknown conversion type %s",type); + break; + } + break; + } + goto list; +boolean: + noname= line; + sprintf(noname,"no%s",name); + hno = gp_compute_hash(noname); + hyes= gp_compute_hash( name); + found=0; + /* search list backwards, stopping at first find */ + for(alptr= ARGLIST +(NLIST-1); alptr >= ARGHEAD; alptr--) + { + if(alptr->hash != hno && alptr->hash != hyes) continue; + if(strcmp(alptr->argname, name)== 0) + { + if(alptr->argval[0] == '\0') *val= 1; + else *val= atol(alptr->argval); + found++; + break; + } + if(strcmp(alptr->argname,noname)== 0) + { *val= 0; found++; break; } + } + list: + if(FLAGS & LIST) + { + switch(*type) + { + case 'd': sprintf(line,"(int) = %d",*val); break; + case 'f': flt= (float *)val; + sprintf(line,"(flt) = %14.6e",*flt); break; + case 'F': dbl= (double *)val; + sprintf(line,"(dbl) = %14.6e",*dbl); break; + /* case 's': sprintf(line,"(str) = %s",val); break;*/ + case 's': sprintf(line,"(str) = %s",sptr); break; + case 'b': sprintf(line,"(boo) = %d",*val); break; + case 'v': switch(type[1]) + { + /* should list these out */ + case 'd': sprintf(line,"(int vec)"); + break; + case 'f': sprintf(line,"(flt vec)"); + break; + case 'F': sprintf(line,"(dbl vec)"); + break; + default : sprintf(line," vec type error"); + break; + } + break; + default : sprintf(line," type error"); break; + } + fprintf(LISTFILE,"%16s (%s) %s \n",name, + (found ? "set":"def"),line); + } + return(found); + } +FILE *gp_create_dump(fname,filetype) +char *fname; +char *filetype; + { + FILE *temp; + + if(*fname == '\0') return(stderr); + if(strcmp(fname,"stderr") == 0) return(stderr); + if(strcmp(fname,"stdout") == 0) return(stdout); + if( (temp= fopen(fname,"w")) != NULL) return(temp); + fprintf(stderr,"%s[setpar]: cannot create %s file %s\n", + PROGNAME,filetype,fname); + return(stderr); + } + +int gp_close_dump(file) +FILE *file; + { + if(file == stderr || file == stdout) return(0); + fclose(file); + return(0); + } + +int gp_compute_hash(s) +register char *s; + { + register int h; + h= s[0]; + if(s[1]) h |= (s[1])<<8; else return(h); + if(s[2]) h |= (s[2])<<16; else return(h); + if(s[3]) h |= (s[3])<<24; + return(h); + } + +void gp_do_par_file(fname,level) +char *fname; +int level; + { + register char *pl, *pn, *pv; + char t1, t2, line[MAXLINE], name[MAXNAME], value[MAXVALUE]; + FILE *file, *fopen(); + + if(level > MAXPARLEVEL) + gp_getpar_err("setpar","%d (too many) recursive par file",level); + + if( (file=fopen(fname,"r"))==NULL) + gp_getpar_err("setpar","cannot open par file %s",fname); + + while( fgets(line,MAXLINE,file) != NULL ) + { + pl= line; + /* loop over entries on each line */ + loop: while(*pl==' ' || *pl=='\t') pl++; + if(*pl=='\0'|| *pl=='\n') continue; + if(*pl=='#') continue; /* comments on rest of line */ + + /* get name */ + pn= name; + while(*pl != '=' && *pl != '\0' && *pl != ' ' + && *pl != '\t') *pn++ = *pl++; + *pn = '\0'; + if(*pl == '=') pl++; + + /* get value */ + *value= '\0'; + pv= value; + if(*pl=='"' || *pl=='\'') { t1= t2= *pl++; } + else { t1= ' '; t2= '\t'; } + while(*pl!=t1 && *pl!=t2 && + *pl!='\0' && *pl!='\n') *pv++= *pl++; + *pv= '\0'; + if(*pl=='"' || *pl=='\'') pl++; + gp_add_entry(name,value); + if(strcmp("par",name) == 0) + gp_do_par_file(value,level+1); + goto loop; + } + fclose(file); + } + +int gp_getpar_err(subname,mess,a1,a2,a3,a4) +char *subname, *mess; +int a1, a2, a3, a4; + { + fprintf(stderr,"\n***** ERROR in %s[%s] *****\n\t", + (PROGNAME == NULL ? "(unknown)" : PROGNAME),subname); + fprintf(stderr,mess,a1,a2,a3,a4); + fprintf(stderr,"\n"); + exit(GETPAR_ERROR); + } +int gp_getvector(list,type,val) +char *list, *type; +int *val; + { + register char *p; + register int index, cnt; + char *valptr; + int limit; + int ival, *iptr; + float fval, *fptr; + double dval, *dptr, atof(); + + limit= MAXVECTOR; + if(type[2] == '(' || type[2] == '[') limit= atol(&type[3]); + if(limit <= 0) + gp_getpar_err("getpar","bad limit=%d specified",limit,0,0,0); + index= 0; + p= list; + while(*p != '\0' && index < limit) + { + cnt=1; + backup: /* return to here if we find a repetition factor */ + while(*p == ' ' || *p == '\t') p++; + if(*p == '\0') return(index); + valptr= p; + while( *p != ',' && *p != '*' && *p != 'x' && *p != 'X' && + *p != '\0') p++; + if(*p == '*' || *p == 'x' || *p == 'X') + { + cnt= atol(valptr); + if(cnt <= 0) + gp_getpar_err("getpar", + "bad repetition factor=%d specified", + cnt,0,0,0); + if(index+cnt > limit) cnt= limit - index; + p++; + goto backup; + } + switch(type[1]) + { + case 'd': + iptr= (int *) val; + ival= atol(valptr); + while(cnt--) iptr[index++] = ival; + break; + case 'f': + fptr= (float *) val; + fval= atof(valptr); + while(cnt--) fptr[index++] = fval; + break; + case 'F': + dptr= (double *) val; + dval= atof(valptr); + while(cnt--) dptr[index++] = dval; + break; + default: + gp_getpar_err("getpar", + "bad vector type=%c specified",type[1],0,0,0); + break; + } + if(*p != '\0') p++; + } + return(index); + } diff --git a/TDMT_GMT/getpar.o b/TDMT_GMT/getpar.o new file mode 100644 index 0000000..1954253 Binary files /dev/null and b/TDMT_GMT/getpar.o differ diff --git a/TDMT_GMT/makefile b/TDMT_GMT/makefile new file mode 100755 index 0000000..69e9f2a --- /dev/null +++ b/TDMT_GMT/makefile @@ -0,0 +1,97 @@ +# Standalone Makefile for Solaris and Sun Studio 12 compiler suite + +cc = gcc +CFLAGS = -m64 +FFLAGS = +FF = gfortran + +######################################################################## +# If you want plotting within tdmt_invc using the stdplt library, +# define the following; otherwise comment them out: +#:: STDPLTDIR = ../stdplt/plotsubs +#:: PLTLIB = -L$(STDPLTDIR) -lstdplt +#:: TDMT_PLOT_SOURCE = mt_plot6iso2_linux.c +#:: TDMT_PLOT_SWITCH = -DTDMT_PLOT + +# To turn off all exceptt errors being written to stderr, +# define the following; otherwise comment it out +#TDMT_STDERR_SWITCH = -DONLY_ERRORS_TO_STDERR +######################################################################## + +TDMT_OPT_FLAGS = $(TDMT_PLOT_SWITCH) $(TDMT_STDERR_SWITCH) +LIBS = $(PLTLIB) -lm + +TARGET1 = sac2helm +CSRCS1 = sac2helm.c getpar.c +TARGET2 = tdmt_invc_iso +CSRCS2 = tdmt_invc_iso.c minvdbl.c correl2b.c readhelm.c fitcheck2_iso.c \ + mt_plot_dat.c ${TDMT_PLOT_SOURCE} +CSRCS3 = tdmt_invc_iso_fwd.c minvdbl.c correl2b.c readhelm.c fitcheck2_iso.c \ + mt_plot_dat.c ${TDMT_PLOT_SOURCE} +CSRCS4 = tdmt_invc_iso_out.c minvdbl.c correl2b.c readhelm.c fitcheck2_iso.c \ + mt_plot_dat.c ${TDMT_PLOT_SOURCE} +CSRCS5 = tdmt_invc_iso_zcor.c minvdbl.c correl2b.c readhelm.c fitcheck2_iso.c \ + mt_plot_dat.c ${TDMT_PLOT_SOURCE} +CSRCS6 = tdmt_invc_iso_outputmtline.c minvdbl.c correl2b.c readhelm.c fitcheck2_iso.c \ + mt_plot_dat.c ${TDMT_PLOT_SOURCE} +TARGET5 = tdmt_invc_iso_fwd +TARGET6 = tdmt_invc_iso_out +TARGET7 = tdmt_invc_iso_zcor +TARGET8 = tdmt_invc_iso_outputmtline +TARGET3 = fmap +FSRCS3 = fmap_new.f +TARGET4 = mtmanip +FSRCS4 = mtmanip_new.f +FSRCSUB = mtmanip_module.f +FSRCSUB2 = fmap_subs_new.f + +ALL = $(TARGET1) $(TARGET2) $(TARGET3) $(TARGET4) $(TARGET5) $(TARGET6) $(TARGET7) $(TARGET8) +OBJS1 = $(CSRCS1:%.c=%.o) +OBJS2 = $(CSRCS2:%.c=%.o) +OBJS3 = $(FSRCS3:%.f=%.o) +OBJS4 = $(FSRCS4:%.f=%.o) +OBJS5 = $(CSRCS3:%.c=%.o) +OBJS6 = $(CSRCS4:%.c=%.o) +OBJS7 = $(CSRCS5:%.c=%.o) +OBJS8 = $(CSRCS6:%.c=%.o) +FOBJSUB = $(FSRCSUB:%.f=%.o) +FOBJSUB2= $(FSRCSUB2:%.f=%.o) + +all: $(ALL) + +$(TARGET1): $(OBJS1) + $(cc) $(CFLAGS) -o $@ $(OBJS1) + +$(TARGET2): $(FOBJSUB) $(OBJS2) $(FOBJSUB2) + $(FF) $(CFLAGS) -o $@ $(OBJS2) $(FOBJSUB2) $(FOBJSUB) $(LIBS) + +$(TARGET3): $(FOBJSUB) $(OBJS3) $(FOBJSUB2) + $(FF) $(CFLAGS) -o $@ $(OBJS3) $(FOBJSUB2) $(FOBJSUB) + +$(TARGET4): $(FOBJSUB2) $(OBJS4) $(FOBJSUB) + $(FF) $(CFLAGS) -o $@ $(OBJS4) $(FOBJSUB) $(FOBJSUB2) + +$(TARGET5): $(FOBJSUB) $(OBJS5) $(FOBJSUB2) + $(FF) $(CFLAGS) -o $@ $(OBJS5) $(FOBJSUB2) $(FOBJSUB) $(LIBS) + +$(TARGET6): $(FOBJSUB) $(OBJS6) $(FOBJSUB2) + $(FF) $(CFLAGS) -o $@ $(OBJS6) $(FOBJSUB2) $(FOBJSUB) $(LIBS) + +$(TARGET7): $(FOBJSUB) $(OBJS7) $(FOBJSUB2) + $(FF) $(CFLAGS) -o $@ $(OBJS7) $(FOBJSUB2) $(FOBJSUB) $(LIBS) + +$(TARGET8): $(FOBJSUB) $(OBJS8) $(FOBJSUB2) + $(FF) $(CFLAGS) -o $@ $(OBJS8) $(FOBJSUB2) $(FOBJSUB) $(LIBS) + +.c.o: + $(cc) $< -c $(CFLAGS) $(TDMT_OPT_FLAGS) + +.f.o: + $(FF) $< -c $(CFLAGS) + +clean: + \rm *.o tdmt_invc_iso tdmt_invc_iso_fwd mtmanip sac2helm fmap + +install: $(ALL) $(SCRIPTS) + \mv tdmt_invc_iso mtmanip tdmt_invc_iso_fwd tdmt_invc_iso_out tdmt_invc_iso_outputmtline tdmt_invc_iso_zcor ../BIN + diff --git a/TDMT_GMT/minvdbl.c b/TDMT_GMT/minvdbl.c new file mode 100755 index 0000000..f3c7850 --- /dev/null +++ b/TDMT_GMT/minvdbl.c @@ -0,0 +1,713 @@ +#include +#define SWAP(a,b) {double temp=(a);(a)=(b);(b)=temp;} + +#if defined(__STDC__) || defined(ANSI) || defined(NRANSI) /* ANSI */ + +#include +#include +#include +#define NR_END 1 +#define FREE_ARG char* + +void nrerror(char error_text[]) +/* Numerical Recipes standard error handler */ +{ + fprintf(stderr,"Numerical Recipes run-time error...\n"); + fprintf(stderr,"%s\n",error_text); + fprintf(stderr,"...now exiting to system...\n"); + exit(1); +} + +float *vector(int nl, int nh) +/* allocate a float vector with subscript range v[nl..nh] */ +{ + float *v; + + v=(float *)malloc((size_t) ((nh-nl+1+NR_END)*sizeof(float))); + if (!v) nrerror("allocation failure in vector()"); + return v-nl+NR_END; +} + +int *ivector(int nl, int nh) +/* allocate an int vector with subscript range v[nl..nh] */ +{ + int *v; + + v=(int *)malloc((size_t) ((nh-nl+1+NR_END)*sizeof(int))); + if (!v) nrerror("allocation failure in ivector()"); + return v-nl+NR_END; +} + + +unsigned char *cvector(int nl, int nh) +/* allocate an unsigned char vector with subscript range v[nl..nh] */ +{ + unsigned char *v; + + v=(unsigned char *)malloc((size_t) ((nh-nl+1+NR_END)*sizeof(unsigned char))); + if (!v) nrerror("allocation failure in cvector()"); + return v-nl+NR_END; +} + +unsigned int *lvector(int nl, int nh) +/* allocate an unsigned long vector with subscript range v[nl..nh] */ +{ + unsigned int *v; + + v=(unsigned int *)malloc((size_t) ((nh-nl+1+NR_END)*sizeof(int))); + if (!v) nrerror("allocation failure in lvector()"); + return v-nl+NR_END; +} + +double *dvector(int nl, int nh) +/* allocate a double vector with subscript range v[nl..nh] */ +{ + double *v; + + v=(double *)malloc((size_t) ((nh-nl+1+NR_END)*sizeof(double))); + if (!v) nrerror("allocation failure in dvector()"); + return v-nl+NR_END; +} + +float **matrix(int nrl, int nrh, int ncl, int nch) +/* allocate a float matrix with subscript range m[nrl..nrh][ncl..nch] */ +{ + int i, nrow=nrh-nrl+1,ncol=nch-ncl+1; + float **m; + + /* allocate pointers to rows */ + m=(float **) malloc((size_t)((nrow+NR_END)*sizeof(float*))); + if (!m) nrerror("allocation failure 1 in matrix()"); + m += NR_END; + m -= nrl; + + /* allocate rows and set pointers to them */ + m[nrl]=(float *) malloc((size_t)((nrow*ncol+NR_END)*sizeof(float))); + if (!m[nrl]) nrerror("allocation failure 2 in matrix()"); + m[nrl] += NR_END; + m[nrl] -= ncl; + + for(i=nrl+1;i<=nrh;i++) m[i]=m[i-1]+ncol; + + /* return pointer to array of pointers to rows */ + return m; +} + +double **dmatrix(int nrl, int nrh, int ncl, int nch) +/* allocate a double matrix with subscript range m[nrl..nrh][ncl..nch] */ +{ + int i, nrow=nrh-nrl+1,ncol=nch-ncl+1; + double **m; + + /* allocate pointers to rows */ + m=(double **) malloc((size_t)((nrow+NR_END)*sizeof(double*))); + if (!m) nrerror("allocation failure 1 in matrix()"); + m += NR_END; + m -= nrl; + + /* allocate rows and set pointers to them */ + m[nrl]=(double *) malloc((size_t)((nrow*ncol+NR_END)*sizeof(double))); + if (!m[nrl]) nrerror("allocation failure 2 in matrix()"); + m[nrl] += NR_END; + m[nrl] -= ncl; + + for(i=nrl+1;i<=nrh;i++) m[i]=m[i-1]+ncol; + + /* return pointer to array of pointers to rows */ + return m; +} + +int **imatrix(int nrl, int nrh, int ncl, int nch) +/* allocate a int matrix with subscript range m[nrl..nrh][ncl..nch] */ +{ + int i, nrow=nrh-nrl+1,ncol=nch-ncl+1; + int **m; + + /* allocate pointers to rows */ + m=(int **) malloc((size_t)((nrow+NR_END)*sizeof(int*))); + if (!m) nrerror("allocation failure 1 in matrix()"); + m += NR_END; + m -= nrl; + + + /* allocate rows and set pointers to them */ + m[nrl]=(int *) malloc((size_t)((nrow*ncol+NR_END)*sizeof(int))); + if (!m[nrl]) nrerror("allocation failure 2 in matrix()"); + m[nrl] += NR_END; + m[nrl] -= ncl; + + for(i=nrl+1;i<=nrh;i++) m[i]=m[i-1]+ncol; + + /* return pointer to array of pointers to rows */ + return m; +} + +float **submatrix(float **a, int oldrl, int oldrh, int oldcl, int oldch, + int newrl, int newcl) +/* point a submatrix [newrl..][newcl..] to a[oldrl..oldrh][oldcl..oldch] */ +{ + int i,j,nrow=oldrh-oldrl+1,ncol=oldcl-newcl; + float **m; + + /* allocate array of pointers to rows */ + m=(float **) malloc((size_t) ((nrow+NR_END)*sizeof(float*))); + if (!m) nrerror("allocation failure in submatrix()"); + m += NR_END; + m -= newrl; + + /* set pointers to rows */ + for(i=oldrl,j=newrl;i<=oldrh;i++,j++) m[j]=a[i]+ncol; + + /* return pointer to array of pointers to rows */ + return m; +} + +float **convert_matrix(float *a, int nrl, int nrh, int ncl, int nch) +/* allocate a float matrix m[nrl..nrh][ncl..nch] that points to the matrix +declared in the standard C manner as a[nrow][ncol], where nrow=nrh-nrl+1 +and ncol=nch-ncl+1. The routine should be called with the address +&a[0][0] as the first argument. */ +{ + int i,j,nrow=nrh-nrl+1,ncol=nch-ncl+1; + float **m; + + /* allocate pointers to rows */ + m=(float **) malloc((size_t) ((nrow+NR_END)*sizeof(float*))); + if (!m) nrerror("allocation failure in convert_matrix()"); + m += NR_END; + m -= nrl; + + /* set pointers to rows */ + m[nrl]=a-ncl; + for(i=1,j=nrl+1;i +#define NR_END 1 +#define FREE_ARG char* + +void nrerror(error_text) +char error_text[]; +/* Numerical Recipes standard error handler */ +{ + void exit(); + + fprintf(stderr,"Numerical Recipes run-time error...\n"); + fprintf(stderr,"%s\n",error_text); + fprintf(stderr,"...now exiting to system...\n"); + exit(1); +} + +float *vector(nl,nh) +int nh,nl; +/* allocate a float vector with subscript range v[nl..nh] */ +{ + float *v; + + v=(float *)malloc((unsigned int) ((nh-nl+1+NR_END)*sizeof(float))); + if (!v) nrerror("allocation failure in vector()"); + return v-nl+NR_END; +} + +int *ivector(nl,nh) +int nh,nl; +/* allocate an int vector with subscript range v[nl..nh] */ +{ + int *v; + + v=(int *)malloc((unsigned int) ((nh-nl+1+NR_END)*sizeof(int))); + if (!v) nrerror("allocation failure in ivector()"); + return v-nl+NR_END; +} + +unsigned char *cvector(nl,nh) +int nh,nl; +/* allocate an unsigned char vector with subscript range v[nl..nh] */ +{ + unsigned char *v; + + v=(unsigned char *)malloc((unsigned int) ((nh-nl+1+NR_END)*sizeof(unsigned char))); + if (!v) nrerror("allocation failure in cvector()"); + return v-nl+NR_END; +} + +unsigned int *lvector(nl,nh) +int nh,nl; +/* allocate an unsigned long vector with subscript range v[nl..nh] */ +{ + unsigned int *v; + + v=(unsigned int *)malloc((unsigned int) ((nh-nl+1+NR_END)*sizeof(int))); + if (!v) nrerror("allocation failure in lvector()"); + return v-nl+NR_END; +} + +double *dvector(nl,nh) +int nh,nl; +/* allocate a double vector with subscript range v[nl..nh] */ +{ + double *v; + + v=(double *)malloc((unsigned int) ((nh-nl+1+NR_END)*sizeof(double))); + if (!v) nrerror("allocation failure in dvector()"); + return v-nl+NR_END; +} + +float **matrix(nrl,nrh,ncl,nch) +int nch,ncl,nrh,nrl; +/* allocate a float matrix with subscript range m[nrl..nrh][ncl..nch] */ +{ + int i, nrow=nrh-nrl+1,ncol=nch-ncl+1; + float **m; + + /* allocate pointers to rows */ + m=(float **) malloc((unsigned int)((nrow+NR_END)*sizeof(float*))); + if (!m) nrerror("allocation failure 1 in matrix()"); + m += NR_END; + m -= nrl; + + /* allocate rows and set pointers to them */ + m[nrl]=(float *) malloc((unsigned int)((nrow*ncol+NR_END)*sizeof(float))); + if (!m[nrl]) nrerror("allocation failure 2 in matrix()"); + m[nrl] += NR_END; + m[nrl] -= ncl; + + for(i=nrl+1;i<=nrh;i++) m[i]=m[i-1]+ncol; + + /* return pointer to array of pointers to rows */ + return m; +} + +double **dmatrix(nrl,nrh,ncl,nch) +int nch,ncl,nrh,nrl; +/* allocate a double matrix with subscript range m[nrl..nrh][ncl..nch] */ +{ + int i, nrow=nrh-nrl+1,ncol=nch-ncl+1; + double **m; + + /* allocate pointers to rows */ + m=(double **) malloc((unsigned int)((nrow+NR_END)*sizeof(double*))); + if (!m) nrerror("allocation failure 1 in matrix()"); + m += NR_END; + m -= nrl; + + /* allocate rows and set pointers to them */ + m[nrl]=(double *) malloc((unsigned int)((nrow*ncol+NR_END)*sizeof(double))); + if (!m[nrl]) nrerror("allocation failure 2 in matrix()"); + m[nrl] += NR_END; + m[nrl] -= ncl; + + for(i=nrl+1;i<=nrh;i++) m[i]=m[i-1]+ncol; + + /* return pointer to array of pointers to rows */ + return m; +} + +int **imatrix(nrl,nrh,ncl,nch) +int nch,ncl,nrh,nrl; +/* allocate a int matrix with subscript range m[nrl..nrh][ncl..nch] */ +{ + int i, nrow=nrh-nrl+1,ncol=nch-ncl+1; + int **m; + + /* allocate pointers to rows */ + m=(int **) malloc((unsigned int)((nrow+NR_END)*sizeof(int*))); + if (!m) nrerror("allocation failure 1 in matrix()"); + m += NR_END; + m -= nrl; + + + /* allocate rows and set pointers to them */ + m[nrl]=(int *) malloc((unsigned int)((nrow*ncol+NR_END)*sizeof(int))); + if (!m[nrl]) nrerror("allocation failure 2 in matrix()"); + m[nrl] += NR_END; + m[nrl] -= ncl; + + for(i=nrl+1;i<=nrh;i++) m[i]=m[i-1]+ncol; + + /* return pointer to array of pointers to rows */ + return m; +} + +float **submatrix(a,oldrl,oldrh,oldcl,oldch,newrl,newcl) +float **a; +int newcl,newrl,oldch,oldcl,oldrh,oldrl; +/* point a submatrix [newrl..][newcl..] to a[oldrl..oldrh][oldcl..oldch] */ +{ + int i,j,nrow=oldrh-oldrl+1,ncol=oldcl-newcl; + float **m; + + /* allocate array of pointers to rows */ + m=(float **) malloc((unsigned int) ((nrow+NR_END)*sizeof(float*))); + if (!m) nrerror("allocation failure in submatrix()"); + m += NR_END; + m -= newrl; + + /* set pointers to rows */ + for(i=oldrl,j=newrl;i<=oldrh;i++,j++) m[j]=a[i]+ncol; + + /* return pointer to array of pointers to rows */ + return m; +} + +float **convert_matrix(a,nrl,nrh,ncl,nch) +float *a; +int nch,ncl,nrh,nrl; +/* allocate a float matrix m[nrl..nrh][ncl..nch] that points to the matrix +declared in the standard C manner as a[nrow][ncol], where nrow=nrh-nrl+1 +and ncol=nch-ncl+1. The routine should be called with the address +&a[0][0] as the first argument. */ +{ + int i,j,nrow=nrh-nrl+1,ncol=nch-ncl+1; + float **m; + + /* allocate pointers to rows */ + m=(float **) malloc((unsigned int) ((nrow+NR_END)*sizeof(float*))); + if (!m) nrerror("allocation failure in convert_matrix()"); + m += NR_END; + m -= nrl; + + /* set pointers to rows */ + m[nrl]=a-ncl; + for(i=1,j=nrl+1;i= big) { + big=(double)fabs(a[j][k]); + irow=j; + icol=k; + } + } //else if (ipiv[k] > 1) nrerror("GAUSSJ: Singular Matrix-1"); // according patch w208210 from Mon Oct 01 08:02:58 2001 + } + ++(ipiv[icol]); + if (irow != icol) { + for (l=1;l<=n;l++) SWAP(a[irow][l], a[icol][l]) + for (l=1;l<=m;l++) SWAP(b[irow][l], b[icol][l]) + } + indxr[i]=irow; + indxc[i]=icol; + if (a[icol][icol] == 0.0) nrerror("GAUSSJ: Singular Matrix-2"); + pivinv=1.0/a[icol][icol]; + a[icol][icol]=1.0; + for (l=1;l<=n;l++) a[icol][l] *= pivinv; + for (l=1;l<=m;l++) b[icol][l] *= pivinv; + for (ll=1;ll<=n;ll++) + if (ll != icol) { + dum=a[ll][icol]; + a[ll][icol]=0.0; + for (l=1;l<=n;l++) a[ll][l] -= a[icol][l]*dum; + for (l=1;l<=m;l++) b[ll][l] -= b[icol][l]*dum; + } + } + for (l=n;l>=1;l--) { + if (indxr[l] != indxc[l]) + for (k=1;k<=n;k++) + SWAP(a[k][indxr[l]], a[k][indxc[l]]); + } + free_ivector(ipiv, 1, n); + free_ivector(indxr, 1, n); + free_ivector(indxc, 1, n); +} + +#undef SWAP + +double **dsubmatrix(a,oldrl,oldrh,oldcl,oldch,newrl,newcl) +double **a; +int newcl,newrl,oldch,oldcl,oldrh,oldrl; +/* point a submatrix [newrl..][newcl..] to a[oldrl..oldrh][oldcl..oldch] */ +{ + int i,j,nrow=oldrh-oldrl+1,ncol=oldcl-newcl; + double **m; + + /* allocate array of pointers to rows */ + m=(double **) malloc((unsigned int) ((nrow+NR_END)*sizeof(double*))); + if (!m) nrerror("allocation failure in submatrix()"); + m += NR_END; + m -= newrl; + + /* set pointers to rows */ + for(i=oldrl,j=newrl;i<=oldrh;i++,j++) m[j]=a[i]+ncol; + + /* return pointer to array of pointers to rows */ + return m; +} + +void minvdbl(x,y,n,m) + double **x,**y; + int n,m; +{ + int i,j; + double **p,**pp; + + p=dsubmatrix(x,0,n,0,n,1,1); + pp=dsubmatrix(y,0,n,0,m,1,1); + gaussj(p,n,pp,m); +} + diff --git a/TDMT_GMT/minvdbl.o b/TDMT_GMT/minvdbl.o new file mode 100644 index 0000000..a470736 Binary files /dev/null and b/TDMT_GMT/minvdbl.o differ diff --git a/TDMT_GMT/mt_plot_dat.c b/TDMT_GMT/mt_plot_dat.c new file mode 100755 index 0000000..7eec68e --- /dev/null +++ b/TDMT_GMT/mt_plot_dat.c @@ -0,0 +1,92 @@ +/*Copyright (c) Peter Lombard +Berkeley Seismological Laboratory +University of California, Berkeley */ + +#include "tdmtinv_iso.h" +#include +#include + +int mt_dat(ss,gg,nsta,Strike,Rake,Dip,St2,Rk2,Dp2,M,scale,Pdc,Pclvd,Piso,Mo,Mw,E,VR,depth,fname) + int nsta, depth; + struct MOMENT M; + struct GREEN *gg; + struct DATA *ss; + float Strike, Rake, Dip, Pdc, Pclvd, Piso, Mo, Mw, E, VR, scale; + float St2, Rk2, Dp2; + char *fname; +{ + int i,j, n, Z, Zg, Np, Zd, count; + float Az, dt; + FILE *fp; + + /* open data file */ + fp = fopen(fname,"w"); + if (fp == NULL) { + fprintf(stderr, "mt_dat: error opening %s: %s\n", fname, + strerror(errno)); + return -1; + } + + /* write tdmt-run related info: */ + fprintf(fp, "#depth variance VR nsta\n"); + fprintf(fp, "%3d %10.3e %10.3e %3d\n", depth, E, VR, nsta); + fprintf(fp, "#mxx mxy mxz myy myz mzz\n"); + fprintf(fp, "%10.3e %10.3e %10.3e %10.3e %10.3e %10.3e\n", M.mxx*scale, + M.mxy*scale, M.mxz*scale, M.myy*scale,M.myz*scale, M.mzz*scale); + fprintf(fp, "#st1 rk1 dp1 st2 rk2 dp2\n"); + fprintf(fp, "%3.1f %3.1f %3.1f %3.1f %3.1f %3.1f\n", Strike, Rake, Dip, + St2, Rk2, Dp2); + fprintf(fp, "#pdc pclvd piso\n"); + fprintf(fp, "%3.1f %3.1f %3.1f\n", Pdc, Pclvd, Piso); + + for(i=0; i < nsta; i++) { + Np = ss[i].nn; + Z = ss[i].zz; + dt = ss[i].dt; + Az = ss[i].azi; + Zg = gg[i].zz; + + fprintf(fp, "\n#filename\n"); + fprintf(fp, "%s\n", ss[i].name); + fprintf(fp, "#dt npts dist Az Zcor VR\n"); + fprintf(fp, "%5.3f %4d %5.1f %5.1f %4d %10.3e\n", dt, Np, ss[i].dist, + Az*180.0/PI, Z, ss[i].vr); + fprintf(fp, "#data T R Z synth T R Z\n"); + + for(j=0; j < Np; j++) { + fprintf(fp, "%10.3e %10.3e %10.3e %10.3e %10.3e %10.3e\n", + ss[i].t[j+Z], + ss[i].r[j+Z], + ss[i].z[j+Z], + (M.mxx*0.5*gg[i].u1[j+Zg]*sin(2*Az) /*Tangential Synth*/ + - M.myy*0.5*gg[i].u1[j+Zg]*sin(2*Az) + - M.mxy*gg[i].u1[j+Zg]*cos(2*Az) + - M.mxz*gg[i].u2[j+Zg]*sin(Az) + + M.myz*gg[i].u2[j+Zg]*cos(Az)), + (M.mxx*0.166667*gg[i].u5[j+Zg] /*Radial Synth*/ + - M.mxx*0.5*gg[i].u3[j+Zg]*cos(2*Az) + + M.mxx*0.3333*gg[i].u9[j+Zg] + + M.myy*0.166667*gg[i].u5[j+Zg] + + M.myy*0.5*gg[i].u3[j+Zg]*cos(2*Az) + + M.myy*0.3333*gg[i].u9[j+Zg] + + M.mzz*0.3333*gg[i].u9[j+Zg] + - M.mzz*0.3333*gg[i].u5[j+Zg] + - M.mxy*gg[i].u3[j+Zg]*sin(2*Az) + + M.mxz*gg[i].u4[j+Zg]*cos(Az) + + M.myz*gg[i].u4[j+Zg]*sin(Az)), + (M.mxx*0.166667*gg[i].u8[j+Zg] /*Vertical Synth*/ + - M.mxx*0.5*gg[i].u6[j+Zg]*cos(2*Az) + + M.mxx*0.3333*gg[i].u10[j+Zg] + + M.myy*0.166667*gg[i].u8[j+Zg] + + M.myy*0.5*gg[i].u6[j+Zg]*cos(2*Az) + + M.myy*0.3333*gg[i].u10[j+Zg] + + M.mzz*0.3333*gg[i].u10[j+Zg] + - M.mzz*0.3333*gg[i].u8[j+Zg] + - M.mxy*gg[i].u6[j+Zg]*sin(2*Az) + + M.mxz*gg[i].u7[j+Zg]*cos(Az) + + M.myz*gg[i].u7[j+Zg]*sin(Az))); + } + } + fclose(fp); + return 0; +} diff --git a/TDMT_GMT/mt_plot_dat.o b/TDMT_GMT/mt_plot_dat.o new file mode 100644 index 0000000..65937d4 Binary files /dev/null and b/TDMT_GMT/mt_plot_dat.o differ diff --git a/TDMT_GMT/mtmanip_module.f b/TDMT_GMT/mtmanip_module.f new file mode 100755 index 0000000..6bc3f16 --- /dev/null +++ b/TDMT_GMT/mtmanip_module.f @@ -0,0 +1,56 @@ + module trigd + + implicit none + + contains + + function sind(x) + real*4 sind, x, pi180_sp + pi180_sp=2.0e0 * asin(1.0e0) / 180.0e0 + sind = sin(pi180_sp * x) + end function sind + + function dsind(x) + real*8 dsind, x, pi180_dp + pi180_dp=2.0d0 * asin(1.0d0) / 180.0d0 + dsind = sin(pi180_dp * x) + end function dsind + + function dasind(x) + real*8 dasind, x, pi180_dp + pi180_dp=2.0d0 * asin(1.0d0) / 180.0d0 + dasind = asin(x) / pi180_dp + end function dasind + + function cosd(x) + real*4 cosd, x, pi180_sp + pi180_sp=2.0e0 * asin(1.0e0) / 180.0e0 + print *,pi180_sp, x + cosd = cos(pi180_sp * x) + end function cosd + + function dcosd(x) + real*8 dcosd, x, pi180_dp + pi180_dp=2.0d0 * asin(1.0d0) / 180.0d0 + dcosd = cos(pi180_dp * x) + end function dcosd + + function dacosd(x) + real*8 dacosd, x, pi180_dp + pi180_dp=2.0d0 * asin(1.0d0) / 180.0d0 + dacosd = acos(x) / pi180_dp + end function dacosd + + function atan2d(x,y) + real*4 atan2d, x, y, pi180_sp + pi180_sp=2.0e0 * asin(1.0e0) / 180.0e0 + atan2d = atan2(x,y) / pi180_sp + end function atan2d + + function datan2d(x,y) + real*8 datan2d, x, y, pi180_dp + pi180_dp=2.0d0 * asin(1.0d0) / 180.0d0 + datan2d = atan2(x,y) / pi180_dp + end function datan2d + + end module trigd diff --git a/TDMT_GMT/mtmanip_module.o b/TDMT_GMT/mtmanip_module.o new file mode 100644 index 0000000..b394bc7 Binary files /dev/null and b/TDMT_GMT/mtmanip_module.o differ diff --git a/TDMT_GMT/mtmanip_new.f b/TDMT_GMT/mtmanip_new.f new file mode 100755 index 0000000..ac50cb9 --- /dev/null +++ b/TDMT_GMT/mtmanip_new.f @@ -0,0 +1,844 @@ + program mtmanip +c +c Manipulate moment sensors: convert between various MT representations +c Based on fmap subroutines by Bob Uhrhammer +c .... Pete Lombard, 20 June 2007 +c + character*1 axis(3) + integer*4 mtrep,iouta,ioute,ioutk,ioutm,ioutr,ioutt, + $ ioutp,ioutw,iexp + real*8 d_mf(6),d_mt_in(3,3),d_mt_out(3,3),d_d(3),d_v(3,3) + real*8 d_miso(3,3),d_mdc(3,3),d_mclvd(3,3),d_m0,d_mw,scale + real*8 d_plunge(3),d_azimuth(3),d_strike(2),d_dip(2),d_slip(2) + real*8 d_pcdc,d_pcclvd,d_pciso + real*4 mt(6),r_iso(6),r_dc(6),r_clvd(6) + character*3 label(6) + character*80 k_conv, a_conv, s_conv + + k_conv = '(Kanamori convention)' + a_conv = '(Aki convention)' + s_conv = '(Spherical coordinates)' +c +c Handle commandline inputs and conversion to tensor representation + iouta=0 + ioute=0 + ioutk=0 + ioutm=0 + ioutr=0 + ioutt=0 + ioutp=0 + ioutw=0 + call clintrp(d_mt_in,mtrep,iouta,ioute,ioutk,ioutm,ioutr,ioutt, + $ ioutp,ioutw) +c +c Compute all other representations from tensor + call m0dcf(mtrep,d_mf,d_mt_in,d_d,d_v,d_miso,d_mdc,d_mclvd, + 1 d_m0,d_mw,axis,d_plunge,d_azimuth,d_strike,d_dip, + 2 d_slip,d_pcdc,d_pcclvd,d_pciso) +c + +c Output the requested representations + call findscale(d_mt_in,iexp,scale,d_miso, d_mdc, d_mclvd) + + if (iouta .eq. 1) then + write(6,'(1h ,a)') ' Plane Strike Rake Dip' + write(6,'(1h ,4x,"NP1",5x,i4,4x,i4,4x,i2)') + $ idnint(d_strike(1)),idnint(d_slip(1)),idnint(d_dip(1)) + write(6,'(1h ,4x,"NP2",5x,i4,4x,i4,4x,i2)') + $ idnint(d_strike(2)),idnint(d_slip(2)),idnint(d_dip(2)) + write(6,'(1h )') + endif + if (ioutp .eq. 1) then + write(6,'(1h ,a)') 'Source Composition:' + write(6,'(1h ,a)') ' Type Percent' + write(6,'(1h ,4x,a,3x,f5.1)') 'DC ',sngl(d_pcdc) + write(6,'(1h ,4x,a,3x,f5.1)') 'CLVD',sngl(d_pcclvd) + write(6,'(1h ,4x,a,3x,f5.1)') 'Iso ',sngl(d_pciso) + write(6,'(1h )') + endif + if (ioutw .eq. 1) then + write(6,'(1h ,a,f4.2,a,e8.2,a)') + $ ' Mw = ',sngl(d_mw),' Mo = ',sngl(d_m0),' Dyne-cm' + write(6,'(1h )') + endif + +c d_mt_in is in "mtrep" format; convert to "standard" format + call mtsrce(mtrep,d_mf,d_mt_in,d_mt_out) + + if (ioutk .eq. 1) then ! Kanamori convention + call convent(d_mt_out,mt,d_miso,r_iso,d_mdc,r_dc,d_mclvd, + $ r_clvd,label,1) + call prmt(mt,r_iso,r_dc,r_clvd,label,ioutt,iexp,k_conv) + endif + if (ioutm .eq. 1) then ! Aki convention + call convent(d_mt_out,mt,d_miso,r_iso,d_mdc,r_dc,d_mclvd, + $ r_clvd,label,2) + call prmt(mt,r_iso,r_dc,r_clvd,label,ioutt,iexp,a_conv) + endif + if (ioutr .eq. 1) then ! Spherical coordinates + call convent(d_mt_out,mt,d_miso,r_iso,d_mdc,r_dc,d_mclvd, + $ r_clvd,label,3) + call prmt(mt,r_iso,r_dc,r_clvd,label,ioutt,iexp,s_conv) + endif + if (ioute .eq. 1) then + do 100 i = 1,3 + if (axis(i) .eq. 'T') then + ixt = i + elseif (axis(i) .eq. 'N') then + ixn = i + elseif (axis(i) .eq. 'P') then + ixp = i + endif + 100 continue + write(6,'(1h ,a,i2,a)') 'Scale = 1.0e+', iexp,' Dyne-cm' + write(6,'(1h ,a)') 'Principal Axes:' + write(6,'(1h ,a)') + $ ' Axis Value(dev) Value(tot) Plunge Azimuth' + write(6,'(1h ,5x,"T",3x,f7.3,4x,f7.3,6x,i2,6x,i3)') + $ d_d(ixt)/scale, d_d(ixt) / scale + d_miso(1,1), + $ idnint(d_plunge(ixt)),idnint(d_azimuth(ixt)) + write(6,'(1h ,5x,"N",3x,f7.3,4x,f7.3,6x,i2,6x,i3)') + $ d_d(ixn)/scale, d_d(ixn) / scale + d_miso(1,1), + $ idnint(d_plunge(ixn)),idnint(d_azimuth(ixn)) + write(6,'(1h ,5x,"P",3x,f7.3,4x,f7.3,6x,i2,6x,i3)') + $ d_d(ixp)/scale, d_d(ixp) /scale + d_miso(1,1), + $ idnint(d_plunge(ixp)),idnint(d_azimuth(ixp)) + write(6,'(1h )') + endif +c + stop + end + + + subroutine clintrp(d_mt,mtrep,iouta,ioute,ioutk,ioutm,ioutr,ioutt, + $ ioutp,ioutw) +c +c ...... command line interpreter +c + real*8 d_mt(3,3), d(3), plunge(3), azimuth(3) + real*4 scale,mxx,mxy,mxz,myy,myz,mzz,m0 + real*4 vp, vn, vt, pp, pn, pt, ap, an, at + real*4 mrr, mtt, mpp, mrt, mrp, mtp + integer*4 mtrep,iouta,ioute,ioutk,ioutm,ioutr,ioutt, + $ ioutp,ioutw +c + character*80 ficarg(20) + integer*4 nfic +c + itype = 0 + nfic=iargc() + if(nfic.gt.22) then + write(*,*) 'Invalid number of command line parameters' + stop + endif + if(nfic.eq.0) then + call mtm_info + stop + endif + do 1 i=1,nfic + call getarg(i,ficarg(i)) + 1 continue + ii=0 + 2 ii=ii+1 + lfa=lnblnk(ficarg(ii)) + if(ficarg(ii)(1:lfa).eq.'-h') then + call mtm_info + if(nfic.eq.1) stop + elseif(ficarg(ii)(1:lfa).eq.'-a') then + if(itype.eq.0) then + itype=1 + else + write(*,*) + $ 'Illegal to input more than one of -a, -e, -k, -m, -r d + $ata' + stop + endif + ii=ii+1 + lfa=lnblnk(ficarg(ii)) + read(ficarg(ii)(1:lfa),*,err=10) m0 + ii=ii+1 + lfa=lnblnk(ficarg(ii)) + read(ficarg(ii)(1:lfa),*,err=10) strike + ii=ii+1 + lfa=lnblnk(ficarg(ii)) + read(ficarg(ii)(1:lfa),*,err=10) rake + ii=ii+1 + lfa=lnblnk(ficarg(ii)) + read(ficarg(ii)(1:lfa),*,err=10) dip + call mtcomp(m0,strike,rake,dip,scale,mxx,mxy,mxz, + 1 myy,myz,mzz) + mtrep=1 + d_mt(1,1)=dble(scale*mxx) + d_mt(1,2)=dble(scale*mxy) + d_mt(1,3)=dble(scale*mxz) + d_mt(2,1)=dble(scale*mxy) + d_mt(2,2)=dble(scale*myy) + d_mt(2,3)=dble(scale*myz) + d_mt(3,1)=dble(scale*mxz) + d_mt(3,2)=dble(scale*myz) + d_mt(3,3)=dble(scale*mzz) + elseif(ficarg(ii)(1:lfa).eq.'-e') then + if(itype.eq.0) then + itype=2 + else + write(*,*) + $ 'Illegal to input more than one of -a, -e, -k, -m, -r d + $ata' + stop + endif + ii=ii+1 + lfa=lnblnk(ficarg(ii)) + read(ficarg(ii)(1:lfa),*,err=10) vp + ii=ii+1 + lfa=lnblnk(ficarg(ii)) + read(ficarg(ii)(1:lfa),*,err=10) vn + ii=ii+1 + lfa=lnblnk(ficarg(ii)) + read(ficarg(ii)(1:lfa),*,err=10) vt + ii=ii+1 + lfa=lnblnk(ficarg(ii)) + read(ficarg(ii)(1:lfa),*,err=10) pp + ii=ii+1 + lfa=lnblnk(ficarg(ii)) + read(ficarg(ii)(1:lfa),*,err=10) pn + ii=ii+1 + lfa=lnblnk(ficarg(ii)) + read(ficarg(ii)(1:lfa),*,err=10) pt + ii=ii+1 + lfa=lnblnk(ficarg(ii)) + read(ficarg(ii)(1:lfa),*,err=10) ap + ii=ii+1 + lfa=lnblnk(ficarg(ii)) + read(ficarg(ii)(1:lfa),*,err=10) an + ii=ii+1 + lfa=lnblnk(ficarg(ii)) + read(ficarg(ii)(1:lfa),*,err=10) at + + mtrep=1 + d(1)=dble(vp) + d(2)=dble(vn) + d(3)=dble(vt) + plunge(1)=dble(pp) + plunge(2)=dble(pn) + plunge(3)=dble(pt) + azimuth(1)=dble(ap) + azimuth(2)=dble(an) + azimuth(3)=dble(at) + call epa2mt(d,plunge,azimuth,d_mt) + + elseif(ficarg(ii)(1:lfa).eq.'-m') then + if(itype.eq.0) then + itype=2 + else + write(*,*) + $ 'Illegal to input more than one of -a, -e, -k, -m, -r d + $ata' + stop + endif + ii=ii+1 + lfa=lnblnk(ficarg(ii)) + read(ficarg(ii)(1:lfa),*,err=10) scale + ii=ii+1 + lfa=lnblnk(ficarg(ii)) + read(ficarg(ii)(1:lfa),*,err=10) mxx + ii=ii+1 + lfa=lnblnk(ficarg(ii)) + read(ficarg(ii)(1:lfa),*,err=10) mxy + ii=ii+1 + lfa=lnblnk(ficarg(ii)) + read(ficarg(ii)(1:lfa),*,err=10) mxz + ii=ii+1 + lfa=lnblnk(ficarg(ii)) + read(ficarg(ii)(1:lfa),*,err=10) myy + ii=ii+1 + lfa=lnblnk(ficarg(ii)) + read(ficarg(ii)(1:lfa),*,err=10) myz + ii=ii+1 + lfa=lnblnk(ficarg(ii)) + read(ficarg(ii)(1:lfa),*,err=10) mzz + + mtrep=1 + d_mt(1,1)=dble(scale*mxx) + d_mt(1,2)=dble(scale*mxy) + d_mt(1,3)=dble(scale*mxz) + d_mt(2,1)=dble(scale*mxy) + d_mt(2,2)=dble(scale*myy) + d_mt(2,3)=dble(scale*myz) + d_mt(3,1)=dble(scale*mxz) + d_mt(3,2)=dble(scale*myz) + d_mt(3,3)=dble(scale*mzz) + elseif(ficarg(ii)(1:lfa).eq.'-k') then + if(itype.eq.0) then + itype=2 + else + write(*,*) + $ 'Illegal to input more than one of -a, -e, -k, -m, -r d + $ata' + stop + endif + ii=ii+1 + lfa=lnblnk(ficarg(ii)) + read(ficarg(ii)(1:lfa),*,err=10) scale + ii=ii+1 + lfa=lnblnk(ficarg(ii)) + read(ficarg(ii)(1:lfa),*,err=10) mxx + ii=ii+1 + lfa=lnblnk(ficarg(ii)) + read(ficarg(ii)(1:lfa),*,err=10) mxy + ii=ii+1 + lfa=lnblnk(ficarg(ii)) + read(ficarg(ii)(1:lfa),*,err=10) mxz + ii=ii+1 + lfa=lnblnk(ficarg(ii)) + read(ficarg(ii)(1:lfa),*,err=10) myy + ii=ii+1 + lfa=lnblnk(ficarg(ii)) + read(ficarg(ii)(1:lfa),*,err=10) myz + ii=ii+1 + lfa=lnblnk(ficarg(ii)) + read(ficarg(ii)(1:lfa),*,err=10) mzz + + mtrep=1 + d_mt(1,1)= -dble(scale*mxx) + d_mt(1,2)= -dble(scale*mxy) + d_mt(1,3)= -dble(scale*mxz) + d_mt(2,1)= -dble(scale*mxy) + d_mt(2,2)= -dble(scale*myy) + d_mt(2,3)= -dble(scale*myz) + d_mt(3,1)= -dble(scale*mxz) + d_mt(3,2)= -dble(scale*myz) + d_mt(3,3)= -dble(scale*mzz) + elseif(ficarg(ii)(1:lfa).eq.'-r') then + if(itype.eq.0) then + itype=2 + else + write(*,*) + $ 'Illegal to input more than one of -a, -e, -k, -m, -r d + $ata' + stop + endif + ii=ii+1 + lfa=lnblnk(ficarg(ii)) + read(ficarg(ii)(1:lfa),*,err=10) scale + ii=ii+1 + lfa=lnblnk(ficarg(ii)) + read(ficarg(ii)(1:lfa),*,err=10) mrr + ii=ii+1 + lfa=lnblnk(ficarg(ii)) + read(ficarg(ii)(1:lfa),*,err=10) mrt + ii=ii+1 + lfa=lnblnk(ficarg(ii)) + read(ficarg(ii)(1:lfa),*,err=10) mrp + ii=ii+1 + lfa=lnblnk(ficarg(ii)) + read(ficarg(ii)(1:lfa),*,err=10) mtt + ii=ii+1 + lfa=lnblnk(ficarg(ii)) + read(ficarg(ii)(1:lfa),*,err=10) mtp + ii=ii+1 + lfa=lnblnk(ficarg(ii)) + read(ficarg(ii)(1:lfa),*,err=10) mpp + + mtrep=0 + d_mt(1,1)=dble(scale*mrr) + d_mt(1,2)=dble(scale*mrt) + d_mt(1,3)=dble(scale*mrp) + d_mt(2,1)=dble(scale*mrt) + d_mt(2,2)=dble(scale*mtt) + d_mt(2,3)=dble(scale*mtp) + d_mt(3,1)=dble(scale*mrp) + d_mt(3,2)=dble(scale*mtp) + d_mt(3,3)=dble(scale*mpp) + elseif(ficarg(ii)(1:lfa).eq.'-A') then + iouta = 1 + elseif(ficarg(ii)(1:lfa).eq.'-E') then + ioute = 1 + elseif(ficarg(ii)(1:lfa).eq.'-K') then + ioutk = 1 + elseif(ficarg(ii)(1:lfa).eq.'-M') then + ioutm = 1 + elseif(ficarg(ii)(1:lfa).eq.'-P') then + ioutp = 1 + elseif(ficarg(ii)(1:lfa).eq.'-R') then + ioutr = 1 + elseif(ficarg(ii)(1:lfa).eq.'-T') then + ioutt = 1 + elseif(ficarg(ii)(1:lfa).eq.'-W') then + ioutw = 1 + elseif(ficarg(ii)(1:lfa).eq.'-Z') then + iouta = 1 + ioute = 1 + ioutk = 1 + ioutm = 1 + ioutp = 1 + ioutr = 1 + ioutt = 1 + ioutw = 1 + else + write(*,*) 'Invalid parameter ' + do 3 i=1,nfic + write(*,'(a,i2,2a)') 'ficarg(',i,') = ', + 1 ficarg(i)(1:lnblnk(ficarg(i))) + 3 continue + stop + endif +c print *,iouta,ioute,ioutk,ioutm,ioutp,ioutr,ioutt,ioutw + if(ii.lt.nfic) go to 2 + + if(itype.eq.0) then + write(*,*) + $ 'Invalid input: one of -a, -e, -k, -m, -r is REQUIRED' + call mtm_info + stop + endif +c + return +c + 10 continue + write(*,*) 'Parameter ficarg(',ii,') = ',ficarg(ii)(1:lfa), + 1 ' is illegal' + call mtm_info + stop +c + end + + subroutine mtm_info +c +c ...... print out information about fps_pr +c + implicit real*8 (a-h,o-z) + implicit integer*4 (i-n) +c + write(*,'(1h ,a)') + 1 'fmap - Focal Mechanism ASCII Plot File Program' +c + write(*,'(1h ,a)') 'Syntax:' + write(*,'(1h ,7x,a)') 'mtmanip -a m0 strike rake dip |' + write(*,'(1h ,7x,a)') + $ ' -e P N T plngP plngN plngT azP azN azT | ' + write(*,'(1h ,7x,a)') ' -k scale mxx mxy mxz myy myz mzz |' + write(*,'(1h ,7x,a)') ' -m scale mxx mxy mxz myy myz mzz |' + write(*,'(1h ,7x,a)') ' -r scale mrr mrt mrp mtt mtp mpp ' + write(*,'(1h ,7x,a)') + $ ' [-A] [-E] [-K | -M | -R [-T]] [-P] [-W] [-Z]' + write(*,'(1h ,7x,a)') ' [-h]' +c + write(*,'(1h ,7x,a)') 'where input is one of:' + write(*,'(1h ,7x,a)') + 1 '-a Angle Input - m0, strike, rake & dip' + write(*,'(1h ,7x,a)') + 1 '-e Eigenvalue Input - P value, N value, T value' + write(*,'(1h ,7x,a)') + 1 ' P plunge N plunge T plunge' + write(*,'(1h ,7x,a)') + 1 ' P azimuth N azimuth T azimuth' + write(*,'(1h ,7x,a)') + $ '-k MT Input (Kanamori convention) - scale, mxx, ' + write(*,'(1h ,7x,a)') ' mxy, mxz, myy, myz & mzz' + write(*,'(1h ,7x,a)') + $ '-m MT Input (Aki convention) - scale, mxx, ' + write(*,'(1h ,7x,a)') ' mxy, mxz, myy, myz & mzz' + write(*,'(1h ,7x,a)') + $ '-r MT Input (Spherical coordinates) - scale, mrr, ' + write(*,'(1h ,7x,a)') ' mrt, mrp, mtt, mtp & mpp' + write(*,'(1h ,7x,a)') + $ ' where r -> radial, t -> theta, p -> phi components' + write(*,'(1h ,7x,a)') + write(*,'(1h ,7x,a)') 'and output is one or more of:' + write(*,'(1h ,7x,a)') + $ '-A Angle Output - m0, strike1, rake1, dip1, strike2, rake2 + $ , dip2' + write(*,'(1h ,7x,a)')'-E Eigenvalue, Azimuth and Plunge output' + write(*,'(1h ,7x,a)') + $ '-K MT Output (Kanamori convention) - m0, mxx, ' + write(*,'(1h ,7x,a)') ' mxy, mxz, myy, myz & mzz' + write(*,'(1h ,7x,a)') + $ '-M MT Output (Aki convention) - m0, mxx, ' + write(*,'(1h ,7x,a)') ' mxy, mxz, myy, myz & mzz' + write(*,'(1h ,7x,a)') + $ '-R MT Output (Spherical coordinates) - m0, mrr, ' + write(*,'(1h ,7x,a)') ' mrt, mrp, mtt, mtp & mpp' + write(*,'(1h ,7x,a)') + $ '-T split any of tensor outputs into double couple, CLVD' + write(*,'(1h ,7x,a)') ' and isotropic tensors' + write(*,'(1h ,7x,a)') + $ '-P percent double couple, CLVD and isotropic' + write(*,'(1h ,7x,a)') '-W Mw output' + write(*,'(1h ,7x,a)') '-Z the whole enchilada (all outputs)' + write(*,'(1h ,7x,a)') + 1 '-h Help - prints this help message' +c +c xxx1xxxxxxxxx2xxxxxxxxx3xxxxxxxxx4xxxxxxxxx5xxxxxxxxx6xxxxxxxxx7xx +c + write(*,'(1h ,a)') 'Notes:' + write(*,'(1h ,7x,a)') 'The scalar moment (m0) is given in ' + write(*,'(1h ,7x,a)') ' dyne-cm and the strike, rake' + write(*,'(1h ,7x,a)') ' and dip are given in degrees.' + write(*,'(1h ,7x,a)') 'The moment input is given in dyne-cm,' + write(*,'(1h ,7x,a)') ' and the scale factor is a' + write(*,'(1h ,7x,a)') ' power of ten.' +c + return + end + + + + + subroutine epa2mt(d,plunge,azimuth,mt) +c +c Given three eigenvalues, plunges and azimuths, compute moment tensor +c + real*8 d(3),plunge(3),azimuth(3),mt(3,3) +c + integer*4 n,i,j + real*8 v(3,3),work(3,3),diag(3,3) + n = 3 + + call zalp(plunge, azimuth, v) + do 20 i = 1,3 + do 10 j = 1,3 + if (i .eq. j) then + diag(i,j) = d(i) + else + diag(i,j) = 0.0 + endif + 10 continue + 20 continue + + call transpose(v,n) + call mmulquad(v,diag,mt,work,n,n) + + return + end + + subroutine zalp(plunge,azimuth,v) + use trigd +c Compute orthonormal eigenvectors from azimuth and plunge +c Fatal error if vector described by azimuth and plunge are not orthogonal + real*8 plunge(3), azimuth(3), v(3,3),sum(3), tol + integer*4 i + + data tol/1.0d-4/ + + do 10 i = 1, 3 + sum(i) = 0.0d0 + v(3,i) = dsind(plunge(i)) + v(2,i) = dcosd(plunge(i)) * dsind(azimuth(i)) + v(1,i) = dcosd(plunge(i)) * dcosd(azimuth(i)) + 10 continue + do 20 i = 1,3 + sum(1) = sum(1) + v(i,1) * v(i,2) + sum(2) = sum(2) + v(i,2) * v(i,3) + sum(3) = sum(3) + v(i,3) * v(i,1) + 20 continue + do 30 i = 1,3 + if (dabs(sum(i)) .gt. tol) then + write(*,*) 'Plunge/azimuth vectors are not orthogonal' + stop + endif + 30 continue +c + return + end + + + +C****************************************************************** +C +C SUBROUTINE: MMULQUAD +C +C SYNOPSIS: Computes the matrix product C = A'*B*A where +C full storage is used for the symmetric matrix C. +C +C PARAMETERS: +C A MxN double precision matrix +C B MxM double precision matrix +C C NxN double precision matrix +C WORK NxM double precision matrix +C +C****************************************************************** + + + subroutine mmulquad(a,b,c,work,m,n) + integer*4 m,n + real*8 a(m,n),b(m,m),c(n,n),work(n,m) +c +c +c do the multiplications + call mmul2 (a,b,work,n,m,m) + call mmulmmsf (work,a, c, n,m) +c +c finish + return + end + + + +C****************************************************************** +C +C SUBROUTINE: MMUL2 +C +C SYNOPSIS: Computes the matrix product C = A'*B +C +C PARAMETERS: +C A N*M double precision matrix +C B N*P double precision matrix +C C MxP double precision matrix +C M column dimension of A and row dimension of C +C N row dimension of A and row dimension of B +C P column dimension of B and coulmn dimesnion of C +C +C******************************************************************* + + + subroutine mmul2(a,b,c,m,n,p) + integer*4 m,n,p + real*8 a(n,m),b(n,p),c(m,p) +c +C Local Declarations + integer*4 i,j,k + real*8 temp +C +C The main loop + do 30 i=1,m + do 20 j=1,p + temp =0 + do 10 k=1,n + temp = temp+a(k,i)*b(k,j) + 10 continue + c(i,j) = temp + 20 continue + 30 continue +C +C finish + return + end + + +C****************************************************************** +C +C SUBROUTINE: MMULMMSF +C +C SYNOPSIS: Computes the matrix product C = AxB , when A is +C assumed to be symmetric. C is the only parameter that is +C changed upon exit. +C +C PARAMETERS: +C A MxN double precision matrix +C B NxM double precision matrix +C C MxM double precision matrix +C M Row dimension of A and C. Column dimension of B and C. +C N Column dimension of A. Row dimension of B. +C +C****************************************************************** + + + subroutine mmulmmsf(a,b,c,m,n) + integer*4 m,n + real*8 a(m,n),b(n,m),c(m,m) +c +C Local Declarations + integer*4 i,j,k + real*8 temp +c +C The main loop + do 30 i=1,m + do 20 j=i,m + temp =0 + do 10 k=1,n + temp = temp+a(i,k)*b(k,j) + 10 continue + c(i,j) = temp + c(j,i) = temp + 20 continue + 30 continue +c +C finish + return + end + + + subroutine transpose(v,n) + integer*4 n, i, j + real*8 v(n,n), tmp + + if (n .lt. 2) return + + do 20 i = 1, n + do 10 j = i+1, n + tmp = v(i,j) + v(i,j) = v(j,i) + v(j,i) = tmp + 10 continue + 20 continue + return + end + + subroutine findscale(d_mt,iexp,scale,d_miso, d_mdc, d_mclvd) +c Compute a scaling factor that is a power of ten +c Apply the scaling to d_mt + real*8 d_mt(3,3),d_miso(3,3), d_mdc(3,3), d_mclvd(3,3) + real*8 scale, argmax + integer*4 iexp, i, j + + argmax=dabs(d_mt(1,1)) + do 22 i=1,3 + do 21 j=1,3 + if (dabs(d_mt(i,j)) .gt. argmax) + $ argmax = abs(d_mt(i,j)) + 21 continue + 22 continue + + iexp=0 + 23 iexp=iexp+1 + scale = 10.0d0 ** iexp + if (scale .le. argmax) goto 23 + iexp=iexp-1 + scale = 10.0d0 ** iexp + + do 25 i = 1,3 + do 24 j = 1,3 + d_mt(i,j) = d_mt(i,j) / scale + d_miso(i,j) = d_miso(i,j) / scale + d_mdc(i,j) = d_mdc(i,j) / scale + d_mclvd(i,j) = d_mclvd(i,j) / scale + 24 continue + 25 continue + return + end + + + subroutine convent(d_mt,mt,d_miso,r_iso,d_mdc,r_dc,d_mclvd,r_clvd, + $ label,iconv) + real*8 d_mt(3,3),d_miso(3,3),d_mdc(3,3),d_mclvd(3,3) + real*4 mt(6),r_iso(6),r_dc(6),r_clvd(6) + character*3 label(6) + + if (iconv .eq. 1) then ! Kanamori convention + label(1) = 'mxx' + mt(1) = -d_mt(1,1) + r_iso(1) = -d_miso(1,1) + r_dc(1) = -d_mdc(1,1) + r_clvd(1) = -d_mclvd(1,1) + + label(2) = 'mxy' + mt(2) = -d_mt(1,2) + r_iso(2) = -d_miso(1,2) + r_dc(2) = -d_mdc(1,2) + r_clvd(2) = -d_mclvd(1,2) + + label(3) = 'mxz' + mt(3) = -d_mt(1,3) + r_iso(3) = -d_miso(1,3) + r_dc(3) = -d_mdc(1,3) + r_clvd(3) = -d_mclvd(1,3) + + label(4) = 'myy' + mt(4) = -d_mt(2,2) + r_iso(4) = -d_miso(2,2) + r_dc(4) = -d_mdc(2,2) + r_clvd(4) = -d_mclvd(2,2) + + label(5) = 'myz' + mt(5) = -d_mt(2,3) + r_iso(5) = -d_miso(2,3) + r_dc(5) = -d_mdc(2,3) + r_clvd(5) = -d_mclvd(2,3) + + label(6) = 'mzz' + mt(6) = -d_mt(3,3) + r_iso(6) = -d_miso(3,3) + r_dc(6) = -d_mdc(3,3) + r_clvd(6) = -d_mclvd(3,3) + elseif (iconv .eq. 2) then ! Aki convention + label(1) = 'mxx' + mt(1) = d_mt(1,1) + r_iso(1) = d_miso(1,1) + r_dc(1) = d_mdc(1,1) + r_clvd(1) = d_mclvd(1,1) + label(2) = 'mxy' + mt(2) = d_mt(1,2) + r_iso(2) = d_miso(1,2) + r_dc(2) = d_mdc(1,2) + r_clvd(2) = d_mclvd(1,2) + label(3) = 'mxz' + mt(3) = d_mt(1,3) + r_iso(3) = d_miso(1,3) + r_dc(3) = d_mdc(1,3) + r_clvd(3) = d_mclvd(1,3) + label(4) = 'myy' + mt(4) = d_mt(2,2) + r_iso(4) = d_miso(2,2) + r_dc(4) = d_mdc(2,2) + r_clvd(4) = d_mclvd(2,2) + label(5) = 'myz' + mt(5) = d_mt(2,3) + r_iso(5) = d_miso(2,3) + r_dc(5) = d_mdc(2,3) + r_clvd(5) = d_mclvd(2,3) + label(6) = 'mzz' + mt(6) = d_mt(3,3) + r_iso(6) = d_miso(3,3) + r_dc(6) = d_mdc(3,3) + r_clvd(6) = d_mclvd(3,3) + elseif (iconv .eq. 3) then ! Spherical coordinates + label(1) = 'mrr' + mt(1) = d_mt(3,3) + r_iso(1) = d_miso(3,3) + r_dc(1) = d_mdc(3,3) + r_clvd(1) = d_mclvd(3,3) + label(2) = 'mrt' + mt(2) = d_mt(1,3) + r_iso(2) = d_miso(1,3) + r_dc(2) = d_mdc(1,3) + r_clvd(2) = d_mclvd(1,3) + label(3) = 'mrp' + mt(3) = -d_mt(2,3) + r_iso(3) = -d_miso(2,3) + r_dc(3) = -d_mdc(2,3) + r_clvd(3) = -d_mclvd(2,3) + label(4) = 'mtt' + mt(4) = d_mt(1,1) + r_iso(4) = d_miso(1,1) + r_dc(4) = d_mdc(1,1) + r_clvd(4) = d_mclvd(1,1) + label(5) = 'mtp' + mt(5) = -d_mt(1,2) + r_iso(5) = -d_miso(1,2) + r_dc(5) = -d_mdc(1,2) + r_clvd(5) = -d_mclvd(1,2) + label(6) = 'mpp' + mt(6) = d_mt(2,2) + r_iso(6) = d_miso(2,2) + r_dc(6) = d_mdc(2,2) + r_clvd(6) = d_mclvd(2,2) + endif + return + end + + subroutine prmt(mt,r_iso,r_dc,r_clvd,label,ioutt,iexp,conv) + real*4 mt(6),r_iso(6),r_dc(6),r_clvd(6) + character*3 label(6) + character*(*) conv + + write(6,'(1h ,a,i2,a,a)') 'Moment Tensor: Scale = 10**', + 1 iexp,' Dyne-cm ', conv + if (ioutt .eq. 1) then + write(6,'(1h ,a)') + $ ' Component iso dc clvd total' + else + write(6,'(1h ,a)') ' Component Value' + endif + do 100 i = 1,6 + if (ioutt .eq. 1) then + write(6,'(1h ,6x,a,4(2x,f7.3))') label(i),r_iso(i), + $ r_dc(i),r_clvd(i),mt(i) + else + write(6,'(1h ,6x,a,5x,f7.3)') label(i),mt(i) + endif + 100 continue + write(6,'(1h )') + + + return + end diff --git a/TDMT_GMT/mtmanip_new.f_save b/TDMT_GMT/mtmanip_new.f_save new file mode 100755 index 0000000..46b441f --- /dev/null +++ b/TDMT_GMT/mtmanip_new.f_save @@ -0,0 +1,835 @@ + program mtmanip +c +c Manipulate moment sensors: convert between various MT representations +c Based on fmap subroutines by Bob Uhrhammer +c .... Pete Lombard, 20 June 2007 +c + character*1 axis(3) + integer*4 mtrep,iouta,ioute,ioutk,ioutm,ioutr,ioutt, + $ ioutp,ioutw,iexp + real*8 d_mf(6),d_mt_in(3,3),d_mt_out(3,3),d_d(3),d_v(3,3) + real*8 d_miso(3,3),d_mdc(3,3),d_mclvd(3,3),d_m0,d_mw,scale + real*8 d_plunge(3),d_azimuth(3),d_strike(2),d_dip(2),d_slip(2) + real*8 d_pcdc,d_pcclvd,d_pciso + real*4 mt(6),r_iso(6),r_dc(6),r_clvd(6) + character*3 label(6) + character*80 k_conv, a_conv, s_conv + + k_conv = '(Kanamori convention)' + a_conv = '(Aki convention)' + s_conv = '(Spherical coordinates)' +c +c Handle commandline inputs and conversion to tensor representation + call clintrp(d_mt_in,mtrep,iouta,ioute,ioutk,ioutm,ioutr,ioutt, + $ ioutp,ioutw) +c +c Compute all other representations from tensor + call m0dcf(mtrep,d_mf,d_mt_in,d_d,d_v,d_miso,d_mdc,d_mclvd, + 1 d_m0,d_mw,axis,d_plunge,d_azimuth,d_strike,d_dip, + 2 d_slip,d_pcdc,d_pcclvd,d_pciso) +c + +c Output the requested representations + call findscale(d_mt_in,iexp,scale,d_miso, d_mdc, d_mclvd) + + if (iouta .eq. 1) then + write(6,'(1h ,a)') ' Plane Strike Rake Dip' + write(6,'(1h ,4x,"NP1",5x,i4,4x,i4,4x,i2)') + $ idnint(d_strike(1)),idnint(d_slip(1)),idnint(d_dip(1)) + write(6,'(1h ,4x,"NP2",5x,i4,4x,i4,4x,i2)') + $ idnint(d_strike(2)),idnint(d_slip(2)),idnint(d_dip(2)) + write(6,'(1h )') + endif + if (ioute .eq. 1) then + do 100 i = 1,3 + if (axis(i) .eq. 'T') then + ixt = i + elseif (axis(i) .eq. 'N') then + ixn = i + elseif (axis(i) .eq. 'P') then + ixp = i + endif + 100 continue + write(6,'(1h ,a,i2,a)') 'Scale = 1.0e+', iexp,' Dyne-cm' + write(6,'(1h ,a)') 'Principal Axes:' + write(6,'(1h ,a)') + $ ' Axis Value(dev) Value(tot) Plunge Azimuth' + write(6,'(1h ,5x,"T",3x,f7.3,4x,f7.3,6x,i2,6x,i3)') + $ d_d(ixt)/scale, d_d(ixt) / scale + d_miso(1,1), + $ idnint(d_plunge(ixt)),idnint(d_azimuth(ixt)) + write(6,'(1h ,5x,"N",3x,f7.3,4x,f7.3,6x,i2,6x,i3)') + $ d_d(ixn)/scale, d_d(ixn) / scale + d_miso(1,1), + $ idnint(d_plunge(ixn)),idnint(d_azimuth(ixn)) + write(6,'(1h ,5x,"P",3x,f7.3,4x,f7.3,6x,i2,6x,i3)') + $ d_d(ixp)/scale, d_d(ixp) /scale + d_miso(1,1), + $ idnint(d_plunge(ixp)),idnint(d_azimuth(ixp)) + write(6,'(1h )') + endif + if (ioutp .eq. 1) then + write(6,'(1h ,a)') 'Source Composition:' + write(6,'(1h ,a)') ' Type Percent' + write(6,'(1h ,4x,a,3x,f5.1)') 'DC ',sngl(d_pcdc) + write(6,'(1h ,4x,a,3x,f5.1)') 'CLVD',sngl(d_pcclvd) + write(6,'(1h ,4x,a,3x,f5.1)') 'Iso ',sngl(d_pciso) + write(6,'(1h )') + endif + if (ioutw .eq. 1) then + write(6,'(1h ,a,f4.2,a,e8.2,a)') + $ ' Mw = ',sngl(d_mw),' Mo = ',sngl(d_m0),' Dyne-cm' + write(6,'(1h )') + endif + +c d_mt_in is in "mtrep" format; convert to "standard" format + call mtsrce(mtrep,d_mf,d_mt_in,d_mt_out) + + if (ioutk .eq. 1) then ! Kanamori convention + call convent(d_mt_out,mt,d_miso,r_iso,d_mdc,r_dc,d_mclvd, + $ r_clvd,label,1) + call prmt(mt,r_iso,r_dc,r_clvd,label,ioutt,iexp,k_conv) + endif + if (ioutm .eq. 1) then ! Aki convention + call convent(d_mt_out,mt,d_miso,r_iso,d_mdc,r_dc,d_mclvd, + $ r_clvd,label,2) + call prmt(mt,r_iso,r_dc,r_clvd,label,ioutt,iexp,a_conv) + endif + if (ioutr .eq. 1) then ! Spherical coordinates + call convent(d_mt_out,mt,d_miso,r_iso,d_mdc,r_dc,d_mclvd, + $ r_clvd,label,3) + call prmt(mt,r_iso,r_dc,r_clvd,label,ioutt,iexp,s_conv) + endif +c + stop + end + + + subroutine clintrp(d_mt,mtrep,iouta,ioute,ioutk,ioutm,ioutr,ioutt, + $ ioutp,ioutw) +c +c ...... command line interpreter +c + real*8 d_mt(3,3), d(3), plunge(3), azimuth(3) + real*4 scale,mxx,mxy,mxz,myy,myz,mzz,m0 + real*4 vp, vn, vt, pp, pn, pt, ap, an, at + real*4 mrr, mtt, mpp, mrt, mrp, mtp + integer*4 mtrep,iouta,ioute,ioutk,ioutm,ioutr,ioutt, + $ ioutp,ioutw +c + character*80 ficarg(20) + integer*4 nfic +c + itype = 0 + nfic=iargc() + if(nfic.gt.22) then + write(*,*) 'Invalid number of command line parameters' + stop + endif + if(nfic.eq.0) then + call mtm_info + stop + endif + do 1 i=1,nfic + call getarg(i,ficarg(i)) + 1 continue + ii=0 + 2 ii=ii+1 + lfa=lnblnk(ficarg(ii)) + if(ficarg(ii)(1:lfa).eq.'-h') then + call mtm_info + if(nfic.eq.1) stop + elseif(ficarg(ii)(1:lfa).eq.'-a') then + if(itype.eq.0) then + itype=1 + else + write(*,*) + $ 'Illegal to input more than one of -a, -e, -k, -m, -r d + $ata' + stop + endif + ii=ii+1 + lfa=lnblnk(ficarg(ii)) + read(ficarg(ii)(1:lfa),*,err=10) m0 + ii=ii+1 + lfa=lnblnk(ficarg(ii)) + read(ficarg(ii)(1:lfa),*,err=10) strike + ii=ii+1 + lfa=lnblnk(ficarg(ii)) + read(ficarg(ii)(1:lfa),*,err=10) rake + ii=ii+1 + lfa=lnblnk(ficarg(ii)) + read(ficarg(ii)(1:lfa),*,err=10) dip + call mtcomp(m0,strike,rake,dip,scale,mxx,mxy,mxz, + 1 myy,myz,mzz) + mtrep=1 + d_mt(1,1)=dble(scale*mxx) + d_mt(1,2)=dble(scale*mxy) + d_mt(1,3)=dble(scale*mxz) + d_mt(2,1)=dble(scale*mxy) + d_mt(2,2)=dble(scale*myy) + d_mt(2,3)=dble(scale*myz) + d_mt(3,1)=dble(scale*mxz) + d_mt(3,2)=dble(scale*myz) + d_mt(3,3)=dble(scale*mzz) + elseif(ficarg(ii)(1:lfa).eq.'-e') then + if(itype.eq.0) then + itype=2 + else + write(*,*) + $ 'Illegal to input more than one of -a, -e, -k, -m, -r d + $ata' + stop + endif + ii=ii+1 + lfa=lnblnk(ficarg(ii)) + read(ficarg(ii)(1:lfa),*,err=10) vp + ii=ii+1 + lfa=lnblnk(ficarg(ii)) + read(ficarg(ii)(1:lfa),*,err=10) vn + ii=ii+1 + lfa=lnblnk(ficarg(ii)) + read(ficarg(ii)(1:lfa),*,err=10) vt + ii=ii+1 + lfa=lnblnk(ficarg(ii)) + read(ficarg(ii)(1:lfa),*,err=10) pp + ii=ii+1 + lfa=lnblnk(ficarg(ii)) + read(ficarg(ii)(1:lfa),*,err=10) pn + ii=ii+1 + lfa=lnblnk(ficarg(ii)) + read(ficarg(ii)(1:lfa),*,err=10) pt + ii=ii+1 + lfa=lnblnk(ficarg(ii)) + read(ficarg(ii)(1:lfa),*,err=10) ap + ii=ii+1 + lfa=lnblnk(ficarg(ii)) + read(ficarg(ii)(1:lfa),*,err=10) an + ii=ii+1 + lfa=lnblnk(ficarg(ii)) + read(ficarg(ii)(1:lfa),*,err=10) at + + mtrep=1 + d(1)=dble(vp) + d(2)=dble(vn) + d(3)=dble(vt) + plunge(1)=dble(pp) + plunge(2)=dble(pn) + plunge(3)=dble(pt) + azimuth(1)=dble(ap) + azimuth(2)=dble(an) + azimuth(3)=dble(at) + call epa2mt(d,plunge,azimuth,d_mt) + + elseif(ficarg(ii)(1:lfa).eq.'-m') then + if(itype.eq.0) then + itype=2 + else + write(*,*) + $ 'Illegal to input more than one of -a, -e, -k, -m, -r d + $ata' + stop + endif + ii=ii+1 + lfa=lnblnk(ficarg(ii)) + read(ficarg(ii)(1:lfa),*,err=10) scale + ii=ii+1 + lfa=lnblnk(ficarg(ii)) + read(ficarg(ii)(1:lfa),*,err=10) mxx + ii=ii+1 + lfa=lnblnk(ficarg(ii)) + read(ficarg(ii)(1:lfa),*,err=10) mxy + ii=ii+1 + lfa=lnblnk(ficarg(ii)) + read(ficarg(ii)(1:lfa),*,err=10) mxz + ii=ii+1 + lfa=lnblnk(ficarg(ii)) + read(ficarg(ii)(1:lfa),*,err=10) myy + ii=ii+1 + lfa=lnblnk(ficarg(ii)) + read(ficarg(ii)(1:lfa),*,err=10) myz + ii=ii+1 + lfa=lnblnk(ficarg(ii)) + read(ficarg(ii)(1:lfa),*,err=10) mzz + + mtrep=1 + d_mt(1,1)=dble(scale*mxx) + d_mt(1,2)=dble(scale*mxy) + d_mt(1,3)=dble(scale*mxz) + d_mt(2,1)=dble(scale*mxy) + d_mt(2,2)=dble(scale*myy) + d_mt(2,3)=dble(scale*myz) + d_mt(3,1)=dble(scale*mxz) + d_mt(3,2)=dble(scale*myz) + d_mt(3,3)=dble(scale*mzz) + elseif(ficarg(ii)(1:lfa).eq.'-k') then + if(itype.eq.0) then + itype=2 + else + write(*,*) + $ 'Illegal to input more than one of -a, -e, -k, -m, -r d + $ata' + stop + endif + ii=ii+1 + lfa=lnblnk(ficarg(ii)) + read(ficarg(ii)(1:lfa),*,err=10) scale + ii=ii+1 + lfa=lnblnk(ficarg(ii)) + read(ficarg(ii)(1:lfa),*,err=10) mxx + ii=ii+1 + lfa=lnblnk(ficarg(ii)) + read(ficarg(ii)(1:lfa),*,err=10) mxy + ii=ii+1 + lfa=lnblnk(ficarg(ii)) + read(ficarg(ii)(1:lfa),*,err=10) mxz + ii=ii+1 + lfa=lnblnk(ficarg(ii)) + read(ficarg(ii)(1:lfa),*,err=10) myy + ii=ii+1 + lfa=lnblnk(ficarg(ii)) + read(ficarg(ii)(1:lfa),*,err=10) myz + ii=ii+1 + lfa=lnblnk(ficarg(ii)) + read(ficarg(ii)(1:lfa),*,err=10) mzz + + mtrep=1 + d_mt(1,1)= -dble(scale*mxx) + d_mt(1,2)= -dble(scale*mxy) + d_mt(1,3)= -dble(scale*mxz) + d_mt(2,1)= -dble(scale*mxy) + d_mt(2,2)= -dble(scale*myy) + d_mt(2,3)= -dble(scale*myz) + d_mt(3,1)= -dble(scale*mxz) + d_mt(3,2)= -dble(scale*myz) + d_mt(3,3)= -dble(scale*mzz) + elseif(ficarg(ii)(1:lfa).eq.'-r') then + if(itype.eq.0) then + itype=2 + else + write(*,*) + $ 'Illegal to input more than one of -a, -e, -k, -m, -r d + $ata' + stop + endif + ii=ii+1 + lfa=lnblnk(ficarg(ii)) + read(ficarg(ii)(1:lfa),*,err=10) scale + ii=ii+1 + lfa=lnblnk(ficarg(ii)) + read(ficarg(ii)(1:lfa),*,err=10) mrr + ii=ii+1 + lfa=lnblnk(ficarg(ii)) + read(ficarg(ii)(1:lfa),*,err=10) mrt + ii=ii+1 + lfa=lnblnk(ficarg(ii)) + read(ficarg(ii)(1:lfa),*,err=10) mrp + ii=ii+1 + lfa=lnblnk(ficarg(ii)) + read(ficarg(ii)(1:lfa),*,err=10) mtt + ii=ii+1 + lfa=lnblnk(ficarg(ii)) + read(ficarg(ii)(1:lfa),*,err=10) mtp + ii=ii+1 + lfa=lnblnk(ficarg(ii)) + read(ficarg(ii)(1:lfa),*,err=10) mpp + + mtrep=0 + d_mt(1,1)=dble(scale*mrr) + d_mt(1,2)=dble(scale*mrt) + d_mt(1,3)=dble(scale*mrp) + d_mt(2,1)=dble(scale*mrt) + d_mt(2,2)=dble(scale*mtt) + d_mt(2,3)=dble(scale*mtp) + d_mt(3,1)=dble(scale*mrp) + d_mt(3,2)=dble(scale*mtp) + d_mt(3,3)=dble(scale*mpp) + elseif(ficarg(ii)(1:lfa).eq.'-A') then + iouta = 1 + elseif(ficarg(ii)(1:lfa).eq.'-E') then + ioute = 1 + elseif(ficarg(ii)(1:lfa).eq.'-K') then + ioutk = 1 + elseif(ficarg(ii)(1:lfa).eq.'-M') then + ioutm = 1 + elseif(ficarg(ii)(1:lfa).eq.'-P') then + ioutp = 1 + elseif(ficarg(ii)(1:lfa).eq.'-R') then + ioutr = 1 + elseif(ficarg(ii)(1:lfa).eq.'-T') then + ioutt = 1 + elseif(ficarg(ii)(1:lfa).eq.'-W') then + ioutw = 1 + elseif(ficarg(ii)(1:lfa).eq.'-Z') then + iouta = 1 + ioute = 1 + ioutk = 1 + ioutm = 1 + ioutp = 1 + ioutr = 1 + ioutt = 1 + ioutw = 1 + else + write(*,*) 'Invalid parameter ' + do 3 i=1,nfic + write(*,'(a,i2,2a)') 'ficarg(',i,') = ', + 1 ficarg(i)(1:lnblnk(ficarg(i))) + 3 continue + stop + endif + if(ii.lt.nfic) go to 2 + + if(itype.eq.0) then + write(*,*) + $ 'Invalid input: one of -a, -e, -k, -m, -r is REQUIRED' + call mtm_info + stop + endif +c + return +c + 10 continue + write(*,*) 'Parameter ficarg(',ii,') = ',ficarg(ii)(1:lfa), + 1 ' is illegal' + call mtm_info + stop +c + end + + subroutine mtm_info +c +c ...... print out information about fps_pr +c + implicit real*8 (a-h,o-z) + implicit integer*4 (i-n) +c + write(*,'(1h ,a)') + 1 'fmap - Focal Mechanism ASCII Plot File Program' +c + write(*,'(1h ,a)') 'Syntax:' + write(*,'(1h ,7x,a)') 'mtmanip -a m0 strike rake dip |' + write(*,'(1h ,7x,a)') + $ ' -e P N T plngP plngN plngT azP azN azT | ' + write(*,'(1h ,7x,a)') ' -k scale mxx mxy mxz myy myz mzz |' + write(*,'(1h ,7x,a)') ' -m scale mxx mxy mxz myy myz mzz |' + write(*,'(1h ,7x,a)') ' -r scale mrr mrt mrp mtt mtp mpp ' + write(*,'(1h ,7x,a)') + $ ' [-A] [-E] [-K | -M | -R [-T]] [-P] [-W] [-Z]' + write(*,'(1h ,7x,a)') ' [-h]' +c + write(*,'(1h ,7x,a)') 'where input is one of:' + write(*,'(1h ,7x,a)') + 1 '-a Angle Input - m0, strike, rake & dip' + write(*,'(1h ,7x,a)') + 1 '-e Eigenvalue Input - P value, N value, T value' + write(*,'(1h ,7x,a)') + 1 ' P plunge N plunge T plunge' + write(*,'(1h ,7x,a)') + 1 ' P azimuth N azimuth T azimuth' + write(*,'(1h ,7x,a)') + $ '-k MT Input (Kanamori convention) - scale, mxx, ' + write(*,'(1h ,7x,a)') ' mxy, mxz, myy, myz & mzz' + write(*,'(1h ,7x,a)') + $ '-m MT Input (Aki convention) - scale, mxx, ' + write(*,'(1h ,7x,a)') ' mxy, mxz, myy, myz & mzz' + write(*,'(1h ,7x,a)') + $ '-r MT Input (Spherical coordinates) - scale, mrr, ' + write(*,'(1h ,7x,a)') ' mrt, mrp, mtt, mtp & mpp' + write(*,'(1h ,7x,a)') + $ ' where r -> radial, t -> theta, p -> phi components' + write(*,'(1h ,7x,a)') + write(*,'(1h ,7x,a)') 'and output is one or more of:' + write(*,'(1h ,7x,a)') + $ '-A Angle Output - m0, strike1, rake1, dip1, strike2, rake2 + $ , dip2' + write(*,'(1h ,7x,a)')'-E Eigenvalue, Azimuth and Plunge output' + write(*,'(1h ,7x,a)') + $ '-K MT Output (Kanamori convention) - m0, mxx, ' + write(*,'(1h ,7x,a)') ' mxy, mxz, myy, myz & mzz' + write(*,'(1h ,7x,a)') + $ '-M MT Output (Aki convention) - m0, mxx, ' + write(*,'(1h ,7x,a)') ' mxy, mxz, myy, myz & mzz' + write(*,'(1h ,7x,a)') + $ '-R MT Output (Spherical coordinates) - m0, mrr, ' + write(*,'(1h ,7x,a)') ' mrt, mrp, mtt, mtp & mpp' + write(*,'(1h ,7x,a)') + $ '-T split any of tensor outputs into double couple, CLVD' + write(*,'(1h ,7x,a)') ' and isotropic tensors' + write(*,'(1h ,7x,a)') + $ '-P percent double couple, CLVD and isotropic' + write(*,'(1h ,7x,a)') '-W Mw output' + write(*,'(1h ,7x,a)') '-Z the whole enchilada (all outputs)' + write(*,'(1h ,7x,a)') + 1 '-h Help - prints this help message' +c +c xxx1xxxxxxxxx2xxxxxxxxx3xxxxxxxxx4xxxxxxxxx5xxxxxxxxx6xxxxxxxxx7xx +c + write(*,'(1h ,a)') 'Notes:' + write(*,'(1h ,7x,a)') 'The scalar moment (m0) is given in ' + write(*,'(1h ,7x,a)') ' dyne-cm and the strike, rake' + write(*,'(1h ,7x,a)') ' and dip are given in degrees.' + write(*,'(1h ,7x,a)') 'The moment input is given in dyne-cm,' + write(*,'(1h ,7x,a)') ' and the scale factor is a' + write(*,'(1h ,7x,a)') ' power of ten.' +c + return + end + + + + + subroutine epa2mt(d,plunge,azimuth,mt) +c +c Given three eigenvalues, plunges and azimuths, compute moment tensor +c + real*8 d(3),plunge(3),azimuth(3),mt(3,3) +c + integer*4 n,i,j + real*8 v(3,3),work(3,3),diag(3,3) + n = 3 + + call zalp(plunge, azimuth, v) + do 20 i = 1,3 + do 10 j = 1,3 + if (i .eq. j) then + diag(i,j) = d(i) + else + diag(i,j) = 0.0 + endif + 10 continue + 20 continue + + call transpose(v,n) + call mmulquad(v,diag,mt,work,n,n) + + return + end + + subroutine zalp(plunge,azimuth,v) + use trigd +c Compute orthonormal eigenvectors from azimuth and plunge +c Fatal error if vector described by azimuth and plunge are not orthogonal + real*8 plunge(3), azimuth(3), v(3,3),sum(3), tol + integer*4 i + + data tol/1.0d-4/ + + do 10 i = 1, 3 + sum(i) = 0.0d0 + v(3,i) = dsind(plunge(i)) + v(2,i) = dcosd(plunge(i)) * dsind(azimuth(i)) + v(1,i) = dcosd(plunge(i)) * dcosd(azimuth(i)) + 10 continue + do 20 i = 1,3 + sum(1) = sum(1) + v(i,1) * v(i,2) + sum(2) = sum(2) + v(i,2) * v(i,3) + sum(3) = sum(3) + v(i,3) * v(i,1) + 20 continue + do 30 i = 1,3 + if (dabs(sum(i)) .gt. tol) then + write(*,*) 'Plunge/azimuth vectors are not orthogonal' + stop + endif + 30 continue +c + return + end + + + +C****************************************************************** +C +C SUBROUTINE: MMULQUAD +C +C SYNOPSIS: Computes the matrix product C = A'*B*A where +C full storage is used for the symmetric matrix C. +C +C PARAMETERS: +C A MxN double precision matrix +C B MxM double precision matrix +C C NxN double precision matrix +C WORK NxM double precision matrix +C +C****************************************************************** + + + subroutine mmulquad(a,b,c,work,m,n) + integer*4 m,n + real*8 a(m,n),b(m,m),c(n,n),work(n,m) +c +c +c do the multiplications + call mmul2 (a,b,work,n,m,m) + call mmulmmsf (work,a, c, n,m) +c +c finish + return + end + + + +C****************************************************************** +C +C SUBROUTINE: MMUL2 +C +C SYNOPSIS: Computes the matrix product C = A'*B +C +C PARAMETERS: +C A N*M double precision matrix +C B N*P double precision matrix +C C MxP double precision matrix +C M column dimension of A and row dimension of C +C N row dimension of A and row dimension of B +C P column dimension of B and coulmn dimesnion of C +C +C******************************************************************* + + + subroutine mmul2(a,b,c,m,n,p) + integer*4 m,n,p + real*8 a(n,m),b(n,p),c(m,p) +c +C Local Declarations + integer*4 i,j,k + real*8 temp +C +C The main loop + do 30 i=1,m + do 20 j=1,p + temp =0 + do 10 k=1,n + temp = temp+a(k,i)*b(k,j) + 10 continue + c(i,j) = temp + 20 continue + 30 continue +C +C finish + return + end + + +C****************************************************************** +C +C SUBROUTINE: MMULMMSF +C +C SYNOPSIS: Computes the matrix product C = AxB , when A is +C assumed to be symmetric. C is the only parameter that is +C changed upon exit. +C +C PARAMETERS: +C A MxN double precision matrix +C B NxM double precision matrix +C C MxM double precision matrix +C M Row dimension of A and C. Column dimension of B and C. +C N Column dimension of A. Row dimension of B. +C +C****************************************************************** + + + subroutine mmulmmsf(a,b,c,m,n) + integer*4 m,n + real*8 a(m,n),b(n,m),c(m,m) +c +C Local Declarations + integer*4 i,j,k + real*8 temp +c +C The main loop + do 30 i=1,m + do 20 j=i,m + temp =0 + do 10 k=1,n + temp = temp+a(i,k)*b(k,j) + 10 continue + c(i,j) = temp + c(j,i) = temp + 20 continue + 30 continue +c +C finish + return + end + + + subroutine transpose(v,n) + integer*4 n, i, j + real*8 v(n,n), tmp + + if (n .lt. 2) return + + do 20 i = 1, n + do 10 j = i+1, n + tmp = v(i,j) + v(i,j) = v(j,i) + v(j,i) = tmp + 10 continue + 20 continue + return + end + + subroutine findscale(d_mt,iexp,scale,d_miso, d_mdc, d_mclvd) +c Compute a scaling factor that is a power of ten +c Apply the scaling to d_mt + real*8 d_mt(3,3),d_miso(3,3), d_mdc(3,3), d_mclvd(3,3) + real*8 scale, argmax + integer*4 iexp, i, j + + argmax=dabs(d_mt(1,1)) + do 22 i=1,3 + do 21 j=1,3 + if (dabs(d_mt(i,j)) .gt. argmax) + $ argmax = abs(d_mt(i,j)) + 21 continue + 22 continue + + iexp=0 + 23 iexp=iexp+1 + scale = 10.0d0 ** iexp + if (scale .le. argmax) goto 23 + iexp=iexp-1 + scale = 10.0d0 ** iexp + + do 25 i = 1,3 + do 24 j = 1,3 + d_mt(i,j) = d_mt(i,j) / scale + d_miso(i,j) = d_miso(i,j) / scale + d_mdc(i,j) = d_mdc(i,j) / scale + d_mclvd(i,j) = d_mclvd(i,j) / scale + 24 continue + 25 continue + return + end + + + subroutine convent(d_mt,mt,d_miso,r_iso,d_mdc,r_dc,d_mclvd,r_clvd, + $ label,iconv) + real*8 d_mt(3,3),d_miso(3,3),d_mdc(3,3),d_mclvd(3,3) + real*4 mt(6),r_iso(6),r_dc(6),r_clvd(6) + character*3 label(6) + + if (iconv .eq. 1) then ! Kanamori convention + label(1) = 'mxx' + mt(1) = -d_mt(1,1) + r_iso(1) = -d_miso(1,1) + r_dc(1) = -d_mdc(1,1) + r_clvd(1) = -d_mclvd(1,1) + + label(2) = 'mxy' + mt(2) = -d_mt(1,2) + r_iso(2) = -d_miso(1,2) + r_dc(2) = -d_mdc(1,2) + r_clvd(2) = -d_mclvd(1,2) + + label(3) = 'mxz' + mt(3) = -d_mt(1,3) + r_iso(3) = -d_miso(1,3) + r_dc(3) = -d_mdc(1,3) + r_clvd(3) = -d_mclvd(1,3) + + label(4) = 'myy' + mt(4) = -d_mt(2,2) + r_iso(4) = -d_miso(2,2) + r_dc(4) = -d_mdc(2,2) + r_clvd(4) = -d_mclvd(2,2) + + label(5) = 'myz' + mt(5) = -d_mt(2,3) + r_iso(5) = -d_miso(2,3) + r_dc(5) = -d_mdc(2,3) + r_clvd(5) = -d_mclvd(2,3) + + label(6) = 'mzz' + mt(6) = -d_mt(3,3) + r_iso(6) = -d_miso(3,3) + r_dc(6) = -d_mdc(3,3) + r_clvd(6) = -d_mclvd(3,3) + elseif (iconv .eq. 2) then ! Aki convention + label(1) = 'mxx' + mt(1) = d_mt(1,1) + r_iso(1) = d_miso(1,1) + r_dc(1) = d_mdc(1,1) + r_clvd(1) = d_mclvd(1,1) + label(2) = 'mxy' + mt(2) = d_mt(1,2) + r_iso(2) = d_miso(1,2) + r_dc(2) = d_mdc(1,2) + r_clvd(2) = d_mclvd(1,2) + label(3) = 'mxz' + mt(3) = d_mt(1,3) + r_iso(3) = d_miso(1,3) + r_dc(3) = d_mdc(1,3) + r_clvd(3) = d_mclvd(1,3) + label(4) = 'myy' + mt(4) = d_mt(2,2) + r_iso(4) = d_miso(2,2) + r_dc(4) = d_mdc(2,2) + r_clvd(4) = d_mclvd(2,2) + label(5) = 'myz' + mt(5) = d_mt(2,3) + r_iso(5) = d_miso(2,3) + r_dc(5) = d_mdc(2,3) + r_clvd(5) = d_mclvd(2,3) + label(6) = 'mzz' + mt(6) = d_mt(3,3) + r_iso(6) = d_miso(3,3) + r_dc(6) = d_mdc(3,3) + r_clvd(6) = d_mclvd(3,3) + elseif (iconv .eq. 3) then ! Spherical coordinates + label(1) = 'mrr' + mt(1) = d_mt(3,3) + r_iso(1) = d_miso(3,3) + r_dc(1) = d_mdc(3,3) + r_clvd(1) = d_mclvd(3,3) + label(2) = 'mrt' + mt(2) = d_mt(1,3) + r_iso(2) = d_miso(1,3) + r_dc(2) = d_mdc(1,3) + r_clvd(2) = d_mclvd(1,3) + label(3) = 'mrp' + mt(3) = -d_mt(2,3) + r_iso(3) = -d_miso(2,3) + r_dc(3) = -d_mdc(2,3) + r_clvd(3) = -d_mclvd(2,3) + label(4) = 'mtt' + mt(4) = d_mt(1,1) + r_iso(4) = d_miso(1,1) + r_dc(4) = d_mdc(1,1) + r_clvd(4) = d_mclvd(1,1) + label(5) = 'mtp' + mt(5) = -d_mt(1,2) + r_iso(5) = -d_miso(1,2) + r_dc(5) = -d_mdc(1,2) + r_clvd(5) = -d_mclvd(1,2) + label(6) = 'mpp' + mt(6) = d_mt(2,2) + r_iso(6) = d_miso(2,2) + r_dc(6) = d_mdc(2,2) + r_clvd(6) = d_mclvd(2,2) + endif + return + end + + subroutine prmt(mt,r_iso,r_dc,r_clvd,label,ioutt,iexp,conv) + real*4 mt(6),r_iso(6),r_dc(6),r_clvd(6) + character*3 label(6) + character*(*) conv + + write(6,'(1h ,a,i2,a,a)') 'Moment Tensor: Scale = 10**', + 1 iexp,' Dyne-cm ', conv + if (ioutt .eq. 1) then + write(6,'(1h ,a)') + $ ' Component iso dc clvd total' + else + write(6,'(1h ,a)') ' Component Value' + endif + do 100 i = 1,6 + if (ioutt .eq. 1) then + write(6,'(1h ,6x,a,4(2x,f7.3))') label(i),r_iso(i), + $ r_dc(i),r_clvd(i),mt(i) + else + write(6,'(1h ,6x,a,5x,f7.3)') label(i),mt(i) + endif + 100 continue + write(6,'(1h )') + + + return + end diff --git a/TDMT_GMT/mtmanip_new.o b/TDMT_GMT/mtmanip_new.o new file mode 100644 index 0000000..464059a Binary files /dev/null and b/TDMT_GMT/mtmanip_new.o differ diff --git a/TDMT_GMT/readhelm.c b/TDMT_GMT/readhelm.c new file mode 100755 index 0000000..218dc9e --- /dev/null +++ b/TDMT_GMT/readhelm.c @@ -0,0 +1,235 @@ +/* Routine to read Helmberger Format Seismograms and to return */ +/* information about number of traces, number of time points, */ +/* dt, and the data vector */ +#include +#include +#include +#include +#include + +int chkform(); + +#define FORM1a "%8d\n" +#define FORM1b "%8d %s\n" +#define FORM2 "%s\n" +#define FORM3 " %11.4e %11.4e 0 0 0.00\n" +#define FORM4a "%8d %8.5f %11.4e\n" +#define TRUE 1 +#define FALSE 0 + +char form[32] = "(6e12.5)"; +FILE *fopen(),*inf,*of; +int fclose(); + +void readhelm(in,N,NT,DT,vec1) +char *in; +int *N, *NT; +float *DT; +float *vec1; +{ + int nt, orignt, nx,nxx, perline, i, j, cnt, left, f_width, ss, I; + int nn, ntemp, test=0; + float BA, dt, mul=0.0, *pv, *vec, *p, dd, tt, mxtt=0.0; + float pi=3.14159265; + char c_form[512],c_form1[128],c_form2[128]; + char line[128]; + char out[50]; + + inf=fopen(in,"r"); + + /* read initial header */ + fgets(line,100,inf); + sscanf(line,"%d",&nxx); + *N=nxx; /*send number of traces*/ + fgets(form,100,inf); + perline = chkform(form,c_form1,c_form2,&f_width); + + + nx=nxx; + + for(I=0;I +#include +#include +#include +#include +#include + +void setpar(); +int getpar(); +int mstpar(); +void endpar(); + +FILE *fopen(), *fd4; + +/*convert SAC binary to headerless binary file (fromHelm.c generated) */ +/*or Helmberger ascii */ +int main(ac,av) + int ac; + char **av; + { + int i, npts, ihd[40],fd1, fd2, fd3; + float dt, fhd[70], *tr; + char chd[8][24], out[100], line[100], line1[100], line2[100]; + +setpar(ac,av); +mstpar("out","s",out); +endpar(); + +fd1=open("tmp1",O_RDONLY,0644); +fd2=open("tmp2",O_RDONLY,0644); +fd3=open("tmp3",O_RDONLY,0644); +fd4=fopen(out,"w"); + + +read(fd1,fhd,70*4); /*Read Sac Float Field*/ +read(fd1,ihd,40*4); /*Read Sac Int Field*/ +read(fd1,chd,24*8); /*Read Sac Char. Field*/ +npts=ihd[9]; +dt=fhd[0]; +read(fd2,fhd,70*4); /*Read Sac Float Field*/ +read(fd2,ihd,40*4); /*Read Sac Int Field*/ +read(fd2,chd,24*8); /*Read Sac Char. Field*/ +if(ihd[9] != npts) + { + fprintf(stderr,"SAC2HELM ERROR npts not equal\n"); + exit(-1); + } +if(fhd[0] != dt) + { + fprintf(stderr,"SAC2HELM ERROR dt not equal\n"); + exit(-1); + } +read(fd3,fhd,70*4); /*Read Sac Float Field*/ +read(fd3,ihd,40*4); /*Read Sac Int Field*/ +read(fd3,chd,24*8); /*Read Sac Char. Field*/ +if(ihd[9] != npts) + { + fprintf(stderr,"SAC2HELM ERROR npts not equal\n"); + exit(-1); + } +if(fhd[0] != dt) + { + fprintf(stderr,"SAC2HELM ERROR dt not equal\n"); + exit(-1); + } +fprintf(stderr,"npts=%d dt=%f\n",npts,dt); + +sprintf(line1," 0.0000e+00 0.0000e+00 0 0 0.00"); +sprintf(line2,"%8d %8.5f %11.4e",npts,dt,0.0); + +tr=(float *)malloc(sizeof(float)*npts); +read(fd1,tr,npts*sizeof(float)); +fprintf(fd4," 3\n"); +fprintf(fd4,"(7e14.5)\n"); +fprintf(fd4,"%s\n",line1); +fprintf(fd4,"%s\n",line2); +for(i=1; i <= npts; i++) + if(i % 7 == 0 && i != 1 && i != npts) + fprintf(fd4," %12.5e\n",tr[i-1]); + else + fprintf(fd4," %12.5e",tr[i-1]); + +read(fd2,tr,npts*sizeof(float)); +fprintf(fd4,"\n%s\n",line1); +fprintf(fd4,"%s\n",line2); +for(i=1; i <= npts; i++) + if(i % 7 == 0 && i != 1 && i != npts) + fprintf(fd4," %12.5e\n",tr[i-1]); + else + fprintf(fd4," %12.5e",tr[i-1]); + +read(fd3,tr,npts*sizeof(float)); +fprintf(fd4,"\n%s\n",line1); +fprintf(fd4,"%s\n",line2); +for(i=1; i <= npts; i++) + if(i % 7 == 0 && i != 1 && i != npts) + fprintf(fd4," %12.5e\n",tr[i-1]); + else + fprintf(fd4," %12.5e",tr[i-1]); + + +} diff --git a/TDMT_GMT/sac2helm.o b/TDMT_GMT/sac2helm.o new file mode 100644 index 0000000..192550b Binary files /dev/null and b/TDMT_GMT/sac2helm.o differ diff --git a/TDMT_GMT/tdmt_invc_iso.c b/TDMT_GMT/tdmt_invc_iso.c new file mode 100755 index 0000000..2377127 --- /dev/null +++ b/TDMT_GMT/tdmt_invc_iso.c @@ -0,0 +1,444 @@ +/*Copyright (c) Douglas Dreger +Berkeley Seismological Laboratory +University of California, Berkeley */ +/* Moment Tensor Inversion */ +/* Uses previously determined Green's functions in D. Helmbergers format*/ + +#include +#include"tdmtinv_iso.h" + +void gaussj(); +int correlate(),fclose(); +void readhelm(); +void minvdbl(); +void m0dcf_(); +void fitcheck(); +int mt_dat(); + +FILE *fopen(), *par,*fd, *fd1, *fd2, *fd3, *out; + + +int main() +{ + int i,j,l,N,Np,Z,k,ntr,nsta,nn,*npts = NULL ,wflag=0; + int depth,cnt1, cnt2, cnt3, np2,QUAL,plotflag,WCNT; + float *W, *tmpp,*tmp,*dt, cormax, mindist, dist,E,VR,junk; + float tracelang; + float eigen1, eigen2, eigen3; + float eigaz1, eigaz2, eigaz3; + float eigpl1, eigpl2, eigpl3; + double **AIV,**AJ, **B; + struct MOMENT M; + struct DATA *ss; + struct GREEN *gg; + char infile[200],binfile[205],datfile[200]; + /**Variables needed for implementation of R Urhammer's MT decomposition**/ + /**Algorithms**/ + char d_axis[3]; + int d_mtrep; + double d_mf[6],d_mt[3][3],d_d[3],d_v[3][3],d_miso[3][3],d_mdc[3][3], + d_mclvd[3][3],d_m0,d_mw,d_plunge[3],d_azimuth[3],d_strike[3], + d_slip[3],d_dip[3],d_pcdc,d_pcclvd,d_pciso; + float gfscale=1.0e+20; /*Dyne cm*/ + /**************/ + float fullMo, Mo, isoMo, Mw, Strike, Rake, Dip, Pdc, Piso, Pclvd,F; + float St2, Rk2, Dp2; + int isoflag; + + if ( (par=fopen(MT_INFILE,"r")) == NULL) { + fprintf(stderr, "Error opening %s: %s\n", MT_INFILE, strerror(errno)); + exit(1); + } + + if ( (out=fopen(MT_OUTFILE,"a")) == NULL) { + fprintf(stderr, "Error opening %s: %s\n", MT_OUTFILE, strerror(errno)); + exit(1); + } + + fscanf(par,"%d %d %d %d %d\n",&nsta,&depth,&wflag,&isoflag,&plotflag); + M.isoflag=isoflag; + fprintf(out,"isoflag=%d Depth=%d\n",isoflag,depth); +#ifndef ONLY_ERRORS_TO_STDERR + fprintf(stderr,"isoflag=%d Depth=%d\n",isoflag,depth); +#endif + + AIV=(double **)malloc(sizeof(double *)*isoflag); + for(i=0 ; i < isoflag ; i++) + AIV[i]=(double *)malloc(sizeof(double)*isoflag); + + B=(double **)malloc(sizeof(double *)*isoflag); + for(i=0 ; i < isoflag; i++) + B[i]=(double *)malloc(sizeof(double)*1); + + ss=(struct DATA *)malloc(sizeof(struct DATA)*(nsta)); + gg=(struct GREEN *)malloc(sizeof(struct GREEN)*(nsta)); + + tmp=(float *)malloc(sizeof(float)*10*10000); + npts=(int *)malloc(sizeof(int)*10); + dt =(float *)malloc(sizeof(float)*10); + + + mindist=100000.0; + WCNT=0; + ntr = 0; + for(i=0 ; i < nsta ; i++) + { + fscanf(par,"%s %f %f %d %d\n",infile,&dist,&(ss[i].azi),&Z,&Np); + WCNT += Np; + strcpy(ss[i].name,infile); + ss[i].azi *= PI/180.0; + + /* Read in seismic trace data */ + readhelm(infile,&ntr,npts,dt,tmp); + if (ntr != 3) { + fprintf(stderr, "read %d traces from %s; 3 required\n",ntr,infile); + exit(1); + } + /* We assume all the traces have the same time step and length; + That's why we have tdmt_redi_sched, to do this checking */ + nn=npts[0]; + + if(mindist > dist) mindist = dist; + ss[i].dist=dist; + ss[i].np=nn; + ss[i].dt=dt[0]; + ss[i].nn=Np; + ss[i].zz=Z; + + /*Allocate data structure arrays*/ + ss[i].t=(float *)malloc(sizeof(float)*nn); + ss[i].r=(float *)malloc(sizeof(float)*nn); + ss[i].z=(float *)malloc(sizeof(float)*nn); + + for(j=0 ; j < nn ; j++) + { + ss[i].t[j]=tmp[j]; + ss[i].r[j]=tmp[j + nn]; + ss[i].z[j]=tmp[j + 2*nn]; + } + } + + + /*Allocate Memory for Station Weights*/ + WCNT *= 3; + W=(float *)malloc(sizeof(float)*WCNT); + for(j=0; j < WCNT ; j++) + W[j]=1.0; + + /*Allocate Memory for A matrix*/ + AJ=(double **)malloc(sizeof(double *)*isoflag); + for(j=0 ; j < isoflag ; j++) + AJ[j]=(double *)malloc(sizeof(double)*WCNT); + + /* Read in the Green's functions */ + ntr = 0; + for(i=0 ; i < nsta ; i++) { + fscanf(par,"%s %d %d\n",infile,&Z,&Np); + /* Pete's binary read + sprintf(binfile, "%s.bin", infile); + if (access(binfile, R_OK) == 0) + readbin(binfile, &ntr, &npts, &dt, &tmp); + else + */ + readhelm(infile,&ntr,npts,dt,tmp); + + if ( (isoflag == 5 && ntr < 8) || isoflag == 6 && ntr != 10) { + fprintf(stderr, "GF file %s has unexpected sector count: %d\n", + infile, ntr); + exit(1); + } + + nn=npts[0]; + + gg[i].np=nn; + gg[i].dt=dt[0]; + gg[i].nn=Np; + gg[i].zz=Z; + + /*Allocate Greens Function structure arrays*/ + gg[i].u1=(float *)malloc(sizeof(float)*nn); + gg[i].u2=(float *)malloc(sizeof(float)*nn); + gg[i].u3=(float *)malloc(sizeof(float)*nn); + gg[i].u4=(float *)malloc(sizeof(float)*nn); + gg[i].u5=(float *)malloc(sizeof(float)*nn); + gg[i].u6=(float *)malloc(sizeof(float)*nn); + gg[i].u7=(float *)malloc(sizeof(float)*nn); + gg[i].u8=(float *)malloc(sizeof(float)*nn); + gg[i].u9=(float *)malloc(sizeof(float)*nn); + gg[i].u10=(float *)malloc(sizeof(float)*nn); + + for(j=0 ; j < nn ; j++) { + gg[i].u1[j]=tmp[j]; + gg[i].u2[j]=tmp[j + nn]; + gg[i].u3[j]=tmp[j + 2*nn]; + gg[i].u4[j]=tmp[j + 3*nn]; + gg[i].u5[j]=tmp[j + 4*nn]; + gg[i].u6[j]=tmp[j + 5*nn] * (-1.0);/*Note the vertical GF's are*/ + gg[i].u7[j]=tmp[j + 6*nn] * (-1.0);/*flipped in earqt1.f and TW's*/ + gg[i].u8[j]=tmp[j + 7*nn] * (-1.0);/* Blackbox.f DVH conv. z + down*/ + if(isoflag==6) { + gg[i].u9[j]=tmp[j + 8*nn] * (-1.0); /*Radial exp*/ + gg[i].u10[j]=tmp[j + 9*nn] * (-1.0); /*Vertical exp*/ + } + if(isoflag==5) { + gg[i].u9[j]=0.0; + gg[i].u10[j]=0.0; + } + } + } + + /* Cross-Correlation to obtain zero shift*/ + for(i=0; i < nsta; i++) { + N =ss[i].np; + Np=ss[i].nn; + Z =ss[i].zz; + np2 = (int)(log10((float)N)/log10(2.0) + 0.5); + np2=2<<(np2-1); + if(Z == 0) + ss[i].zz=correlate(ss,gg,i,np2);/*Compute cross-correlation*/ + /*Return zshift for largest cor.*/ + } + + /*Construct distance 1/R weighting*/ + if(wflag==1 && (nsta >= 1)) { +#ifndef ONLY_ERRORS_TO_STDERR + fprintf(stderr,"Station Information\n"); +#endif + fprintf(out,"Station Information\n"); + l=0; + for(i=0; i < nsta; i++) { + cormax = ss[i].dist / mindist; +#ifndef ONLY_ERRORS_TO_STDERR + fprintf(stderr,"Station(%d): %s R=%.1fkm AZI=%.1f W=%.3f Zcor=%d\n", i, ss[i].name,ss[i].dist,ss[i].azi*180.0/PI,cormax,ss[i].zz); +#endif + fprintf(out,"Station(%d): %s R=%.1fkm AZI=%.1f W=%.3f Zcor=%d\n", i, ss[i].name,ss[i].dist,ss[i].azi*180.0/PI,cormax,ss[i].zz); + N = ss[i].nn; + for(j=0; j < 3*N; j++) + W[l++]=cormax; + } + } + + /* INVERSION ROUTINE */ + + for(i=0 ; i < isoflag ; i++) /*Normalize AtA and AIV matix*/ + for(l=0 ; l < isoflag ; l++) + AIV[i][l]=0.0; + + for(i=0 ; i < isoflag ; i++) + B[i][0]=0.0; + + cnt1=cnt2=cnt3=0; + for(i=0; i < nsta; i++) { + Np=ss[i].nn; + Z =gg[i].zz; + cnt1=cnt2 = cnt3; + cnt2 += Np; + cnt3 += 2*Np; + for(j=Z; j < Z+Np; j++) { /*Index over time*/ + /*Mxx term*/ + AJ[0][cnt1] = (double)(0.5*sin(2*ss[i].azi)*gg[i].u1[j]); + if(isoflag==6) + { + AJ[0][cnt2] = (double)(0.166667*gg[i].u5[j] - 0.5*cos(2*ss[i].azi)*gg[i].u3[j] + 0.33333*gg[i].u9[j]); + AJ[0][cnt3] = (double)(0.166667*gg[i].u8[j] - 0.5*cos(2*ss[i].azi)*gg[i].u6[j] + 0.33333*gg[i].u10[j]); + } + if(isoflag==5) + { + AJ[0][cnt2] = (double)(0.500000*gg[i].u5[j] - 0.5*cos(2*ss[i].azi)*gg[i].u3[j]); + AJ[0][cnt3] = (double)(0.500000*gg[i].u8[j] - 0.5*cos(2*ss[i].azi)*gg[i].u6[j]); + } + /*Myy term*/ + AJ[1][cnt1] = (double)((-0.5)*sin(2*ss[i].azi)*gg[i].u1[j]); + if(isoflag==6) + { + AJ[1][cnt2] = (double)(0.166667*gg[i].u5[j] + 0.5*cos(2*ss[i].azi)*gg[i].u3[j] + 0.33333*gg[i].u9[j]); + AJ[1][cnt3] = (double)(0.166667*gg[i].u8[j] + 0.5*cos(2*ss[i].azi)*gg[i].u6[j] + 0.33333*gg[i].u10[j]); + } + if(isoflag==5) + { + AJ[1][cnt2] = (double)(0.500000*gg[i].u5[j] + 0.5*cos(2*ss[i].azi)*gg[i].u3[j]); + AJ[1][cnt3] = (double)(0.500000*gg[i].u8[j] + 0.5*cos(2*ss[i].azi)*gg[i].u6[j]); + } + + /*Mxy term*/ + AJ[2][cnt1] = (double)((-1.0)*cos(2*ss[i].azi)*gg[i].u1[j]); + AJ[2][cnt2] = (double)((-1.0)*sin(2*ss[i].azi)*gg[i].u3[j]); + AJ[2][cnt3] = (double)((-1.0)*sin(2*ss[i].azi)*gg[i].u6[j]); + + /*Mxz term*/ + AJ[3][cnt1] = (double)((-1.0)*sin(ss[i].azi)*gg[i].u2[j]); + AJ[3][cnt2] = (double)( cos(ss[i].azi)*gg[i].u4[j]); + AJ[3][cnt3] = (double)( cos(ss[i].azi)*gg[i].u7[j]); + + /*Myz term*/ + AJ[4][cnt1] = (double)( cos(ss[i].azi)*gg[i].u2[j]); + AJ[4][cnt2] = (double)( sin(ss[i].azi)*gg[i].u4[j]); + AJ[4][cnt3] = (double)( sin(ss[i].azi)*gg[i].u7[j]); + + /*Mzz term*/ + if(isoflag==6) + { + AJ[5][cnt1] = (double)(0.0); + AJ[5][cnt2] = (double)(0.33333*gg[i].u9[j]-0.33333*gg[i].u5[j]); + AJ[5][cnt3] = (double)(0.33333*gg[i].u10[j]-0.3333*gg[i].u8[j]); + } + + + cnt1++; + cnt2++; + cnt3++; + } + } + + + for(i=0 ; i < isoflag ; i++) /*Compute AtA */ + for(j=0 ; j < isoflag ; j++) + for(k=0 ; k < cnt3 ; k++) + AIV[i][j] += AJ[i][k]* AJ[j][k] * (double)W[k]; + + + cnt1=cnt2=cnt3=0; + tmp=(float *)malloc(10*N*nn*sizeof(float)); + for(j=0; j < nsta; j++) { + l=0; + Z =ss[j].zz; + Np=ss[j].nn; + cnt1=cnt2 = cnt3; + cnt2 += Np; + cnt3 += 2*Np; + for(i=Z ; i < Np+Z ; i++) { + tmp[cnt1] = ss[j].t[i]; + tmp[cnt2] = ss[j].r[i]; + tmp[cnt3] = ss[j].z[i]; + cnt1++; + cnt2++; + cnt3++; + } + } + + for(i=0 ; i < isoflag ; i++) /* Calculate Righthand Side */ + for(j=0 ; j < cnt3 ; j++) + B[i][0] += AJ[i][j] * (double)tmp[j] * (double)W[j]; + + + minvdbl(AIV,B,isoflag,1); /* Determine Solution Vector */ + + M.mxx=(float)B[0][0]; + M.myy=(float)B[1][0]; + M.mxy=(float)B[2][0]; + M.mxz=(float)B[3][0]; + M.myz=(float)B[4][0]; +if(isoflag==6) + M.mzz=(float)B[5][0]; +if(isoflag==5) + M.mzz=-1.0*(M.mxx + M.myy); + + /*Call Bob's MT decomposition routines*/ + /*The minus one is needed to map Helmbergers convention into Aki's*/ + /*Jost and Hermann (1989) state that AKI's convention is -1*LANGSTONS*/ + +tracelang=(M.mxx+M.myy+M.mzz)/3; /*Trace of MT*/ + + d_mtrep=1; +/*Convert deviatoric moment tensor to AKI convention*/ +d_mt[0][0] = (double)(-1.0*gfscale*M.mxx); +d_mt[0][1] = (double)(-1.0*gfscale*M.mxy); +d_mt[0][2] = (double)(-1.0*gfscale*M.mxz); +d_mt[1][0] = (double)(-1.0*gfscale*M.mxy); +d_mt[1][1] = (double)(-1.0*gfscale*M.myy); +d_mt[1][2] = (double)(-1.0*gfscale*M.myz); +d_mt[2][0] = (double)(-1.0*gfscale*M.mxz); +d_mt[2][1] = (double)(-1.0*gfscale*M.myz); +d_mt[2][2] = (double)(-1.0*gfscale*M.mzz); + +fprintf(out,"isomoment=%g\n",tracelang*gfscale); + + + fprintf(out,"MT Aki Convention\n"); + fprintf(out,"Mxx=%.3f\nMxy=%.3f\nMxz=%.3f\nMyy=%.3f\nMyz=%.3f\nMzz=%.3f\n", + d_mt[0][0]/gfscale,d_mt[0][1]/gfscale,d_mt[0][2]/gfscale,d_mt[1][1]/gfscale, + d_mt[1][2]/gfscale,d_mt[2][2]/gfscale); + + m0dcf_(&d_mtrep,d_mf,d_mt,d_d,d_v,d_miso,d_mdc,d_mclvd, + &d_m0,&d_mw,d_axis,d_plunge,d_azimuth,d_strike,d_dip, + d_slip,&d_pcdc,&d_pcclvd,&d_pciso); + + eigen1=(float)d_d[0]; + eigen2=(float)d_d[1]; + eigen3=(float)d_d[2]; +fprintf(out,"eigen1=%g\n",eigen1); +fprintf(out,"eigen2=%g\n",eigen2); +fprintf(out,"eigen3=%g\n",eigen3); + eigaz1=(float)d_azimuth[0]; + eigaz2=(float)d_azimuth[1]; + eigaz3=(float)d_azimuth[2]; + eigpl1=(float)d_plunge[0]; + eigpl2=(float)d_plunge[1]; + eigpl3=(float)d_plunge[2]; + + Mo = (float) d_m0; + Mw = (float) d_mw; + Strike = (float) d_strike[0]; + Rake = (float) d_slip[0]; + Dip = (float) d_dip[0]; + St2 = (float) d_strike[1]; + Rk2 = (float) d_slip[1]; + Dp2 = (float) d_dip[1]; + Pdc = (float) d_pcdc; + Pclvd = (float) d_pcclvd; + Piso = (float) d_pciso; + isoMo = tracelang*gfscale; + fullMo = fabs(isoMo) + fabs(eigen3); + if(isoflag == 6) + Mw = (log10(fullMo)-16.05)*2/3; + +/*Code to Convert Percentages to those based on Bowers moment*/ +/*Fortran subroutines compute Dziewonski moment + isotropic*/ +Piso=fabs(isoMo)/fullMo*100; +F=-eigen1/eigen3; +Pdc=(fabs(eigen3)/fullMo)*fabs(1-2*F)*100; +Pclvd=(fabs(eigen3)/fullMo)*fabs(F)*200; +/*End Percent Decomposition Modification 01122016*/ + +#ifndef ONLY_ERRORS_TO_STDERR + fprintf(stderr,"isoMo: %g\n",isoMo); + fprintf(stderr,"Mo=%g (%g)\nMw=%.2f\nStrike=%.0f ; %.0f\nRake=%.0f ; %.0f\nDip=%.0f; %.0f\nPdc=%.0f\nPclvd=%.0f\nPiso=%.0f\n", + Mo,fullMo,Mw,Strike,St2,Rake,Rk2,Dip,Dp2,Pdc,Pclvd,Piso); +#endif + fprintf(out,"Mo=%g (%g)\nMw=%.2f\nStrike=%.0f ; %.0f\nRake=%.0f ; %.0f\nDip=%.0f ; %.0f\nPdc=%.0f\nPclvd=%.0f\nPiso=%.0f\n", + Mo,fullMo,Mw,Strike,St2,Rake,Rk2,Dip,Dp2,Pdc,Pclvd,Piso); + + fitcheck(ss,gg,W,M,Mo,nsta,isoflag,&E,&VR); /*Function to compute vr and flag bad stations*/ + +#ifdef TDMT_PLOT + if(plotflag==1) + mt_plot(ss,gg,nsta,Strike,Rake,Dip,St2,Rk2,Dp2,M,d_mt,Pdc,Pclvd,Piso,Mo,Mw,E,VR); +#endif + + fprintf(out,"Variance=%.3e\n",E); + fprintf(out,"VarRed=%.3e\n",VR); +#ifndef ONLY_ERRORS_TO_STDERR + fprintf(stderr,"Var/Pdc=%.3e\n",E/Pdc); +#endif + fprintf(out,"Var/Pdc=%.3e\n",E/Pdc); + + if(VR < 20.0) QUAL=0; + if(VR > 20.0 && VR < 40.0) QUAL=1; + if(VR > 40.0 && VR < 60.0) QUAL=2; + if(VR > 60.0 && VR < 80.0) QUAL=3; + if(VR > 80.0) QUAL=4; + fprintf(out,"Quality=%d\n",QUAL); +#ifndef ONLY_ERRORS_TO_STDERR + fprintf(stderr,"Quality=%d\n",QUAL); +#endif + + if (fscanf(par, "%s\n", datfile) == 1) { + mt_dat(ss,gg,nsta,Strike,Rake,Dip,St2,Rk2,Dp2,M,-gfscale,Pdc,Pclvd,Piso,Mo,Mw,E,VR,depth,datfile); + } + fclose(par); + fclose(out); + + return 0; +} + + diff --git a/TDMT_GMT/tdmt_invc_iso.o b/TDMT_GMT/tdmt_invc_iso.o new file mode 100644 index 0000000..36f50ea Binary files /dev/null and b/TDMT_GMT/tdmt_invc_iso.o differ diff --git a/TDMT_GMT/tdmt_invc_iso_frobius.c b/TDMT_GMT/tdmt_invc_iso_frobius.c new file mode 100755 index 0000000..2377127 --- /dev/null +++ b/TDMT_GMT/tdmt_invc_iso_frobius.c @@ -0,0 +1,444 @@ +/*Copyright (c) Douglas Dreger +Berkeley Seismological Laboratory +University of California, Berkeley */ +/* Moment Tensor Inversion */ +/* Uses previously determined Green's functions in D. Helmbergers format*/ + +#include +#include"tdmtinv_iso.h" + +void gaussj(); +int correlate(),fclose(); +void readhelm(); +void minvdbl(); +void m0dcf_(); +void fitcheck(); +int mt_dat(); + +FILE *fopen(), *par,*fd, *fd1, *fd2, *fd3, *out; + + +int main() +{ + int i,j,l,N,Np,Z,k,ntr,nsta,nn,*npts = NULL ,wflag=0; + int depth,cnt1, cnt2, cnt3, np2,QUAL,plotflag,WCNT; + float *W, *tmpp,*tmp,*dt, cormax, mindist, dist,E,VR,junk; + float tracelang; + float eigen1, eigen2, eigen3; + float eigaz1, eigaz2, eigaz3; + float eigpl1, eigpl2, eigpl3; + double **AIV,**AJ, **B; + struct MOMENT M; + struct DATA *ss; + struct GREEN *gg; + char infile[200],binfile[205],datfile[200]; + /**Variables needed for implementation of R Urhammer's MT decomposition**/ + /**Algorithms**/ + char d_axis[3]; + int d_mtrep; + double d_mf[6],d_mt[3][3],d_d[3],d_v[3][3],d_miso[3][3],d_mdc[3][3], + d_mclvd[3][3],d_m0,d_mw,d_plunge[3],d_azimuth[3],d_strike[3], + d_slip[3],d_dip[3],d_pcdc,d_pcclvd,d_pciso; + float gfscale=1.0e+20; /*Dyne cm*/ + /**************/ + float fullMo, Mo, isoMo, Mw, Strike, Rake, Dip, Pdc, Piso, Pclvd,F; + float St2, Rk2, Dp2; + int isoflag; + + if ( (par=fopen(MT_INFILE,"r")) == NULL) { + fprintf(stderr, "Error opening %s: %s\n", MT_INFILE, strerror(errno)); + exit(1); + } + + if ( (out=fopen(MT_OUTFILE,"a")) == NULL) { + fprintf(stderr, "Error opening %s: %s\n", MT_OUTFILE, strerror(errno)); + exit(1); + } + + fscanf(par,"%d %d %d %d %d\n",&nsta,&depth,&wflag,&isoflag,&plotflag); + M.isoflag=isoflag; + fprintf(out,"isoflag=%d Depth=%d\n",isoflag,depth); +#ifndef ONLY_ERRORS_TO_STDERR + fprintf(stderr,"isoflag=%d Depth=%d\n",isoflag,depth); +#endif + + AIV=(double **)malloc(sizeof(double *)*isoflag); + for(i=0 ; i < isoflag ; i++) + AIV[i]=(double *)malloc(sizeof(double)*isoflag); + + B=(double **)malloc(sizeof(double *)*isoflag); + for(i=0 ; i < isoflag; i++) + B[i]=(double *)malloc(sizeof(double)*1); + + ss=(struct DATA *)malloc(sizeof(struct DATA)*(nsta)); + gg=(struct GREEN *)malloc(sizeof(struct GREEN)*(nsta)); + + tmp=(float *)malloc(sizeof(float)*10*10000); + npts=(int *)malloc(sizeof(int)*10); + dt =(float *)malloc(sizeof(float)*10); + + + mindist=100000.0; + WCNT=0; + ntr = 0; + for(i=0 ; i < nsta ; i++) + { + fscanf(par,"%s %f %f %d %d\n",infile,&dist,&(ss[i].azi),&Z,&Np); + WCNT += Np; + strcpy(ss[i].name,infile); + ss[i].azi *= PI/180.0; + + /* Read in seismic trace data */ + readhelm(infile,&ntr,npts,dt,tmp); + if (ntr != 3) { + fprintf(stderr, "read %d traces from %s; 3 required\n",ntr,infile); + exit(1); + } + /* We assume all the traces have the same time step and length; + That's why we have tdmt_redi_sched, to do this checking */ + nn=npts[0]; + + if(mindist > dist) mindist = dist; + ss[i].dist=dist; + ss[i].np=nn; + ss[i].dt=dt[0]; + ss[i].nn=Np; + ss[i].zz=Z; + + /*Allocate data structure arrays*/ + ss[i].t=(float *)malloc(sizeof(float)*nn); + ss[i].r=(float *)malloc(sizeof(float)*nn); + ss[i].z=(float *)malloc(sizeof(float)*nn); + + for(j=0 ; j < nn ; j++) + { + ss[i].t[j]=tmp[j]; + ss[i].r[j]=tmp[j + nn]; + ss[i].z[j]=tmp[j + 2*nn]; + } + } + + + /*Allocate Memory for Station Weights*/ + WCNT *= 3; + W=(float *)malloc(sizeof(float)*WCNT); + for(j=0; j < WCNT ; j++) + W[j]=1.0; + + /*Allocate Memory for A matrix*/ + AJ=(double **)malloc(sizeof(double *)*isoflag); + for(j=0 ; j < isoflag ; j++) + AJ[j]=(double *)malloc(sizeof(double)*WCNT); + + /* Read in the Green's functions */ + ntr = 0; + for(i=0 ; i < nsta ; i++) { + fscanf(par,"%s %d %d\n",infile,&Z,&Np); + /* Pete's binary read + sprintf(binfile, "%s.bin", infile); + if (access(binfile, R_OK) == 0) + readbin(binfile, &ntr, &npts, &dt, &tmp); + else + */ + readhelm(infile,&ntr,npts,dt,tmp); + + if ( (isoflag == 5 && ntr < 8) || isoflag == 6 && ntr != 10) { + fprintf(stderr, "GF file %s has unexpected sector count: %d\n", + infile, ntr); + exit(1); + } + + nn=npts[0]; + + gg[i].np=nn; + gg[i].dt=dt[0]; + gg[i].nn=Np; + gg[i].zz=Z; + + /*Allocate Greens Function structure arrays*/ + gg[i].u1=(float *)malloc(sizeof(float)*nn); + gg[i].u2=(float *)malloc(sizeof(float)*nn); + gg[i].u3=(float *)malloc(sizeof(float)*nn); + gg[i].u4=(float *)malloc(sizeof(float)*nn); + gg[i].u5=(float *)malloc(sizeof(float)*nn); + gg[i].u6=(float *)malloc(sizeof(float)*nn); + gg[i].u7=(float *)malloc(sizeof(float)*nn); + gg[i].u8=(float *)malloc(sizeof(float)*nn); + gg[i].u9=(float *)malloc(sizeof(float)*nn); + gg[i].u10=(float *)malloc(sizeof(float)*nn); + + for(j=0 ; j < nn ; j++) { + gg[i].u1[j]=tmp[j]; + gg[i].u2[j]=tmp[j + nn]; + gg[i].u3[j]=tmp[j + 2*nn]; + gg[i].u4[j]=tmp[j + 3*nn]; + gg[i].u5[j]=tmp[j + 4*nn]; + gg[i].u6[j]=tmp[j + 5*nn] * (-1.0);/*Note the vertical GF's are*/ + gg[i].u7[j]=tmp[j + 6*nn] * (-1.0);/*flipped in earqt1.f and TW's*/ + gg[i].u8[j]=tmp[j + 7*nn] * (-1.0);/* Blackbox.f DVH conv. z + down*/ + if(isoflag==6) { + gg[i].u9[j]=tmp[j + 8*nn] * (-1.0); /*Radial exp*/ + gg[i].u10[j]=tmp[j + 9*nn] * (-1.0); /*Vertical exp*/ + } + if(isoflag==5) { + gg[i].u9[j]=0.0; + gg[i].u10[j]=0.0; + } + } + } + + /* Cross-Correlation to obtain zero shift*/ + for(i=0; i < nsta; i++) { + N =ss[i].np; + Np=ss[i].nn; + Z =ss[i].zz; + np2 = (int)(log10((float)N)/log10(2.0) + 0.5); + np2=2<<(np2-1); + if(Z == 0) + ss[i].zz=correlate(ss,gg,i,np2);/*Compute cross-correlation*/ + /*Return zshift for largest cor.*/ + } + + /*Construct distance 1/R weighting*/ + if(wflag==1 && (nsta >= 1)) { +#ifndef ONLY_ERRORS_TO_STDERR + fprintf(stderr,"Station Information\n"); +#endif + fprintf(out,"Station Information\n"); + l=0; + for(i=0; i < nsta; i++) { + cormax = ss[i].dist / mindist; +#ifndef ONLY_ERRORS_TO_STDERR + fprintf(stderr,"Station(%d): %s R=%.1fkm AZI=%.1f W=%.3f Zcor=%d\n", i, ss[i].name,ss[i].dist,ss[i].azi*180.0/PI,cormax,ss[i].zz); +#endif + fprintf(out,"Station(%d): %s R=%.1fkm AZI=%.1f W=%.3f Zcor=%d\n", i, ss[i].name,ss[i].dist,ss[i].azi*180.0/PI,cormax,ss[i].zz); + N = ss[i].nn; + for(j=0; j < 3*N; j++) + W[l++]=cormax; + } + } + + /* INVERSION ROUTINE */ + + for(i=0 ; i < isoflag ; i++) /*Normalize AtA and AIV matix*/ + for(l=0 ; l < isoflag ; l++) + AIV[i][l]=0.0; + + for(i=0 ; i < isoflag ; i++) + B[i][0]=0.0; + + cnt1=cnt2=cnt3=0; + for(i=0; i < nsta; i++) { + Np=ss[i].nn; + Z =gg[i].zz; + cnt1=cnt2 = cnt3; + cnt2 += Np; + cnt3 += 2*Np; + for(j=Z; j < Z+Np; j++) { /*Index over time*/ + /*Mxx term*/ + AJ[0][cnt1] = (double)(0.5*sin(2*ss[i].azi)*gg[i].u1[j]); + if(isoflag==6) + { + AJ[0][cnt2] = (double)(0.166667*gg[i].u5[j] - 0.5*cos(2*ss[i].azi)*gg[i].u3[j] + 0.33333*gg[i].u9[j]); + AJ[0][cnt3] = (double)(0.166667*gg[i].u8[j] - 0.5*cos(2*ss[i].azi)*gg[i].u6[j] + 0.33333*gg[i].u10[j]); + } + if(isoflag==5) + { + AJ[0][cnt2] = (double)(0.500000*gg[i].u5[j] - 0.5*cos(2*ss[i].azi)*gg[i].u3[j]); + AJ[0][cnt3] = (double)(0.500000*gg[i].u8[j] - 0.5*cos(2*ss[i].azi)*gg[i].u6[j]); + } + /*Myy term*/ + AJ[1][cnt1] = (double)((-0.5)*sin(2*ss[i].azi)*gg[i].u1[j]); + if(isoflag==6) + { + AJ[1][cnt2] = (double)(0.166667*gg[i].u5[j] + 0.5*cos(2*ss[i].azi)*gg[i].u3[j] + 0.33333*gg[i].u9[j]); + AJ[1][cnt3] = (double)(0.166667*gg[i].u8[j] + 0.5*cos(2*ss[i].azi)*gg[i].u6[j] + 0.33333*gg[i].u10[j]); + } + if(isoflag==5) + { + AJ[1][cnt2] = (double)(0.500000*gg[i].u5[j] + 0.5*cos(2*ss[i].azi)*gg[i].u3[j]); + AJ[1][cnt3] = (double)(0.500000*gg[i].u8[j] + 0.5*cos(2*ss[i].azi)*gg[i].u6[j]); + } + + /*Mxy term*/ + AJ[2][cnt1] = (double)((-1.0)*cos(2*ss[i].azi)*gg[i].u1[j]); + AJ[2][cnt2] = (double)((-1.0)*sin(2*ss[i].azi)*gg[i].u3[j]); + AJ[2][cnt3] = (double)((-1.0)*sin(2*ss[i].azi)*gg[i].u6[j]); + + /*Mxz term*/ + AJ[3][cnt1] = (double)((-1.0)*sin(ss[i].azi)*gg[i].u2[j]); + AJ[3][cnt2] = (double)( cos(ss[i].azi)*gg[i].u4[j]); + AJ[3][cnt3] = (double)( cos(ss[i].azi)*gg[i].u7[j]); + + /*Myz term*/ + AJ[4][cnt1] = (double)( cos(ss[i].azi)*gg[i].u2[j]); + AJ[4][cnt2] = (double)( sin(ss[i].azi)*gg[i].u4[j]); + AJ[4][cnt3] = (double)( sin(ss[i].azi)*gg[i].u7[j]); + + /*Mzz term*/ + if(isoflag==6) + { + AJ[5][cnt1] = (double)(0.0); + AJ[5][cnt2] = (double)(0.33333*gg[i].u9[j]-0.33333*gg[i].u5[j]); + AJ[5][cnt3] = (double)(0.33333*gg[i].u10[j]-0.3333*gg[i].u8[j]); + } + + + cnt1++; + cnt2++; + cnt3++; + } + } + + + for(i=0 ; i < isoflag ; i++) /*Compute AtA */ + for(j=0 ; j < isoflag ; j++) + for(k=0 ; k < cnt3 ; k++) + AIV[i][j] += AJ[i][k]* AJ[j][k] * (double)W[k]; + + + cnt1=cnt2=cnt3=0; + tmp=(float *)malloc(10*N*nn*sizeof(float)); + for(j=0; j < nsta; j++) { + l=0; + Z =ss[j].zz; + Np=ss[j].nn; + cnt1=cnt2 = cnt3; + cnt2 += Np; + cnt3 += 2*Np; + for(i=Z ; i < Np+Z ; i++) { + tmp[cnt1] = ss[j].t[i]; + tmp[cnt2] = ss[j].r[i]; + tmp[cnt3] = ss[j].z[i]; + cnt1++; + cnt2++; + cnt3++; + } + } + + for(i=0 ; i < isoflag ; i++) /* Calculate Righthand Side */ + for(j=0 ; j < cnt3 ; j++) + B[i][0] += AJ[i][j] * (double)tmp[j] * (double)W[j]; + + + minvdbl(AIV,B,isoflag,1); /* Determine Solution Vector */ + + M.mxx=(float)B[0][0]; + M.myy=(float)B[1][0]; + M.mxy=(float)B[2][0]; + M.mxz=(float)B[3][0]; + M.myz=(float)B[4][0]; +if(isoflag==6) + M.mzz=(float)B[5][0]; +if(isoflag==5) + M.mzz=-1.0*(M.mxx + M.myy); + + /*Call Bob's MT decomposition routines*/ + /*The minus one is needed to map Helmbergers convention into Aki's*/ + /*Jost and Hermann (1989) state that AKI's convention is -1*LANGSTONS*/ + +tracelang=(M.mxx+M.myy+M.mzz)/3; /*Trace of MT*/ + + d_mtrep=1; +/*Convert deviatoric moment tensor to AKI convention*/ +d_mt[0][0] = (double)(-1.0*gfscale*M.mxx); +d_mt[0][1] = (double)(-1.0*gfscale*M.mxy); +d_mt[0][2] = (double)(-1.0*gfscale*M.mxz); +d_mt[1][0] = (double)(-1.0*gfscale*M.mxy); +d_mt[1][1] = (double)(-1.0*gfscale*M.myy); +d_mt[1][2] = (double)(-1.0*gfscale*M.myz); +d_mt[2][0] = (double)(-1.0*gfscale*M.mxz); +d_mt[2][1] = (double)(-1.0*gfscale*M.myz); +d_mt[2][2] = (double)(-1.0*gfscale*M.mzz); + +fprintf(out,"isomoment=%g\n",tracelang*gfscale); + + + fprintf(out,"MT Aki Convention\n"); + fprintf(out,"Mxx=%.3f\nMxy=%.3f\nMxz=%.3f\nMyy=%.3f\nMyz=%.3f\nMzz=%.3f\n", + d_mt[0][0]/gfscale,d_mt[0][1]/gfscale,d_mt[0][2]/gfscale,d_mt[1][1]/gfscale, + d_mt[1][2]/gfscale,d_mt[2][2]/gfscale); + + m0dcf_(&d_mtrep,d_mf,d_mt,d_d,d_v,d_miso,d_mdc,d_mclvd, + &d_m0,&d_mw,d_axis,d_plunge,d_azimuth,d_strike,d_dip, + d_slip,&d_pcdc,&d_pcclvd,&d_pciso); + + eigen1=(float)d_d[0]; + eigen2=(float)d_d[1]; + eigen3=(float)d_d[2]; +fprintf(out,"eigen1=%g\n",eigen1); +fprintf(out,"eigen2=%g\n",eigen2); +fprintf(out,"eigen3=%g\n",eigen3); + eigaz1=(float)d_azimuth[0]; + eigaz2=(float)d_azimuth[1]; + eigaz3=(float)d_azimuth[2]; + eigpl1=(float)d_plunge[0]; + eigpl2=(float)d_plunge[1]; + eigpl3=(float)d_plunge[2]; + + Mo = (float) d_m0; + Mw = (float) d_mw; + Strike = (float) d_strike[0]; + Rake = (float) d_slip[0]; + Dip = (float) d_dip[0]; + St2 = (float) d_strike[1]; + Rk2 = (float) d_slip[1]; + Dp2 = (float) d_dip[1]; + Pdc = (float) d_pcdc; + Pclvd = (float) d_pcclvd; + Piso = (float) d_pciso; + isoMo = tracelang*gfscale; + fullMo = fabs(isoMo) + fabs(eigen3); + if(isoflag == 6) + Mw = (log10(fullMo)-16.05)*2/3; + +/*Code to Convert Percentages to those based on Bowers moment*/ +/*Fortran subroutines compute Dziewonski moment + isotropic*/ +Piso=fabs(isoMo)/fullMo*100; +F=-eigen1/eigen3; +Pdc=(fabs(eigen3)/fullMo)*fabs(1-2*F)*100; +Pclvd=(fabs(eigen3)/fullMo)*fabs(F)*200; +/*End Percent Decomposition Modification 01122016*/ + +#ifndef ONLY_ERRORS_TO_STDERR + fprintf(stderr,"isoMo: %g\n",isoMo); + fprintf(stderr,"Mo=%g (%g)\nMw=%.2f\nStrike=%.0f ; %.0f\nRake=%.0f ; %.0f\nDip=%.0f; %.0f\nPdc=%.0f\nPclvd=%.0f\nPiso=%.0f\n", + Mo,fullMo,Mw,Strike,St2,Rake,Rk2,Dip,Dp2,Pdc,Pclvd,Piso); +#endif + fprintf(out,"Mo=%g (%g)\nMw=%.2f\nStrike=%.0f ; %.0f\nRake=%.0f ; %.0f\nDip=%.0f ; %.0f\nPdc=%.0f\nPclvd=%.0f\nPiso=%.0f\n", + Mo,fullMo,Mw,Strike,St2,Rake,Rk2,Dip,Dp2,Pdc,Pclvd,Piso); + + fitcheck(ss,gg,W,M,Mo,nsta,isoflag,&E,&VR); /*Function to compute vr and flag bad stations*/ + +#ifdef TDMT_PLOT + if(plotflag==1) + mt_plot(ss,gg,nsta,Strike,Rake,Dip,St2,Rk2,Dp2,M,d_mt,Pdc,Pclvd,Piso,Mo,Mw,E,VR); +#endif + + fprintf(out,"Variance=%.3e\n",E); + fprintf(out,"VarRed=%.3e\n",VR); +#ifndef ONLY_ERRORS_TO_STDERR + fprintf(stderr,"Var/Pdc=%.3e\n",E/Pdc); +#endif + fprintf(out,"Var/Pdc=%.3e\n",E/Pdc); + + if(VR < 20.0) QUAL=0; + if(VR > 20.0 && VR < 40.0) QUAL=1; + if(VR > 40.0 && VR < 60.0) QUAL=2; + if(VR > 60.0 && VR < 80.0) QUAL=3; + if(VR > 80.0) QUAL=4; + fprintf(out,"Quality=%d\n",QUAL); +#ifndef ONLY_ERRORS_TO_STDERR + fprintf(stderr,"Quality=%d\n",QUAL); +#endif + + if (fscanf(par, "%s\n", datfile) == 1) { + mt_dat(ss,gg,nsta,Strike,Rake,Dip,St2,Rk2,Dp2,M,-gfscale,Pdc,Pclvd,Piso,Mo,Mw,E,VR,depth,datfile); + } + fclose(par); + fclose(out); + + return 0; +} + + diff --git a/TDMT_GMT/tdmt_invc_iso_fwd.c b/TDMT_GMT/tdmt_invc_iso_fwd.c new file mode 100755 index 0000000..0cf1453 --- /dev/null +++ b/TDMT_GMT/tdmt_invc_iso_fwd.c @@ -0,0 +1,449 @@ +/* Moment Tensor Inversion */ +/* Uses previously determined Green's functions in D. Helmbergers format*/ + +#include +#include"tdmtinv_iso.h" +#define MT_FWDFILE "mt_fwd.in" + +void gaussj(); +int correlate(),fclose(); +void readhelm(); +void minvdbl(); +void m0dcf_(); +void fitcheck(); +int mt_dat(); + +FILE *fopen(), *par, *inp, *fd, *fd1, *fd2, *fd3, *out; + +int main() +{ + int i,j,l,N,Np,Z,k,ntr,nsta,nn,*npts = NULL ,wflag=0; + int depth,cnt1, cnt2, cnt3, np2,QUAL,plotflag,WCNT; + float *W, *tmpp,*tmp,*dt, cormax, mindist, dist,E,VR,junk; + float tracelang; + float eigen1, eigen2, eigen3; + float eigaz1, eigaz2, eigaz3; + float eigpl1, eigpl2, eigpl3; + double **AIV,**AJ, **B; + struct MOMENT M; + struct DATA *ss; + struct GREEN *gg; + char infile[200],binfile[205],datfile[200]; + /**Variables needed for implementation of R Urhammer's MT decomposition**/ + /**Algorithms**/ + char d_axis[3]; + int d_mtrep; + double d_mf[6],d_mt[3][3],d_d[3],d_v[3][3],d_miso[3][3],d_mdc[3][3], + d_mclvd[3][3],d_m0,d_mw,d_plunge[3],d_azimuth[3],d_strike[3], + d_slip[3],d_dip[3],d_pcdc,d_pcclvd,d_pciso; + float gfscale=1.0e+20; /*Dyne cm*/ + /**************/ + float fullMo, Mo, isoMo, Mw, Strike, Rake, Dip, Pdc, Piso, Pclvd,F; + float St2, Rk2, Dp2; + float m11, m12, m13, m22, m23, m33; + int isoflag; + + if ( (par=fopen(MT_INFILE,"r")) == NULL) { + fprintf(stderr, "Error opening %s: %s\n", MT_INFILE, strerror(errno)); + exit(1); + } + + if ( (out=fopen(MT_OUTFILE,"a")) == NULL) { + fprintf(stderr, "Error opening %s: %s\n", MT_OUTFILE, strerror(errno)); + exit(1); + } + + if ( (inp=fopen(MT_FWDFILE,"r")) == NULL) { + fprintf(stderr, "Error opening %s: %s\n", MT_FWDFILE, strerror(errno)); + exit(1); + } + + fscanf(inp,"%f %f %f %f %f %f\n",&m11,&m12,&m13,&m22,&m23,&m33); + fclose(inp); + fscanf(par,"%d %d %d %d %d\n",&nsta,&depth,&wflag,&isoflag,&plotflag); + M.isoflag=isoflag; + fprintf(out,"isoflag=%d Depth=%d\n",isoflag,depth); +#ifndef ONLY_ERRORS_TO_STDERR + fprintf(stderr,"isoflag=%d Depth=%d\n",isoflag,depth); +#endif + + AIV=(double **)malloc(sizeof(double *)*isoflag); + for(i=0 ; i < isoflag ; i++) + AIV[i]=(double *)malloc(sizeof(double)*isoflag); + + B=(double **)malloc(sizeof(double *)*isoflag); + for(i=0 ; i < isoflag; i++) + B[i]=(double *)malloc(sizeof(double)*1); + + ss=(struct DATA *)malloc(sizeof(struct DATA)*(nsta)); + gg=(struct GREEN *)malloc(sizeof(struct GREEN)*(nsta)); + + tmp=(float *)malloc(sizeof(float)*10*10000); + npts=(int *)malloc(sizeof(int)*10); + dt =(float *)malloc(sizeof(float)*10); + + mindist=100000.0; + WCNT=0; + ntr = 0; + for(i=0 ; i < nsta ; i++) + { + fscanf(par,"%s %f %f %d %d\n",infile,&dist,&(ss[i].azi),&Z,&Np); + WCNT += Np; + strcpy(ss[i].name,infile); + ss[i].azi *= PI/180.0; + + /* Read in seismic trace data */ + readhelm(infile,&ntr,npts,dt,tmp); + if (ntr != 3) { + fprintf(stderr, "read %d traces from %s; 3 required\n",ntr,infile); + exit(1); + } + /* We assume all the traces have the same time step and length; + That's why we have tdmt_redi_sched, to do this checking */ + nn=npts[0]; + + if(mindist > dist) mindist = dist; + ss[i].dist=dist; + ss[i].np=nn; + ss[i].dt=dt[0]; + ss[i].nn=Np; + ss[i].zz=Z; + + /*Allocate data structure arrays*/ + ss[i].t=(float *)malloc(sizeof(float)*nn); + ss[i].r=(float *)malloc(sizeof(float)*nn); + ss[i].z=(float *)malloc(sizeof(float)*nn); + + for(j=0 ; j < nn ; j++) + { + ss[i].t[j]=tmp[j]; + ss[i].r[j]=tmp[j + nn]; + ss[i].z[j]=tmp[j + 2*nn]; + } + } + + /*Allocate Memory for Station Weights*/ + WCNT *= 3; + W=(float *)malloc(sizeof(float)*WCNT); + for(j=0; j < WCNT ; j++) + W[j]=1.0; + + /*Allocate Memory for A matrix*/ + AJ=(double **)malloc(sizeof(double *)*isoflag); + for(j=0 ; j < isoflag ; j++) + AJ[j]=(double *)malloc(sizeof(double)*WCNT); + + /* Read in the Green's functions */ + ntr = 0; + for(i=0 ; i < nsta ; i++) { + fscanf(par,"%s %d %d\n",infile,&Z,&Np); + /* Pete's binary read + sprintf(binfile, "%s.bin", infile); + if (access(binfile, R_OK) == 0) + readbin(binfile, &ntr, &npts, &dt, &tmp); + else + */ + readhelm(infile,&ntr,npts,dt,tmp); + + if ( (isoflag == 5 && ntr < 8) || isoflag == 6 && ntr != 10) { + fprintf(stderr, "GF file %s has unexpected sector count: %d\n", + infile, ntr); + exit(1); + } + + nn=npts[0]; + + gg[i].np=nn; + gg[i].dt=dt[0]; + gg[i].nn=Np; + gg[i].zz=Z; + + /*Allocate Greens Function structure arrays*/ + gg[i].u1=(float *)malloc(sizeof(float)*nn); + gg[i].u2=(float *)malloc(sizeof(float)*nn); + gg[i].u3=(float *)malloc(sizeof(float)*nn); + gg[i].u4=(float *)malloc(sizeof(float)*nn); + gg[i].u5=(float *)malloc(sizeof(float)*nn); + gg[i].u6=(float *)malloc(sizeof(float)*nn); + gg[i].u7=(float *)malloc(sizeof(float)*nn); + gg[i].u8=(float *)malloc(sizeof(float)*nn); + gg[i].u9=(float *)malloc(sizeof(float)*nn); + gg[i].u10=(float *)malloc(sizeof(float)*nn); + + for(j=0 ; j < nn ; j++) { + gg[i].u1[j]=tmp[j]; + gg[i].u2[j]=tmp[j + nn]; + gg[i].u3[j]=tmp[j + 2*nn]; + gg[i].u4[j]=tmp[j + 3*nn]; + gg[i].u5[j]=tmp[j + 4*nn]; + gg[i].u6[j]=tmp[j + 5*nn] * (-1.0);/*Note the vertical GF's are*/ + gg[i].u7[j]=tmp[j + 6*nn] * (-1.0);/*flipped in earqt1.f and TW's*/ + gg[i].u8[j]=tmp[j + 7*nn] * (-1.0);/* Blackbox.f DVH conv. z + down*/ + if(isoflag==6) { + gg[i].u9[j]=tmp[j + 8*nn] * (-1.0); /*Radial exp*/ + gg[i].u10[j]=tmp[j + 9*nn] * (-1.0); /*Vertical exp*/ + } + if(isoflag==5) { + gg[i].u9[j]=0.0; + gg[i].u10[j]=0.0; + } + } + } + free(tmp); + free(npts); + free(dt); + + /* Cross-Correlation to obtain zero shift*/ + for(i=0; i < nsta; i++) { + N =ss[i].np; + Np=ss[i].nn; + Z =ss[i].zz; + np2 = (int)(log10((float)N)/log10(2.0) + 0.5); + np2=2<<(np2-1); + if(Z == 0) + ss[i].zz=correlate(ss,gg,i,np2);/*Compute cross-correlation*/ + /*Return zshift for largest cor.*/ + } + + /*Construct distance 1/R weighting*/ + if(wflag==1 && (nsta >= 1)) { +#ifndef ONLY_ERRORS_TO_STDERR + fprintf(stderr,"Station Information\n"); +#endif + fprintf(out,"Station Information\n"); + l=0; + for(i=0; i < nsta; i++) { + cormax = ss[i].dist / mindist; +#ifndef ONLY_ERRORS_TO_STDERR + fprintf(stderr,"Station(%d): %s R=%.1fkm AZI=%.1f W=%.3f Zcor=%d\n", i, ss[i].name,ss[i].dist,ss[i].azi*180.0/PI,cormax,ss[i].zz); +#endif + fprintf(out,"Station(%d): %s R=%.1fkm AZI=%.1f W=%.3f Zcor=%d\n", i, ss[i].name,ss[i].dist,ss[i].azi*180.0/PI,cormax,ss[i].zz); + N = ss[i].nn; + for(j=0; j < 3*N; j++) + W[l++]=cormax; + } + } + + /* INVERSION ROUTINE */ + + for(i=0 ; i < isoflag ; i++) /*Normalize AtA and AIV matix*/ + for(l=0 ; l < isoflag ; l++) + AIV[i][l]=0.0; + + for(i=0 ; i < isoflag ; i++) + B[i][0]=0.0; + + cnt1=cnt2=cnt3=0; + for(i=0; i < nsta; i++) { + Np=ss[i].nn; + Z =gg[i].zz; + cnt1=cnt2 = cnt3; + cnt2 += Np; + cnt3 += 2*Np; + for(j=Z; j < Z+Np; j++) { /*Index over time*/ + /*Mxx term*/ + AJ[0][cnt1] = (double)(0.5*sin(2*ss[i].azi)*gg[i].u1[j]); + if(isoflag==6) + { + AJ[0][cnt2] = (double)(0.166667*gg[i].u5[j] - 0.5*cos(2*ss[i].azi)*gg[i].u3[j] + 0.33333*gg[i].u9[j]); + AJ[0][cnt3] = (double)(0.166667*gg[i].u8[j] - 0.5*cos(2*ss[i].azi)*gg[i].u6[j] + 0.33333*gg[i].u10[j]); + } + if(isoflag==5) + { + AJ[0][cnt2] = (double)(0.500000*gg[i].u5[j] - 0.5*cos(2*ss[i].azi)*gg[i].u3[j]); + AJ[0][cnt3] = (double)(0.500000*gg[i].u8[j] - 0.5*cos(2*ss[i].azi)*gg[i].u6[j]); + } + /*Myy term*/ + AJ[1][cnt1] = (double)((-0.5)*sin(2*ss[i].azi)*gg[i].u1[j]); + if(isoflag==6) + { + AJ[1][cnt2] = (double)(0.166667*gg[i].u5[j] + 0.5*cos(2*ss[i].azi)*gg[i].u3[j] + 0.33333*gg[i].u9[j]); + AJ[1][cnt3] = (double)(0.166667*gg[i].u8[j] + 0.5*cos(2*ss[i].azi)*gg[i].u6[j] + 0.33333*gg[i].u10[j]); + } + if(isoflag==5) + { + AJ[1][cnt2] = (double)(0.500000*gg[i].u5[j] + 0.5*cos(2*ss[i].azi)*gg[i].u3[j]); + AJ[1][cnt3] = (double)(0.500000*gg[i].u8[j] + 0.5*cos(2*ss[i].azi)*gg[i].u6[j]); + } + + /*Mxy term*/ + AJ[2][cnt1] = (double)((-1.0)*cos(2*ss[i].azi)*gg[i].u1[j]); + AJ[2][cnt2] = (double)((-1.0)*sin(2*ss[i].azi)*gg[i].u3[j]); + AJ[2][cnt3] = (double)((-1.0)*sin(2*ss[i].azi)*gg[i].u6[j]); + + /*Mxz term*/ + AJ[3][cnt1] = (double)((-1.0)*sin(ss[i].azi)*gg[i].u2[j]); + AJ[3][cnt2] = (double)( cos(ss[i].azi)*gg[i].u4[j]); + AJ[3][cnt3] = (double)( cos(ss[i].azi)*gg[i].u7[j]); + + /*Myz term*/ + AJ[4][cnt1] = (double)( cos(ss[i].azi)*gg[i].u2[j]); + AJ[4][cnt2] = (double)( sin(ss[i].azi)*gg[i].u4[j]); + AJ[4][cnt3] = (double)( sin(ss[i].azi)*gg[i].u7[j]); + + /*Mzz term*/ + if(isoflag==6) + { + AJ[5][cnt1] = (double)(0.0); + AJ[5][cnt2] = (double)(0.33333*gg[i].u9[j]-0.33333*gg[i].u5[j]); + AJ[5][cnt3] = (double)(0.33333*gg[i].u10[j]-0.3333*gg[i].u8[j]); + } + + + cnt1++; + cnt2++; + cnt3++; + } + } + + + for(i=0 ; i < isoflag ; i++) /*Compute AtA */ + for(j=0 ; j < isoflag ; j++) + for(k=0 ; k < cnt3 ; k++) + AIV[i][j] += AJ[i][k]* AJ[j][k] * (double)W[k]; + + + cnt1=cnt2=cnt3=0; + tmp=(float *)malloc(10*N*nn*sizeof(float)); + for(j=0; j < nsta; j++) { + l=0; + Z =ss[j].zz; + Np=ss[j].nn; + cnt1=cnt2 = cnt3; + cnt2 += Np; + cnt3 += 2*Np; + for(i=Z ; i < Np+Z ; i++) { + tmp[cnt1] = ss[j].t[i]; + tmp[cnt2] = ss[j].r[i]; + tmp[cnt3] = ss[j].z[i]; + cnt1++; + cnt2++; + cnt3++; + } + } + + for(i=0 ; i < isoflag ; i++) /* Calculate Righthand Side */ + for(j=0 ; j < cnt3 ; j++) + B[i][0] += AJ[i][j] * (double)tmp[j] * (double)W[j]; + + + minvdbl(AIV,B,isoflag,1); /* Determine Solution Vector */ + M.mxx=m11; + M.myy=m22; + M.mxy=m12; + M.mxz=m13; + M.myz=m23; +if(isoflag==6) + M.mzz=m33; +if(isoflag==5) + M.mzz=-1.0*(m11 + m22); + + /*Call Bob's MT decomposition routines*/ + /*The minus one is needed to map Helmbergers convention into Aki's*/ + /*Jost and Hermann (1989) state that AKI's convention is -1*LANGSTONS*/ + +tracelang=(M.mxx+M.myy+M.mzz)/3; /*Trace of MT*/ + + d_mtrep=1; +/*Convert deviatoric moment tensor to AKI convention*/ +d_mt[0][0] = (double)(-1.0*gfscale*M.mxx); +d_mt[0][1] = (double)(-1.0*gfscale*M.mxy); +d_mt[0][2] = (double)(-1.0*gfscale*M.mxz); +d_mt[1][0] = (double)(-1.0*gfscale*M.mxy); +d_mt[1][1] = (double)(-1.0*gfscale*M.myy); +d_mt[1][2] = (double)(-1.0*gfscale*M.myz); +d_mt[2][0] = (double)(-1.0*gfscale*M.mxz); +d_mt[2][1] = (double)(-1.0*gfscale*M.myz); +d_mt[2][2] = (double)(-1.0*gfscale*M.mzz); + +fprintf(out,"isomoment=%g\n",tracelang*gfscale); + + + fprintf(out,"MT Aki Convention\n"); + fprintf(out,"Mxx=%.3f\nMxy=%.3f\nMxz=%.3f\nMyy=%.3f\nMyz=%.3f\nMzz=%.3f\n", + d_mt[0][0]/gfscale,d_mt[0][1]/gfscale,d_mt[0][2]/gfscale,d_mt[1][1]/gfscale, + d_mt[1][2]/gfscale,d_mt[2][2]/gfscale); + + m0dcf_(&d_mtrep,d_mf,d_mt,d_d,d_v,d_miso,d_mdc,d_mclvd, + &d_m0,&d_mw,d_axis,d_plunge,d_azimuth,d_strike,d_dip, + d_slip,&d_pcdc,&d_pcclvd,&d_pciso); + + eigen1=(float)d_d[0]; + eigen2=(float)d_d[1]; + eigen3=(float)d_d[2]; +fprintf(out,"eigen1=%g\n",eigen1); +fprintf(out,"eigen2=%g\n",eigen2); +fprintf(out,"eigen3=%g\n",eigen3); + eigaz1=(float)d_azimuth[0]; + eigaz2=(float)d_azimuth[1]; + eigaz3=(float)d_azimuth[2]; + eigpl1=(float)d_plunge[0]; + eigpl2=(float)d_plunge[1]; + eigpl3=(float)d_plunge[2]; + + Mo = (float) d_m0; + Mw = (float) d_mw; + Strike = (float) d_strike[0]; + Rake = (float) d_slip[0]; + Dip = (float) d_dip[0]; + St2 = (float) d_strike[1]; + Rk2 = (float) d_slip[1]; + Dp2 = (float) d_dip[1]; + Pdc = (float) d_pcdc; + Pclvd = (float) d_pcclvd; + Piso = (float) d_pciso; + isoMo = tracelang*gfscale; + fullMo = fabs(isoMo) + fabs(eigen3); + if(isoflag == 6) + Mw = (log10(fullMo)-16.05)*2/3; + +/*Code to Convert Percentages to those based on Bowers moment*/ +/*Fortran subroutines compute Dziewonski moment + isotropic*/ +Piso=fabs(isoMo)/fullMo*100; +F=-eigen1/eigen3; +Pdc=(fabs(eigen3)/fullMo)*fabs(1-2*F)*100; +Pclvd=(fabs(eigen3)/fullMo)*fabs(F)*200; +/*End Percent Decomposition Modification 01122016*/ + +#ifndef ONLY_ERRORS_TO_STDERR + fprintf(stderr,"isoMo: %g\n",isoMo); + fprintf(stderr,"Mo=%g (%g)\nMw=%.2f\nStrike=%.0f ; %.0f\nRake=%.0f ; %.0f\nDip=%.0f; %.0f\nPdc=%.0f\nPclvd=%.0f\nPiso=%.0f\n", + Mo,fullMo,Mw,Strike,St2,Rake,Rk2,Dip,Dp2,Pdc,Pclvd,Piso); +#endif + fprintf(out,"Mo=%g (%g)\nMw=%.2f\nStrike=%.0f ; %.0f\nRake=%.0f ; %.0f\nDip=%.0f ; %.0f\nPdc=%.0f\nPclvd=%.0f\nPiso=%.0f\n", + Mo,fullMo,Mw,Strike,St2,Rake,Rk2,Dip,Dp2,Pdc,Pclvd,Piso); + + fitcheck(ss,gg,W,M,Mo,nsta,isoflag,&E,&VR); /*Function to compute vr and flag bad stations*/ + +#ifdef TDMT_PLOT + if(plotflag==1) + mt_plot(ss,gg,nsta,Strike,Rake,Dip,St2,Rk2,Dp2,M,d_mt,Pdc,Pclvd,Piso,Mo,Mw,E,VR); +#endif + + fprintf(out,"Variance=%.3e\n",E); + fprintf(out,"VarRed=%.3e\n",VR); +#ifndef ONLY_ERRORS_TO_STDERR + fprintf(stderr,"Var/Pdc=%.3e\n",E/Pdc); +#endif + fprintf(out,"Var/Pdc=%.3e\n",E/Pdc); + + if(VR < 20.0) QUAL=0; + if(VR > 20.0 && VR < 40.0) QUAL=1; + if(VR > 40.0 && VR < 60.0) QUAL=2; + if(VR > 60.0 && VR < 80.0) QUAL=3; + if(VR > 80.0) QUAL=4; + fprintf(out,"Quality=%d\n",QUAL); +#ifndef ONLY_ERRORS_TO_STDERR + fprintf(stderr,"Quality=%d\n",QUAL); +#endif + + if (fscanf(par, "%s\n", datfile) == 1) { + mt_dat(ss,gg,nsta,Strike,Rake,Dip,St2,Rk2,Dp2,M,-gfscale,Pdc,Pclvd,Piso,Mo,Mw,E,VR,depth,datfile); + } + fclose(par); + fclose(out); + + return 0; +} + + diff --git a/TDMT_GMT/tdmt_invc_iso_fwd.o b/TDMT_GMT/tdmt_invc_iso_fwd.o new file mode 100644 index 0000000..1c7718b Binary files /dev/null and b/TDMT_GMT/tdmt_invc_iso_fwd.o differ diff --git a/TDMT_GMT/tdmt_invc_iso_out.c b/TDMT_GMT/tdmt_invc_iso_out.c new file mode 100755 index 0000000..f587982 --- /dev/null +++ b/TDMT_GMT/tdmt_invc_iso_out.c @@ -0,0 +1,477 @@ +/*Copyright (c) Douglas Dreger +Berkeley Seismological Laboratory +University of California, Berkeley */ +/* Moment Tensor Inversion */ +/* Uses previously determined Green's functions in D. Helmbergers format*/ + +#include +#include"tdmtinv_iso.h" + +void gaussj(); +int correlate(),fclose(); +void readhelm(); +void minvdbl(); +void m0dcf_(); +void fitcheck(); +int mt_dat(); + +FILE *fopen(), *par,*fd, *fd1, *fd2, *fd3, *out, *outd, *outw, *outG; + + +int main() +{ + int i,j,l,N,Np,Z,k,ntr,nsta,nn,*npts = NULL ,wflag=0; + int depth,cnt1, cnt2, cnt3, np2,QUAL,plotflag,WCNT; + float *W, *tmpp,*tmp,*dt, cormax, mindist, dist,E,VR,junk; + float tracelang; + float eigen1, eigen2, eigen3; + float eigaz1, eigaz2, eigaz3; + float eigpl1, eigpl2, eigpl3; + double **AIV,**AJ, **B; + struct MOMENT M; + struct DATA *ss; + struct GREEN *gg; + char infile[200],binfile[205],datfile[200]; + /**Variables needed for implementation of R Urhammer's MT decomposition**/ + /**Algorithms**/ + char d_axis[3]; + int d_mtrep; + double d_mf[6],d_mt[3][3],d_d[3],d_v[3][3],d_miso[3][3],d_mdc[3][3], + d_mclvd[3][3],d_m0,d_mw,d_plunge[3],d_azimuth[3],d_strike[3], + d_slip[3],d_dip[3],d_pcdc,d_pcclvd,d_pciso; + float gfscale=1.0e+20; /*Dyne cm*/ + /**************/ + float fullMo, Mo, isoMo, Mw, Strike, Rake, Dip, Pdc, Piso, Pclvd,F; + float St2, Rk2, Dp2; + int isoflag; + + if ( (par=fopen(MT_INFILE,"r")) == NULL) { + fprintf(stderr, "Error opening %s: %s\n", MT_INFILE, strerror(errno)); + exit(1); + } + + if ( (out=fopen(MT_OUTFILE,"a")) == NULL) { + fprintf(stderr, "Error opening %s: %s\n", MT_OUTFILE, strerror(errno)); + exit(1); + } + + + fscanf(par,"%d %d %d %d %d\n",&nsta,&depth,&wflag,&isoflag,&plotflag); + M.isoflag=isoflag; + fprintf(out,"isoflag=%d Depth=%d\n",isoflag,depth); +#ifndef ONLY_ERRORS_TO_STDERR + fprintf(stderr,"isoflag=%d Depth=%d\n",isoflag,depth); +#endif + + if(isoflag == 6) + { + fprintf(stderr,"Opening d, G and W outfiles\n"); + outd=fopen("d.out","w"); + outG=fopen("G.out","w"); + outw=fopen("W.out","w"); + } + + AIV=(double **)malloc(sizeof(double *)*isoflag); + for(i=0 ; i < isoflag ; i++) + AIV[i]=(double *)malloc(sizeof(double)*isoflag); + + B=(double **)malloc(sizeof(double *)*isoflag); + for(i=0 ; i < isoflag; i++) + B[i]=(double *)malloc(sizeof(double)*1); + + ss=(struct DATA *)malloc(sizeof(struct DATA)*(nsta)); + gg=(struct GREEN *)malloc(sizeof(struct GREEN)*(nsta)); + + tmp=(float *)malloc(sizeof(float)*10*10000); + npts=(int *)malloc(sizeof(int)*10); + dt =(float *)malloc(sizeof(float)*10); + + mindist=100000.0; + WCNT=0; + ntr = 0; + for(i=0 ; i < nsta ; i++) + { + fscanf(par,"%s %f %f %d %d\n",infile,&dist,&(ss[i].azi),&Z,&Np); + WCNT += Np; + strcpy(ss[i].name,infile); + ss[i].azi *= PI/180.0; + + /* Read in seismic trace data */ + readhelm(infile,&ntr,npts,dt,tmp); + if (ntr != 3) { + fprintf(stderr, "read %d traces from %s; 3 required\n",ntr,infile); + exit(1); + } + /* We assume all the traces have the same time step and length; + That's why we have tdmt_redi_sched, to do this checking */ + nn=npts[0]; + + if(mindist > dist) mindist = dist; + ss[i].dist=dist; + ss[i].np=nn; + ss[i].dt=dt[0]; + ss[i].nn=Np; + ss[i].zz=Z; + + /*Allocate data structure arrays*/ + ss[i].t=(float *)malloc(sizeof(float)*nn); + ss[i].r=(float *)malloc(sizeof(float)*nn); + ss[i].z=(float *)malloc(sizeof(float)*nn); + + for(j=0 ; j < nn ; j++) + { + ss[i].t[j]=tmp[j]; + ss[i].r[j]=tmp[j + nn]; + ss[i].z[j]=tmp[j + 2*nn]; + } + } + + /*Allocate Memory for Station Weights*/ + WCNT *= 3; + W=(float *)malloc(sizeof(float)*WCNT); + for(j=0; j < WCNT ; j++) + W[j]=1.0; + + /*Allocate Memory for A matrix*/ + AJ=(double **)malloc(sizeof(double *)*isoflag); + for(j=0 ; j < isoflag ; j++) + AJ[j]=(double *)malloc(sizeof(double)*WCNT); + + /* Read in the Green's functions */ + ntr = 0; + for(i=0 ; i < nsta ; i++) { + fscanf(par,"%s %d %d\n",infile,&Z,&Np); + /*Pete's binary read + sprintf(binfile, "%s.bin", infile); + if (access(binfile, R_OK) == 0) + readbin(binfile, &ntr, &npts, &dt, &tmp); + else + */ + readhelm(infile,&ntr,npts,dt,tmp); + + if ( (isoflag == 5 && ntr < 8) || isoflag == 6 && ntr != 10) { + fprintf(stderr, "GF file %s has unexpected sector count: %d\n", + infile, ntr); + exit(1); + } + + nn=npts[0]; + + gg[i].np=nn; + gg[i].dt=dt[0]; + gg[i].nn=Np; + gg[i].zz=Z; + + /*Allocate Greens Function structure arrays*/ + gg[i].u1=(float *)malloc(sizeof(float)*nn); + gg[i].u2=(float *)malloc(sizeof(float)*nn); + gg[i].u3=(float *)malloc(sizeof(float)*nn); + gg[i].u4=(float *)malloc(sizeof(float)*nn); + gg[i].u5=(float *)malloc(sizeof(float)*nn); + gg[i].u6=(float *)malloc(sizeof(float)*nn); + gg[i].u7=(float *)malloc(sizeof(float)*nn); + gg[i].u8=(float *)malloc(sizeof(float)*nn); + gg[i].u9=(float *)malloc(sizeof(float)*nn); + gg[i].u10=(float *)malloc(sizeof(float)*nn); + + for(j=0 ; j < nn ; j++) { + gg[i].u1[j]=tmp[j]; + gg[i].u2[j]=tmp[j + nn]; + gg[i].u3[j]=tmp[j + 2*nn]; + gg[i].u4[j]=tmp[j + 3*nn]; + gg[i].u5[j]=tmp[j + 4*nn]; + gg[i].u6[j]=tmp[j + 5*nn] * (-1.0);/*Note the vertical GF's are*/ + gg[i].u7[j]=tmp[j + 6*nn] * (-1.0);/*flipped in earqt1.f and TW's*/ + gg[i].u8[j]=tmp[j + 7*nn] * (-1.0);/* Blackbox.f DVH conv. z + down*/ + if(isoflag==6) { + gg[i].u9[j]=tmp[j + 8*nn] * (-1.0); /*Radial exp*/ + gg[i].u10[j]=tmp[j + 9*nn] * (-1.0); /*Vertical exp*/ + } + if(isoflag==5) { + gg[i].u9[j]=0.0; + gg[i].u10[j]=0.0; + } + } + } + free(tmp); + free(npts); + free(dt); + + /* Cross-Correlation to obtain zero shift*/ + for(i=0; i < nsta; i++) { + N =ss[i].np; + Np=ss[i].nn; + Z =ss[i].zz; + np2 = (int)(log10((float)N)/log10(2.0) + 0.5); + np2=2<<(np2-1); + if(Z == 0) + ss[i].zz=correlate(ss,gg,i,np2);/*Compute cross-correlation*/ + /*Return zshift for largest cor.*/ + } + + /*Construct distance 1/R weighting*/ + if(wflag==1 && (nsta >= 1)) { +#ifndef ONLY_ERRORS_TO_STDERR + fprintf(stderr,"Station Information\n"); +#endif + fprintf(out,"Station Information\n"); + l=0; + for(i=0; i < nsta; i++) { + cormax = ss[i].dist / mindist; +#ifndef ONLY_ERRORS_TO_STDERR + fprintf(stderr,"Station(%d): %s R=%.1fkm AZI=%.1f W=%.3f Zcor=%d\n", i, ss[i].name,ss[i].dist,ss[i].azi*180.0/PI,cormax,ss[i].zz); +#endif + fprintf(out,"Station(%d): %s R=%.1fkm AZI=%.1f W=%.3f Zcor=%d\n", i, ss[i].name,ss[i].dist,ss[i].azi*180.0/PI,cormax,ss[i].zz); + N = ss[i].nn; + for(j=0; j < 3*N; j++) + W[l++]=cormax; + } + } + +fprintf(stderr,"HERE1, l=%d\n",l); + if(isoflag ==6) + { + for(i=0; i < l; i++) /*Write weight file*/ + fprintf(outw,"%f\n",W[i]); + + fclose(outw); + } +fprintf(stderr,"HERE2\n"); + + /* INVERSION ROUTINE */ + + for(i=0 ; i < isoflag ; i++) /*Normalize AtA and AIV matix*/ + for(l=0 ; l < isoflag ; l++) + AIV[i][l]=0.0; + + for(i=0 ; i < isoflag ; i++) + B[i][0]=0.0; + + cnt1=cnt2=cnt3=0; + for(i=0; i < nsta; i++) { + Np=ss[i].nn; + Z =gg[i].zz; + cnt1=cnt2 = cnt3; + cnt2 += Np; + cnt3 += 2*Np; + for(j=Z; j < Z+Np; j++) { /*Index over time*/ + /*Mxx term*/ + AJ[0][cnt1] = (double)(0.5*sin(2*ss[i].azi)*gg[i].u1[j]); + if(isoflag==6) + { + AJ[0][cnt2] = (double)(0.166667*gg[i].u5[j] - 0.5*cos(2*ss[i].azi)*gg[i].u3[j] + 0.33333*gg[i].u9[j]); + AJ[0][cnt3] = (double)(0.166667*gg[i].u8[j] - 0.5*cos(2*ss[i].azi)*gg[i].u6[j] + 0.33333*gg[i].u10[j]); + } + if(isoflag==5) + { + AJ[0][cnt2] = (double)(0.500000*gg[i].u5[j] - 0.5*cos(2*ss[i].azi)*gg[i].u3[j]); + AJ[0][cnt3] = (double)(0.500000*gg[i].u8[j] - 0.5*cos(2*ss[i].azi)*gg[i].u6[j]); + } + /*Myy term*/ + AJ[1][cnt1] = (double)((-0.5)*sin(2*ss[i].azi)*gg[i].u1[j]); + if(isoflag==6) + { + AJ[1][cnt2] = (double)(0.166667*gg[i].u5[j] + 0.5*cos(2*ss[i].azi)*gg[i].u3[j] + 0.33333*gg[i].u9[j]); + AJ[1][cnt3] = (double)(0.166667*gg[i].u8[j] + 0.5*cos(2*ss[i].azi)*gg[i].u6[j] + 0.33333*gg[i].u10[j]); + } + if(isoflag==5) + { + AJ[1][cnt2] = (double)(0.500000*gg[i].u5[j] + 0.5*cos(2*ss[i].azi)*gg[i].u3[j]); + AJ[1][cnt3] = (double)(0.500000*gg[i].u8[j] + 0.5*cos(2*ss[i].azi)*gg[i].u6[j]); + } + + /*Mxy term*/ + AJ[2][cnt1] = (double)((-1.0)*cos(2*ss[i].azi)*gg[i].u1[j]); + AJ[2][cnt2] = (double)((-1.0)*sin(2*ss[i].azi)*gg[i].u3[j]); + AJ[2][cnt3] = (double)((-1.0)*sin(2*ss[i].azi)*gg[i].u6[j]); + + /*Mxz term*/ + AJ[3][cnt1] = (double)((-1.0)*sin(ss[i].azi)*gg[i].u2[j]); + AJ[3][cnt2] = (double)( cos(ss[i].azi)*gg[i].u4[j]); + AJ[3][cnt3] = (double)( cos(ss[i].azi)*gg[i].u7[j]); + + /*Myz term*/ + AJ[4][cnt1] = (double)( cos(ss[i].azi)*gg[i].u2[j]); + AJ[4][cnt2] = (double)( sin(ss[i].azi)*gg[i].u4[j]); + AJ[4][cnt3] = (double)( sin(ss[i].azi)*gg[i].u7[j]); + + /*Mzz term*/ + if(isoflag==6) + { + AJ[5][cnt1] = (double)(0.0); + AJ[5][cnt2] = (double)(0.33333*gg[i].u9[j]-0.33333*gg[i].u5[j]); + AJ[5][cnt3] = (double)(0.33333*gg[i].u10[j]-0.3333*gg[i].u8[j]); + } + + + cnt1++; + cnt2++; + cnt3++; + } + } + + if(isoflag==6) + { + for(i=0; i < cnt3; i++) /*Write G matrix*/ + fprintf(outG,"%g %g %g %g %g %g\n",AJ[0][i],AJ[1][i],AJ[2][i],AJ[3][i],AJ[4][i],AJ[5][i]); + fclose(outG); + } + + + for(i=0 ; i < isoflag ; i++) /*Compute AtA */ + for(j=0 ; j < isoflag ; j++) + for(k=0 ; k < cnt3 ; k++) + AIV[i][j] += AJ[i][k]* AJ[j][k] * (double)W[k]; + + + cnt1=cnt2=cnt3=0; + tmp=(float *)malloc(10*N*nn*sizeof(float)); + for(j=0; j < nsta; j++) { + l=0; + Z =ss[j].zz; + Np=ss[j].nn; + cnt1=cnt2 = cnt3; + cnt2 += Np; + cnt3 += 2*Np; + for(i=Z ; i < Np+Z ; i++) { + tmp[cnt1] = ss[j].t[i]; + tmp[cnt2] = ss[j].r[i]; + tmp[cnt3] = ss[j].z[i]; + cnt1++; + cnt2++; + cnt3++; + } + } + if(isoflag ==6) + { + for(i=0; i < cnt3; i++) + fprintf(outd,"%g\n",tmp[i]); + fclose(outd); + } + + for(i=0 ; i < isoflag ; i++) /* Calculate Righthand Side */ + for(j=0 ; j < cnt3 ; j++) + B[i][0] += AJ[i][j] * (double)tmp[j] * (double)W[j]; + + + minvdbl(AIV,B,isoflag,1); /* Determine Solution Vector */ + + M.mxx=(float)B[0][0]; + M.myy=(float)B[1][0]; + M.mxy=(float)B[2][0]; + M.mxz=(float)B[3][0]; + M.myz=(float)B[4][0]; +if(isoflag==6) + M.mzz=(float)B[5][0]; +if(isoflag==5) + M.mzz=-1.0*(M.mxx + M.myy); + + /*Call Bob's MT decomposition routines*/ + /*The minus one is needed to map Helmbergers convention into Aki's*/ + /*Jost and Hermann (1989) state that AKI's convention is -1*LANGSTONS*/ + +tracelang=(M.mxx+M.myy+M.mzz)/3; /*Trace of MT*/ + + d_mtrep=1; +/*Convert deviatoric moment tensor to AKI convention*/ +d_mt[0][0] = (double)(-1.0*gfscale*M.mxx); +d_mt[0][1] = (double)(-1.0*gfscale*M.mxy); +d_mt[0][2] = (double)(-1.0*gfscale*M.mxz); +d_mt[1][0] = (double)(-1.0*gfscale*M.mxy); +d_mt[1][1] = (double)(-1.0*gfscale*M.myy); +d_mt[1][2] = (double)(-1.0*gfscale*M.myz); +d_mt[2][0] = (double)(-1.0*gfscale*M.mxz); +d_mt[2][1] = (double)(-1.0*gfscale*M.myz); +d_mt[2][2] = (double)(-1.0*gfscale*M.mzz); + +fprintf(out,"isomoment=%g\n",tracelang*gfscale); + + + fprintf(out,"MT Aki Convention\n"); + fprintf(out,"Mxx=%.3f\nMxy=%.3f\nMxz=%.3f\nMyy=%.3f\nMyz=%.3f\nMzz=%.3f\n", + d_mt[0][0]/gfscale,d_mt[0][1]/gfscale,d_mt[0][2]/gfscale,d_mt[1][1]/gfscale, + d_mt[1][2]/gfscale,d_mt[2][2]/gfscale); + + m0dcf_(&d_mtrep,d_mf,d_mt,d_d,d_v,d_miso,d_mdc,d_mclvd, + &d_m0,&d_mw,d_axis,d_plunge,d_azimuth,d_strike,d_dip, + d_slip,&d_pcdc,&d_pcclvd,&d_pciso); + + eigen1=(float)d_d[0]; + eigen2=(float)d_d[1]; + eigen3=(float)d_d[2]; +fprintf(out,"eigen1=%g\n",eigen1); +fprintf(out,"eigen2=%g\n",eigen2); +fprintf(out,"eigen3=%g\n",eigen3); + eigaz1=(float)d_azimuth[0]; + eigaz2=(float)d_azimuth[1]; + eigaz3=(float)d_azimuth[2]; + eigpl1=(float)d_plunge[0]; + eigpl2=(float)d_plunge[1]; + eigpl3=(float)d_plunge[2]; + + Mo = (float) d_m0; + Mw = (float) d_mw; + Strike = (float) d_strike[0]; + Rake = (float) d_slip[0]; + Dip = (float) d_dip[0]; + St2 = (float) d_strike[1]; + Rk2 = (float) d_slip[1]; + Dp2 = (float) d_dip[1]; + Pdc = (float) d_pcdc; + Pclvd = (float) d_pcclvd; + Piso = (float) d_pciso; + isoMo = tracelang*gfscale; + fullMo = fabs(isoMo) + fabs(eigen3); + if(isoflag == 6) + Mw = (log10(fullMo)-16.05)*2/3; + +/*Code to Convert Percentages to those based on Bowers moment*/ +/*Fortran subroutines compute Dziewonski moment + isotropic*/ +Piso=fabs(isoMo)/fullMo*100; +F=-eigen1/eigen3; +Pdc=(fabs(eigen3)/fullMo)*fabs(1-2*F)*100; +Pclvd=(fabs(eigen3)/fullMo)*fabs(F)*200; +/*End Percent Decomposition Modification 01122016*/ + +#ifndef ONLY_ERRORS_TO_STDERR + fprintf(stderr,"isoMo: %g\n",isoMo); + fprintf(stderr,"Mo=%g (%g)\nMw=%.2f\nStrike=%.0f ; %.0f\nRake=%.0f ; %.0f\nDip=%.0f; %.0f\nPdc=%.0f\nPclvd=%.0f\nPiso=%.0f\n", + Mo,fullMo,Mw,Strike,St2,Rake,Rk2,Dip,Dp2,Pdc,Pclvd,Piso); +#endif + fprintf(out,"Mo=%g (%g)\nMw=%.2f\nStrike=%.0f ; %.0f\nRake=%.0f ; %.0f\nDip=%.0f ; %.0f\nPdc=%.0f\nPclvd=%.0f\nPiso=%.0f\n", + Mo,fullMo,Mw,Strike,St2,Rake,Rk2,Dip,Dp2,Pdc,Pclvd,Piso); + + fitcheck(ss,gg,W,M,Mo,nsta,isoflag,&E,&VR); /*Function to compute vr and flag bad stations*/ + +#ifdef TDMT_PLOT + if(plotflag==1) + mt_plot(ss,gg,nsta,Strike,Rake,Dip,St2,Rk2,Dp2,M,d_mt,Pdc,Pclvd,Piso,Mo,Mw,E,VR); +#endif + + fprintf(out,"Variance=%.3e\n",E); + fprintf(out,"VarRed=%.3e\n",VR); +#ifndef ONLY_ERRORS_TO_STDERR + fprintf(stderr,"Var/Pdc=%.3e\n",E/Pdc); +#endif + fprintf(out,"Var/Pdc=%.3e\n",E/Pdc); + + if(VR < 20.0) QUAL=0; + if(VR > 20.0 && VR < 40.0) QUAL=1; + if(VR > 40.0 && VR < 60.0) QUAL=2; + if(VR > 60.0 && VR < 80.0) QUAL=3; + if(VR > 80.0) QUAL=4; + fprintf(out,"Quality=%d\n",QUAL); +#ifndef ONLY_ERRORS_TO_STDERR + fprintf(stderr,"Quality=%d\n",QUAL); +#endif + + if (fscanf(par, "%s\n", datfile) == 1) { + mt_dat(ss,gg,nsta,Strike,Rake,Dip,St2,Rk2,Dp2,M,-gfscale,Pdc,Pclvd,Piso,Mo,Mw,E,VR,depth,datfile); + } + fclose(par); + fclose(out); + + return 0; +} + + diff --git a/TDMT_GMT/tdmt_invc_iso_out.o b/TDMT_GMT/tdmt_invc_iso_out.o new file mode 100644 index 0000000..3c0ba82 Binary files /dev/null and b/TDMT_GMT/tdmt_invc_iso_out.o differ diff --git a/TDMT_GMT/tdmt_invc_iso_outputmtline.c b/TDMT_GMT/tdmt_invc_iso_outputmtline.c new file mode 100755 index 0000000..4fd9643 --- /dev/null +++ b/TDMT_GMT/tdmt_invc_iso_outputmtline.c @@ -0,0 +1,458 @@ +/*Copyright (c) Douglas Dreger +Berkeley Seismological Laboratory +University of California, Berkeley */ +/* Moment Tensor Inversion */ +/* Uses previously determined Green's functions in D. Helmbergers format*/ + +#include +#include"tdmtinv_iso.h" + +void gaussj(); +int correlate(),fclose(); +void readhelm(); +void minvdbl(); +void m0dcf_(); +void fitcheck(); +int mt_dat(); + +FILE *fopen(), *par,*fd, *fd1, *fd2, *fd3, *out, *out2; + + +int main() +{ + int i,j,l,N,Np,Z,k,ntr,nsta,nn,*npts = NULL ,wflag=0; + int depth,cnt1, cnt2, cnt3, np2,QUAL,plotflag,WCNT; + float *W, *tmpp,*tmp,*dt, cormax, mindist, dist,E,VR,junk; + float tracelang; + float eigen1, eigen2, eigen3; + float eigaz1, eigaz2, eigaz3; + float eigpl1, eigpl2, eigpl3; + double **AIV,**AJ, **B; + struct MOMENT M; + struct DATA *ss; + struct GREEN *gg; + char infile[200],binfile[205],datfile[200]; + /**Variables needed for implementation of R Urhammer's MT decomposition**/ + /**Algorithms**/ + char d_axis[3]; + int d_mtrep; + double d_mf[6],d_mt[3][3],d_d[3],d_v[3][3],d_miso[3][3],d_mdc[3][3], + d_mclvd[3][3],d_m0,d_mw,d_plunge[3],d_azimuth[3],d_strike[3], + d_slip[3],d_dip[3],d_pcdc,d_pcclvd,d_pciso; + float gfscale=1.0e+20; /*Dyne cm*/ + /**************/ + float fullMo, Mo, isoMo, Mw, Strike, Rake, Dip, Pdc, Piso, Pclvd,F; + float St2, Rk2, Dp2; + int isoflag; + + if ( (par=fopen(MT_INFILE,"r")) == NULL) { + fprintf(stderr, "Error opening %s: %s\n", MT_INFILE, strerror(errno)); + exit(1); + } + + if ( (out=fopen(MT_OUTFILE,"a")) == NULL) { + fprintf(stderr, "Error opening %s: %s\n", MT_OUTFILE, strerror(errno)); + exit(1); + } + if ( (out2=fopen("mt_line.out","a")) == NULL) { + fprintf(stderr, "Error opening mt_line.out %s\n", MT_OUTFILE); + exit(1); + } + + fscanf(par,"%d %d %d %d %d\n",&nsta,&depth,&wflag,&isoflag,&plotflag); + M.isoflag=isoflag; + fprintf(out,"isoflag=%d Depth=%d\n",isoflag,depth); +#ifndef ONLY_ERRORS_TO_STDERR + fprintf(stderr,"isoflag=%d Depth=%d\n",isoflag,depth); +#endif + + AIV=(double **)malloc(sizeof(double *)*isoflag); + for(i=0 ; i < isoflag ; i++) + AIV[i]=(double *)malloc(sizeof(double)*isoflag); + + B=(double **)malloc(sizeof(double *)*isoflag); + for(i=0 ; i < isoflag; i++) + B[i]=(double *)malloc(sizeof(double)*1); + + ss=(struct DATA *)malloc(sizeof(struct DATA)*(nsta)); + gg=(struct GREEN *)malloc(sizeof(struct GREEN)*(nsta)); + + tmp=(float *)malloc(sizeof(float)*10*10000); + npts=(int *)malloc(sizeof(int)*10); + dt =(float *)malloc(sizeof(float)*10); + + mindist=100000.0; + WCNT=0; + ntr = 0; + for(i=0 ; i < nsta ; i++) + { + fscanf(par,"%s %f %f %d %d\n",infile,&dist,&(ss[i].azi),&Z,&Np); + WCNT += Np; + strcpy(ss[i].name,infile); + ss[i].azi *= PI/180.0; + + /* Read in seismic trace data */ + readhelm(infile,&ntr,npts,dt,tmp); + if (ntr != 3) { + fprintf(stderr, "read %d traces from %s; 3 required\n",ntr,infile); + exit(1); + } + /* We assume all the traces have the same time step and length; + That's why we have tdmt_redi_sched, to do this checking */ + nn=npts[0]; + + if(mindist > dist) mindist = dist; + ss[i].dist=dist; + ss[i].np=nn; + ss[i].dt=dt[0]; + ss[i].nn=Np; + ss[i].zz=Z; + + /*Allocate data structure arrays*/ + ss[i].t=(float *)malloc(sizeof(float)*nn); + ss[i].r=(float *)malloc(sizeof(float)*nn); + ss[i].z=(float *)malloc(sizeof(float)*nn); + + for(j=0 ; j < nn ; j++) + { + ss[i].t[j]=tmp[j]; + ss[i].r[j]=tmp[j + nn]; + ss[i].z[j]=tmp[j + 2*nn]; + } + } + + /*Allocate Memory for Station Weights*/ + WCNT *= 3; + W=(float *)malloc(sizeof(float)*WCNT); + for(j=0; j < WCNT ; j++) + W[j]=1.0; + + /*Allocate Memory for A matrix*/ + AJ=(double **)malloc(sizeof(double *)*isoflag); + for(j=0 ; j < isoflag ; j++) + AJ[j]=(double *)malloc(sizeof(double)*WCNT); + + /* Read in the Green's functions */ + ntr = 0; + for(i=0 ; i < nsta ; i++) { + fscanf(par,"%s %d %d\n",infile,&Z,&Np); + /*Pete's binary read + sprintf(binfile, "%s.bin", infile); + if (access(binfile, R_OK) == 0) + readbin(binfile, &ntr, &npts, &dt, &tmp); + else + */ + readhelm(infile,&ntr,npts,dt,tmp); + + if ( (isoflag == 5 && ntr < 8) || isoflag == 6 && ntr != 10) { + fprintf(stderr, "GF file %s has unexpected sector count: %d\n", + infile, ntr); + exit(1); + } + + nn=npts[0]; + + gg[i].np=nn; + gg[i].dt=dt[0]; + gg[i].nn=Np; + gg[i].zz=Z; + + /*Allocate Greens Function structure arrays*/ + gg[i].u1=(float *)malloc(sizeof(float)*nn); + gg[i].u2=(float *)malloc(sizeof(float)*nn); + gg[i].u3=(float *)malloc(sizeof(float)*nn); + gg[i].u4=(float *)malloc(sizeof(float)*nn); + gg[i].u5=(float *)malloc(sizeof(float)*nn); + gg[i].u6=(float *)malloc(sizeof(float)*nn); + gg[i].u7=(float *)malloc(sizeof(float)*nn); + gg[i].u8=(float *)malloc(sizeof(float)*nn); + gg[i].u9=(float *)malloc(sizeof(float)*nn); + gg[i].u10=(float *)malloc(sizeof(float)*nn); + + for(j=0 ; j < nn ; j++) { + gg[i].u1[j]=tmp[j]; + gg[i].u2[j]=tmp[j + nn]; + gg[i].u3[j]=tmp[j + 2*nn]; + gg[i].u4[j]=tmp[j + 3*nn]; + gg[i].u5[j]=tmp[j + 4*nn]; + gg[i].u6[j]=tmp[j + 5*nn] * (-1.0);/*Note the vertical GF's are*/ + gg[i].u7[j]=tmp[j + 6*nn] * (-1.0);/*flipped in earqt1.f and TW's*/ + gg[i].u8[j]=tmp[j + 7*nn] * (-1.0);/* Blackbox.f DVH conv. z + down*/ + if(isoflag==6) { + gg[i].u9[j]=tmp[j + 8*nn] * (-1.0); /*Radial exp*/ + gg[i].u10[j]=tmp[j + 9*nn] * (-1.0); /*Vertical exp*/ + } + if(isoflag==5) { + gg[i].u9[j]=0.0; + gg[i].u10[j]=0.0; + } + } + } + + /* Cross-Correlation to obtain zero shift*/ + for(i=0; i < nsta; i++) { + N =ss[i].np; + Np=ss[i].nn; + Z =ss[i].zz; + np2 = (int)(log10((float)N)/log10(2.0) + 0.5); + np2=2<<(np2-1); + if(Z == 0) + ss[i].zz=correlate(ss,gg,i,np2);/*Compute cross-correlation*/ + /*Return zshift for largest cor.*/ + } + + /*Construct distance 1/R weighting*/ + if(wflag==1 && (nsta >= 1)) { +#ifndef ONLY_ERRORS_TO_STDERR + fprintf(stderr,"Station Information\n"); +#endif + fprintf(out,"Station Information\n"); + l=0; + for(i=0; i < nsta; i++) { + cormax = ss[i].dist / mindist; +#ifndef ONLY_ERRORS_TO_STDERR + fprintf(stderr,"Station(%d): %s R=%.1fkm AZI=%.1f W=%.3f Zcor=%d\n", i, ss[i].name,ss[i].dist,ss[i].azi*180.0/PI,cormax,ss[i].zz); +#endif + fprintf(out,"Station(%d): %s R=%.1fkm AZI=%.1f W=%.3f Zcor=%d\n", i, ss[i].name,ss[i].dist,ss[i].azi*180.0/PI,cormax,ss[i].zz); + N = ss[i].nn; + for(j=0; j < 3*N; j++) + W[l++]=cormax; + } + } + + /* INVERSION ROUTINE */ + + for(i=0 ; i < isoflag ; i++) /*Normalize AtA and AIV matix*/ + for(l=0 ; l < isoflag ; l++) + AIV[i][l]=0.0; + + for(i=0 ; i < isoflag ; i++) + B[i][0]=0.0; + + cnt1=cnt2=cnt3=0; + for(i=0; i < nsta; i++) { + Np=ss[i].nn; + Z =gg[i].zz; + cnt1=cnt2 = cnt3; + cnt2 += Np; + cnt3 += 2*Np; + for(j=Z; j < Z+Np; j++) { /*Index over time*/ + /*Mxx term*/ + AJ[0][cnt1] = (double)(0.5*sin(2*ss[i].azi)*gg[i].u1[j]); + if(isoflag==6) + { + AJ[0][cnt2] = (double)(0.166667*gg[i].u5[j] - 0.5*cos(2*ss[i].azi)*gg[i].u3[j] + 0.33333*gg[i].u9[j]); + AJ[0][cnt3] = (double)(0.166667*gg[i].u8[j] - 0.5*cos(2*ss[i].azi)*gg[i].u6[j] + 0.33333*gg[i].u10[j]); + } + if(isoflag==5) + { + AJ[0][cnt2] = (double)(0.500000*gg[i].u5[j] - 0.5*cos(2*ss[i].azi)*gg[i].u3[j]); + AJ[0][cnt3] = (double)(0.500000*gg[i].u8[j] - 0.5*cos(2*ss[i].azi)*gg[i].u6[j]); + } + /*Myy term*/ + AJ[1][cnt1] = (double)((-0.5)*sin(2*ss[i].azi)*gg[i].u1[j]); + if(isoflag==6) + { + AJ[1][cnt2] = (double)(0.166667*gg[i].u5[j] + 0.5*cos(2*ss[i].azi)*gg[i].u3[j] + 0.33333*gg[i].u9[j]); + AJ[1][cnt3] = (double)(0.166667*gg[i].u8[j] + 0.5*cos(2*ss[i].azi)*gg[i].u6[j] + 0.33333*gg[i].u10[j]); + } + if(isoflag==5) + { + AJ[1][cnt2] = (double)(0.500000*gg[i].u5[j] + 0.5*cos(2*ss[i].azi)*gg[i].u3[j]); + AJ[1][cnt3] = (double)(0.500000*gg[i].u8[j] + 0.5*cos(2*ss[i].azi)*gg[i].u6[j]); + } + + /*Mxy term*/ + AJ[2][cnt1] = (double)((-1.0)*cos(2*ss[i].azi)*gg[i].u1[j]); + AJ[2][cnt2] = (double)((-1.0)*sin(2*ss[i].azi)*gg[i].u3[j]); + AJ[2][cnt3] = (double)((-1.0)*sin(2*ss[i].azi)*gg[i].u6[j]); + + /*Mxz term*/ + AJ[3][cnt1] = (double)((-1.0)*sin(ss[i].azi)*gg[i].u2[j]); + AJ[3][cnt2] = (double)( cos(ss[i].azi)*gg[i].u4[j]); + AJ[3][cnt3] = (double)( cos(ss[i].azi)*gg[i].u7[j]); + + /*Myz term*/ + AJ[4][cnt1] = (double)( cos(ss[i].azi)*gg[i].u2[j]); + AJ[4][cnt2] = (double)( sin(ss[i].azi)*gg[i].u4[j]); + AJ[4][cnt3] = (double)( sin(ss[i].azi)*gg[i].u7[j]); + + /*Mzz term*/ + if(isoflag==6) + { + AJ[5][cnt1] = (double)(0.0); + AJ[5][cnt2] = (double)(0.33333*gg[i].u9[j]-0.33333*gg[i].u5[j]); + AJ[5][cnt3] = (double)(0.33333*gg[i].u10[j]-0.3333*gg[i].u8[j]); + } + + + cnt1++; + cnt2++; + cnt3++; + } + } + + + for(i=0 ; i < isoflag ; i++) /*Compute AtA */ + for(j=0 ; j < isoflag ; j++) + for(k=0 ; k < cnt3 ; k++) + AIV[i][j] += AJ[i][k]* AJ[j][k] * (double)W[k]; + + + cnt1=cnt2=cnt3=0; + tmp=(float *)malloc(10*N*nn*sizeof(float)); + for(j=0; j < nsta; j++) { + l=0; + Z =ss[j].zz; + Np=ss[j].nn; + cnt1=cnt2 = cnt3; + cnt2 += Np; + cnt3 += 2*Np; + for(i=Z ; i < Np+Z ; i++) { + tmp[cnt1] = ss[j].t[i]; + tmp[cnt2] = ss[j].r[i]; + tmp[cnt3] = ss[j].z[i]; + cnt1++; + cnt2++; + cnt3++; + } + } + + for(i=0 ; i < isoflag ; i++) /* Calculate Righthand Side */ + for(j=0 ; j < cnt3 ; j++) + B[i][0] += AJ[i][j] * (double)tmp[j] * (double)W[j]; + + + minvdbl(AIV,B,isoflag,1); /* Determine Solution Vector */ + + M.mxx=(float)B[0][0]; + M.myy=(float)B[1][0]; + M.mxy=(float)B[2][0]; + M.mxz=(float)B[3][0]; + M.myz=(float)B[4][0]; +if(isoflag==6) + M.mzz=(float)B[5][0]; +if(isoflag==5) + M.mzz=-1.0*(M.mxx + M.myy); + + /*Call Bob's MT decomposition routines*/ + /*The minus one is needed to map Helmbergers convention into Aki's*/ + /*Jost and Hermann (1989) state that AKI's convention is -1*LANGSTONS*/ + +tracelang=(M.mxx+M.myy+M.mzz)/3; /*Trace of MT*/ + + d_mtrep=1; +/*Convert deviatoric moment tensor to AKI convention*/ +d_mt[0][0] = (double)(-1.0*gfscale*M.mxx); +d_mt[0][1] = (double)(-1.0*gfscale*M.mxy); +d_mt[0][2] = (double)(-1.0*gfscale*M.mxz); +d_mt[1][0] = (double)(-1.0*gfscale*M.mxy); +d_mt[1][1] = (double)(-1.0*gfscale*M.myy); +d_mt[1][2] = (double)(-1.0*gfscale*M.myz); +d_mt[2][0] = (double)(-1.0*gfscale*M.mxz); +d_mt[2][1] = (double)(-1.0*gfscale*M.myz); +d_mt[2][2] = (double)(-1.0*gfscale*M.mzz); + +fprintf(out,"isomoment=%g\n",tracelang*gfscale); + + + fprintf(out,"MT Aki Convention\n"); + fprintf(out,"Mxx=%.3f\nMxy=%.3f\nMxz=%.3f\nMyy=%.3f\nMyz=%.3f\nMzz=%.3f\n", + d_mt[0][0]/gfscale,d_mt[0][1]/gfscale,d_mt[0][2]/gfscale,d_mt[1][1]/gfscale, + d_mt[1][2]/gfscale,d_mt[2][2]/gfscale); + +/* moved down to also plot VR + fprintf(out2,"%.3f %.3f %.3f %.3f %.3f %.3f\n", + d_mt[0][0]/gfscale,d_mt[0][1]/gfscale,d_mt[0][2]/gfscale,d_mt[1][1]/gfscale, + d_mt[1][2]/gfscale,d_mt[2][2]/gfscale); +*/ + + m0dcf_(&d_mtrep,d_mf,d_mt,d_d,d_v,d_miso,d_mdc,d_mclvd, + &d_m0,&d_mw,d_axis,d_plunge,d_azimuth,d_strike,d_dip, + d_slip,&d_pcdc,&d_pcclvd,&d_pciso); + + eigen1=(float)d_d[0]; + eigen2=(float)d_d[1]; + eigen3=(float)d_d[2]; +fprintf(out,"eigen1=%g\n",eigen1); +fprintf(out,"eigen2=%g\n",eigen2); +fprintf(out,"eigen3=%g\n",eigen3); + eigaz1=(float)d_azimuth[0]; + eigaz2=(float)d_azimuth[1]; + eigaz3=(float)d_azimuth[2]; + eigpl1=(float)d_plunge[0]; + eigpl2=(float)d_plunge[1]; + eigpl3=(float)d_plunge[2]; + + Mo = (float) d_m0; + Mw = (float) d_mw; + Strike = (float) d_strike[0]; + Rake = (float) d_slip[0]; + Dip = (float) d_dip[0]; + St2 = (float) d_strike[1]; + Rk2 = (float) d_slip[1]; + Dp2 = (float) d_dip[1]; + Pdc = (float) d_pcdc; + Pclvd = (float) d_pcclvd; + Piso = (float) d_pciso; + isoMo = tracelang*gfscale; + fullMo = fabs(isoMo) + fabs(eigen3); + if(isoflag == 6) + Mw = (log10(fullMo)-16.05)*2/3; + +/*Code to Convert Percentages to those based on Bowers moment*/ +/*Fortran subroutines compute Dziewonski moment + isotropic*/ +Piso=fabs(isoMo)/fullMo*100; +F=-eigen1/eigen3; +Pdc=(fabs(eigen3)/fullMo)*fabs(1-2*F)*100; +Pclvd=(fabs(eigen3)/fullMo)*fabs(F)*200; +/*End Percent Decomposition Modification 01122016*/ + +#ifndef ONLY_ERRORS_TO_STDERR + fprintf(stderr,"isoMo: %g\n",isoMo); + fprintf(stderr,"Mo=%g (%g)\nMw=%.2f\nStrike=%.0f ; %.0f\nRake=%.0f ; %.0f\nDip=%.0f; %.0f\nPdc=%.0f\nPclvd=%.0f\nPiso=%.0f\n", + Mo,fullMo,Mw,Strike,St2,Rake,Rk2,Dip,Dp2,Pdc,Pclvd,Piso); +#endif + fprintf(out,"Mo=%g (%g)\nMw=%.2f\nStrike=%.0f ; %.0f\nRake=%.0f ; %.0f\nDip=%.0f ; %.0f\nPdc=%.0f\nPclvd=%.0f\nPiso=%.0f\n", + Mo,fullMo,Mw,Strike,St2,Rake,Rk2,Dip,Dp2,Pdc,Pclvd,Piso); + + fitcheck(ss,gg,W,M,Mo,nsta,isoflag,&E,&VR); /*Function to compute vr and flag bad stations*/ + +#ifdef TDMT_PLOT + if(plotflag==1) + mt_plot(ss,gg,nsta,Strike,Rake,Dip,St2,Rk2,Dp2,M,d_mt,Pdc,Pclvd,Piso,Mo,Mw,E,VR); +#endif + + fprintf(out,"Variance=%.3e\n",E); + fprintf(out,"VarRed=%.3e\n",VR); + + fprintf(out2,"%.2f %.3f %.3f %.3f %.3f %.3f %.3f\n", + VR,d_mt[0][0]/gfscale,d_mt[0][1]/gfscale,d_mt[0][2]/gfscale,d_mt[1][1]/gfscale, + d_mt[1][2]/gfscale,d_mt[2][2]/gfscale); + + +#ifndef ONLY_ERRORS_TO_STDERR + fprintf(stderr,"Var/Pdc=%.3e\n",E/Pdc); +#endif + fprintf(out,"Var/Pdc=%.3e\n",E/Pdc); + + if(VR < 20.0) QUAL=0; + if(VR > 20.0 && VR < 40.0) QUAL=1; + if(VR > 40.0 && VR < 60.0) QUAL=2; + if(VR > 60.0 && VR < 80.0) QUAL=3; + if(VR > 80.0) QUAL=4; + fprintf(out,"Quality=%d\n",QUAL); +#ifndef ONLY_ERRORS_TO_STDERR + fprintf(stderr,"Quality=%d\n",QUAL); +#endif + + if (fscanf(par, "%s\n", datfile) == 1) { + mt_dat(ss,gg,nsta,Strike,Rake,Dip,St2,Rk2,Dp2,M,-gfscale,Pdc,Pclvd,Piso,Mo,Mw,E,VR,depth,datfile); + } + fclose(par); + fclose(out); + + return 0; +} + + diff --git a/TDMT_GMT/tdmt_invc_iso_outputmtline.o b/TDMT_GMT/tdmt_invc_iso_outputmtline.o new file mode 100644 index 0000000..9a80793 Binary files /dev/null and b/TDMT_GMT/tdmt_invc_iso_outputmtline.o differ diff --git a/TDMT_GMT/tdmt_invc_iso_zcor.c b/TDMT_GMT/tdmt_invc_iso_zcor.c new file mode 100755 index 0000000..f039c80 --- /dev/null +++ b/TDMT_GMT/tdmt_invc_iso_zcor.c @@ -0,0 +1,448 @@ +/*Copyright (c) Douglas Dreger +Berkeley Seismological Laboratory +University of California, Berkeley */ +/* Moment Tensor Inversion */ +/* Uses previously determined Green's functions in D. Helmbergers format*/ + +#include +#include"tdmtinv_iso.h" + +void gaussj(); +int correlate(),fclose(); +void readhelm(); +void minvdbl(); +void m0dcf_(); +void fitcheck(); +int mt_dat(); + +FILE *fopen(), *par,*fd, *fd1, *fd2, *fd3, *out, *out2; + + +int main() +{ + int i,j,l,N,Np,Z,k,ntr,nsta,nn,*npts = NULL ,wflag=0; + int depth,cnt1, cnt2, cnt3, np2,QUAL,plotflag,WCNT; + float *W, *tmpp,*tmp,*dt, cormax, mindist, dist,E,VR,junk; + float tracelang; + float eigen1, eigen2, eigen3; + float eigaz1, eigaz2, eigaz3; + float eigpl1, eigpl2, eigpl3; + double **AIV,**AJ, **B; + struct MOMENT M; + struct DATA *ss; + struct GREEN *gg; + char infile[200],binfile[205],datfile[200]; + /**Variables needed for implementation of R Urhammer's MT decomposition**/ + /**Algorithms**/ + char d_axis[3]; + int d_mtrep; + double d_mf[6],d_mt[3][3],d_d[3],d_v[3][3],d_miso[3][3],d_mdc[3][3], + d_mclvd[3][3],d_m0,d_mw,d_plunge[3],d_azimuth[3],d_strike[3], + d_slip[3],d_dip[3],d_pcdc,d_pcclvd,d_pciso; + float gfscale=1.0e+20; /*Dyne cm*/ + /**************/ + float fullMo, Mo, isoMo, Mw, Strike, Rake, Dip, Pdc, Piso, Pclvd; + float St2, Rk2, Dp2; + int isoflag; + + if ( (par=fopen(MT_INFILE,"r")) == NULL) { + fprintf(stderr, "Error opening %s: %s\n", MT_INFILE, strerror(errno)); + exit(1); + } + + if ( (out=fopen(MT_OUTFILE,"a")) == NULL) { + fprintf(stderr, "Error opening %s: %s\n", MT_OUTFILE, strerror(errno)); + exit(1); + } + + if ( (out2=fopen("zcor_out.txt","a")) == NULL) { + fprintf(stderr, "Error opening zcor_out.txt\n"); + exit(1); + } + + fscanf(par,"%d %d %d %d %d\n",&nsta,&depth,&wflag,&isoflag,&plotflag); + M.isoflag=isoflag; + fprintf(out,"isoflag=%d Depth=%d\n",isoflag,depth); +#ifndef ONLY_ERRORS_TO_STDERR + fprintf(stderr,"isoflag=%d Depth=%d\n",isoflag,depth); +#endif + + AIV=(double **)malloc(sizeof(double *)*isoflag); + for(i=0 ; i < isoflag ; i++) + AIV[i]=(double *)malloc(sizeof(double)*isoflag); + + B=(double **)malloc(sizeof(double *)*isoflag); + for(i=0 ; i < isoflag; i++) + B[i]=(double *)malloc(sizeof(double)*1); + + ss=(struct DATA *)malloc(sizeof(struct DATA)*(nsta)); + gg=(struct GREEN *)malloc(sizeof(struct GREEN)*(nsta)); + + tmp=(float *)malloc(sizeof(float)*10*10000); + npts=(int *)malloc(sizeof(int)*10); + dt =(float *)malloc(sizeof(float)*10); + + mindist=100000.0; + WCNT=0; + ntr = 0; + for(i=0 ; i < nsta ; i++) + { + fscanf(par,"%s %f %f %d %d\n",infile,&dist,&(ss[i].azi),&Z,&Np); + WCNT += Np; + strcpy(ss[i].name,infile); + ss[i].azi *= PI/180.0; + + /* Read in seismic trace data */ + readhelm(infile,&ntr,npts,dt,tmp); + if (ntr != 3) { + fprintf(stderr, "read %d traces from %s; 3 required\n",ntr,infile); + exit(1); + } + /* We assume all the traces have the same time step and length; + That's why we have tdmt_redi_sched, to do this checking */ + nn=npts[0]; + + if(mindist > dist) mindist = dist; + ss[i].dist=dist; + ss[i].np=nn; + ss[i].dt=dt[0]; + ss[i].nn=Np; + ss[i].zz=Z; + + /*Allocate data structure arrays*/ + ss[i].t=(float *)malloc(sizeof(float)*nn); + ss[i].r=(float *)malloc(sizeof(float)*nn); + ss[i].z=(float *)malloc(sizeof(float)*nn); + + for(j=0 ; j < nn ; j++) + { + ss[i].t[j]=tmp[j]; + ss[i].r[j]=tmp[j + nn]; + ss[i].z[j]=tmp[j + 2*nn]; + } + } + + /*Allocate Memory for Station Weights*/ + WCNT *= 3; + W=(float *)malloc(sizeof(float)*WCNT); + for(j=0; j < WCNT ; j++) + W[j]=1.0; + + /*Allocate Memory for A matrix*/ + AJ=(double **)malloc(sizeof(double *)*isoflag); + for(j=0 ; j < isoflag ; j++) + AJ[j]=(double *)malloc(sizeof(double)*WCNT); + + /* Read in the Green's functions */ + ntr = 0; + for(i=0 ; i < nsta ; i++) { + fscanf(par,"%s %d %d\n",infile,&Z,&Np); + /*Pete's binary read + sprintf(binfile, "%s.bin", infile); + if (access(binfile, R_OK) == 0) + readbin(binfile, &ntr, &npts, &dt, &tmp); + else + */ + readhelm(infile,&ntr,npts,dt,tmp); + + if ( (isoflag == 5 && ntr < 8) || isoflag == 6 && ntr != 10) { + fprintf(stderr, "GF file %s has unexpected sector count: %d\n", + infile, ntr); + exit(1); + } + + nn=npts[0]; + + gg[i].np=nn; + gg[i].dt=dt[0]; + gg[i].nn=Np; + gg[i].zz=Z; + + /*Allocate Greens Function structure arrays*/ + gg[i].u1=(float *)malloc(sizeof(float)*nn); + gg[i].u2=(float *)malloc(sizeof(float)*nn); + gg[i].u3=(float *)malloc(sizeof(float)*nn); + gg[i].u4=(float *)malloc(sizeof(float)*nn); + gg[i].u5=(float *)malloc(sizeof(float)*nn); + gg[i].u6=(float *)malloc(sizeof(float)*nn); + gg[i].u7=(float *)malloc(sizeof(float)*nn); + gg[i].u8=(float *)malloc(sizeof(float)*nn); + gg[i].u9=(float *)malloc(sizeof(float)*nn); + gg[i].u10=(float *)malloc(sizeof(float)*nn); + + for(j=0 ; j < nn ; j++) { + gg[i].u1[j]=tmp[j]; + gg[i].u2[j]=tmp[j + nn]; + gg[i].u3[j]=tmp[j + 2*nn]; + gg[i].u4[j]=tmp[j + 3*nn]; + gg[i].u5[j]=tmp[j + 4*nn]; + gg[i].u6[j]=tmp[j + 5*nn] * (-1.0);/*Note the vertical GF's are*/ + gg[i].u7[j]=tmp[j + 6*nn] * (-1.0);/*flipped in earqt1.f and TW's*/ + gg[i].u8[j]=tmp[j + 7*nn] * (-1.0);/* Blackbox.f DVH conv. z + down*/ + if(isoflag==6) { + gg[i].u9[j]=tmp[j + 8*nn] * (-1.0); /*Radial exp*/ + gg[i].u10[j]=tmp[j + 9*nn] * (-1.0); /*Vertical exp*/ + } + if(isoflag==5) { + gg[i].u9[j]=0.0; + gg[i].u10[j]=0.0; + } + } + } + + /* Cross-Correlation to obtain zero shift*/ + for(i=0; i < nsta; i++) { + N =ss[i].np; + Np=ss[i].nn; + Z =ss[i].zz; + np2 = (int)(log10((float)N)/log10(2.0) + 0.5); + np2=2<<(np2-1); + if(Z == 0) + ss[i].zz=correlate(ss,gg,i,np2);/*Compute cross-correlation*/ + /*Return zshift for largest cor.*/ + } + + /*Construct distance 1/R weighting*/ + if(wflag==1 && (nsta >= 1)) { +#ifndef ONLY_ERRORS_TO_STDERR + fprintf(stderr,"Station Information\n"); +#endif + fprintf(out,"Station Information\n"); + l=0; + for(i=0; i < nsta; i++) { + cormax = ss[i].dist / mindist; +#ifndef ONLY_ERRORS_TO_STDERR + fprintf(stderr,"Station(%d): %s R=%.1fkm AZI=%.1f W=%.3f Zcor=%d\n", i, ss[i].name,ss[i].dist,ss[i].azi*180.0/PI,cormax,ss[i].zz); +#endif + fprintf(out,"Station(%d): %s R=%.1fkm AZI=%.1f W=%.3f Zcor=%d\n", i, ss[i].name,ss[i].dist,ss[i].azi*180.0/PI,cormax,ss[i].zz); + N = ss[i].nn; + for(j=0; j < 3*N; j++) + W[l++]=cormax; + } + } + + /* INVERSION ROUTINE */ + + for(i=0 ; i < isoflag ; i++) /*Normalize AtA and AIV matix*/ + for(l=0 ; l < isoflag ; l++) + AIV[i][l]=0.0; + + for(i=0 ; i < isoflag ; i++) + B[i][0]=0.0; + + cnt1=cnt2=cnt3=0; + for(i=0; i < nsta; i++) { + Np=ss[i].nn; + Z =gg[i].zz; + cnt1=cnt2 = cnt3; + cnt2 += Np; + cnt3 += 2*Np; + for(j=Z; j < Z+Np; j++) { /*Index over time*/ + /*Mxx term*/ + AJ[0][cnt1] = (double)(0.5*sin(2*ss[i].azi)*gg[i].u1[j]); + if(isoflag==6) + { + AJ[0][cnt2] = (double)(0.166667*gg[i].u5[j] - 0.5*cos(2*ss[i].azi)*gg[i].u3[j] + 0.33333*gg[i].u9[j]); + AJ[0][cnt3] = (double)(0.166667*gg[i].u8[j] - 0.5*cos(2*ss[i].azi)*gg[i].u6[j] + 0.33333*gg[i].u10[j]); + } + if(isoflag==5) + { + AJ[0][cnt2] = (double)(0.500000*gg[i].u5[j] - 0.5*cos(2*ss[i].azi)*gg[i].u3[j]); + AJ[0][cnt3] = (double)(0.500000*gg[i].u8[j] - 0.5*cos(2*ss[i].azi)*gg[i].u6[j]); + } + /*Myy term*/ + AJ[1][cnt1] = (double)((-0.5)*sin(2*ss[i].azi)*gg[i].u1[j]); + if(isoflag==6) + { + AJ[1][cnt2] = (double)(0.166667*gg[i].u5[j] + 0.5*cos(2*ss[i].azi)*gg[i].u3[j] + 0.33333*gg[i].u9[j]); + AJ[1][cnt3] = (double)(0.166667*gg[i].u8[j] + 0.5*cos(2*ss[i].azi)*gg[i].u6[j] + 0.33333*gg[i].u10[j]); + } + if(isoflag==5) + { + AJ[1][cnt2] = (double)(0.500000*gg[i].u5[j] + 0.5*cos(2*ss[i].azi)*gg[i].u3[j]); + AJ[1][cnt3] = (double)(0.500000*gg[i].u8[j] + 0.5*cos(2*ss[i].azi)*gg[i].u6[j]); + } + + /*Mxy term*/ + AJ[2][cnt1] = (double)((-1.0)*cos(2*ss[i].azi)*gg[i].u1[j]); + AJ[2][cnt2] = (double)((-1.0)*sin(2*ss[i].azi)*gg[i].u3[j]); + AJ[2][cnt3] = (double)((-1.0)*sin(2*ss[i].azi)*gg[i].u6[j]); + + /*Mxz term*/ + AJ[3][cnt1] = (double)((-1.0)*sin(ss[i].azi)*gg[i].u2[j]); + AJ[3][cnt2] = (double)( cos(ss[i].azi)*gg[i].u4[j]); + AJ[3][cnt3] = (double)( cos(ss[i].azi)*gg[i].u7[j]); + + /*Myz term*/ + AJ[4][cnt1] = (double)( cos(ss[i].azi)*gg[i].u2[j]); + AJ[4][cnt2] = (double)( sin(ss[i].azi)*gg[i].u4[j]); + AJ[4][cnt3] = (double)( sin(ss[i].azi)*gg[i].u7[j]); + + /*Mzz term*/ + if(isoflag==6) + { + AJ[5][cnt1] = (double)(0.0); + AJ[5][cnt2] = (double)(0.33333*gg[i].u9[j]-0.33333*gg[i].u5[j]); + AJ[5][cnt3] = (double)(0.33333*gg[i].u10[j]-0.3333*gg[i].u8[j]); + } + + + cnt1++; + cnt2++; + cnt3++; + } + } + + + for(i=0 ; i < isoflag ; i++) /*Compute AtA */ + for(j=0 ; j < isoflag ; j++) + for(k=0 ; k < cnt3 ; k++) + AIV[i][j] += AJ[i][k]* AJ[j][k] * (double)W[k]; + + + cnt1=cnt2=cnt3=0; + tmp=(float *)malloc(10*N*nn*sizeof(float)); + for(j=0; j < nsta; j++) { + l=0; + Z =ss[j].zz; + Np=ss[j].nn; + cnt1=cnt2 = cnt3; + cnt2 += Np; + cnt3 += 2*Np; + for(i=Z ; i < Np+Z ; i++) { + tmp[cnt1] = ss[j].t[i]; + tmp[cnt2] = ss[j].r[i]; + tmp[cnt3] = ss[j].z[i]; + cnt1++; + cnt2++; + cnt3++; + } + } + + for(i=0 ; i < isoflag ; i++) /* Calculate Righthand Side */ + for(j=0 ; j < cnt3 ; j++) + B[i][0] += AJ[i][j] * (double)tmp[j] * (double)W[j]; + + + minvdbl(AIV,B,isoflag,1); /* Determine Solution Vector */ + + M.mxx=(float)B[0][0]; + M.myy=(float)B[1][0]; + M.mxy=(float)B[2][0]; + M.mxz=(float)B[3][0]; + M.myz=(float)B[4][0]; +if(isoflag==6) + M.mzz=(float)B[5][0]; +if(isoflag==5) + M.mzz=-1.0*(M.mxx + M.myy); + + /*Call Bob's MT decomposition routines*/ + /*The minus one is needed to map Helmbergers convention into Aki's*/ + /*Jost and Hermann (1989) state that AKI's convention is -1*LANGSTONS*/ + +tracelang=(M.mxx+M.myy+M.mzz)/3; /*Trace of MT*/ + + d_mtrep=1; +/*Convert deviatoric moment tensor to AKI convention*/ +d_mt[0][0] = (double)(-1.0*gfscale*M.mxx); +d_mt[0][1] = (double)(-1.0*gfscale*M.mxy); +d_mt[0][2] = (double)(-1.0*gfscale*M.mxz); +d_mt[1][0] = (double)(-1.0*gfscale*M.mxy); +d_mt[1][1] = (double)(-1.0*gfscale*M.myy); +d_mt[1][2] = (double)(-1.0*gfscale*M.myz); +d_mt[2][0] = (double)(-1.0*gfscale*M.mxz); +d_mt[2][1] = (double)(-1.0*gfscale*M.myz); +d_mt[2][2] = (double)(-1.0*gfscale*M.mzz); + +fprintf(out,"isomoment=%g\n",tracelang*gfscale); + + + fprintf(out,"MT Aki Convention\n"); + fprintf(out,"Mxx=%.3f\nMxy=%.3f\nMxz=%.3f\nMyy=%.3f\nMyz=%.3f\nMzz=%.3f\n", + d_mt[0][0]/gfscale,d_mt[0][1]/gfscale,d_mt[0][2]/gfscale,d_mt[1][1]/gfscale, + d_mt[1][2]/gfscale,d_mt[2][2]/gfscale); + + m0dcf_(&d_mtrep,d_mf,d_mt,d_d,d_v,d_miso,d_mdc,d_mclvd, + &d_m0,&d_mw,d_axis,d_plunge,d_azimuth,d_strike,d_dip, + d_slip,&d_pcdc,&d_pcclvd,&d_pciso); + + eigen1=(float)d_d[0]; + eigen2=(float)d_d[1]; + eigen3=(float)d_d[2]; +fprintf(out,"eigen1=%g\n",eigen1); +fprintf(out,"eigen2=%g\n",eigen2); +fprintf(out,"eigen3=%g\n",eigen3); + eigaz1=(float)d_azimuth[0]; + eigaz2=(float)d_azimuth[1]; + eigaz3=(float)d_azimuth[2]; + eigpl1=(float)d_plunge[0]; + eigpl2=(float)d_plunge[1]; + eigpl3=(float)d_plunge[2]; + + Mo = (float) d_m0; + Mw = (float) d_mw; + Strike = (float) d_strike[0]; + Rake = (float) d_slip[0]; + Dip = (float) d_dip[0]; + St2 = (float) d_strike[1]; + Rk2 = (float) d_slip[1]; + Dp2 = (float) d_dip[1]; + Pdc = (float) d_pcdc; + Pclvd = (float) d_pcclvd; + Piso = (float) d_pciso; + isoMo = tracelang*gfscale; + fullMo = fabs(isoMo) + fabs(eigen3); + if(isoflag == 6) + Mw = (log10(fullMo)-16.05)*2/3; + +#ifndef ONLY_ERRORS_TO_STDERR + fprintf(stderr,"isoMo: %g\n",isoMo); + fprintf(stderr,"Mo=%g (%g)\nMw=%.2f\nStrike=%.0f ; %.0f\nRake=%.0f ; %.0f\nDip=%.0f; %.0f\nPdc=%.0f\nPclvd=%.0f\nPiso=%.0f\n", + Mo,fullMo,Mw,Strike,St2,Rake,Rk2,Dip,Dp2,Pdc,Pclvd,Piso); +#endif + fprintf(out,"Mo=%g (%g)\nMw=%.2f\nStrike=%.0f ; %.0f\nRake=%.0f ; %.0f\nDip=%.0f ; %.0f\nPdc=%.0f\nPclvd=%.0f\nPiso=%.0f\n", + Mo,fullMo,Mw,Strike,St2,Rake,Rk2,Dip,Dp2,Pdc,Pclvd,Piso); + + fitcheck(ss,gg,W,M,Mo,nsta,isoflag,&E,&VR); /*Function to compute vr and flag bad stations*/ + +#ifdef TDMT_PLOT + if(plotflag==1) + mt_plot(ss,gg,nsta,Strike,Rake,Dip,St2,Rk2,Dp2,M,d_mt,Pdc,Pclvd,Piso,Mo,Mw,E,VR); +#endif + + fprintf(out,"Variance=%.3e\n",E); + fprintf(out,"VarRed=%.3e\n",VR); +#ifndef ONLY_ERRORS_TO_STDERR + fprintf(stderr,"Var/Pdc=%.3e\n",E/Pdc); +#endif + fprintf(out,"Var/Pdc=%.3e\n",E/Pdc); + + if(VR < 20.0) QUAL=0; + if(VR > 20.0 && VR < 40.0) QUAL=1; + if(VR > 40.0 && VR < 60.0) QUAL=2; + if(VR > 60.0 && VR < 80.0) QUAL=3; + if(VR > 80.0) QUAL=4; + fprintf(out,"Quality=%d\n",QUAL); +#ifndef ONLY_ERRORS_TO_STDERR + fprintf(stderr,"Quality=%d\n",QUAL); +#endif + + if (fscanf(par, "%s\n", datfile) == 1) { + mt_dat(ss,gg,nsta,Strike,Rake,Dip,St2,Rk2,Dp2,M,-gfscale,Pdc,Pclvd,Piso,Mo,Mw,E,VR,depth,datfile); + } + +/*Added to write station specific zcor, vr and global vr file*/ +for (i=0; i < nsta; i++) + { + fprintf(out2,"%d %.2f ",ss[i].zz,ss[i].vr); + } + fprintf(out2,"%.2f\n",VR); + + fclose(par); + fclose(out); + fclose(out2); + + return 0; +} + + diff --git a/TDMT_GMT/tdmt_invc_iso_zcor.o b/TDMT_GMT/tdmt_invc_iso_zcor.o new file mode 100644 index 0000000..5fe3155 Binary files /dev/null and b/TDMT_GMT/tdmt_invc_iso_zcor.o differ diff --git a/TDMT_GMT/tdmtinv_iso.h b/TDMT_GMT/tdmtinv_iso.h new file mode 100755 index 0000000..2f1c644 --- /dev/null +++ b/TDMT_GMT/tdmtinv_iso.h @@ -0,0 +1,74 @@ +#include +#include +#include +#include +#include +#include + +#define PI 3.1415927 +#define MT_INFILE "mt_inv.in" +#define MT_OUTFILE "mt_inv.out" + +struct MOMENT + { + float mxx; + float myy; + float mxy; + float mxz; + float myz; + float mzz; + int isoflag; + }; + +struct GREEN + { + int zz,nn; + int np; + float dt; + float *u1; /* Tangential Strikeslip */ + float *u2; /* Tangential Dipslip */ + float *u3; /* Radial Strikeslip */ + float *u4; /* Radial Dipslip */ + float *u5; /* Radial 45_slip */ + float *u6; /* Vertical Strikeslip */ + float *u7; /* Vertical Dipslip */ + float *u8; /* Vertical 45_slip */ + float *u9; /* radial explosion */ + float *u10; /* Vertical explosion */ + }; + +struct DATA + { + int zz,nn; + int np; + float dt; + float *t; /* Tangential */ + float *r; /* Radial */ + float *z; /* Vertical */ + float dist; + float azi; + float vr; + char name[250]; + }; + +void correl(); +void realft(); +void twofft(); +void four1(); +void nrerror(); +float *vector(); +int *ivector(); +double *dvector(); +float **matrix(); +double **dmatrix(); +int **imatrix(); +float **submatrix(); +void free_vector(); +void free_ivector(); +void free_dvector(); +void free_matrix(); +void free_dmatrix(); +void free_imatrix(); +void free_submatrix(); +float **convert_matrix(); +void free_convert_matrix(); diff --git a/TDMT_GMT/trigd.mod b/TDMT_GMT/trigd.mod new file mode 100644 index 0000000..84fc3b2 Binary files /dev/null and b/TDMT_GMT/trigd.mod differ diff --git a/UTILITIES/.DS_Store b/UTILITIES/.DS_Store new file mode 100644 index 0000000..917548c Binary files /dev/null and b/UTILITIES/.DS_Store differ diff --git a/UTILITIES/b2s.par b/UTILITIES/b2s.par new file mode 100755 index 0000000..34d3499 --- /dev/null +++ b/UTILITIES/b2s.par @@ -0,0 +1,18 @@ +npts=512 +dt=0.50 +stime=5.0 +year=1993 +jday=016 +hour=6 +min=29 +sec=0 +msec=0 +ename="Synth" +#sname="PAS" +#dist=21.0 +#azi=180.0 +#bazi=0.0 +#stla=35.30 +#stlo=-116.81 +#evla=38.05 +#evlo=-119.12 diff --git a/UTILITIES/bin2sac.c b/UTILITIES/bin2sac.c new file mode 100755 index 0000000..f6eae57 --- /dev/null +++ b/UTILITIES/bin2sac.c @@ -0,0 +1,93 @@ +#include +#include +#include +#include +#include + +void setpar(); +int getpar(); +int mstpar(); +void endpar(); + +/*convert headerless binary file (fromHelm.c generated) to SAC binary file */ +int main(ac,av) + int ac; + char **av; + { + int i, npts, year, jday, hour, min, sec, msec, ihd[40]; + float dt, B=0.0, E, fhd[70], *tr, evla, evlo, stla, stlo, dist; + float azi, bazi, cmpaz, cmpinc; +/* char chd[8][24], ename[17], sname[9];*/ +/* Seung-Hoon Yoo: 2012/10/23 */ + char chd[8][26], ename[17], sname[9]; + +/* Initialize Header */ +for(i=0;i<40;i++) ihd[i]=-12345; +for(i=0;i<70;i++) fhd[i]=-12345.00; +for(i=0;i<8;i++) sprintf(chd[i],"-12345 -12345 -12345 ") ; +sprintf(ename,"-12345 -12345 "); +sprintf(sname,"-12345 "); + +/* Set Essential Parameters */ +ihd[35]=1; /*Sets file to evenly spaced*/ +ihd[15]=1; /*Sets file type to Timeseries*/ +ihd[6]=6; /*Variable Name Internal */ + + +setpar(ac,av); +mstpar("npts","d",&npts); + ihd[9]=npts; +mstpar("dt","f",&dt); + fhd[0]=dt; +if(getpar("stime","f",&B)) + fhd[5]=B; +if(getpar("year","d",&year)) + ihd[0]=year; +if(getpar("jday","d",&jday)) + ihd[1]=jday; +if(getpar("hour","d",&hour)) + ihd[2]=hour; +if(getpar("min","d",&min)) + ihd[3]=min; +if(getpar("sec","d",&sec)) + ihd[4]=sec; +if(getpar("msec","d",&msec)) + ihd[5]=msec; + +getpar("ename","s",ename); /*Event name*/ +getpar("sname","s",sname); /*Station name*/ + +if(getpar("cmpaz","f",&cmpaz)) + fhd[57]=cmpaz; +if(getpar("cmpinc","f",&cmpinc)) + fhd[58]=cmpinc; + +if(getpar("stla","f",&stla)) /*Station lat and lon.*/ + { + fhd[31]=stla; + fprintf(stderr,"%f\n",fhd[31]); + } +if(getpar("stlo","f",&stlo)) + fhd[32]=stlo; +if(getpar("evla","f",&evla)) /*Event lat and lon.*/ + fhd[35]=evla; +if(getpar("evlo","f",&evlo)) + fhd[36]=evlo; +if(getpar("dist","f",&dist)) + fhd[50]=dist; +if(getpar("azi","f",&azi)) + fhd[51]=azi; +if(getpar("bazi","f",&bazi)) + fhd[52]=bazi; +endpar(); +sprintf(chd[0],"%-8s%-16s",sname,ename); + +fhd[6]=B+fhd[0]*(ihd[9]-1); /*Set E variable*/ +tr=(float *)malloc(sizeof(float)*npts); +read(0,tr,npts*sizeof(float)); + +write(1,fhd,70*4); /*Write Sac Float Field*/ +write(1,ihd,40*4); /*Write Sac Int Field*/ +write(1,chd,24*8); /*Write Sac Char. Field*/ +write(1,tr,ihd[9]*4); /*Write timeseries file*/ +} diff --git a/UTILITIES/bin2sac_old.c b/UTILITIES/bin2sac_old.c new file mode 100755 index 0000000..f7cd97f --- /dev/null +++ b/UTILITIES/bin2sac_old.c @@ -0,0 +1,87 @@ +#include +#include +#include +#include + +/*convert headerless binary file (fromHelm.c generated) to SAC binary file */ +main(ac,av) + int ac; + char **av; + { + int i, npts, year, jday, hour, min, sec, msec, ihd[40]; + float dt, B=0.0, E, fhd[70], *tr, evla, evlo, stla, stlo, dist; + float azi, bazi, cmpaz, cmpinc; + char chd[8][24], ename[17], sname[9]; + + +/* Initialize Header */ +for(i=0;i<40;i++) ihd[i]=-12345; +for(i=0;i<70;i++) fhd[i]=-12345.00; +for(i=0;i<8;i++) sprintf(chd[i],"-12345 -12345 -12345 "); +sprintf(ename,"-12345 -12345 "); +sprintf(sname,"-12345 "); + +/* Set Essential Parameters */ +ihd[35]=1; /*Sets file to evenly spaced*/ +ihd[15]=1; /*Sets file type to Timeseries*/ +ihd[6]=6; /*Variable Name Internal */ + + +setpar(ac,av); +mstpar("npts","d",&npts); + ihd[9]=npts; +mstpar("dt","f",&dt); + fhd[0]=dt; +if(getpar("stime","f",&B)) + fhd[5]=B; +if(getpar("year","d",&year)) + ihd[0]=year; +if(getpar("jday","d",&jday)) + ihd[1]=jday; +if(getpar("hour","d",&hour)) + ihd[2]=hour; +if(getpar("min","d",&min)) + ihd[3]=min; +if(getpar("sec","d",&sec)) + ihd[4]=sec; +if(getpar("msec","d",&msec)) + ihd[5]=msec; + +getpar("ename","s",ename); /*Event name*/ +getpar("sname","s",sname); /*Station name*/ + +if(getpar("cmpaz","f",&cmpaz)) + fhd[57]=cmpaz; +if(getpar("cmpinc","f",&cmpinc)) + fhd[58]=cmpinc; + +if(getpar("stla","f",&stla)) /*Station lat and lon.*/ + { + fhd[31]=stla; + fprintf(stderr,"%f\n",fhd[31]); + } +if(getpar("stlo","f",&stlo)) + fhd[32]=stlo; +if(getpar("evla","f",&evla)) /*Event lat and lon.*/ + fhd[35]=evla; +if(getpar("evlo","f",&evlo)) + fhd[36]=evlo; +if(getpar("dist","f",&dist)) + fhd[50]=dist; +if(getpar("azi","f",&azi)) + fhd[51]=azi; +if(getpar("bazi","f",&bazi)) + fhd[52]=bazi; +endpar(); +sprintf(chd[0],"%-8s%-16s",sname,ename); +fhd[6]=B+fhd[0]*(ihd[9]-1); /*Set E variable*/ +tr=(float *)malloc(sizeof(float)*npts); +read(0,tr,npts*sizeof(float)); + +write(1,fhd,70*4); /*Write Sac Float Field*/ +write(1,ihd,40*4); /*Write Sac Int Field*/ +write(1,chd,24*8); /*Write Sac Char. Field*/ +write(1,tr,ihd[9]*4); /*Write timeseries file*/ +} + + diff --git a/UTILITIES/cwrite.c b/UTILITIES/cwrite.c new file mode 100755 index 0000000..a868ada --- /dev/null +++ b/UTILITIES/cwrite.c @@ -0,0 +1,17 @@ +#include +#include +#include + +cwrite_(data,n,nd,l) + float *data; + int *n,*nd,*l; + { + int bytes,fd; + bytes=*n; + if(*nd==1 && *l==1) + fd=open("vec",O_WRONLY | O_CREAT | O_TRUNC,0644); + else + fd=open("vec",O_WRONLY | O_CREAT | O_APPEND,0644); + write(fd,data,bytes*sizeof(float)); + close(fd); + } diff --git a/UTILITIES/declarations.h b/UTILITIES/declarations.h new file mode 100644 index 0000000..5a94359 --- /dev/null +++ b/UTILITIES/declarations.h @@ -0,0 +1,11 @@ +/*Header for function declaration*/ +void setpar(); +int getpar(); +int mstpar(); +void endpar(); +void gp_add_entry(); +void gp_do_par_file(); +int gp_close_dump(); +int gp_getpar_err(); +int gp_getvector(); +int gp_compute_hash(); diff --git a/UTILITIES/deconnew.c b/UTILITIES/deconnew.c new file mode 100755 index 0000000..13b6b07 --- /dev/null +++ b/UTILITIES/deconnew.c @@ -0,0 +1,109 @@ +#include +#include +#include +#include +#include +#include + +void setpar(); +int getpar(); +int mstpar(); +void endpar(); +void convlv(); + + +int main(ac,av) + int ac; + char **av; + { + int i, npts1, npts2, N, test, ihd[40], ihdsave[40],fd1, fd2, fd3, conv=-1; + float dt,dt1, dt2, fhd[70], fhdsave[70], h2olev=0.01; + float tau=0.0, *tmp,*outtrace; + double *tr1, *tr2, *ans; + char chd[8][24], chdsave[8][24], in1[100], in2[100], out[100]; + +setpar(ac,av); +mstpar("in1","s",in1); +mstpar("in2","s",in2); +mstpar("out","s",out); +getpar("h2olev","f",&h2olev); +getpar("conv","d",&conv); +getpar("tau","f",&tau); +endpar(); + +fd1=open(in1,O_RDONLY,0644); +fd2=open(in2,O_RDONLY,0644); +fd3=open(out,O_CREAT | O_TRUNC | O_WRONLY,0644); + +read(fd1,fhdsave,70*4); /*Read Sac Float Field*/ +read(fd1,ihdsave,40*4); /*Read Sac Int Field*/ +read(fd1,chdsave,24*8); /*Read Sac Char. Field*/ +npts1=ihdsave[9]; +dt1=fhdsave[0]; +fprintf(stderr,"npts=%d dt=%f\n",npts1,dt1); + +read(fd2,fhd,70*4); /*Read Sac Float Field*/ +read(fd2,ihd,40*4); /*Read Sac Int Field*/ +read(fd2,chd,24*8); /*Read Sac Char. Field*/ +npts2=ihd[9]; +if((npts2 % 2) == 0) + { + npts2 -= 1; + fprintf(stderr,"Resp points changed to %d\n",npts2); + } +dt2=fhd[0]; +fprintf(stderr,"npts=%d dt=%f\n",npts2,dt2); + +if(dt1 != dt2) + { + fprintf(stderr,"DECON ERROR dt not equal\n"); + exit(-1); + } + dt=dt1; + +if (2*npts2 > npts1) + test=2*npts2; +else + test=npts1; + +N=(int)(log10((float)test)/log10(2.0) + 2.0); +N=pow(2.0,(float)N); +/* +tau=(float)npts1/2.0*dt1; +*/ +fprintf(stderr,"npts1=%d 2*npts2=%d N=%d\n",npts1,2*npts2, N); + + +tr1=(double *)malloc(sizeof(double)*(N+1)); +tr2=(double *)malloc(sizeof(double)*(N+1)); +tmp=(float *)malloc(sizeof(float)*(N+1)); +for(i=0; i < N; i++) + tr1[i]=tr2[i]=tmp[i]=0.0; +ans=(double *)malloc(sizeof(double)*2*(N+1)); +outtrace=(float *)malloc(sizeof(float)*2*(N+1)); +read(fd1,tmp,npts1*sizeof(float)); +for(i=0; i +#include +#include + +static float sqrarg; +#define SQR(a) (sqrarg=(a),sqrarg*sqrarg) +#define ABS(x) ((x) >= 0.0 ? (x) : -(x)) + +void convlv(data,n,respns,m,isign,ans,h2olev,tau,dt) +float data[],respns[],ans[],h2olev,tau,dt; +int n,m,isign; +{ + int i,no2,j,cnt; + float dum,mag2,*fft,*vector(),respmax=0.0,*tmp; + float PI=3.14159,real,imag; + void four1(),twofft(),realft(),nrerror(),free_vector(); + + fprintf(stderr,"n=%d\n",n); + + fft=vector(1,2*n); + tmp=vector(1,2*n); + for (i=1;i<=(m-1)/2;i++) + respns[n+1-i]=respns[m+1-i]; + for (i=(m+3)/2;i<=n-(m-1)/2;i++) + respns[i]=0.0; + twofft(data,respns,fft,ans,n); + + +/* This Block is added for 10 percent water level*/ + if(isign == -1) /*If deconvolving*/ + { + fprintf(stderr,"%.3f Water Level Applied to Response Spectrum\n",h2olev); + cnt=0; + for(i=1; i<=n+1; i++) + if(ABS(ans[i]) > respmax) + { + respmax=ABS(ans[i]); + cnt++; + } + fprintf(stderr,"respmax=%g occurred at frequency %d\n",respmax,cnt); + respmax *= h2olev; + fprintf(stderr,"respmax=%g\n",respmax); + } + + +/*Perform Deconvolution Convolution*/ + no2=n/2; + for (i=2;i<=n+2;i+=2) { + if (isign == 1) { + ans[i-1]=(fft[i-1]*(dum=ans[i-1])-fft[i]*ans[i])/no2; + ans[i]=(fft[i]*dum+fft[i-1]*ans[i])/no2; + } else if (isign == -1) { + if ((mag2=SQR(ans[i-1])+SQR(ans[i])) == 0.0) + nrerror("Deconvolving at response zero in CONVLV"); + /*added for H2O level*/ + if ((mag2=SQR(ans[i-1])+SQR(ans[i])) < 2*respmax*respmax) + mag2=2*respmax*respmax; + + + ans[i-1]=(fft[i-1]*(dum=ans[i-1])+fft[i]*ans[i])/mag2/no2; + ans[i]=(fft[i]*dum-fft[i-1]*ans[i])/mag2/no2; + } else nrerror("No meaning for ISIGN in CONVLV"); + } + + if(isign == -1) + { + fprintf(stderr,"Tau=%g\n",tau); + for(i=2; i <= n+2; i+=2) + { + real=ans[i-1]; + imag=ans[i]; + ans[i-1]=real*cos((i-2)*PI/dt/n*tau)-imag*sin((i-2)*PI/dt/n*tau); + ans[i]=imag*cos((i-2)*PI/dt/n*tau)+real*sin((i-2)*PI/dt/n*tau); + } + } + + ans[2]=ans[n+1]; + realft(ans,no2,-1); + free_vector(fft,1,2*n); +} + +#undef SQR + +void twofft(data1,data2,fft1,fft2,n) +float data1[],data2[],fft1[],fft2[]; +int n; +{ + int nn3,nn2,jj,j; + float rep,rem,aip,aim; + void four1(); + + nn3=1+(nn2=2+n+n); + for (j=1,jj=2;j<=n;j++,jj+=2) { + fft1[jj-1]=data1[j]; + fft1[jj]=data2[j]; + } + four1(fft1,n,1); + fft2[1]=fft1[2]; + fft1[2]=fft2[2]=0.0; + for (j=3;j<=n+1;j+=2) { + rep=0.5*(fft1[j]+fft1[nn2-j]); + rem=0.5*(fft1[j]-fft1[nn2-j]); + aip=0.5*(fft1[j+1]+fft1[nn3-j]); + aim=0.5*(fft1[j+1]-fft1[nn3-j]); + fft1[j]=rep; + fft1[j+1]=aim; + fft1[nn2-j]=rep; + fft1[nn3-j] = -aim; + fft2[j]=aip; + fft2[j+1] = -rem; + fft2[nn2-j]=aip; + fft2[nn3-j]=rem; + } +} + +void realft(data,n,isign) +float data[]; +int n,isign; +{ + int i,i1,i2,i3,i4,n2p3; + float c1=0.5,c2,h1r,h1i,h2r,h2i; + double wr,wi,wpr,wpi,wtemp,theta; + void four1(); + + theta=3.141592653589793/(double) n; + if (isign == 1) { + c2 = -0.5; + four1(data,n,1); + } else { + c2=0.5; + theta = -theta; + } + wtemp=sin(0.5*theta); + wpr = -2.0*wtemp*wtemp; + wpi=sin(theta); + wr=1.0+wpr; + wi=wpi; + n2p3=2*n+3; + for (i=2;i<=n/2;i++) { + i4=1+(i3=n2p3-(i2=1+(i1=i+i-1))); + h1r=c1*(data[i1]+data[i3]); + h1i=c1*(data[i2]-data[i4]); + h2r = -c2*(data[i2]+data[i4]); + h2i=c2*(data[i1]-data[i3]); + data[i1]=h1r+wr*h2r-wi*h2i; + data[i2]=h1i+wr*h2i+wi*h2r; + data[i3]=h1r-wr*h2r+wi*h2i; + data[i4] = -h1i+wr*h2i+wi*h2r; + wr=(wtemp=wr)*wpr-wi*wpi+wr; + wi=wi*wpr+wtemp*wpi+wi; + } + if (isign == 1) { + data[1] = (h1r=data[1])+data[2]; + data[2] = h1r-data[2]; + } else { + data[1]=c1*((h1r=data[1])+data[2]); + data[2]=c1*(h1r-data[2]); + four1(data,n,-1); + } +} + +#define SWAP(a,b) tempr=(a);(a)=(b);(b)=tempr + +void four1(data,nn,isign) +float data[]; +int nn,isign; +{ + int n,mmax,m,j,istep,i; + double wtemp,wr,wpr,wpi,wi,theta; + float tempr,tempi; + + n=nn << 1; + j=1; + for (i=1;i i) { + SWAP(data[j],data[i]); + SWAP(data[j+1],data[i+1]); + } + m=n >> 1; + while (m >= 2 && j > m) { + j -= m; + m >>= 1; + } + j += m; + } + mmax=2; + while (n > mmax) { + istep=2*mmax; + theta=6.28318530717959/(isign*mmax); + wtemp=sin(0.5*theta); + wpr = -2.0*wtemp*wtemp; + wpi=sin(theta); + wr=1.0; + wi=0.0; + for (m=1;m=nrl;i--) free((char*) (m[i]+ncl)); + free((char*) (m+nrl)); +} + +void free_dmatrix(m,nrl,nrh,ncl,nch) +double **m; +int nrl,nrh,ncl,nch; +{ + int i; + + for(i=nrh;i>=nrl;i--) free((char*) (m[i]+ncl)); + free((char*) (m+nrl)); +} + +void free_imatrix(m,nrl,nrh,ncl,nch) +int **m; +int nrl,nrh,ncl,nch; +{ + int i; + + for(i=nrh;i>=nrl;i--) free((char*) (m[i]+ncl)); + free((char*) (m+nrl)); +} + + + +void free_submatrix(b,nrl,nrh,ncl,nch) +float **b; +int nrl,nrh,ncl,nch; +{ + free((char*) (b+nrl)); +} + + + +float **convert_matrix(a,nrl,nrh,ncl,nch) +float *a; +int nrl,nrh,ncl,nch; +{ + int i,j,nrow,ncol; + float **m; + + nrow=nrh-nrl+1; + ncol=nch-ncl+1; + m = (float **) malloc((unsigned) (nrow)*sizeof(float*)); + if (!m) nrerror("allocation failure in convert_matrix()"); + m -= nrl; + for(i=0,j=nrl;i<=nrow-1;i++,j++) m[j]=a+ncol*i-ncl; + return m; +} + + + +void free_convert_matrix(b,nrl,nrh,ncl,nch) +float **b; +int nrl,nrh,ncl,nch; +{ + free((char*) (b+nrl)); +} diff --git a/UTILITIES/deconnew_subs2.c b/UTILITIES/deconnew_subs2.c new file mode 100755 index 0000000..06b16f8 --- /dev/null +++ b/UTILITIES/deconnew_subs2.c @@ -0,0 +1,423 @@ +#include +#include +#include +#include + +static float sqrarg; +#define SQR(a) (sqrarg=(a),sqrarg*sqrarg) +#define ABS(x) ((x) >= 0.0 ? (x) : -(x)) + +void convlv(data,n,respns,m,isign,ans,h2olev,tau,dt) +float h2olev,tau,dt; +double data[],respns[],ans[]; +int n,m,isign; +{ + int i,no2,j,cnt; + double dum,mag2,*fft,*dvector(),respmax=0.0,*tmp; + double PI=3.14159,real,imag; + void four1(),twofft(),realft(),nrerror(),free_dvector(); + + fprintf(stderr,"n=%d\n",n); + + fft=dvector(1,2*n); + tmp=dvector(1,2*n); + for (i=1;i<=(m-1)/2;i++) + respns[n+1-i]=respns[m+1-i]; + for (i=(m+3)/2;i<=n-(m-1)/2;i++) + respns[i]=0.0; + twofft(data,respns,fft,ans,n); + fprintf(stderr,"Finished FFTs\n"); + + +/* This Block is added for 10 percent water level*/ + if(isign == -1) /*If deconvolving*/ + { + fprintf(stderr,"%.3f Water Level Applied to Response Spectrum\n",h2olev); + cnt=0; + for(i=1; i<=n+1; i++) + if(ABS(ans[i]) > respmax) + { + respmax=ABS(ans[i]); + cnt++; + } + fprintf(stderr,"respmax=%g occurred at frequency %d\n",respmax,cnt); + respmax *= h2olev; + fprintf(stderr,"respmax=%g\n",respmax); + } + + +/*Perform Deconvolution Convolution*/ + no2=n/2; + for (i=2;i<=n+2;i+=2) { + if (isign == 1) { + ans[i-1]=(fft[i-1]*(dum=ans[i-1])-fft[i]*ans[i])/no2; + ans[i]=(fft[i]*dum+fft[i-1]*ans[i])/no2; + } else if (isign == -1) { + /*added for H2O level*/ + if ((mag2=SQR(ans[i-1])+SQR(ans[i])) < 2*respmax*respmax) + mag2=2*respmax*respmax; + + + ans[i-1]=(fft[i-1]*(dum=ans[i-1])+fft[i]*ans[i])/mag2/no2; + ans[i]=(fft[i]*dum-fft[i-1]*ans[i])/mag2/no2; + } else nrerror("No meaning for ISIGN in CONVLV"); + } + + if(tau) + { + fprintf(stderr,"Tau=%g\n",tau); + for(i=2; i <= n+2; i+=2) + { + real=ans[i-1]; + imag=ans[i]; + ans[i-1]=real*cos((i-2)*PI/dt/n*tau)-imag*sin((i-2)*PI/dt/n*tau); + ans[i]=imag*cos((i-2)*PI/dt/n*tau)+real*sin((i-2)*PI/dt/n*tau); + } + } + + fprintf(stderr,"Finished condecon\n"); + + ans[2]=ans[n+1]; + realft(ans,no2,-1); + free_dvector(fft,1,2*n); + fprintf(stderr,"Subs finished\n"); +} + +#undef SQR + +void twofft(data1,data2,fft1,fft2,n) +double data1[],data2[],fft1[],fft2[]; +int n; +{ + int nn3,nn2,jj,j; + double rep,rem,aip,aim; + void four1(); + + nn3=1+(nn2=2+n+n); + for (j=1,jj=2;j<=n;j++,jj+=2) { + fft1[jj-1]=data1[j]; + fft1[jj]=data2[j]; + } + four1(fft1,n,1); + fft2[1]=fft1[2]; + fft1[2]=fft2[2]=0.0; + for (j=3;j<=n+1;j+=2) { + rep=0.5*(fft1[j]+fft1[nn2-j]); + rem=0.5*(fft1[j]-fft1[nn2-j]); + aip=0.5*(fft1[j+1]+fft1[nn3-j]); + aim=0.5*(fft1[j+1]-fft1[nn3-j]); + fft1[j]=rep; + fft1[j+1]=aim; + fft1[nn2-j]=rep; + fft1[nn3-j] = -aim; + fft2[j]=aip; + fft2[j+1] = -rem; + fft2[nn2-j]=aip; + fft2[nn3-j]=rem; + } +} + +void realft(data,n,isign) +double data[]; +int n,isign; +{ + int i,i1,i2,i3,i4,n2p3; + double c1=0.5,c2,h1r,h1i,h2r,h2i; + double wr,wi,wpr,wpi,wtemp,theta; + void four1(); + + theta=3.141592653589793/(double) n; + if (isign == 1) { + c2 = -0.5; + four1(data,n,1); + } else { + c2=0.5; + theta = -theta; + } + wtemp=sin(0.5*theta); + wpr = -2.0*wtemp*wtemp; + wpi=sin(theta); + wr=1.0+wpr; + wi=wpi; + n2p3=2*n+3; + for (i=2;i<=n/2;i++) { + i4=1+(i3=n2p3-(i2=1+(i1=i+i-1))); + h1r=c1*(data[i1]+data[i3]); + h1i=c1*(data[i2]-data[i4]); + h2r = -c2*(data[i2]+data[i4]); + h2i=c2*(data[i1]-data[i3]); + data[i1]=h1r+wr*h2r-wi*h2i; + data[i2]=h1i+wr*h2i+wi*h2r; + data[i3]=h1r-wr*h2r+wi*h2i; + data[i4] = -h1i+wr*h2i+wi*h2r; + wr=(wtemp=wr)*wpr-wi*wpi+wr; + wi=wi*wpr+wtemp*wpi+wi; + } + if (isign == 1) { + data[1] = (h1r=data[1])+data[2]; + data[2] = h1r-data[2]; + } else { + data[1]=c1*((h1r=data[1])+data[2]); + data[2]=c1*(h1r-data[2]); + four1(data,n,-1); + } +} + +#define SWAP(a,b) tempr=(a);(a)=(b);(b)=tempr + +void four1(data,nn,isign) +double data[]; +int nn,isign; +{ + int n,mmax,m,j,istep,i; + double wtemp,wr,wpr,wpi,wi,theta; + double tempr,tempi; + + n=nn << 1; + j=1; + for (i=1;i i) { + SWAP(data[j],data[i]); + SWAP(data[j+1],data[i+1]); + } + m=n >> 1; + while (m >= 2 && j > m) { + j -= m; + m >>= 1; + } + j += m; + } + mmax=2; + while (n > mmax) { + istep=2*mmax; + theta=6.28318530717959/(isign*mmax); + wtemp=sin(0.5*theta); + wpr = -2.0*wtemp*wtemp; + wpi=sin(theta); + wr=1.0; + wi=0.0; + for (m=1;m=nrl;i--) free((char*) (m[i]+ncl)); + free((char*) (m+nrl)); +} + +void free_dmatrix(m,nrl,nrh,ncl,nch) +double **m; +int nrl,nrh,ncl,nch; +{ + int i; + + for(i=nrh;i>=nrl;i--) free((char*) (m[i]+ncl)); + free((char*) (m+nrl)); +} + +void free_imatrix(m,nrl,nrh,ncl,nch) +int **m; +int nrl,nrh,ncl,nch; +{ + int i; + + for(i=nrh;i>=nrl;i--) free((char*) (m[i]+ncl)); + free((char*) (m+nrl)); +} + + + +void free_submatrix(b,nrl,nrh,ncl,nch) +float **b; +int nrl,nrh,ncl,nch; +{ + free((char*) (b+nrl)); +} + + + +float **convert_matrix(a,nrl,nrh,ncl,nch) +float *a; +int nrl,nrh,ncl,nch; +{ + int i,j,nrow,ncol; + float **m; + + nrow=nrh-nrl+1; + ncol=nch-ncl+1; + m = (float **) malloc((unsigned) (nrow)*sizeof(float*)); + if (!m) nrerror("allocation failure in convert_matrix()"); + m -= nrl; + for(i=0,j=nrl;i<=nrow-1;i++,j++) m[j]=a+ncol*i-ncl; + return m; +} + + + +void free_convert_matrix(b,nrl,nrh,ncl,nch) +float **b; +int nrl,nrh,ncl,nch; +{ + free((char*) (b+nrl)); +} diff --git a/UTILITIES/distaz.c b/UTILITIES/distaz.c new file mode 100755 index 0000000..9f3985c --- /dev/null +++ b/UTILITIES/distaz.c @@ -0,0 +1,154 @@ + +/*Distance Subroutine - Spherical Earth*/ +int delaz5_(thei, alei, thsi, alsi, delt, deltdg, deltkm, + azes, azesdg, azse, azsedg, i) + +float *thei, *alei, *thsi, *alsi, *delt, *deltdg, *deltkm, *azes; +float *azesdg, *azse, *azsedg; +int *i; +{ + double d__1, d__2, d__3; + + double tan(), atan(), sin(), cos(), acos(), atan2(), sqrt(), asin(); + + /* Local variables */ + static double a, b, c, d, e, g, h; + static float c1, c3, c4, c5, c6; + static double ak, ap, bp, cp, dp, ep, gp, hp; + static float aaa, ale; + static double akp; + static float als, the, ths; + + if (*i <= 0) { + goto L50; + } else { + goto L51; + } +/* IF COORDINATES ARE GEOGRAPH DEG I=0 */ +/* IF COORDINATES ARE GEOCENT RADIAN I=1 */ +L50: + the = *thei * (float).01745329252; + ale = *alei * (float).01745329252; + ths = *thsi * (float).01745329252; + als = *alsi * (float).01745329252; + aaa = tan(the) * (float).9931177; + the = atan(aaa); + aaa = tan(ths) * (float).9931177; + ths = atan(aaa); + goto L32; +L51: + the = *thei; + ale = *alei; + ths = *thsi; + als = *alsi; +L32: + c = sin(the); + ak = -(double)cos(the); + d = sin(ale); + e = -(double)cos(ale); + a = ak * e; + b = -ak * d; + g = -c * e; + h = c * d; + cp = sin(ths); + akp = -(double)cos(ths); + dp = sin(als); + ep = -(double)cos(als); + ap = akp * ep; + bp = -akp * dp; + gp = -cp * ep; + hp = cp * dp; + c1 = a * ap + b * bp + c * cp; + if (c1 - (float).94 >= (float)0.) { + goto L31; + } else { + goto L30; + } +L30: + if (c1 + (float).94 <= (float)0.) { + goto L28; + } else { + goto L29; + } +L29: + *delt = acos(c1); +L33: + *deltkm = *delt * (float)6371.; +/* Computing 2nd power */ + d__1 = ap - d; +/* Computing 2nd power */ + d__2 = bp - e; +/* Computing 2nd power */ + d__3 = cp; + c3 = d__1 * d__1 + d__2 * d__2 + d__3 * d__3 - (float)2.; +/* Computing 2nd power */ + d__1 = ap - g; +/* Computing 2nd power */ + d__2 = bp - h; +/* Computing 2nd power */ + d__3 = cp - ak; + c4 = d__1 * d__1 + d__2 * d__2 + d__3 * d__3 - (float)2.; +/* Computing 2nd power */ + d__1 = a - dp; +/* Computing 2nd power */ + d__2 = b - ep; +/* Computing 2nd power */ + d__3 = c; + c5 = d__1 * d__1 + d__2 * d__2 + d__3 * d__3 - (float)2.; +/* Computing 2nd power */ + d__1 = a - gp; +/* Computing 2nd power */ + d__2 = b - hp; +/* Computing 2nd power */ + d__3 = c - akp; + c6 = d__1 * d__1 + d__2 * d__2 + d__3 * d__3 - (float)2.; + *deltdg = *delt * (float)57.29577951; + *azes = atan2(c3, c4); + if (*azes >= (float)0.) { + goto L81; + } else { + goto L80; + } +L80: + *azes += (float)6.283185308; +L81: + *azse = atan2(c5, c6); + if (*azse >= (float)0.) { + goto L71; + } else { + goto L70; + } +L70: + *azse += (float)6.283185308; +L71: + *azesdg = *azes * (float)57.29577951; + *azsedg = *azse * (float)57.29577951; + return 0; +L31: +/* Computing 2nd power */ + d__1 = a - ap; +/* Computing 2nd power */ + d__2 = b - bp; +/* Computing 2nd power */ + d__3 = c - cp; + c1 = d__1 * d__1 + d__2 * d__2 + d__3 * d__3; + c1 = sqrt(c1); + c1 /= (float)2.; + *delt = asin(c1); + *delt *= (float)2.; + goto L33; +L28: +/* Computing 2nd power */ + d__1 = a + ap; +/* Computing 2nd power */ + d__2 = b + bp; +/* Computing 2nd power */ + d__3 = c + cp; + c1 = d__1 * d__1 + d__2 * d__2 + d__3 * d__3; + c1 = sqrt(c1); + c1 /= (float)2.; + *delt = acos(c1); + *delt *= (float)2.; + goto L33; +} /* delaz5_ */ + diff --git a/UTILITIES/fromHelm_new.c b/UTILITIES/fromHelm_new.c new file mode 100755 index 0000000..b5ed1ff --- /dev/null +++ b/UTILITIES/fromHelm_new.c @@ -0,0 +1,46 @@ +#include +#include +#include +#include +/*This program converts Helmberger Ascii to headerless binary. It assumes all +vectors have the same length*/ + +int main() + { + int i, j, ntr, n; + float *tr, *vec, dt; + char line[120]; + + fgets(line,120,stdin); /*number of traces line*/ + sscanf(line,"%d",&ntr); + fprintf(stderr,"ntr=%d\n",ntr); + fgets(line,120,stdin); /*format line*/ + fgets(line,120,stdin); /*variables line*/ + fgets(line,120,stdin); /*npts and dt line*/ + sscanf(line,"%d %f",&n,&dt); + fprintf(stderr,"n=%d dt=%g\n",n,dt); + + tr=(float *)malloc(sizeof(float)*n); + vec=(float *)malloc(sizeof(float)*n*ntr); + for(i=0; i +#include +#include +#include +#include "declarations.h" + +FILE *fopen(), *in, *out; +int getenv_s(); + +struct comp + { + float re; + float im; + }; + +int main(ac,av) + int ac; + char **av; + { + int i, n, nzero, npole, env; + float gain, junk; + double date=2500.0, time=2358.9, date1, date2; + int year1,jday1,hrmin1, year2,jday2,hrmin2; + struct comp zero[20], pole[20]; + char line[120], tmp[50], tmp1[20], tmp2[20], tmp3[20], name[5], comp[5]; + char name1[5], comp1[5], padding_character='\0'; + char temp1[13], temp2[13]; + char *resp_name; + + resp_name=(char *)malloc(sizeof(char)*120); + + setpar(ac,av); + mstpar("name","s",name); + mstpar("comp","s",comp); + getpar("date","F",&date); + getpar("time","F",&time); + endpar(); + + if(! getenv_s("REDI_MT_RESP", &resp_name)) { + env=1; + } + if(env==1) + { + fprintf(stderr,"Missing environment variable (source tdmt.config)\n"); + exit(-1); + } + if((in=fopen(resp_name,"r")) == NULL) + { + fprintf(stderr,"Error opening: %s\n",resp_name); + exit(-1); + } + + sprintf(tmp,"%s_%s.zp",name,comp); + fprintf(stderr,"name=%s\n",tmp); + + if((out=fopen(tmp,"w")) == NULL) + { + fprintf(stderr,"Error opening %s\n",tmp); + exit(-1); + } + +date += time/1e+7; + +while(fgets(line,120,in) != NULL) + { + sscanf(line,"%4s %4s %d.%d.%d %d.%d.%d", + name1,comp1,&year1,&jday1,&hrmin1,&year2,&jday2,&hrmin2); + + + if (!strcmp(name,name1) && !strcmp(comp,comp1)) + { + date1 = (double)year1 +(double)(jday1)/1000.0+(double)hrmin1/1e+7; + date2 = (double)year2 +(double)(jday2)/1000.0+(double)hrmin2/1e+7; + if(date <= date2 && date >= date1) + { + + fprintf(stderr,"search date=%.7f time=%.1f\n",date,time); + fprintf(stderr," date1=%.7f\n",date1); + fprintf(stderr," date2=%.7f\n",date2); + + fscanf(in,"%g %d %d\n",&gain,&nzero,&npole); + + fprintf(out,"CONSTANT %g\n",gain); + + for(i=0 ; i < nzero ; i++) + fscanf(in,"%f %f\n",&zero[i].re,&zero[i].im); + + fprintf(out,"ZEROS %d\n",nzero); + for(i=0 ; i < nzero ; i++) + fprintf(out,"%.4e %.4e\n",zero[i].re,zero[i].im); + + + for(i=0 ; i < npole ; i++) + { + if(i % 2 == 0) + fscanf(in,"%f %f\n",&pole[i].re,&pole[i].im); + else + fscanf(in,"%f %f",&pole[i].re,&pole[i].im); + } + + fprintf(out,"POLES %d\n",npole); + for(i=0 ; i < npole ; i++) + fprintf(out,"%.4e %.4e\n",pole[i].re,pole[i].im); + + fflush(out); + fclose(in); + fclose(out); + exit(-1); + } + } + + } /*End While*/ + fprintf(stderr,"%s%s date=%.3f NOT FOUND\n",name,comp,date); + + + + + + } /*END*/ diff --git a/UTILITIES/getenv_utils.c b/UTILITIES/getenv_utils.c new file mode 100755 index 0000000..2744d1c --- /dev/null +++ b/UTILITIES/getenv_utils.c @@ -0,0 +1,91 @@ +#ifndef lint +static char sccsid[] = "%W% %G% %U%"; +#endif + +#include +#include +#include + +/************************************************************************/ +/* getenv_lf: */ +/* Get the value of an environment variable as a double. */ +/* Return 1 on success, 0 on failure. */ +/************************************************************************/ +int getenv_lf (var, pval) + char *var; + double *pval; +{ + char *str; + if (str = getenv(var)) { + char *p; + double val; + val = strtod (str, &p); + if (*p == '\0') { + *pval = val; + return(1); + } + } + return (0); +} + +/************************************************************************/ +/* getenv_f: */ +/* Get the value of an environment variable as a float. */ +/* Return 1 on success, 0 on failure. */ +/************************************************************************/ +int getenv_f (var, pval) + char *var; + float *pval; +{ + char *str; + if (str = getenv(var)) { + char *p; + double val; + val = (float)strtod (str, &p); + if (*p == '\0') { + *pval = val; + return(1); + } + } + return (0); +} + +/************************************************************************/ +/* getenv_d: */ +/* Get the value of an environment variable as a int. */ +/* Return 1 on success, 0 on failure. */ +/************************************************************************/ +int getenv_d (var, pval) + char *var; + int *pval; +{ + char *str; + if (str = getenv(var)) { + char *p; + int val; + val = strtol (str, &p, 10); + if (*p == '\0') { + *pval = val; + return(1); + } + } + return (0); +} + +/************************************************************************/ +/* getenv_s: */ +/* Get the value of an environment variable as a string. */ +/* Return 1 on success, 0 on failure. */ +/************************************************************************/ +int getenv_s (var, pval) + char *var; + char **pval; +{ + char *str; + if (str = getenv(var)) { + *pval = str; + return(1); + } + return (0); +} + diff --git a/UTILITIES/getpar.c b/UTILITIES/getpar.c new file mode 100755 index 0000000..6975072 --- /dev/null +++ b/UTILITIES/getpar.c @@ -0,0 +1,534 @@ +/* copyright (c) Robert W. Clayton + * Seismological Laboratory + * Caltech + * Pasadena, CA 91125 + * + * Getpar routines: + * + * Externally visable routines: + * + * setpar(argc,argv) + * getpar(name,type,valptr) + * mstpar(name,type,valptr) + * endpar() + * + * To get C-version: + * cc -c getpar.c + * + */ +#include +#include +#include + +#define MAXLINE 1024 /* max length of line in par file */ +#define MAXNAME 64 /* max length of name */ +#define MAXVALUE 1024 /* max length of value */ +#define MAXFILENAME 64 /* max length of par file name */ +#define MAXVECTOR 10 /* max # of elements for unspecified vectors */ +#define GETPAR_ERROR 100 /* exit status for getpar error */ +#define GETPAR_STOP 101 /* exit status for STOP or mstpar */ +#define MAXPARLEVEL 4 /* max recurrsion level for par files */ + +#define GETPAR getpar +#define MSTPAR mstpar +#define ENDPAR endpar + +#define INIT 1 /* bits for FLAGS (ext_par.argflags) */ +#define STOP 2 +#define LIST 4 +#define END_PAR 8 +#define VERBOSE 16 + +#define LISTINC 32 /* increment size for arglist */ +#define BUFINC 1024 /* increment size for argbuf */ + +struct arglist /* structure of list set up by setpar */ + { + char *argname; + char *argval; + int hash; + }; +struct ext_par /* global variables for getpar */ + { + char *progname; + int argflags; + struct arglist *arglist; + struct arglist *arghead; + char *argbuf; + int nlist; + int nbuf; + int listmax; + int bufmax; + FILE *listout; + } ext_par; + +/* abbreviations: */ +#define AL struct arglist +#define PROGNAME ext_par.progname +#define FLAGS ext_par.argflags +#define ARGLIST ext_par.arglist +#define ARGHEAD ext_par.arghead +#define ARGBUF ext_par.argbuf +#define NLIST ext_par.nlist +#define NBUF ext_par.nbuf +#define LISTMAX ext_par.listmax +#define BUFMAX ext_par.bufmax +#define LISTFILE ext_par.listout + +/*declare functions*/ +void setpar(); +int getpar(); +int mstpar(); +void endpar(); +void gp_add_entry(); +void gp_do_par_file(); +int gp_close_dump(); +int gp_getpar_err(); +int gp_getvector(); +int gp_compute_hash(); + +void setpar(ac,av) /* set up arglist & process INPUT command */ +int ac; char **av; + { + register char *pl, *pn, *pv; + char t, name[MAXNAME], value[MAXVALUE]; + FILE *file, *gp_create_dump(); + int i, addflags, nevlist, testav, testae; + struct arglist *alptr; + + PROGNAME= *av; + FLAGS= INIT; + LISTFILE= stderr; + + ARGLIST= NULL; + ARGBUF = NULL; + NLIST= NBUF= LISTMAX= BUFMAX= 0; + + nevlist= NLIST; + while(--ac>0) + { + av++; + pl= *av; + while(*pl == ' ' || *pl == '\t') pl++; + /* get name */ + pn= name; + while(*pl != '=' && *pl != '\0') *pn++ = *pl++; + *pn++ = '\0'; + /* get value */ + if(*pl == '=') pl++; + pv= value; + if(*pl == '"' || *pl == '\'') + { + t= *pl++; + while(*pl != '\0') + { + if(*pl == t) + { + if(pl[-1] != '\\') break; + pv[-1]= t; + pl++; + } + else *pv++ = *pl++; + } + } + else while(*pl) *pv++ = *pl++; + *pv= '\0'; + if(name[0] == '-') gp_add_entry("SWITCH",&name[1]); + else gp_add_entry(name,value); + if(strcmp("par",name)==0) /* par file */ + gp_do_par_file(value,1); + } + /* do not internally call getpar before this point because + ARGHEAD is not set. The search will have no stopping point */ + ARGHEAD= ARGLIST; + + addflags= 0; + *value= '\0'; + if(GETPAR("STOP","s",value)) addflags |= STOP; + *value= '\0'; + if(GETPAR("VERBOSE","s",value)) addflags |= VERBOSE; + *value= '\0'; + if(GETPAR("LIST","s",value)) + { + addflags |= LIST; + LISTFILE =gp_create_dump(value,"list"); + } + *value= '\0'; + if(GETPAR("INPUT","s",value)) + { + file =gp_create_dump(value,"list input"); + fprintf(file,"%s: getpar input listing\n",PROGNAME); + for(i=0, alptr=ARGLIST; iargname,alptr->argval); + } + gp_close_dump(file); + } + FLAGS |= addflags; + } + +void gp_add_entry(name,value) /* add an entry to arglist, expanding memory */ +register char *name, *value; /* if necessary */ + { + struct arglist *alptr; + int len; + register char *ptr; + + /* check arglist memory */ + if(NLIST >= LISTMAX) + { + LISTMAX += LISTINC; + if(ARGLIST == NULL) + ARGLIST= (AL *)malloc(LISTMAX * sizeof(AL)); + else ARGLIST= (AL *)realloc(ARGLIST,LISTMAX * sizeof(AL)); + } + /* check argbuf memory */ + len= strlen(name) + strlen(value) + 2; /* +2 for terminating nulls */ + if(NBUF+len >= BUFMAX) + { + BUFMAX += BUFINC; + if(ARGBUF == NULL) + ARGBUF= (char *)malloc(BUFMAX); + else ARGBUF= (char *)realloc(ARGBUF,BUFMAX); + } + if(ARGBUF == NULL || ARGLIST == NULL) + gp_getpar_err("setpar","cannot allocate memory",0,0,0,0); + + /* add name */ + alptr= ARGLIST + NLIST; + alptr->hash= gp_compute_hash(name); + ptr= alptr->argname= ARGBUF + NBUF; + do *ptr++ = *name; while(*name++); + + /* add value */ + NBUF += len; + alptr->argval= ptr; + do *ptr++ = *value; while(*value++); + NLIST++; + } + +void ENDPAR() /* free arglist & argbuf memory, & process STOP command */ + { + if(ARGLIST != NULL) free(ARGLIST); + if(ARGBUF != NULL) free(ARGBUF); + ARGBUF= NULL; + ARGLIST= NULL; + if(FLAGS & STOP) + { + fprintf(stderr,"%s[endpar]: stop due to STOP in input\n", + PROGNAME); + exit(GETPAR_STOP); + } + FLAGS= END_PAR; /* this stops further getpar calls */ + } + +int mstpar(name,type,val) +char *name, *type; +int *val; + { + int cnt; + char *typemess; + + if( (cnt= GETPAR(name,type,val)) > 0) return(cnt); + + /* The following line corrects a common input error */ + if(type[1]=='v') { type[1]= type[0]; type[0]='v'; } + + switch(*type) + { + case 'd': typemess= "an integer"; break; + case 'f': typemess= "a float"; break; + case 'F': typemess= "a double"; break; + case 's': typemess= "a string"; break; + case 'b': typemess= "a boolean"; break; + case 'v': switch(type[1]) + { + case 'd': typemess= "an integer vector"; break; + case 'f': typemess= "a float vector"; break; + case 'F': typemess= "a double vector"; break; + default : typemess= "unknow vectorn (error)"; + break; + } + return(0); + break; + default : typemess= "unknown (error)"; return(0); break; + } + gp_getpar_err("mstpar","must specify value for '%s', expecting %s", + name,typemess,0,0,0); + return(0); + } + +int getpar(name,type,val) +char *name, *type; +int *val; + { + register char *sptr; + register struct arglist *alptr; + register int i; + double atof(), *dbl; + float *flt; + int h, hno, hyes, found; + char line[MAXLINE], *str, *noname; + + if(FLAGS & END_PAR) + gp_getpar_err("getpar","called after endpar"); + if( (FLAGS & INIT) == 0) + gp_getpar_err("getpar","not initialized with setpar"); + if(FLAGS & VERBOSE) + fprintf(stderr,"getpar: looking for %s\n",name); + + /* The following line corrects a common input error */ + if(type[1]=='v') { type[1]= type[0]; type[0]='v'; } + + + if(*type == 'b') goto boolean; + + h= gp_compute_hash(name); + found=0; + /* search list backwards, stopping at first find */ + for(alptr= ARGLIST +(NLIST-1); alptr >= ARGHEAD; alptr--) + { + if(alptr->hash != h) continue; + if(strcmp(alptr->argname,name) != 0) continue; + str= alptr->argval; + switch(*type) + { + case 'd': + *val= atoi(str); + found=1; + break; + case 'f': + flt= (float *) val; + *flt= atof(str); + found=1; + break; + case 'F': + dbl= (double *) val; + *dbl= atof(str); + found=1; + break; + case 's': + sptr= (char *) val; + while(*str) *sptr++ = *str++; + *sptr= '\0'; + found=1; + break; + case 'v': + found= gp_getvector(str,type,val); + break; + default: + gp_getpar_err("getpar", + "unknown conversion type %s",type); + break; + } + break; + } + goto list; +boolean: + noname= line; + sprintf(noname,"no%s",name); + hno = gp_compute_hash(noname); + hyes= gp_compute_hash( name); + found=0; + /* search list backwards, stopping at first find */ + for(alptr= ARGLIST +(NLIST-1); alptr >= ARGHEAD; alptr--) + { + if(alptr->hash != hno && alptr->hash != hyes) continue; + if(strcmp(alptr->argname, name)== 0) + { + if(alptr->argval[0] == '\0') *val= 1; + else *val= atol(alptr->argval); + found++; + break; + } + if(strcmp(alptr->argname,noname)== 0) + { *val= 0; found++; break; } + } + list: + if(FLAGS & LIST) + { + switch(*type) + { + case 'd': sprintf(line,"(int) = %d",*val); break; + case 'f': flt= (float *)val; + sprintf(line,"(flt) = %14.6e",*flt); break; + case 'F': dbl= (double *)val; + sprintf(line,"(dbl) = %14.6e",*dbl); break; + /* case 's': sprintf(line,"(str) = %s",val); break;*/ + case 's': sprintf(line,"(str) = %s",sptr); break; + case 'b': sprintf(line,"(boo) = %d",*val); break; + case 'v': switch(type[1]) + { + /* should list these out */ + case 'd': sprintf(line,"(int vec)"); + break; + case 'f': sprintf(line,"(flt vec)"); + break; + case 'F': sprintf(line,"(dbl vec)"); + break; + default : sprintf(line," vec type error"); + break; + } + break; + default : sprintf(line," type error"); break; + } + fprintf(LISTFILE,"%16s (%s) %s \n",name, + (found ? "set":"def"),line); + } + return(found); + } +FILE *gp_create_dump(fname,filetype) +char *fname; +char *filetype; + { + FILE *temp; + + if(*fname == '\0') return(stderr); + if(strcmp(fname,"stderr") == 0) return(stderr); + if(strcmp(fname,"stdout") == 0) return(stdout); + if( (temp= fopen(fname,"w")) != NULL) return(temp); + fprintf(stderr,"%s[setpar]: cannot create %s file %s\n", + PROGNAME,filetype,fname); + return(stderr); + } + +int gp_close_dump(file) +FILE *file; + { + if(file == stderr || file == stdout) return(0); + fclose(file); + return(0); + } + +int gp_compute_hash(s) +register char *s; + { + register int h; + h= s[0]; + if(s[1]) h |= (s[1])<<8; else return(h); + if(s[2]) h |= (s[2])<<16; else return(h); + if(s[3]) h |= (s[3])<<24; + return(h); + } + +void gp_do_par_file(fname,level) +char *fname; +int level; + { + register char *pl, *pn, *pv; + char t1, t2, line[MAXLINE], name[MAXNAME], value[MAXVALUE]; + FILE *file, *fopen(); + + if(level > MAXPARLEVEL) + gp_getpar_err("setpar","%d (too many) recursive par file",level); + + if( (file=fopen(fname,"r"))==NULL) + gp_getpar_err("setpar","cannot open par file %s",fname); + + while( fgets(line,MAXLINE,file) != NULL ) + { + pl= line; + /* loop over entries on each line */ + loop: while(*pl==' ' || *pl=='\t') pl++; + if(*pl=='\0'|| *pl=='\n') continue; + if(*pl=='#') continue; /* comments on rest of line */ + + /* get name */ + pn= name; + while(*pl != '=' && *pl != '\0' && *pl != ' ' + && *pl != '\t') *pn++ = *pl++; + *pn = '\0'; + if(*pl == '=') pl++; + + /* get value */ + *value= '\0'; + pv= value; + if(*pl=='"' || *pl=='\'') { t1= t2= *pl++; } + else { t1= ' '; t2= '\t'; } + while(*pl!=t1 && *pl!=t2 && + *pl!='\0' && *pl!='\n') *pv++= *pl++; + *pv= '\0'; + if(*pl=='"' || *pl=='\'') pl++; + gp_add_entry(name,value); + if(strcmp("par",name) == 0) + gp_do_par_file(value,level+1); + goto loop; + } + fclose(file); + } + +int gp_getpar_err(subname,mess,a1,a2,a3,a4) +char *subname, *mess; +int a1, a2, a3, a4; + { + fprintf(stderr,"\n***** ERROR in %s[%s] *****\n\t", + (PROGNAME == NULL ? "(unknown)" : PROGNAME),subname); + fprintf(stderr,mess,a1,a2,a3,a4); + fprintf(stderr,"\n"); + exit(GETPAR_ERROR); + } +int gp_getvector(list,type,val) +char *list, *type; +int *val; + { + register char *p; + register int index, cnt; + char *valptr; + int limit; + int ival, *iptr; + float fval, *fptr; + double dval, *dptr, atof(); + + limit= MAXVECTOR; + if(type[2] == '(' || type[2] == '[') limit= atol(&type[3]); + if(limit <= 0) + gp_getpar_err("getpar","bad limit=%d specified",limit,0,0,0); + index= 0; + p= list; + while(*p != '\0' && index < limit) + { + cnt=1; + backup: /* return to here if we find a repetition factor */ + while(*p == ' ' || *p == '\t') p++; + if(*p == '\0') return(index); + valptr= p; + while( *p != ',' && *p != '*' && *p != 'x' && *p != 'X' && + *p != '\0') p++; + if(*p == '*' || *p == 'x' || *p == 'X') + { + cnt= atol(valptr); + if(cnt <= 0) + gp_getpar_err("getpar", + "bad repetition factor=%d specified", + cnt,0,0,0); + if(index+cnt > limit) cnt= limit - index; + p++; + goto backup; + } + switch(type[1]) + { + case 'd': + iptr= (int *) val; + ival= atol(valptr); + while(cnt--) iptr[index++] = ival; + break; + case 'f': + fptr= (float *) val; + fval= atof(valptr); + while(cnt--) fptr[index++] = fval; + break; + case 'F': + dptr= (double *) val; + dval= atof(valptr); + while(cnt--) dptr[index++] = dval; + break; + default: + gp_getpar_err("getpar", + "bad vector type=%c specified",type[1],0,0,0); + break; + } + if(*p != '\0') p++; + } + return(index); + } diff --git a/UTILITIES/getpar.c_save b/UTILITIES/getpar.c_save new file mode 100755 index 0000000..38aead9 --- /dev/null +++ b/UTILITIES/getpar.c_save @@ -0,0 +1,602 @@ +/* copyright (c) Robert W. Clayton + * Seismological Laboratory + * Caltech + * Pasadena, CA 91125 + * + * Getpar routines: + * + * Externally visable routines: + * + * setpar(argc,argv) + * getpar(name,type,valptr) + * mstpar(name,type,valptr) + * endpar() + * + * To get C-version: + * cc -c getpar.c + * + * To get F77-version: + * cp getpar.c fgetpar.c + * cc -c -DFORTRAN fgetpar.c + * rm fgetpar.c + * + * To get the environment processing stuff add the flag + *-DENVIRONMENT to each of the cc's above. + */ +#include +#include +#include + +#define MAXLINE 1024 /* max length of line in par file */ +#define MAXNAME 64 /* max length of name */ +#define MAXVALUE 1024 /* max length of value */ +#define MAXFILENAME 64 /* max length of par file name */ +#define MAXVECTOR 10 /* max # of elements for unspecified vectors */ +#define GETPAR_ERROR 100 /* exit status for getpar error */ +#define GETPAR_STOP 101 /* exit status for STOP or mstpar */ +#define MAXPARLEVEL 4 /* max recurrsion level for par files */ + +#ifdef FORTRAN +#define GETPAR getpar_ +#define MSTPAR mstpar_ +#define ENDPAR endpar_ +#else +#define GETPAR getpar +#define MSTPAR mstpar +#define ENDPAR endpar +#endif + +#define INIT 1 /* bits for FLAGS (ext_par.argflags) */ +#define STOP 2 +#define LIST 4 +#define END_PAR 8 +#define VERBOSE 16 + +#define LISTINC 32 /* increment size for arglist */ +#define BUFINC 1024 /* increment size for argbuf */ + +struct arglist /* structure of list set up by setpar */ + { + char *argname; + char *argval; + int hash; + }; +struct ext_par /* global variables for getpar */ + { + char *progname; + int argflags; + struct arglist *arglist; + struct arglist *arghead; + char *argbuf; + int nlist; + int nbuf; + int listmax; + int bufmax; + FILE *listout; + } ext_par; + +/* abbreviations: */ +#define AL struct arglist +#define PROGNAME ext_par.progname +#define FLAGS ext_par.argflags +#define ARGLIST ext_par.arglist +#define ARGHEAD ext_par.arghead +#define ARGBUF ext_par.argbuf +#define NLIST ext_par.nlist +#define NBUF ext_par.nbuf +#define LISTMAX ext_par.listmax +#define BUFMAX ext_par.bufmax +#define LISTFILE ext_par.listout + +#ifdef FORTRAN +setpar_() +#else +setpar(ac,av) /* set up arglist & process INPUT command */ +int ac; char **av; +#endif + { + register char *pl, *pn, *pv; + char t, name[MAXNAME], value[MAXVALUE]; + FILE *file, *gp_create_dump(); + int i, addflags, nevlist, testav, testae; + struct arglist *alptr; +#ifdef FORTRAN + int ac; char **av; + extern int xargc; extern char **xargv; + ac= xargc; av= xargv; +#endif + + PROGNAME= *av; + FLAGS= INIT; + LISTFILE= stderr; + + ARGLIST= NULL; + ARGBUF = NULL; + NLIST= NBUF= LISTMAX= BUFMAX= 0; +#ifdef ENVIRONMENT + gp_do_environment(ac,av); +#endif + nevlist= NLIST; + while(--ac>0) + { + av++; + pl= *av; + while(*pl == ' ' || *pl == '\t') pl++; + /* get name */ + pn= name; + while(*pl != '=' && *pl != '\0') *pn++ = *pl++; + *pn++ = '\0'; + /* get value */ + if(*pl == '=') pl++; + pv= value; + if(*pl == '"' || *pl == '\'') + { + t= *pl++; + while(*pl != '\0') + { + if(*pl == t) + { + if(pl[-1] != '\\') break; + pv[-1]= t; + pl++; + } + else *pv++ = *pl++; + } + } + else while(*pl) *pv++ = *pl++; + *pv= '\0'; + if(name[0] == '-') gp_add_entry("SWITCH",&name[1]); + else gp_add_entry(name,value); + if(strcmp("par",name)==0) /* par file */ + gp_do_par_file(value,1); + } + /* do not internally call getpar before this point because + ARGHEAD is not set. The search will have no stopping point */ + ARGHEAD= ARGLIST; +#ifdef ENVIRONMENT + *value= '\0'; + if(GETPAR("NOENV","s",value)) ARGHEAD= ARGLIST+ nevlist; +#endif + addflags= 0; + *value= '\0'; + if(GETPAR("STOP","s",value)) addflags |= STOP; + *value= '\0'; + if(GETPAR("VERBOSE","s",value)) addflags |= VERBOSE; + *value= '\0'; + if(GETPAR("LIST","s",value)) + { + addflags |= LIST; + LISTFILE =gp_create_dump(value,"list"); + } + *value= '\0'; + if(GETPAR("INPUT","s",value)) + { + file =gp_create_dump(value,"list input"); + fprintf(file,"%s: getpar input listing\n",PROGNAME); + for(i=0, alptr=ARGLIST; iargname,alptr->argval); + } + gp_close_dump(file); + } + FLAGS |= addflags; + } + +gp_add_entry(name,value) /* add an entry to arglist, expanding memory */ +register char *name, *value; /* if necessary */ + { + struct arglist *alptr; + int len; + register char *ptr; + + /* check arglist memory */ + if(NLIST >= LISTMAX) + { + LISTMAX += LISTINC; + if(ARGLIST == NULL) + ARGLIST= (AL *)malloc(LISTMAX * sizeof(AL)); + else ARGLIST= (AL *)realloc(ARGLIST,LISTMAX * sizeof(AL)); + } + /* check argbuf memory */ + len= strlen(name) + strlen(value) + 2; /* +2 for terminating nulls */ + if(NBUF+len >= BUFMAX) + { + BUFMAX += BUFINC; + if(ARGBUF == NULL) + ARGBUF= (char *)malloc(BUFMAX); + else ARGBUF= (char *)realloc(ARGBUF,BUFMAX); + } + if(ARGBUF == NULL || ARGLIST == NULL) + gp_getpar_err("setpar","cannot allocate memory"); + + /* add name */ + alptr= ARGLIST + NLIST; + alptr->hash= gp_compute_hash(name); + ptr= alptr->argname= ARGBUF + NBUF; + do *ptr++ = *name; while(*name++); + + /* add value */ + NBUF += len; + alptr->argval= ptr; + do *ptr++ = *value; while(*value++); + NLIST++; + } +#ifdef ENVIRONMENT +gp_do_environment(ac,av) +int ac; char **av; + { + char **ae; + register char *pl, *pn, *pv; + char name[MAXNAME], value[MAXVALUE], t; + + /* The environ pointer ae, is assumed to have a specific relation + to the arg pointer av. This may not be portable. */ + ae= av +(ac+1); + if(ae == NULL) return; + + while(*ae != NULL) + { + pl= *ae++; + while(*pl == ' ' || *pl == '\t') pl++; + /* get name */ + pn= name; + while(*pl != '=' && *pl != '\0') *pn++ = *pl++; + *pn = '\0'; + if(strcmp("NOENV",pn) == 0) return; + + /* get value */ + if(*pl == '=') pl++; + pv= value; + if(*pl == '"' || *pl == '\'') + { + t= *pl++; + while(*pl != '\0') + { + if(*pl == t) + { + if(pl[-1] != '\\') break; + pv[-1]= t; + pl++; + } + else *pv++ = *pl++; + } + } + else while(*pl) *pv++ = *pl++; + *pv= '\0'; + gp_add_entry(name,value); + } + } +#endif + +ENDPAR() /* free arglist & argbuf memory, & process STOP command */ + { + if(ARGLIST != NULL) free(ARGLIST); + if(ARGBUF != NULL) free(ARGBUF); + ARGBUF= NULL; + ARGLIST= NULL; + if(FLAGS & STOP) + { + fprintf(stderr,"%s[endpar]: stop due to STOP in input\n", + PROGNAME); + exit(GETPAR_STOP); + } + FLAGS= END_PAR; /* this stops further getpar calls */ + } + +#ifdef FORTRAN +mstpar_(name,type,val,dum1,dum2) +int dum1, dum2; /* dum1 & dum2 are extra args that fortran puts in */ +#else +mstpar(name,type,val) +#endif +char *name, *type; +int *val; + { + int cnt; + char *typemess; + + if( (cnt= GETPAR(name,type,val)) > 0) return(cnt); + + /* The following line corrects a common input error */ + if(type[1]=='v') { type[1]= type[0]; type[0]='v'; } + + switch(*type) + { + case 'd': typemess= "an integer"; break; + case 'f': typemess= "a float"; break; + case 'F': typemess= "a double"; break; + case 's': typemess= "a string"; break; + case 'b': typemess= "a boolean"; break; + case 'v': switch(type[1]) + { + case 'd': typemess= "an integer vector"; break; + case 'f': typemess= "a float vector"; break; + case 'F': typemess= "a double vector"; break; + default : typemess= "unknow vectorn (error)"; + break; + } + break; + default : typemess= "unknown (error)"; break; + } + gp_getpar_err("mstpar","must specify value for '%s', expecting %s", + name,typemess); + } + +#ifdef FORTRAN +getpar_(name,type,val,dum1,dum2) +int dum1, dum2; /* dum1 & dum2 are extra args that fortran puts in */ +#else +getpar(name,type,val) +#endif +char *name, *type; +int *val; + { + register char *sptr; + register struct arglist *alptr; + register int i; + double atof(), *dbl; + float *flt; + int h, hno, hyes, found; + char line[MAXLINE], *str, *noname; + + if(FLAGS & END_PAR) + gp_getpar_err("getpar","called after endpar"); + if( (FLAGS & INIT) == 0) + gp_getpar_err("getpar","not initialized with setpar"); + if(FLAGS & VERBOSE) + fprintf(stderr,"getpar: looking for %s\n",name); + + /* The following line corrects a common input error */ + if(type[1]=='v') { type[1]= type[0]; type[0]='v'; } + + + if(*type == 'b') goto boolean; + + h= gp_compute_hash(name); + found=0; + /* search list backwards, stopping at first find */ + for(alptr= ARGLIST +(NLIST-1); alptr >= ARGHEAD; alptr--) + { + if(alptr->hash != h) continue; + if(strcmp(alptr->argname,name) != 0) continue; + str= alptr->argval; + switch(*type) + { + case 'd': + *val= atoi(str); + found=1; + break; + case 'f': + flt= (float *) val; + *flt= atof(str); + found=1; + break; + case 'F': + dbl= (double *) val; + *dbl= atof(str); + found=1; + break; + case 's': + sptr= (char *) val; + while(*str) *sptr++ = *str++; + *sptr= '\0'; + found=1; + break; + case 'v': + found= gp_getvector(str,type,val); + break; + default: + gp_getpar_err("getpar", + "unknown conversion type %s",type); + break; + } + break; + } + goto list; +boolean: + noname= line; + sprintf(noname,"no%s",name); + hno = gp_compute_hash(noname); + hyes= gp_compute_hash( name); + found=0; + /* search list backwards, stopping at first find */ + for(alptr= ARGLIST +(NLIST-1); alptr >= ARGHEAD; alptr--) + { + if(alptr->hash != hno && alptr->hash != hyes) continue; + if(strcmp(alptr->argname, name)== 0) + { + if(alptr->argval[0] == '\0') *val= 1; + else *val= atol(alptr->argval); + found++; + break; + } + if(strcmp(alptr->argname,noname)== 0) + { *val= 0; found++; break; } + } + list: + if(FLAGS & LIST) + { + switch(*type) + { + case 'd': sprintf(line,"(int) = %d",*val); break; + case 'f': flt= (float *)val; + sprintf(line,"(flt) = %14.6e",*flt); break; + case 'F': dbl= (double *)val; + sprintf(line,"(dbl) = %14.6e",*dbl); break; + /* case 's': sprintf(line,"(str) = %s",val); break;*/ + case 's': sprintf(line,"(str) = %s",sptr); break; + case 'b': sprintf(line,"(boo) = %d",*val); break; + case 'v': switch(type[1]) + { + /* should list these out */ + case 'd': sprintf(line,"(int vec)"); + break; + case 'f': sprintf(line,"(flt vec)"); + break; + case 'F': sprintf(line,"(dbl vec)"); + break; + default : sprintf(line," vec type error"); + break; + } + break; + default : sprintf(line," type error"); break; + } + fprintf(LISTFILE,"%16s (%s) %s \n",name, + (found ? "set":"def"),line); + } + return(found); + } +FILE *gp_create_dump(fname,filetype) +char *fname; +char *filetype; + { + FILE *temp; + + if(*fname == '\0') return(stderr); + if(strcmp(fname,"stderr") == 0) return(stderr); + if(strcmp(fname,"stdout") == 0) return(stdout); + if( (temp= fopen(fname,"w")) != NULL) return(temp); + fprintf(stderr,"%s[setpar]: cannot create %s file %s\n", + PROGNAME,filetype,fname); + return(stderr); + } + +gp_close_dump(file) +FILE *file; + { + if(file == stderr || file == stdout) return(0); + fclose(file); + } + +gp_compute_hash(s) +register char *s; + { + register int h; + h= s[0]; + if(s[1]) h |= (s[1])<<8; else return(h); + if(s[2]) h |= (s[2])<<16; else return(h); + if(s[3]) h |= (s[3])<<24; + return(h); + } + +void gp_do_par_file(fname,level) +char *fname; +int level; + { + register char *pl, *pn, *pv; + char t1, t2, line[MAXLINE], name[MAXNAME], value[MAXVALUE]; + FILE *file, *fopen(); + + if(level > MAXPARLEVEL) + gp_getpar_err("setpar","%d (too many) recursive par file",level); + + if( (file=fopen(fname,"r"))==NULL) + gp_getpar_err("setpar","cannot open par file %s",fname); + + while( fgets(line,MAXLINE,file) != NULL ) + { + pl= line; + /* loop over entries on each line */ + loop: while(*pl==' ' || *pl=='\t') pl++; + if(*pl=='\0'|| *pl=='\n') continue; + if(*pl=='#') continue; /* comments on rest of line */ + + /* get name */ + pn= name; + while(*pl != '=' && *pl != '\0' && *pl != ' ' + && *pl != '\t') *pn++ = *pl++; + *pn = '\0'; + if(*pl == '=') pl++; + + /* get value */ + *value= '\0'; + pv= value; + if(*pl=='"' || *pl=='\'') { t1= t2= *pl++; } + else { t1= ' '; t2= '\t'; } + while(*pl!=t1 && *pl!=t2 && + *pl!='\0' && *pl!='\n') *pv++= *pl++; + *pv= '\0'; + if(*pl=='"' || *pl=='\'') pl++; + gp_add_entry(name,value); + if(strcmp("par",name) == 0) + gp_do_par_file(value,level+1); + goto loop; + } + fclose(file); + } + +gp_getpar_err(subname,mess,a1,a2,a3,a4) +char *subname, *mess; +int a1, a2, a3, a4; + { + fprintf(stderr,"\n***** ERROR in %s[%s] *****\n\t", + (PROGNAME == NULL ? "(unknown)" : PROGNAME),subname); + fprintf(stderr,mess,a1,a2,a3,a4); + fprintf(stderr,"\n"); + exit(GETPAR_ERROR); + } +gp_getvector(list,type,val) +char *list, *type; +int *val; + { + register char *p; + register int index, cnt; + char *valptr; + int limit; + int ival, *iptr; + float fval, *fptr; + double dval, *dptr, atof(); + + limit= MAXVECTOR; + if(type[2] == '(' || type[2] == '[') limit= atol(&type[3]); + if(limit <= 0) + gp_getpar_err("getpar","bad limit=%d specified",limit); + index= 0; + p= list; + while(*p != '\0' && index < limit) + { + cnt=1; + backup: /* return to here if we find a repetition factor */ + while(*p == ' ' || *p == '\t') p++; + if(*p == '\0') return(index); + valptr= p; + while( *p != ',' && *p != '*' && *p != 'x' && *p != 'X' && + *p != '\0') p++; + if(*p == '*' || *p == 'x' || *p == 'X') + { + cnt= atol(valptr); + if(cnt <= 0) + gp_getpar_err("getpar", + "bad repetition factor=%d specified", + cnt); + if(index+cnt > limit) cnt= limit - index; + p++; + goto backup; + } + switch(type[1]) + { + case 'd': + iptr= (int *) val; + ival= atol(valptr); + while(cnt--) iptr[index++] = ival; + break; + case 'f': + fptr= (float *) val; + fval= atof(valptr); + while(cnt--) fptr[index++] = fval; + break; + case 'F': + dptr= (double *) val; + dval= atof(valptr); + while(cnt--) dptr[index++] = dval; + break; + default: + gp_getpar_err("getpar", + "bad vector type=%c specified",type[1]); + break; + } + if(*p != '\0') p++; + } + return(index); + } diff --git a/UTILITIES/makefile b/UTILITIES/makefile new file mode 100755 index 0000000..56fc17c --- /dev/null +++ b/UTILITIES/makefile @@ -0,0 +1,58 @@ +ALLFLAGS= -m64 +CFLAGS = +FFLAGS = + +ALL = sac2helm window bin2sac sac2bin fromHelm mkHelm putmech putmt decon sac2columns sac2columns_mt +PROGLIST= get_resp sac2helm window bin2sac sac2bin fromHelm mkHelm putmech putmtsac2columns sac2columns_mt + +all: $(ALL) + +sac2helm: sac2helm.c getpar.c + gcc $(ALLFLAGS) -o sac2helm sac2helm.c getpar.c + \mv sac2helm ../BIN + +sac2bin: sac2bin.c getpar.c + gcc $(ALLFLAGS) -o sac2bin sac2bin.c getpar.c + \mv sac2bin ../BIN + +putmech: putmech_iso.c readhelm.c getpar.c + gcc $(ALLFLAGS) -g -o putmech putmech_iso.c readhelm.c getpar.c -lm + \mv putmech ../BIN + +putmt: putmt.c readhelm.c getpar.c + gcc $(ALLFLAGS) -g -o putmt putmt.c readhelm.c getpar.c -lm + \mv putmt ../BIN + +window: window.c getpar.c + gcc $(ALLFLAGS) -o window window.c getpar.c + \mv window ../BIN + + +fromHelm: fromHelm_new.c + gcc $(ALLFLAGS) -o fromHelm fromHelm_new.c + \mv fromHelm ../BIN + +mkHelm: mkHelm2.c getpar.c + gcc $(ALLFLAGS) -o mkHelm mkHelm2.c getpar.c + \mv mkHelm ../BIN + +decon: deconnew.c deconnew_subs2.c getpar.c + gcc $(ALLFLAGS) -o decon deconnew.c deconnew_subs2.c getpar.c -lm + \mv decon ../BIN + +bin2sac: bin2sac.c getpar.c + gcc $(ALLFLAGS) -o bin2sac bin2sac.c getpar.c + \mv bin2sac ../BIN + +sac2columns: sac2columns.c + gcc $(ALLFLAGS) -o sac2columns sac2columns.c + \mv sac2columns ../BIN + +sac2columns_mt: sac2columns_mt.c + gcc $(ALLFLAGS) -o sac2columns_mt sac2columns_mt.c + \mv sac2columns_mt ../BIN + +##Cleanup routine + +clean: + \rm *.o diff --git a/UTILITIES/mkHelm2.c b/UTILITIES/mkHelm2.c new file mode 100755 index 0000000..aa17482 --- /dev/null +++ b/UTILITIES/mkHelm2.c @@ -0,0 +1,214 @@ +#include +#include +#include +#include + +void setpar(); +int getpar(); +int mstpar(); +void endpar(); +int chkform(); + +#define FORM1a "%8d\n" +#define FORM1b "%8d %s\n" +#define FORM2 "%s\n" +#define FORM3 " %11.4e %11.4e 0 0 0.00\n" +#define FORM4a "%8d %8.5f %11.4e\n" +#define TRUE 1 +#define FALSE 0 + +char form[32] = "(7e14.5)"; +char comment1[80]; + +int main(ac,av) +int ac; char **av; +{ + int nt, nx, ntr, comm1, perline, nread, i, j, l, cnt, left, f_width; + float offset=0.0, t0=0.0, mul=0.0; + float dt, *pv, *vec; + char c_form[512]; + + setpar(ac,av); + mstpar("nt","d",&nt); + mstpar("dt","f",&dt); + if (getpar("ntr","d",&nx) == 0) + getpar("nx","d",&nx); + getpar("mul","f",&mul); + if (getpar("format","s",form)) { + fprintf(stderr,"mkHelm: input format: %s\n",form); + perline = chkform(form,c_form,&f_width); + fprintf(stderr," format used: %s\n",form); + } + else perline = chkform(form,c_form,&f_width); + comm1 = getpar("comment1","s",comment1); + getpar("offset","f",&offset); + getpar("starttime","f",&t0); + endpar(); + ntr=nx; + fprintf(stderr,"ntr=%d\n",ntr); + + vec = (float *) malloc(4*nt); + + + /* write initial header */ + if (comm1) fprintf(stdout,FORM1b,nx,comment1); + else fprintf(stdout,FORM1a,nx); + fprintf(stdout,FORM2,form); + fprintf(stdout,FORM3,offset,t0); + fprintf(stdout,FORM4a,nt,dt,mul); + + /* write data */ + for (l=0 ; l < ntr ; l++) + { + if ((nread = read(0,vec,4*nt)) != 4*nt) + { + fprintf(stderr,"mkHelm: Read error, only %d bytes\n",nread); + exit(-1); + } + cnt = nt / perline; + left = nt % perline; + pv = vec; + for (i=0;i +#include + +#define sign(X,Y) (( (Y) < 0.0 ) ? -fabs(X) : fabs(X) ) +#define PI 3.1415926 +#define DTR 0.0174533 +#define RTD 57.29578 +#define SQRT2 1.414214 +#define EPS 0.00001 +#define SMALLER 0.05 +#define PATD 2 +#define PATC 0 +#define TEXFAC 0.05 +#define DL rad*0.025 + +struct coord { float x, y; }; +struct azphi { float az, phi; }; +float delt1, delt2; +float dip2, al2, strk2; +float dip1, al1, strk1; +struct azphi p, t, b; +int fast; +char ip[4]="P"; +char it[4]="T"; + +mplot(nsta,rsta,rcode,iqual,ev,radc,xdip1,xal1,xstrk1,enam,opt,pro,delt) + +/* + mplot: by RJ Stead + subroutine for mechanism plot, based on JH Whitcomb's focplt + and H Kanamori's mplot + nsta = number of stations, + rsta = station name, + rcode = c or d or n(nodal), + iqual = quality (1 or 2); 2 is higher q and plots a larger symbol, + ev = event azimuth and take-off angle, + radc = radius of the plot in cm (if <0, do fast plot), + dip1 = dip angle, al1 = slip angle, strk1 = strike of plane1, + enam = event id, + opt determines plot option: + opt = 0 1st-motion data only + opt = 1 ist-motion data plus station name + opt = 2 1st-motion data plus nodal lines + opt = 3 1st-motion data, station name plus nodal lines + opt = 4 nodal lines only( p and t axes labelled ) + opt = 5 shade quadrants only + pro = 0 plots on equal area projection + pro = 1 plots on wulff net (equal angle). + delt = absolute position on plot of center of focal sphere in inches +*/ + +char **rsta; +int nsta, *iqual, opt, pro; +char **rcode; +char *enam; +struct azphi *ev; +struct coord delt; +float radc, xdip1, xal1, xstrk1; +{ + static char **iptl; + int i, iline, nplp1, nplp2; + struct azphi pt[2], pl1[100000], pl2[100000], *pp; + float px[3], tx[3], bx[3]; + float xz, yz; + float rad, dip1r, dip2r, azr; + int incr, ok; + int pat2[32], patC[32]; + static int alreadydone=0; + + if (alreadydone == 0) { + iptl = (char **) malloc(4 * 2); + iptl[0] = ip; + iptl[1] = it; + alreadydone = 1; + } + + dip1 = xdip1; al1 = xal1; strk1 = xstrk1; + for (i=0; i<32; i++) pat2[i] = 0; + defpattern(2,pat2); + delt1 = delt.x; + delt2 = delt.y; + fast = 0.; + if (radc < 0) { + fast = 1; + radc *= -1; + } + rad = radc / 2.54; + incr = 1; + if (fast) incr = 10; + + /* plot and label circle */ + i = 360/incr; + circl3a (rad, delt, i); + plot(delt1 - rad - DL, delt2, 0); + plot(delt1 - rad + DL, delt2, 1); + plot(delt1 - DL, delt2, 0); + plot(delt1 + DL, delt2, 1); + plot(delt1 + rad - DL, delt2, 0); + plot(delt1 + rad + DL, delt2, 1); + plot(delt1, delt2 - rad - DL, 0); + plot(delt1, delt2 - rad + DL, 1); + plot(delt1, delt2 - DL, 0); + plot(delt1, delt2 + DL, 1); + plot(delt1, delt2 + rad - DL, 0); + plot(delt1, delt2 + rad + DL, 1); + + /* plot first motion data */ + if (opt < 4) { + iline = 0; + plambm(nsta, rsta, rcode, iqual, ev, rad, opt, iline, pro); + } + + /* plot nodal lines */ + if (opt > 1) { + + /* solve for planes, stress axes */ + ok = fptr(&strk1,&dip1,&al1,&strk2,&dip2,&al2,px,tx,bx,0); + if (ok < 0) return(-1); + + /* set dips to range 0 90.0) { + dip1 = 180.0 - dip1; + strk1 = 180.0 + strk1; + al1 = 360.0 - al1; + } + if (dip2 > 90.0) { + dip2 = 180.0 - dip2; + strk2 = 180.0 + strk2; + al2 = 360.0 - al2; + } + dip1r = dip1 * DTR; + dip2r = dip2 * DTR; + + /* find plane #1 */ + nplp1 = 181; + if (fast) nplp1 = 19; + pl1->az = strk1; + pl1->phi = 90.0; + + /* for vertical plane, 2 points suffice */ + if (fabs(dip1) > 89.9) { + pl1[1].az = pl1->az + 180.0; + pl1[1].phi = 90.0; + nplp1 = 2; + } + else { + pp = pl1; + for (i = 1; i<(nplp1-1); i++) { + pp++; + pp->az = strk1 + incr * (float) i; + pp->phi = 90.0; + if (fabs(dip1) >= 0.01) { + azr = (pp->az - strk1) * DTR; + pp->phi = RTD * atan(1.0 / (tan(dip1r) + * sin(azr))); + } + } + pp++; + pp->az = strk1 + 180.; + pp->phi = 90.; + } + + /* find plane #2 */ + nplp2 = 181; + if (fast) nplp2 = 19; + pl2->az = strk2; + pl2->phi = 90.0; + if (fabs(dip2) > 89.9) { + pl2[1].az = pl2->az + 180.0; + pl2[1].phi = 90.0; + nplp2 = 2; + } + else { + pp = pl2; + for (i = 1; i <(nplp2-1); i++) { + pp++; + pp->az = strk2 + incr * (float) i; + pp->phi = 90.0; + if(fabs(dip2) >= 0.01) { + azr = (pp->az - strk2) * DTR; + pp->phi = RTD * atan(1.0 / (tan(dip2r) + * sin(azr))); + } + } + pp++; + pp->az = strk2 + 180.; + pp->phi = 90.; + } + + /* plot P and T axes */ + if (opt < 5) { + pt->az = p.az; + pt[1].az = t.az; + pt->phi = 180.0 - p.phi; + pt[1].phi = 180.0 - t.phi; + iline = 2; + plambm(2,iptl,rcode,iqual,pt,rad,opt,iline,pro); + } + + /* shade quadrants */ + else { + setbrushpat(nsta); + mshade(nplp1,nplp2,pl1,pl2,al1,al2,rad,pro); + } + + /* plot nodal lines */ + iline = 1; + plambm(nplp1,rsta,rcode,iqual,pl1,rad,opt,iline,pro); + plambm(nplp2,rsta,rcode,iqual,pl2,rad,opt,iline,pro); + } + + /* print mechanism label */ + settextsize(rad*TEXFAC); + xz = delt1 - rad; + yz = delt2 + 1.3 * rad; + text(xz, yz, "%s", enam); + return(0); +} + +plambm(npts, rsta, rcode, iqual, ar, rad, opt, iline, pro) + +/* + plamb plots in stereographic projection the points in ar for + a circle of radius rad inches; + iline = 0 plot first-motion data; + iline = 1 plot nodal lines; + iline = 2 plot p and t axes; + pro = 0 equal area projection; + pro = 1 wulff net; + opt determines the plot option, see mplot; + ar: angular spherical location of each point (in degrees); + rad: desired radius of plotted circle (inches); + npts: the number of points to be plotted; + delt1: x offset of center of plotted circle from lower left + corner of plotter page (inches); + delt2: y offset of center. +*/ + +char **rsta; +char **rcode; +int *iqual, npts, opt, iline, pro; +struct azphi *ar; +float rad; +{ + struct coord axy[100000], *paxy; + struct coord ptaxes[4]; + float xc, yc, rsize; + int i, nspar; + char c='c', d='d', n='x'; + float smrat=SMALLER; + int ipatc=PATC; + int ipatd=PATD; + + nspar = 0; + if (opt == 1 || opt == 3) nspar = 1; + net_coord(ar,axy,pro,npts,rad); + +/* plot first motions and station name */ + + switch (iline) { + + case 0: + paxy = axy; + settextsize(rad*smrat); + settextcenter(0,1); + for (i = 0; i < npts; i++) { + xc = paxy->x; + yc = paxy->y; + rsize = rad * smrat * 0.3; + if (iqual[i] == 2) rsize = rsize * 2.0; + if (iqual[i] == 3) rsize = rsize * 1.9; + if (iqual[i] == 4) rsize = rsize * 1.8; + if (iqual[i] == 5) rsize = rsize * 1.7; + if (iqual[i] == 6) rsize = rsize * 1.6; + if (iqual[i] == 7) rsize = rsize * 1.5; + if (iqual[i] == 8) rsize = rsize * 1.4; + if (iqual[i] == 9) rsize = rsize * 1.3; +/* if (rcode[i] == c)*/ + if (!strncmp(rcode[i],"c",1)) + { + circl3(1.0 * rsize, *paxy, 32, ipatc); + } +/* if (rcode[i] == d)*/ + if (!strncmp(rcode[i],"d",1)) + { + circl3(1.0 * rsize, *paxy, 32, ipatd); + } +/* if (rcode[i] == n) {*/ + if (!strncmp(rcode[i],"n",1)) + { + plot(xc - 0.6*rsize,yc - rsize,0); + plot(xc + 0.6*rsize,yc + rsize,1); + plot(xc - 0.6*rsize,yc + rsize,0); + plot(xc + 0.6*rsize,yc - rsize,1); + } + if (nspar == 1) + text(xc + 1.1*rsize,yc,"%s %s",rsta[i],rcode[i]); + circl3a(1.0 * rsize, *paxy, 32); + paxy++; + } + settextcenter(0,0); + break; + + case 1: + plot(axy->x, axy->y, 0); + paxy = axy; + for (i = 1; i < npts; i++) { + paxy++; + plot(paxy->x, paxy->y, 1); + } + break; + + case 2: + rsize = rad * smrat; + paxy = axy; + settextsize(rsize * 0.8); + setbrushpat(0); + for (i = 0; i < npts; i++) { + /* + symbol(paxy->x,paxy->y,8,rsize,0.0); + */ + setbrushpat(0); + ptaxes[0].x = paxy->x+rsize/3.; + ptaxes[0].y = paxy->y; + ptaxes[1].x = paxy->x; + ptaxes[1].y = paxy->y+rsize/2.; + ptaxes[2].x = paxy->x-rsize/3.; + ptaxes[2].y = paxy->y; + ptaxes[3].x = paxy->x; + ptaxes[3].y = paxy->y-rsize/2.; + polyfill(ptaxes,4); + if(opt >= 3) + text(paxy->x + 0.7*rsize,paxy->y,"%s",rsta[i]); + paxy++; + } + break; + } +} + +circl3a (rad, cent, kpts) +float rad; +struct coord cent; +int kpts; +{ + float x, y, theta; + float hold; + int l; + + x= cent.x + rad; + plot (x, cent.y, 0); + hold = 2. * PI / kpts; + for (l = 1; l <= kpts; l++) { + theta = l * hold; + x = rad * cos(theta) + cent.x; + y = rad * sin(theta) + cent.y; + plot (x, y, 1); + } +} + +circl3 (rad, cent, kpts, ipat) +float rad; +struct coord cent; +int kpts, ipat; +{ + struct coord circ[361]; + float theta; + float hold; + int l; + + if (kpts <= 3 || kpts > 360) kpts = 360; + hold = 2. * PI / kpts; + for (l = 0; l < kpts; l++) { + theta = l * hold; + circ[l].x = cent.x + rad * cos(theta); + circ[l].y = cent.y + rad * sin(theta); + } + setbrushpat(ipat); + polyfill(circ,kpts); +} + +fptr(f1d, d1d, al1d, f2d, d2d, al2d, px, tx, bx, id) + +/* + f1d, f2d: strike in degrees; + did, d2d: dip in degrees; + al1, al2: slip angle in degrees; + px: pressure axis in geographical coordinates; + tx: tension axis in geograph. coordinates; + bx: null axis in geograph. coordinates; + geograph. coord. are: + north = x1, west = x2, vertical = x3; + d1d should be 90=>d1d>0; + p, t, b: azimuth and polar angle (degrees) of pressure, tension and null; + if id = 0 f1d, d1d, al1d are given, calculate other parameters; + if id != 0 f1d, d1d, f2d, d2d are given, calculate other parameters; + al1d is uncertain by 180.0; + by RJ Stead, based on H Kanamori's fptr subroutine. +*/ + +float *f1d, *d1d, *al1d, *f2d, *d2d, *al2d; +float *px, *tx, *bx; +int id; +{ + float a[3], an[3]; + float f1, d1, al1, f2, d2, col, sil, cod, sid, cof, sif; + float cc1, cc2, cc3, pck, tck, bck, atest1, atest2, c3, c4, c5; + float c0 = 0.70711; + float c1 = 57.29578; + float c2 = 0.0174533; + int i, ia3, ian3; + + f1 = *f1d * c2; + d1 = *d1d * c2; + al1 = *al1d * c2; + f2 = *f2d * c2; + d2 = *d2d * c2; + al2 = *al2d * c2; + col = cos(al1); + sil = sin(al1); + cod = cos(d1); + sid = sin(d1); + cof = cos(f1); + sif = sin(f1); + if (id != 0) { + cc3 = cod * cos(d2) + sid * sin(d2) * cos(f2 - f1); + if (fabs(cc3) >= 0.05) { + fprintf(stderr,"two planes are not orthogonal\n"); + return(-1); + } + cc1 = cos(d2) / sid; + cc2 = sin(d2) * sin(f1 - f2); + al1 = atan2(cc1, cc2); + col = cos(al1); + sil = sin(al1); + } + a[0] = col * cof + sil * cod * sif; + a[1] = -col * sif + sil * cod * cof; + a[2] = sil * sid; + an[0] = -sid * sif; + an[1] = -sid * cof; + an[2] = cod; + bx[0] = an[1] * a[2] - an[2] * a[1]; + bx[1] = an[2] * a[0] - an[0] * a[2]; + bx[2] = an[0] * a[1] - an[1] * a[0]; + tx[0] = (a[0] + an[0]) * c0; + tx[1] = (a[1] + an[1]) * c0; + tx[2] = (a[2] + an[2]) * c0; + px[0] = (an[0] - a[0]) * c0; + px[1] = (an[1] - a[1]) * c0; + px[2] = (an[2] - a[2]) * c0; + for (i = 0; i < 3; i++) { + if ( fabs(px[i]) >= 1.0 ) px[i] = sign(1.0, px[i]); + if ( fabs(tx[i]) >= 1.0 ) tx[i] = sign(1.0, tx[i]); + if ( fabs(bx[i]) >= 1.0 ) bx[i] = sign(1.0, bx[i]); + } + p.az = 0.0; + t.az = 0.0; + b.az = 0.0; + pck = px[0] * px[0] + px[1] * px[1]; + if (pck > 0.000001) p.az = (-atan2(px[1], px[0])) * c1; + tck = tx[0] * tx[0] + tx[1] * tx[1]; + if (tck > 0.000001) t.az = (-atan2(tx[1], tx[0])) * c1; + bck = bx[0] * bx[0] + bx[1] * bx[1]; + if (bck > 0.000001) b.az = (-atan2(bx[1], bx[0])) * c1; + p.phi = acos(px[2]) * c1; + t.phi = acos(tx[2]) * c1; + b.phi = acos(bx[2]) * c1; + atest1 = fabs(an[0]) + fabs(an[1]); + atest2 = fabs(a[0]) + fabs(a[1]); + if (atest1 <= 0.0 || atest2 <= 0.0) { + /* horizontal fault or vertical slip */ + *f1d = c1 * f1; + *d1d = c1 * d1; + *al1d = c1 * al1; + if (*d1d > 90) { + *d1d = 180. - *d1d; + *f1d += 180.; + *al1d = 360. - *al1d; + } + *f2d = -(*f1d); + *d2d = 90. - *d1d; + *al2d = *al1d; + return(0); + } + d2 = acos(a[2]); + c3 = -sid * col; + al2 = atan2(cod, c3); + ia3 = a[2]; + ian3 = an[2]; + if (ian3 == 0 || ia3 == 0) + f2 = atan2(-a[0], -a[1]); + else { + c4 = -1.0 / (tan(d1) * tan(d2)); + c5 = col / sin(d2); + f2 = f1 - atan2(c5, c4); + } + *f1d = c1 * f1; + *d1d = c1 * d1; + *al1d = c1 * al1; + *f2d = c1 * f2; + *d2d = c1 * d2; + *al2d = c1 * al2; + if (*d1d > 90.0) { + *d1d = 180.0 - *d1d; + *f1d = *f1d + 180.0; + *al1d = 360.0 - *al1d; + } + if (*d2d <= 90.0) return(1); + *d2d = 180.0 - *d2d; + *f2d = *f2d + 180.0; + *al2d = 360.0 - *al2d; + return(1); +} + +net_coord(in,out,pro,n,rad) +struct azphi *in; +struct coord *out; +int pro, n; +float rad; +{ + float r, az, phi; + + while (n--) { + out->x = delt1; + out->y = delt2; + if (fabs(in->phi) > EPS) { + az = DTR * in->az; + phi = DTR * in->phi / 2.; + if (in->phi > 90.) az += PI; + if (in->phi > 90.) phi = PI / 2. - phi; + if (pro == 0) r = SQRT2 * rad * sin(phi); + else r = rad * tan(phi); + out->x += r * sin(az); + out->y += r * cos(az); + } + out++; + in++; + } +} + +mshade(n1,n2,plane1,plane2,rake1,rake2,rad,pro) + +/* shades quadrants of focal sphere */ +/* + Uses convention strike to left as you look down dip, + 0az = plane1->az; + area->phi = plane1->phi; + total = 1; + pa = area; + pa++; + last = plane2->az; + lasttest = sin(DTR*(last - first)); + which1 = cos(DTR*rake1); + which2 = cos(DTR*rake2); + if (fabs(which1)az = plane2->az; + area->phi = plane2->phi; + which1 = sin(DTR*rake2); + } + if (which1 > 0.) { + if (first < 180.) first += 180.; + else first -= 180.; + area->az = first; + } + for (i=0; i<180; i += di) { + first += incr; + pa->az = first; + pa->phi = 90.; + pa++; + } + total = 180 / di; + net_coord(area,areaxy,pro,total,rad); + polyfill(areaxy,total); + return; + } + + if (which1 > 0.) { + /* thrust */ + pp1 = plane1; + pa = area; + for (i=0; i<(n1-1); i++) { + pa->az = pp1->az; + pa->phi = pp1->phi; + pa++; + pp1++; + } + pp2 = plane2; + for (i=0; i<(n2-1); i++) { + pa->az = pp2->az; + pa->phi = pp2->phi; + pa++; + pp2++; + } + total = n1 + n2 - 2; + net_coord(area,areaxy,pro,total,rad); + polyfill(areaxy,total); + return; + } + + /* normal fault */ + pa = area; + pp1 = plane1; + for (i=0; i<(n1-1); i++) { + pa->az = pp1->az; + pa->phi = pp1->phi; + pa++; + pp1++; + } + first = plane1->az+180.0; + for (i=0; i<(n1-1); i++) { + pa->az = first; + pa->phi = 90.; + first -= incr; + pa++; + } + total = 2*n1 - 2; + net_coord(area,areaxy,pro,total,rad); + polyfill(areaxy,total); + pa = area; + pp2 = plane2; + for (i=0; i<(n2-1); i++) { + pa->az = pp2->az; + pa->phi = pp2->phi; + pa++; + pp2++; + } + first = plane2->az+180.0; + for (i=0; i<(n2-1); i++) { + pa->az = first; + pa->phi = 90.; + first -= incr; + pa++; + } + total = 2*n2 - 2; + net_coord(area,areaxy,pro,total,rad); + polyfill(areaxy,total); + return; + } + + /* oblique- or strike-slip */ + /* area #1 */ + lasttest *= which1; + if (lasttest < 0.) last = plane2[n2-1].az; + cnt0 = (int) (last - first); + if (which1 < 0.) cnt0 = (int) (first - last); + notdone = 1; + while (notdone) { + if (cnt0 < 0) cnt0 += 180; + else if (cnt0 >= 180) cnt0 -= 180; + else notdone = 0; + } + cnt0 /= di; + pincr = incr; + if (which1 < 0.) pincr *= -1.; + for (i=0; iaz = first; + pa->phi = 90.; + pa++; + } + total += cnt0; + pa->az = last; + pa->phi = 90.; + pa++; + total++; + pp2 = plane2; + xincr = 1; + if (lasttest < 0.) { + pp2 += n2 - 1; + xincr = -1; + } + if (n2 != 2) { + /* leg #2 */ + pp2 += xincr; + cnt1 = 0; + notdone = 1; + baz = b.az; + while (notdone) { + if (baz < plane2->az) baz += 180.; + else if (baz >= (plane2->az+180)) baz -= 180.; + else notdone = 0; + } + while (fabs(pp2->az - baz) > incr) { + pa->az = pp2->az; + pa->phi = pp2->phi; + pa++; + cnt1++; + pp2 += xincr; + } + total += cnt1; + } + pa->az = b.az; + pa->phi = 180. - b.phi; + pa++; + total++; + pp1 = plane1; + if (n1 != 2) { + /* leg #3 */ + pp1++; + cnt2 = 0; + notdone = 1; + baz = b.az; + while (notdone) { + if (baz < plane1->az) baz += 180.; + else if (baz >= (plane1->az+180)) baz -= 180.; + else notdone = 0; + } + while (fabs(pp1->az - baz) > incr) { + pp1++; + cnt2++; + } + pp1--; + for (i=0; iaz = pp1->az; + pa->phi = pp1->phi; + pa++; + pp1--; + } + total += cnt2; + } + net_coord(area,areaxy,pro,total,rad); + polyfill(areaxy,total); + + /* area #2 */ + first = area->az = plane1[n1-1].az; + area->phi = plane1[n1-1].phi; + total = 1; + pa = area; + pa++; + last = plane2[n2-1].az; + if (lasttest < 0.) last = plane2->az; + total += cnt0; + while (cnt0--) { + /* leg #4 */ + first += pincr; + pa->az = first; + pa->phi = 90.; + pa++; + } + pa->az = last; + pa->phi = 90.; + pa++; + total++; + pp2 = plane2; + xincr = -xincr; + if (lasttest >= 0.) pp2 += n2 - 1; + if (n2 != 2) { + /* leg #5 */ + pp2 += xincr; + cnt1 = n2 - cnt1 - 2; + total += cnt1; + while (cnt1--) { + pa->az = pp2->az; + pa->phi = pp2->phi; + pa++; + pp2 += xincr; + } + } + pa->az = b.az; + pa->phi = 180. - b.phi; + pa++; + total++; + pp1 = plane1; + pp1 += n1 - 1; + if (n1 != 2) { + /* leg #6 */ + cnt2 = n1 - cnt2 - 2; + pp1 -= cnt2; + total += cnt2; + while (cnt2--) { + pa->az = pp1->az; + pa->phi = pp1->phi; + pa++; + pp1++; + } + } + net_coord(area,areaxy,pro,total,rad); + polyfill(areaxy,total); + return; +} diff --git a/UTILITIES/putmech.dSYM/Contents/Info.plist b/UTILITIES/putmech.dSYM/Contents/Info.plist new file mode 100644 index 0000000..7be5c06 --- /dev/null +++ b/UTILITIES/putmech.dSYM/Contents/Info.plist @@ -0,0 +1,20 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleIdentifier + com.apple.xcode.dsym.putmech + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + dSYM + CFBundleSignature + ???? + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/UTILITIES/putmech.dSYM/Contents/Resources/DWARF/putmech b/UTILITIES/putmech.dSYM/Contents/Resources/DWARF/putmech new file mode 100644 index 0000000..6edae68 Binary files /dev/null and b/UTILITIES/putmech.dSYM/Contents/Resources/DWARF/putmech differ diff --git a/UTILITIES/putmech_iso.c b/UTILITIES/putmech_iso.c new file mode 100755 index 0000000..e76442f --- /dev/null +++ b/UTILITIES/putmech_iso.c @@ -0,0 +1,112 @@ +#include +#include +#include +#include +#include +#include + +void setpar(); +int getpar(); +int mstpar(); +void endpar(); +void readhelm(); + +int main(ac,av) +int ac; +char **av; + { + int i,n,ntr,*npts, nn; + int ihd[40], fdt, fdr, fdz; + float A[6], *dt, pi=3.14159, *tan,*rad,*ver, *tmp; + float fhd[70], azimuth, strike, rake, dip; + float Mo=1.0e+20, isoMo=0.0; + char infile[120],outname[120], name[120]; + char chd[8][24]; + + for(i=0;i<40;i++) ihd[i]=-12345; + for(i=0;i<70;i++) fhd[i]=-12345.00; + for(i=0;i<8;i++) sprintf(chd[i],"-12345 -12345 -12345 "); + /*Set essential sac parameters*/ + ihd[35]=1; /*Sets file to evenly spaced*/ + ihd[15]=1; /*Sets file type to Timeseries*/ + ihd[6]=6; /*Variable Name Internal */ + fhd[5]=0.0; /*B variable*/ + + + setpar(ac,av); + mstpar("in","s",infile); + mstpar("out","s",outname); + mstpar("azimuth","f",&azimuth); + mstpar("strike","f",&strike); + mstpar("rake","f",&rake); + mstpar("dip","f",&dip); + getpar("moment","f",&Mo); + getpar("isomoment","f",&isoMo); + endpar(ac,av); + + tan=(float *)malloc(sizeof(float)*1*10000); + rad=(float *)malloc(sizeof(float)*1*10000); + ver=(float *)malloc(sizeof(float)*1*10000); + tmp=(float *)malloc(sizeof(float)*10*10000); + npts=(int *)malloc(sizeof(int)*10); + dt =(float *)malloc(sizeof(float)*10); + + strike = azimuth - strike; + fprintf(stderr,"str=%.2f rake=%.2f dip=%.2f\n",strike, rake, dip); + strike *= pi/180.0; + rake *= pi/180.0; + dip *= pi/180.0; + + Mo /= 1.0e+20; + isoMo /=1.0e+20; + A[0]=sin(2.0*strike)*cos(rake)*sin(dip) + 0.5*cos(2.0*strike)*sin(rake)*sin(2.0*dip); + A[1]=cos(strike)*cos(rake)*cos(dip) - sin(strike)*sin(rake)*cos(2.0*dip); + A[2]=0.5*sin(rake)*sin(2.0*dip); + A[3]=cos(2.0*strike)*cos(rake)*sin(dip) - 0.5*sin(2.0*strike)*sin(rake)*sin(2.0*dip); + A[4]=sin(strike)*cos(rake)*cos(dip) + cos(strike)*sin(rake)*cos(2.0*dip); + A[4] *= -1.0; + +fprintf(stderr,"A1=%f A2=%f A3=%f A4=%f A5=%f\n",A[0],A[1],A[2],A[3],A[4]); + + readhelm(infile,&ntr,npts,dt,tmp); + if(ntr != 10) + { + fprintf(stderr,"GF file only 8 vectors\n"); + /*exit(-1);*/ + } + + nn=npts[0]; + fprintf(stderr,"nn=%d\n",nn); + ihd[9]=nn; + fhd[0]=dt[0]; + + for (i=0; i < nn; i++) + { + tan[i]=Mo*(A[3]*tmp[i] + A[4]*tmp[i+nn]); + rad[i]=Mo*(A[0]*tmp[i+2*nn] + A[1]*tmp[i+3*nn] + A[2]*tmp[i+4*nn]) + + isoMo*tmp[i+8*nn]; + ver[i]=-1.0*Mo*(A[0]*tmp[i+5*nn] + A[1]*tmp[i+6*nn] + A[2]*tmp[i+7*nn]) + + isoMo*tmp[i+9*nn]; + } + +sprintf(name,"%s.tan",outname); +fdt=open(name,O_WRONLY | O_CREAT | O_TRUNC, 0644); +sprintf(name,"%s.rad",outname); +fdr=open(name,O_WRONLY | O_CREAT | O_TRUNC, 0644); +sprintf(name,"%s.ver",outname); +fdz=open(name,O_WRONLY | O_CREAT | O_TRUNC, 0644); + +write(fdt,fhd,70*4); /*Write Sac Float Field*/ +write(fdt,ihd,40*4); /*Write Sac Int Field*/ +write(fdt,chd,24*8); /*Write Sac Char. Field*/ +write(fdt,tan,ihd[9]*4); /*Write timeseries file*/ +write(fdr,fhd,70*4); /*Write Sac Float Field*/ +write(fdr,ihd,40*4); /*Write Sac Int Field*/ +write(fdr,chd,24*8); /*Write Sac Char. Field*/ +write(fdr,rad,ihd[9]*4); /*Write timeseries file*/ +write(fdz,fhd,70*4); /*Write Sac Float Field*/ +write(fdz,ihd,40*4); /*Write Sac Int Field*/ +write(fdz,chd,24*8); /*Write Sac Char. Field*/ +write(fdz,ver,ihd[9]*4); /*Write timeseries file*/ + +}/*END*/ diff --git a/UTILITIES/putmt.c b/UTILITIES/putmt.c new file mode 100755 index 0000000..42bf550 --- /dev/null +++ b/UTILITIES/putmt.c @@ -0,0 +1,146 @@ +#include +#include +#include +#include +#include +#include + +void setpar(); +int getpar(); +int mstpar(); +void endpar(); +void readhelm(); + +int main(ac,av) +int ac; +char **av; + { + int i,n,ntr,*npts, nn; + int ihd[40], fdt, fdr, fdz; + float A[6], *dt, pi=3.14159, *tan,*rad,*ver, *tmp; + float fhd[70], Az,mxx,mxy,mxz,myy,myz,mzz,trace; + float Mo=1.0e+20; + char infile[120],outname[120], name[120]; + char chd[8][24]; + + for(i=0;i<40;i++) ihd[i]=-12345; + for(i=0;i<70;i++) fhd[i]=-12345.00; + for(i=0;i<8;i++) sprintf(chd[i],"-12345 -12345 -12345 "); + /*Set essential sac parameters*/ + ihd[35]=1; /*Sets file to evenly spaced*/ + ihd[15]=1; /*Sets file type to Timeseries*/ + ihd[6]=6; /*Variable Name Internal */ + fhd[5]=0.0; /*B variable*/ + + + setpar(ac,av); + mstpar("in","s",infile); + mstpar("out","s",outname); + mstpar("azimuth","f",&Az); + mstpar("mxx","f",&mxx); + mstpar("mxy","f",&mxy); + mstpar("mxz","f",&mxz); + mstpar("myy","f",&myy); + mstpar("myz","f",&myz); + mstpar("mzz","f",&mzz); + getpar("moment","f",&Mo); + endpar(ac,av); + + fprintf(stderr,"Reading Aki Convention\n"); + fprintf(stderr,"Converting Aki to Langston\n"); + trace=(mxx+myy+mzz)/3.0; + mxx*=-1.0; + myy*=-1.0; + mzz*=-1.0; + mxy*=-1.; + mxz*=-1.; + myz*=-1.; + + Az *= pi/180.0; + + tan=(float *)malloc(sizeof(float)*1*80000); + rad=(float *)malloc(sizeof(float)*1*80000); + ver=(float *)malloc(sizeof(float)*1*80000); + tmp=(float *)malloc(sizeof(float)*10*80000); + for(i=0; i< 8000;i++) + tan[i]=rad[i]=ver[i]=0.0; + for(i=0; i< 8000*10;i++) + tmp[i]=0.0; + + npts=(int *)malloc(sizeof(int)*10); + dt =(float *)malloc(sizeof(float)*10); + + Mo /= 1.0e+20; + + readhelm(infile,&ntr,npts,dt,tmp); + + nn=npts[0]; + fprintf(stderr,"nn=%d\n",nn); + ihd[9]=nn; + fhd[0]=dt[0]; + + for (i=0; i < nn; i++) /*Apply the -1 correction to the zss,zds, zdd, zexp*/ + { + tmp[i+5*nn] *= -1.0; + tmp[i+6*nn] *= -1.0; + tmp[i+7*nn] *= -1.0; + tmp[i+8*nn] *= -1.0; + tmp[i+9*nn] *= -1.0; + } + +fprintf(stderr,"%f %f %f %f %f %f\n",mxx,mxy,mxz,myy,myz,mzz); + for (i=0; i < nn; i++) + { + tan[i]=Mo*(mxx*0.5*tmp[i]*sin(2*Az) + - myy*0.5*tmp[i]*sin(2*Az) + - mxy*tmp[i]*cos(2*Az) + - mxz*tmp[i+nn]*sin(Az) + + myz*tmp[i+nn]*cos(Az)); + + rad[i]= Mo*(mxx*0.166667*tmp[i+4*nn] + - mxx*0.5*tmp[i+2*nn]*cos(2*Az) + + mxx*0.33333*tmp[i+8*nn] + + myy*0.166667*tmp[i+4*nn] + + myy*0.5*tmp[i+2*nn]*cos(2*Az) + + myy*0.33333*tmp[i+8*nn] + + mzz*0.33333*tmp[i+8*nn] + - mzz*0.33333*tmp[i+4*nn] + - mxy*tmp[i+2*nn]*sin(2*Az) + + mxz*tmp[i+3*nn]*cos(Az) + + myz*tmp[i+3*nn]*sin(Az)); + + ver[i]= Mo*(mxx*0.166667*tmp[i+7*nn] + - mxx*0.5*tmp[i+5*nn]*cos(2*Az) + + mxx*0.33333*tmp[i+9*nn] + + myy*0.166667*tmp[i+7*nn] + + myy*0.5*tmp[i+5*nn]*cos(2*Az) + + myy*0.33333*tmp[i+9*nn] + + mzz*0.33333*tmp[i+9*nn] + - mzz*0.33333*tmp[i+7*nn] + - mxy*tmp[i+5*nn]*sin(2*Az) + + mxz*tmp[i+6*nn]*cos(Az) + + myz*tmp[i+6*nn]*sin(Az)); + + } + +sprintf(name,"%s.tan",outname); +fdt=open(name,O_WRONLY | O_CREAT | O_TRUNC, 0644); +sprintf(name,"%s.rad",outname); +fdr=open(name,O_WRONLY | O_CREAT | O_TRUNC, 0644); +sprintf(name,"%s.ver",outname); +fdz=open(name,O_WRONLY | O_CREAT | O_TRUNC, 0644); + +write(fdt,fhd,70*4); /*Write Sac Float Field*/ +write(fdt,ihd,40*4); /*Write Sac Int Field*/ +write(fdt,chd,24*8); /*Write Sac Char. Field*/ +write(fdt,tan,ihd[9]*4); /*Write timeseries file*/ +write(fdr,fhd,70*4); /*Write Sac Float Field*/ +write(fdr,ihd,40*4); /*Write Sac Int Field*/ +write(fdr,chd,24*8); /*Write Sac Char. Field*/ +write(fdr,rad,ihd[9]*4); /*Write timeseries file*/ +write(fdz,fhd,70*4); /*Write Sac Float Field*/ +write(fdz,ihd,40*4); /*Write Sac Int Field*/ +write(fdz,chd,24*8); /*Write Sac Char. Field*/ +write(fdz,ver,ihd[9]*4); /*Write timeseries file*/ + +}/*END*/ diff --git a/UTILITIES/putmt.dSYM/Contents/Info.plist b/UTILITIES/putmt.dSYM/Contents/Info.plist new file mode 100644 index 0000000..c358148 --- /dev/null +++ b/UTILITIES/putmt.dSYM/Contents/Info.plist @@ -0,0 +1,20 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleIdentifier + com.apple.xcode.dsym.putmt + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + dSYM + CFBundleSignature + ???? + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/UTILITIES/putmt.dSYM/Contents/Resources/DWARF/putmt b/UTILITIES/putmt.dSYM/Contents/Resources/DWARF/putmt new file mode 100644 index 0000000..66dc747 Binary files /dev/null and b/UTILITIES/putmt.dSYM/Contents/Resources/DWARF/putmt differ diff --git a/UTILITIES/readhelm.c b/UTILITIES/readhelm.c new file mode 100755 index 0000000..218dc9e --- /dev/null +++ b/UTILITIES/readhelm.c @@ -0,0 +1,235 @@ +/* Routine to read Helmberger Format Seismograms and to return */ +/* information about number of traces, number of time points, */ +/* dt, and the data vector */ +#include +#include +#include +#include +#include + +int chkform(); + +#define FORM1a "%8d\n" +#define FORM1b "%8d %s\n" +#define FORM2 "%s\n" +#define FORM3 " %11.4e %11.4e 0 0 0.00\n" +#define FORM4a "%8d %8.5f %11.4e\n" +#define TRUE 1 +#define FALSE 0 + +char form[32] = "(6e12.5)"; +FILE *fopen(),*inf,*of; +int fclose(); + +void readhelm(in,N,NT,DT,vec1) +char *in; +int *N, *NT; +float *DT; +float *vec1; +{ + int nt, orignt, nx,nxx, perline, i, j, cnt, left, f_width, ss, I; + int nn, ntemp, test=0; + float BA, dt, mul=0.0, *pv, *vec, *p, dd, tt, mxtt=0.0; + float pi=3.14159265; + char c_form[512],c_form1[128],c_form2[128]; + char line[128]; + char out[50]; + + inf=fopen(in,"r"); + + /* read initial header */ + fgets(line,100,inf); + sscanf(line,"%d",&nxx); + *N=nxx; /*send number of traces*/ + fgets(form,100,inf); + perline = chkform(form,c_form1,c_form2,&f_width); + + + nx=nxx; + + for(I=0;I +#include +#include +#include +#include + +int chkform(); + +#define FORM1a "%8d\n" +#define FORM1b "%8d %s\n" +#define FORM2 "%s\n" +#define FORM3 " %11.4e %11.4e 0 0 0.00\n" +#define FORM4a "%8d %8.5f %11.4e\n" +#define TRUE 1 +#define FALSE 0 + +char form[32] = "(6e12.5)"; +FILE *fopen(),*inf,*of; +int fclose(); + +void readhelm(in,N,NT,DT,vec1) +char *in; +int *N, *NT; +float *DT; +float *vec1; +{ + int nt, orignt, nx,nxx, perline, i, j, cnt, left, f_width, ss; + int nn, ntemp, test=0; + float BA, dt, mul=0.0, *pv, *vec, *p, dd, tt, mxtt=0.0; + float pi=3.14159265; + char c_form[512],c_form1[128],c_form2[128]; + char line[128]; + char out[50]; + + inf=fopen(in,"r"); + + /* read initial header */ + fgets(line,100,inf); + sscanf(line,"%d",&nxx); + *N=nxx; /*send number of traces*/ + fgets(form,100,inf); + perline = chkform(form,c_form1,c_form2,&f_width); + /*fprintf(stderr,"perline=%d form=%s\n",perline,form);*/ + + nx=nxx; + while (nx) { + fgets(line,100,inf); + sscanf(line,"%f %f",&dd,&tt); + fgets(line,100,inf); + sscanf(line,"%d %f",&nt,&dt); + *NT++ = nt; + *DT++ = dt; +/* fprintf(stderr,"nt=%d dt=%f\n",nt,dt);*/ + + if(nx == nxx) + { + orignt = nt; + vec = (float *) malloc(4*nt); + p=vec1; + } + /* Zero out so variable length and dt's are read + else + { + if (nt != orignt) { + fprintf (stderr, "nt is not the same throughout\n"); + exit(1); + } + } + */ + cnt = nt / perline; + left = nt % perline; + pv = vec; + for (i=0;i +#include +#include +#include +#include + +void setpar(); +int getpar(); +int mstpar(); +void endpar(); + +/*convert SAC binary to headerless binary file (fromHelm.c generated) */ +/*or Helmberger ascii */ +int main(ac,av) + int ac; + char **av; + { + int i, npts, ihd[40],fd1, fd2, binflag=1; + float dt, fhd[70], *tr; + char chd[8][24], in[100], out[100], line[100]; + +setpar(ac,av); +mstpar("in","s",in); +mstpar("out","s",out); +getpar("binflag","d",&binflag); +endpar(); + +fd1=open(in,O_RDONLY,0644); +fd2=open(out,O_WRONLY | O_CREAT | O_TRUNC,0644); + + +read(fd1,fhd,70*4); /*Read Sac Float Field*/ +read(fd1,ihd,40*4); /*Read Sac Int Field*/ +read(fd1,chd,24*8); /*Read Sac Char. Field*/ +npts=ihd[9]; +dt=fhd[0]; +fprintf(stderr,"npts=%d dt=%f\n",npts,dt); + +tr=(float *)malloc(sizeof(float)*npts); +read(fd1,tr,npts*sizeof(float)); + +write(fd2,tr,npts*4); /*Write timeseries file*/ + +if(binflag != 1) /*Make Helmberger File*/ + { + fprintf(stderr,"Making Helmberger Ascii\n"); + sprintf(line,"mkHelm ntr=1 dt=%f nt=%d < %s > %s.helm",dt,npts,out,out); + system(line); + sprintf(line,"rm %s",out); + system(line); + } + +} + + diff --git a/UTILITIES/sac2columns.c b/UTILITIES/sac2columns.c new file mode 100755 index 0000000..41d5839 --- /dev/null +++ b/UTILITIES/sac2columns.c @@ -0,0 +1,74 @@ +#include +#include +#include +#include + +/*convert SAC binary to headerless binary file (fromHelm.c generated) */ +/*or Helmberger ascii */ +int main(ac,av) + int ac; + char **av; + { + int i, npts, ihd[40],fd1, fd2, fd3, fd4, fd5; + float dt, fhd[70], *tr1, *tr2, *tr3, *tr4, *tr5; + char chd[8][24]; + +fd1=open("thf",O_RDONLY,0644); +fd2=open("rhf",O_RDONLY,0644); +fd3=open("zhf",O_RDONLY,0644); +fd4=open("rvf",O_RDONLY,0644); +fd5=open("zvf",O_RDONLY,0644); + + +read(fd1,fhd,70*4); /*Read Sac Float Field*/ +read(fd1,ihd,40*4); /*Read Sac Int Field*/ +read(fd1,chd,24*8); /*Read Sac Char. Field*/ +npts=ihd[9]; +dt=fhd[0]; +fprintf(stderr,"npts=%d dt=%f\n",npts,dt); +tr1=(float *)malloc(sizeof(float)*npts); +read(fd1,tr1,npts*sizeof(float)); +close(fd1); + +read(fd2,fhd,70*4); /*Read Sac Float Field*/ +read(fd2,ihd,40*4); /*Read Sac Int Field*/ +read(fd2,chd,24*8); /*Read Sac Char. Field*/ +tr2=(float *)malloc(sizeof(float)*npts); +read(fd2,tr2,npts*sizeof(float)); +close(fd2); + +read(fd3,fhd,70*4); /*Read Sac Float Field*/ +read(fd3,ihd,40*4); /*Read Sac Int Field*/ +read(fd3,chd,24*8); /*Read Sac Char. Field*/ +tr3=(float *)malloc(sizeof(float)*npts); +read(fd3,tr3,npts*sizeof(float)); +close(fd3); + +read(fd4,fhd,70*4); /*Read Sac Float Field*/ +read(fd4,ihd,40*4); /*Read Sac Int Field*/ +read(fd4,chd,24*8); /*Read Sac Char. Field*/ +tr4=(float *)malloc(sizeof(float)*npts); +read(fd4,tr4,npts*sizeof(float)); +close(fd4); + +read(fd5,fhd,70*4); /*Read Sac Float Field*/ +read(fd5,ihd,40*4); /*Read Sac Int Field*/ +read(fd5,chd,24*8); /*Read Sac Char. Field*/ +tr5=(float *)malloc(sizeof(float)*npts); +read(fd5,tr5,npts*sizeof(float)); +close(fd5); + +fprintf(stdout,"%% %d %f\n",npts,dt); +for(i=0; i < npts; i++) + { + fprintf(stdout,"%+12.6E %+12.6E %+12.6E %+12.6E %+12.6E\n",tr1[i],tr2[i],tr3[i],tr4[i],tr5[i]); + } + +free(tr1); +free(tr2); +free(tr3); +free(tr4); +free(tr5); +} + + diff --git a/UTILITIES/sac2columns_mt.c b/UTILITIES/sac2columns_mt.c new file mode 100755 index 0000000..5c35c69 --- /dev/null +++ b/UTILITIES/sac2columns_mt.c @@ -0,0 +1,119 @@ +#include +#include +#include +#include + +/*convert SAC binary to headerless binary file (fromHelm.c generated) */ +/*or Helmberger ascii */ +int main(ac,av) + int ac; + char **av; + { + int i, npts, ihd[40],fd1, fd2, fd3, fd4, fd5, fd6, fd7, fd8, fd9, fd10; + float dt, fhd[70], *tr1, *tr2, *tr3, *tr4, *tr5, *tr6, *tr7, *tr8, *tr9, *tr10; + char chd[8][24]; + +fd1=open("tss",O_RDONLY,0644); +fd2=open("tds",O_RDONLY,0644); +fd3=open("rss",O_RDONLY,0644); +fd4=open("rds",O_RDONLY,0644); +fd5=open("rdd",O_RDONLY,0644); +fd6=open("zss",O_RDONLY,0644); +fd7=open("zds",O_RDONLY,0644); +fd8=open("zdd",O_RDONLY,0644); +fd9=open("rex",O_RDONLY,0644); +fd10=open("zex",O_RDONLY,0644); + + +read(fd1,fhd,70*4); /*Read Sac Float Field*/ +read(fd1,ihd,40*4); /*Read Sac Int Field*/ +read(fd1,chd,24*8); /*Read Sac Char. Field*/ +npts=ihd[9]; +dt=fhd[0]; +fprintf(stderr,"npts=%d dt=%f\n",npts,dt); +tr1=(float *)malloc(sizeof(float)*npts); +read(fd1,tr1,npts*sizeof(float)); +close(fd1); + +read(fd2,fhd,70*4); /*Read Sac Float Field*/ +read(fd2,ihd,40*4); /*Read Sac Int Field*/ +read(fd2,chd,24*8); /*Read Sac Char. Field*/ +tr2=(float *)malloc(sizeof(float)*npts); +read(fd2,tr2,npts*sizeof(float)); +close(fd2); + +read(fd3,fhd,70*4); /*Read Sac Float Field*/ +read(fd3,ihd,40*4); /*Read Sac Int Field*/ +read(fd3,chd,24*8); /*Read Sac Char. Field*/ +tr3=(float *)malloc(sizeof(float)*npts); +read(fd3,tr3,npts*sizeof(float)); +close(fd3); + +read(fd4,fhd,70*4); /*Read Sac Float Field*/ +read(fd4,ihd,40*4); /*Read Sac Int Field*/ +read(fd4,chd,24*8); /*Read Sac Char. Field*/ +tr4=(float *)malloc(sizeof(float)*npts); +read(fd4,tr4,npts*sizeof(float)); +close(fd4); + +read(fd5,fhd,70*4); /*Read Sac Float Field*/ +read(fd5,ihd,40*4); /*Read Sac Int Field*/ +read(fd5,chd,24*8); /*Read Sac Char. Field*/ +tr5=(float *)malloc(sizeof(float)*npts); +read(fd5,tr5,npts*sizeof(float)); +close(fd5); + +read(fd6,fhd,70*4); /*Read Sac Float Field*/ +read(fd6,ihd,40*4); /*Read Sac Int Field*/ +read(fd6,chd,24*8); /*Read Sac Char. Field*/ +tr6=(float *)malloc(sizeof(float)*npts); +read(fd6,tr6,npts*sizeof(float)); +close(fd6); + +read(fd7,fhd,70*4); /*Read Sac Float Field*/ +read(fd7,ihd,40*4); /*Read Sac Int Field*/ +read(fd7,chd,24*8); /*Read Sac Char. Field*/ +tr7=(float *)malloc(sizeof(float)*npts); +read(fd7,tr7,npts*sizeof(float)); +close(fd7); + +read(fd8,fhd,70*4); /*Read Sac Float Field*/ +read(fd8,ihd,40*4); /*Read Sac Int Field*/ +read(fd8,chd,24*8); /*Read Sac Char. Field*/ +tr8=(float *)malloc(sizeof(float)*npts); +read(fd8,tr8,npts*sizeof(float)); +close(fd8); + +read(fd9,fhd,70*4); /*Read Sac Float Field*/ +read(fd9,ihd,40*4); /*Read Sac Int Field*/ +read(fd9,chd,24*8); /*Read Sac Char. Field*/ +tr9=(float *)malloc(sizeof(float)*npts); +read(fd9,tr9,npts*sizeof(float)); +close(fd9); + +read(fd10,fhd,70*4); /*Read Sac Float Field*/ +read(fd10,ihd,40*4); /*Read Sac Int Field*/ +read(fd10,chd,24*8); /*Read Sac Char. Field*/ +tr10=(float *)malloc(sizeof(float)*npts); +read(fd10,tr10,npts*sizeof(float)); +close(fd10); + +fprintf(stdout,"%% %d %f\n",npts,dt); +for(i=0; i < npts; i++) + { + fprintf(stdout,"%+12.6E %+12.6E %+12.6E %+12.6E %+12.6E %+12.6E %+12.6E %+12.6E %+12.6E %+12.6E\n",tr1[i],tr2[i],tr3[i],tr4[i],tr5[i],tr6[i],tr7[i],tr8[i],tr9[i],tr10[i]); + } + +free(tr1); +free(tr2); +free(tr3); +free(tr4); +free(tr5); +free(tr6); +free(tr7); +free(tr8); +free(tr9); +free(tr10); +} + + diff --git a/UTILITIES/sac2helm.c b/UTILITIES/sac2helm.c new file mode 100755 index 0000000..d729f7f --- /dev/null +++ b/UTILITIES/sac2helm.c @@ -0,0 +1,102 @@ +#include +#include +#include +#include +#include +#include + +void setpar(); +int getpar(); +int mstpar(); +void endpar(); + +FILE *fopen(), *fd4; + +/*convert SAC binary to headerless binary file (fromHelm.c generated) */ +/*or Helmberger ascii */ +int main(ac,av) + int ac; + char **av; + { + int i, npts, ihd[40],fd1, fd2, fd3; + float dt, fhd[70], *tr; + char chd[8][24], out[100], line[100], line1[100], line2[100]; + +setpar(ac,av); +mstpar("out","s",out); +endpar(); + +fd1=open("tmp1",O_RDONLY,0644); +fd2=open("tmp2",O_RDONLY,0644); +fd3=open("tmp3",O_RDONLY,0644); +fd4=fopen(out,"w"); + + +read(fd1,fhd,70*4); /*Read Sac Float Field*/ +read(fd1,ihd,40*4); /*Read Sac Int Field*/ +read(fd1,chd,24*8); /*Read Sac Char. Field*/ +npts=ihd[9]; +dt=fhd[0]; +read(fd2,fhd,70*4); /*Read Sac Float Field*/ +read(fd2,ihd,40*4); /*Read Sac Int Field*/ +read(fd2,chd,24*8); /*Read Sac Char. Field*/ +if(ihd[9] != npts) + { + fprintf(stderr,"SAC2HELM ERROR npts not equal\n"); + exit(-1); + } +if(fhd[0] != dt) + { + fprintf(stderr,"SAC2HELM ERROR dt not equal\n"); + exit(-1); + } +read(fd3,fhd,70*4); /*Read Sac Float Field*/ +read(fd3,ihd,40*4); /*Read Sac Int Field*/ +read(fd3,chd,24*8); /*Read Sac Char. Field*/ +if(ihd[9] != npts) + { + fprintf(stderr,"SAC2HELM ERROR npts not equal\n"); + exit(-1); + } +if(fhd[0] != dt) + { + fprintf(stderr,"SAC2HELM ERROR dt not equal\n"); + exit(-1); + } +fprintf(stderr,"npts=%d dt=%f\n",npts,dt); + +sprintf(line1," 0.0000e+00 0.0000e+00 0 0 0.00"); +sprintf(line2,"%8d %8.5f %11.4e",npts,dt,0.0); + +tr=(float *)malloc(sizeof(float)*npts); +read(fd1,tr,npts*sizeof(float)); +fprintf(fd4," 3\n"); +fprintf(fd4,"(7e14.5)\n"); +fprintf(fd4,"%s\n",line1); +fprintf(fd4,"%s\n",line2); +for(i=1; i <= npts; i++) + if(i % 7 == 0 && i != 1 && i != npts) + fprintf(fd4," %12.5e\n",tr[i-1]); + else + fprintf(fd4," %12.5e",tr[i-1]); + +read(fd2,tr,npts*sizeof(float)); +fprintf(fd4,"\n%s\n",line1); +fprintf(fd4,"%s\n",line2); +for(i=1; i <= npts; i++) + if(i % 7 == 0 && i != 1 && i != npts) + fprintf(fd4," %12.5e\n",tr[i-1]); + else + fprintf(fd4," %12.5e",tr[i-1]); + +read(fd3,tr,npts*sizeof(float)); +fprintf(fd4,"\n%s\n",line1); +fprintf(fd4,"%s\n",line2); +for(i=1; i <= npts; i++) + if(i % 7 == 0 && i != 1 && i != npts) + fprintf(fd4," %12.5e\n",tr[i-1]); + else + fprintf(fd4," %12.5e",tr[i-1]); + + +} diff --git a/UTILITIES/save.c b/UTILITIES/save.c new file mode 100755 index 0000000..eae9c0a --- /dev/null +++ b/UTILITIES/save.c @@ -0,0 +1,603 @@ +/* copyright (c) Robert W. Clayton + * Seismological Laboratory + * Caltech + * Pasadena, CA 91125 + * + * Getpar routines: + * + * Externally visable routines: + * + * setpar(argc,argv) + * getpar(name,type,valptr) + * mstpar(name,type,valptr) + * endpar() + * + * To get C-version: + * cc -c getpar.c + * + * To get F77-version: + * cp getpar.c fgetpar.c + * cc -c -DFORTRAN fgetpar.c + * rm fgetpar.c + * + * To get the environment processing stuff add the flag + *-DENVIRONMENT to each of the cc's above. + */ +#include +#include +#include +#include "getpar.h" + +#define MAXLINE 1024 /* max length of line in par file */ +#define MAXNAME 64 /* max length of name */ +#define MAXVALUE 1024 /* max length of value */ +#define MAXFILENAME 64 /* max length of par file name */ +#define MAXVECTOR 10 /* max # of elements for unspecified vectors */ +#define GETPAR_ERROR 100 /* exit status for getpar error */ +#define GETPAR_STOP 101 /* exit status for STOP or mstpar */ +#define MAXPARLEVEL 4 /* max recurrsion level for par files */ + +#ifdef FORTRAN +#define GETPAR getpar_ +#define MSTPAR mstpar_ +#define ENDPAR endpar_ +#else +#define GETPAR getpar +#define MSTPAR mstpar +#define ENDPAR endpar +#endif + +#define INIT 1 /* bits for FLAGS (ext_par.argflags) */ +#define STOP 2 +#define LIST 4 +#define END_PAR 8 +#define VERBOSE 16 + +#define LISTINC 32 /* increment size for arglist */ +#define BUFINC 1024 /* increment size for argbuf */ + +struct arglist /* structure of list set up by setpar */ + { + char *argname; + char *argval; + int hash; + }; +struct ext_par /* global variables for getpar */ + { + char *progname; + int argflags; + struct arglist *arglist; + struct arglist *arghead; + char *argbuf; + int nlist; + int nbuf; + int listmax; + int bufmax; + FILE *listout; + } ext_par; + +/* abbreviations: */ +#define AL struct arglist +#define PROGNAME ext_par.progname +#define FLAGS ext_par.argflags +#define ARGLIST ext_par.arglist +#define ARGHEAD ext_par.arghead +#define ARGBUF ext_par.argbuf +#define NLIST ext_par.nlist +#define NBUF ext_par.nbuf +#define LISTMAX ext_par.listmax +#define BUFMAX ext_par.bufmax +#define LISTFILE ext_par.listout + +#ifdef FORTRAN +setpar_() +#else +void setpar(ac,av) /* set up arglist & process INPUT command */ +int ac; char **av; +#endif + { + register char *pl, *pn, *pv; + char t, name[MAXNAME], value[MAXVALUE]; + FILE *file, *gp_create_dump(); + int i, addflags, nevlist, testav, testae; + struct arglist *alptr; +#ifdef FORTRAN + int ac; char **av; + extern int xargc; extern char **xargv; + ac= xargc; av= xargv; +#endif + + PROGNAME= *av; + FLAGS= INIT; + LISTFILE= stderr; + + ARGLIST= NULL; + ARGBUF = NULL; + NLIST= NBUF= LISTMAX= BUFMAX= 0; +#ifdef ENVIRONMENT + gp_do_environment(ac,av); +#endif + nevlist= NLIST; + while(--ac>0) + { + av++; + pl= *av; + while(*pl == ' ' || *pl == '\t') pl++; + /* get name */ + pn= name; + while(*pl != '=' && *pl != '\0') *pn++ = *pl++; + *pn++ = '\0'; + /* get value */ + if(*pl == '=') pl++; + pv= value; + if(*pl == '"' || *pl == '\'') + { + t= *pl++; + while(*pl != '\0') + { + if(*pl == t) + { + if(pl[-1] != '\\') break; + pv[-1]= t; + pl++; + } + else *pv++ = *pl++; + } + } + else while(*pl) *pv++ = *pl++; + *pv= '\0'; + if(name[0] == '-') gp_add_entry("SWITCH",&name[1]); + else gp_add_entry(name,value); + if(strcmp("par",name)==0) /* par file */ + gp_do_par_file(value,1); + } + /* do not internally call getpar before this point because + ARGHEAD is not set. The search will have no stopping point */ + ARGHEAD= ARGLIST; +#ifdef ENVIRONMENT + *value= '\0'; + if(GETPAR("NOENV","s",value)) ARGHEAD= ARGLIST+ nevlist; +#endif + addflags= 0; + *value= '\0'; + if(GETPAR("STOP","s",value)) addflags |= STOP; + *value= '\0'; + if(GETPAR("VERBOSE","s",value)) addflags |= VERBOSE; + *value= '\0'; + if(GETPAR("LIST","s",value)) + { + addflags |= LIST; + LISTFILE =gp_create_dump(value,"list"); + } + *value= '\0'; + if(GETPAR("INPUT","s",value)) + { + file =gp_create_dump(value,"list input"); + fprintf(file,"%s: getpar input listing\n",PROGNAME); + for(i=0, alptr=ARGLIST; iargname,alptr->argval); + } + gp_close_dump(file); + } + FLAGS |= addflags; + } + +void gp_add_entry(name,value) /* add an entry to arglist, expanding memory */ +register char *name, *value; /* if necessary */ + { + struct arglist *alptr; + int len; + register char *ptr; + + /* check arglist memory */ + if(NLIST >= LISTMAX) + { + LISTMAX += LISTINC; + if(ARGLIST == NULL) + ARGLIST= (AL *)malloc(LISTMAX * sizeof(AL)); + else ARGLIST= (AL *)realloc(ARGLIST,LISTMAX * sizeof(AL)); + } + /* check argbuf memory */ + len= strlen(name) + strlen(value) + 2; /* +2 for terminating nulls */ + if(NBUF+len >= BUFMAX) + { + BUFMAX += BUFINC; + if(ARGBUF == NULL) + ARGBUF= (char *)malloc(BUFMAX); + else ARGBUF= (char *)realloc(ARGBUF,BUFMAX); + } + if(ARGBUF == NULL || ARGLIST == NULL) + gp_getpar_err("setpar","cannot allocate memory"); + + /* add name */ + alptr= ARGLIST + NLIST; + alptr->hash= gp_compute_hash(name); + ptr= alptr->argname= ARGBUF + NBUF; + do *ptr++ = *name; while(*name++); + + /* add value */ + NBUF += len; + alptr->argval= ptr; + do *ptr++ = *value; while(*value++); + NLIST++; + } +#ifdef ENVIRONMENT +gp_do_environment(ac,av) +int ac; char **av; + { + char **ae; + register char *pl, *pn, *pv; + char name[MAXNAME], value[MAXVALUE], t; + + /* The environ pointer ae, is assumed to have a specific relation + to the arg pointer av. This may not be portable. */ + ae= av +(ac+1); + if(ae == NULL) return; + + while(*ae != NULL) + { + pl= *ae++; + while(*pl == ' ' || *pl == '\t') pl++; + /* get name */ + pn= name; + while(*pl != '=' && *pl != '\0') *pn++ = *pl++; + *pn = '\0'; + if(strcmp("NOENV",pn) == 0) return; + + /* get value */ + if(*pl == '=') pl++; + pv= value; + if(*pl == '"' || *pl == '\'') + { + t= *pl++; + while(*pl != '\0') + { + if(*pl == t) + { + if(pl[-1] != '\\') break; + pv[-1]= t; + pl++; + } + else *pv++ = *pl++; + } + } + else while(*pl) *pv++ = *pl++; + *pv= '\0'; + gp_add_entry(name,value); + } + } +#endif + +void ENDPAR() /* free arglist & argbuf memory, & process STOP command */ + { + if(ARGLIST != NULL) free(ARGLIST); + if(ARGBUF != NULL) free(ARGBUF); + ARGBUF= NULL; + ARGLIST= NULL; + if(FLAGS & STOP) + { + fprintf(stderr,"%s[endpar]: stop due to STOP in input\n", + PROGNAME); + exit(GETPAR_STOP); + } + FLAGS= END_PAR; /* this stops further getpar calls */ + } + +#ifdef FORTRAN +int mstpar_(name,type,val,dum1,dum2) +int dum1, dum2; /* dum1 & dum2 are extra args that fortran puts in */ +#else +int mstpar(name,type,val) +#endif +char *name, *type; +int *val; + { + int cnt; + char *typemess; + + if( (cnt= GETPAR(name,type,val)) > 0) return(cnt); + + /* The following line corrects a common input error */ + if(type[1]=='v') { type[1]= type[0]; type[0]='v'; } + + switch(*type) + { + case 'd': typemess= "an integer"; break; + case 'f': typemess= "a float"; break; + case 'F': typemess= "a double"; break; + case 's': typemess= "a string"; break; + case 'b': typemess= "a boolean"; break; + case 'v': switch(type[1]) + { + case 'd': typemess= "an integer vector"; break; + case 'f': typemess= "a float vector"; break; + case 'F': typemess= "a double vector"; break; + default : typemess= "unknow vectorn (error)"; + break; + } + break; + default : typemess= "unknown (error)"; break; + } + gp_getpar_err("mstpar","must specify value for '%s', expecting %s", + name,typemess); + } + +#ifdef FORTRAN +int getpar_(name,type,val,dum1,dum2) +int dum1, dum2; /* dum1 & dum2 are extra args that fortran puts in */ +#else +getpar(name,type,val) +#endif +char *name, *type; +int *val; + { + register char *sptr; + register struct arglist *alptr; + register int i; + double atof(), *dbl; + float *flt; + int h, hno, hyes, found; + char line[MAXLINE], *str, *noname; + + if(FLAGS & END_PAR) + gp_getpar_err("getpar","called after endpar"); + if( (FLAGS & INIT) == 0) + gp_getpar_err("getpar","not initialized with setpar"); + if(FLAGS & VERBOSE) + fprintf(stderr,"getpar: looking for %s\n",name); + + /* The following line corrects a common input error */ + if(type[1]=='v') { type[1]= type[0]; type[0]='v'; } + + + if(*type == 'b') goto boolean; + + h= gp_compute_hash(name); + found=0; + /* search list backwards, stopping at first find */ + for(alptr= ARGLIST +(NLIST-1); alptr >= ARGHEAD; alptr--) + { + if(alptr->hash != h) continue; + if(strcmp(alptr->argname,name) != 0) continue; + str= alptr->argval; + switch(*type) + { + case 'd': + *val= atoi(str); + found=1; + break; + case 'f': + flt= (float *) val; + *flt= atof(str); + found=1; + break; + case 'F': + dbl= (double *) val; + *dbl= atof(str); + found=1; + break; + case 's': + sptr= (char *) val; + while(*str) *sptr++ = *str++; + *sptr= '\0'; + found=1; + break; + case 'v': + found= gp_getvector(str,type,val); + break; + default: + gp_getpar_err("getpar", + "unknown conversion type %s",type); + break; + } + break; + } + goto list; +boolean: + noname= line; + sprintf(noname,"no%s",name); + hno = gp_compute_hash(noname); + hyes= gp_compute_hash( name); + found=0; + /* search list backwards, stopping at first find */ + for(alptr= ARGLIST +(NLIST-1); alptr >= ARGHEAD; alptr--) + { + if(alptr->hash != hno && alptr->hash != hyes) continue; + if(strcmp(alptr->argname, name)== 0) + { + if(alptr->argval[0] == '\0') *val= 1; + else *val= atol(alptr->argval); + found++; + break; + } + if(strcmp(alptr->argname,noname)== 0) + { *val= 0; found++; break; } + } + list: + if(FLAGS & LIST) + { + switch(*type) + { + case 'd': sprintf(line,"(int) = %d",*val); break; + case 'f': flt= (float *)val; + sprintf(line,"(flt) = %14.6e",*flt); break; + case 'F': dbl= (double *)val; + sprintf(line,"(dbl) = %14.6e",*dbl); break; + /* case 's': sprintf(line,"(str) = %s",val); break;*/ + case 's': sprintf(line,"(str) = %s",sptr); break; + case 'b': sprintf(line,"(boo) = %d",*val); break; + case 'v': switch(type[1]) + { + /* should list these out */ + case 'd': sprintf(line,"(int vec)"); + break; + case 'f': sprintf(line,"(flt vec)"); + break; + case 'F': sprintf(line,"(dbl vec)"); + break; + default : sprintf(line," vec type error"); + break; + } + break; + default : sprintf(line," type error"); break; + } + fprintf(LISTFILE,"%16s (%s) %s \n",name, + (found ? "set":"def"),line); + } + return(found); + } +FILE *gp_create_dump(fname,filetype) +char *fname; +char *filetype; + { + FILE *temp; + + if(*fname == '\0') return(stderr); + if(strcmp(fname,"stderr") == 0) return(stderr); + if(strcmp(fname,"stdout") == 0) return(stdout); + if( (temp= fopen(fname,"w")) != NULL) return(temp); + fprintf(stderr,"%s[setpar]: cannot create %s file %s\n", + PROGNAME,filetype,fname); + return(stderr); + } + +int gp_close_dump(file) +FILE *file; + { + if(file == stderr || file == stdout) return(0); + fclose(file); + } + +gp_compute_hash(s) +register char *s; + { + register int h; + h= s[0]; + if(s[1]) h |= (s[1])<<8; else return(h); + if(s[2]) h |= (s[2])<<16; else return(h); + if(s[3]) h |= (s[3])<<24; + return(h); + } + +void gp_do_par_file(fname,level) +char *fname; +int level; + { + register char *pl, *pn, *pv; + char t1, t2, line[MAXLINE], name[MAXNAME], value[MAXVALUE]; + FILE *file, *fopen(); + + if(level > MAXPARLEVEL) + gp_getpar_err("setpar","%d (too many) recursive par file",level); + + if( (file=fopen(fname,"r"))==NULL) + gp_getpar_err("setpar","cannot open par file %s",fname); + + while( fgets(line,MAXLINE,file) != NULL ) + { + pl= line; + /* loop over entries on each line */ + loop: while(*pl==' ' || *pl=='\t') pl++; + if(*pl=='\0'|| *pl=='\n') continue; + if(*pl=='#') continue; /* comments on rest of line */ + + /* get name */ + pn= name; + while(*pl != '=' && *pl != '\0' && *pl != ' ' + && *pl != '\t') *pn++ = *pl++; + *pn = '\0'; + if(*pl == '=') pl++; + + /* get value */ + *value= '\0'; + pv= value; + if(*pl=='"' || *pl=='\'') { t1= t2= *pl++; } + else { t1= ' '; t2= '\t'; } + while(*pl!=t1 && *pl!=t2 && + *pl!='\0' && *pl!='\n') *pv++= *pl++; + *pv= '\0'; + if(*pl=='"' || *pl=='\'') pl++; + gp_add_entry(name,value); + if(strcmp("par",name) == 0) + gp_do_par_file(value,level+1); + goto loop; + } + fclose(file); + } + +gp_getpar_err(subname,mess,a1,a2,a3,a4) +char *subname, *mess; +int a1, a2, a3, a4; + { + fprintf(stderr,"\n***** ERROR in %s[%s] *****\n\t", + (PROGNAME == NULL ? "(unknown)" : PROGNAME),subname); + fprintf(stderr,mess,a1,a2,a3,a4); + fprintf(stderr,"\n"); + exit(GETPAR_ERROR); + } +gp_getvector(list,type,val) +char *list, *type; +int *val; + { + register char *p; + register int index, cnt; + char *valptr; + int limit; + int ival, *iptr; + float fval, *fptr; + double dval, *dptr, atof(); + + limit= MAXVECTOR; + if(type[2] == '(' || type[2] == '[') limit= atol(&type[3]); + if(limit <= 0) + gp_getpar_err("getpar","bad limit=%d specified",limit); + index= 0; + p= list; + while(*p != '\0' && index < limit) + { + cnt=1; + backup: /* return to here if we find a repetition factor */ + while(*p == ' ' || *p == '\t') p++; + if(*p == '\0') return(index); + valptr= p; + while( *p != ',' && *p != '*' && *p != 'x' && *p != 'X' && + *p != '\0') p++; + if(*p == '*' || *p == 'x' || *p == 'X') + { + cnt= atol(valptr); + if(cnt <= 0) + gp_getpar_err("getpar", + "bad repetition factor=%d specified", + cnt); + if(index+cnt > limit) cnt= limit - index; + p++; + goto backup; + } + switch(type[1]) + { + case 'd': + iptr= (int *) val; + ival= atol(valptr); + while(cnt--) iptr[index++] = ival; + break; + case 'f': + fptr= (float *) val; + fval= atof(valptr); + while(cnt--) fptr[index++] = fval; + break; + case 'F': + dptr= (double *) val; + dval= atof(valptr); + while(cnt--) dptr[index++] = dval; + break; + default: + gp_getpar_err("getpar", + "bad vector type=%c specified",type[1]); + break; + } + if(*p != '\0') p++; + } + return(index); + } diff --git a/UTILITIES/window.c b/UTILITIES/window.c new file mode 100755 index 0000000..8770281 --- /dev/null +++ b/UTILITIES/window.c @@ -0,0 +1,114 @@ +/* +program to window portion of 3-D data file + +window len= [in= out= planes= vecs= esize= p0= v0= e0= np= nv= ne= dp= dv= de=] + +arguments: + in=stdin input file, a series of vectors + out=stdout windowed result + len=(nt=) length of input file vector + vecs=(nx=) number of vectors in a plane + planes=1 number of planes in dataset + esize=4 size of input file element in bytes + p0=0 the first plane to be copied + v0=0 the first vector in each plane to be copied + e0=0 the first element of each vector to be copied + np=(planes-p0)/dp the number of planes to be copied + nv=(vecs-v0)/dv the number of vectors to be copied + ne=(len-e0)/de the number of elements from each vector to be copied + dp=1 increment between input planes + dv=1 increment between input vectors + de=1 increment between input elements +*/ + +#include +#include +#include +#include + +void setpar(); +int getpar(); +int mstpar(); +void endpar(); +int dowrite(); + +char in[40], out[40]; +int main (argc,argv) +int argc; char **argv; + { + int rfile,wfile; + int len,esize=4; + int v0=0,e0=0,nv=0,ne=0,dv=1,de=1; + int nseek,de1,i,j,k,planes=1,vecs=0,np=0,dp=1,nseek1,m,p0=0; + char *x; + + /* fetch parameters */ + setpar(argc,argv); + if (getpar("in","s",in)) { + if ((rfile = open(in,O_RDONLY,0644)) < 2) { + fprintf(stderr,"cannot open %s\n",in); + exit(-1); + } + } + else rfile=0; + if (getpar("out","s",out)) { + if ((wfile = open(out,O_WRONLY | O_CREAT | O_TRUNC,0644)) < 2) { + fprintf(stderr,"cannot open %s\n",out); + exit(-1); + } + } + else wfile=1; + if (getpar("len","d",&len)==0) + if (getpar("nt","d",&len)==0) fprintf(stderr,"len= missing\n"); + getpar("planes","d",&planes); + getpar("esize","d",&esize); + if (getpar("vecs","d",&vecs)==0) mstpar("nx","d",&vecs); + getpar("p0","d",&p0); + getpar("v0","d",&v0); + getpar("e0","d",&e0); + getpar("dp","d",&dp); + getpar("dv","d",&dv); + getpar("de","d",&de); + if (getpar("np","d",&np)==0) np = (planes - p0) / dp; + if (getpar("nv","d",&nv)==0) nv = (vecs - v0) / dv; + if (getpar("ne","d",&ne)==0) ne = (len - e0) / de; + endpar(); + fprintf(stderr," in=%s out=%s\n",in,out); + fprintf(stderr," planes=%d vecs=%d len=%d esize=%d\n",planes,vecs,len,esize); + fprintf(stderr," np=%d p0=%d dp=%d\n",np,p0,dp); + fprintf(stderr," nv=%d v0=%d dv=%d\n",nv,v0,dv); + fprintf(stderr," ne=%d e0=%d de=%d\n",ne,e0,de); + + x = (char *) malloc(ne*de*esize); + lseek (rfile,((p0*vecs+v0)*len+e0)*esize,0); + nseek = ((dv - 1) * len + (len - ne * de)) * esize; + nseek1 = ((dp - 1) * vecs + (vecs - nv * dv)) * len * esize; + ne *= esize; + de1 = (de - 1) * esize; + while (np-->0) { + for (m=0; m1) for (i=j=0; i total) { + if ((nwrite = write(fd,buf,bytes-total)) <= 0) return(total); + total += nwrite; + buf += nwrite; + } + return(total); +}