Module invplot

Expand source code
import csv
import pathlib 
import re
import sys

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import scipy.interpolate
import matplotlib

def autoplot(inv_file, iteration, return_dict=False, **kwargs):
    """Function to run all intermedaite functions and resinv_plot to simply read and plot everything in one call.

    Parameters
    ----------
    inv_file : str or pathlib.PurePath object
        Filepath to .inv file of interest. The .inv file should be one generated from Res2DInv.
    iteration : int or list or either str {':', 'all'}
        Integer or list of integers indicating which iteration of the .inv result to use for plotting. If list, all will be plotted separately. If ':' or 'all', will plot all iterations successively.
    return_dict : bool, optional
        Whether to return results as a dictionary, by default False
    **kwargs
        Other keyword arguments may be read into autoplot. These are read in as **kwargs to either resinv_plot() or matplotlib.pyplot.imshow via the resinv_plot function. See documentation for resinv_plot for available parameters for resinv_plot.

    Returns
    -------
    inv_dict : dict
        If return_dict set to True, Dictionary containing all input parameters and data generated along the way, including the output figures and axes

    """
    if isinstance(inv_file, pathlib.PurePath):
        pass
    else:
        inv_file = pathlib.Path(inv_file)

    inv_dict = ingest_inv(inv_file, verbose=False, show_iterations=False)
    inv_dict = read_inv_data(inv_file=inv_file, inv_dict=inv_dict)

    allIterList = [':', 'all']
    if type(iteration) is int:
        iteration = [iteration]
    elif iteration.lower() in allIterList:
        iteration = inv_dict['iterationDF'].Iteration.tolist()

    resinv_params_list = ['inv_dict', 'colMap', 'cBarFormat', 'cBarLabel', 'cBarOrientation', 'cMin', 'cMax', 
                          'griddedFt', 'griddedM', 'title', 'normType', 'primaryUnit', 'showPoints','whichTicks', 
                          'figsize', 'dpi', 'reverse', 'tight_layout', 'savefig', 'saveformat']
    resinv_kwargs = {}
    imshow_kwargs = {}
    for key, value in kwargs.items():
        if key in resinv_params_list:
            resinv_kwargs[key] = value
        else:
            imshow_kwargs[key] = value

    iterIndList = []
    iterNoList = []
    figList = []
    axList = []

    for i in iteration:
        iterNo = i
        inv_dict['iterationNo'] = iterNo
        iterInd = inv_dict['iterationDF'][inv_dict['iterationDF'].Iteration==i].index.tolist()[0]
        inv_dict['iterationInd'] = iterInd
        inv_dict = read_inv_data_other(inv_file=inv_file, inv_dict=inv_dict, iteration_no=iterNo)
        inv_dict = read_error_data(inv_file=inv_file, inv_dict=inv_dict)
        inv_dict = get_resistivitiy_model(inv_file=inv_file, inv_dict=inv_dict)
        fig, ax = resinv_plot(inv_dict=inv_dict, imshow_kwargs=imshow_kwargs, **kwargs)

        iterIndList.append(i)
        iterNoList.append(inv_dict['iterationDF'].loc[iterInd, 'Iteration'])
        figList.append(fig)
        axList.append(ax)

    inv_dict['iterationNo'] = iterIndList
    inv_dict['iterationInd'] = iterNoList
    inv_dict['fig'] = figList
    inv_dict['ax'] = axList
    
    if return_dict:
        return inv_dict
    return

#Function that performs all the actual plotting
def resinv_plot(inv_dict, colMap='nipy_spectral', cBarFormat ='%3.0f', cBarLabel='Resistivity (ohm-m)', cBarOrientation='horizontal', cMin=None, cMax=None, griddedFt=[False,False], griddedM=[False,False], title=None, normType='log', primaryUnit='m', showPoints=False,whichTicks='major', figsize=None, dpi=None, reverse=False, tight_layout=True, savefig=False, saveformat='png', imshow_kwargs=None, **kwargs):
    """Function to pull everything together and plot it nicely.

    It is recommended to use the autoplot function rather than resinv_plot directly, since using autoplot() incorporates all the setup needed to create the input dictionary keys/values correctly.

    Parameters
    ----------
    inv_dict : dict
        Dictionary of inversion results generated from previous steps
    colMap : str, optional
        Colormap, any acceptable from matplotlib, by default 'nipy_spectral'
    cBarFormat : str, optional
        Format string for colorbar tick labels, by default '%3.0f'
    cBarLabel : str, optional
        Colorbar label, by default 'Resistivity (ohm-m)'
    cBarOrientation : str {'horizonta', 'vertical'}, optional
        Orientation of the colorbar, by default 'horizontal'
    cMin : float, optional
        Minimum of colorbar/colormap, by default None, which uses the minimum value of the dataset.
    cMax : float, optional
        Maximum of colorbar/colormap, by default None, which uses the maximum value of the dataset.
    griddedFt : list, optional
        Whether to show gridlines on the feet ticks, first position is x, second position is y, by default [False,False]
    griddedM : list, optional
        Whether to show gridlines on the meter tickes, first position is x, second posistion is y, by default [False,False]
    title : str, optional
        String to show as the title, if desired to set manually, by default None, which shows the filename as the title
    normType : str {'log', 'linear'}, optional
        Normalization type, by default 'log'. Determines whether matplotlib.colors.LogNorm or matplotlib.colors.Normalize is used for colormap.
    primaryUnit : str {'m', 'ft'}, optional
        Whether to display meters or feet as primary unit (this determines which unit is larger on the axis and is on the left and top), by default 'm'
    showPoints : bool, optional
        Whether to show the datapoints used for interpolation, by default False
    whichTicks : str {'major', 'minor', 'both'}, optional
        If griddedFt or griddedM has any True, this determines whether major, minor, or both gridlines are used; by default 'major'.
    figsize : tuple, optional
        Tuple (width, height) of the figsize, read into plt.rcParams['figure.figsize'], by default None.
    dpi : int or float, optional
        Resolution (dots per square inch) of final figure, read into plt.rcParams['figure.dpi'], by default None.
    reverse : bool, optional
        Whether to display the data in reverse (flipped along x) of what is read into from .inv file, by default False
    tight_layout : bool, optional
        If true, calls fig.tight_layout(). Otherwise, tries to maximize space on the figure using plt.subplots_adjust, by default True
    savefig : bool, optional
        If False, will not save figure. Otherwise, calls plt.savefig() and the value of this parameter will be used as the output filepath, by default False.
    saveformat : str, optional
        Read into plt.savefig(format) paramater, by default 'png'.

    Returns
    -------
    dict
        Returns existing inv_dict input, but with added keys of ['fig'] and ['ax'] containing a list of the fig and ax objects (list, since multiple iterations can be done at once)
    """
    if title is None:
        title = inv_dict['inv_file_Path'].stem
    
    if 'figure.dpi' not in list(inv_dict.keys()):
        inv_dict['figure.dpi'] = 250
    if 'figure.figsize' not in list(inv_dict.keys()):
        inv_dict['figure.figsize'] = (12,5)

    x = inv_dict['resistModelDF']['x'].copy()
    z = inv_dict['resistModelDF']['zElev'].copy()
    v = inv_dict['resistModelDF']['Data'].copy()

    if figsize is None:
        plt.rcParams['figure.figsize'] = inv_dict['figure.figsize']
    else:
        inv_dict['figure.figsize'] = figsize
        plt.rcParams['figure.figsize'] = figsize

    if dpi is None:
        plt.rcParams['figure.dpi'] = inv_dict['figure.dpi']
    else:
        inv_dict['figure.dpi'] = dpi
        plt.rcParams['figure.dpi'] = dpi
    
    maxXDist = max(np.float_(inv_dict['electrodes']))

    if cMin is None:
        cMin = inv_dict['resistModelDF']['Data'].min()
    if cMax is None:
        cMax = inv_dict['resistModelDF']['Data'].max()
    

    for i in enumerate(v):
        v[i[0]] = abs(float(i[1]))

    xi, zi = np.linspace(min(x), max(x), int(max(x))), np.linspace(min(z), max(z), int(max(z)))
    xi, zi = np.meshgrid(xi, zi)

    vi = scipy.interpolate.griddata((x, z), v, (xi, zi))#, method='linear')

    ptSize = round(100 / maxXDist * 35, 1)

    fig, axes = plt.subplots(1)
    cmap = matplotlib.cm.binary
    my_cmap = cmap(np.arange(cmap.N))
    my_cmap[:,-1] = np.linspace(0,1,cmap.N)
    my_cmap = matplotlib.colors.ListedColormap(my_cmap)

    vmax98 = np.percentile(v, 98)
    vmin2 = np.percentile(v, 2)
    minx = min(x)
    maxx = max(x)
    minz = min(z)
    maxz = max(z)

    vmax = cMax
    vmin = cMin

    #if cMax >= resistModelDF['Data'].max():
    #  vmax = vmax98
    #else:
    #  vmax = cMax
    #if cMin <= resistModelDF['Data'].min():
    #  vmin = vmin2
    #else:
    #  vmin = cMin
    #cbarTicks = np.arange(np.round(vmin,-1),np.round(vmax-1)+1,10) 

    arStep = np.round((vmax-vmin)/10,-1)
    cbarTicks = np.arange(np.round(vmin, -1), np.ceil(vmax/10)*10,arStep)

    #Get default values or kwargs, depending on if kwargs have been used
    if 'norm' in imshow_kwargs.keys():
        norm = imshow_kwargs['norm']
    else:
        if normType=='log':
            if vmin <= 0:
                vmin = 0.1
            norm = matplotlib.colors.LogNorm(vmin = vmin, vmax = vmax)
            #cBarFormat = '%.1e'
            #cbarTicks = np.logspace(np.log10(vmin),np.log10(vmax),num=10)
        else:
            norm = matplotlib.colors.Normalize(vmin = vmin, vmax = vmax)

    #im = self.axes.imshow(vi, vmin=vmin, vmax=vmax, origin='lower',
    if 'extent' in imshow_kwargs.keys():
        extent = imshow_kwargs['extent']
        imshow_kwargs.pop('extent', None)
    else:
        extent = [minx, maxx, minz, maxz]
        
    if 'aspect' in imshow_kwargs.keys():
        aspect = imshow_kwargs['aspect']
        imshow_kwargs.pop('aspect', None)
    else:
        aspect = 'auto'
        
    if 'cmap' in imshow_kwargs.keys():
        cmap = imshow_kwargs['cmap']
        imshow_kwargs.pop('cmap', None)
    else:
        cmap=colMap   
        
    if 'interpolation' in imshow_kwargs.keys():
        interp = imshow_kwargs['interpolation']
        imshow_kwargs.pop('interpolation', None)
    else:
        interp='spline36'   
    
    im = axes.imshow(vi, origin='lower',
                extent=extent,
                aspect=aspect,
                cmap =cmap,
                norm = norm,
                interpolation=interp, **imshow_kwargs)
    f, a = __plot_pretty(inv_dict, x,z,v,fig=fig,im=im,ax=axes,colMap=colMap,cMin=cMin,cMax=cMax, 
                       gridM=griddedM, gridFt=griddedFt, primaryUnit=primaryUnit, t=title, tight_layout=tight_layout,
                       cbarTicks=cbarTicks,cBarFormat=cBarFormat,cBarLabel=cBarLabel,cBarOrient=cBarOrientation,
                       showPoints=showPoints, norm=norm, whichTicks=whichTicks, reverse=reverse)

    plt.show(fig)
    if savefig is not False:
        plt.savefig(savefig, format=saveformat, facecolor='white')

    plt.close(f)
    return f, a

#Function to ingest inv file and find key information for later
def ingest_inv(inv_file, verbose=True, show_iterations=True):
    """Function to ingest inversion file and get key points (row numbers) in the file

    Parameters
    ----------
    inv_file : str or pathlib.Path object
        The res2Dinv .inv file to work with.
    verbose : bool, default=True
        Whether to print results to terminal. Here, prints a pandas dataframe with information about iterations.
    show_iterations : bool, default=True
        Whether to show a matplotlib plot with the iteration on x axis and percent error on y axis.

    Returns
    -------
    inv_dict : dict
        Dictionary containing the important locations in the file
    """
    if isinstance(inv_file, pathlib.PurePath):
        pass
    else:
        inv_file = pathlib.Path(inv_file)
    
    fileHeader = []
    iterationStartRowList = []
    layerRowList = []
    layerDepths = []
    noLayerRow = -1
    blockRow = -1
    layerRow = -1
    layerInfoRow = -1
    resistDF = pd.DataFrame()
    dataList = []
    noPoints = []
    calcResistivityRowList = []
    refResistRow=-1
    topoDataRow = -1
    iterationsInfoRow = -1

    with open(str(inv_file)) as datafile: 
        filereader = csv.reader(datafile)
        for row in enumerate(filereader):
            startLayer = 0
            endLayer = 0
            lay = -1
            #print(row[0])
            if row[0] <= 8:
               
                if len(row[1])>1:
                    fileHeader.append(row[1][0]+', '+row[1][1])
                    continue
                else:
                    fileHeader.append(row[1][0].strip())
                    continue
                
            if 'NUMBER OF LAYERS' in str(row[1]):
                noLayerRow = row[0]+1
                continue
            if row[0] == noLayerRow:
                noLayers = int(row[1][0])
                layerList = np.linspace(1,noLayers, noLayers)
                continue
            
            if 'NUMBER OF BLOCKS' in str(row[1]):
                blockRow = row[0]+1
                continue
            if row[0]==blockRow:
                noBlocks = int(row[1][0])
                continue

            if 'ITERATION' in str(row[1]):
                iterationStartRowList.append(row[0]) #Add row of iteration to iterationStartRowList
                continue

            if 'LAYER ' in str(row[1]):
                iterInd = len(iterationStartRowList)-1
                if iterInd > len(layerRowList)-1:
                    layerRowList.append([row[0]])
                else:
                    layerRowList[iterInd].append(row[0])
                layerInfoRow = row[0]+1
                continue
            if row[0]==layerInfoRow:
                noPoints.append(int(row[1][0].strip()))
                layerDepths.append(row[1][1].strip())
                continue
            
            if 'CALCULATED APPARENT RESISTIVITY' in str(row[1]):
                calcResistivityRowList.append(row[0])
                continue
            
            if 'Reference resistivity is' in str(row[1]):
                refResistRow = row[0]+1 
                continue
            
            if row[0]==refResistRow:
                refResist = float(row[1][0].strip())
                continue
            
            if 'TOPOGRAPHICAL DATA' in str(row[1]):
                topoDataRow = row[0]
                continue
            if row[0]==topoDataRow+2:
                noTopoPts = int(row[1][0].strip())
                continue

            if 'COORDINATES FOR ELECTRODES' in str(row[1]):
                electrodeCoordsRow = row[0]
                continue

            if 'Shift matrix' in str(row[1]):
                shiftMatrixRow = row[0]
                continue

            if 'Blocks sensitivity and uncertainity values (with smoothness constrain)' in str(row[1]):
                sensAndUncertainRow = row[0]
                continue

            if 'Error Distribution' in str(row[1]):
                errorDistRow = row[0] #no of data points
                continue

            if 'Total Time' in str(row[1]):
                iterationsInfoRow=row[0]
                iterDataList = []
                continue
            
            if iterationsInfoRow > 1:
                if row[1] == []:
                    print('   ')
                    noIterations = row[0]-iterationsInfoRow-1
                    break
                iterDataList.append(row[1][0].split())

    layerDepths = layerDepths[0:noLayers]
    layerDepths[noLayers-1] = float(layerDepths[(noLayers-2)])+(float(layerDepths[noLayers-2])-float(layerDepths[noLayers-3]))
    layerDepths = [float(x) for x in layerDepths]

    noPoints = noPoints[0:noLayers]

    keyList=['Name', 'NomElectrodeSpacing', 'ArrayCode', 'ProtocolCode', 'MeasureHeader', 'MeasureType', 'NoDataPoints','DistanceType','FinalFlag']

    global fileHeaderDict
    fileHeaderDict = dict(zip(keyList, fileHeader))
    noDataPoints = int(fileHeaderDict['NoDataPoints'])
    iterationDF = pd.DataFrame(iterDataList, columns=['Iteration',  'Time for this iteration', 'Total Time', '%AbsError'])
    iterationDF = iterationDF.apply(pd.to_numeric)

    if verbose:
        print(iterationDF)
    if show_iterations:
        fig1, ax1 = plt.subplots(1)
        iterationDF.plot('Iteration','%AbsError',figsize=(3,3), ax=ax1, c='k')
        iterationDF.plot('Iteration','%AbsError',figsize=(3,3),kind='scatter', ax=ax1, c='k')
        ax1.set_title(inv_file.stem)
        ax1.set_xticks(np.arange(0,iterationDF['Iteration'].max()+1))
        ax1.get_legend().remove()
        plt.show(fig1)

    inv_dict = {
        'inv_file_Path':inv_file,
        'fileHeader':fileHeader,
        'iterationStartRowList':iterationStartRowList,
        'layerRowList':layerRowList, 
        'layerDepths':layerDepths,
        'noLayerRow':noLayerRow,
        'blockRow':blockRow ,
        'layerRow':layerRow ,
        'layerInfoRow':layerInfoRow ,
        'resistDF':resistDF,
        'dataList':dataList,
        'noPoints':noPoints,
        'calcResistivityRowList':calcResistivityRowList ,
        'refResistRow':refResistRow,
        'topoDataRow':topoDataRow,
        'iterationsInfoRow':iterationsInfoRow,
        'iterationDF':iterationDF,
        'noIterations':noIterations,
        'noDataPoints':noDataPoints,
        'shiftMatrixRow':shiftMatrixRow,
        'electrodeCoordsRow':electrodeCoordsRow,
        'noTopoPts':noTopoPts,
        'sensAndUncertainRow':sensAndUncertainRow,
        'noModelBlocks':[],
        'errorDistRow':errorDistRow,
        'fileHeaderDict':fileHeaderDict}
    
    return inv_dict

#Input Data
def read_inv_data(inv_file, inv_dict, startRow=9):
    """Function to do initial read of .inv file. 
    
    This data does not change with iteration, as in later function. This function should be readafter ingest_inv, using the output from that as inv_dict.
     
    

    Parameters
    ----------
    inv_file : str or pathlib.Path object
        Filepath of .inv file
    inv_dict : dict
        Dictionary (output from ingest_inv) containing information about where data is located in the .inv file
    startRow : int, optional
        Where to start the read. This is, by default 9, which works well for .inv files tested.

    Returns
    -------
    _type_
        _description_
    """
    noDataPoints = inv_dict['noDataPoints']
    if isinstance(inv_file, pathlib.PurePath):
        pass
    else:
        inv_file = pathlib.Path(inv_file)

    import csv
    with open(inv_file) as datafile: 
        filereader = csv.reader(datafile)
        start = 0
        inDataList = []
        for row in enumerate(filereader):
            if row[0] < startRow:
                continue
            elif row[0] < startRow+noDataPoints:
                inDataList.append(re.sub('\s+',' ',row[1][0]).split(' '))
            else:
                break
    inDF = pd.DataFrame(inDataList)
    if startRow == 9:
        inDF.drop([0],inplace=True,axis=1)
    inDF.astype(np.float64)
    inDF.columns=['NoElectrodes', 'A(x)', 'A(z)', 'B(x)', 'B(z)', 'M(x)', 'M(z)', 'N(x)', 'N(z)', 'Data']
    inv_dict['resistDF']  = inDF
    return inv_dict

#Read other important inversion data
def read_inv_data_other(inv_file, inv_dict, iteration_no=None):
    """Function to read inversion data. 

    Parameters
    ----------
    inv_file : str or pathlib.Path object
        Filepath to .inv file of interest.
    inv_dict : dict
        Dictionary contianing outputs from ingest_inv and read_in_data.
    iteration_no : int
        Iteration number of interest.

    Returns
    -------
    dict
        Dictionary with more information appended to input dictionary
    """
    if iteration_no is None:
        print('Please run read_inv_data_other again and specify iteration by setting iteration_no parameter equal to integer.')
        return
    #Extract needed variables from dict
    iterationInd = inv_dict['iterationDF'][inv_dict['iterationDF'].Iteration==iteration_no].index.tolist()[0]
    inv_dict['iterationNo'] = iteration_no
    inv_dict['iterationInd'] = iterationInd


    invDF = inv_dict['resistDF']
    layerRowList = inv_dict['layerRowList']
    noPoints = inv_dict['noPoints']
    layerDepths = inv_dict['layerDepths']
    shiftMatrixRow = inv_dict['shiftMatrixRow']
    electrodeCoordsRow = inv_dict['electrodeCoordsRow']
    topoDataRow = inv_dict['topoDataRow']
    noTopoPts = inv_dict['noTopoPts']
    sensAndUncertainRow = inv_dict['sensAndUncertainRow']

    #Get Electrodes
    electrodes= pd.concat([invDF['A(x)'],invDF['B(x)'], invDF['M(x)'], invDF['B(x)'], invDF['N(x)']],ignore_index=True)
    electrodes.reset_index(inplace=True, drop=True)
    electrodes = electrodes.unique()
    inv_dict['electrodes'] = electrodes

    #ElectrodeCoordinates
    noModelElects = shiftMatrixRow-electrodeCoordsRow-1
    electrodeCoordsDF = pd.read_table(inv_file,skiprows=electrodeCoordsRow,nrows=noModelElects, sep='\s+')
    electrodeCoordsDF.dropna(axis=1,inplace=True)
    electrodeCoordsDF.columns=['xDist','RelElevation']
    electrodeCoordsDF['ElectrodeNo'] = electrodeCoordsDF.index+1
    inv_dict['electrodeCoordsDF'] = electrodeCoordsDF

    #Topographical Data
    topoDF = pd.read_table(inv_file,skiprows=topoDataRow+2,nrows=noTopoPts, sep='\s+')
    topoDF.reset_index(inplace=True)
    topoDF.columns=['xDist','Elevation']
    topoDF['ElectrodeNo'] = topoDF.index+1
    inv_dict['topoDF'] = topoDF

    #Resistivity Model
    resistModelDF = pd.DataFrame()
    for r in enumerate(layerRowList[iterationInd]):
        layerDepth = layerDepths[r[0]]
        noPtsInLyr = noPoints[r[0]]
        currDF = pd.read_table(inv_file,skiprows=r[1]+1, nrows=noPtsInLyr,sep=',')
        currDF.columns=['ElectrodeNo','Data']
        currDF['z'] = layerDepth
        resistModelDF= pd.concat([resistModelDF,currDF],ignore_index=True).copy()
    resistModelDF.reset_index(inplace=True, drop=True)
    noModelBlocks=resistModelDF.shape[0]
    inv_dict['resistModelDF'] = resistModelDF
    inv_dict['noModelBlocks'] = noModelBlocks

    #Shift Matrix
    shiftMatrixDF = pd.read_table(inv_file,skiprows=shiftMatrixRow+1,nrows=noModelElects, sep='\s+',header=None,index_col=0)
    shiftMatrixDF.dropna(axis=1,inplace=True)
    for c in shiftMatrixDF:
        shiftMatrixDF.rename(columns={c:'Layer'+str(int(c)-1)},inplace=True)
    inv_dict['shiftMatrixDF'] = shiftMatrixDF

    #Sensitivity
    sensDF = pd.read_table(inv_file,skiprows=sensAndUncertainRow+3,nrows=noModelBlocks, sep='\s+',header=None,index_col=0)
    sensDF.dropna(axis=1,inplace=True)
    sensDF.reset_index(inplace=True)
    sensDF.columns=['BlockNo','Sensitivity', '%ApproxUncertainty']
    inv_dict['sensDF'] = sensDF

    return inv_dict

#Error Distribution
def read_error_data(inv_file, inv_dict):
    """Function to read data pertaining to model error

    Parameters
    ----------
    inv_file : str or pathlib.Path object
        Filepath to .inv file of interest
    inv_dict : dict 
        Dictionary containing cumulative output from ingest_inv, read_inv_data, and read_inv_data_other

    Returns
    -------
    dict
        Ouptput dictionary containing all information from read_error_data and previous functions.
    """
    import csv
    noDataPoints = inv_dict['noDataPoints']
    startRow = inv_dict['errorDistRow']+1

    with open(inv_file) as datafile: 
        filereader = csv.reader(datafile)
        inDataList = []
        for row in enumerate(filereader):
            if row[0] < startRow:
                continue
            elif row[0] < startRow+noDataPoints:
                newrow = row[1]
                newrow.append(newrow[4][8:])
                newrow[4] = newrow[4][0:8]
                newrow = [x.strip() for x in newrow]
                inDataList.append(newrow)
            else:
                break
    inDF = pd.DataFrame(inDataList)
    inv_dict['errDistDF'] = inDF

    errDistDF = inv_dict['errDistDF']

    colList=['xDist?','nFactor?','Measure1','Measure2','PercentError','MoreStacks','AvgMeasure']
    for i in range(0,5):
        errDistDF[i]=errDistDF[i].astype(np.float64)
        errDistDF.rename(columns={i:colList[i]},inplace=True)
    errDistDF['AvgMeasure'] = (errDistDF['Measure1']+errDistDF['Measure2'])/2
    inv_dict['errDistDF'] = errDistDF
    return inv_dict

#Interpolate between two points, simple
def map_diff(xIn, x1,x2,y1,y2):
    """Simple, linear interpolation between two points

    Parameters
    ----------
    xIn : float, int, or numeric
        X Location at which y-value is desired. This should fall between (or be equal to) x1 and x2
    x1 : float, int, or numeric
        Initial X location, with known y-value y1 
    x2 : float, int, or numeric
        Second X location, with known y-value y2
    y1 : float, int, or numeric
        Known y-value at x1
    y2 : float, int, or numeric
        Known y-value at x2

    Returns
    -------
    float
        Y-value that is proportionally scaled based on the xIn relative distance to x1 and x2
    """
    if x1==xIn:
        yOut=y1
    elif x2==xIn:
        yOut = y2
    else:
        totXDiff = x2-x1
        percXDiff = (xIn-x1)/totXDiff
        totYDiff = y2-y1
        yOut = y1 + totYDiff*percXDiff
    return yOut

#Function to get resistivity model as pandas dataframe
def get_resistivitiy_model(inv_file, inv_dict):
    """Function to read the resistivity model, given the iteration of interest.

    Parameters
    ----------
    inv_file : str or pathlib.Path object
        Filepath to .inv file of interest
    inv_dict : dict
        Dictionary containing cumulative output from invest_inv, read_inv_data, read_inv_data_other, and read_error_data.

    Returns
    -------
    dict
        Dictionary with resistivity model contained in new key:value pair of inv_dict
    """
    resistModelDF = pd.DataFrame()
    layerRowList= inv_dict['layerRowList']
    iterationInd= inv_dict['iterationInd']
    layerDepths= inv_dict['layerDepths']
    noPoints= inv_dict['noPoints']
    electrodeCoordsDF= inv_dict['electrodeCoordsDF']
    topoDF= inv_dict['topoDF']

    for r in enumerate(layerRowList[iterationInd]):
        layerDepth = layerDepths[r[0]]
        noPtsInLyr = noPoints[r[0]]
        currDF = pd.read_table(inv_file, skiprows=r[1]+1, nrows=noPtsInLyr,sep=',')
        currDF.iloc[currDF.shape[0]-1,1] =  currDF.iloc[currDF.shape[0]-1,0]
        currDF.iloc[currDF.shape[0]-1,0] = currDF.iloc[currDF.shape[0]-2,0]+1
        currDF.columns=['ElectrodeNo','Data']
        currDF['zDepth'] = layerDepth

        for i in currDF.index:
            lowerElecNo = currDF.loc[i,'ElectrodeNo']#-1
            elecInd = electrodeCoordsDF.loc[electrodeCoordsDF['ElectrodeNo']==lowerElecNo].index.values[0]
            currDF.loc[i,'x'] = (electrodeCoordsDF.loc[elecInd,'xDist'] + electrodeCoordsDF.loc[elecInd+1,'xDist'])/2
            for xT in enumerate(topoDF['xDist']):
                if xT[1] < currDF.loc[i,'x']:
                    continue
                else:
                    topoX1 = topoDF.loc[xT[0]-1,'xDist']
                    topoX2 = topoDF.loc[xT[0],'xDist']
                    topoZ1 = topoDF.loc[xT[0]-1,'Elevation']
                    topoZ2 = topoDF.loc[xT[0],'Elevation']
                    break
            currDF.loc[i,'zElev'] = map_diff(currDF.loc[i,'x'],topoX1, topoX2, topoZ1, topoZ2)-currDF.loc[i,'zDepth']
        if r[0] == 0:
            surfDF = currDF.copy()
            surfDF['zElev'] = surfDF.loc[:,'zElev']+surfDF.loc[:,'zDepth']
            surfDF['zDepth'] = 0
            resistModelDF = pd.concat([resistModelDF, surfDF], ignore_index=True)
            resistModelDF.reset_index(inplace=True, drop=True)
        resistModelDF = pd.concat([resistModelDF, currDF], ignore_index=True)
        resistModelDF.reset_index(inplace=True, drop=True)
    inv_dict['resistModelDF'] = resistModelDF
    return inv_dict

#Helper function for __plot_pretty
def __label_plot(fig, ax, gridM, gridFt, whichTicks, pUnit, pUnitXLocs, pUnitYLocs, pUnitXLabels,pUnitYLabels, sUnit, sUnitXLocs, sUnitYLocs, sUnitXLabels, sUnitYLabels, xLims, yLims, t):
    """See __plot_pretty, as all input parameters are derived from there"""
    #matplotlib.rc('font', family='sans-serif')   
    #matplotlib.rc('font', serif='Helvetica') 
    #matplotlib.rc('text', usetex='false')   
    #fontName = {'fontname':'Helvetica'}
    
    plt.title(t)
    ax.set_title(t, fontsize=20)
    ax.set_xlabel('Distance ['+sUnit+']', fontsize = 12)
    ax.set_ylabel('Elevation ['+pUnit+']', fontsize = 14)
    ax.set_xticks(sUnitXLocs)
    ax.set_yticks(pUnitYLocs)
    ax.set_xticklabels(sUnitXLabels,fontsize=12)
    ax.set_yticklabels(pUnitYLabels,fontsize=12)
    ax.set_yticks(pUnitYLocs)
    ax.set_ylim(yLims)
    ax.set_xlim(xLims)
    ax.minorticks_on()

    ax2=ax.twiny()
    ax2.set_xticks(pUnitXLocs)
    ax2.set_xticklabels(pUnitXLabels,fontdict={'fontsize':14})
    ax2.set_xlabel('Distance ['+pUnit+']',fontsize=14)
    ax2.minorticks_on()
    ax2.set_xlim(xLims)

    ax3=ax2.twinx()
    ax3.set_yticks(sUnitYLocs)
    ax3.set_yticklabels(sUnitYLabels,fontdict={'fontsize':10})
    ax3.set_ylim(yLims)
    ax3.set_ylabel('Elevation ['+sUnit+']',fontsize=14, rotation=270, labelpad = 20)
    ax3.minorticks_on()
    

    if pUnit=='m':
        if gridM[0]:
           ax2.grid(axis='x',alpha=0.5, c='k', which=whichTicks)
        if gridM[1]:
            ax.grid(axis='y',alpha=0.5, c='k', which=whichTicks)
        if gridFt[0]:
            ax.grid(axis='x',alpha=0.5, c='k', which=whichTicks)
        if gridFt[1]:
            ax3.grid(axis='y',alpha=0.5, c='k', which=whichTicks)
    else:
        if gridFt[0]:
            ax2.grid(axis='x',alpha=0.5, c='k', which=whichTicks)
        if gridFt[1]:
            ax.grid(axis='y',alpha=0.5, c='k', which=whichTicks)
        if gridM[0]:
            ax.grid(axis='x',alpha=0.5, c='k', which=whichTicks)
        if gridM[1]:
            ax3.grid(axis='y',alpha=0.5, c='k', which=whichTicks)

#Helper function for resinv_plot
def __plot_pretty(inv_dict, x,z,v,im,cbarTicks,fig,ax, colMap='jet',cMin=None,cMax=None, gridFt=[False,False], gridM=[False,False], t='', primaryUnit='m', tight_layout=True, cBarOrient='vertical', cBarFormat ='%3.0f',cBarLabel ='Resistivity (ohm-m)', showPoints=False, norm=0, whichTicks='major', reverse=False):
    """Helper function for resinv_plot, parameters derived from there."""

    topoDF = inv_dict['topoDF']
    if cMin is None:
        cMin = inv_dict['resistModelDF']['Data'].min()
    if cMax is None:
        cMax = inv_dict['resistModelDF']['Data'].max()
    
    plt.rcParams["figure.dpi"] = 300  
    plt.rcParams['xtick.bottom'] = plt.rcParams['xtick.labelbottom'] = False
    plt.rcParams['xtick.top'] = plt.rcParams['xtick.labeltop'] = plt.rcParams["xtick.top"] = True
    plt.sca(ax)
    

    vmax90 = np.percentile(v, 90)
    vmin2 = np.percentile(v, 2)
    vmax = v.max()
    vmin = v.min()
    minx = topoDF['xDist'].min()
    maxx = topoDF['xDist'].max()
    minz = min(z)
    maxz = max(z)

    #xlocsM = ax.get_xticks()
    if maxx>800:
        xlocsM = np.arange(minx,maxx+1,100)
        if max(xlocsM) < maxx:
            xlocsM = np.arange(minx,maxx+101,100)
    else:
        xlocsM = np.arange(minx,maxx+1,50)
        if max(xlocsM) < maxx:
            xlocsM = np.arange(minx,maxx+51,50)

    xlabelsM = [str(int(x)) for x in xlocsM]
    xlocsFt = np.uint16(xlocsM*3.2808399)
    xlabelsFt = [str(int(x)) for x in xlocsFt]

    minFtxLoc = xlocsFt[0]
    maxFtxLoc = xlocsFt[-1]
    xLabelsFtEven = np.arange(np.round(minFtxLoc,-1), np.round(maxFtxLoc,-1), 100)

    if np.round(maxFtxLoc,-1) < maxFtxLoc:
        xLabelsFtEven=np.insert(xLabelsFtEven, len(xLabelsFtEven),np.round(maxFtxLoc,-2))
        xLabelsFtEven=np.insert(xLabelsFtEven, len(xLabelsFtEven),np.round(maxFtxLoc,-2)+100)
    xLocs_FTinM = xLabelsFtEven / 3.2808399

    if np.ceil(maxz)>np.round(maxz,-1):
        zEnd = np.round(maxz,-1)+11
    else:
        zEnd = np.round(maxz,-1)+1
    
    ylocsM = np.arange(np.round(minz,-1),zEnd,10)
    ylabelsM = [str(x) for x in ylocsM]
    yLabelsFt = np.uint16(ylocsM*3.2808399)

    minFtyLabel = yLabelsFt.min()
    maxFtyLabel = yLabelsFt.max()
    yLabelsFtEven = np.arange(0, np.round(maxFtyLabel,-1), 20)
    

    if np.round(maxFtyLabel,-1) < maxFtyLabel:
        yLabelsFtEven=np.insert(yLabelsFtEven, len(yLabelsFtEven),yLabelsFtEven[-1]+20)
    yLocs_FTinM = yLabelsFtEven / 3.2808399

    yLimsM = [ylocsM[1],maxz+3]
    yLimsM = [minz,maxz+3]
    yLimsFt = [np.round(yLimsM[0]*3.2808399, 0), np.round(yLimsM[1]*3.2808399, 0)]

    ax.fill_between(topoDF['xDist'],topoDF['Elevation'],topoDF['Elevation']+10,color='w')
    ax.plot(topoDF['xDist'],topoDF['Elevation'],color='k',linewidth=1)
    ax.scatter(topoDF['xDist'],topoDF['Elevation'],marker='v',edgecolors='w',color='k',s=30)
    if showPoints:
        plt.scatter(x,z, c=v, marker='.', cmap='nipy_spectral', norm=norm)

    xLimsM = [minx,maxx]
    xLimsFt = [np.round(xLimsM[0]*3.2808399, 0), np.round(xLimsM[1]*3.2808399, 0)]
    if reverse:
        xLimsM.reverse()
        xLimsFt.reverse()
        #xlocsM=np.flip(xlocsM)
        #xLocs_FTinM=np.flip(xLocs_FTinM)


    if primaryUnit in ['m', 'meters', 'meter', 'metres', 'metre', 'metric']:
        pUnit = 'm'
        pUnitXLocs = xlocsM
        pUnitYLocs = ylocsM
        pUnitXLabels = xlabelsM
        pUnitYLabels = ylabelsM

        sUnit = 'ft'
        sUnitXLocs = xLocs_FTinM
        sUnitYLocs = yLocs_FTinM
        sUnitXLabels = xLabelsFtEven
        sUnitYLabels = yLabelsFtEven
        __label_plot(fig, ax, gridM, gridFt, whichTicks, primaryUnit, pUnitXLocs, pUnitYLocs, pUnitXLabels,pUnitYLabels, sUnit, sUnitXLocs, sUnitYLocs, sUnitXLabels, sUnitYLabels, xLims=xLimsM, yLims=yLimsM, t=t)
    
    elif primaryUnit in ['f', 'ft', 'feet', 'foot', 'US']:
        pUnit = 'ft'
        pUnitXLocs = xLocs_FTinM
        pUnitYLocs = yLocs_FTinM
        pUnitXLabels = xLabelsFtEven
        pUnitYLabels = yLabelsFtEven
        
        sUnit = 'm'
        sUnitXLocs = xlocsM
        sUnitYLocs = ylocsM
        sUnitXLabels = xlabelsM
        sUnitYLabels = ylabelsM
        __label_plot(fig, ax, gridM, gridFt, whichTicks, primaryUnit, pUnitXLocs, pUnitYLocs, pUnitXLabels,pUnitYLabels, sUnit, sUnitXLocs, sUnitYLocs, sUnitXLabels, sUnitYLabels, xLims=xLimsM, yLims=yLimsM, t=t)  
    
    else:
        pUnit = 'm'
        pUnitXLocs = xlocsM
        pUnitYLocs = ylocsM
        pUnitXLabels = xlabelsM
        pUnitYLabels = ylabelsM

        sUnit = 'ft'
        sUnitXLocs = xLocs_FTinM
        sUnitYLocs = yLocs_FTinM
        sUnitXLabels = xLabelsFtEven
        sUnitYLabels = yLabelsFtEven
        __label_plot(fig, ax, gridM, gridFt, whichTicks, primaryUnit, pUnitXLocs, pUnitYLocs, pUnitXLabels,pUnitYLabels, sUnit, sUnitXLocs, sUnitYLocs, sUnitXLabels, sUnitYLabels, xLims=xLimsM, yLims=yLimsM, t=t)
    
    if tight_layout:
        fig.tight_layout()
    else:
        plt.subplots_adjust(top=0.99, bottom=0.01, left=0.01, right=0.99, 
                    wspace=0, hspace=0)

    if cBarOrient=='horizontal':
        aspect=50
    else:
        aspect=25
    cbar = fig.colorbar(im, ax=ax,orientation=cBarOrient, aspect=aspect, extend='both',ticks=cbarTicks,format=cBarFormat)
    cbar.ax.tick_params(labelsize=12)#set_ticks(cbarTicks)
    cbar.set_label(label=cBarLabel, size=16)

    ax_h, ax_w = ax.bbox.height, ax.bbox.width
    axRatio = ax_w/ax_h
    aspRatio = (xLimsM[1]-xLimsM[0])/(yLimsM[1]-yLimsM[0])
    vertExag = abs(round(aspRatio/axRatio, 1))
    iterNo = inv_dict['iterationNo']
    iterInd = inv_dict['iterationInd']
    iterErr = inv_dict['iterationDF'].loc[iterInd, '%AbsError']
    ax.annotate('Iteration {}\nRMS Error: {}%'.format(iterNo, iterErr),xy=(0.02,0.02),xycoords='subfigure fraction', ha='left', va='bottom')
    ax.annotate('Vert.Exag: '+str(vertExag)+'x',xy=(0.98,0.02),xycoords='subfigure fraction', ha='right', va='bottom')
    fig.set_facecolor("w")

    return fig, ax

Functions

def autoplot(inv_file, iteration, return_dict=False, **kwargs)

Function to run all intermedaite functions and resinv_plot to simply read and plot everything in one call.

Parameters

inv_file : str or pathlib.PurePath object
Filepath to .inv file of interest. The .inv file should be one generated from Res2DInv.
iteration : int or list or either str {':', 'all'}
Integer or list of integers indicating which iteration of the .inv result to use for plotting. If list, all will be plotted separately. If ':' or 'all', will plot all iterations successively.
return_dict : bool, optional
Whether to return results as a dictionary, by default False
**kwargs
Other keyword arguments may be read into autoplot. These are read in as **kwargs to either resinv_plot() or matplotlib.pyplot.imshow via the resinv_plot function. See documentation for resinv_plot for available parameters for resinv_plot.

Returns

inv_dict : dict
Dictionary containing all input parameters and data generated along the way, including the output figures and axes
Expand source code
def autoplot(inv_file, iteration, return_dict=False, **kwargs):
    """Function to run all intermedaite functions and resinv_plot to simply read and plot everything in one call.

    Parameters
    ----------
    inv_file : str or pathlib.PurePath object
        Filepath to .inv file of interest. The .inv file should be one generated from Res2DInv.
    iteration : int or list or either str {':', 'all'}
        Integer or list of integers indicating which iteration of the .inv result to use for plotting. If list, all will be plotted separately. If ':' or 'all', will plot all iterations successively.
    return_dict : bool, optional
        Whether to return results as a dictionary, by default False
    **kwargs
        Other keyword arguments may be read into autoplot. These are read in as **kwargs to either resinv_plot() or matplotlib.pyplot.imshow via the resinv_plot function. See documentation for resinv_plot for available parameters for resinv_plot.

    Returns
    -------
    inv_dict : dict
        Dictionary containing all input parameters and data generated along the way, including the output figures and axes

    """
    if isinstance(inv_file, pathlib.PurePath):
        pass
    else:
        inv_file = pathlib.Path(inv_file)

    inv_dict = ingest_inv(inv_file, verbose=False, show_iterations=False)
    inv_dict = read_inv_data(inv_file=inv_file, inv_dict=inv_dict)

    allIterList = [':', 'all']
    if type(iteration) is int:
        iteration = [iteration]
    elif iteration.lower() in allIterList:
        iteration = inv_dict['iterationDF'].Iteration.tolist()

    resinv_params_list = ['inv_dict', 'colMap', 'cBarFormat', 'cBarLabel', 'cBarOrientation', 'cMin', 'cMax', 
                          'griddedFt', 'griddedM', 'title', 'normType', 'primaryUnit', 'showPoints','whichTicks', 
                          'figsize', 'dpi', 'reverse', 'tight_layout', 'savefig', 'saveformat']
    resinv_kwargs = {}
    imshow_kwargs = {}
    for key, value in kwargs.items():
        if key in resinv_params_list:
            resinv_kwargs[key] = value
        else:
            imshow_kwargs[key] = value

    iterIndList = []
    iterNoList = []
    figList = []
    axList = []

    for i in iteration:
        iterNo = i
        inv_dict['iterationNo'] = iterNo
        iterInd = inv_dict['iterationDF'][inv_dict['iterationDF'].Iteration==i].index.tolist()[0]
        inv_dict['iterationInd'] = iterInd
        inv_dict = read_inv_data_other(inv_file=inv_file, inv_dict=inv_dict, iteration_no=iterNo)
        inv_dict = read_error_data(inv_file=inv_file, inv_dict=inv_dict)
        inv_dict = get_resistivitiy_model(inv_file=inv_file, inv_dict=inv_dict)
        fig, ax = resinv_plot(inv_dict=inv_dict, imshow_kwargs=imshow_kwargs, **kwargs)

        iterIndList.append(i)
        iterNoList.append(inv_dict['iterationDF'].loc[iterInd, 'Iteration'])
        figList.append(fig)
        axList.append(ax)

    inv_dict['iterationNo'] = iterIndList
    inv_dict['iterationInd'] = iterNoList
    inv_dict['fig'] = figList
    inv_dict['ax'] = axList
    
    if return_dict:
        return inv_dict
    return
def get_resistivitiy_model(inv_file, inv_dict)

Function to read the resistivity model, given the iteration of interest.

Parameters

inv_file : str or pathlib.Path object
Filepath to .inv file of interest
inv_dict : dict
Dictionary containing cumulative output from invest_inv, read_inv_data, read_inv_data_other, and read_error_data.

Returns

dict
Dictionary with resistivity model contained in new key:value pair of inv_dict
Expand source code
def get_resistivitiy_model(inv_file, inv_dict):
    """Function to read the resistivity model, given the iteration of interest.

    Parameters
    ----------
    inv_file : str or pathlib.Path object
        Filepath to .inv file of interest
    inv_dict : dict
        Dictionary containing cumulative output from invest_inv, read_inv_data, read_inv_data_other, and read_error_data.

    Returns
    -------
    dict
        Dictionary with resistivity model contained in new key:value pair of inv_dict
    """
    resistModelDF = pd.DataFrame()
    layerRowList= inv_dict['layerRowList']
    iterationInd= inv_dict['iterationInd']
    layerDepths= inv_dict['layerDepths']
    noPoints= inv_dict['noPoints']
    electrodeCoordsDF= inv_dict['electrodeCoordsDF']
    topoDF= inv_dict['topoDF']

    for r in enumerate(layerRowList[iterationInd]):
        layerDepth = layerDepths[r[0]]
        noPtsInLyr = noPoints[r[0]]
        currDF = pd.read_table(inv_file, skiprows=r[1]+1, nrows=noPtsInLyr,sep=',')
        currDF.iloc[currDF.shape[0]-1,1] =  currDF.iloc[currDF.shape[0]-1,0]
        currDF.iloc[currDF.shape[0]-1,0] = currDF.iloc[currDF.shape[0]-2,0]+1
        currDF.columns=['ElectrodeNo','Data']
        currDF['zDepth'] = layerDepth

        for i in currDF.index:
            lowerElecNo = currDF.loc[i,'ElectrodeNo']#-1
            elecInd = electrodeCoordsDF.loc[electrodeCoordsDF['ElectrodeNo']==lowerElecNo].index.values[0]
            currDF.loc[i,'x'] = (electrodeCoordsDF.loc[elecInd,'xDist'] + electrodeCoordsDF.loc[elecInd+1,'xDist'])/2
            for xT in enumerate(topoDF['xDist']):
                if xT[1] < currDF.loc[i,'x']:
                    continue
                else:
                    topoX1 = topoDF.loc[xT[0]-1,'xDist']
                    topoX2 = topoDF.loc[xT[0],'xDist']
                    topoZ1 = topoDF.loc[xT[0]-1,'Elevation']
                    topoZ2 = topoDF.loc[xT[0],'Elevation']
                    break
            currDF.loc[i,'zElev'] = map_diff(currDF.loc[i,'x'],topoX1, topoX2, topoZ1, topoZ2)-currDF.loc[i,'zDepth']
        if r[0] == 0:
            surfDF = currDF.copy()
            surfDF['zElev'] = surfDF.loc[:,'zElev']+surfDF.loc[:,'zDepth']
            surfDF['zDepth'] = 0
            resistModelDF = pd.concat([resistModelDF, surfDF], ignore_index=True)
            resistModelDF.reset_index(inplace=True, drop=True)
        resistModelDF = pd.concat([resistModelDF, currDF], ignore_index=True)
        resistModelDF.reset_index(inplace=True, drop=True)
    inv_dict['resistModelDF'] = resistModelDF
    return inv_dict
def ingest_inv(inv_file, verbose=True, show_iterations=True)

Function to ingest inversion file and get key points (row numbers) in the file

Parameters

inv_file : str or pathlib.Path object
The res2Dinv .inv file to work with.
verbose : bool, default=True
Whether to print results to terminal. Here, prints a pandas dataframe with information about iterations.
show_iterations : bool, default=True
Whether to show a matplotlib plot with the iteration on x axis and percent error on y axis.

Returns

inv_dict : dict
Dictionary containing the important locations in the file
Expand source code
def ingest_inv(inv_file, verbose=True, show_iterations=True):
    """Function to ingest inversion file and get key points (row numbers) in the file

    Parameters
    ----------
    inv_file : str or pathlib.Path object
        The res2Dinv .inv file to work with.
    verbose : bool, default=True
        Whether to print results to terminal. Here, prints a pandas dataframe with information about iterations.
    show_iterations : bool, default=True
        Whether to show a matplotlib plot with the iteration on x axis and percent error on y axis.

    Returns
    -------
    inv_dict : dict
        Dictionary containing the important locations in the file
    """
    if isinstance(inv_file, pathlib.PurePath):
        pass
    else:
        inv_file = pathlib.Path(inv_file)
    
    fileHeader = []
    iterationStartRowList = []
    layerRowList = []
    layerDepths = []
    noLayerRow = -1
    blockRow = -1
    layerRow = -1
    layerInfoRow = -1
    resistDF = pd.DataFrame()
    dataList = []
    noPoints = []
    calcResistivityRowList = []
    refResistRow=-1
    topoDataRow = -1
    iterationsInfoRow = -1

    with open(str(inv_file)) as datafile: 
        filereader = csv.reader(datafile)
        for row in enumerate(filereader):
            startLayer = 0
            endLayer = 0
            lay = -1
            #print(row[0])
            if row[0] <= 8:
               
                if len(row[1])>1:
                    fileHeader.append(row[1][0]+', '+row[1][1])
                    continue
                else:
                    fileHeader.append(row[1][0].strip())
                    continue
                
            if 'NUMBER OF LAYERS' in str(row[1]):
                noLayerRow = row[0]+1
                continue
            if row[0] == noLayerRow:
                noLayers = int(row[1][0])
                layerList = np.linspace(1,noLayers, noLayers)
                continue
            
            if 'NUMBER OF BLOCKS' in str(row[1]):
                blockRow = row[0]+1
                continue
            if row[0]==blockRow:
                noBlocks = int(row[1][0])
                continue

            if 'ITERATION' in str(row[1]):
                iterationStartRowList.append(row[0]) #Add row of iteration to iterationStartRowList
                continue

            if 'LAYER ' in str(row[1]):
                iterInd = len(iterationStartRowList)-1
                if iterInd > len(layerRowList)-1:
                    layerRowList.append([row[0]])
                else:
                    layerRowList[iterInd].append(row[0])
                layerInfoRow = row[0]+1
                continue
            if row[0]==layerInfoRow:
                noPoints.append(int(row[1][0].strip()))
                layerDepths.append(row[1][1].strip())
                continue
            
            if 'CALCULATED APPARENT RESISTIVITY' in str(row[1]):
                calcResistivityRowList.append(row[0])
                continue
            
            if 'Reference resistivity is' in str(row[1]):
                refResistRow = row[0]+1 
                continue
            
            if row[0]==refResistRow:
                refResist = float(row[1][0].strip())
                continue
            
            if 'TOPOGRAPHICAL DATA' in str(row[1]):
                topoDataRow = row[0]
                continue
            if row[0]==topoDataRow+2:
                noTopoPts = int(row[1][0].strip())
                continue

            if 'COORDINATES FOR ELECTRODES' in str(row[1]):
                electrodeCoordsRow = row[0]
                continue

            if 'Shift matrix' in str(row[1]):
                shiftMatrixRow = row[0]
                continue

            if 'Blocks sensitivity and uncertainity values (with smoothness constrain)' in str(row[1]):
                sensAndUncertainRow = row[0]
                continue

            if 'Error Distribution' in str(row[1]):
                errorDistRow = row[0] #no of data points
                continue

            if 'Total Time' in str(row[1]):
                iterationsInfoRow=row[0]
                iterDataList = []
                continue
            
            if iterationsInfoRow > 1:
                if row[1] == []:
                    print('   ')
                    noIterations = row[0]-iterationsInfoRow-1
                    break
                iterDataList.append(row[1][0].split())

    layerDepths = layerDepths[0:noLayers]
    layerDepths[noLayers-1] = float(layerDepths[(noLayers-2)])+(float(layerDepths[noLayers-2])-float(layerDepths[noLayers-3]))
    layerDepths = [float(x) for x in layerDepths]

    noPoints = noPoints[0:noLayers]

    keyList=['Name', 'NomElectrodeSpacing', 'ArrayCode', 'ProtocolCode', 'MeasureHeader', 'MeasureType', 'NoDataPoints','DistanceType','FinalFlag']

    global fileHeaderDict
    fileHeaderDict = dict(zip(keyList, fileHeader))
    noDataPoints = int(fileHeaderDict['NoDataPoints'])
    iterationDF = pd.DataFrame(iterDataList, columns=['Iteration',  'Time for this iteration', 'Total Time', '%AbsError'])
    iterationDF = iterationDF.apply(pd.to_numeric)

    if verbose:
        print(iterationDF)
    if show_iterations:
        fig1, ax1 = plt.subplots(1)
        iterationDF.plot('Iteration','%AbsError',figsize=(3,3), ax=ax1, c='k')
        iterationDF.plot('Iteration','%AbsError',figsize=(3,3),kind='scatter', ax=ax1, c='k')
        ax1.set_title(inv_file.stem)
        ax1.set_xticks(np.arange(0,iterationDF['Iteration'].max()+1))
        ax1.get_legend().remove()
        plt.show(fig1)

    inv_dict = {
        'inv_file_Path':inv_file,
        'fileHeader':fileHeader,
        'iterationStartRowList':iterationStartRowList,
        'layerRowList':layerRowList, 
        'layerDepths':layerDepths,
        'noLayerRow':noLayerRow,
        'blockRow':blockRow ,
        'layerRow':layerRow ,
        'layerInfoRow':layerInfoRow ,
        'resistDF':resistDF,
        'dataList':dataList,
        'noPoints':noPoints,
        'calcResistivityRowList':calcResistivityRowList ,
        'refResistRow':refResistRow,
        'topoDataRow':topoDataRow,
        'iterationsInfoRow':iterationsInfoRow,
        'iterationDF':iterationDF,
        'noIterations':noIterations,
        'noDataPoints':noDataPoints,
        'shiftMatrixRow':shiftMatrixRow,
        'electrodeCoordsRow':electrodeCoordsRow,
        'noTopoPts':noTopoPts,
        'sensAndUncertainRow':sensAndUncertainRow,
        'noModelBlocks':[],
        'errorDistRow':errorDistRow,
        'fileHeaderDict':fileHeaderDict}
    
    return inv_dict
def map_diff(xIn, x1, x2, y1, y2)

Simple, linear interpolation between two points

Parameters

xIn : float, int, or numeric
X Location at which y-value is desired. This should fall between (or be equal to) x1 and x2
x1 : float, int, or numeric
Initial X location, with known y-value y1
x2 : float, int, or numeric
Second X location, with known y-value y2
y1 : float, int, or numeric
Known y-value at x1
y2 : float, int, or numeric
Known y-value at x2

Returns

float
Y-value that is proportionally scaled based on the xIn relative distance to x1 and x2
Expand source code
def map_diff(xIn, x1,x2,y1,y2):
    """Simple, linear interpolation between two points

    Parameters
    ----------
    xIn : float, int, or numeric
        X Location at which y-value is desired. This should fall between (or be equal to) x1 and x2
    x1 : float, int, or numeric
        Initial X location, with known y-value y1 
    x2 : float, int, or numeric
        Second X location, with known y-value y2
    y1 : float, int, or numeric
        Known y-value at x1
    y2 : float, int, or numeric
        Known y-value at x2

    Returns
    -------
    float
        Y-value that is proportionally scaled based on the xIn relative distance to x1 and x2
    """
    if x1==xIn:
        yOut=y1
    elif x2==xIn:
        yOut = y2
    else:
        totXDiff = x2-x1
        percXDiff = (xIn-x1)/totXDiff
        totYDiff = y2-y1
        yOut = y1 + totYDiff*percXDiff
    return yOut
def read_error_data(inv_file, inv_dict)

Function to read data pertaining to model error

Parameters

inv_file : str or pathlib.Path object
Filepath to .inv file of interest
inv_dict : dict
Dictionary containing cumulative output from ingest_inv, read_inv_data, and read_inv_data_other

Returns

dict
Ouptput dictionary containing all information from read_error_data and previous functions.
Expand source code
def read_error_data(inv_file, inv_dict):
    """Function to read data pertaining to model error

    Parameters
    ----------
    inv_file : str or pathlib.Path object
        Filepath to .inv file of interest
    inv_dict : dict 
        Dictionary containing cumulative output from ingest_inv, read_inv_data, and read_inv_data_other

    Returns
    -------
    dict
        Ouptput dictionary containing all information from read_error_data and previous functions.
    """
    import csv
    noDataPoints = inv_dict['noDataPoints']
    startRow = inv_dict['errorDistRow']+1

    with open(inv_file) as datafile: 
        filereader = csv.reader(datafile)
        inDataList = []
        for row in enumerate(filereader):
            if row[0] < startRow:
                continue
            elif row[0] < startRow+noDataPoints:
                newrow = row[1]
                newrow.append(newrow[4][8:])
                newrow[4] = newrow[4][0:8]
                newrow = [x.strip() for x in newrow]
                inDataList.append(newrow)
            else:
                break
    inDF = pd.DataFrame(inDataList)
    inv_dict['errDistDF'] = inDF

    errDistDF = inv_dict['errDistDF']

    colList=['xDist?','nFactor?','Measure1','Measure2','PercentError','MoreStacks','AvgMeasure']
    for i in range(0,5):
        errDistDF[i]=errDistDF[i].astype(np.float64)
        errDistDF.rename(columns={i:colList[i]},inplace=True)
    errDistDF['AvgMeasure'] = (errDistDF['Measure1']+errDistDF['Measure2'])/2
    inv_dict['errDistDF'] = errDistDF
    return inv_dict
def read_inv_data(inv_file, inv_dict, startRow=9)

Function to do initial read of .inv file.

This data does not change with iteration, as in later function. This function should be readafter ingest_inv, using the output from that as inv_dict.

Parameters

inv_file : str or pathlib.Path object
Filepath of .inv file
inv_dict : dict
Dictionary (output from ingest_inv) containing information about where data is located in the .inv file
startRow : int, optional
Where to start the read. This is, by default 9, which works well for .inv files tested.

Returns

_type_
description
Expand source code
def read_inv_data(inv_file, inv_dict, startRow=9):
    """Function to do initial read of .inv file. 
    
    This data does not change with iteration, as in later function. This function should be readafter ingest_inv, using the output from that as inv_dict.
     
    

    Parameters
    ----------
    inv_file : str or pathlib.Path object
        Filepath of .inv file
    inv_dict : dict
        Dictionary (output from ingest_inv) containing information about where data is located in the .inv file
    startRow : int, optional
        Where to start the read. This is, by default 9, which works well for .inv files tested.

    Returns
    -------
    _type_
        _description_
    """
    noDataPoints = inv_dict['noDataPoints']
    if isinstance(inv_file, pathlib.PurePath):
        pass
    else:
        inv_file = pathlib.Path(inv_file)

    import csv
    with open(inv_file) as datafile: 
        filereader = csv.reader(datafile)
        start = 0
        inDataList = []
        for row in enumerate(filereader):
            if row[0] < startRow:
                continue
            elif row[0] < startRow+noDataPoints:
                inDataList.append(re.sub('\s+',' ',row[1][0]).split(' '))
            else:
                break
    inDF = pd.DataFrame(inDataList)
    if startRow == 9:
        inDF.drop([0],inplace=True,axis=1)
    inDF.astype(np.float64)
    inDF.columns=['NoElectrodes', 'A(x)', 'A(z)', 'B(x)', 'B(z)', 'M(x)', 'M(z)', 'N(x)', 'N(z)', 'Data']
    inv_dict['resistDF']  = inDF
    return inv_dict
def read_inv_data_other(inv_file, inv_dict, iteration_no=None)

Function to read inversion data.

Parameters

inv_file : str or pathlib.Path object
Filepath to .inv file of interest.
inv_dict : dict
Dictionary contianing outputs from ingest_inv and read_in_data.
iteration_no : int
Iteration number of interest.

Returns

dict
Dictionary with more information appended to input dictionary
Expand source code
def read_inv_data_other(inv_file, inv_dict, iteration_no=None):
    """Function to read inversion data. 

    Parameters
    ----------
    inv_file : str or pathlib.Path object
        Filepath to .inv file of interest.
    inv_dict : dict
        Dictionary contianing outputs from ingest_inv and read_in_data.
    iteration_no : int
        Iteration number of interest.

    Returns
    -------
    dict
        Dictionary with more information appended to input dictionary
    """
    if iteration_no is None:
        print('Please run read_inv_data_other again and specify iteration by setting iteration_no parameter equal to integer.')
        return
    #Extract needed variables from dict
    iterationInd = inv_dict['iterationDF'][inv_dict['iterationDF'].Iteration==iteration_no].index.tolist()[0]
    inv_dict['iterationNo'] = iteration_no
    inv_dict['iterationInd'] = iterationInd


    invDF = inv_dict['resistDF']
    layerRowList = inv_dict['layerRowList']
    noPoints = inv_dict['noPoints']
    layerDepths = inv_dict['layerDepths']
    shiftMatrixRow = inv_dict['shiftMatrixRow']
    electrodeCoordsRow = inv_dict['electrodeCoordsRow']
    topoDataRow = inv_dict['topoDataRow']
    noTopoPts = inv_dict['noTopoPts']
    sensAndUncertainRow = inv_dict['sensAndUncertainRow']

    #Get Electrodes
    electrodes= pd.concat([invDF['A(x)'],invDF['B(x)'], invDF['M(x)'], invDF['B(x)'], invDF['N(x)']],ignore_index=True)
    electrodes.reset_index(inplace=True, drop=True)
    electrodes = electrodes.unique()
    inv_dict['electrodes'] = electrodes

    #ElectrodeCoordinates
    noModelElects = shiftMatrixRow-electrodeCoordsRow-1
    electrodeCoordsDF = pd.read_table(inv_file,skiprows=electrodeCoordsRow,nrows=noModelElects, sep='\s+')
    electrodeCoordsDF.dropna(axis=1,inplace=True)
    electrodeCoordsDF.columns=['xDist','RelElevation']
    electrodeCoordsDF['ElectrodeNo'] = electrodeCoordsDF.index+1
    inv_dict['electrodeCoordsDF'] = electrodeCoordsDF

    #Topographical Data
    topoDF = pd.read_table(inv_file,skiprows=topoDataRow+2,nrows=noTopoPts, sep='\s+')
    topoDF.reset_index(inplace=True)
    topoDF.columns=['xDist','Elevation']
    topoDF['ElectrodeNo'] = topoDF.index+1
    inv_dict['topoDF'] = topoDF

    #Resistivity Model
    resistModelDF = pd.DataFrame()
    for r in enumerate(layerRowList[iterationInd]):
        layerDepth = layerDepths[r[0]]
        noPtsInLyr = noPoints[r[0]]
        currDF = pd.read_table(inv_file,skiprows=r[1]+1, nrows=noPtsInLyr,sep=',')
        currDF.columns=['ElectrodeNo','Data']
        currDF['z'] = layerDepth
        resistModelDF= pd.concat([resistModelDF,currDF],ignore_index=True).copy()
    resistModelDF.reset_index(inplace=True, drop=True)
    noModelBlocks=resistModelDF.shape[0]
    inv_dict['resistModelDF'] = resistModelDF
    inv_dict['noModelBlocks'] = noModelBlocks

    #Shift Matrix
    shiftMatrixDF = pd.read_table(inv_file,skiprows=shiftMatrixRow+1,nrows=noModelElects, sep='\s+',header=None,index_col=0)
    shiftMatrixDF.dropna(axis=1,inplace=True)
    for c in shiftMatrixDF:
        shiftMatrixDF.rename(columns={c:'Layer'+str(int(c)-1)},inplace=True)
    inv_dict['shiftMatrixDF'] = shiftMatrixDF

    #Sensitivity
    sensDF = pd.read_table(inv_file,skiprows=sensAndUncertainRow+3,nrows=noModelBlocks, sep='\s+',header=None,index_col=0)
    sensDF.dropna(axis=1,inplace=True)
    sensDF.reset_index(inplace=True)
    sensDF.columns=['BlockNo','Sensitivity', '%ApproxUncertainty']
    inv_dict['sensDF'] = sensDF

    return inv_dict
def resinv_plot(inv_dict, colMap='nipy_spectral', cBarFormat='%3.0f', cBarLabel='Resistivity (ohm-m)', cBarOrientation='horizontal', cMin=None, cMax=None, griddedFt=[False, False], griddedM=[False, False], title=None, normType='log', primaryUnit='m', showPoints=False, whichTicks='major', figsize=None, dpi=None, reverse=False, tight_layout=True, savefig=False, saveformat='png', imshow_kwargs=None, **kwargs)

Function to pull everything together and plot it nicely.

It is recommended to use the autoplot function rather than resinv_plot directly, since using autoplot() incorporates all the setup needed to create the input dictionary keys/values correctly.

Parameters

inv_dict : dict
Dictionary of inversion results generated from previous steps
colMap : str, optional
Colormap, any acceptable from matplotlib, by default 'nipy_spectral'
cBarFormat : str, optional
Format string for colorbar tick labels, by default '%3.0f'
cBarLabel : str, optional
Colorbar label, by default 'Resistivity (ohm-m)'
cBarOrientation : str {'horizonta', 'vertical'}, optional
Orientation of the colorbar, by default 'horizontal'
cMin : float, optional
Minimum of colorbar/colormap, by default None, which uses the minimum value of the dataset.
cMax : float, optional
Maximum of colorbar/colormap, by default None, which uses the maximum value of the dataset.
griddedFt : list, optional
Whether to show gridlines on the feet ticks, first position is x, second position is y, by default [False,False]
griddedM : list, optional
Whether to show gridlines on the meter tickes, first position is x, second posistion is y, by default [False,False]
title : str, optional
String to show as the title, if desired to set manually, by default None, which shows the filename as the title
normType : str {'log', 'linear'}, optional
Normalization type, by default 'log'. Determines whether matplotlib.colors.LogNorm or matplotlib.colors.Normalize is used for colormap.
primaryUnit : str {'m', 'ft'}, optional
Whether to display meters or feet as primary unit (this determines which unit is larger on the axis and is on the left and top), by default 'm'
showPoints : bool, optional
Whether to show the datapoints used for interpolation, by default False
whichTicks : str {'major', 'minor', 'both'}, optional
If griddedFt or griddedM has any True, this determines whether major, minor, or both gridlines are used; by default 'major'.
figsize : tuple, optional
Tuple (width, height) of the figsize, read into plt.rcParams['figure.figsize'], by default None.
dpi : int or float, optional
Resolution (dots per square inch) of final figure, read into plt.rcParams['figure.dpi'], by default None.
reverse : bool, optional
Whether to display the data in reverse (flipped along x) of what is read into from .inv file, by default False
tight_layout : bool, optional
If true, calls fig.tight_layout(). Otherwise, tries to maximize space on the figure using plt.subplots_adjust, by default True
savefig : bool, optional
If False, will not save figure. Otherwise, calls plt.savefig() and the value of this parameter will be used as the output filepath, by default False.
saveformat : str, optional
Read into plt.savefig(format) paramater, by default 'png'.

Returns

dict
Returns existing inv_dict input, but with added keys of ['fig'] and ['ax'] containing a list of the fig and ax objects (list, since multiple iterations can be done at once)
Expand source code
def resinv_plot(inv_dict, colMap='nipy_spectral', cBarFormat ='%3.0f', cBarLabel='Resistivity (ohm-m)', cBarOrientation='horizontal', cMin=None, cMax=None, griddedFt=[False,False], griddedM=[False,False], title=None, normType='log', primaryUnit='m', showPoints=False,whichTicks='major', figsize=None, dpi=None, reverse=False, tight_layout=True, savefig=False, saveformat='png', imshow_kwargs=None, **kwargs):
    """Function to pull everything together and plot it nicely.

    It is recommended to use the autoplot function rather than resinv_plot directly, since using autoplot() incorporates all the setup needed to create the input dictionary keys/values correctly.

    Parameters
    ----------
    inv_dict : dict
        Dictionary of inversion results generated from previous steps
    colMap : str, optional
        Colormap, any acceptable from matplotlib, by default 'nipy_spectral'
    cBarFormat : str, optional
        Format string for colorbar tick labels, by default '%3.0f'
    cBarLabel : str, optional
        Colorbar label, by default 'Resistivity (ohm-m)'
    cBarOrientation : str {'horizonta', 'vertical'}, optional
        Orientation of the colorbar, by default 'horizontal'
    cMin : float, optional
        Minimum of colorbar/colormap, by default None, which uses the minimum value of the dataset.
    cMax : float, optional
        Maximum of colorbar/colormap, by default None, which uses the maximum value of the dataset.
    griddedFt : list, optional
        Whether to show gridlines on the feet ticks, first position is x, second position is y, by default [False,False]
    griddedM : list, optional
        Whether to show gridlines on the meter tickes, first position is x, second posistion is y, by default [False,False]
    title : str, optional
        String to show as the title, if desired to set manually, by default None, which shows the filename as the title
    normType : str {'log', 'linear'}, optional
        Normalization type, by default 'log'. Determines whether matplotlib.colors.LogNorm or matplotlib.colors.Normalize is used for colormap.
    primaryUnit : str {'m', 'ft'}, optional
        Whether to display meters or feet as primary unit (this determines which unit is larger on the axis and is on the left and top), by default 'm'
    showPoints : bool, optional
        Whether to show the datapoints used for interpolation, by default False
    whichTicks : str {'major', 'minor', 'both'}, optional
        If griddedFt or griddedM has any True, this determines whether major, minor, or both gridlines are used; by default 'major'.
    figsize : tuple, optional
        Tuple (width, height) of the figsize, read into plt.rcParams['figure.figsize'], by default None.
    dpi : int or float, optional
        Resolution (dots per square inch) of final figure, read into plt.rcParams['figure.dpi'], by default None.
    reverse : bool, optional
        Whether to display the data in reverse (flipped along x) of what is read into from .inv file, by default False
    tight_layout : bool, optional
        If true, calls fig.tight_layout(). Otherwise, tries to maximize space on the figure using plt.subplots_adjust, by default True
    savefig : bool, optional
        If False, will not save figure. Otherwise, calls plt.savefig() and the value of this parameter will be used as the output filepath, by default False.
    saveformat : str, optional
        Read into plt.savefig(format) paramater, by default 'png'.

    Returns
    -------
    dict
        Returns existing inv_dict input, but with added keys of ['fig'] and ['ax'] containing a list of the fig and ax objects (list, since multiple iterations can be done at once)
    """
    if title is None:
        title = inv_dict['inv_file_Path'].stem
    
    if 'figure.dpi' not in list(inv_dict.keys()):
        inv_dict['figure.dpi'] = 250
    if 'figure.figsize' not in list(inv_dict.keys()):
        inv_dict['figure.figsize'] = (12,5)

    x = inv_dict['resistModelDF']['x'].copy()
    z = inv_dict['resistModelDF']['zElev'].copy()
    v = inv_dict['resistModelDF']['Data'].copy()

    if figsize is None:
        plt.rcParams['figure.figsize'] = inv_dict['figure.figsize']
    else:
        inv_dict['figure.figsize'] = figsize
        plt.rcParams['figure.figsize'] = figsize

    if dpi is None:
        plt.rcParams['figure.dpi'] = inv_dict['figure.dpi']
    else:
        inv_dict['figure.dpi'] = dpi
        plt.rcParams['figure.dpi'] = dpi
    
    maxXDist = max(np.float_(inv_dict['electrodes']))

    if cMin is None:
        cMin = inv_dict['resistModelDF']['Data'].min()
    if cMax is None:
        cMax = inv_dict['resistModelDF']['Data'].max()
    

    for i in enumerate(v):
        v[i[0]] = abs(float(i[1]))

    xi, zi = np.linspace(min(x), max(x), int(max(x))), np.linspace(min(z), max(z), int(max(z)))
    xi, zi = np.meshgrid(xi, zi)

    vi = scipy.interpolate.griddata((x, z), v, (xi, zi))#, method='linear')

    ptSize = round(100 / maxXDist * 35, 1)

    fig, axes = plt.subplots(1)
    cmap = matplotlib.cm.binary
    my_cmap = cmap(np.arange(cmap.N))
    my_cmap[:,-1] = np.linspace(0,1,cmap.N)
    my_cmap = matplotlib.colors.ListedColormap(my_cmap)

    vmax98 = np.percentile(v, 98)
    vmin2 = np.percentile(v, 2)
    minx = min(x)
    maxx = max(x)
    minz = min(z)
    maxz = max(z)

    vmax = cMax
    vmin = cMin

    #if cMax >= resistModelDF['Data'].max():
    #  vmax = vmax98
    #else:
    #  vmax = cMax
    #if cMin <= resistModelDF['Data'].min():
    #  vmin = vmin2
    #else:
    #  vmin = cMin
    #cbarTicks = np.arange(np.round(vmin,-1),np.round(vmax-1)+1,10) 

    arStep = np.round((vmax-vmin)/10,-1)
    cbarTicks = np.arange(np.round(vmin, -1), np.ceil(vmax/10)*10,arStep)

    #Get default values or kwargs, depending on if kwargs have been used
    if 'norm' in imshow_kwargs.keys():
        norm = imshow_kwargs['norm']
    else:
        if normType=='log':
            if vmin <= 0:
                vmin = 0.1
            norm = matplotlib.colors.LogNorm(vmin = vmin, vmax = vmax)
            #cBarFormat = '%.1e'
            #cbarTicks = np.logspace(np.log10(vmin),np.log10(vmax),num=10)
        else:
            norm = matplotlib.colors.Normalize(vmin = vmin, vmax = vmax)

    #im = self.axes.imshow(vi, vmin=vmin, vmax=vmax, origin='lower',
    if 'extent' in imshow_kwargs.keys():
        extent = imshow_kwargs['extent']
        imshow_kwargs.pop('extent', None)
    else:
        extent = [minx, maxx, minz, maxz]
        
    if 'aspect' in imshow_kwargs.keys():
        aspect = imshow_kwargs['aspect']
        imshow_kwargs.pop('aspect', None)
    else:
        aspect = 'auto'
        
    if 'cmap' in imshow_kwargs.keys():
        cmap = imshow_kwargs['cmap']
        imshow_kwargs.pop('cmap', None)
    else:
        cmap=colMap   
        
    if 'interpolation' in imshow_kwargs.keys():
        interp = imshow_kwargs['interpolation']
        imshow_kwargs.pop('interpolation', None)
    else:
        interp='spline36'   
    
    im = axes.imshow(vi, origin='lower',
                extent=extent,
                aspect=aspect,
                cmap =cmap,
                norm = norm,
                interpolation=interp, **imshow_kwargs)
    f, a = __plot_pretty(inv_dict, x,z,v,fig=fig,im=im,ax=axes,colMap=colMap,cMin=cMin,cMax=cMax, 
                       gridM=griddedM, gridFt=griddedFt, primaryUnit=primaryUnit, t=title, tight_layout=tight_layout,
                       cbarTicks=cbarTicks,cBarFormat=cBarFormat,cBarLabel=cBarLabel,cBarOrient=cBarOrientation,
                       showPoints=showPoints, norm=norm, whichTicks=whichTicks, reverse=reverse)

    plt.show(fig)
    if savefig is not False:
        plt.savefig(savefig, format=saveformat, facecolor='white')

    plt.close(f)
    return f, a