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.
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
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))
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
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
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)