def strike_angle(z_array=None, z_object=None, pt_array=None, pt_object=None, skew_threshold=5, eccentricity_threshold=0.1): """ Estimate strike angle from 2D parts of the impedance tensor given the skew and eccentricity thresholds Arguments ------------ **z_array** : np.ndarray(nf, 2, 2) numpy array of impedance elements *default* is None **z_object** : mtpy.core.z.Z z_object *default* is None **pt_array** : np.ndarray(nf, 2, 2) numpy array of phase tensor elements *default* is None **pt_object** : mtpy.analysis.pt.PT phase tensor object *default* is None **skew_threshold** : float threshold on the skew angle in degrees, anything above this value is 3-D or azimuthally anisotropic *default* is 5 degrees **eccentricity_threshold** : float threshold on eccentricty in dimensionaless units, anything below this value is 1-D *default* is 0.1 Returns ---------- **strike** : np.ndarray(nf) an array of strike angles in degrees for each frequency assuming 0 is north, and e is 90. There is a 90 degree ambiguity in the angle. Examples ---------- :Estimate Dimesions: :: >>> import mtpy.analysis.geometry as geometry >>> strike = geometry.strike_angle(z_object=z_obj, >>> skew_threshold=3) """ if z_array is not None: pt_obj = MTpt.PhaseTensor(z_array=z_array) elif z_object is not None: if not isinstance(z_object, MTz.Z): raise MTex.MTpyError_Z( 'Input argument is not an instance of the Z class') pt_obj = MTpt.PhaseTensor(z_object=z_object) elif pt_array is not None: pt_obj = MTpt.PhaseTensor(pt_array=pt_array) elif pt_object is not None: if not isinstance(pt_object, MTpt.PhaseTensor): raise MTex.MTpyError_PT( 'Input argument is not an instance of the PhaseTensor class') pt_obj = pt_object lo_dims = dimensionality(pt_object=pt_obj, skew_threshold=skew_threshold, eccentricity_threshold=eccentricity_threshold) lo_strikes = [] for idx, dim in enumerate(lo_dims): if dim == 1: lo_strikes.append((np.nan, np.nan)) continue a = pt_obj.alpha[idx] b = pt_obj.beta[idx] strike1 = (a - b) % 90 if 0 < strike1 < 45: strike2 = strike1 + 90 else: strike2 = strike1 - 90 s1 = min(strike1, strike2) s2 = max(strike1, strike2) lo_strikes.append((s1, s2)) return np.array(lo_strikes)
def eccentricity(z_array=None, z_object=None, pt_array=None, pt_object=None): """ Estimate eccentricy of a given impedance or phase tensor object Arguments ------------ **z_array** : np.ndarray(nf, 2, 2) numpy array of impedance elements *default* is None **z_object** : mtpy.core.z.Z z_object *default* is None **pt_array** : np.ndarray(nf, 2, 2) numpy array of phase tensor elements *default* is None **pt_object** : mtpy.analysis.pt.PT phase tensor object *default* is None Returns ---------- **eccentricity** : np.ndarray(nf) **eccentricity_err** : np.ndarray(nf) Examples ---------- :Estimate Dimesions: :: >>> import mtpy.analysis.geometry as geometry >>> ec, ec_err= geometry.eccentricity(z_object=z_obj) """ if z_array is not None: pt_obj = MTpt.PhaseTensor(z_array=z_array) elif z_object is not None: if not isinstance(z_object, MTz.Z): raise MTex.MTpyError_Z( 'Input argument is not an instance of the Z class') pt_obj = MTpt.PhaseTensor(z_object=z_object) elif pt_array is not None: pt_obj = MTpt.PhaseTensor(pt_array=pt_array) elif pt_object is not None: if not isinstance(pt_object, MTpt.PhaseTensor): raise MTex.MTpyError_PT( 'Input argument is not an instance of the PhaseTensor class') pt_obj = pt_object lo_ecc = [] lo_eccerr = [] if not isinstance(pt_obj, MTpt.PhaseTensor): raise MTex.MTpyError_PT( 'Input argument is not an instance of the PhaseTensor class') for idx_f in range(len(pt_obj.pt)): lo_ecc.append(pt_obj._pi1()[0][idx_f] / pt_obj._pi2()[0][idx_f]) ecc_err = None if (pt_obj._pi1()[1] is not None) and (pt_obj._pi2()[1] is not None): ecc_err = np.sqrt( (pt_obj._pi1()[1][idx_f] / pt_obj._pi1()[0][idx_f])**2 + (pt_obj._pi2()[1][idx_f] / pt_obj._pi2()[0][idx_f])**2) lo_eccerr.append(ecc_err) return np.array(lo_ecc), np.array(lo_eccerr)
def dimensionality(z_array=None, z_object=None, pt_array=None, pt_object=None, skew_threshold=5, eccentricity_threshold=0.1): """ Esitmate dimensionality of an impedance tensor, frequency by frequency. Dimensionality is estimated from the phase tensor given the threshold criteria on the skew angle and eccentricity following Bibby et al., 2005 and Booker, 2014. Arguments ------------ **z_array** : np.ndarray(nf, 2, 2) numpy array of impedance elements *default* is None **z_object** : mtpy.core.z.Z z_object *default* is None **pt_array** : np.ndarray(nf, 2, 2) numpy array of phase tensor elements *default* is None **pt_object** : mtpy.analysis.pt.PT phase tensor object *default* is None **skew_threshold** : float threshold on the skew angle in degrees, anything above this value is 3-D or azimuthally anisotropic *default* is 5 degrees **eccentricity_threshold** : float threshold on eccentricty in dimensionaless units, anything below this value is 1-D *default* is 0.1 Returns ---------- **dimensions** : np.ndarray(nf, dtype=int) an array of dimesions for each frequency the values are [ 1 | 2 | 3 ] Examples ---------- :Estimate Dimesions: :: >>> import mtpy.analysis.geometry as geometry >>> dim = geometry.dimensionality(z_object=z_obj, >>> skew_threshold=3) """ lo_dimensionality = [] if z_array is not None: pt_obj = MTpt.PhaseTensor(z_array=z_array) elif z_object is not None: if not isinstance(z_object, MTz.Z): raise MTex.MTpyError_Z( 'Input argument is not an instance of the Z class') pt_obj = MTpt.PhaseTensor(z_object=z_object) elif pt_array is not None: pt_obj = MTpt.PhaseTensor(pt_array=pt_array) elif pt_object is not None: if not isinstance(pt_object, MTpt.PhaseTensor): raise MTex.MTpyError_PT( 'Input argument is not an instance of the PhaseTensor class') pt_obj = pt_object # use criteria from Bibby et al. 2005 for determining the dimensionality # for each frequency of the pt/z array: for idx_f in range(len(pt_obj.pt)): #1. determine skew value... skew = pt_obj.beta[idx_f] #compare with threshold for 3D if skew > skew_threshold: lo_dimensionality.append(3) else: #2.check for eccentricity: ecc = pt_obj._pi1()[0][idx_f] / pt_obj._pi2()[0][idx_f] if ecc > eccentricity_threshold: lo_dimensionality.append(2) else: lo_dimensionality.append(1) return np.array(lo_dimensionality)
def z2pt(z_array, z_err_array = None): """ Calculate Phase Tensor from Z array (incl. uncertainties) Input: - Z : 2x2 complex valued Numpy array Optional: - Z-error : 2x2 real valued Numpy array Return: - PT : 2x2 real valued Numpy array - PT-error : 2x2 real valued Numpy array """ if z_array is not None: try: if not len(z_array.shape) in [2,3]: raise if not z_array.shape[-2:] == (2,2): raise if not z_array.dtype in ['complex', 'float']: raise except: raise MTex.MTpyError_PT('Error - incorrect z array: %s;%s instead of (N,2,2);complex'%(str(z_array.shape), str(z_array.dtype))) if z_err_array is not None: try: if not len(z_err_array.shape) in [2,3]: raise if not z_err_array.shape[-2:] == (2,2): raise if not z_err_array.dtype in ['float']: raise except: raise MTex.MTpyError_PT('Error - incorrect z-err-array: %s;%s instead of (N,2,2);real'%(str(z_err_array.shape), str(z_err_array.dtype))) if not z_array.shape == z_err_array.shape: raise MTex.MTpyError_PT('Error - z-array and z-err-array have different shape: %s;%s'%(str(z_array.shape), str(z_err_array.shape))) #for a single matrix as input: if len(z_array.shape) == 2: pt_array = np.zeros((2,2)) realz = np.real(z_array) imagz = np.imag(z_array) detreal = np.linalg.det(realz) if detreal == 0 : if np.linalg.norm(realz) == 0 and np.linalg.norm(imagz) == 0: pt_err_array = np.zeros_like(pt_array) if z_err_array is None: pt_err_array = None return pt_array, pt_err_array else: raise MTex.MTpyError_PT('Error - z-array contains a singular matrix, thus it cannot be converted into a PT!' ) pt_array[0,0] = realz[1,1] * imagz[0,0] - realz[0,1] * imagz[1,0] pt_array[0,1] = realz[1,1] * imagz[0,1] - realz[0,1] * imagz[1,1] pt_array[1,0] = realz[0,0] * imagz[1,0] - realz[1,0] * imagz[0,0] pt_array[1,1] = realz[0,0] * imagz[1,1] - realz[1,0] * imagz[0,1] pt_array /= detreal if z_err_array is None: return pt_array, None pt_err_array = np.zeros_like(pt_array) #Z entries are independent -> use Gaussian error propagation (squared sums/2-norm) pt_err_array[0,0] = 1/np.abs(detreal) * np.sqrt(np.sum([np.abs(-pt_array[0,0] * realz[1,1] * z_err_array[0,0])**2, np.abs( pt_array[0,0] * realz[0,1] * z_err_array[1,0])**2, np.abs(((imagz[0,0] * realz[1,0] - realz[0,0] * imagz[1,0]) / np.abs(detreal) * realz[0,0] ) * z_err_array[0,1])**2, np.abs(((imagz[1,0] * realz[0,0] - realz[1,0] * imagz[1,1]) / np.abs(detreal) * realz[0,1] ) * z_err_array[1,1])**2, np.abs(realz[1,1] * z_err_array[0,0])**2, np.abs(realz[0,1] * z_err_array[1,0])**2 ])) pt_err_array[0,1] = 1/np.abs(detreal) * np.sqrt( np.sum([np.abs( -pt_array[0,1] * realz[1,1] * z_err_array[0,0])**2, np.abs( pt_array[0,1] * realz[0,1] * z_err_array[1,0])**2, np.abs( ( (imagz[0,1] * realz[1,0] - realz[0,0] * imagz[1,1]) / np.abs(detreal) * realz[1,1] ) * z_err_array[0,1])**2, np.abs( ( (imagz[1,1] * realz[0,0] - realz[0,1] * imagz[1,0]) / np.abs(detreal) * realz[0,1] ) * z_err_array[1,1])**2, np.abs( realz[1,1] * z_err_array[0,1])**2, np.abs( realz[0,1] * z_err_array[1,1])**2 ])) pt_err_array[1,0] = 1/np.abs(detreal) * np.sqrt( np.sum([np.abs( pt_array[1,0] * realz[1,0] * z_err_array[0,1])**2, np.abs( -pt_array[1,0] * realz[0,0] * z_err_array[1,1])**2, np.abs( ( (imagz[0,0] * realz[1,1] - realz[0,1] * imagz[1,1]) / np.abs(detreal) * realz[1,0] ) * z_err_array[0,0])**2, np.abs( ( (imagz[1,0] * realz[0,1] - realz[1,1] * imagz[0,0]) / np.abs(detreal) * realz[0,0] ) * z_err_array[0,1])**2, np.abs( realz[1,0] * z_err_array[0,0])**2, np.abs( realz[0,0] * z_err_array[1,0])**2 ])) pt_err_array[1,1] = 1/np.abs(detreal) * np.sqrt( np.sum([np.abs( pt_array[1,1] * realz[1,0] * z_err_array[0,1])**2, np.abs( -pt_array[1,1] * realz[0,0] * z_err_array[1,1])**2, np.abs( ( (imagz[0,1] * realz[1,1] - realz[0,1] * imagz[1,1]) / np.abs(detreal) * realz[1,0] ) * z_err_array[0,0])**2, np.abs( ( (imagz[1,1] * realz[0,1] - realz[1,1] * imagz[0,1]) / np.abs(detreal) * realz[0,0] ) * z_err_array[0,1])**2, np.abs( - realz[1,0] * z_err_array[0,1])**2, np.abs( realz[0,0] * z_err_array[1,1])**2 ])) return pt_array, pt_err_array #else: pt_array = np.zeros((z_array.shape[0],2,2)) for idx_f in range(len(z_array)): realz = np.real(z_array[idx_f]) imagz = np.imag(z_array[idx_f]) detreal = np.linalg.det(realz) if detreal == 0 : raise MTex.MTpyError_Z('Warning - z-array no. {0} contains a singular matrix,'\ ' thus it cannot be converted into a PT!'.format(idx_f)) pt_array[idx_f,0,0] = realz[1,1] * imagz[0,0] - realz[0,1] * imagz[1,0] pt_array[idx_f,0,1] = realz[1,1] * imagz[0,1] - realz[0,1] * imagz[1,1] pt_array[idx_f,1,0] = realz[0,0] * imagz[1,0] - realz[1,0] * imagz[0,0] pt_array[idx_f,1,1] = realz[0,0] * imagz[1,1] - realz[1,0] * imagz[0,1] pt_array /= detreal if z_err_array is None: return pt_array, pt_err_array pt_err_array = np.zeros_like(pt_array) pt_err_array[idx_f,0,0] = 1/detreal * (np.abs( -pt_array[idx_f,0,0] * realz[1,1] * z_err_array[0,0]) + \ np.abs( pt_array[idx_f,0,0] * realz[0,1] * z_err_array[1,0]) + \ np.abs( (imagz[0,0] - pt_array[idx_f,0,0] * realz[0,0] ) * z_err_array[1,1]) +\ np.abs( (-imagz[1,0]+ pt_array[idx_f,0,0] * realz[1,0] ) * z_err_array[0,1]) + \ np.abs( realz[1,1] * z_err_array[0,0]) + np.abs( realz[0,1] * z_err_array[1,0]) ) pt_err_array[idx_f,0,1] = 1/detreal * (np.abs( -pt_array[idx_f,0,1] * realz[1,1] * z_err_array[0,0]) + \ np.abs( pt_array[idx_f,0,1] * realz[0,1] * z_err_array[1,0]) + \ np.abs( (imagz[0,1] - pt_array[idx_f,0,1] * realz[0,0] ) * z_err_array[1,1]) +\ np.abs( (-imagz[1,1]+ pt_array[idx_f,0,1] * realz[1,0] ) * z_err_array[0,1]) + \ np.abs( realz[1,1] * z_err_array[0,1]) + np.abs( realz[0,1] * z_err_array[1,1]) ) pt_err_array[idx_f,1,0] = 1/detreal * (np.abs( (imagz[1,0] - pt_array[idx_f,1,0] * realz[1,1] ) * z_err_array[0,0]) +\ np.abs( pt_array[idx_f,1,0] * realz[1,0] * z_err_array[0,1]) + \ np.abs( (-imagz[0,0] + pt_array[idx_f,1,0] * realz[0,1] ) * z_err_array[1,0]) + \ np.abs( -pt_array[idx_f,1,0] * realz[0,0] * z_err_array[1,1]) + \ np.abs( realz[0,0] * z_err_array[1,0]) + np.abs( -realz[1,0] * z_err_array[0,0]) ) pt_err_array[idx_f,1,1] = 1/detreal * (np.abs( (imagz[1,1] - pt_array[idx_f,1,1] * realz[1,1] ) * z_err_array[0,0]) +\ np.abs( pt_array[idx_f,1,1] * realz[1,0] * z_err_array[0,1]) + \ np.abs( (-imagz[0,1] + pt_array[idx_f,1,1] * realz[0,1] ) * z_err_array[1,0]) + \ np.abs( -pt_array[idx_f,1,1] * realz[0,0] * z_err_array[1,1]) + \ np.abs( realz[0,0] * z_err_array[1,1]) + np.abs( -realz[1,0] * z_err_array[0,1]) ) return pt_array, pt_err_array