Coverage for /opt/obspy/update-docs/src/obspy/obspy/imaging/scripts/mopad : 5%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
#!/usr/bin/env python # -*- coding: utf-8 -*- #------------------------------------------------------------------- # Filename: mopad.py # Purpose: Moment tensor Plotting and Decomposition tool # Author: Lars Krieger, Sebastian Heimann # Email: lars.krieger@zmaw.de, sebastian.heimann@zmaw.de # # Copyright (C) 2010 Lars Krieger, Sebastian Heimann #--------------------------------------------------------------------- USAGE: obspy-mopad [plot,decompose,gmt,convert] SOURCE_MECHANISM [OPTIONS]
::
####################################################################### ######################### MoPaD ####################################
######### Moment tensor Plotting and Decomposition tool ############# #######################################################################
Multi method tool for:
- Plotting and saving of focal sphere diagrams ('Beachballs').
- Decomposition and Conversion of seismic moment tensors.
- Generating coordinates, describing a focal sphere diagram, to be piped into GMT's psxy (Useful where psmeca or pscoupe fail.)
For more help, please run ``python mopad.py --help``.
#######################################################################
Version 0.7
#######################################################################
Copyright (C) 2010 Lars Krieger & Sebastian Heimann
Contact lars.krieger@zmaw.de & sebastian.heimann@zmaw.de
#######################################################################
License:
GNU Lesser General Public License, Version 3
This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. """
############################################################################### #################### MoPaD (Version %.1f) ##############################
usage: mopad.py 'method' 'source mechanism' ['options'] or python mopad.py 'method' 'source mechanism' ['options']
###############################################################################
Several methods are implemented in this tool. It is chosen by providing its name as the first argument:
- (p)lot : plotting a beachball projection of the provided mechanism - (d)ecompose : decompose a given mechanism into its components - (g)mt : return the beachball as a string, to be piped into GMT's psxy - (c)onvert : convert a mechanism to/in (strike,dip,slip-rake) from/to matrix form *or* convert a matrix, vector, tuple into different basis representations
-------------------------------------------------------------------------------
The 'source mechanism' as a comma-separated list of length:
- 3 (strike,dip,rake), - 4 (strike,dip,rake,moment), - 6 (M11,M22,M33,M12,M13,M23), - 7 (M11,M22,M33,M12,M13,M23,moment), - 9 (full moment tensor)
(With all angles to be given in degrees) -------------------------------------------------------------------------------
HELP for a particular method is shown with:
elk_mopad.py 'method' -h or elk_mopad.py 'method' --help
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Example:
To generate a beachball for a normal faulting mechnism (a snake's eye type):
elk_mopad.py plot 0,45,-90 or elk_mopad.py plot p 0,1,-1,0,0,0
###############################################################################
""" % (MOPAD_VERSION)
# constants:
""" """ """ 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. """
source_mechanism = M self._original_M = M[:]
# basis system: self._input_basis = 'NED' self._list_of_possible_input_bases = ['NED', 'USE', 'XYZ', 'NWU'] self._list_of_possible_output_bases = ['NED', 'USE', 'XYZ', 'NWU']
self._input_basis = system.upper()
# bring M to symmetric matrix form self._M = self._setup_M(source_mechanism)
# transform M into NED system for internal calculations self._rotate_2_NED()
# # set attributes list #
# decomposition: self._decomposition_key = 20
# 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
# # RUN: #
# 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()
""" 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 == None: print '\n ERROR !! - Please provide a mechanism !!\n' raise MTError(' !! ')
# if some stupid nesting occurs if len(mech) == 1: mech = mech[0]
# all 9 elements are given if N.prod(N.shape(mech)) == 9: if N.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 = N.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 if len(mech) == 6 or len(mech) == 7: M = mech new_M = N.matrix([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]
# if given as strike, dip, rake, conventions from Jost & Herrmann hold # resulting matrix is in NED-basis: if len(mech) == 3 or len(mech) == 4: try: [float(val) for val in mech] except: msg = "angles must be given as floats, separated by commas" sys.exit('\n ERROR - %s\n ' % msg)
strike = mech[0] if not 0 <= strike <= 360: msg = "strike angle must be between 0° and 360°" sys.exit('\n ERROR - %s\n ' % msg) dip = mech[1] if not -90 <= dip <= 90: msg = "dip angle must be between -90° and 90°" sys.exit('\n ERROR - %s\n ' % msg) rake = mech[2] if not -180 <= rake <= 180: msg = "slip-rake angle must be between -180° and 180°" sys.exit('\n ERROR - %s\n ' % msg)
moms = strikediprake_2_moments(strike, dip, rake)
new_M = N.matrix([moms[0], moms[3], moms[4], moms[3], moms[1], moms[5], moms[4], moms[5], moms[2]]).reshape(3, 3)
if len(mech) == 4: new_M *= mech[3]
# to assure right basis system - others are meaningless, provided # these angles self._input_basis = 'NED'
return N.asmatrix(new_M)
""" Rotates the mechanism to the basis NED.
All internal calculations are carried out within the NED space. """ if not self._input_basis in self._list_of_possible_input_bases: print 'provided input basis not implemented - please specify one', print 'of the following bases:', self._list_of_possible_input_bases raise MTError(' !! ')
NED_2_NED = N.asmatrix(N.diag([1, 1, 1]))
rotmat_USE_2_NED = NED_2_NED.copy() rotmat_USE_2_NED[:] = 0 rotmat_USE_2_NED[0, 1] = -1 rotmat_USE_2_NED[1, 2] = 1 rotmat_USE_2_NED[2, 0] = -1
rotmat_XYZ_2_NED = NED_2_NED.copy() rotmat_XYZ_2_NED[:] = 0 rotmat_XYZ_2_NED[0, 1] = 1 rotmat_XYZ_2_NED[1, 0] = 1 rotmat_XYZ_2_NED[2, 2] = -1
rotmat_NWU_2_NED = NED_2_NED.copy() rotmat_NWU_2_NED[1, 1] = -1 rotmat_NWU_2_NED[2, 2] = -1
if self._input_basis == 'NED': pass elif self._input_basis == 'USE': self._M = N.dot(rotmat_USE_2_NED, N.dot(self._M, rotmat_USE_2_NED.T)) elif self._input_basis == 'XYZ': self._M = N.dot(rotmat_XYZ_2_NED, N.dot(self._M, rotmat_XYZ_2_NED.T)) elif self._input_basis == 'NWU': self._M = N.dot(rotmat_NWU_2_NED, N.dot(self._M, rotmat_NWU_2_NED.T))
""" 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) """ if self._decomposition_key == 20: self._standard_decomposition() elif self._decomposition_key == 21: self._decomposition_w_2DC() elif self._decomposition_key == 31: self._decomposition_w_3DC() else: raise MTError(' only standard decompositions supported ')
""" 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 = N.diag(N.array([1. / 3 * N.trace(M), 1. / 3 * N.trace(M), 1. / 3 * N.trace(M)])) M0_iso = abs(1. / 3 * N.trace(M))
# deviatoric part M_devi = M - M_iso
self._isotropic = M_iso self._deviatoric = M_devi
# eigenvalues and -vectors eigenwtot, eigenvtot = N.linalg.eig(M)
# eigenvalues and -vectors of the deviatoric part eigenw1, eigenv1 = N.linalg.eig(M_devi)
# eigenvalues in ascending order: eigenw = N.real(N.take(eigenw1, N.argsort(abs(eigenwtot)))) eigenv = N.real(N.take(eigenv1, N.argsort(abs(eigenwtot)), 1))
# eigenvalues in ascending order in absolute value!!: eigenw_devi = N.real(N.take(eigenw1, N.argsort(abs(eigenw1)))) eigenv_devi = N.real(N.take(eigenv1, N.argsort(abs(eigenw1)), 1))
M0_devi = max(abs(eigenw_devi))
# named according to Jost & Herrmann: a1 = eigenv[:, 0] a2 = eigenv[:, 1] a3 = eigenv[:, 2]
F = -eigenw_devi[0] / eigenw_devi[2]
M_DC = eigenw[2] * (1 - 2 * F) * (N.outer(a3, a3) - N.outer(a2, a2)) M_CLVD = eigenw[2] * F * (2 * N.outer(a3, a3) - N.outer(a2, a2) - \ N.outer(a1, a1))
try: M_DC_percentage = int(round((1 - 2 * abs(F)) * 100, 6)) except ValueError: # this should only occur in the pure isotropic case M_DC_percentage = 0.
# 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
self._DC = M_DC self._CLVD = M_CLVD self._DC_percentage = int(round((100 - M_iso_percentage) * \ M_DC_percentage / 100.))
# self._seismic_moment = N.sqrt(1./2*N.sum(eigenw**2) ) self._seismic_moment = M0 self._moment_magnitude = \ N.log10(self._seismic_moment * 1.0e7) / 1.5 - 10.7
""" 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 = N.diag(N.array([1. / 3 * N.trace(M), 1. / 3 * N.trace(M), 1. / 3 * N.trace(M)])) M0_iso = abs(1. / 3 * N.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 = N.linalg.eig(M_devi)
# eigenvalues in ascending order of their absolute values: eigenw = N.real(N.take(eigenw1, N.argsort(abs(eigenw1)))) eigenv = N.real(N.take(eigenv1, N.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 = eigenw[2] * (N.outer(a3, a3) - N.outer(a2, a2)) M_DC2 = eigenw[0] * (N.outer(a1, a1) - N.outer(a2, a2))
M_DC_percentage = int(round(abs(eigenw[2] / (abs(eigenw[2]) + \ abs(eigenw[0]))) * 100.))
# according to Bowers & Hudson: M0 = M0_iso + M0_devi
M_iso_percentage = int(round(M0_iso / M0 * 100)) self._iso_percentage = M_iso_percentage
self._DC = M_DC self._DC2 = M_DC2 # self._DC_percentage = M_DC_percentage self._DC_percentage = int(round((100 - M_iso_percentage) * \ M_DC_percentage / 100.)) # and M_DC2_percentage?
# self._seismic_moment = N.sqrt(1./2*N.sum(eigenw**2) ) self._seismic_moment = M0 self._moment_magnitude = \ N.log10(self._seismic_moment * 1.0e7) / 1.5 - 10.7
""" 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 = N.diag(N.array([1. / 3 * N.trace(M), 1. / 3 * N.trace(M), 1. / 3 * N.trace(M)])) M0_iso = abs(1. / 3 * N.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 = N.linalg.eig(M_devi) M0_devi = max(abs(eigenw1))
# eigenvalues and -vectors of the full M !!!!!!!! eigenw1, eigenv1 = N.linalg.eig(M)
# eigenvalues in ascending order of their absolute values: eigenw = N.real(N.take(eigenw1, N.argsort(abs(eigenw1)))) eigenv = N.real(N.take(eigenv1, N.argsort(abs(eigenw1)), 1))
# named according to Jost & Herrmann: a1 = eigenv[:, 0] a2 = eigenv[:, 1] a3 = eigenv[:, 2]
M_DC1 = 1. / 3. * (eigenw[0] - eigenw[1]) * (N.outer(a1, a1) - \ N.outer(a2, a2)) M_DC2 = 1. / 3. * (eigenw[1] - eigenw[2]) * (N.outer(a2, a2) - \ N.outer(a3, a3)) M_DC3 = 1. / 3. * (eigenw[2] - eigenw[0]) * (N.outer(a3, a3) - \ N.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 = N.sqrt(1./2*N.sum(eigenw**2) ) self._seismic_moment = M0 self._moment_magnitude = \ N.log10(self._seismic_moment * 1.0e7) / 1.5 - 10.7
""" 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 = N.linalg.eigh(M_devi) EW_order = N.argsort(EW_devi)
# print 'order',EW_order
if 1: # self._plot_isotropic_part: trace_M = N.trace(M) if abs(trace_M) < epsilon: trace_M = 0 EW, EV = N.linalg.eigh(M) for i, ew in enumerate(EW): if abs(EW[i]) < epsilon: EW[i] = 0 else: trace_M = N.trace(M_devi) if abs(trace_M) < epsilon: trace_M = 0
EW, EV = N.linalg.eigh(M_devi) for i, ew in enumerate(EW): if abs(EW[i]) < epsilon: EW[i] = 0 trace_M_devi = N.trace(M_devi)
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 = N.asmatrix(N.zeros((3, 3))) chng_basis_tmp[:, 0] = EV1_devi chng_basis_tmp[:, 1] = EV2_devi chng_basis_tmp[:, 2] = EV3_devi det_mat = N.linalg.det(chng_basis_tmp)
symmetry_around_tension = 1 clr = 1
# print '\nEWs: ', [EW1,EW2,EW3], ' Trace: ',sum([EW1,EW2,EW3]) # print 'EWs devi',[EW1_devi,EW2_devi,EW3_devi], 'trace M devi', # print trace_M_devi
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 # if abs(trace_M_devi)< epsilon: # if abs(EW1_devi) < abs(EW3_devi): # symmetry_around_tension = 1 # clr = 1 # # if EW2_devi < 0: # symmetry_around_tension = 1 # clr = 1 # # # if EW1_devi < 0 and EW2_devi < 0 and EW3_devi > 0: # symmetry_around_tension = 1 # clr = 1 else: pass # # pure deviatoric movement # if trace_M == 0 and EW[2] == abs(EW[0]): # print 'shear' # print EW # #print EW1 # #exit() # # if EW[2] != abs(EW[0]): # print 'CLVD' # # if EW[2] < abs(EW[0]): # print 'symmetry around tension ( red)' # # symmetry_around_tension = 0 # clr = 1 # else: # print 'symmetry around pressure (white)' # # symmetry_around_tension = 0 # clr = -1 # # # if EW[2] > abs( EW[0]): # symmetry_around_tension = 0 # # clr = -1 # # # # elif abs(EW3) == EW1 and EW[0] > EW[2]: # # symmetry_around_tension = 0 # # #logger.debug( 'SIGMA AXIS = tension\n') # # clr = 1 # # else: # symmetry_around_tension = 1 # #logger.debug( 'SIGMA AXIS = tension\n') # clr = -1 # # # # elif trace_M == 0: # # symmetry_around_tension = 1 # # if # # # print 'detmat', det_mat # # if det_mat > 0: # # symmetry_around_tension = 0 # # clr = 1 # # 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 N.trace(self._M) >= 0): print 'Houston, we have had a problem - check M !!!!!!' raise MTError(' !! ')
if trace_M == 0: if EW2 == 0: symmetry_around_tension = 1 clr = 1 elif 2 * abs(EW2) == abs(EW1) or 2 * abs(EW2) == abs(EW3): if abs(EW1) < EW3: symmetry_around_tension = 1 clr = 1 else: symmetry_around_tension = 0 clr = -1 else: if abs(EW1) < EW3: symmetry_around_tension = 1 clr = 1 else: symmetry_around_tension = 0 clr = -1
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 = N.asmatrix(N.zeros((3, 3))) chng_fp_basis = N.asmatrix(N.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()
""" Sets the two angle-triples, describing the faultplanes of the Double Couple, defined by the eigenvectors P and T of the moment tensor object.
Defining 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: mit Sebastians Konventionen:
rotationsmatrix1 = EV Matrix von M, allerdings in der Reihenfolge TNP (nicht, wie hier PNT!!!)
referenz-DC mit strike, dip, rake = 0,0,0 in NED - Darstellung: M = 0,0,0,0,-1,0
davon die EV ebenfalls in eine Matrix:
trafo-matrix2 = EV Matrix von Referenz-DC in der REihenfolge TNP
effektive Rotationsmatrix = (rotationsmatrix1 * trafo-matrix2.T).T
durch check, ob det <0, schauen, ob die Matrix mit -1 multipliziert werden muss
flip_matrix = 0,0,-1,0,-1,0,-1,0,0
andere DC Orientierung wird durch flip * effektive Rotationsmatrix erhalten
beide Rotataionmatrizen in matrix_2_euler ) """ # reference Double Couple (in NED basis) # it has strike, dip, slip-rake = 0,0,0 refDC = N.matrix([[0., 0., -1.], [0., 0., 0.], [-1., 0., 0.]], dtype=N.float) refDC_evals, refDC_evecs = N.linalg.eigh(refDC)
# matrix which is turning from one fault plane to the other flip_dc = N.matrix([[0., 0., -1.], [0., -1., 0.], [-1., 0., 0.]], dtype=N.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 = (N.dot(pnt_sorted_EV_matrix, refDC_evecs.T)).T
# check, if rotation has right orientation if N.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 = N.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]
""" 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)
""" Builds a column vector (matrix type) from a 3 tuple. """ return N.matrix([[x, y, z]], dtype=N.float).T
""" 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 = N.cross(ez.T, ezs.T).T if N.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 = N.arccos(cos_alpha) beta = N.mod(N.arctan2(enodes[1, 0], enodes[0, 0]), N.pi * 2.) gamma = N.mod(-N.arctan2(enodess[1, 0], enodess[0, 0]), N.pi * 2.) return self._unique_euler(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 = N.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 = N.mod(alpha, 2.0 * pi) beta = N.mod(beta, 2.0 * pi) gamma = N.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 = N.mod(beta - pi, 2.0 * pi) gamma = N.mod(gamma + pi, 2.0 * pi) - pi assert 0. <= beta < pi assert -pi <= gamma < pi
if alpha < 1e-7: beta = N.mod(beta + gamma, 2.0 * pi) gamma = 0.
return (alpha, beta, gamma)
""" Gives the provided matrix in the desired basis 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. """ if not system.upper() in self._list_of_possible_output_bases: print '\nprovided output basis not supported - please specify', print 'one of the following bases: (default=NED)\n', print self._list_of_possible_input_bases, '\n' raise MTError(' !! ')
fancy = 0 if style.lower() in ['f', 'fan', 'fancy']: fancy = 1
if system.upper() == 'NED': if fancy: return fancy_matrix(M2return) else: return M2return
elif system.upper() == 'USE': if fancy: return fancy_matrix(NED2USE(M2return)) else: return NED2USE(M2return)
elif system.upper() == 'XYZ': if fancy: return fancy_matrix(NED2XYZ(M2return)) else: return NED2XYZ(M2return)
elif system.upper() == 'NWU': if fancy: return fancy_matrix(NED2NWU(M2return)) else: return NED2NWU(M2return)
""" Gives the provided vector(s) in the desired basis 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. """ if not system.upper() in self._list_of_possible_output_bases: print '\n provided output basis not supported - please specify', print 'one of the following bases: (default=NED)\n', print self._list_of_possible_input_bases, '\n' raise MTError(' !! ')
fancy = 0 if style.lower() in ['f', 'fan', 'fancy']: fancy = 1
lo_vectors = []
# if list of vectors if type(vectors) == list: for vec in vectors: if N.prod(N.shape(vec)) != 3: print '\n please provide vector(s) from R³ \n ' raise MTError(' !! ') lo_vectors = vectors else: if N.prod(N.shape(vectors)) % 3 != 0: print '\n please provide vector(s) from R³ \n ' raise MTError(' !! ')
if N.shape(vectors)[0] == 3: for ii in xrange(N.shape(vectors)[1]): lo_vectors.append(vectors[:, ii]) else: for ii in xrange(N.shape(vectors)[0]): lo_vectors.append(vectors[:, ii].transpose())
lo_vecs_to_show = []
for vec in lo_vectors: if system.upper() == 'NED': if fancy: lo_vecs_to_show.append(fancy_vector(vec)) else: lo_vecs_to_show.append(vec) elif system.upper() == 'USE': if fancy: lo_vecs_to_show.append(fancy_vector(NED2USE(vec))) else: lo_vecs_to_show.append(NED2USE(vec)) elif system.upper() == 'XYZ': if fancy: lo_vecs_to_show.append(fancy_vector(NED2XYZ(vec))) else: lo_vecs_to_show.append(NED2XYZ(vec)) elif system.upper() == 'NWU': if fancy: lo_vecs_to_show.append(fancy_vector(NED2NWU(vec))) else: lo_vecs_to_show.append(NED2NWU(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
""" 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') """ if style == 'f': print '\n Full moment tensor in %s-coordinates: ' % (system) return self._matrix_w_style_and_system(self._M, system, style) else: return self._matrix_w_style_and_system(self._M, system, style)
""" 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()]
""" Nice compilation of decomposition result to be viewed in the shell (call with 'print'). """ mexp = pow(10, N.ceil(N.log10(N.max(N.abs(self._M))))) m = self._M / mexp s = '\nScalar Moment: M0 = %g Nm (Mw = %3.1f)\n' s += 'Moment Tensor: Mnn = %6.3f, Mee = %6.3f, Mdd = %6.3f,\n' s += ' Mne = %6.3f, Mnd = %6.3f, Med = %6.3f ' s += '[ x %g ]\n\n' s = s % (self._seismic_moment, self._moment_magnitude, m[0, 0], m[1, 1], m[2, 2], m[0, 1], m[0, 2], m[1, 2], mexp) s += self._fault_planes_as_str() return s
""" 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
""" Returns the basis system of the input. """ if style == 'f': print '\n Basis system of the input:\n ' return self._input_basis
""" Returns the basis system of the input. """ if style == 'f': print '\n Basis system of the output: \n ' return self._output_basis
""" Returns the decomposition type. """ decomp_dict = dict(zip(('20', '21', '31'), ('ISO + DC + CLVD', 'ISO + major DC + minor DC', 'ISO + DC1 + DC2 + DC3'))) if style == 'f': print '\n Decomposition type: \n ' return decomp_dict[str(self._decomposition_key)]
return self._decomposition_key
""" 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') """ if style == 'f': print '\n Isotropic part in %s-coordinates: ' % (system) return self._matrix_w_style_and_system(self._isotropic, system, style)
""" 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') """ if style == 'f': print '\n Deviatoric part in %s-coordinates: ' % (system) return self._matrix_w_style_and_system(self._deviatoric, system, style)
""" 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') """ if style == 'f': print '\n Double Couple part in %s-coordinates:' % (system) return self._matrix_w_style_and_system(self._DC, system, style)
""" 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 style == 'f': print '\n second Double Couple part in %s-coordinates:' % (system) if self._DC2 == None: if style == 'f': print ' not available in this decomposition type ' return ''
return self._matrix_w_style_and_system(self._DC2, system, style)
""" 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 style == 'f': print '\n third Double Couple part in %s-coordinates:' % (system)
if self._DC3 == None: if style == 'f': print ' not available in this decomposition type ' return '' return self._matrix_w_style_and_system(self._DC3, system, style)
""" 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 style == 'f': print '\n CLVD part in %s-coordinates: \n' % (system) if self._CLVD == None: if style == 'f': print ' not available in this decomposition type ' return ''
return self._matrix_w_style_and_system(self._CLVD, system, style)
""" Returns the percentage of the DC part of the moment tensor in matrix representation. """ if style == 'f': print '\n Double Couple percentage: \n' return self._DC_percentage
""" Returns the percentage of the DC part of the moment tensor in matrix representation. """ if style == 'f': print '\n CLVD percentage: \n' if self._CLVD == None: if style == 'f': print ' not available in this decomposition type ' return '' return int(100 - self._DC_percentage - self._iso_percentage)
""" Returns the percentage of the second DC part of the moment tensor in matrix representation. """ if style == 'f': print "\n second Double Couple's percentage: \n" if self._DC2 == None: if style == 'f': print ' not available in this decomposition type ' return '' return self._DC2_percentage
""" Returns the percentage of the third DC part of the moment tensor in matrix representation. """ if style == 'f': print "\n third Double Couple percentage: \n" if self._DC3 == None: if style == 'f': print ' not available in this decomposition type ' return '' return int(100 - self._DC2_percentage - self._DC_percentage)
""" Returns the percentage of the isotropic part of the moment tensor in matrix representation. """ if style == 'f': print '\n Isotropic percentage: \n' return self._iso_percentage
""" Returns the percentage of the deviatoric part of the moment tensor in matrix representation. """ if style == 'f': print '\n Deviatoric percentage: \n' return int(100 - self._iso_percentage)
""" Returns the seismic moment (in Nm) of the moment tensor. """ if style == 'f': print '\n Seismic moment (in Nm) : \n ' return self._seismic_moment
""" Returns the moment magnitude M_w of the moment tensor. """ if style == 'f': print '\n Moment magnitude Mw: \n ' return self._moment_magnitude
""" 10 = standard decomposition (Jost & Herrmann) """ if style == 'f': print '\n Decomposition key (standard = 10): \n ' return self._decomposition_key
""" Returns a list of the eigenvalues of the moment tensor. """ if style == 'f': if self._plot_clr_order < 0: print '\n Eigenvalues T N P :\n' else: print '\n Eigenvalues P N T :\n' # in the order HNS: return self._eigenvalues
""" 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') """ if style == 'f':
if self._plot_clr_order < 0: print '\n Eigenvectors T N P (in basis system %s): ' % \ (system) else: print '\n Eigenvectors P N T (in basis system %s): ' % \ (system)
return self._vector_w_style_and_system(self._eigenvectors, system, style)
""" 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') """ if style == 'f': print '\n Null-axis in %s -coordinates: ' % (system) return self._vector_w_style_and_system(self._null_axis, system, style)
""" 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') """ if style == 'f': print '\n Tension-axis in %s -coordinates: ' % (system) return self._vector_w_style_and_system(self._t_axis, system, style)
""" 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') """ if style == 'f': print '\n Pressure-axis in %s -coordinates: ' % (system) return self._vector_w_style_and_system(self._p_axis, system, style)
""" 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') """ if style == 'f': print '\n rotation matrix in %s -coordinates: ' % (system) return self._matrix_w_style_and_system(self._rotation_matrix, system, style)
""" 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() == 'f': return self._fault_planes_as_str() else: return self._faultplanes
""" Returns the value of the plotting order (only important in BeachBall instances). """ style = kwargs.get('style', '0')[0].lower() if style == 'f': print '\n Colour order key: ' return self._plot_clr_order
#--------------------------------------------------------------- # # external functions: # #---------------------------------------------------------------
lo_bases = ['NED', 'USE', 'XYZ', 'NWU'] if (not in_basis in lo_bases) and(out_basis in lo_bases): sys.exit('wrong basis chosen')
if in_basis == out_basis: transformed_in = mat_tup_arr_vec elif in_basis == 'NED': if out_basis == 'USE': transformed_in = NED2USE(mat_tup_arr_vec) if out_basis == 'XYZ': transformed_in = NED2XYZ(mat_tup_arr_vec) if out_basis == 'NWU': transformed_in = NED2NWU(mat_tup_arr_vec) elif in_basis == 'USE': if out_basis == 'NED': transformed_in = USE2NED(mat_tup_arr_vec) if out_basis == 'XYZ': transformed_in = USE2XYZ(mat_tup_arr_vec) if out_basis == 'NWU': transformed_in = USE2NWU(mat_tup_arr_vec) elif in_basis == 'XYZ': if out_basis == 'NED': transformed_in = XYZ2NED(mat_tup_arr_vec) if out_basis == 'USE': transformed_in = XYZ2USE(mat_tup_arr_vec) if out_basis == 'NWU': transformed_in = XYZ2NWU(mat_tup_arr_vec) elif in_basis == 'NWU': if out_basis == 'NED': transformed_in = NWU2NED(mat_tup_arr_vec) if out_basis == 'USE': transformed_in = NWU2USE(mat_tup_arr_vec) if out_basis == 'XYZ': transformed_in = NWU2XYZ(mat_tup_arr_vec)
if len(mat_tup_arr_vec) == 3 and N.prod(N.shape(mat_tup_arr_vec)) != 9: tmp_array = N.array([0, 0, 0]) tmp_array[:] = transformed_in return tmp_array else: return transformed_in
""" Generates the output for the functions, yielding matrices, vectors, and arrays in new basis systems.
Allowed input are 3x3 matrices, 3-vectors, 3-vector collections, 3-arrays, and 6-tuples. Matrices are transformed directly, 3-vectors the same.
6-arrays are interpreted as 6 independent components of a moment tensor, so they are brought into symmetric 3x3 matrix form. This is transformed, and the 6 standard components 11,22,33,12,13,23 are returned. """ if (not N.prod(N.shape(ma_ve_ar)) in [3, 6, 9]) or \ (not len(N.shape(ma_ve_ar)) in [1, 2]): print '\n wrong input - ', print 'provide either 3x3 matrix or 3-element vector \n' raise MTError(' !! ')
if N.prod(N.shape(ma_ve_ar)) == 9: return N.dot(basis_change_matrix, N.dot(ma_ve_ar, basis_change_matrix.T)) elif N.prod(N.shape(ma_ve_ar)) == 6: m_in = ma_ve_ar orig_matrix = N.matrix([[m_in[0], m_in[3], m_in[4]], [m_in[3], m_in[1], m_in[5]], [m_in[4], m_in[5], m_in[2]]], dtype=N.float) m_out_mat = N.dot(basis_change_matrix, N.dot(orig_matrix, basis_change_matrix.T))
return m_out_mat[0, 0], m_out_mat[1, 1], m_out_mat[2, 2], \ m_out_mat[0, 1], m_out_mat[0, 2], m_out_mat[1, 2] else: if N.shape(ma_ve_ar)[0] == 1: return N.dot(basis_change_matrix, ma_ve_ar.transpose()) else: return N.dot(basis_change_matrix, ma_ve_ar)
""" Function for basis transform from basis USE to NED.
Input: 3x3 matrix or 3-element vector or 6-element array in USE basis representation
Output: 3x3 matrix or 3-element vector or 6-element array in NED basis representation """ basis_change_matrix = N.matrix([[0., -1., 0.], [0., 0., 1.], [-1., 0., 0.]], dtype=N.float) return _return_matrix_vector_array(some_matrix_or_vector, basis_change_matrix)
""" Function for basis transform from basis XYZ to NED.
Input: 3x3 matrix or 3-element vector or 6-element array in XYZ basis representation
Output: 3x3 matrix or 3-element vector or 6-element array in NED basis representation """ basis_change_matrix = N.matrix([[0., 1., 0.], [1., 0., 0.], [0., 0., -1.]], dtype=N.float) return _return_matrix_vector_array(some_matrix_or_vector, basis_change_matrix)
""" Function for basis transform from basis NWU to NED.
Input: 3x3 matrix or 3-element vector or 6-element array in NWU basis representation
Output: 3x3 matrix or 3-element vector or 6-element array in NED basis representation """ basis_change_matrix = N.matrix([[1., 0., 0.], [0., -1., 0.], [0., 0., -1.]], dtype=N.float) return _return_matrix_vector_array(some_matrix_or_vector, basis_change_matrix)
""" Function for basis transform from basis NED to USE.
Input: 3x3 matrix or 3-element vector or 6-element array in NED basis representation
Output: 3x3 matrix or 3-element vector or 6-element array in USE basis representation """ basis_change_matrix = N.matrix([[0., -1., 0.], [0., 0., 1.], [-1., 0., 0.]], dtype=N.float).I return _return_matrix_vector_array(some_matrix_or_vector, basis_change_matrix)
""" Function for basis transform from basis XYZ to USE.
Input: 3x3 matrix or 3-element vector or 6-element array in XYZ basis representation
Output: 3x3 matrix or 3-element vector or 6-element array in USE basis representation """ basis_change_matrix = N.matrix([[0., 0., 1.], [0., -1., 0.], [1., 0., 0.]], dtype=N.float) return _return_matrix_vector_array(some_matrix_or_vector, basis_change_matrix)
""" Function for basis transform from basis NED to XYZ.
Input: 3x3 matrix or 3-element vector or 6-element array in NED basis representation
Output: 3x3 matrix or 3-element vector or 6-element array in XYZ basis representation """ basis_change_matrix = N.matrix([[0., 1., 0.], [1., 0., 0.], [0., 0., -1.]], dtype=N.float).I return _return_matrix_vector_array(some_matrix_or_vector, basis_change_matrix)
""" Function for basis transform from basis NED to NWU.
Input: 3x3 matrix or 3-element vector or 6-element array in NED basis representation
Output: 3x3 matrix or 3-element vector or 6-element array in NWU basis representation """ basis_change_matrix = N.matrix([[1., 0., 0.], [0., -1., 0.], [0., 0., -1.]], dtype=N.float).I return _return_matrix_vector_array(some_matrix_or_vector, basis_change_matrix)
""" Function for basis transform from basis USE to XYZ.
Input: 3x3 matrix or 3-element vector or 6-element array in USE basis representation
Output: 3x3 matrix or 3-element vector or 6-element array in XYZ basis representation """ basis_change_matrix = N.matrix([[0., 0., 1.], [0., -1., 0.], [1., 0., 0.]], dtype=N.float).I return _return_matrix_vector_array(some_matrix_or_vector, basis_change_matrix)
""" Function for basis transform from basis USE to XYZ.
Input: 3x3 matrix or 3-element vector or 6-element array in USE basis representation
Output: 3x3 matrix or 3-element vector or 6-element array in XYZ basis representation """ basis_change_matrix = N.matrix([[0., -1., 0.], [1., 0., 0.], [0., 0., 1.]], dtype=N.float) return _return_matrix_vector_array(some_matrix_or_vector, basis_change_matrix)
""" Function for basis transform from basis USE to XYZ.
Input: 3x3 matrix or 3-element vector or 6-element array in USE basis representation
Output: 3x3 matrix or 3-element vector or 6-element array in XYZ basis representation """ basis_change_matrix = N.matrix([[0., 0., 1.], [-1., 0., 0.], [0., -1., 0.]], dtype=N.float) return _return_matrix_vector_array(some_matrix_or_vector, basis_change_matrix)
""" Function for basis transform from basis USE to XYZ.
Input: 3x3 matrix or 3-element vector or 6-element array in USE basis representation
Output: 3x3 matrix or 3-element vector or 6-element array in XYZ basis representation """ basis_change_matrix = N.matrix([[0., -1., 0.], [1., 0., 0.], [0., 0., 1.]], dtype=N.float).I return _return_matrix_vector_array(some_matrix_or_vector, basis_change_matrix)
""" Function for basis transform from basis USE to XYZ.
Input: 3x3 matrix or 3-element vector or 6-element array in USE basis representation
Output: 3x3 matrix or 3-element vector or 6-element array in XYZ basis representation """ basis_change_matrix = N.matrix([[0., 0., 1.], [-1., 0., 0.], [0., -1., 0.]], dtype=N.float).I return _return_matrix_vector_array(some_matrix_or_vector, basis_change_matrix)
""" angles are defined as in Jost&Herman (given in degrees)
strike angle clockwise between north and plane ( in [0,360] ) dip angle between surface and dipping plane ( in [0,90] ), 0 = horizontal, 90 = vertical rake angle on the rupture plane between strike vector and actual movement (defined mathematically positive: ccw rotation is positive)
basis for output is NED (= X,Y,Z)
output: M = M_nn, M_ee, M_dd, M_ne, M_nd, M_ed """ S_rad = strike / rad2deg D_rad = dip / rad2deg R_rad = rake / rad2deg
for ang in S_rad, D_rad, R_rad: if abs(ang) < epsilon: ang = 0.
M1 = -(N.sin(D_rad) * N.cos(R_rad) * N.sin(2 * S_rad) + \ N.sin(2 * D_rad) * N.sin(R_rad) * N.sin(S_rad) ** 2) M2 = (N.sin(D_rad) * N.cos(R_rad) * N.sin(2 * S_rad) - \ N.sin(2 * D_rad) * N.sin(R_rad) * N.cos(S_rad) ** 2) M3 = (N.sin(2 * D_rad) * N.sin(R_rad)) M4 = (N.sin(D_rad) * N.cos(R_rad) * N.cos(2 * S_rad) + \ 0.5 * N.sin(2 * D_rad) * N.sin(R_rad) * N.sin(2 * S_rad)) M5 = -(N.cos(D_rad) * N.cos(R_rad) * N.cos(S_rad) + \ N.cos(2 * D_rad) * N.sin(R_rad) * N.sin(S_rad)) M6 = -(N.cos(D_rad) * N.cos(R_rad) * N.sin(S_rad) - \ N.cos(2 * D_rad) * N.sin(R_rad) * N.cos(S_rad))
Moments = [M1, M2, M3, M4, M5, M6]
return tuple(Moments)
""" Returns a given 3x3 matrix or array in a cute way on the shell, if you use 'print' on the return value. """ m = m_in.copy()
# aftercom = 1 # maxlen = (int(N.log10(N.max(N.abs(m))))) # if maxlen < 0: # aftercom = -maxlen + 1 # maxlen = 1 norm_factor = round(max(abs(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])
""" 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 for plotting: # #---------------------------------------------------------------
""" Class for generating a beachball projection for a provided moment tensor object.
Input: a MomentTensor object
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. """ 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 = N.arange(3 * npoints) - 1 # self._identify_faultplanes()
""" Plots the projection of the beachball onto a unit sphere. """ self._update_attributes(kwargs) self._setup_BB() self._plot_US(ax=ax)
""" Saves the 2D projection of the beachball without plotting.
: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()
""" Saves the beachball unit sphere plot into a given file. """ try: del matplotlib # @UndefinedVariable except: pass try: del pylab # @UndefinedVariable except: pass try: del P # @UndefinedVariable except: pass
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') # matplotlib.use('GDK') # try: # matplotlib.use('GTKCairo') # except: # matplotlib.use('GTKCairo') # pass
import pylab as P
plotfig = self._setup_plot_US(P)
outfile_format = self._plot_outfile_format outfile_name = self._plot_outfile
outfile_abs_name = op.realpath(op.abspath(op.join(os.curdir, outfile_name)))
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
""" Returns one string, to be piped into psxy of GMT.
: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
""" 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) N.savetxt(FH_string, self._GMT_scaling * curve.transpose())
""" 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)
""" Returns the original moment tensor object, handed over to the class at generating this instance. """ return self.MT
""" Plot of the full beachball, projected on a circle with a radius 2. """ self._update_attributes(kwargs) self._setup_BB() self._aux_plot()
""" Generates the final plot of the total sphere (according to the chosen 2D-projection. """ import matplotlib 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(N.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 = -N.arctan2(y_coord, x_coord) + pi / 2. original_rho = N.sqrt(x_coord ** 2 + y_coord ** 2)
marker_x = (original_rho - \ (3 * symsize / points_per_unit)) * N.sin(rot_angle) marker_y = (original_rho - \ (3 * symsize / points_per_unit)) * N.cos(rot_angle) annot_x = (original_rho - (8.5 * fontsize / \ points_per_unit)) * N.sin(rot_angle) annot_y = (original_rho - (8.5 * fontsize / \ points_per_unit)) * N.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()
del P del matplotlib
""" Plot of the solution in the principal axes system. """ import matplotlib 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 (N.arange(4) + 1) * 0.2: r_steps.append(i) r_labels = ['S'] for ii in xrange(len(r_steps)): if (ii + 1) % 2 == 0: r_labels.append(str(r_steps[ii])) else: r_labels.append(' ')
t_angles = N.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()
del P del matplotlib
""" Sets default values of mandatory arguments. """ # plot basis system and view point: self._plot_basis = 'NED' self._plot_projection = 'stereo' 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'
""" 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)
""" 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 N.trace(self.MT.get_M()) < 0: self._plot_clr_order = 1 else: self._plot_clr_order = -1
""" 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'] projection = self._plot_projection
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 = N.sqrt(obj2cor_in_right_order[0, 0] ** 2 + \ obj2cor_in_right_order[1, 0] ** 2) r_last_point = N.sqrt(obj2cor_in_right_order[0, -1] ** 2 + \ obj2cor_in_right_order[1, -1] ** 2) dist_last_first_point = \ N.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 = N.arctan2(obj2cor_in_right_order[0, -1], obj2cor_in_right_order[1, -1]) % (2 * pi) R_end = r_last_point phi_start = N.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 = N.sign(phi_end - phi_start) angle_smaller_pi = N.sign(pi - N.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 xrange(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 * M.sin(current_phi), current_radius * N.cos(current_phi)] obj2cor_in_right_order.append(temp) obj2cor_in_right_order = \ N.array(obj2cor_in_right_order).transpose() else: obj2cor_in_right_order = \ list(obj2cor_in_right_order.transpose()) for kk in xrange(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 * M.sin(current_phi), current_radius * N.cos(current_phi)] obj2cor_in_right_order.append(temp) obj2cor_in_right_order = \ N.array(obj2cor_in_right_order).transpose() setattr(self, '_' + obj + '_in_order', obj2cor_in_right_order) return 1
""" 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 = (N.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 * N.trace(self._M) EWn_devi = self.MT.get_eigvals()[1] - 1. / 3 * N.trace(self._M) EWs_devi = self.MT.get_eigvals()[2] - 1. / 3 * N.trace(self._M)
if not self._plot_isotropic_part: EWh = EWh_devi EWn = EWn_devi EWs = EWs_devi
# iso_perc = self.MT._iso_percentage # sign_colour= self._plot_clr_order # positiv, wenn symmetrie um # # explos. komp. # sign_trace = N.sign(N.trace(self._M)) # positiv, wenn explos.
# sign_total =sign_colour* sign_trace
# EWs *= (1+sign_total*2*iso_perc/100)
else: EWh_tmp = self.MT.get_eigvals()[0] # - 1./3 * N.trace(self._M) EWn_tmp = self.MT.get_eigvals()[1] # - 1./3 * N.trace(self._M) EWs_tmp = self.MT.get_eigvals()[2] # - 1./3 * N.trace(self._M)
trace_m = N.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(N.abs([EWh, EWn, EWs]))
[EWh, EWn, EWs] = [xx / norm_factor for xx in [EWh, EWn, EWs]]
RHS = -EWs / (EWn * N.cos(phi) ** 2 + EWh * N.sin(phi) ** 2) # for i,grad in enumerate(phi): # if abs(360-grad) < 1 or abs(0-grad) < 1 or abs(90-grad) < 1 : # RHS[i] -= 0.5
if N.all([N.sign(xx) >= 0 for xx in RHS]): alpha = N.arctan(N.sqrt(RHS)) * rad2deg else: alpha = phi.copy() alpha[:] = 90 self._pure_isotropic = 1
# fault planes: RHS_FP = 1. / (N.sin(phi) ** 2) alpha_FP = N.arctan(N.sqrt(RHS_FP)) * rad2deg
# horizontal coordinates of curves r_hor = N.sin(alpha / rad2deg) r_hor_FP = N.sin(alpha_FP / rad2deg)
self._r_hor_for_pa_plot = r_hor self._r_hor_FP_for_pa_plot = r_hor_FP
H_values = N.sin(phi) * r_hor N_values = N.cos(phi) * r_hor H_values_FP = N.sin(phi) * r_hor_FP N_values_FP = N.cos(phi) * r_hor_FP
# set vertical value of curve point coordinates - two symmetric curves # exist S_values_positive = N.cos(alpha / rad2deg) S_values_negative = -N.cos(alpha / rad2deg) S_values_positive_FP = N.cos(alpha_FP / rad2deg) S_values_negative_FP = -N.cos(alpha_FP / rad2deg)
############# # change basis back to original input reference system #########
chng_basis = self.MT._rotation_matrix
line_tuple_pos = N.zeros((3, n_curve_points)) line_tuple_neg = N.zeros((3, n_curve_points))
for ii in xrange(n_curve_points): pos_vec_in_EV_basis = N.array([H_values[ii], N_values[ii], S_values_positive[ii]]).transpose() neg_vec_in_EV_basis = N.array([H_values[ii], N_values[ii], S_values_negative[ii]]).transpose() line_tuple_pos[:, ii] = N.dot(chng_basis, pos_vec_in_EV_basis) line_tuple_neg[:, ii] = N.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 = N.zeros((3, 6))
EVh_orig = N.dot(chng_basis, EVs) all_EV[:, 0] = EVh.transpose() # _orig.transpose() EVn_orig = N.dot(chng_basis, EVn) all_EV[:, 1] = EVn.transpose() # _orig.transpose() EVs_orig = N.dot(chng_basis, EVh) all_EV[:, 2] = EVs.transpose() # _orig.transpose() EVh_orig_neg = N.dot(chng_basis, EVs) all_EV[:, 3] = -EVh.transpose() # _orig_neg.transpose() EVn_orig_neg = N.dot(chng_basis, EVn) all_EV[:, 4] = -EVn.transpose() # _orig_neg.transpose() EVs_orig_neg = N.dot(chng_basis, EVh) all_EV[:, 5] = -EVs.transpose() # _orig_neg.transpose()
# basis vectors: all_BV = N.zeros((3, 6)) all_BV[:, 0] = N.array((1, 0, 0)) all_BV[:, 1] = N.array((-1, 0, 0)) all_BV[:, 2] = N.array((0, 1, 0)) all_BV[:, 3] = N.array((0, -1, 0)) all_BV[:, 4] = N.array((0, 0, 1)) all_BV[:, 5] = N.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 = N.zeros((3, n_curve_points)) FP2 = N.zeros((3, n_curve_points))
for ii in xrange(midpoint_idx): FP1_vec = N.array([H_values_FP[ii], N_values_FP[ii], S_values_positive_FP[ii]]).transpose() FP2_vec = N.array([H_values_FP[ii], N_values_FP[ii], S_values_negative_FP[ii]]).transpose() FP1[:, ii] = N.dot(chng_basis, FP1_vec) FP2[:, ii] = N.dot(chng_basis, FP2_vec)
for jj in xrange(midpoint_idx): ii = n_curve_points - jj - 1
FP1_vec = N.array([H_values_FP[ii], N_values_FP[ii], S_values_negative_FP[ii]]).transpose() FP2_vec = N.array([H_values_FP[ii], N_values_FP[ii], S_values_positive_FP[ii]]).transpose() FP1[:, ii] = N.dot(chng_basis, FP1_vec) FP2[:, ii] = N.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
""" 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
""" 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 = N.sin(new_latitude / rad2deg) if abs(s_lat) < epsilon: s_lat = 0 c_lat = N.cos(new_latitude / rad2deg) if abs(c_lat) < epsilon: c_lat = 0 s_lon = N.sin(new_longitude / rad2deg) if abs(s_lon) < epsilon: s_lon = 0 c_lon = N.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 = - ( N.array( ( s_lat, c_lat*c_lon, -c_lat*s_lon ) ) ) down_prime = -(N.array((s_lat, c_lat * s_lon, -c_lat * c_lon)))
#normalise: down_prime /= N.sqrt(N.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 = N.array((1., 0., 0.)) - \ (N.dot(down_prime, N.array((1., 0., 0.))) / \ (N.dot(down_prime, down_prime)) * down_prime)
len_north_prime_not_normalised = \ N.sqrt(N.dot(north_prime_not_normalised, north_prime_not_normalised)) # check for poles: if N.abs(len_north_prime_not_normalised) < epsilon: # case: north pole if s_lat > 0: north_prime = N.array((0., 0., 1.)) # case: south pole else: north_prime = N.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 = N.cross(down_prime, north_prime)
# normalise: east_prime /= N.sqrt(N.dot(east_prime, east_prime))
rotmat_pos_raw = N.zeros((3, 3)) rotmat_pos_raw[:, 0] = north_prime rotmat_pos_raw[:, 1] = east_prime rotmat_pos_raw[:, 2] = down_prime
rotmat_pos = N.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 = N.zeros((3, 3)) s_az = N.sin(new_azimuth / rad2deg) if abs(s_az) < epsilon: s_az = 0. c_az = N.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 = N.asmatrix(only_rotation)
# apply rotation from left!! total_rotation_matrix = N.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
""" 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() # logger.debug(str(N.shape(object2rotate)),str(len(object2rotate))) # logger.debug(str(N.shape(self._plot_basis_change)))
rotated_thing = object2rotate.copy() for i in xrange(len(object2rotate)): rotated_thing[i] = N.dot(self._plot_basis_change, object2rotate[i])
rotated_object = rotated_thing.copy() setattr(self, '_' + obj + '_rotated', rotated_object.transpose())
#---------------------------------------------------------------
""" 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(' !! ')
""" 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 = N.zeros((2, n_points))
for ll in xrange(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 = N.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
""" 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 = N.zeros((2, n_points))
for ll in xrange(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 = N.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
""" 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 = N.zeros((2, n_points))
for ll in xrange(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 = N.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 / N.sqrt(1. - co_z)
if plot_upper_hem: new_rho = 2 - (rho_hor / N.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 / N.sqrt(1. + co_z))
if plot_upper_hem: new_rho = rho_hor / N.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
""" 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 = N.zeros((2, n_points))
for ll in xrange(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 = N.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 = \ N.cos(N.arcsin(rho_hor)) * N.tan(N.arcsin(rho_hor))
if plot_upper_hem: new_rho = 2 - (N.cos(N.arcsin(rho_hor)) * \ N.tan(N.arcsin(rho_hor)))
new_x = co_x / rho_hor * new_rho new_y = co_y / rho_hor * new_rho
else: new_rho = 2 - (N.cos(N.arcsin(rho_hor)) * \ N.tan(N.arcsin(rho_hor)))
if plot_upper_hem: new_rho = N.cos(N.arcsin(rho_hor)) * \ N.tan(N.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
""" 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 = N.zeros((2, len(phi))) UnitSphere[0, :] = N.cos(phi) UnitSphere[1, :] = N.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
""" Checks, if curve points are in right order for line plotting.
If not, a re-arranging is carried out. """ sorted_curve = N.zeros((2, len(curve[0, :]))) # in polar coordinates r_phi_curve = N.zeros((len(curve[0, :]), 2)) for ii in xrange(curve.shape[1]): r_phi_curve[ii, 0] = M.sqrt(curve[0, ii] ** 2 + curve[1, ii] ** 2) r_phi_curve[ii, 1] = M.atan2(curve[0, ii], curve[1, ii]) % (2 * pi) # find index with highest r largest_r_idx = N.argmax(r_phi_curve[:, 0])
# check, if perhaps more values with same r - if so, take point with # lowest phi other_idces = \ list(N.where(r_phi_curve[:, 0] == r_phi_curve[largest_r_idx, 0])) if len(other_idces) > 1: best_idx = N.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 xrange(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 xrange(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 = (M.atan2(sorted_curve[0, 0], sorted_curve[1, 0]) % (2 * pi)) angle_point_2 = (M.atan2(sorted_curve[0, 1], sorted_curve[1, 1]) % (2 * pi)) angle_point_3 = (M.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 = M.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 * M.sin(new_angle) sorted_curve[1, 0] = r_old * M.cos(new_angle)
return sorted_curve
""" 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 = N.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 = M.sqrt(val[0] ** 2 + val[1] ** 2) r2 = M.sqrt(obj[idx + 1][0] ** 2 + obj[idx + 1][1] ** 2) phi1 = M.atan2(val[0], val[1]) phi2 = M.atan2(obj[idx + 1][0], obj[idx + 1][1])
phi2_larger = N.sign(phi2 - phi1) angle_smaller_pi = N.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 = N.zeros((n_fillpoints, 2)) if go_cw: angles = ((N.arange(n_fillpoints) + 1) * openangle / \ (n_fillpoints + 1) + phi1) % (2 * pi) else: angles = (phi1 - (N.arange(n_fillpoints) + 1) * \ openangle / (n_fillpoints + 1)) % (2 * pi)
radii = (N.arange(n_fillpoints) + 1) * \ radius_diff / (n_fillpoints + 1) + r1
fill_array[:, 0] = radii * N.sin(angles) fill_array[:, 1] = radii * N.cos(angles)
smoothed_list.append(fill_array)
smoothed_list.append([obj[idx + 1]])
smoothed_array = N.vstack(smoothed_list) setattr(self, '_' + curve2smooth + '_final', smoothed_array.transpose())
""" 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 N.prod(self.MT._rotation_matrix.A1 == testarray) and \ (self.MT._eigenvalues[1] == 0): self._plot_curve_in_curve = -1 self._plot_clr_order = 1
""" 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
""" 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 = N.require(verts, dtype=N.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
""" 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 = N.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 = N.sqrt(val[0] ** 2 + val[1] ** 2) if old_radius <= 1 + epsilon: tmp_obj.append(val) tmp_obj2 = N.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 = N.sqrt(val[0] ** 2 + val[1] ** 2) if r_ev <= 1: lo_visible_EV.append([val[0], val[1], idx]) visible_EVs = N.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 = M.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 = N.array(lo_visible_BV)
self._all_BV_2D_US = visible_BVs
""" 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 matplotlib 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') del P del matplotlib
""" 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'] EV_labels = ['P', 'N', 'T', 'P', 'N', 'T']
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( N.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 = -N.arctan2(y_coord, x_coord) + pi / 2. original_rho = N.sqrt(x_coord ** 2 + y_coord ** 2)
marker_x = (original_rho - \ (1.5 * symsize / points_per_unit)) * N.sin(rot_angle) marker_y = (original_rho - \ (1.5 * symsize / points_per_unit)) * N.cos(rot_angle) annot_x = (original_rho - \ (4.5 * fontsize / points_per_unit)) * N.sin(rot_angle) annot_y = (original_rho - \ (4.5 * fontsize / points_per_unit)) * N.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
#------------------------------------------------------------------- # # input and call management # #-------------------------------------------------------------------
""" Usage
elk_mopad plot M elk_mopad save M elk_mopad gmt M elk_mopad convert M elk_mopad decompose M """ def _which_call(call_raw): """ """ try: call = \ dict(zip(('p', 'g', 'c', 'd'), ('plot', 'gmt', 'convert', 'decompose')))[call_raw[0]] except: call = 'help'
return call
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 keyword args 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') # build the moment tensor object mt = MomentTensor(M=M_in, system=in_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 == 'save': # return _call_save(MT,kwargs_dict) elif main_call == 'convert': return _call_convert(MT, kwargs_dict) elif main_call == 'gmt': return _call_gmt(MT, kwargs_dict) elif main_call == 'decompose': return _call_decompose(MT, kwargs_dict)
# def _call_save(MT,kwargs_dict): # bb2plot = BeachBall(MT,kwargs_dict) # bb2plot.save_BB(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_convert(MT, kwargs_dict): """ """ if kwargs_dict['type_conversion']:
if kwargs_dict['type_conversion'].lower() == 'sdr': if kwargs_dict['fancy_conversion']: return MT.get_fps(style='f') else: return MT.get_fps(style='n') elif kwargs_dict['type_conversion'].upper() == 'T': if kwargs_dict['basis_conversion']: out_system = kwargs_dict['out_system'].upper() if kwargs_dict['fancy_conversion']: return MT.get_M(system=out_system, style='f') else: return MT.get_M(system=out_system, style='n') else: if kwargs_dict['fancy_conversion']: return MT.get_M(style='f') else: return MT.get_M(style='n')
if kwargs_dict['basis_conversion']: if len(MT._original_M) in [6, 7]: if len(MT._original_M) == 6: M_converted = _puzzle_basis_transformation(MT.get_M(), 'NED', kwargs_dict['out_system']) if kwargs_dict['fancy_conversion']: print '\n Moment tensor in basis %s:\n ' % \ (kwargs_dict['in_system'].upper()) print fancy_matrix(MT.get_M( system=kwargs_dict['in_system'].upper())) print '\n Moment tensor in basis %s:\n ' % \ (kwargs_dict['out_system'].upper()) return fancy_matrix(M_converted) else: return M_converted[0, 0], M_converted[1, 1], \ M_converted[2, 2], M_converted[0, 1], \ M_converted[0, 2], M_converted[1, 2] else: M_converted = _puzzle_basis_transformation(MT.get_M(),\ 'NED', kwargs_dict['out_system']) if kwargs_dict['fancy_conversion']: print '\n Moment tensor in basis %s:\n ' % \ (kwargs_dict['in_system'].upper()) print fancy_matrix(MT.get_M( system=kwargs_dict['in_system'].upper())) print '\n Moment tensor in basis %s:\n ' % \ (kwargs_dict['out_system'].upper()) return fancy_matrix(M_converted) else: return M_converted[0, 0], M_converted[1, 1], \ M_converted[2, 2], M_converted[0, 1], \ M_converted[0, 2], M_converted[1, 2], \ MT._original_M[6] elif len(MT._original_M) == 9: new_M = N.asarray(MT._original_M).reshape(3, 3).copy() if kwargs_dict['fancy_conversion']: return fancy_matrix(_puzzle_basis_transformation(new_M, \ kwargs_dict['in_system'], kwargs_dict['out_system'])) else: return _puzzle_basis_transformation(new_M, \ kwargs_dict['in_system'], kwargs_dict['out_system']) elif len(MT._original_M) == 3: M_converted = _puzzle_basis_transformation(MT.get_M(), 'NED', \ kwargs_dict['out_system']) if kwargs_dict['fancy_conversion']: print '\n Moment tensor in basis %s: ' % \ (kwargs_dict['out_system'].upper()) return fancy_matrix(M_converted) else: return M_converted[0, 0], M_converted[1, 1], \ M_converted[2, 2], M_converted[0, 1], \ M_converted[0, 2], M_converted[1, 2] elif len(MT._original_M) == 4: M_converted = MT._original_M[3] * \ _puzzle_basis_transformation(MT.get_M(), 'NED', kwargs_dict['out_system']) if kwargs_dict['fancy_conversion']: print '\n Momemnt tensor in basis %s: ' % \ (kwargs_dict['out_system'].upper()) return fancy_matrix(M_converted) else: return M_converted[0, 0], M_converted[1, 1], \ M_converted[2, 2], M_converted[0, 1], \ M_converted[0, 2], M_converted[1, 2] else: print 'this try is meaningless - read the possible', print 'choices!\n(perhaps you want option "-v"(convert a', print 'vector) or "-t"(convert strike, dip, rake to a matrix', print 'and show THAT matrix in another basis system)', print 'instead!?!?)\n' sys.exit(-1)
if kwargs_dict['vector_conversion']: if kwargs_dict['fancy_conversion']: print '\n Vector in basis %s:\n ' % \ (kwargs_dict['vector_in_system'].upper()) print fancy_vector(MT._original_M) print '\n Vector in basis %s:\n ' % \ (kwargs_dict['vector_out_system'].upper()) return fancy_vector(_puzzle_basis_transformation(\ MT._original_M, kwargs_dict['vector_in_system'], \ kwargs_dict['vector_out_system'])) else: return _puzzle_basis_transformation(MT._original_M, \ kwargs_dict['vector_in_system'], \ kwargs_dict['vector_out_system']) else: msg = 'provide either option -t with one argument, or -b with ' msg += 'two arguments, or -v with 2 arguments' sys.exit(msg)
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['decomp_out_system'] MT._output_basis = out_system MT._decomposition_key = kwargs_dict['decomposition_key']
MT._decompose_M()
# if total decomposition: if kwargs_dict['decomp_out_complete']: if kwargs_dict['decomp_out_fancy']: print MT.get_full_decomposition() return else: return MT.get_decomposition(in_system=kwargs_dict['in_system'], out_system=out_system) # otherwise: else: # argument dictionary - setting the appropriate calls do_calls = dict(zip(('in', 'out', 'type', 'full', 'm', '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', 'fps', 'faultplanes', 'fp', 'decomp_key'), ('input_system', 'output_system', 'decomp_type', 'M', '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', 'fps', 'fps', 'fps', 'decomp_type') ))
# build argument for local call within MT object: lo_args = kwargs_dict['decomp_out_part']
if kwargs_dict['decomp_out_fancy']: arg_dict = dict(style='f', system=out_system)
else: arg_dict = dict(style='n', system=out_system)
# for single element output: if len(lo_args) == 1: if kwargs_dict['decomp_out_fancy']: print getattr(MT, 'get_' + \ do_calls[lo_args[0]])(style='f', system=out_system) return else: return getattr(MT, 'get_' + \ do_calls[lo_args[0]])(style='n', system=out_system) # for list of elements: else: outlist = [] for arg in lo_args: if kwargs_dict['decomp_out_fancy']: print getattr(MT, 'get_' + \ do_calls[arg])(style='f', system=out_system) else: outlist.append(getattr(MT, 'get_' + \ do_calls[arg])(style='n', system=out_system)) if kwargs_dict['decomp_out_fancy']: return else: return outlist
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', 'decomp_in_system', 'decomp_out_system', 'decomp_key'] lo_allowed_systems = ['NED', 'USE', 'XYZ', 'NWU']
# check for allowed options: for ao in lo_allowed_options: if hasattr(options, ao): temp_dict[ao] = getattr(options, ao)
consistent_kwargs_dict['in_system'] = 'NED' if temp_dict['decomp_in_system']: if temp_dict['decomp_in_system'].upper() in lo_allowed_systems: consistent_kwargs_dict['in_system'] = \ temp_dict['decomp_in_system'].upper()
consistent_kwargs_dict['decomp_out_system'] = 'NED' if temp_dict['decomp_out_system']: if temp_dict['decomp_out_system'].upper() in lo_allowed_systems: consistent_kwargs_dict['decomp_out_system'] = \ temp_dict['decomp_out_system'].upper()
consistent_kwargs_dict['decomposition_key'] = 20 if temp_dict['decomp_key']: try: key_no = int(float(temp_dict['decomp_key'])) if key_no in [20, 21, 31]: consistent_kwargs_dict['decomposition_key'] = key_no except: pass # if option 'partial' is not chosen, take complete decomposition: if not temp_dict['decomp_out_part']: consistent_kwargs_dict['decomp_out_complete'] = 1 consistent_kwargs_dict['decomp_out_part'] = 0
if temp_dict['decomp_out_fancy']: consistent_kwargs_dict['decomp_out_fancy'] = 1 else: consistent_kwargs_dict['decomp_out_fancy'] = 0 # otherwise take only partial decomposition: else: lo_allowed_attribs = ['in', 'out', 'type', 'full', 'm', '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', 'fps', 'faultplanes', 'fp', 'decomp_key'] lo_desired_attribs = temp_dict['decomp_out_part'].split(',') lo_correct_attribs = []
# check for allowed parts of decomposition: for da in lo_desired_attribs: if da.lower() in lo_allowed_attribs: lo_correct_attribs.append(da.lower())
# if only wrong or no arguments are handed over, change to complete # decomposition: if len(lo_correct_attribs) == 0: print ' no correct attributes for partial decomposition -', print 'returning complete decomposition' consistent_kwargs_dict['decomp_out_complete'] = 1 consistent_kwargs_dict['decomp_out_part'] = 0 if temp_dict['decomp_out_fancy']: consistent_kwargs_dict['decomp_out_fancy'] = 1 else: consistent_kwargs_dict['decomp_out_fancy'] = 0
# if only one part is desired to be schown, fancy style is possible elif len(lo_correct_attribs) == 1: consistent_kwargs_dict['decomp_out_complete'] = 0 consistent_kwargs_dict['decomp_out_part'] = \ [lo_correct_attribs[0]] if temp_dict['decomp_out_fancy']: consistent_kwargs_dict['decomp_out_fancy'] = 1 else: consistent_kwargs_dict['decomp_out_fancy'] = 0
# if several parts are desired to be schown, fancy style is # NOT possible: else: consistent_kwargs_dict['decomp_out_complete'] = 0 consistent_kwargs_dict['decomp_out_part'] = lo_correct_attribs if temp_dict['decomp_out_fancy']: consistent_kwargs_dict['decomp_out_fancy'] = 1 else: consistent_kwargs_dict['decomp_out_fancy'] = 0
consistent_kwargs_dict['style'] = 'n' if consistent_kwargs_dict['decomp_out_fancy']: consistent_kwargs_dict['style'] = 'f'
return consistent_kwargs_dict
def _build_convert_dict(options, optparser): """ """ consistent_kwargs_dict = {} temp_dict = {} lo_allowed_options = ['type_conversion', 'basis_conversion', 'vector_conversion', 'fancy_conversion'] # check for allowed options: for ao in lo_allowed_options: if hasattr(options, ao): temp_dict[ao] = getattr(options, ao)
consistent_kwargs_dict['in_system'] = 'NED' consistent_kwargs_dict = temp_dict
if 'out_system' not in consistent_kwargs_dict: consistent_kwargs_dict['out_system'] = 'NED'
if (temp_dict['type_conversion'] and temp_dict['vector_conversion']): print 'decide for ONE option of "-t" OR "-v" ' optparser.print_help() sys.exit(-1)
if (temp_dict['type_conversion']) and \ (not temp_dict['type_conversion'].lower() in ['sdr', 't']): print 'argument of -t must be "sdr" or "T" ' optparser.print_help() sys.exit(-1)
if (temp_dict['basis_conversion']): for arg in temp_dict['basis_conversion']: if not arg.upper() in ['NED', 'XYZ', 'USE', 'NWU']: print 'arguments of -b must be "NED","USE","XYZ","NWU" ' optparser.print_help() sys.exit(-1) consistent_kwargs_dict['in_system'] = \ temp_dict['basis_conversion'][0].upper() consistent_kwargs_dict['out_system'] = \ temp_dict['basis_conversion'][1].upper()
if temp_dict['type_conversion'] and \ temp_dict['type_conversion'].lower() == 'sdr': if temp_dict['basis_conversion']: if temp_dict['basis_conversion'][1] != 'NED': print 'output "sdr" from type conversion cannot be', print 'displayed in another basis system!' consistent_kwargs_dict['out_system'] = 'NED'
if (temp_dict['vector_conversion']): for arg in temp_dict['vector_conversion']: if not arg.upper() in ['NED', 'XYZ', 'USE', 'NWU']: print 'arguments of -v must be "NED","USE","XYZ","NWU" ' optparser.print_help() sys.exit(-1) consistent_kwargs_dict['vector_in_system'] = \ temp_dict['vector_conversion'][0].upper() consistent_kwargs_dict['vector_out_system'] = \ temp_dict['vector_conversion'][1].upper()
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_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) = op.split(temp_dict['plot_outfile']) if not filename: filename = 'dummy_filename.svg' (shortname, extension) = op.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'] = \ op.realpath(op.abspath(op.join(os.curdir, \ filepath, shortname + extension[1:].lower()))) else: consistent_kwargs_dict['plot_outfile'] = \ op.realpath(op.abspath(op.join(os.curdir, \ filepath, shortname + '.' + \ extension[1:].lower()))) else: if filename.endswith('.'): consistent_kwargs_dict['plot_outfile'] = \ op.realpath(op.abspath(op.join(os.curdir, \ filepath, filename + lo_possible_formats[0]))) else: consistent_kwargs_dict['plot_outfile'] = \ op.realpath(op.abspath(op.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: <name>.<format> !!\n' msg += ' <format> 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_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_save_dict(options, optparser): """ """ options.plot_save_plot = True consistent_kwargs_dict = _build_plot_dict(options, optparser)
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: msg = '\n ERROR - check carefully number of arguments ' msg += 'for all options\n' sys.exit(msg)
if main_call == 'plot': consistent_kwargs_dict = _build_plot_dict(options, optparser) # elif main_call =='save': # consistent_kwargs_dict = _build_save_dict(options,optparser) elif main_call == 'gmt': consistent_kwargs_dict = _build_gmt_dict(options, optparser) # convert elif main_call == 'convert': consistent_kwargs_dict = _build_convert_dict(options, optparser) # decompose elif main_call == 'decompose': consistent_kwargs_dict = _build_decompose_dict(options, optparser)
return consistent_kwargs_dict
def _build_optparsers(): """ build dictionary with 4 (5 incl. 'save') sets of options, belonging to the 4 (5) possible calls """ _do_parsers = {}
from optparse import OptionParser, OptionGroup
# gmt desc = """Tool providing strings to be piped into the 'psxy' from GMT.
Either a string describing the fillable area (to be used with option '-L' within psxy) or the nodallines or the coordinates of the principle axes are given. """
parser_gmt = OptionParser(usage="\nelk_mopad gmt M [options]", description=desc)
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="choice of psxy data: area to fill (fill)), nodal lines " + \ "(lines), or eigenvector positions (ev)", metavar='<type>') group_show.add_option('-s', '--scaling', dest='GMT_scaling', action='store', default='1', type='float', metavar='<scaling factor>', help='spatial scaling of the beachball') group_show.add_option('-r', '--colour1', dest='GMT_tension_colour', type='int', action='store', metavar='<tension colour>', default='1', help="-Z option's key for the tension colour of the " + \ "beachball - type: integer ") group_show.add_option('-w', '--colour2', dest='GMT_pressure_colour', type='int', action='store', metavar='<pressure colour>', default='0', help="-Z option's key for the pressure colour of the " + \ "beachball - type: integer") group_show.add_option('-D', '--faultplanes', dest='GMT_show_2FP2', action='store_true', default=False, help='boolean key, if 2 faultplanes shall be shown') group_show.add_option('-d', '--show_1fp', type='choice', dest='GMT_show_1FP', choices=['1', '2'], metavar='<FP index>', action='store', default=False, help="integer key (1,2), what faultplane shall be " + \ "shown [%default]") group_geo.add_option('-V', '--viewpoint', action="store", dest='plot_viewpoint', metavar='<lat,lon,azi>', default=None, help='coordinates of the viewpoint - 3-tuple of angles in degree') group_geo.add_option('-p', '--projection', action="store", dest='GMT_projection', metavar='<projection>', default=None, help='projection of the sphere') group_show.add_option('-I', '--show_isotropic_part', dest='GMT_plot_isotropic_part', action='store_true', default=False, help="key, if isotropic part shall be considered for " + \ "plotting [%default]")
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
# convert desc = """Tool providing converted input.
Choose between the conversion from/to matrix-moment-tensor form (-t), the change of the output basis system for a given moment tensor (-b), or the change of basis for a 3D vector (-v). """ parser_convert = OptionParser( usage="\n\n elk_mopad convert M [options]", description=desc)
group_type = OptionGroup(parser_convert, 'Type conversion') group_basis = OptionGroup(parser_convert, 'M conversion') group_vector = OptionGroup(parser_convert, 'Vector conversion') # group_show = OptionGroup(parser_convert,'Appearance')
group_type.add_option('-t', '--type', action="store", dest='type_conversion', default=False, type='string', metavar='<output type>', nargs=1, help="type conversion - convert to: strike,dip,rake (sdr) or " + \ "Tensor (T) ") group_basis.add_option('-b', '--basis', action="store", dest='basis_conversion', default=False, metavar='<input system> <output system>', type='string', nargs=2, help="basis conversion for M - provide 2 arguments: input- " + \ "and output basis (NED,USE,XYZ,NWU)") group_vector.add_option('-v', '--vector', action="store", dest='vector_conversion', metavar='<input system> <output system>', type='string', default=False, nargs=2, help="basis conversion for a vector - provide M as a 3Dvector " + \ "and 2 option-arguments of -v: input- and output basis " + \ "(NED,USE,XYZ,NWU)") parser_convert.add_option('-y', '--fancy', action="store_true", dest='fancy_conversion', default=False, help='output in a stylish way ')
parser_convert.add_option_group(group_type) parser_convert.add_option_group(group_basis) parser_convert.add_option_group(group_vector) # parser_convert.add_option_group(group_show)
_do_parsers['convert'] = parser_convert
# plot desc_plot = """Plots a beachball diagram of the provided mechanism.
Several styles and configurations are available. Also saving on the fly can be enabled. """ parser_plot = OptionParser( usage="\n\n elk_mopad plot M [options]", description=desc_plot)
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('-S', '--save',\ # action="store_true",\ # dest='plot_save_plot',\ # default=False,\ # help='key, if plot shall be saved ') group_save.add_option('-f', '--output_file', action="store", dest='plot_outfile', metavar='<filename>', default=None, nargs=1, help='filename for saving ') # group_save.add_option('-f', '--file_format',\ # action="store",\ # dest='plot_outfile_format',\ # metavar='<file format>',\ # default=None,\ # nargs = 1,\ # help='format of outfile - choose between png,svg,ps,pdf,eps ') group_type.add_option('-P', '--pa_system', action="store_true", dest='plot_pa_plot', default=False, help='key, if principal axis system shall be plotted instead ') group_type.add_option('-O', '--full_sphere', action="store_true", dest='plot_full_sphere', default=False, help='key, if full sphere shall be plotted instead ') group_geo.add_option('-V', '--viewpoint', action="store", dest='plot_viewpoint', metavar='<lat,lon,azi>', default=None, help='coordinates of the viewpoint - 3-tuple ') group_geo.add_option('-p', '--projection', action="store", dest='plot_projection', metavar='<projection>', default=None, help='projection of the sphere ') group_type.add_option('-U', '--upper', action="store_true", dest='plot_show_upper_hemis', default=False, help='key, if upper hemisphere shall be shown ') group_quality.add_option('-N', '--points', action="store", metavar='<no. of points>', dest='plot_n_points', type="int", default=None, help='minimum number of points, used for nodallines ') group_app.add_option('-s', '--size', action="store", dest='plot_size', metavar='<size in cm>', type="float", default=None, help='size of plot in cm') group_colours.add_option('-w', '--pressure_colour', action="store", dest='plot_pressure_colour', metavar='<colour>', default=None, help='colour of the tension area ') group_colours.add_option('-r', '--tension_colour', action="store", dest='plot_tension_colour', metavar='<colour>', default=None, help='colour of the pressure area ') group_app.add_option('-a', '--alpha', action="store", dest='plot_total_alpha', metavar='<alpha>', type='float', default=None, help="alpha value for the plot - float from 1=opaque to " + \ "0=transparent ") group_dc.add_option('-D', '--dc', action="store_true", dest='plot_show_faultplanes', default=False, help='key, if double couple faultplanes shall be plotted ') group_dc.add_option('-d', '--show1fp', action="store", metavar='<index> <linewidth> <colour> <alpha>', dest='plot_show_1faultplane', default=None, nargs=4, help="plot 1 faultplane - arguments are: index [1,2] of the " + \ "resp. FP, linewidth(float), line colour(string or " + \ "rgb-tuple), and alpha value (float between 0 and 1) ") group_misc.add_option('-e', '--eigenvectors', action="store", dest='plot_show_princ_axes', metavar='<size> <linewidth> <alpha>', default=None, nargs=3, help="show eigenvectors - if used, provide 3 arguments: " + \ "symbol size, symbol linewidth, and symbol alpha value ") group_misc.add_option('-b', '--basis_vectors', action="store_true", dest='plot_show_basis_axes', default=False, help='show NED basis in plot') group_app.add_option('-l', '--lines', action="store", dest='plot_outerline', metavar='<linewidth> <colour> <alpha>', nargs=3, default=None, help="gives the style of the outer line - 3 arguments " + \ "needed: linewidth(float),line colour(string or " + \ "rgb-tuple), and alpha value (float between 0 and 1) ") group_app.add_option('-n', '--nodals', action="store", dest='plot_nodalline', metavar='<linewidth> <colour> <alpha>', default=None, nargs=3, help="gives the style of the nodal lines - 3 arguments " + \ "needed: linewidth(float),line colour(string or " + \ "rgb-tuple), and alpha value (float between 0 and 1) ") group_quality.add_option('-q', '--quality', action="store", dest='plot_dpi', metavar='<dpi>', type="int", default=None, help="changes the quality for the plot in terms of dpi " + \ "(minimum=200) ") group_type.add_option('-L', '--lines_only', action="store_true", dest='plot_only_lines', default=False, help='key, if only lines are shown (no fill - so ' + \ 'overwrites "fill"-related options) ') group_misc.add_option('-i', '--input_system', action="store", dest='plot_input_system', metavar='<basis>', default=False, help='if source mechanism is given as tensor in another ' + \ 'system than NED (USE, XYZ,NWU) ') group_type.add_option('-I', '--show_isotropic_part', dest='plot_isotropic_part', action='store_true', default=False, help='key, if isotropic part shall be considered for ' + \ 'plotting [%default]')
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 # # ## save # # desc_save="""Saves a beachball diagram of the provided mechanism # without plotting. # # # Several styles and configurations are available. Same options # as for 'plot' are available, the saving-option '-S' is set by # default. # # """ # # # parser_save = parser_plot # # parser_save.description = desc_save # # parser_save.usage = "\n\n elk_mopad save M [options]" # # _do_parsers['save'] = parser_save
# decompose desc_decomp = """Returns a decomposition of the input moment tensor.\n
Different decompositions are available (following Jost & Herrmann). Either the complete decomposition or only parts are returned; in- and output basis systema can be chosen. The 'fancy' option is available for better human reading. """
parser_decompose = OptionParser( usage="\n\n elk_mopad decompose M [options] ", description=desc_decomp)
group_type = OptionGroup(parser_decompose, 'Type of decomposition') group_part = OptionGroup(parser_decompose, 'Partial decomposition') group_system = OptionGroup(parser_decompose, 'Basis systems')
helpstring11 = """ Returns a list 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)
If option 'fancy' is set, only a small overview about geometry and strength is provided instead. """ group_part.add_option('-c', '--complete', action="store_true", dest='decomp_out_complete', default=False, help=helpstring11) parser_decompose.add_option('-y', '--fancy', action="store_true", dest='decomp_out_fancy', default=False, help='key for a stylish output') group_part.add_option('-p', '--partial', action="store", dest='decomp_out_part', default=False, metavar='<part1,part2,... >', help='provide an argument, what part(s) shall be displayed ' + \ '(if multiple, separate by commas): in, out, type, full, ' + \ 'iso, iso_perc, devi, devi_perc, dc, dc_perc, dc2, ' + \ 'dc2_perc, dc3, dc3_perc, clvd, clvd_perc, mom, mag, ' + \ 'eigvals, eigvecs, t, n, p, faultplanes') group_system.add_option('-i', '--input_system', action="store", dest='decomp_in_system', metavar='<basis>', default=False, help='set to provide input in another system than NED ' + \ '(XYZ,USE,NWU) ') group_system.add_option('-o', '--output_system', action="store", dest='decomp_out_system', metavar='<basis>', default=False, help='set to return output in anaother system than NED ' + \ '(XYZ,USE,NWU) ') group_type.add_option('-t', '--type', action="store", dest='decomp_key', metavar='<decomposition key>', default=False, type='int', help='integer key to choose the type of decomposition - 20: ' + \ 'ISO+DC+CLVD ; 21: ISO+major DC+ minor DC ; 31: ISO + 3 DCs ')
parser_decompose.add_option_group(group_type) parser_decompose.add_option_group(group_part) parser_decompose.add_option_group(group_system)
_do_parsers['decompose'] = parser_decompose
return _do_parsers
try: call = _which_call(sys.argv[1].lower()[0]) except: call = 'help'
if len(sys.argv) < 3: call = 'help'
if call == 'help': print USAGE_STRING sys.exit()
try: M_raw = [float(xx) for xx in sys.argv[2].split(',')] except: # sys.exit('\n ERROR - Provide valid source mechanism !!!\n') # if sys.argv[2].startswith('-'): 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(N.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 != None: print aa
if __name__ == '__main__': main() |