Esempio n. 1
0
def find_1d_distortion(z_object, include_non1d = False):
    """
    find 1D distortion tensor from z object

    ONly use the 1D part of the Z to determine D. 
    Treat all frequencies as 1D, if  "include_non1d = True".

    
    """

    if not isinstance(z_object, MTz.Z):
        raise MTex.MTpyError_inputarguments('first argument must be an ' 
                                            'instance of the Z class')

    z_obj = z_object

    lo_dims = MTge.dimensionality(z_object=z_obj)

    if include_non1d is True:
        lo_dims = [1 for i in lo_dims]

    if len(list(np.where(np.array(lo_dims) == 1))) == 0:
        raise MTex.MTpyError_inputarguments('Z object does not have '
                                  'frequencies with spatial 1D characteristic')

    print lo_dims

    return  find_distortion(z_obj, lo_dims = lo_dims)
Esempio n. 2
0
def find_2d_distortion(z_object, include_non2d=False):
    """
    find 2D distortion tensor from z object

    ONly use the 2D part of the Z to determine D. 
    Treat all frequencies as 2D, if  "include_non2d = True".
    
    """

    if not isinstance(z_object, MTz.Z):
        raise MTex.MTpyError_inputarguments('first argument must be an '
                                            'instance of the Z class')

    z_obj = z_object

    lo_dims = MTge.dimensionality(z_object = z_obj)

    #avoid the (standard) 1D distortion call -> remove all 1
    lo_dims = [ 4 if i == 1 else i for i in lo_dims ]

    if include_non2d is True:
        lo_dims = [2 for i in lo_dims]

    if len(list(np.where(np.array(lo_dims) == 2))) == 0:
        raise MTex.MTpyError_inputarguments('Z object does not have'
                                 ' frequencies with spatial 2D characteristic')


    return  find_distortion(z_obj, lo_dims = lo_dims)
Esempio n. 3
0
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
Esempio n. 4
0
    def test_get_dimensionality_from_edi_file(self):
        mt_obj = MT(
            os.path.normpath(
                os.path.join(TEST_MTPY_ROOT,
                             "examples/data/edi_files/pb42c.edi")))
        dimensionality_result = np.array([
            1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2,
            2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3
        ])
        dimensionality = mtg.dimensionality(z_object=mt_obj.Z)

        self.assertTrue(
            np.allclose(dimensionality, dimensionality_result, 1e-8))
def test_fun():
    """
    test function
    :return: T/F
    """
    # mtObj = MT(r'C:\Git\mtpy\examples\data\edi_files\pb42c.edi')
    
    mtObj = MT(os.path.join(EDI_DATA_DIR, 'pb42c.edi'))

    dimensionality_result = np.array([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2,
                                      2, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2])

    dimensionality = mtg.dimensionality(z_object=mtObj.Z)
    differ = np.abs(dimensionality - dimensionality_result)

    print differ

    assert np.all(np.abs(dimensionality - dimensionality_result) < 1e-8)
Esempio n. 6
0
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)
Esempio n. 7
0
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)
Esempio n. 8
0
# -*- coding: utf-8 -*-
"""
Created on Tue Oct 31 13:19:35 2017

@author: u64125
"""

from mtpy.core.mt import MT
import mtpy.analysis.geometry as mtg
import numpy as np

mtObj = MT(r'C:\Git\mtpy\examples\data\edi_files\pb42c.edi')

dimensionality_result = np.array([
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2,
    2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2
])

dimensionality = mtg.dimensionality(z_object=mtObj.Z)

assert np.all(np.abs(dimensionality - dimensionality < 1e-8))
Esempio n. 9
0
    def plot(self):

        plt.rcParams['font.size'] = self.font_size
        plt.rcParams['figure.subplot.left'] = .07
        plt.rcParams['figure.subplot.right'] = .98
        plt.rcParams['figure.subplot.bottom'] = .09
        plt.rcParams['figure.subplot.top'] = .90
        plt.rcParams['figure.subplot.wspace'] = .2
        plt.rcParams['figure.subplot.hspace'] = .4

        bw = self.bin_width
        histrange = (0, 360)

        # set empty lists that will hold dictionaries with keys as the period
        ptlist = []
        tiprlist = []

        # initialize some parameters
        nc = len(self.mt_list)
        nt = 0
        kk = 0

        for dd, mt in enumerate(self.mt_list):

            #--> set the period
            period = mt.period

            # get maximum length of periods
            if len(period) > nt:
                nt = len(period)

            # estimate where only the 2D sections are
            dim_2d = MTgy.dimensionality(z_object=mt._Z,
                                         skew_threshold=self.skew_threshold)
            index_2d = np.where(dim_2d == 2)[0]
            #------------get strike from phase tensor strike angle-------------
            pt = mt.pt
            az = (90 - pt.azimuth[index_2d]) % 360
            az_err = pt.azimuth_err[index_2d]

            # need to add 90 because pt assumes 0 is north and
            # negative because measures clockwise.

            # put an error max on the estimation of strike angle
            if self.pt_error_floor:
                az[np.where(az_err > self.pt_error_floor)] = 0.0

            # make a dictionary of strikes with keys as period
            mdictpt = dict([(ff, jj)
                            for ff, jj in zip(mt.period[index_2d], az)])
            ptlist.append(mdictpt)

            #-----------get tipper strike------------------------------------
            tip = mt.Tipper
            if tip.tipper is None:
                tip.tipper = np.zeros((len(mt.period), 1, 2),
                                              dtype='complex')
                tip.compute_components()

            # needs to be negative because measures clockwise
            tipr = -tip.angle_real[index_2d]

            tipr[np.where(tipr == 180.)] = 0.0
            tipr[np.where(tipr == -180.)] = 0.0

            # make sure the angle is between 0 and 360
            tipr = tipr % 360

            # make a dictionary of strikes with keys as period
            tiprdict = dict([(ff, jj)
                             for ff, jj in zip(mt.period[index_2d], tipr)])
            tiprlist.append(tiprdict)

        #--> get min and max period
        maxper = np.max([np.max(list(mm.keys())) for mm in ptlist if list(mm.keys())])
        minper = np.min([np.min(list(mm.keys())) for mm in ptlist if list(mm.keys())])

        # make empty arrays to put data into for easy manipulation
        medpt = np.zeros((nt, nc))
        medtipr = np.zeros((nt, nc))

        # make a list of periods from the longest period list
        plist = np.logspace(
            np.log10(minper),
            np.log10(maxper),
            num=nt,
            base=10)
        pdict = dict([(ii, jj) for jj, ii in enumerate(plist)])

        self._plist = plist

        # put data into arrays
        for ii, mm in enumerate(ptlist):
            mperiod = list(mm.keys())
            for jj, mp in enumerate(mperiod):
                for kk in list(pdict.keys()):
                    if mp > kk * (1 - self.period_tolerance) and \
                            mp < kk * (1 + self.period_tolerance):
                        ll = pdict[kk]
                        medpt[ll, ii] = ptlist[ii][mp]
                        medtipr[ll, ii] = tiprlist[ii][mp]
                    else:
                        pass

        # make the arrays local variables
        self._medpt = medpt
        self._medtp = medtipr

        #-----Plot Histograms of the strike angles-----------------------------
        if self.plot_range == 'data':
            brange = np.arange(np.floor(np.log10(minper)),
                               np.ceil(np.log10(maxper)), 1)
        else:
            brange = np.arange(np.floor(self.plot_range[0]),
                               np.ceil(self.plot_range[1]), 1)

        self._brange = brange

        # font dictionary
        fd = {'size': self.font_size, 'weight': 'normal'}

        #------------------plot indivdual decades------------------------------
        if self.plot_type == 1:
            # plot specs
            plt.rcParams['figure.subplot.hspace'] = .3
            plt.rcParams['figure.subplot.wspace'] = .3

            self.fig = plt.figure(self.fig_num, dpi=self.fig_dpi)
            plt.clf()
            nb = len(brange)
            for jj, bb in enumerate(brange, 1):
                # make subplots for invariants and phase tensor azimuths
                if self.plot_tipper == 'n':
                    self.axhpt = self.fig.add_subplot(1, nb, jj, polar=True)
                    axlist = [self.axhpt]

                if self.plot_tipper == 'y':
                    self.axhpt = self.fig.add_subplot(2, nb, jj, polar=True)
                    self.axhtip = self.fig.add_subplot(2, nb, jj + nb,
                                                       polar=True)
                    axlist = [self.axhpt, self.axhtip]

                # make a list of indicies for each decades
                binlist = []
                for ii, ff in enumerate(plist):
                    if ff > 10**bb and ff < 10**(bb + 1):
                        binlist.append(ii)

                # extract just the subset for each decade
                gg = medpt[binlist, :]
                if self.plot_tipper == 'y':
                    tr = medtipr[binlist, :]

                    # compute the historgram for the tipper strike
                    trhist = np.histogram(tr[np.nonzero(tr)].flatten(),
                                          bins=int(360/bw),
                                          range=histrange)

                    # make a bar graph with each bar being width of bw degrees
                    bartr = self.axhtip.bar((trhist[1][:-1]) * np.pi / 180,
                                            trhist[0],
                                            width=bw * np.pi / 180)

                    # set color of the bars according to the number in that bin
                    # tipper goes from dark blue (low) to light blue (high)
                    for cc, bar in enumerate(bartr):
                        try:
                            fc = float(trhist[0][cc]) / trhist[0].max() * .9
                        except ZeroDivisionError:
                            fc = 1.0
                        bar.set_facecolor((0, 1 - fc / 2, fc))

                # estimate the histogram for the decade for invariants and pt
                pthist = np.histogram(gg[np.nonzero(gg)].flatten(),
                                      bins=int(360/bw),
                                      range=histrange)

                # plot the histograms
                self.barpt = self.axhpt.bar((pthist[1][:-1]) * np.pi / 180,
                                            pthist[0],
                                            width=bw * np.pi / 180)

                # set the color of the bars according to the number in that bin
                # pt goes from green (low) to orange (high)
                for cc, bar in enumerate(self.barpt):
                    try:
                        fc = float(pthist[0][cc]) / pthist[0].max() * .8
                    except ZeroDivisionError:
                        fc = 1.0
                    bar.set_facecolor((fc, 1 - fc, 0))

                # make axis look correct with N to the top at 90.
                for aa, axh in enumerate(axlist):
                    # set multiple locator to be every 15 degrees
                    axh.xaxis.set_major_locator(
                        MultipleLocator(30 * np.pi / 180))

                    # set labels on the correct axis
                    axh.xaxis.set_ticklabels(['', 'E', '', '',
                                              'N', '', '',
                                              'W', '', '',
                                              'S', '', ''])
                    # make a light grid
                    axh.grid(alpha=.25)

                    # set pt axes properties
                    if aa == 0:
                        # limits go from -180 to 180 as that is how the angle
                        # is calculated
                        axh.set_xlim(0, 2 * np.pi)

                        # label plot with the mode of the strike angle
                        ptmode = (90 - pthist[1][np.where(
                                  pthist[0] == pthist[0].max())[0][0]]) % 360

                        ptmedian = (90 - np.median(gg[np.nonzero(gg)])) % 360

                        ptmean = (90 - np.mean(gg[np.nonzero(gg)])) % 360

                        axh.text(np.pi, axh.get_ylim()[1] * self.text_pad,
                                 '{0:.1f}$^o$'.format(ptmode),
                                 horizontalalignment='center',
                                 verticalalignment='baseline',
                                 fontdict={'size': self.text_size},
                                 bbox={'facecolor': (.9, .9, 0), 'alpha': .25})

                        # print out the results for the strike angles
                        print('-----Period Range {0:.3g} to {1:.3g} (s)-----'.format(10**bb,
                                                                                     10**(bb + 1)))
                        print('   *PT Strike:     median={0:.1f} mode={1:.1f} mean={2:.1f}'.format(
                            ptmedian,
                            ptmode,
                            ptmean))

                        if self.plot_tipper != 'y':
                            print('\n')

                         #--> set title of subplot
                        axh.set_title(self.title_dict[bb], fontdict=fd,
                                      bbox={'facecolor': 'white', 'alpha': .25})

                        #--> set the title offset
                        axh.titleOffsetTrans._t = (0, .1)

                    # set tipper axes properties
                    elif aa == 1:
                        # limits go from -180 to 180
                        axh.set_xlim(0, 2 * np.pi)

                        # label plot with mode
                        tpmode = (90 - trhist[1][np.where(
                                  trhist[0] == trhist[0].max())[0][0]]) % 360

                        tpmedian = (90 - np.median(tr[np.nonzero(tr)])) % 360

                        tpmean = (90 - np.mean(tr[np.nonzero(tr)])) % 360

                        axh.text(np.pi, axh.get_ylim()[1] * self.text_pad,
                                 '{0:.1f}$^o$'.format(tpmode),
                                 horizontalalignment='center',
                                 verticalalignment='baseline',
                                 fontdict={'size': self.text_size},
                                 bbox={'facecolor': (0, .1, .9), 'alpha': .25})

                        # print out statistics for strike angle
                        print('   *Tipper Strike: median={0:.1f} mode={1:.1f} mean={2:.1f}'.format(
                            tpmedian,
                            tpmode,
                            tpmode))
                        print('\n')
                        if nb > 5:
                            axh.set_title(self.title_dict[bb], fontdict=fd,
                                          bbox={'facecolor': 'white', 'alpha': .25})

                    # set plot labels
                    if jj == 1:
                        if aa == 0:
                            axh.set_ylabel('PT Azimuth', fontdict=fd,
                                           labelpad=self.font_size,
                                           bbox={'facecolor': (.9, .9, 0),
                                                 'alpha': .25})
                        elif aa == 1:
                            axh.set_ylabel('Tipper Strike', fd,
                                           labelpad=self.font_size,
                                           bbox={'facecolor': (0, .1, .9),
                                                 'alpha': 0.25})

                    plt.setp(axh.yaxis.get_ticklabels(), visible=False)

            print('Note: North is assumed to be 0 and the strike angle is measured' +\
                  'clockwise positive.')

            plt.show()

        #------------------Plot strike angles for all period ranges------------
        elif self.plot_type == 2:
            # plot specs
            plt.rcParams['figure.subplot.left'] = .07
            plt.rcParams['figure.subplot.right'] = .98
            plt.rcParams['figure.subplot.bottom'] = .100
            plt.rcParams['figure.subplot.top'] = .88
            plt.rcParams['figure.subplot.hspace'] = .3
            plt.rcParams['figure.subplot.wspace'] = .2

            self.fig = plt.figure(self.fig_num,
                                  self.fig_size,
                                  dpi=self.fig_dpi)
            plt.clf()
            # make subplots for invariants and phase tensor azimuths
            if self.plot_tipper == 'n':
                self.axhpt = self.fig.add_subplot(1, 1, 1, polar=True)
                axlist = [self.axhpt]
            else:
                self.axhpt = self.fig.add_subplot(1, 2, 1, polar=True)
                self.axhtip = self.fig.add_subplot(1, 2, 2, polar=True)
                axlist = [self.axhpt, self.axhtip]

            # make a list of indicies for each decades
            binlist = [pdict[ff] for ff in plist
                       if ff > 10**brange.min() and ff < 10**brange.max()]

            # extract just the subset for each decade
            gg = medpt[binlist, :]

            # estimate the histogram for the decade for invariants and pt
            pthist = np.histogram(gg[np.nonzero(gg)].flatten(),
                                  bins=int(360/bw),
                                  range=histrange)

            # plot the histograms
            self.barpt = self.axhpt.bar((pthist[1][:-1]) * np.pi / 180,
                                        pthist[0],
                                        width=bw * np.pi / 180)

            # set color of pt from green (low) to orange (high count)
            for cc, bar in enumerate(self.barpt):
                fc = float(pthist[0][cc]) / pthist[0].max() * .8
                bar.set_facecolor((fc, 1 - fc, 0))

            # plot tipper if desired
            if self.plot_tipper == 'y':
                tr = self._medtp[binlist, :]

                trhist = np.histogram(tr[np.nonzero(tr)].flatten(),
                                      bins=int(360/bw),
                                      range=histrange)

                self.bartr = self.axhtip.bar((trhist[1][:-1]) * np.pi / 180,
                                             trhist[0],
                                             width=bw * np.pi / 180)

                # set tipper color from dark blue (low) to light blue (high)
                for cc, bar in enumerate(self.bartr):
                    try:
                        fc = float(trhist[0][cc]) / trhist[0].max() * .9
                        bar.set_facecolor((0, 1 - fc / 2, fc))
                    except ZeroDivisionError:
                        pass

            # make axis look correct with N to the top at 90.
            for aa, axh in enumerate(axlist):
                # set major ticks to be every 30 degrees
                axh.xaxis.set_major_locator(MultipleLocator(2 * np.pi / 12))

                # set a light grid
                axh.grid(alpha=0.25)

                # set tick labels to be invisible
                plt.setp(axh.yaxis.get_ticklabels(), visible=False)

                # place the correct label at the cardinal directions
                axh.xaxis.set_ticklabels(['', 'E', '', '',
                                          'N', '', '',
                                          'W', '', '',
                                          'S', '', ''])

                # set pt axes properties
                if aa == 0:
                    axh.set_ylim(0, pthist[0].max())

                    ptmode = (90 - pthist[1][np.where(
                              pthist[0] == pthist[0].max())[0][0]]) % 360

                    ptmedian = (90 - np.median(gg[np.nonzero(gg)])) % 360

                    ptmean = (90 - np.mean(gg[np.nonzero(gg)])) % 360

                    axh.text(170 * np.pi / 180, axh.get_ylim()[1] * .65,
                             '{0:.1f}$^o$'.format(ptmode),
                             horizontalalignment='center',
                             verticalalignment='baseline',
                             fontdict={'size': self.text_size},
                             bbox={'facecolor': (.9, .9, 0), 'alpha': 0.25})

                    # print results of strike analysis for pt
                    print('-----Period Range {0:.3g} to {1:.3g} (s)-----'.format(10**brange[0],
                                                                                 10**brange[-1]))
                    print('   *PT Strike:     median={0:.1f} mode={1:.1f} mean={2:.1f}'.format(
                        ptmedian,
                        ptmode,
                        ptmean))

                    if self.plot_tipper != 'y':
                        print('\n')

                    axh.set_title('PT Azimuth', fontdict=fd,
                                  bbox={'facecolor': (.9, .9, 0), 'alpha': 0.25})

                # set tipper axes properties
                elif aa == 2:
                    axh.set_ylim(0, trhist[0].max())

                    tpmode = (90 - trhist[1][np.where(
                              trhist[0] == trhist[0].max())[0][0]]) % 360

                    tpmedian = (90 - np.median(tr[np.nonzero(tr)])) % 360

                    tpmean = (90 - np.mean(tr[np.nonzero(tr)])) % 360

                    axh.text(170 * np.pi / 180, axh.get_ylim()[1] * .65,
                             '{0:.1f}$^o$'.format(tpmode),
                             horizontalalignment='center',
                             verticalalignment='baseline',
                             fontdict={'size': self.text_size},
                             bbox={'facecolor': (0, .1, .9), 'alpha': 0.25})

                    print('   *Tipper Stike:  median={0:.1f} mode={1:.1f} mean={2:.1f}\n'.format(
                        tpmedian,
                        tpmode,
                        tpmean))

                    axh.set_title('Tipper Strike', fontdict=fd,
                                  bbox={'facecolor': (0, .1, .9), 'alpha': 0.25})

                # move title up a little to make room for labels
                axh.titleOffsetTrans._t = (0, .15)

            # remind the user what the assumptions of the strike angle are
            print('Note: North is assumed to be 0 and the strike angle is ' +\
                  'measured clockwise positive.')

            plt.show()
Esempio n. 10
0
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)
Esempio n. 11
0
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)
Esempio n. 12
0
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
Esempio n. 13
0
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
# directory containing edis
edi_path = r'C:\mtpywin\mtpy\examples\data\edi_files_2'
savepath = r'C:/tmp'

# edi file name
edi_file = os.path.join(edi_path, 'Synth00.edi')

# read edi file into an MT object
mtObj = MT(edi_file)

# use the phase tensor to determine which frequencies are 1D/2D/3D
dim = dimensionality(
    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)
)

# create a True/False array to mask with
mask = dim < 3

new_Z_object = Z(z_array=mtObj.Z.z[mask],
                 z_err_array=mtObj.Z.z_err[mask],
                 freq=mtObj.Z.freq[mask])

new_Tipper_object = Tipper(tipper_array=mtObj.Tipper.tipper[mask],
                           tipper_err_array=mtObj.Tipper.tipper_err[mask],
                           freq=mtObj.Tipper.freq[mask])
Esempio n. 15
0
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
strike_pt_arr = np.zeros((num_period, num_station))
strike_tip_arr = np.zeros((num_period, num_station))
strike_tip_arr_im = np.zeros((num_period, num_station))

ellip_arr = np.zeros((num_period, num_station))

period_dict = dict([(np.round(key, 5), value)
                    for value, key in enumerate(period_list)])

for st_index, mt_obj in enumerate(mt_list):
    # make a dictionary that coorelates with index values
    st_period_dict = dict([(np.round(key, 5), value)
                           for value, key in enumerate(1.0 / mt_obj.Z.freq)])

    # get dimensionality of mt response
    dim_2d = mt_geometry.dimensionality(z_object=mt_obj.Z, beta_threshold=3)

    st_ellip = mt_obj.pt.ellipticity[0]
    # get strike angle and skew from phase tensor
    st_strike = (90 - mt_obj.pt.azimuth[0]) % 360
    st_skew = 2.0 * mt_obj.pt.beta[0]
    if mt_obj.Tipper.tipper is not None:
        st_tip_strike = (-mt_obj.Tipper.angle_real + 180) % 360
        st_tip_strike_im = (180 - mt_obj.Tipper.angle_imag) % 360

    else:
        st_tip_strike = np.zeros(mt_obj.Z.freq.shape[0])
        st_tip_strike_im = np.zeros(mt_obj.Z.freq.shape[0])

    # fill the arrays with data
    for st_key, strike, d_2d, strike_tip, strike_tip_im, ellip in zip(
Esempio n. 17
0
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
Esempio n. 18
0
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')