def calculate_znb(z_object = None, z_array = None, periods = None): """ Determine an array of Z_nb (depth dependent Niblett-Bostick transformed Z) from the 1D and 2D parts of an impedance tensor array Z. input: - Z output: - Z_nb The calculation of the Z_nb needs 6 steps: 1) Determine the dimensionality of the Z(T), discard all 3D parts 2) Rotate all Z(T) to TE/TM setup (T_parallel/T_ortho) 3) Transform every component individually by Niblett-Bostick 4) collect the respective 2 components each for equal/similar depths 5) interprete them as TE_nb/TM_nb 6) set up Z_nb(depth) If 1D layers occur inbetween 2D layers, the strike angle is undefined therein. We take an - arbitrarily chosen - linear interpolation of strike angle for these layers, with the values varying between the angles of the bounding upper and lower 2D layers (linearly w.r.t. the periods). Use the output for instance for the determination of NB-transformed phase tensors. Note: No propagation of errors implemented yet! """ #deal with inputs #if zobject: z = z_object.z periods = 1./z_object.freq #else: z = z periods = periods dimensions = MTge.dimensionality() angles = MTge.strike_angle(z) #reduce actual Z by the 3D layers: z2 = z[np.where(dimensions != 3)[0]] angles2 = angles[np.where(dimensions != 3)[0]] periods2 = periods[np.where(dimensions != 3)[0]] return Z_nb
def test_fun(): """ test function :return: T/F """ # mtObj = MT(r'C:\Git\mtpy\examples\data\edi_files\pb42c.edi') # mtObj = MT(r'E:/Githubz/mtpy/examples/data/edi_files/pb42c.edi') edifile = os.path.join(MTPY_ROOT, 'examples/data/edi_files/pb42c.edi') mtObj = MT(edifile) strike_angle_pb42c = np.array([[np.nan, np.nan], [np.nan, np.nan], [np.nan, np.nan], [np.nan, np.nan], [np.nan, np.nan], [np.nan, np.nan], [np.nan, np.nan], [np.nan, np.nan], [np.nan, np.nan], [np.nan, np.nan], [np.nan, np.nan], [np.nan, np.nan], [np.nan, np.nan], [np.nan, np.nan], [np.nan, np.nan], [np.nan, np.nan], [np.nan, np.nan], [np.nan, np.nan], [38.45662316, 128.45662316], [28.61883115, 118.61883115], [14.45341494, 104.45341494], [8.43320651, 98.43320651], [4.94952784, 94.94952784], [2.09090369, 92.09090369], [1.39146887, 91.39146887], [0.39905337, 90.39905337], [-5.49553673, 84.50446327], [-6.28846049, 83.71153951], [-7.31641788, 82.68358212], [-10.45341947, 79.54658053], [-7.07075086, 82.92924914], [-7.5429295, 82.4570705], [-6.06405688, 83.93594312], [-3.54915951, 86.45084049], [-3.12596637, 86.87403363], [-0.47404093, 89.52595907], [2.74343665, 92.74343665], [4.78078759, 94.78078759], [7.71125988, 97.71125988], [11.0123521, 101.0123521], [13.81639678, 103.81639678], [13.60497071, 103.60497071], [15.87672806, 105.87672806]]) strike_angle = mtg.strike_angle(z_object=mtObj.Z) differ = np.abs(strike_angle[np.isfinite(strike_angle)] - strike_angle_pb42c[np.isfinite(strike_angle_pb42c)]) < 1e-8 print(differ) assert np.all(differ)
def test_get_strike_from_edi_file(self): edifile = os.path.normpath( os.path.join(TEST_MTPY_ROOT, 'examples/data/edi_files/pb42c.edi')) mt_obj = MT(edifile) strike_angle_pb42c = np.array([[np.nan, np.nan], [np.nan, np.nan], [np.nan, np.nan], [np.nan, np.nan], [np.nan, np.nan], [np.nan, np.nan], [np.nan, np.nan], [np.nan, np.nan], [np.nan, np.nan], [np.nan, np.nan], [np.nan, np.nan], [np.nan, np.nan], [np.nan, np.nan], [np.nan, np.nan], [np.nan, np.nan], [np.nan, np.nan], [np.nan, np.nan], [np.nan, np.nan], [38.45662316, 128.45662316], [28.61883115, 118.61883115], [14.45341494, 104.45341494], [8.43320651, 98.43320651], [4.94952784, 94.94952784], [2.09090369, 92.09090369], [1.39146887, 91.39146887], [0.39905337, 90.39905337], [-5.49553673, 84.50446327], [-6.28846049, 83.71153951], [-7.31641788, 82.68358212], [-10.45341947, 79.54658053], [-7.07075086, 82.92924914], [-7.5429295, 82.4570705], [-6.06405688, 83.93594312], [-3.54915951, 86.45084049], [-3.12596637, 86.87403363], [-0.47404093, 89.52595907], [2.74343665, 92.74343665], [4.78078759, 94.78078759], [7.71125988, 97.71125988], [11.0123521, 101.0123521], [13.81639678, 103.81639678], [13.60497071, 103.60497071], [15.87672806, 105.87672806]]) strike_angle = mtg.strike_angle(z_object=mt_obj.Z) self.assertTrue( np.allclose(strike_angle[np.isfinite(strike_angle)], strike_angle_pb42c[np.isfinite(strike_angle_pb42c)], 1e-8))
def test_get_strike_from_edi_file(self): edifile = os.path.normpath( os.path.join(TEST_MTPY_ROOT, 'examples/data/edi_files/pb42c.edi')) mt_obj = MT(edifile) strike_angle_pb42c = np.array([[np.nan, np.nan], [np.nan, np.nan], [np.nan, np.nan], [np.nan, np.nan], [np.nan, np.nan], [np.nan, np.nan], [np.nan, np.nan], [np.nan, np.nan], [np.nan, np.nan], [np.nan, np.nan], [np.nan, np.nan], [np.nan, np.nan], [np.nan, np.nan], [np.nan, np.nan], [np.nan, np.nan], [np.nan, np.nan], [np.nan, np.nan], [np.nan, np.nan], [38.45662316, -51.54337684], [28.61883115, -61.38116885], [14.45341494, -75.54658506], [8.43320651, -81.56679349], [4.94952784, -85.05047216], [2.09090369, -87.90909631], [1.39146887, -88.60853113], [0.39905337, -89.60094663], [-5.49553673, 84.50446327], [np.nan, np.nan], [np.nan, np.nan], [np.nan, np.nan], [np.nan, np.nan], [np.nan, np.nan], [-6.06405688, 83.93594312], [-3.54915951, 86.45084049], [-3.12596637, 86.87403363], [-0.47404093, 89.52595907], [2.74343665, -87.25656335], [4.78078759, -85.21921241], [7.71125988, -82.28874012], [11.0123521, -78.9876479], [13.81639678, -76.18360322], [13.60497071, -76.39502929], [np.nan, np.nan]]) strike_angle = mtg.strike_angle(z_object=mt_obj.Z) self.assertTrue( np.allclose(strike_angle[np.isfinite(strike_angle)], strike_angle_pb42c[np.isfinite(strike_angle_pb42c)], 1e-8))
def find_distortion(z_object, lo_dims = None): """ find optimal distortion tensor from z object automatically determine the dimensionality over all frequencies, then find the appropriate distortion tensor D """ z_obj = z_object if lo_dims is None : lo_dims = MTge.dimensionality(z_object = z_obj) try: if len(lo_dims) != len(z_obj.z): lo_dims = MTge.dimensionality(z_object = z_obj) except: pass #dictionary of values that should be no distortion in case distortion #cannot be calculated for that component dis_dict = {(0,0):1, (0,1):0, (1,0):0, (1,1):1} lo_dis = [] lo_diserr = [] if 1 in lo_dims: idx_1 = np.where(np.array(lo_dims) == 1)[0] for idx in idx_1: realz = np.real(z_obj.z[idx]) imagz = np.imag(z_obj.z[idx]) mat1 = np.matrix([[0, -1],[1, 0]]) gr = np.sqrt(np.linalg.det(realz)) gi = np.sqrt(np.linalg.det(imagz)) lo_dis.append(1./gr*np.dot(realz,mat1)) lo_dis.append(1./gi*np.dot(imagz,mat1)) if z_obj.zerr is not None: #find errors of entries for calculating weights lo_diserr.append(1./gr*\ np.array([[np.abs(z_obj.zerr[idx][0,1]), np.abs(z_obj.zerr[idx][0,0])], [np.abs(z_obj.zerr[idx][1,1]), np.abs(z_obj.zerr[idx][1,0])]])) lo_diserr.append(1./gi*\ np.array([[np.abs(z_obj.zerr[idx][0,1]), np.abs(z_obj.zerr[idx][0,0])], [np.abs(z_obj.zerr[idx][1,1]), np.abs(z_obj.zerr[idx][1,0])]])) else: #otherwise go for evenly weighted average lo_diserr.append(np.ones((2, 2))) lo_diserr.append(np.ones((2, 2))) dis = np.identity(2) diserr = np.identity(2) for i in range(2): for j in range(2): try: dis[i,j], dummy = np.average(np.array([k[i, j] for k in lo_dis]), weights=np.array([1./(k[i,j])**2 for k in lo_diserr]), returned=True) diserr[i,j] = np.sqrt(1./dummy) #if the distortion came out as nan set it to an appropriate #value if np.nan_to_num(dis[i,j]) == 0: dis[i, j] = dis_dict[i, j] diserr[i, j] = dis_dict[i, j] except ZeroDivisionError: print ('Could not get distortion for dis[{0}, {1}]'.format( i, j)+' setting value to {0}'.format(dis_dict[i,j])) dis[i, j] = dis_dict[i, j] diserr[i, j] = dis_dict[i, j]*1e-6 return dis, diserr if 2 in lo_dims: idx_2 = np.where(np.array(lo_dims) == 2)[0] #follow bibby et al. 2005 first alternative: P = 1 P = 1 lo_strikes = MTge.strike_angle(z_object = z_obj) lo_tetms = [] lo_t = [] lo_tetm_errs =[] for idx in idx_2: mat = z_obj.z[idx] ang = -lo_strikes[idx][0] if np.isnan(ang): ang = 0. errmat = None if z_obj.zerr is not None: errmat = z_obj.zerr[idx] tetm_mat, tetm_err = MTcc.rotatematrix_incl_errors(mat, ang, inmatrix_err=errmat) lo_tetms.append(tetm_mat) lo_tetm_errs.append(tetm_err) realz = np.real(tetm_mat) imagz = np.imag(tetm_mat) lo_t.append(-4*P*realz[0,1]*realz[1,0]/np.linalg.det(realz) ) lo_t.append(-4*P*imagz[0,1]*imagz[1,0]/np.linalg.det(imagz) ) #since there is no 'wrong' solution by a different value of T, no #error is given/calculated for T ! try: #just add 0.1% for avoiding numerical issues in the squareroots #later on T = np.sqrt(max(lo_t))+0.001 except: T = 2 for idx in range(len(lo_tetms)): realz = np.real(lo_tetms[idx]) imagz = np.imag(lo_tetms[idx]) errmat = lo_tetm_errs[idx] sr = np.sqrt(T**2+4*P*realz[0, 1]*realz[1, 0]/np.linalg.det(realz)) si = np.sqrt(T**2+4*P*imagz[0, 1]*imagz[1, 0]/np.linalg.det(imagz)) par_r = 2*realz[0, 1]/(T-sr) orth_r = 2*realz[1, 0]/(T+sr) par_i = 2*imagz[0, 1]/(T-si) orth_i = 2*imagz[1, 0]/(T+si) mat2_r = np.matrix([[0, 1./orth_r], [1./par_r, 0]]) mat2_i = np.matrix([[0, 1./orth_i], [1./par_i ,0]]) lo_dis.append(np.dot(realz,mat2_r)) lo_dis.append(np.dot(imagz,mat2_i)) if z_obj.zerr is not None: #find errors of entries for calculating weights sigma_sr = np.sqrt((-(2*P*realz[0,1]*realz[1,0]*\ realz[1,1]*errmat[0,0])/\ (np.linalg.det(realz)**2*sr))**2+\ ((2*P*realz[0,0]*realz[1,0]*\ realz[1,1]*errmat[0,1])/\ (np.linalg.det(realz)**2*sr))**2+\ ((2*P*realz[0,0]* realz[0,1]*\ realz[1,1]*errmat[1,0])/\ (np.linalg.det(realz)**2*sr))**2 +\ (-(2*P*realz[0,1]* realz[1,0]*\ realz[0,0]*errmat[1,1])/\ (np.linalg.det(realz)**2*sr))**2) sigma_dr_11 = 0.5*sigma_sr sigma_dr_22 = 0.5*sigma_sr sigma_dr_12 = np.sqrt((mat2_r[0,1]/realz[0,0]*errmat[0,0])**2+\ (mat2_r[0,1]/realz[1,0]*errmat[1,0])**2+\ (0.5*realz[0,0]/realz[1,0]*sigma_sr)**2) sigma_dr_21 = np.sqrt((mat2_r[1,0]/realz[1,1]*errmat[1,1])**2+\ (mat2_r[1,0]/realz[0,1]*errmat[0,1])**2+\ (0.5*realz[1,1]/realz[0,1]*sigma_sr)**2) lo_diserr.append(np.array([[sigma_dr_11, sigma_dr_12], [sigma_dr_21, sigma_dr_22]])) sigma_si = np.sqrt((-(2*P*imagz[0,1]*imagz[1,0]*\ imagz[1,1]*errmat[0,0])/\ (np.linalg.det(imagz)**2*sr))**2+\ ((2*P*imagz[0,0]*imagz[1,0]*\ imagz[1,1]*errmat[0,1])/\ (np.linalg.det(imagz)**2*sr))**2+\ ((2*P*imagz[0,0]*imagz[0,1]*\ imagz[1,1]*errmat[1,0])/\ (np.linalg.det(imagz)**2*sr))**2+\ (-(2*P*imagz[0,1]*imagz[1,0]*\ imagz[0,0]*errmat[1,1])/\ (np.linalg.det(imagz)**2*sr))**2) sigma_di_11 = 0.5*sigma_si sigma_di_22 = 0.5*sigma_si sigma_di_12 = np.sqrt((mat2_i[0,1]/imagz[0,0]*errmat[0,0])**2+\ (mat2_i[0,1]/imagz[1,0]*errmat[1,0])**2+\ (0.5*imagz[0,0]/imagz[1,0]*sigma_si)**2) sigma_di_21 = np.sqrt((mat2_i[1,0]/imagz[1,1]*errmat[1,1])**2+\ (mat2_i[1,0]/imagz[0,1]*errmat[0,1])**2+\ (0.5*imagz[1,1]/imagz[0,1]*sigma_si)**2) lo_diserr.append(np.array([[sigma_di_11, sigma_di_12], [sigma_di_21, sigma_di_22]])) else: #otherwise go for evenly weighted average lo_diserr.append(np.ones((2, 2))) lo_diserr.append(np.ones((2, 2))) dis = np.zeros((2, 2)) diserr = np.zeros((2, 2)) for i in range(2): for j in range(2): dis[i, j], dummy = np.average(np.array([k[i, j] for k in lo_dis]), weights=np.array([1./(k[i,j])**2 for k in lo_diserr]), returned=True ) diserr[i, j] = np.sqrt(1./dummy) return dis, diserr #if only 3D, use identity matrix - no distortion calculated dis = np.identity(2) diserr = diserr = np.zeros((2, 2)) return dis, diserr
def calculate_znb(z_object=None, z_array=None, periods=None): """ Determine an array of Z_nb (depth dependent Niblett-Bostick transformed Z) from the 1D and 2D parts of an impedance tensor array Z. input: - Z (object or array) - periods (mandatory, if Z is just array) output: - Z_nb The calculation of the Z_nb needs 6 steps: 1) Determine the dimensionality of the Z(T), discard all 3D parts 2) Rotate all Z(T) to TE/TM setup (T_parallel/T_ortho) 3) Transform every component individually by Niblett-Bostick 4) collect the respective 2 components each for equal/similar depths 5) interprete them as TE_nb/TM_nb 6) set up Z_nb(depth) If 1D layers occur inbetween 2D layers, the strike angle is undefined therein. We take an - arbitrarily chosen - linear interpolation of strike angle for these layers, with the values varying between the angles of the bounding upper and lower 2D layers (linearly w.r.t. the periods). Use the output for instance for the determination of NB-transformed phase tensors. Note: No propagation of errors implemented yet! """ #deal with inputs #if zobject: #z = z_object.z #periods = 1./z_object.freq #else: z = z_array periods = periods dimensions = MTge.dimensionality(z) angles = MTge.strike_angle(z) #reduce actual Z by the 3D layers: z2 = z[np.where(dimensions != 3)[0]] angles2 = angles[np.where(dimensions != 3)[0]] periods2 = periods[np.where(dimensions != 3)[0]] angles_incl1D = interpolate_strike_angles(angles2[:, 0], periods2) z3 = MTz.rotate_z(z2, -angles_incl1D)[0] #at this point we assume that the two modes are the off-diagonal elements!! #TE is element (1,2), TM at (2,1) lo_nb_max = [] lo_nb_min = [] app_res = MTz.z2resphi(z3, periods2)[0] phase = MTz.z2resphi(z3, periods2)[1] for i, per in enumerate(periods): te_rho, te_depth = rhophi2rhodepth(app_res[i][0, 1], phase[i][0, 1], per) tm_rho, tm_depth = rhophi2rhodepth(app_res[i][1, 0], phase[i][1, 0], per) if te_rho > tm_rho: lo_nb_max.append([te_depth, te_rho]) lo_nb_min.append([tm_depth, tm_rho]) else: lo_nb_min.append([te_depth, te_rho]) lo_nb_max.append([tm_depth, tm_rho]) return np.array(lo_nb_max), np.array(lo_nb_min)
def calculate_rho_minmax(z_object=None, z_array=None, periods=None): """ Determine 2 arrays of Niblett-Bostick transformed aparent resistivities: minumum and maximum values for respective periods. Values are calculated from the 1D and 2D parts of an impedance tensor array Z. input: - Z (object or array) - periods (mandatory, if Z is just array) output: - n x 3 array, depth/rho_nb/angle for rho_nb max - n x 3 array, depth/rho_nb/angle for rho_nb min The calculation is carried out by : 1) Determine the dimensionality of the Z(T), discard all 3D parts 2) loop over periods * rotate Z and calculate app_res_NB for off-diagonal elements * find maximum and minimum values * write out respective depths and rho values Note: No propagation of errors implemented yet! """ #deal with inputs #if zobject: #z = z_object.z #periods = 1./z_object.freq #else: z = z_array periods = periods dimensions = MTge.dimensionality(z) angles = MTge.strike_angle(z) #reduce actual Z by the 3D layers: z2 = z[np.where(dimensions != 3)[0]] angles2 = angles[np.where(dimensions != 3)[0]] periods2 = periods[np.where(dimensions != 3)[0]] lo_nb_max = [] lo_nb_min = [] rotsteps = 360 rotangles = np.arange(rotsteps) * 180. / rotsteps for i, per in enumerate(periods2): z_curr = z2[i] temp_vals = np.zeros((rotsteps, 4)) for j, d in enumerate(rotangles): new_z = MTcc.rotatematrix_incl_errors(z_curr, d)[0] #print i,per,j,d res = MTz.z2resphi(new_z, per)[0] phs = MTz.z2resphi(new_z, per)[1] te_rho, te_depth = rhophi2rhodepth(res[0, 1], phs[0, 1], per) tm_rho, tm_depth = rhophi2rhodepth(res[1, 0], phs[1, 0], per) temp_vals[j, 0] = te_depth temp_vals[j, 1] = te_rho temp_vals[j, 2] = tm_depth temp_vals[j, 3] = tm_rho column = (np.argmax([np.max(temp_vals[:, 1]), np.max(temp_vals[:, 3])])) * 2 + 1 maxidx = np.argmax(temp_vals[:, column]) max_rho = temp_vals[maxidx, column] max_depth = temp_vals[maxidx, column - 1] max_ang = rotangles[maxidx] #alternative 1 min_column = (np.argmin( [np.max(temp_vals[:, 1]), np.max(temp_vals[:, 3])])) * 2 + 1 if max_ang <= 90: min_ang = max_ang + 90 else: min_ang = max_ang - 90 minidx = np.argmin(np.abs(rotangles - min_ang)) min_rho = temp_vals[minidx, min_column] min_depth = temp_vals[minidx, min_column - 1] lo_nb_max.append([max_depth, max_rho, max_ang]) lo_nb_min.append([min_depth, min_rho]) return np.array(lo_nb_max), np.array(lo_nb_min)
def calculate_znb(z_object = None, z_array = None, periods = None): """ Determine an array of Z_nb (depth dependent Niblett-Bostick transformed Z) from the 1D and 2D parts of an impedance tensor array Z. input: - Z (object or array) - periods (mandatory, if Z is just array) output: - Z_nb The calculation of the Z_nb needs 6 steps: 1) Determine the dimensionality of the Z(T), discard all 3D parts 2) Rotate all Z(T) to TE/TM setup (T_parallel/T_ortho) 3) Transform every component individually by Niblett-Bostick 4) collect the respective 2 components each for equal/similar depths 5) interprete them as TE_nb/TM_nb 6) set up Z_nb(depth) If 1D layers occur inbetween 2D layers, the strike angle is undefined therein. We take an - arbitrarily chosen - linear interpolation of strike angle for these layers, with the values varying between the angles of the bounding upper and lower 2D layers (linearly w.r.t. the periods). Use the output for instance for the determination of NB-transformed phase tensors. Note: No propagation of errors implemented yet! """ #deal with inputs #if zobject: #z = z_object.z #periods = 1./z_object.freq #else: z = z_array periods = periods dimensions = MTge.dimensionality(z) angles = MTge.strike_angle(z) #reduce actual Z by the 3D layers: z2 = z[np.where(dimensions != 3)[0]] angles2 = angles[np.where(dimensions != 3)[0]] periods2 = periods[np.where(dimensions != 3)[0]] angles_incl1D = interpolate_strike_angles(angles2[:,0],periods2) z3 = MTz.rotate_z(z2,-angles_incl1D)[0] #at this point we assume that the two modes are the off-diagonal elements!! #TE is element (1,2), TM at (2,1) lo_nb_max = [] lo_nb_min = [] app_res = MTz.z2resphi(z3,periods2)[0] phase = MTz.z2resphi(z3,periods2)[1] for i,per in enumerate(periods): te_rho, te_depth = rhophi2rhodepth(app_res[i][0,1], phase[i][0,1], per) tm_rho, tm_depth = rhophi2rhodepth(app_res[i][1,0], phase[i][1,0], per) if te_rho > tm_rho: lo_nb_max.append([te_depth, te_rho]) lo_nb_min.append([tm_depth, tm_rho]) else: lo_nb_min.append([te_depth, te_rho]) lo_nb_max.append([tm_depth, tm_rho]) return np.array(lo_nb_max), np.array(lo_nb_min)
def calculate_rho_minmax(z_object = None, z_array = None, periods = None): """ Determine 2 arrays of Niblett-Bostick transformed aparent resistivities: minumum and maximum values for respective periods. Values are calculated from the 1D and 2D parts of an impedance tensor array Z. input: - Z (object or array) - periods (mandatory, if Z is just array) output: - n x 3 array, depth/rho_nb/angle for rho_nb max - n x 3 array, depth/rho_nb/angle for rho_nb min The calculation is carried out by : 1) Determine the dimensionality of the Z(T), discard all 3D parts 2) loop over periods * rotate Z and calculate app_res_NB for off-diagonal elements * find maximum and minimum values * write out respective depths and rho values Note: No propagation of errors implemented yet! """ #deal with inputs #if zobject: #z = z_object.z #periods = 1./z_object.freq #else: z = z_array periods = periods dimensions = MTge.dimensionality(z) angles = MTge.strike_angle(z) #reduce actual Z by the 3D layers: z2 = z[np.where(dimensions != 3)[0]] angles2 = angles[np.where(dimensions != 3)[0]] periods2 = periods[np.where(dimensions != 3)[0]] lo_nb_max = [] lo_nb_min = [] rotsteps = 360 rotangles = np.arange(rotsteps)*180./rotsteps for i,per in enumerate(periods2): z_curr = z2[i] temp_vals = np.zeros((rotsteps,4)) for jj,d in enumerate(rotangles): new_z = MTcc.rotatematrix_incl_errors(z_curr, d)[0] #print i,per,jj,d res = MTz.z2resphi(new_z,per)[0] phs = MTz.z2resphi(new_z,per)[1] te_rho, te_depth = rhophi2rhodepth(res[0,1], phs[0,1], per) tm_rho, tm_depth = rhophi2rhodepth(res[1,0], phs[1,0], per) temp_vals[jj,0] = te_depth temp_vals[jj,1] = te_rho temp_vals[jj,2] = tm_depth temp_vals[jj,3] = tm_rho column = (np.argmax([ np.max(temp_vals[:,1]), np.max(temp_vals[:,3])]))*2 + 1 maxidx = np.argmax(temp_vals[:,column]) max_rho = temp_vals[maxidx,column] max_depth = temp_vals[maxidx,column-1] max_ang = rotangles[maxidx] #alternative 1 min_column = (np.argmin([ np.max(temp_vals[:,1]), np.max(temp_vals[:,3])]))*2 + 1 if max_ang <= 90: min_ang = max_ang + 90 else: min_ang = max_ang - 90 minidx = np.argmin(np.abs(rotangles-min_ang)) min_rho = temp_vals[minidx,min_column] min_depth = temp_vals[minidx,min_column-1] lo_nb_max.append([max_depth, max_rho, max_ang]) lo_nb_min.append([min_depth, min_rho]) return np.array(lo_nb_max), np.array(lo_nb_min)
def calculate_depth_nb(z_object = None, z_array = None, periods = None): """ Determine an array of Z_nb (depth dependent Niblett-Bostick transformed Z) from the 1D and 2D parts of an impedance tensor array Z. The calculation of the Z_nb needs 6 steps: 1) Determine the dimensionality of the Z(T), discard all 3D parts 2) Rotate all Z(T) to TE/TM setup (T_parallel/T_ortho) 3) Transform every component individually by Niblett-Bostick 4) collect the respective 2 components each for equal/similar depths 5) interprete them as TE_nb/TM_nb 6) set up Z_nb(depth) If 1D layers occur inbetween 2D layers, the strike angle is undefined therein. We take an - arbitrarily chosen - linear interpolation of strike angle for these layers, with the values varying between the angles of the bounding upper and lower 2D layers (linearly w.r.t. the periods). Use the output for instance for the determination of NB-transformed phase tensors. Note: No propagation of errors implemented yet! Arguments ------------- *z_object* : mtpy.core.z object *z_array* : np.ndarray [num_periods, 2, 2] *periods* : np.ndarray(num_periods) only input if input z_array, otherwise periods are extracted from z_object.freq Returns ------------------ *depth_array* : np.ndarray(num_periods, dtype=['period', 'depth_min', 'depth_max', 'rho_min', 'rho_max']) numpy structured array with keywords. - period --> period in s - depth_min --> minimum depth estimated (m) - depth_max --> maximum depth estimated (m) - rho_min --> minimum resistivity estimated (Ohm-m) - rho_max --> maximum resistivity estimated (Ohm-m) Example ------------ >>> import mtpy.analysis.niblettbostick as nb >>> depth_array = nb.calculate_znb(z_object=z1) >>> # plot the results >>> import matplotlib.pyplot as plt >>> fig = plt.figure() >>> ax = fig.add_subplot(1,1,1) >>> ax.semilogy(depth_array['depth_min'], depth_array['period']) >>> ax.semilogy(depth_array['depth_max'], depth_array['period']) >>> plt.show() """ #deal with inputs if z_object is not None: z = z_object.z periods = 1./z_object.freq else: z = z_array periods = periods dimensions = MTge.dimensionality(z_array=z) angles = MTge.strike_angle(z_array=z) #reduce actual Z by the 3D layers: # z_2d = z[np.where(dimensions != 3)[0]] angles_2d = np.nan_to_num(angles[np.where(dimensions != 3)][:, 0]) periods_2d = periods[np.where(dimensions != 3)] # interperpolate strike angle onto all periods # make a function for strike using only 2d angles strike_interp = spi.interp1d(periods_2d, angles_2d, bounds_error=False, fill_value=0) strike_angles = strike_interp(periods) # rotate z to be along the interpolated strike angles z_rot = MTz.rotate_z(z, strike_angles)[0] # angles_incl1D = interpolate_strike_angles(angles_2d, periods_2d) # z3 = MTz.rotate_z(z_2d, -angles_incl1D)[0] #at this point we assume that the two modes are the off-diagonal elements!! #TE is element (1,2), TM at (2,1) # lo_nb_max = [] # lo_nb_min = [] depth_array = np.zeros(periods.shape[0], dtype=[('period', np.float), ('depth_min', np.float), ('depth_max', np.float), ('rho_min', np.float), ('rho_max', np.float)]) # app_res, app_res_err, phase, phase_err = MTz.z2resphi(z3, periods_2d) app_res, app_res_err, phase, phase_err = MTz.z2resphi(z_rot, periods) for ii, per in enumerate(periods): te_rho, te_depth = rhophi2rhodepth(app_res[ii, 0, 1], phase[ii, 0, 1], per) tm_rho, tm_depth = rhophi2rhodepth(app_res[ii, 1, 0], phase[ii, 1, 0], per) depth_array[ii]['period'] = per depth_array[ii]['depth_min'] = min([te_depth, tm_depth]) depth_array[ii]['depth_max'] = max([te_depth, tm_depth]) depth_array[ii]['rho_min'] = min([te_rho, tm_rho]) depth_array[ii]['rho_max'] = max([te_rho, tm_rho]) return depth_array
def find_distortion(z_object, g='det', num_freq=None, lo_dims=None): """ find optimal distortion tensor from z object automatically determine the dimensionality over all frequencies, then find the appropriate distortion tensor D Parameters ---------- **z_object** : mtpy.core.z object **g** : [ 'det' | '01' | '10 ] type of distortion correction *default* is 'det' **num_freq** : int number of frequencies to look for distortion from the index 0 *default* is None, meaning all frequencies are used **lo_dims** : list list of dimensions for each frequency *default* is None, meaning calculated from data Returns ------- **distortion** : np.ndarray(2, 2) distortion array all real values **distortion_err** : np.ndarray(2, 2) distortion error array Examples -------- :Estimate Distortion: :: >>> import mtpy.analysis.distortion as distortion >>> dis, dis_err = distortion.find_distortion(z_obj, num_freq=12) """ if num_freq is not None: if num_freq > z_object.freq.size: num_freq = z_object.freq.size print('Number of frequencies to sweep over is too high for z') print('setting num_freq to {0}'.format(num_freq)) else: num_freq = z_object.freq.size z_obj = MTz.Z(z_object.z[0:num_freq], z_object.z_err[0:num_freq], z_object.freq[0:num_freq]) g = 'det' dim_arr = MTge.dimensionality(z_object=z_obj) st_arr = -1 * MTge.strike_angle(z_object=z_obj)[:, 0] dis = np.zeros_like(z_obj.z, dtype=np.float) dis_err = np.ones_like(z_obj.z, dtype=np.float) #dictionary of values that should be no distortion in case distortion #cannot be calculated for that component rot_mat = np.matrix([[0, -1], [1, 0]]) for idx, dim in enumerate(dim_arr): if np.any(z_obj.z[idx] == 0.0 + 0.0j) == True: dis[idx] = np.identity(2) print('Found a zero in z at {0}, skipping'.format(idx)) continue if dim == 1: if g in ['01', '10']: gr = np.abs(z_obj.z.real[idx, int(g[0]), int(g[1])]) gi = np.abs(z_obj.z.imag[idx, int(g[0]), int(g[1])]) else: gr = np.sqrt(np.linalg.det(z_obj.z.real[idx])) gi = np.sqrt(np.linalg.det(z_obj.z.imag[idx])) dis[idx] = np.mean(np.array([ (1. / gr * np.dot(z_obj.z.real[idx], rot_mat)), (1. / gi * np.dot(z_obj.z.imag[idx], rot_mat)) ]), axis=0) if z_obj.z_err is not None: # find errors of entries for calculating weights gr_err = 1. / gr * np.abs(z_obj.z_err[idx]) gr_err[np.where(gr_err == 0.0)] = 1.0 gi_err = 1. / gi * np.abs(z_obj.z_err[idx]) gi_err[np.where(gi_err == 0.0)] = 1.0 dis_err[idx] = np.mean(np.array([gi_err, gr_err]), axis=0) elif dim == 2: P = 1 strike_ang = st_arr[idx] if np.isnan(strike_ang): strike_ang = 0.0 if z_obj.z_err is not None: err_arr = z_obj.z_err[idx] err_arr[np.where(err_arr == 0.0)] = 1.0 else: err_arr = None tetm_arr, tetm_err = MTcc.rotatematrix_incl_errors( z_obj.z[idx], strike_ang, inmatrix_err=err_arr) tetm_r = tetm_arr.real tetm_i = tetm_arr.imag t_arr_r = -4 * P * tetm_r[0, 1] * tetm_r[1, 0] / np.linalg.det(tetm_r) t_arr_i = -4 * P * tetm_i[0, 1] * tetm_i[1, 0] / np.linalg.det(tetm_i) try: T = np.sqrt(max([t_arr_r, t_arr_i])) + .001 except ValueError: T = 2 sr = np.sqrt(T**2 + 4 * P * tetm_r[0, 1] * tetm_r[1, 0] / np.linalg.det(tetm_r)) si = np.sqrt(T**2 + 4 * P * tetm_i[0, 1] * tetm_i[1, 0] / np.linalg.det(tetm_i)) par_r = 2 * tetm_r[0, 1] / (T - sr) orth_r = 2 * tetm_r[1, 0] / (T + sr) par_i = 2 * tetm_i[0, 1] / (T - si) orth_i = 2 * tetm_i[1, 0] / (T + si) mat2_r = np.matrix([[0, 1. / orth_r], [1. / par_r, 0]]) mat2_i = np.matrix([[0, 1. / orth_i], [1. / par_i, 0]]) avg_mat = np.mean(np.array( [np.dot(tetm_r, mat2_r), np.dot(tetm_i, mat2_i)]), axis=0) dis[idx] = avg_mat if err_arr is not None: # find errors of entries for calculating weights sigma_sr = np.sqrt((-(2 * P * tetm_r[0, 1] * tetm_r[1, 0] * \ tetm_r[1, 1] * err_arr[0, 0]) / \ (np.linalg.det(tetm_r) ** 2 * sr)) ** 2 + \ ((2 * P * tetm_r[0, 0] * tetm_r[1, 0] * tetm_r[1, 1] * err_arr[0, 1]) / (np.linalg.det(tetm_r) ** 2 * sr)) ** 2 + \ ((2 * P * tetm_r[0, 0] * tetm_r[0, 1] * tetm_r[1, 1] * err_arr[1, 0]) / \ (np.linalg.det(tetm_r) ** 2 * sr)) ** 2 + \ (-(2 * P * tetm_r[0, 1] * tetm_r[1, 0] * \ tetm_r[0, 0] * err_arr[1, 1]) / \ (np.linalg.det(tetm_r) ** 2 * sr)) ** 2) sigma_dr_11 = 0.5 * sigma_sr sigma_dr_22 = 0.5 * sigma_sr sigma_dr_12 = np.sqrt((mat2_r[0, 1] / tetm_r[0, 0] * err_arr[0, 0]) ** 2 + \ (mat2_r[0, 1] / tetm_r[1, 0] * err_arr[1, 0]) ** 2 + \ (0.5 * tetm_r[0, 0] / tetm_r[1, 0] * sigma_sr) ** 2) sigma_dr_21 = np.sqrt((mat2_r[1, 0] / tetm_r[1, 1] * err_arr[1, 1]) ** 2 + \ (mat2_r[1, 0] / tetm_r[0, 1] * err_arr[0, 1]) ** 2 + \ (0.5 * tetm_r[1, 1] / tetm_r[0, 1] * sigma_sr) ** 2) dis_err_r = np.array([[sigma_dr_11, sigma_dr_12], [sigma_dr_21, sigma_dr_22]]) sigma_si = np.sqrt((-(2 * P * tetm_i[0, 1] * tetm_i[1, 0] * \ tetm_i[1, 1] * err_arr[0, 0]) / \ (np.linalg.det(tetm_i) ** 2 * sr)) ** 2 + \ ((2 * P * tetm_i[0, 0] * tetm_i[1, 0] * \ tetm_i[1, 1] * err_arr[0, 1]) / \ (np.linalg.det(tetm_i) ** 2 * sr)) ** 2 + \ ((2 * P * tetm_i[0, 0] * tetm_i[0, 1] * \ tetm_i[1, 1] * err_arr[1, 0]) / \ (np.linalg.det(tetm_i) ** 2 * sr)) ** 2 + \ (-(2 * P * tetm_i[0, 1] * tetm_i[1, 0] * \ tetm_i[0, 0] * err_arr[1, 1]) / \ (np.linalg.det(tetm_i) ** 2 * sr)) ** 2) sigma_di_11 = 0.5 * sigma_si sigma_di_22 = 0.5 * sigma_si sigma_di_12 = np.sqrt((mat2_i[0, 1] / tetm_i[0, 0] * err_arr[0, 0]) ** 2 + \ (mat2_i[0, 1] / tetm_i[1, 0] * err_arr[1, 0]) ** 2 + \ (0.5 * tetm_i[0, 0] / tetm_i[1, 0] * sigma_si) ** 2) sigma_di_21 = np.sqrt((mat2_i[1, 0] / tetm_i[1, 1] * err_arr[1, 1]) ** 2 + \ (mat2_i[1, 0] / tetm_i[0, 1] * err_arr[0, 1]) ** 2 + \ (0.5 * tetm_i[1, 1] / tetm_i[0, 1] * sigma_si) ** 2) dis_err_i = np.array([[sigma_di_11, sigma_di_12], [sigma_di_21, sigma_di_22]]) dis_err[idx] = np.mean(np.array([dis_err_r, dis_err_i])) else: dis[idx] = np.identity(2) nonzero_idx = np.array(list(set(np.nonzero(dis)[0]))) dis_avg, weights_sum = np.average(dis[nonzero_idx], axis=0, weights=(1. / dis_err[nonzero_idx])**2, returned=True) dis_avg_err = np.sqrt(1. / weights_sum) return dis_avg, dis_avg_err
def find_distortion(z_object, g ='det', num_freq=None, lo_dims=None): """ find optimal distortion tensor from z object automatically determine the dimensionality over all frequencies, then find the appropriate distortion tensor D Arguments ------------- **z_object** : mtpy.core.z object **g** : [ 'det' | '01' | '10 ] type of distortion correction *default* is 'det' **num_freq** : int number of frequencies to look for distortion from the index 0 *default* is None, meaning all frequencies are used **lo_dims** : list list of dimensions for each frequency *default* is None, meaning calculated from data Returns --------- **distortion** : np.ndarray(2, 2) distortion array all real values **distortion_err** : np.ndarray(2, 2) distortion error array Example: --------- :Estimate Distortion: :: >>> import mtpy.analysis.distortion as distortion >>> dis, dis_err = distortion.find_distortion(z_obj, num_freq=12) """ z_obj = copy.deepcopy(z_object) if num_freq is not None: if num_freq > z_obj.freq.size: num_freq = z_obj.freq.size print 'Number of frequencies to sweep over is too high for z' print 'setting num_freq to {0}'.format(num_freq) else: num_freq = z_obj.freq.size z_obj.z = z_obj.z[0:num_freq] z_obj.z_err = z_obj.z_err[0:num_freq] z_obj.freq = z_obj.freq[0:num_freq] g = 'det' dim_arr = MTge.dimensionality(z_object=z_obj) st_arr = -1*MTge.strike_angle(z_object=z_obj)[:, 0] dis = np.zeros_like(z_obj.z, dtype=np.float) dis_err = np.ones_like(z_obj.z, dtype=np.float) #dictionary of values that should be no distortion in case distortion #cannot be calculated for that component rot_mat = np.matrix([[0, -1], [1, 0]]) for idx, dim in enumerate(dim_arr): if np.any(z_obj.z[idx] == 0.0+0.0j) == True: dis[idx] = np.identity(2) print 'Found a zero in z at {0}, skipping'.format(idx) continue if dim == 1: if g in ['01', '10']: gr = np.abs(z_obj.z.real[idx, int(g[0]), int(g[1])]) gi = np.abs(z_obj.z.imag[idx, int(g[0]), int(g[1])]) else: gr = np.sqrt(np.linalg.det(z_obj.z.real[idx])) gi = np.sqrt(np.linalg.det(z_obj.z.imag[idx])) dis[idx] = np.mean(np.array([(1./gr*np.dot(z_obj.z.real[idx], rot_mat)), (1./gi*np.dot(z_obj.z.imag[idx], rot_mat))]), axis=0) if z_obj.z_err is not None: #find errors of entries for calculating weights gr_err = 1./gr*np.abs(z_obj.z_err[idx]) gr_err[np.where(gr_err == 0.0)] = 1.0 gi_err = 1./gi*np.abs(z_obj.z_err[idx]) gi_err[np.where(gi_err == 0.0)] = 1.0 dis_err[idx] = np.mean(np.array([gi_err, gr_err]), axis=0) elif dim == 2: P = 1 strike_ang = st_arr[idx] if np.isnan(strike_ang): strike_ang = 0.0 if z_obj.z_err is not None: err_arr = z_obj.z_err[idx] err_arr[np.where(err_arr == 0.0)] = 1.0 else: err_arr = None tetm_arr, tetm_err = MTcc.rotatematrix_incl_errors(z_obj.z[idx], strike_ang, inmatrix_err=err_arr) tetm_r = tetm_arr.real tetm_i = tetm_arr.imag t_arr_r = -4*P*tetm_r[0, 1]*tetm_r[1, 0]/np.linalg.det(tetm_r) t_arr_i = -4*P*tetm_i[0, 1]*tetm_i[1, 0]/np.linalg.det(tetm_i) try: T = np.sqrt(max([t_arr_r, t_arr_i]))+.001 except ValueError: T = 2 sr = np.sqrt(T**2+4*P*tetm_r[0, 1]*tetm_r[1, 0]/np.linalg.det(tetm_r)) si = np.sqrt(T**2+4*P*tetm_i[0, 1]*tetm_i[1, 0]/np.linalg.det(tetm_i)) par_r = 2*tetm_r[0, 1]/(T-sr) orth_r = 2*tetm_r[1, 0]/(T+sr) par_i = 2*tetm_i[0, 1]/(T-si) orth_i = 2*tetm_i[1, 0]/(T+si) mat2_r = np.matrix([[0, 1./orth_r], [1./par_r, 0]]) mat2_i = np.matrix([[0, 1./orth_i], [1./par_i ,0]]) avg_mat = np.mean(np.array([np.dot(tetm_r, mat2_r), np.dot(tetm_i, mat2_i)]), axis=0) dis[idx] = avg_mat if err_arr is not None: #find errors of entries for calculating weights sigma_sr = np.sqrt((-(2*P*tetm_r[0,1]*tetm_r[1,0]*\ tetm_r[1,1]*err_arr[0,0])/\ (np.linalg.det(tetm_r)**2*sr))**2+\ ((2*P*tetm_r[0,0]*tetm_r[1,0]*\ tetm_r[1,1]*err_arr[0,1])/\ (np.linalg.det(tetm_r)**2*sr))**2+\ ((2*P*tetm_r[0,0]* tetm_r[0,1]*\ tetm_r[1,1]*err_arr[1,0])/\ (np.linalg.det(tetm_r)**2*sr))**2 +\ (-(2*P*tetm_r[0,1]* tetm_r[1,0]*\ tetm_r[0,0]*err_arr[1,1])/\ (np.linalg.det(tetm_r)**2*sr))**2) sigma_dr_11 = 0.5*sigma_sr sigma_dr_22 = 0.5*sigma_sr sigma_dr_12 = np.sqrt((mat2_r[0,1]/tetm_r[0,0]*err_arr[0,0])**2+\ (mat2_r[0,1]/tetm_r[1,0]*err_arr[1,0])**2+\ (0.5*tetm_r[0,0]/tetm_r[1,0]*sigma_sr)**2) sigma_dr_21 = np.sqrt((mat2_r[1,0]/tetm_r[1,1]*err_arr[1,1])**2+\ (mat2_r[1,0]/tetm_r[0,1]*err_arr[0,1])**2+\ (0.5*tetm_r[1,1]/tetm_r[0,1]*sigma_sr)**2) dis_err_r = np.array([[sigma_dr_11, sigma_dr_12], [sigma_dr_21, sigma_dr_22]]) sigma_si = np.sqrt((-(2*P*tetm_i[0,1]*tetm_i[1,0]*\ tetm_i[1,1]*err_arr[0,0])/\ (np.linalg.det(tetm_i)**2*sr))**2+\ ((2*P*tetm_i[0,0]*tetm_i[1,0]*\ tetm_i[1,1]*err_arr[0,1])/\ (np.linalg.det(tetm_i)**2*sr))**2+\ ((2*P*tetm_i[0,0]*tetm_i[0,1]*\ tetm_i[1,1]*err_arr[1,0])/\ (np.linalg.det(tetm_i)**2*sr))**2+\ (-(2*P*tetm_i[0,1]*tetm_i[1,0]*\ tetm_i[0,0]*err_arr[1,1])/\ (np.linalg.det(tetm_i)**2*sr))**2) sigma_di_11 = 0.5*sigma_si sigma_di_22 = 0.5*sigma_si sigma_di_12 = np.sqrt((mat2_i[0,1]/tetm_i[0,0]*err_arr[0,0])**2+\ (mat2_i[0,1]/tetm_i[1,0]*err_arr[1,0])**2+\ (0.5*tetm_i[0,0]/tetm_i[1,0]*sigma_si)**2) sigma_di_21 = np.sqrt((mat2_i[1,0]/tetm_i[1,1]*err_arr[1,1])**2+\ (mat2_i[1,0]/tetm_i[0,1]*err_arr[0,1])**2+\ (0.5*tetm_i[1,1]/tetm_i[0,1]*sigma_si)**2) dis_err_i = np.array([[sigma_di_11, sigma_di_12], [sigma_di_21, sigma_di_22]]) dis_err[idx] = np.mean(np.array([dis_err_r, dis_err_i])) else: dis[idx] = np.identity(2) nonzero_idx = np.array(list(set(np.nonzero(dis)[0]))) dis_avg, weights_sum = np.average(dis[nonzero_idx], axis=0, weights=(1./dis_err[nonzero_idx])**2, returned=True) dis_avg_err = np.sqrt(1./weights_sum) return dis_avg, dis_avg_err
get dimensionality/strike angle from an edi file """ import os.path as op import os os.chdir(r'C:\mtpywin\mtpy') # change to path where mtpy is installed import numpy as np from mtpy.core.mt import MT from mtpy.analysis.geometry import dimensionality, strike_angle # directory containing edis edi_path = r'C:\mtpywin\mtpy\examples\data\edi_files_2' # edi file name edi_file = os.path.join(edi_path, 'Synth00.edi') # read edi file into an MT object mtObj = MT(edi_file) # determine strike angle for the 2d parts of impedance tensor strike = strike_angle( z_object=mtObj.Z, skew_threshold= 5, # threshold in skew angle (degrees) to determine if data are 3d eccentricity_threshold= 0.1 # threshold in phase ellipse eccentricity to determine if data are 2d (vs 1d) ) print strike
[38.45662316, 128.45662316], [28.61883115, 118.61883115], [14.45341494, 104.45341494], [8.43320651, 98.43320651], [4.94952784, 94.94952784], [2.09090369, 92.09090369], [1.39146887, 91.39146887], [0.39905337, 90.39905337], [-5.49553673, 84.50446327], [-6.28846049, 83.71153951], [-7.31641788, 82.68358212], [-10.45341947, 79.54658053], [-7.07075086, 82.92924914], [-7.5429295, 82.4570705], [-6.06405688, 83.93594312], [-3.54915951, 86.45084049], [-3.12596637, 86.87403363], [-0.47404093, 89.52595907], [2.74343665, 92.74343665], [4.78078759, 94.78078759], [7.71125988, 97.71125988], [11.0123521, 101.0123521], [13.81639678, 103.81639678], [13.60497071, 103.60497071], [15.87672806, 105.87672806]]) strike_angle = mtg.strike_angle(z_object=mtObj.Z) assert np.all(np.abs(strike_angle[np.isfinite(strike_angle)] - \ strike_angle_result[np.isfinite(strike_angle_result)] < 1e-8))
def analysis_edi(datadir): """ analysis """ # Define the path to your edi file edi_file = datadir + r"edifiles2\15125A.edi" savepath = datadir edi_path = datadir + 'edifiles2' # Create an MT object mt_obj = MT(edi_file) # look at the skew values as a histogram plt.hist(mt_obj.pt.beta, bins=50) plt.xlabel('Skew angle (degree)') plt.ylabel('Number of values') plt.show() # Have a look at the dimensionality dim = dimensionality(z_object=mt_obj.Z, skew_threshold=5, eccentricity_threshold=0.1) print(dim) # calculate strike strike = strike_angle(z_object=mt_obj.Z, skew_threshold=5, eccentricity_threshold=0.1) # display the median strike angle for this station # two values because of 90 degree ambiguity in strike strikemedian = np.nanmedian(strike, axis=0) print(strikemedian) # Use dimensionality to mask a file mask = dim < 3 # Apply masking. The new arrays z_array, z_err_array, and freq will # exclude values where mask is False (i.e. the 3D parts) new_Z_obj = Z(z_array=mt_obj.Z.z[mask], z_err_array=mt_obj.Z.z_err[mask], freq=mt_obj.Z.freq[mask]) new_Tipper_obj = Tipper(tipper_array=mt_obj.Tipper.tipper[mask], tipper_err_array=mt_obj.Tipper.tipper_err[mask], freq=mt_obj.Tipper.freq[mask]) # Write a new edi file mt_obj.write_mt_file(save_dir=savepath, fn_basename='Synth00_mask3d', file_type='edi', new_Z_obj=new_Z_obj, new_Tipper_obj=new_Tipper_obj, longitude_format='LONG', latlon_format='dd') # Plot strike # Get full path to all files with the extension '.edi' in edi_path edi_list = [ os.path.join(edi_path, ff) for ff in os.listdir(edi_path) if ff.endswith('.edi') ] # make a plot (try also plot_type = 1 to plot by decade) strikeplot = PlotStrike(fn_list=edi_list, plot_type=2, plot_tipper='y') # save to file # strikeplot.save_plot(savepath, # file_format='.png', # fig_dpi=400) strike = strikemedian[0] # 0 index chosen based on geological information mt_obj.Z.rotate(strike) mt_obj.Tipper.rotate(strike) # check the rotation angle print(mt_obj.Z.rotation_angle) # Write a new edi file (as before) mt_obj.write_mt_file(save_dir=savepath, fn_basename='Synth00_rotate%1i' % strike, file_type='edi', longitude_format='LONG', latlon_format='dd')