def testCreateTriPrismMesh(): poly = g.Mesh(2) n0 = poly.createNode(0.0, 0.0, 0.) n1 = poly.createNode(1.0, 0.0, 0.) n2 = poly.createNode(0.0, 1.0, 0.) n3 = poly.createNode(1.0, 1.0, 0.) poly.createEdge(n0, n1) poly.createEdge(n1, n3) poly.createEdge(n3, n2) poly.createEdge(n2, n0) mesh2 = g.Mesh(2) g.TriangleWrapper(poly, mesh2, "-pzeAfa0.01q34") mesh3 = g.createMesh3D(mesh2, g.asvector(np.arange(0, -1, -0.1))) mesh3.setCellAttributes(g.asvector(range(0, mesh3.cellCount()))) mesh3.save("prism") mesh3.exportVTK("prism")
def createMesh(self, quality=34.6, maxarea=0.1, addpoints=None): """Create (inversion) mesh by circumventing PLC""" data = self.dataContainer sx = list(pg.x(data.sensorPositions())) sz = list(pg.y(data.sensorPositions())) if addpoints is not None: for po in addpoints: sx.append(po[0]) sz.append(po[1]) iS = np.argsort(np.arctan2(sx - np.mean(sx), sz - np.mean(sz))) plc = pg.Mesh(2) nodes = [plc.createNode(sx[i], sz[i], 0) for i in iS] for i in range(len(nodes) - 1): plc.createEdge(nodes[i], nodes[i + 1]) plc.createEdge(nodes[-1], nodes[0]) tri = pg.TriangleWrapper(plc) tri.setSwitches("-pzFq" + str(quality) + "a" + str(maxarea)) self.setMesh(tri.generate())
def createMesh(poly, quality=30, area=0.0, smooth=None, switches=None, verbose=False, **kwargs): """Create a mesh for a given geometry polygon. The mesh is created by :term:`triangle` or :term:`tetgen` if the gimli support for these mesh generators are installed. The geometry needs to contain nodes and boundaries and should be valid in the sense that the boundaries are non intersecting. If poly is a list of coordinates a simple Delaunay mesh of the convex hull will be created. TODO: Tetgen support need to be implemented Parameters ---------- poly: :gimliapi:`GIMLI::Mesh` or list * 2D or 3D gimli mesh that contains the PLC. * 2D mesh needs edges * 3D mesh needs ... to be implemented * List of x y pairs [[x0, y0], ... ,[xN, yN]] * PLC or list of PLC quality: float 2D triangle quality sets a minimum angle constraint. Be careful with values above 34 degrees. area: float 2D maximum triangle size in m*m smooth: tuple [smoothing algorithm, number of iterations] 0, no smoothing 1, node center 2, weighted node center switches: str Force triangle to use the gives command switches. Returns ------- mesh: :gimliapi:`GIMLI::Mesh` Examples -------- >>> # no need to import matplotlib. pygimli's show does >>> import pygimli as pg >>> import pygimli.meshtools as mt >>> rect = mt.createRectangle(start=[0, 0], end=[4, 1]) >>> ax, _ = pg.show(mt.createMesh(rect, quality=10)) >>> ax, _ = pg.show(mt.createMesh(rect, quality=33)) >>> ax, _ = pg.show(mt.createMesh(rect, quality=33, area=0.01)) >>> pg.wait() """ # poly == [pg.Mesh, ] if isinstance(poly, list): if isinstance(poly[0], pg.Mesh): return createMesh(pg.meshtools.mergePLC(poly), quality, area, smooth, switches, verbose) # poly == [pos, pos, ] if isinstance(poly, list) or isinstance(poly, type(zip)): delPLC = pg.Mesh(2) for p in poly: delPLC.createNode(p[0], p[1], 0.0) return createMesh(delPLC, switches='-zeY') # poly == Mesh if poly.dim() == 2: if poly.nodeCount() == 0: raise Exception("No nodes in poly to create a valid mesh") tri = pg.TriangleWrapper(poly) if switches is None: # -D Conforming delaunay # -F Uses Steven Fortune's sweepline algorithm # no -a here ignores per region area switches = 'pazeA' if area > 0: switches += 'a' + str(area) pass else: switches += 'a' # switches = switches.replace('.', ',') switches += 'q' + str(quality) if not verbose: switches += 'Q' if verbose: print(switches) tri.setSwitches(switches) mesh = tri.generate() if smooth is not None: mesh.smooth(nodeMoving=kwargs.pop('node_move', False), edgeSwapping=False, smoothFunction=smooth[0], smoothIteration=smooth[1]) return mesh else: raise Exception('not yet implemented')
nodes = pg.utils.unique_rows(nodes) # remove duplicate nodes poly = pg.Mesh(2) for node in nodes: poly.createNode(node[0], node[1], 0.0) for i in range(poly.nodeCount() - 1): poly.createEdge(poly.node(i), poly.node(i + 1)) poly.createEdge(poly.node(poly.nodeCount() - 1), poly.node(0)) ############################################################################### # We call the TriangleWrapper to generate the mesh and set the x values as the # data for a color transition. tri = pg.TriangleWrapper(poly) mesh = pg.Mesh(2) tri.setSwitches('-pzeAfa5q33') tri.generate(mesh) data = [] for cell in mesh.cells(): data.append(cell.center().x()) ############################################################################### # Last, we create a BERT caption, visualize the mesh and fine-tune the figure. fig, ax = plt.subplots(figsize=(4, 3)) ax.axis('off') offset = -10 t = ax.text(1.7, offset, 'BERT', fontsize=37, fontweight='bold')
def createMesh(poly, quality=30, area=0.0, smooth=None, switches=None, verbose=False, **kwargs): """Create a mesh for a given geometry polygon. The mesh is created by :term:`triangle` or :term:`tetgen` if the gimli support for these mesh generators are installed. The geometry needs to contain nodes and boundaries and should be valid in the sense that the boundaries are non intersecting. If poly is a list of coordinates a simple Delaunay mesh of the convex hull will be created. TODO: Tetgen support need to be implemented Parameters ---------- poly: :gimliapi:`GIMLI::Mesh` or list or ndarray * 2D or 3D gimli mesh that contains the PLC. * 2D mesh needs edges * 3D mesh needs a plc and tetgen as system component * List of x y pairs [[x0, y0], ... ,[xN, yN]] * ndarray [x_i, y_i] * PLC or list of PLC quality: float 2D triangle quality sets a minimum angle constraint. Be careful with values above 34 degrees. area: float 2D maximum triangle size in m*m smooth: tuple [smoothing algorithm, number of iterations] 0, no smoothing 1, node center 2, weighted node center switches: str Force triangle to use the gives command switches. Returns ------- mesh: :gimliapi:`GIMLI::Mesh` Examples -------- >>> # no need to import matplotlib. pygimli's show does >>> import pygimli as pg >>> import pygimli.meshtools as mt >>> rect = mt.createRectangle(start=[0, 0], end=[4, 1]) >>> ax, _ = pg.show(mt.createMesh(rect, quality=10)) >>> ax, _ = pg.show(mt.createMesh(rect, quality=33)) >>> ax, _ = pg.show(mt.createMesh(rect, quality=33, area=0.01)) >>> pg.wait() """ # poly == [pg.Mesh, ] if isinstance(poly, list): if isinstance(poly[0], pg.Mesh): return createMesh(pg.meshtools.mergePLC(poly), quality, area, smooth, switches, verbose) # poly == [pos, pos, ] if isinstance(poly, list) or \ isinstance(poly, type(zip)) or \ type(poly) == pg.stdVectorRVector3 or \ (isinstance(poly, np.ndarray) and poly.ndim == 2): delPLC = pg.Mesh(2) for p in poly: delPLC.createNode(p[0], p[1], 0.0) return createMesh(delPLC, switches='-zeY') # poly == Mesh if poly.dim() == 2: if poly.nodeCount() == 0: raise Exception("No nodes in poly to create a valid mesh") tri = pg.TriangleWrapper(poly) if switches is None: # -D Conforming delaunay # -F Uses Steven Fortune's sweepline algorithm # no -a here ignores per region area switches = 'pzeA' if area > 0: #switches += 'a' + str(area) # The str function turns everything smaller # than 0.0001 into the scientific notation 1e-5 # which can not be read by triangle. The following # avoids this even for very small numbers switches += 'a' + '{:.20f}'.format(area) pass else: switches += 'a' # switches = switches.replace('.', ',') switches += 'q' + str(quality) if not verbose: switches += 'Q' if verbose: print(switches) tri.setSwitches(switches) mesh = tri.generate() if smooth is not None: mesh.smooth(nodeMoving=kwargs.pop('node_move', True), edgeSwapping=False, smoothFunction=smooth[0], smoothIteration=smooth[1]) return mesh else: tmp = pg.optImport('tempfile') _, namePLC = tmp.mkstemp(suffix='.poly') pg.meshtools.exportPLC(poly, namePLC) mesh = pg.meshtools.syscallTetgen(namePLC, quality, area, verbose=verbose) try: os.remove(namePLC) except: print("can't remove:", namePLC) 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
nodes.append(plc.createNode(xmax, -zlay * 2, 0.)) # 4 nodes.append(plc.createNode(xmin, -zlay * 2, 0.)) # 5 nodes.append(plc.createNode(xmin, -zlay, 0.)) # 6 ############################################################################### # The nodes are connected from from 0 to 6 and back to 0. # An additional edge is drawn from 6 to 3. Node/edge markers do not matter. for i in range(6): plc.createEdge(nodes[i], nodes[i + 1]) plc.createEdge(nodes[6], nodes[0]) plc.createEdge(nodes[6], nodes[3]) ############################################################################### # We insert region markers (0 and 1) into the two layers and generate the mesh. tri = pg.TriangleWrapper(plc) plc.addRegionMarker(pg.RVector3(0., -zlay + .1), 0, 3.) # 10m^2 max area plc.addRegionMarker(pg.RVector3(0., -zlay - .1), 1, 10.) tri.setSwitches('-pzeAfaq34.6') mesh = pg.Mesh(2) tri.generate(mesh) mesh.createNeighbourInfos() print(mesh) ############################################################################### # Next we generate a velocity model from the markers by using a map. # The values are associated to the markers and stored as attributes. v = [1000., 3000.] slomap = pg.stdMapF_F() # mapping markers to real slowness values for i, vi in enumerate(v): slomap.insert(i, 1. / vi)