Exemple #1
0
    def test_NumpyToScalar(self):
        """Implemented through automatic iterator """
        x = pg.RVector(2)
        x3 = pg.R3Vector(2)
        w = pg.RVector()

        x += np.float32(1.0)
        np.testing.assert_equal(sum(x + 1.0), 4.0)
        np.testing.assert_equal(sum(x + np.float32(1)), 4.0)
        np.testing.assert_equal(sum(x + np.float64(1)), 4.0)
        np.testing.assert_equal(sum(x - 1.0), 0.0)
        np.testing.assert_equal(sum(x - np.float32(1)), 0.0)
        np.testing.assert_equal(sum(x - np.float64(1)), 0.0)

        # HarmonicModelling(size_t nh, const RVector & tvec);
        pg.HarmonicModelling(np.int32(1), x)
        pg.HarmonicModelling(np.uint32(1), x)
        pg.HarmonicModelling(np.int64(1), x)
        pg.HarmonicModelling(np.uint64(1), x)

        # pg.PolynomialModelling(1, np.int32(1), x3, x);
        # pg.PolynomialModelling(1, np.int64(1), x3, x);
        # pg.PolynomialModelling(1, np.uint32(1), x3, x);
        # pg.PolynomialModelling(1, np.uint64(1), x3, x);

        x = pg.Pos(0.0, 0.0, 0.0)
        x += np.float32(1)

        np.testing.assert_equal(x, pg.Pos(1.0, 1.0, 1.0))
        np.testing.assert_equal(x - 1, pg.Pos(0.0, 0.0, 0.0))
        np.testing.assert_equal(x - np.float32(1), pg.Pos(0.0, 0.0, 0.0))
        np.testing.assert_equal(x - np.float64(1), pg.Pos(0.0, 0.0, 0.0))
def uAnalytical(p, sourcePos, k, sigma=1):
    """Calculates the analytical solution for the 2.5D geoelectrical problem.

    Solves the 2.5D geoelectrical problem for one wave number k.
    It calculates the normalized (for injection current 1 A and sigma=1 S/m)
    potential at position p for a current injection at position sourcePos.
    Injection at the subsurface is recognized via mirror sources along the
    surface at depth=0.

    Parameters
    ----------
    p : pg.Pos
        Position for the sought potential
    sourcePos : pg.Pos
        Current injection position.
    k : float
        Wave number

    Returns
    -------
    u : float
        Solution u(p)
    """
    r1A = (p - sourcePos).abs()
    # Mirror on surface at depth=0
    r2A = (p - pg.Pos([1.0, -1.0]) * sourcePos).abs()

    if r1A > 1e-12 and r2A > 1e-12:
        return  1 / (2.0 * np.pi) * 1/sigma * \
            (pg.math.besselK0(r1A * k) + pg.math.besselK0(r2A * k))
    else:
        return 0.
Exemple #3
0
    def createElectrodes(self,
                         nElectrodes=24,
                         electrodeSpacing=1,
                         sensorList=None):
        self.data_ = pg.DataContainerERT()

        if sensorList is not None:
            for p in sensorList:
                if isinstance(p, float):
                    self.data_.createSensor((p, 0.))
                else:
                    self.data_.createSensor(p)
        else:
            for i in range(nElectrodes):
                self.data_.createSensor(
                    pg.Pos(float(i) * electrodeSpacing, 0.0))

        self.nElectrodes_ = self.data_.sensorCount()
def mixedBC(boundary, userData):
    """Mixed boundary conditions.

    Define the derivative of the analytical solution regarding the outer normal
    direction :math:`\vec{n}`. So we can define the values for mixed boundary
    condition :math:`\frac{\partial u}{\partial \vec{n}} = -au`
    for the boundaries on the subsurface.

    """
    ### ignore surface boundaries for wildcard boundary condition
    if boundary.norm()[1] == 1.0:
        return 0

    sourcePos = userData['sourcePos']
    k = userData['k']
    sigma = userData['s']
    r1 = boundary.center() - sourcePos

    # Mirror on surface at depth=0
    r2 = boundary.center() - pg.Pos(1.0, -1.0) * sourcePos
    r1A = r1.abs()
    r2A = r2.abs()

    n = boundary.norm()
    if r1A > 1e-12 and r2A > 1e-12:
        alpha = sigma * k * ((r1.dot(n)) / r1A * pg.math.besselK1(r1A * k) +
                            (r2.dot(n)) / r2A * pg.math.besselK1(r2A * k)) / \
            (pg.math.besselK0(r1A * k) + pg.math.besselK0(r2A * k))

        return alpha

        # Note, the above is the same like:
        beta = 1.0
        return [alpha, beta, 0.0]

    else:
        return 0.0
Exemple #5
0
    def test_PosConstMember(self):
        p1 = pg.Pos(1.0, 0.0, 0.0)
        p2 = pg.Pos(0.0, 1.0, 0.0)

        p3 = p1.cross(p2)
        self.assertEqual(p3, pg.Pos(0.0, 0.0, 1.0))
Exemple #6
0
def streamlineDir(mesh,
                  field,
                  startCoord,
                  dLengthSteps,
                  dataMesh=None,
                  maxSteps=150,
                  down=True,
                  verbose=False,
                  coords=(0, 1)):
    """
        down = -1, up = 1, both = 0
    """
    xd = []
    yd = []
    vd = []

    pot = None
    vx = None
    vy = None
    isVectorData = False

    if isinstance(field, pg.core.R3Vector):
        field = field.array()

    if hasattr(field[0], '__len__'):
        if abs(max(field[:, 0])) == 0 and abs(max(field[:, 1]) == 0):
            raise Exception("No data range streamline: min/max == ",
                            min(field[:, 0]))

        vx = pg.Vector(field[:, 0])
        vy = pg.Vector(field[:, 1])

        isVectorData = True
    else:
        if min(field) == max(field):
            raise Exception(
                "No scalar data range for any gradients "
                " to draw a streamline: min/max == ", min(field))

        if dataMesh is not None:
            if len(field) == dataMesh.nodeCount():
                pot = pg.Vector(field)
            elif len(field) == dataMesh.cellCount():
                pot = pg.core.cellDataToPointData(dataMesh, field)
            else:
                print(len(field), dataMesh)
                raise Exception(
                    "Data length (%i) for streamline is "
                    "neighter nodeCount (%i) nor cellCount (%i)" %
                    (len(field), dataMesh.nodeCount(), dataMesh.nodeCount()))
        else:
            if len(field) == mesh.nodeCount():
                pot = pg.Vector(field)
            elif len(field) == mesh.cellCount():
                pot = pg.core.cellDataToPointData(mesh, field)
            else:
                print(len(field), dataMesh)
                raise Exception(
                    "Data length (%i) for streamline is "
                    "neighter nodeCount (%i) nor cellCount (%i)" %
                    (len(field), mesh.nodeCount(), mesh.nodeCount()))

    direction = 1
    if down:
        direction = -1

    # search downward
    pos = pg.RVector3(startCoord)
    c = mesh.findCell(startCoord)
    dLength = c.center().dist(c.node(0).pos()) / dLengthSteps

    # stream line starting point
    if c is not None:
        xd.append(pos[coords[0]])
        yd.append(pos[coords[1]])
        vd.append(-1)

    lastC = c
    lastU = -direction * 1e99

    d = None
    while c is not None and len(xd) < maxSteps:

        # valid .. temporary check if there is already a stream within the cell
        if not c.valid():
            break

        if isVectorData:
            u = 0.
            if len(vx) == mesh.cellCount():
                d = pg.RVector3(vx[c.id()], vy[c.id()])
            elif len(vx) == mesh.nodeCount():
                d = pg.RVector3(c.pot(pos, vx), c.pot(pos, vy))
            elif dataMesh:
                cd = dataMesh.findCell(pos)
                if cd is None:
                    raise Exception("Cannot find " + str(pos) + " dataMesh")
                if len(vx) == dataMesh.cellCount():
                    d = pg.RVector3(vx[cd.id()], vy[cd.id()])
                elif len(vx) == dataMesh.nodeCount() and \
                        len(vy) == dataMesh.nodeCount():
                    d = pg.RVector3(cd.pot(pos, vx), cd.pot(pos, vy))
                else:
                    print(dataMesh, len(vx), len(vy))
                    raise Exception("data size wrong")
            else:
                print("mesh:", mesh, len(vx), len(vy))
                raise Exception("Data length neighter node size or cell size.")
        else:
            if dataMesh:
                cd = dataMesh.findCell(pos)
                if not cd:
                    break

                d = cd.grad(pos, pot)
                u = cd.pot(pos, pot)
            else:
                d = c.grad(pos, pot)
                u = c.pot(pos, pot)

        # always go u down
        dAbs = d.length()
        #print("cell:", c.id(), u, d, dAbs)

        if dAbs == 0.0:
            #print(d, "check this in streamlineDir(",)
            break

        if down:
            if u > lastU:
                break
        else:
            if u < lastU:
                break

        # * min(1.0, ((startCoord - pos).length()))
        pos += direction * d / dAbs * dLength
        c = mesh.findCell(pos, False)

        # Change cell here .. set old cell to be processed
        if c is not None:
            xd.append(pos[coords[0]])
            yd.append(pos[coords[1]])
            # set the stating value here
            if vd[0] == -1:
                vd[0] = dAbs
            vd.append(dAbs)

            ## check for degenerating stream
            if len(xd) > 2:
                pos0 = pg.Pos(xd[-3], yd[-3])
                pos1 = pg.Pos(xd[-2], yd[-2])
                pos2 = pg.Pos(xd[-1], yd[-1])
                if (pos0.dist(pos2) < pos0.dist(pos1)):
                    pg.debug('degenerating stream aborted')
                    break

            # If the new cell is different from the current we move into the
            # new cell and make the last to be invalid ..
            # the last active contains a stream element
            if c.id() != lastC.id():
                lastC.setValid(False)
                lastC = c
                dLength = c.center().dist(c.node(0).pos()) / dLengthSteps
        else:
            # There is no new cell .. the last active contains a stream element
            lastC.setValid(False)

        lastU = u
        if verbose:
            print(pos, u)

    # Stream line has stopped and the current cell (if there is one) ..
    # .. contains a stream element
    if c is not None:

        c.setValid(False)

    if down:
        xd.reverse(), yd.reverse(), vd.reverse()

    return xd, yd, vd
Exemple #7
0
def appendTriangleBoundary(mesh,
                           xbound=10,
                           ybound=10,
                           marker=1,
                           isSubSurface=True,
                           **kwargs):
    """Add a triangle mesh boundary to a given mesh.

    Returns a new mesh that contains a triangulated box around a given mesh
    suitable for geo-simulation (surface boundary with marker = -1  at top and
    marker = -2 in the inner subsurface). The old boundary marker from mesh
    will be preserved, except for marker == -2 which will be switched to 2 as
    we assume -2 is the world marker for outer boundaries in the subsurface.

    Note that this all will only work stable if the mesh generator (triangle)
    preserve all input boundaries. This will lead to bad quality meshes for the
    boundary region so its a good idea to play with the addNodes keyword
    argument to manually refine the newly created outer boundaries.

    Parameters
    ----------
    mesh: :gimliapi:`GIMLI::Mesh`
        Mesh to which the triangle boundary should be appended.
    xbound: float, optional
        Absolute horizontal prolongation distance.
    ybound: float, optional
        Absolute vertical prolongation distance.
    marker: int, optional
        Marker of new cells.
    isSubSurface: boolean [True]
        Apply boundary conditions suitable for geo-simulation and prolongate
        mesh to the surface if necessary.

    Additional Args
    ---------------
    ** kargs forwarded to pg.createMesh

    quality : float, optional
        Triangle quality.
    area: float, optional
        Triangle max size within the boundary.
    smooth : boolean, optional
        Apply mesh smoothing.

    addNodes : int[5], iterable
        Add additional nodes on the outer boundaries. Or for each boundary
        if given 5 values (isSubsurface=True) or 4 for isSubsurface=False

    Returns
    -------
    :gimliapi:`GIMLI::Mesh`
        A new 2D mesh containing the original mesh and a boundary arround.

    See Also
    --------
    :py:mod:`pygimli.meshtools.appendBoundary`
    :py:mod:`pygimli.meshtools.appendTetrahedronBoundary`

    Examples
    --------
    >>> import matplotlib.pyplot as plt
    >>> import pygimli as pg
    >>> from pygimli.viewer.mpl import drawMesh, drawModel
    >>> import pygimli.meshtools as mt
    >>> inner = pg.createGrid(range(5), range(5), marker=1)
    >>> fig, axs = plt.subplots(2,3)
    >>> ax, _ = pg.show(inner, markers=True, showBoundaries=True, showMesh=True, ax=axs[0][0])
    >>> m = mt.appendTriangleBoundary(inner, xbound=3, ybound=3, marker=2, addNodes=0, isSubSurface=False)
    >>> ax, _ = pg.show(m, markers=True, showBoundaries=True, showMesh=True, ax=axs[0][1])
    >>> m = mt.appendTriangleBoundary(inner, xbound=4, ybound=1, marker=2, addNodes=5, isSubSurface=False)
    >>> ax, _ = pg.show(m, markers=True, showBoundaries=True, showMesh=True, ax=axs[0][2])
    >>> m = mt.appendTriangleBoundary(inner, xbound=4, ybound=4, marker=2, addNodes=0, isSubSurface=True)
    >>> ax, _ = pg.show(m, markers=True, showBoundaries=True, showMesh=True, ax=axs[1][0])
    >>> m = mt.appendTriangleBoundary(inner, xbound=4, ybound=4, marker=2, addNodes=5, isSubSurface=True)
    >>> ax, _ = pg.show(m, markers=True, showBoundaries=True, showMesh=True, ax=axs[1][1])
    >>> surf = mt.createPolygon([[0, 0],[5, 3], [10, 1]], boundaryMarker=-1, addNodes=5, interpolate='spline')
    >>> m = mt.appendTriangleBoundary(surf, xbound=4, ybound=4, marker=2, addNodes=5, isSubSurface=True)
    >>> ax, _ = pg.show(m, markers=True, showBoundaries=True, showMesh=True, ax=axs[1][2])
    """
    poly = pg.Mesh(isGeometry=True)

    if isSubSurface:

        bs = mesh.findBoundaryByMarker(pg.core.MARKER_BOUND_HOMOGEN_NEUMANN)

        if len(bs) == 0:
            for b in mesh.boundaries():
                if b.outside() and b.norm()[1] == 1.0:
                    bs.append(b)

        paths = mesh.findPaths(bs)

        if len(paths) > 0:
            startPoint = mesh.node(paths[0][0]).pos()
            endPoint = mesh.node(paths[0][-1]).pos()

            if startPoint[0] > endPoint[0]:
                startPoint = endPoint
                endPoint = mesh.node(paths[0][0]).pos()
        else:
            pg.critical("Can't identify upper part of the mesh to be moved to"
                        "the surface. Maybe define them with Marker==-1")

        addNodes = kwargs.pop('addNodes', 5)

        boundPoly = [pg.Pos(startPoint)]

        if isinstance(addNodes, (float, int)) and addNodes > 0:
            addNodes = np.full(5, addNodes)

        if hasattr(addNodes, '__len__') and len(addNodes) == 5:
            boundPoly.extend([
                boundPoly[-1] - pg.Pos(x, 0) for x in pg.utils.grange(
                    1, xbound, n=addNodes[0] + 1, log=True)
            ])
            boundPoly.extend([
                boundPoly[-1] - pg.Pos(0, y)
                for y in np.linspace(0,
                                     mesh.ymax() - mesh.ymin() +
                                     ybound, addNodes[1] + 1)[1:]
            ])
            boundPoly.extend([
                boundPoly[-1] + pg.Pos(x, 0)
                for x in np.linspace(0, (endPoint - startPoint)[0] +
                                     2 * xbound, addNodes[2] + 1)[1:]
            ])
            boundPoly.extend([
                boundPoly[-1] + pg.Pos(0, y)
                for y in np.linspace(0, endPoint[1] -
                                     boundPoly[-1][1], addNodes[3] + 1)[1:]
            ])
            boundPoly.extend([
                boundPoly[-1] - pg.Pos(xbound - x, 0) for x in pg.utils.grange(
                    1, xbound, n=addNodes[4] + 1, log=True)[::-1][1:]
            ])
        else:
            boundPoly.append(boundPoly[-1] - pg.Pos(xbound, 0))
            boundPoly.append(boundPoly[-1] -
                             pg.Pos(0,
                                    mesh.ymax() - mesh.ymin() + ybound))
            boundPoly.append(boundPoly[-1] +
                             pg.Pos((endPoint - startPoint)[0] +
                                    2 * xbound, 0))
            boundPoly.append(pg.Pos(endPoint) + pg.Pos(xbound, 0))

        boundPoly.append(pg.Pos(endPoint))

        poly = pg.meshtools.createPolygon(boundPoly, isClosed=False)

        poly.addRegionMarker(pg.Pos([poly.xmin(), poly.ymin()]) +
                             [xbound / 100, ybound / 100],
                             marker=marker)

        if mesh.cellCount() > 0:
            poly.addHoleMarker(
                pg.Pos([mesh.xmin(), mesh.ymin()]) + [0.001, 0.001])

    else:  # no isSubSurface

        boundPoly = [[mesh.xmin() - xbound,
                      mesh.ymin() - ybound],
                     [mesh.xmin() - xbound,
                      mesh.ymax() + ybound],
                     [mesh.xmax() + xbound,
                      mesh.ymax() + ybound],
                     [mesh.xmax() + xbound,
                      mesh.ymin() - ybound]]

        poly = pg.meshtools.createPolygon(boundPoly,
                                          isClosed=True,
                                          marker=marker,
                                          addNodes=kwargs.pop('addNodes', 5))

    for b in mesh.boundaries():
        if b.outside() or b.marker() == -1:
            poly.copyBoundary(b)

    preserveSwitch = 'Y'
    # pg.show(poly, boundaryMarkers=True, showNodes=True)
    # pg.wait()

    mesh2 = pg.meshtools.createMesh(poly,
                                    preserveBoundary=preserveSwitch,
                                    **kwargs)

    # pg.show(mesh2, boundaryMarkers=True, showNodes=True)

    # start extracting all cells with marker from mesh2 and all orginal cells
    mesh3 = pg.Mesh(2)

    for c in mesh2.cells():
        if c.marker() == marker:
            mesh3.copyCell(c)

    # ! map copies the cell not the reference, this should not happen
    # **TODO check 20210305
    # map(lambda cell: mesh2.copyCell(cell), mesh2.cells())
    for c in mesh.cells():
        mesh3.copyCell(c)

    # we need to delete the old boundary markers or the new neighbour infos
    # will fail for old outside boundaries
    mesh3.setBoundaryMarkers(np.zeros(mesh3.boundaryCount()))
    mesh3.createNeighborInfos(force=True)

    for b in mesh.boundaries():
        if b.marker() != 0:
            b2 = mesh3.copyBoundary(b)

            # some automagic: original mesh contains bmarker == -2 which means
            # mixed condition, this special marker will be switched to 2
            if b.marker() == -2:
                b2.setMarker(2)

    for b in mesh3.boundaries():
        if b.outside() and b.marker() > -1:
            if b.norm().x() != 0 or b.norm().y() == -1.0:
                b.setMarker(pg.core.MARKER_BOUND_MIXED)
            else:
                b.setMarker(pg.core.MARKER_BOUND_HOMOGEN_NEUMANN)

    return mesh3
Exemple #8
0
def importRes2dInv(filename, verbose=False, return_header=False):
    """Read res2dinv format

    Parameters
    ----------
    filename : str
    verbose : bool [False]
    return_header : bool [False]

    Returns
    -------
    pg.DataContainerERT and (in case of return_header=True)
    header dictionary

    Format
    ------
        str - title
        float - unit spacing [m]
        int - Array Number (1-Wenner, 3-Dipole-dipole atm only)
        int - Number of Datapoints
        float - x-location given in terms of first electrode
                use 1 if mid-point location is given
        int - 0 for no IP, use 1 if IP present
        str - Phase Angle  if IP present
        str - mrad if IP present
        0,90.0 - if IP present
        dataBody
    """

    def getNonEmptyRow(i, comment='#'):
        s = next(i)
        while s[0] is comment:
            s = next(i)
        return s.split('\r\n')[0]
    # def getNonEmptyRow(...)

    with open(filename, 'r') as fi:
        content = fi.readlines()

    it = iter(content)
    header = {}
    header['name'] = getNonEmptyRow(it, comment=';')
    header['spacing'] = float(getNonEmptyRow(it, comment=';'))
    typrow = getNonEmptyRow(it, comment=';')
    typ = int(typrow.rstrip('\n').rstrip('R').rstrip('L'))

    if typ == 11:
        # independent electrode positions
        header['subtype'] = int(getNonEmptyRow(it, comment=';'))
        header['dummy'] = getNonEmptyRow(it, comment=';')
        isR = int(getNonEmptyRow(it, comment=';'))

    nData = int(getNonEmptyRow(it, comment=';'))
    xLoc = float(getNonEmptyRow(it, comment=';'))
    hasIP = int(getNonEmptyRow(it, comment=';'))

    if hasIP:
        header['ipQuantity'] = getNonEmptyRow(it, comment=';')
        header['ipUnit'] = getNonEmptyRow(it, comment=';')
        header['ipData'] = getNonEmptyRow(it, comment=';')
        ipline = header['ipData'].rstrip('\n').rstrip('\r').split(' ')
        if len(ipline) > 2:  # obviously spectral data?
            header['ipNumGates'] = int(ipline[0])
            header['ipDelay'] = float(ipline[1])
            header['onTime'] = float(ipline[-2])
            header['offTime'] = float(ipline[-1])
            header['ipDT'] = np.array(ipline[2:-2], dtype=float)
            header['ipGateT'] = np.cumsum(np.hstack((header['ipDelay'],
                                                     header['ipDT'])))

    data = pg.DataContainerERT()
    data.resize(nData)

    if typ == 9 or typ == 10:
        raise Exception("Don't know how to read:" + str(typ))

    if typ == 11 or typ == 12 or typ == 13:  # mixed array

        res = pg.Vector(nData, 0.0)
        ip = pg.Vector(nData, 0.0)
        specIP = []

        for i in range(nData):
            vals = getNonEmptyRow(it, comment=';').replace(',', ' ').split()

            # row starts with 4
            if int(vals[0]) == 4:
                eaID = data.createSensor(pg.Pos(float(vals[1]),
                                                float(vals[2])))
                ebID = data.createSensor(pg.Pos(float(vals[3]),
                                                float(vals[4])))
                emID = data.createSensor(pg.Pos(float(vals[5]),
                                                float(vals[6])))
                enID = data.createSensor(pg.Pos(float(vals[7]),
                                                float(vals[8])))
            elif int(vals[0]) == 3:
                eaID = data.createSensor(pg.Pos(float(vals[1]),
                                                float(vals[2])))
                ebID = -1
                emID = data.createSensor(pg.Pos(float(vals[3]),
                                                float(vals[4])))
                enID = data.createSensor(pg.Pos(float(vals[5]),
                                                float(vals[6])))
            elif int(vals[0]) == 2:
                eaID = data.createSensor(pg.Pos(float(vals[1]),
                                                float(vals[2])))
                ebID = -1
                emID = data.createSensor(pg.Pos(float(vals[3]),
                                                float(vals[4])))
                enID = -1
            else:
                raise Exception('dont know how to handle row', vals[0])
            res[i] = float(vals[int(vals[0])*2+1])
            if hasIP:
                # ip[i] = float(vals[int(vals[0])*2+2])
                ipCol = int(vals[0])*2+2
                ip[i] = float(vals[ipCol])
                if 'ipNumGates' in header:
                    specIP.append(vals[ipCol:])

            data.createFourPointData(i, eaID, ebID, emID, enID)

        if isR:
            data.set('r', res)
        else:
            data.set('rhoa', res)

        if hasIP:
            data.set('ip', ip)
            if 'ipNumGates' in header:
                A = np.array(specIP, dtype=float)
                A[A > 1000] = -999
                A[A < -1000] = -999
                for i in range(header['ipNumGates']):
                    data.set('ip'+str(i+1), A[:, i])

        data.sortSensorsX()
        data.sortSensorsIndex()
        if return_header:
            return data, header
        else:
            return data

    # amount of values per collumn per typ
    nntyp = [0, 3, 3, 4, 3, 3, 4, 4, 3, 0, 0, 8, 10]

    nn = nntyp[typ] + hasIP

    # dataBody = pg.Matrix(nn, nData)
    dataBody = np.zeros((nn, nData))

    for i in range(nData):
        vals = getNonEmptyRow(it, comment=';').replace(',', ' ').split()
        dataBody[:, i] = np.array(vals, dtype=float)
#        for j in range(nn):
#            dataBody[j][i] = float(vals[j])

    XX = dataBody[0]
    EL = dataBody[1]
    SP = pg.Vector(nData, 1.0)

    if nn - hasIP == 4:
        SP = dataBody[2]

    AA = None
    BB = None
    NN = None
    MM = None

    if typ == 1:  # Wenner
        AA = XX - xLoc * EL * 1.5
        MM = AA + EL
        NN = MM + EL
        BB = NN + EL
    elif typ == 2:  # Pole-Pole
        AA = XX - xLoc * EL * 0.5
        MM = AA + EL
    elif typ == 3:  # Dipole-Dipole
        AA = XX - xLoc * EL * (SP / 2. + 1.)
        BB = AA + EL
        MM = BB + SP * EL
        NN = MM + EL
        pass
    elif typ == 3:  # Dipole-Dipole
        AA = XX - xLoc * EL * (SP / 2. + 1.)
        BB = AA + EL
        MM = BB + SP * EL
        NN = MM + EL
    elif typ == 4:  # WENNER-BETA
        AA = XX - xLoc * EL * 1.5
        BB = AA + EL
        MM = BB + EL
        NN = MM + EL
    elif typ == 5:  # WENNER-GAMMA
        AA = XX - xLoc * EL * 1.5
        MM = AA + EL
        BB = MM + EL
        NN = BB + EL
    elif typ == 6:  # POLE-DIPOLE
        AA = XX - xLoc * SP * EL - (SP - 1.) * (SP < 0.) * EL
        MM = AA + SP * EL
        NN = MM + pg.sign(SP) * EL
    elif typ == 7:  # SCHLUMBERGER
        AA = XX - xLoc * EL * (SP + 0.5)
        MM = AA + SP * EL
        NN = MM + EL
        BB = NN + SP * EL
    else:
        raise Exception('Datatype ' + str(typ) + ' not yet suppoted')

    for i in range(len(AA)):

        if AA is not None:
            eaID = data.createSensor(pg.Pos(AA[i], 0.0))
        else:
            eaID = -1

        if BB is not None:
            ebID = data.createSensor(pg.Pos(BB[i], 0.0))
        else:
            ebID = -1

        if MM is not None:
            emID = data.createSensor(pg.Pos(MM[i], 0.0))
        else:
            emID = -1

        if NN is not None:
            enID = data.createSensor(pg.Pos(NN[i], 0.0))
        else:
            enID = -1

        data.createFourPointData(i, eaID, ebID, emID, enID)

    data.set('rhoa', dataBody[nn - hasIP - 1])
    if hasIP:
        data.set('ip', dataBody[nn - 1])

    data.sortSensorsX()
    if return_header:
        return data, header
    else:
        return data
def interpolate(mesh, data, x, y):
    result = []
    for yi in y:
        cell = mesh.findCell(pg.Pos(x[0], yi))
        result.append(data[cell.id()])
    return np.array(result)