def grange(start, end, dx=0, n=0, log=False): """Create array with possible increasing spacing. Create either array from start step-wise filled with dx until end reached [start, end] (like np.array with defined end). Fill the array from start to end with n steps. [start, end] (like np.linespace) Fill the array from start to end with n steps but logarithmic increasing, dx will be ignored. Parameters ---------- start: float First value of the resulting array end: float Last value of the resulting array dx: float Linear step length, n will be ignored n: int Amount of steps log: bool Examples -------- >>> from pygimli.utils import grange >>> v1 = grange(start=0, end=10, dx=3) >>> v2 = grange(start=0, end=10, n=3) >>> print(v1) 4 [0.0, 3.0, 6.0, 9.0] >>> print(v2) 3 [0.0, 5.0, 10.0] Returns ------- ret: :gimliapi:`GIMLI::RVector` Return resulting array """ s = float(start) e = float(end) d = float(dx) if dx != 0: if end < start and dx > 0: # print("grange: decreasing range but increasing dx, swap dx sign") d = -d if end > start and dx < 0: # print("grange: increasing range but decreasing dx, swap dx sign") d = -d ret = pg.RVector(range(int(floor(abs((e - s) / d)) + 1))) ret *= d ret += s return ret elif n > 0: if not log: return grange(start, end, dx=(e - s) / (n - 1)) else: return pg.increasingRange(start, end, n) else: raise Exception('Either dx or n have to be given.')
def createParaMesh2DGrid(sensors, paraDX=1, paraDZ=1, paraDepth=0, nLayers=11, boundary=-1, paraBoundary=2, **kwargs): """Create a grid style mesh for an inversion parameter mesh. Create a grid style mesh for an inversion parameter mesh. Return parameter grid for a given list of sensor positions. Uses and forwards arguments to :py:mod:`pygimli.meshtools.appendTriangleBoundary`. Parameters ---------- sensors : list of RVector3 objects or data container with sensorPositions Sensor positions. Must be sorted in positive x direction paraDX : float, optional Horizontal distance between sensors, relative regarding sensor distance. Value must be greater than 0 otherwise 1 is assumed. paraDZ : float, optional Vertical distance to the first depth layer, relative regarding sensor distance. Value must be greater than 0 otherwise 1 is assumed. paraDepth : float, optional Maximum depth for parametric domain, 0 (default) means 0.4 * maximum sensor range. nLayers : int, optional [11] Number of depth layers. boundary : int, optional [-1] Boundary width to be appended for domain prolongation in absolute para domain width. Values lower than 0 force the boundary to be 4 times para domain width. paraBoundary : int, optional [2] Offset to the parameter domain boundary in absolute sensor spacing. Returns ------- mesh: :gimliapi:`GIMLI::Mesh` Examples -------- >>> import pygimli as pg >>> import matplotlib.pyplot as plt >>> >>> from pygimli.meshtools import createParaMesh2DGrid >>> mesh = createParaMesh2DGrid(sensors=pg.RVector(range(10)), ... boundary=1, paraDX=1, ... paraDZ=1, paraDepth=5) >>> ax, _ = pg.show(mesh, mesh.cellMarkers(), alpha=0.3, cmap="summer", ... hold=True) >>> ax, _ = pg.show(mesh, ax=ax) """ mesh = pg.Mesh(2) # maybe separate x y z and sort if isinstance(sensors, np.ndarray) or isinstance(sensors, pg.RVector): sensors = [pg.RVector3(s, 0) for s in sensors] sensorX = pg.x(sensors) eSpacing = abs(sensorX[1] - sensorX[0]) xmin = min(sensorX) - paraBoundary * eSpacing xmax = max(sensorX) + paraBoundary * eSpacing if paraDX == 0: paraDX = 1. if paraDZ == 0: paraDZ = 1. dx = eSpacing * paraDX dz = eSpacing * paraDZ if paraDepth == 0: paraDepth = 0.4 * (xmax - xmin) x = pg.utils.grange(xmin, xmax, dx=dx) y = -pg.increasingRange(dz, paraDepth, nLayers) mesh.createGrid(x, y) mesh.setCellMarkers([2] * mesh.cellCount()) paraXLimits = [xmin, xmax] # paraYLimits = [min(y), max(y)] # not used if boundary < 0: boundary = abs((paraXLimits[1] - paraXLimits[0]) * 4.0) mesh = pg.meshtools.appendTriangleBoundary(mesh, xbound=boundary, ybound=boundary, marker=1, **kwargs) return mesh
def appendTriangleBoundary(mesh, xbound=10, ybound=10, marker=1, quality=34.0, area=0.0, smooth=False, markerBoundary=1, isSubSurface=False, verbose=False): """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 at top). Parameters ---------- mesh : mesh object Mesh to which the triangle boundary should be appended. xbound : float, optional Horizontal prolongation distance. Minimal mesh 0.5 x extension. ybound : float, optional Vertical prolongation distance. Minimal mesh 0.5 y extension. marker : int, optional Marker of new cells. markerBoundary : int, optional Marker of the inner boundary edges between mesh and new boundary. quality : float, optional Triangle quality. area: float, optional Triangle max size within the boundary. smooth : boolean, optional Apply mesh smoothing. isSubSurface : boolean, optional Apply boundary conditions suitable for geo-simulation and prolongate mesh to the surface if necessary. verbose : boolean, optional Be verbose. Examples -------- >>> import matplotlib.pyplot as plt >>> import pygimli as pg >>> from pygimli.mplviewer import drawMesh, drawModel >>> from pygimli.meshtools import appendTriangleBoundary >>> inner = pg.createGrid(range(5), range(5), marker=1) >>> mesh = appendTriangleBoundary(inner, xbound=3, ybound=6, marker=2) >>> fig, (ax1, ax2) = plt.subplots(1,2) >>> p1 = drawMesh(ax1, inner) >>> p2 = drawModel(ax2, mesh, mesh.cellMarkers(), label='marker') >>> p3 = drawMesh(ax2, mesh) >>> txt1 = ax1.set_title("a) Input grid") >>> txt2 = ax2.set_title("b) With triangle boundary") See Also -------- appendTetrahedronBoundary """ surface = 0.0 # find boundaries on left/right/bottom/top side le = [b for b in mesh.boundaries() if b.center().x() == mesh.xmin()] bo = [b for b in mesh.boundaries() if b.center().y() == mesh.ymin()] ri = [b for b in mesh.boundaries() if b.center().x() == mesh.xmax()] top = [b for b in mesh.boundaries() if b.center().y() == mesh.ymax()] # gather all right boundary nodes after sorting in boundaryNodes tmp = [] for b in ri: if b.node(0) not in tmp: tmp.append(b.node(0)) if b.node(1) not in tmp: tmp.append(b.node(1)) tmp.sort(key=lambda n: n.pos().y()) tmp.reverse() boundaryNodes = tmp # gather all bottom boundary nodes and add them to boundaryNodes boNode = [] for b in bo: if b.node(0) not in boNode + boundaryNodes: boNode.append(b.node(0)) if b.node(1) not in boNode + boundaryNodes: boNode.append(b.node(1)) boNode.sort(key=lambda n: n.pos().x()) boNode.reverse() boundaryNodes = boundaryNodes + boNode # gather all left boundary nodes and add them to boundaryNodes tmp = [] for b in le: if b.node(0) not in tmp + boundaryNodes: tmp.append(b.node(0)) if b.node(1) not in tmp + boundaryNodes: tmp.append(b.node(1)) tmp.sort(key=lambda n: n.pos().y()) boundaryNodes = boundaryNodes + tmp if isSubSurface: # gather all top boundary nodes and add them to boundaryNodes topNodes = [] for boundary in top: if boundary.node(0) not in topNodes + boundaryNodes: topNodes.append(boundary.node(0)) if boundary.node(1) not in topNodes + boundaryNodes: topNodes.append(boundary.node(1)) topNodes.sort(key=lambda n: n.pos().x()) boundaryNodes = boundaryNodes + topNodes poly = pg.Mesh() preserveSwitch = '' if isSubSurface: # add all boundary nodes for n in boundaryNodes: poly.createNode(n.pos()) # and connect them by a closed polygon for i in range(0, poly.nodeCount()): poly.createEdge( poly.node(i), poly.node((i + 1) % poly.nodeCount()), markerBoundary) # add four corners of the world box xtLen = 12 # x bottom boundary sampling points # xBottom = pg.asvector(np.linspace(mesh.xmin() - xbound, # mesh.xmax() + xbound, xtLen)) n1 = poly.createNode(pg.RVector3(mesh.xmax() + xbound, surface, 0.0)) n2 = poly.createNode(pg.RVector3(mesh.xmin() - xbound, surface, 0.0)) n3 = poly.createNode(pg.RVector3(mesh.xmin() - xbound, mesh.ymin() - ybound, 0.0)) n4 = poly.createNode(pg.RVector3(mesh.xmax() + xbound, mesh.ymin() - ybound, 0.0)) # and connect them by a closed polygon poly.createEdge(n1, n2, pg.MARKER_BOUND_HOMOGEN_NEUMANN) poly.createEdge(n2, n3, pg.MARKER_BOUND_MIXED) poly.createEdge(n3, n4, pg.MARKER_BOUND_MIXED) poly.createEdge(n4, n1, pg.MARKER_BOUND_MIXED) else: # no isSubSurface xbound = max(xbound, 0.5 * (mesh.xmax() - mesh.xmin())) ybound = max(ybound, 0.5 * (mesh.ymax() - mesh.ymin())) # add top right node and boundary nodes dxMin = boNode[0].pos().distance(boNode[1].pos()) * 1.1 xtLen = max(5, int(xbound / dxMin / 2.)) # x top boundary sampling points xTop = pg.increasingRange(dxMin, xbound, xtLen) # y boundary sampling points yLeft = pg.increasingRange(xTop[len(xTop) - 1] - xTop[len(xTop) - 2], abs(mesh.ymin() - ybound), xtLen) xtLen = max(5, int((mesh.xmax() - mesh.xmin()) / dxMin / 2.)) # x bottom boundary sampling points xBottom = pg.RVector(np.linspace(mesh.xmin() - xbound, mesh.xmax() + xbound, 2 * xtLen)) for i, val in enumerate(pg.fliplr(xTop)(0, len(xTop) - 1)): poly.createNode([mesh.xmax() + val, mesh.ymax(), 0.0]) for n in boundaryNodes: poly.createNode(n.pos()) # add top left, bottom left and bottom right node for t in xTop(1, len(xTop)): poly.createNode([mesh.xmin() - t, mesh.ymax(), 0.0]) for t in yLeft(1, len(yLeft)): poly.createNode([mesh.xmin() - xbound, mesh.ymax() - t, 0.0]) for t in xBottom(1, len(xBottom) - 1): poly.createNode([t, mesh.ymin() - ybound, 0.0]) for t in pg.fliplr(yLeft)(0, len(yLeft) - 1): poly.createNode([mesh.xmax() + xbound, mesh.ymax() - t, 0.0]) # create a closed polygon through all new nodes for i in range(0, poly.nodeCount()): poly.createEdge( poly.node(i), poly.node((i + 1) % poly.nodeCount()), markerBoundary) preserveSwitch = 'Y' # poly.exportVTK('out.poly') mesh2 = pg.Mesh(2) # call triangle mesh generation triswitches = '-pzeAfa' + preserveSwitch + 'q' + str(quality) if area > 0: triswitches += 'a' + str(area) if not verbose: triswitches += 'Q' if isSubSurface: margin = 0.0001 poly.addHoleMarker(pg.RVector3(mesh.xmin() + margin, mesh.ymax() - margin)) tri = pg.TriangleWrapper(poly) tri.setSwitches(triswitches) tri.generate(mesh2) else: pg.TriangleWrapper(poly, mesh2, triswitches) if smooth: mesh2.smooth(nodeMoving=True, edgeSwapping=True, smoothFunction=1, smoothIteration=2) mesh2.setCellMarkers([marker] * mesh2.cellCount()) # map copy the cell not the reference, this should not happen # map( lambda cell: mesh2.copyCell( cell ), mesh2.cells() ) for cell in mesh.cells(): mesh2.copyCell(cell) # old neighbor infos need to be cleaned since the new cells are added mesh2.createNeighbourInfos(force=True) for b in mesh2.boundaries(): if b.leftCell() is None or b.rightCell() is None: if b.center().y() == mesh2.ymax(): b.setMarker(pg.MARKER_BOUND_HOMOGEN_NEUMANN) else: b.setMarker(pg.MARKER_BOUND_MIXED) return mesh2
start=math.pi, end=2 * math.pi, isClosed=False) left = plc.createLine(start=(-20, 0.0), end=(-5.1, 0.0), segments=10) left.node(8).setMarker(1) mid = plc.createLine(start=(-4.9, 0.0), end=(4.9, 0.0), segments=20) right = plc.createLine(start=(5.1, 0.0), end=(20, 0.0), segments=10) left.node(2).setMarker(1) border = plc.mergePLC([left, c1, mid, c2, right]) depth = 20 nz = 15 newNodes = [] y = pg.increasingRange(0.2, depth, nz) surface = pg.createMesh2D(border, y, 0, 0, False) #for n in surface.nodes(): #yNodes = pg.increasingRange(yDefault[1], depth+n.y(), nz) #for y in yNodes[0:]: #newNodes.append([n.x(), n.y() -y]) #surface = pg.createGrid(x=yDefault, y=pg.sort(pg.x(surface.positions()))) #for i, n in enumerate(surface.nodes()): #n.setPos(newNodes[i]) #surface.smooth(1, 1, 1, 10) ax, _ = pg.show(surface)
start=math.pi, end=2*math.pi, isClosed=False) c2 = plc.createCircle(pos=( 5.0, 0.0), radius=0.1, segments=5, start=math.pi, end=2*math.pi, isClosed=False) left = plc.createLine(start=(-20, 0.0), end=(-5.1, 0.0), segments=10) left.node(8).setMarker(1) mid = plc.createLine(start=(-4.9, 0.0), end=(4.9, 0.0), segments=20) right= plc.createLine(start=(5.1, 0.0), end=(20, 0.0), segments=10) left.node(2).setMarker(1) border = mergePLC([left, c1, mid, c2, right]) depth = 20 nz = 15 newNodes = [] y = pg.increasingRange(0.2, depth, nz) surface = pg.createMesh2D(border, y, 0, 0, False) #for n in surface.nodes(): #yNodes = pg.increasingRange(yDefault[1], depth+n.y(), nz) #for y in yNodes[0:]: #newNodes.append([n.x(), n.y() -y]) #surface = pg.createGrid(x=yDefault, y=pg.sort(pg.x(surface.positions()))) #for i, n in enumerate(surface.nodes()): #n.setPos(newNodes[i]) #surface.smooth(1, 1, 1, 10) ax, _ = pg.show(surface)
def createParaMesh2DGrid(sensors, paraDX=1, paraDZ=1, paraDepth=0, nLayers=11, boundary=-1, paraBoundary=2, verbose=False, *args, **kwargs): """ Create a grid style mesh for an inversion parameter mesh. Return parameter grid for a given list of sensor positions. Parameters ---------- sensors : list of RVector3 objects Sensor positions. Must be sorted in positive x direction paraDX : float, optional Horizontal distance between sensors, relative regarding sensor distance. Value must be greater than 0 otherwise 1 is assumed. paraDZ : float, optional Vertical distance to the first depth layer, relative regarding sensor distance. Value must be greater than 0 otherwise 1 is assumed. paraDepth : float, optional Maximum depth for parametric domain, 0 (default) means 0.4 * maxmimum sensor range. nLayers : int, optional Number of depth layers. boundary : int, optional Boundary width to be appended for domain prolongation in absolute para domain width. Values lover 0 force the boundary to be 4 times para domain width. paraBoundary : int, optional Offset for parameter domain boundary in absolute sensor distance. 2 (default). verbose : boolean, optional Be verbose. Returns ------- mesh: :gimliapi:`GIMLI::Mesh` Examples -------- >>> import pygimli as pg >>> import matplotlib.pyplot as plt >>> >>> from pygimli.meshtools import createParaMesh2DGrid >>> from pygimli.mplviewer import drawMesh >>> x = pg.RVector(range(10)) >>> mesh = createParaMesh2DGrid(x, boundary=1, paraDX=1, ... paraDZ=1, paraDepth=5) >>> fig, ax = plt.subplots() >>> drawMesh(ax, mesh) >>> plt.show() """ mesh = pg.Mesh(2) # maybe separate x y z and sort if isinstance(sensors, np.ndarray) or isinstance(sensors, pg.RVector): sensors = [pg.RVector3(s, 0) for s in sensors] sensorX = pg.x(sensors) eSpacing = abs(sensorX[1] - sensorX[0]) xmin = min(sensorX) - paraBoundary * eSpacing xmax = max(sensorX) + paraBoundary * eSpacing if paraDX == 0: paraDX = 1. if paraDZ == 0: paraDZ = 1. dx = eSpacing * paraDX dz = eSpacing * paraDZ if paraDepth == 0: paraDepth = 0.4 * (xmax - xmin) x = pg.utils.grange(xmin, xmax, dx=dx) y = -pg.increasingRange(dz, paraDepth, nLayers) mesh.createGrid(x, y) list(map(lambda cell: cell.setMarker(2), mesh.cells())) paraXLimits = [xmin, xmax] # paraYLimits = [min(y), max(y)] # not used if boundary < 0: boundary = abs((paraXLimits[1] - paraXLimits[0]) * 4.0) mesh = pg.meshtools.appendTriangleBoundary(mesh, xbound=boundary, ybound=boundary, marker=1, *args, **kwargs) return mesh
def appendTriangleBoundary( mesh, xbound=10, ybound=10, marker=1, quality=34.0, area=0.0, smooth=False, markerBoundary=1, isSubSurface=False, verbose=False, ): """ 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 at top). Parameters ---------- mesh : mesh object Mesh to which the triangle boundary should be appended. xbound : float, optional Horizontal prolongation distance. Minimal mesh 0.5 x extension. ybound : float, optional Vertical prolongation distance. Minimal mesh 0.5 y extension. marker : int, optional Marker of new cells. markerBoundary : int, optional Marker of the inner boundary edges between mesh and new boundary. quality : float, optional Triangle quality. area: float, optional Triangle max size within the boundary. smooth : boolean, optional Apply mesh smoothing. isSubSurface : boolean, optional Apply boundary conditions suitable for geo-simulation and prolongate mesh to the surface if necessary. verbose : boolean, optional Be verbose. Examples -------- >>> from pygimli.meshtools import appendTriangleBoundary >>> from matplotlib import pyplot as plt >>> import pygimli as pg >>> from pygimli.mplviewer import drawMesh, drawModel >>> inner = pg.createGrid(range(5), range(5)) >>> mesh = appendTriangleBoundary(inner, xbound=3, ybound=6, marker=1) >>> fig, (ax1, ax2) = plt.subplots(1,2) >>> p1 = drawMesh(ax1, inner) >>> p2 = drawModel(ax2, mesh, mesh.cellMarker()) >>> p3 = drawMesh(ax2, mesh) >>> txt1 = ax1.set_title("a) Input grid") >>> txt2 = ax2.set_title("b) With triangle boundary") >>> plt.show() See Also -------- appendTetrahedronBoundary """ def sortNodeY(n1, n2): """function comparing x for using sort.""" return cmp(n1.pos().y(), n2.pos().y()) def sortNodeX(n1, n2): """function comparing y for using sort.""" return cmp(n1.pos().x(), n2.pos().x()) surface = 0.0 # find boundaries on left/right/bottom/top side le = [b for b in mesh.boundaries() if b.center().x() == mesh.xmin()] bo = [b for b in mesh.boundaries() if b.center().y() == mesh.ymin()] ri = [b for b in mesh.boundaries() if b.center().x() == mesh.xmax()] top = [b for b in mesh.boundaries() if b.center().y() == mesh.ymax()] # gather all right boundary nodes after sorting in boundaryNodes tmp = [] for b in ri: if b.node(0) not in tmp: tmp.append(b.node(0)) if b.node(1) not in tmp: tmp.append(b.node(1)) tmp.sort(key=lambda n: n.pos().y()) tmp.reverse() boundaryNodes = tmp # gather all bottom boundary nodes and add them to boundaryNodes boNode = [] for b in bo: if b.node(0) not in boNode + boundaryNodes: boNode.append(b.node(0)) if b.node(1) not in boNode + boundaryNodes: boNode.append(b.node(1)) boNode.sort(key=lambda n: n.pos().x()) boNode.reverse() boundaryNodes = boundaryNodes + boNode # gather all left boundary nodes and add them to boundaryNodes tmp = [] for b in le: if b.node(0) not in tmp + boundaryNodes: tmp.append(b.node(0)) if b.node(1) not in tmp + boundaryNodes: tmp.append(b.node(1)) tmp.sort(key=lambda n: n.pos().y()) boundaryNodes = boundaryNodes + tmp if isSubSurface: # gather all top boundary nodes and add them to boundaryNodes topNodes = [] for boundary in top: if boundary.node(0) not in topNodes + boundaryNodes: topNodes.append(boundary.node(0)) if boundary.node(1) not in topNodes + boundaryNodes: topNodes.append(boundary.node(1)) topNodes.sort(key=lambda n: n.pos().x()) boundaryNodes = boundaryNodes + topNodes poly = pg.Mesh() preserveSwitch = "" if isSubSurface: # add all boundary nodes for n in boundaryNodes: poly.createNode(n.pos()) # and connect them by a closed polygon for i in range(0, poly.nodeCount()): poly.createEdge(poly.node(i), poly.node((i + 1) % poly.nodeCount()), markerBoundary) # add four corners of the world box xtLen = 12 # x bottom boundary sampling points # xBottom = pg.asvector(np.linspace(mesh.xmin() - xbound, # mesh.xmax() + xbound, xtLen)) n1 = poly.createNode(pg.RVector3(mesh.xmax() + xbound, surface, 0.0)) n2 = poly.createNode(pg.RVector3(mesh.xmin() - xbound, surface, 0.0)) n3 = poly.createNode(pg.RVector3(mesh.xmin() - xbound, mesh.ymin() - ybound, 0.0)) n4 = poly.createNode(pg.RVector3(mesh.xmax() + xbound, mesh.ymin() - ybound, 0.0)) # and connect them by a closed polygon poly.createEdge(n1, n2, pg.MARKER_BOUND_HOMOGEN_NEUMANN) poly.createEdge(n2, n3, pg.MARKER_BOUND_MIXED) poly.createEdge(n3, n4, pg.MARKER_BOUND_MIXED) poly.createEdge(n4, n1, pg.MARKER_BOUND_MIXED) else: # no isSubSurface xbound = max(xbound, 0.5 * (mesh.xmax() - mesh.xmin())) ybound = max(ybound, 0.5 * (mesh.ymax() - mesh.ymin())) # add top right node and boundary nodes dxMin = boNode[0].pos().distance(boNode[1].pos()) * 1.1 xtLen = max(5, int(xbound / dxMin / 2.0)) # x top boundary sampling points xTop = pg.increasingRange(dxMin, xbound, xtLen) # y boundary sampling points yLeft = pg.increasingRange(xTop[len(xTop) - 1] - xTop[len(xTop) - 2], abs(mesh.ymin() - ybound), xtLen) xtLen = max(5, int((mesh.xmax() - mesh.xmin()) / dxMin / 2.0)) # x bottom boundary sampling points xBottom = pg.RVector(np.linspace(mesh.xmin() - xbound, mesh.xmax() + xbound, 2 * xtLen)) for i, val in enumerate(pg.fliplr(xTop)(0, len(xTop) - 1)): poly.createNode([mesh.xmax() + val, mesh.ymax(), 0.0]) for n in boundaryNodes: poly.createNode(n.pos()) # add top left, bottom left and bottom right node for t in xTop(1, len(xTop)): poly.createNode([mesh.xmin() - t, mesh.ymax(), 0.0]) for t in yLeft(1, len(yLeft)): poly.createNode([mesh.xmin() - xbound, mesh.ymax() - t, 0.0]) for t in xBottom(1, len(xBottom) - 1): poly.createNode([t, mesh.ymin() - ybound, 0.0]) for t in pg.fliplr(yLeft)(0, len(yLeft) - 1): poly.createNode([mesh.xmax() + xbound, mesh.ymax() - t, 0.0]) # create a closed polygon through all new nodes for i in range(0, poly.nodeCount()): poly.createEdge(poly.node(i), poly.node((i + 1) % poly.nodeCount()), markerBoundary) preserveSwitch = "Y" # poly.exportVTK('out.poly') mesh2 = pg.Mesh(2) # call triangle mesh generation triswitches = "-pzeAfa" + preserveSwitch + "q" + str(quality) if area > 0: triswitches += "a" + str(area) if not verbose: triswitches += "Q" if isSubSurface: margin = 0.0001 poly.addHoleMarker(pg.RVector3(mesh.xmin() + margin, mesh.ymax() - margin)) tri = pg.TriangleWrapper(poly) tri.setSwitches(triswitches) tri.generate(mesh2) else: pg.TriangleWrapper(poly, mesh2, triswitches) if smooth: mesh2.smooth(nodeMoving=True, edgeSwapping=True, smoothFunction=1, smoothIteration=2) list(map(lambda cell: cell.setMarker(marker), mesh2.cells())) # map copy the cell not the reference, this should not happen # map( lambda cell: mesh2.copyCell( cell ), mesh2.cells() ) for cell in mesh.cells(): mesh2.copyCell(cell) # old neighbor infos need to be cleaned since the new cells are added mesh2.createNeighbourInfos(force=True) for b in mesh2.boundaries(): if b.leftCell() is None or b.rightCell() is None: if b.center().y() == mesh2.ymax(): b.setMarker(pg.MARKER_BOUND_HOMOGEN_NEUMANN) else: b.setMarker(pg.MARKER_BOUND_MIXED) return mesh2
def createParaMesh2DGrid(sensors, paraDX=1, paraDZ=1, paraDepth=0, nLayers=11, boundary=-1, paraBoundary=2, **kwargs): """Create a grid style mesh for an inversion parameter mesh. Create a grid style mesh for an inversion parameter mesh. Return parameter grid for a given list of sensor positions. Uses and forwards arguments to :py:mod:`pygimli.meshtools.appendTriangleBoundary`. Parameters ---------- sensors : list of RVector3 objects or data container with sensorPositions Sensor positions. Must be sorted in positive x direction paraDX : float, optional Horizontal distance between sensors, relative regarding sensor distance. Value must be greater than 0 otherwise 1 is assumed. paraDZ : float, optional Vertical distance to the first depth layer, relative regarding sensor distance. Value must be greater than 0 otherwise 1 is assumed. paraDepth : float, optional Maximum depth for parametric domain, 0 (default) means 0.4 * maximum sensor range. nLayers : int, optional [11] Number of depth layers. boundary : int, optional [-1] Boundary width to be appended for domain prolongation in absolute para domain width. Values lower than 0 force the boundary to be 4 times para domain width. paraBoundary : int, optional [2] Offset to the parameter domain boundary in absolute sensor spacing. Returns ------- mesh: :gimliapi:`GIMLI::Mesh` Examples -------- >>> import pygimli as pg >>> import matplotlib.pyplot as plt >>> >>> from pygimli.meshtools import createParaMesh2DGrid >>> mesh = createParaMesh2DGrid(sensors=pg.RVector(range(10)), ... boundary=1, paraDX=1, ... paraDZ=1, paraDepth=5) >>> ax, _ = pg.show(mesh, markers=True, showMesh=True) """ mesh = pg.Mesh(2) # maybe separate x y z and sort if isinstance(sensors, np.ndarray) or isinstance(sensors, pg.RVector): sensors = [pg.RVector3(s, 0) for s in sensors] if isinstance(sensors, pg.DataContainer): sensors = sensors.sensorPositions() sensorX = pg.x(sensors) eSpacing = abs(sensorX[1] - sensorX[0]) xmin = min(sensorX) - paraBoundary * eSpacing xmax = max(sensorX) + paraBoundary * eSpacing if paraDX == 0: paraDX = 1. if paraDZ == 0: paraDZ = 1. dx = paraDX dz = paraDZ if eSpacing > 0: dx = eSpacing * paraDX # dz = eSpacing * paraDZ # not really making sense if paraDepth == 0: paraDepth = 0.4 * (xmax - xmin) # print(xmin, xmax, dx) x = pg.utils.grange(xmin, xmax, dx=dx) y = -pg.increasingRange(dz, paraDepth, nLayers) mesh.createGrid(x, y) mesh.setCellMarkers([2] * mesh.cellCount()) paraXLimits = [xmin, xmax] # paraYLimits = [min(y), max(y)] # not used if boundary < 0: boundary = abs((paraXLimits[1] - paraXLimits[0]) * 4.0) mesh = pg.meshtools.appendTriangleBoundary( mesh, xbound=boundary, ybound=boundary, marker=1, **kwargs) return mesh
def appendTriangleBoundary(mesh, xbound=10, ybound=10, marker=1, quality=34.0, smooth=False, markerBoundary=1, isSubSurface=False, verbose=False): """ Returns a new mesh that contains a triangulated box around a given mesh suitable for geo-simulation (surface boundary at top). Parameters ---------- mesh : mesh object Mesh to which the triangle boundary should be appended. xbound : float, optional Horizontal prolongation distance. ybound : float, optional Vertical prolongation distance. marker : int, optional Marker of new cells. markerBoundary : int, optional Marker of the inner boundary edges between mesh and new boundary. quality : float, optional Triangle quality. smooth : boolean, optional Apply mesh smoothing. isSubSurface : boolean, optional Apply boundary conditions suitable for geo-simulation and prolongate mesh to the surface if necessary. verbose : boolean, optional Be verbose. See Also -------- appendTetrahedronBoundary """ def sortNodeY(n1, n2): """function comparing x for using sort.""" return cmp(n1.pos().y(), n2.pos().y()) def sortNodeX(n1, n2): """function comparing y for using sort.""" return cmp(n1.pos().x(), n2.pos().x()) surface = 0.0 # find boundaries on left/right/bottom/top side le = [b for b in mesh.boundaries() if b.center().x() == mesh.xmin()] bo = [b for b in mesh.boundaries() if b.center().y() == mesh.ymin()] ri = [b for b in mesh.boundaries() if b.center().x() == mesh.xmax()] top = [b for b in mesh.boundaries() if b.center().y() == mesh.ymax()] # gather all right boundary nodes after sorting in boundaryNodes tmp = [] for b in ri: if b.node(0) not in tmp: tmp.append(b.node(0)) if b.node(1) not in tmp: tmp.append(b.node(1)) tmp.sort(key=lambda n: n.pos().y()) tmp.reverse() boundaryNodes = tmp # gather all bottom boundary nodes and add them to boundaryNodes boNode = [] for b in bo: if b.node(0) not in boNode + boundaryNodes: boNode.append(b.node(0)) if b.node(1) not in boNode + boundaryNodes: boNode.append(b.node(1)) boNode.sort(key=lambda n: n.pos().x()) boNode.reverse() boundaryNodes = boundaryNodes + boNode # gather all left boundary nodes and add them to boundaryNodes tmp = [] for b in le: if b.node(0) not in tmp + boundaryNodes: tmp.append(b.node(0)) if b.node(1) not in tmp + boundaryNodes: tmp.append(b.node(1)) tmp.sort(key=lambda n: n.pos().y()) boundaryNodes = boundaryNodes + tmp if isSubSurface: # gather all top boundary nodes and add them to boundaryNodes topNodes = [] for boundary in top: if boundary.node(0) not in topNodes + boundaryNodes: topNodes.append(boundary.node(0)) if boundary.node(1) not in topNodes + boundaryNodes: topNodes.append(boundary.node(1)) topNodes.sort(key=lambda n: n.pos().x()) boundaryNodes = boundaryNodes + topNodes poly = pg.Mesh() preserveSwitch = '' if isSubSurface: # add all boundary nodes for n in boundaryNodes: poly.createNode(n.pos()) # and connect them by a closed polygon for i in range(0, poly.nodeCount()): poly.createEdge( poly.node(i), poly.node((i + 1) % poly.nodeCount()), markerBoundary) # add four corners of the world box xtLen = 12 # x bottom boundary sampling points #xBottom = pg.asvector( np.linspace( mesh.xmin() - xbound, mesh.xmax() + xbound, xtLen ) ) n1 = poly.createNode(pg.RVector3(mesh.xmax() + xbound, surface, 0.0)) n2 = poly.createNode(pg.RVector3(mesh.xmin() - xbound, surface, 0.0)) n3 = poly.createNode(pg.RVector3(mesh.xmin() - xbound, mesh.ymin() - ybound, 0.0)) n4 = poly.createNode(pg.RVector3(mesh.xmax() + xbound, mesh.ymin() - ybound, 0.0)) # and connect them by a closed polygon poly.createEdge(n1, n2, pg.MARKER_BOUND_HOMOGEN_NEUMANN) poly.createEdge(n2, n3, pg.MARKER_BOUND_MIXED) poly.createEdge(n3, n4, pg.MARKER_BOUND_MIXED) poly.createEdge(n4, n1, pg.MARKER_BOUND_MIXED) else: # add top right node and boundary nodes xtLen = 12 dxMin = boNode[0].pos().distance(boNode[1].pos()) * 1.5 # x top boundary sampling points xTop = pg.increasingRange(dxMin, xbound, xtLen) # y boundary sampling points yLeft = pg.increasingRange( xTop[xtLen - 1] - xTop[xtLen - 2], abs(mesh.ymin() - ybound), xtLen) # x bottom boundary sampling points xBottom = pg.asvector( np.linspace(mesh.xmin() - xbound, mesh.xmax() + xbound, xtLen)) for t in pg.fliplr(xTop)(0, len(xTop) - 1): poly.createNode(pg.RVector3(mesh.xmax() + t, mesh.ymax(), 0.0)) for n in boundaryNodes: poly.createNode(n.pos()) # add top left, bottom left and bottom right node for t in xTop(1, len(xTop)): poly.createNode(pg.RVector3(mesh.xmin() - t, mesh.ymax(), 0.0)) for t in yLeft(1, len(yLeft)): poly.createNode( pg.RVector3(mesh.xmin() - xbound, mesh.ymax() - t, 0.0)) for t in xBottom(1, len(xBottom) - 1): poly.createNode(pg.RVector3(t, mesh.ymin() - ybound, 0.0)) for t in pg.fliplr(yLeft)(0, len(yLeft) - 1): poly.createNode( pg.RVector3(mesh.xmax() + xbound, mesh.ymax() - t, 0.0)) # create a closed polygon through all new nodes for i in range(0, poly.nodeCount()): poly.createEdge( poly.node(i), poly.node((i + 1) % poly.nodeCount()), markerBoundary) preserveSwitch = 'Y' # poly.exportVTK('out.poly') mesh2 = pg.Mesh() # call triangle mesh generation triswitches = '-pzeAfa' + preserveSwitch + 'q' + str(quality) if not verbose: triswitches += 'Q' if isSubSurface: tri = pg.TriangleWrapper(poly) # area -1.0 means this is a hole tri.addRegionMarkerTmp(0, pg.RVector3(mesh.xmin() + 0.0001, mesh.ymax() - 0.0001), -1.0) tri.setSwitches(triswitches) tri.generate(mesh2) else: pg.TriangleWrapper(poly, mesh2, triswitches) if smooth: mesh2.smooth(nodeMoving=True, edgeSwapping=True, smoothFunction=1, smoothIteration=2) list(map(lambda cell: cell.setMarker(marker), mesh2.cells())) #! map copy the cell not the reference, this should not happen #! map( lambda cell: mesh2.copyCell( cell ), mesh2.cells() ) for cell in mesh.cells(): mesh2.copyCell(cell) #! old neighbor infos need to be cleaned since the new cells are added mesh2.createNeighbourInfos(force=True) for b in mesh2.boundaries(): if b.leftCell() is None or b.rightCell() is None: if b.center().y() == mesh2.ymax(): b.setMarker(pg.MARKER_BOUND_HOMOGEN_NEUMANN) else: b.setMarker(pg.MARKER_BOUND_MIXED) return mesh2