GMSH#

Since meshwell is a wrapper around GMSH, we will first review GMSH Python API. For a more thorough explanation of the below, see the GMSH documentation (https://gmsh.info/doc/texinfo/gmsh.html), tutorials (https://gitlab.onelab.info/gmsh/gmsh/-/tree/master/tutorials/python) and API (https://gitlab.onelab.info/gmsh/gmsh/-/blob/master/api/gmsh.py?ref_type=heads).

import gmsh
import meshio

from meshwell.visualization import plot2D

Bottom-up construction of CAD entities#

The most general way to create CAD entities in GMSH is “bottom-up”, going from points –> lines –> closed loops –> surfaces –> closed shells –> volumes

# Initialize GMSH
gmsh.initialize()
model = gmsh.model
model.add("bottom_up")

# Create points
p1 = model.occ.addPoint(0, 0, 0)
p2 = model.occ.addPoint(2, 0, 0)
p3 = model.occ.addPoint(2, 1, 0)
p4 = model.occ.addPoint(1, 1, 0)
p5 = model.occ.addPoint(1, 2, 0)
p6 = model.occ.addPoint(0, 2, 0)

# Create lines
l1 = model.occ.addLine(p1, p2)
l2 = model.occ.addLine(p2, p3)
l3 = model.occ.addLine(p3, p4)
l4 = model.occ.addLine(p4, p5)
l5 = model.occ.addLine(p5, p6)
l6 = model.occ.addLine(p6, p1)

# Create curve loop
cl = model.occ.addCurveLoop([l1, l2, l3, l4, l5, l6])

# Create surface
s1 = model.occ.addPlaneSurface([cl])

# Create the mesh
model.occ.synchronize()
model.mesh.generate(2)

gmsh.write("bottom_up.xao")
gmsh.write("bottom_up.msh")
gmsh.finalize()

# Read the mesh
mesh = meshio.read("bottom_up.msh")
Info    : Meshing 1D...
Info    : [  0%] Meshing curve 1 (Line)
Info    : [ 20%] Meshing curve 2 (Line)
Info    : [ 40%] Meshing curve 3 (Line)
Info    : [ 60%] Meshing curve 4 (Line)
Info    : [ 70%] Meshing curve 5 (Line)
Info    : [ 90%] Meshing curve 6 (Line)
Info    : Done meshing 1D (Wall 0.000535601s, CPU 0.000976s)
Info    : Meshing 2D...
Info    : Meshing surface 1 (Plane, Frontal-Delaunay)
Info    : Done meshing 2D (Wall 0.00377082s, CPU 0.003828s)
Info    : 79 nodes 162 elements
Info    : Writing 'bottom_up.xao'...
Info    : Done writing 'bottom_up.xao'
Info    : Writing 'bottom_up.msh'...
Info    : Done writing 'bottom_up.msh'
try:
    gmsh.initialize()
    gmsh.open("bottom_up.xao")
    gmsh.fltk.run()
except:  # noqa: E722
    print("Skipping CAD GUI visualization - only available when running locally")
Info    : Reading 'bottom_up.xao'...
Info    : Done reading 'bottom_up.xao'
Skipping CAD GUI visualization - only available when running locally
Error   : Can't open display:  (FLTK internal error)
try:
    gmsh.initialize()
    gmsh.open("bottom_up.msh")
    gmsh.fltk.run()
except:  # noqa: E722
    print("Skipping mesh GUI visualization - only available when running locally")
Info    : Reading 'bottom_up.msh'...
Info    : 13 entities
Info    : 79 nodes
Info    : 162 elements
Info    : Done reading 'bottom_up.msh'
Skipping mesh GUI visualization - only available when running locally
Warning : Gmsh has aleady been initialized
Error   : Can't open display:  (FLTK internal error)
plot2D(mesh, wireframe=True)
_images/b9f7d9a5330b20f4bb9ae3aa81db117c6f6cdeff0e62650749d744caa5d6e691.png

Construction of CAD entities from primitives#

A limited set of primitives (rectangles, circles, arcs, spheres, boxes, etc.) are also already implemented in GMSH:

gmsh.initialize()
model = gmsh.model
model.add("bottom_up")

# Create rectangle
box = model.occ.addRectangle(0, 0, 0, 1, 1)

# Create circle
circle = model.occ.addDisk(3, 0, 0, 0.5, 0.5)

# Create the mesh
model.occ.synchronize()
model.mesh.generate(2)
gmsh.write("primitives.msh")
gmsh.finalize()
Info    : Meshing 1D...
Info    : [  0%] Meshing curve 1 (Line)
Info    : [ 30%] Meshing curve 2 (Line)
Info    : [ 50%] Meshing curve 3 (Line)
Info    : [ 70%] Meshing curve 4 (Line)
Info    : [ 90%] Meshing curve 5 (Ellipse)
Info    : Done meshing 1D (Wall 0.000454419s, CPU 0.000784s)
Info    : Meshing 2D...
Info    : [  0%] Meshing surface 1 (Plane, Frontal-Delaunay)
Info    : [ 60%] Meshing surface 2 (Plane, Frontal-Delaunay)
Info    : Done meshing 2D (Wall 0.00248567s, CPU 0.002472s)
Info    : 33 nodes 67 elements
Info    : Writing 'primitives.msh'...
Info    : Done writing 'primitives.msh'
Warning : Gmsh has aleady been initialized
mesh = meshio.read("primitives.msh")
plot2D(mesh, wireframe=True)

_images/d46320e2ff05c432704c808f47758ae833e082228529f4c1eba0b4aadd16ce13.png

Constructive geometry operations#

More complex elementary entities can also be created from constructive geometry operations (cut, fuse, intersect):

gmsh.initialize()
model = gmsh.model
model.add("bottom_up")

# Create rectangle
box = model.occ.addRectangle(-1, -1, 0, 2, 2)

# Create circle
circle = model.occ.addDisk(0, 0, 0, 0.5, 0.5)

# Keep difference, delete originals
difference = model.occ.cut(
    [(2, box)], [(2, circle)], removeObject=True, removeTool=True
)

# Create the mesh
model.occ.synchronize()
model.mesh.generate(2)
gmsh.write("booleans.msh")
gmsh.finalize()
Info    : [  0%] Difference                                                                                  
Info    : [ 10%] Difference                                                                                  
Info    : [ 20%] Difference                                                                                  
Info    : [ 30%] Difference - Performing Face-Face intersection                                                                                
Info    : [ 70%] Difference - Performing intersection of shapes                                                                                
Info    : [ 80%] Difference - Making faces                                                                                
Info    : [ 90%] Difference - Adding holes                                                                                
                                                                                
Info    : Meshing 1D...
Info    : [  0%] Meshing curve 5 (Ellipse)
Info    : [ 30%] Meshing curve 6 (Line)
Info    : [ 50%] Meshing curve 7 (Line)
Info    : [ 70%] Meshing curve 8 (Line)
Info    : [ 90%] Meshing curve 9 (Line)
Info    : Done meshing 1D (Wall 0.000442958s, CPU 0.000473s)
Info    : Meshing 2D...
Info    : Meshing surface 1 (Plane, Frontal-Delaunay)
Info    : Done meshing 2D (Wall 0.0017948s, CPU 0.001821s)
Info    : 88 nodes 181 elements
Info    : Writing 'booleans.msh'...
Info    : Done writing 'booleans.msh'
mesh = meshio.read("booleans.msh")
plot2D(mesh, wireframe=True)

_images/5178bc78d265bd00d1d2b801eca08ff8b7b9781ad0d8498b9f6e7195025d44d7.png

Physical entities#

It is almost always extremely useful to be able to refer to all of the mesh nodes within a set of elementary entities. In GMSH, this is achieved by assigning a “physical” group to a set of elementary entities:

gmsh.initialize()
model = gmsh.model
model.add("physicals")

# Create rectangle
box1 = model.occ.addRectangle(0, 0, 0, 1, 1)
box2 = model.occ.addRectangle(-2, -2, 0, 1, 1)

# Create circle
circle1 = model.occ.addDisk(3, 0, 0, 0.5, 0.5)
circle2 = model.occ.addDisk(2, -2, 0, 0.5, 0.5)

model.occ.synchronize()

# Create physical groups
model.addPhysicalGroup(2, [box1, box2], tag=1, name="boxes")
model.addPhysicalGroup(2, [circle1, circle2], tag=2, name="circles")

# Create the mesh
model.occ.synchronize()
model.mesh.generate(2)
gmsh.write("physicals.msh")
gmsh.finalize()
Info    : Meshing 1D...
Info    : [  0%] Meshing curve 1 (Line)
Info    : [ 20%] Meshing curve 2 (Line)
Info    : [ 30%] Meshing curve 3 (Line)
Info    : [ 40%] Meshing curve 4 (Line)
Info    : [ 50%] Meshing curve 5 (Line)
Info    : [ 60%] Meshing curve 6 (Line)
Info    : [ 70%] Meshing curve 7 (Line)
Info    : [ 80%] Meshing curve 8 (Line)
Info    : [ 90%] Meshing curve 9 (Ellipse)
Info    : [100%] Meshing curve 10 (Ellipse)
Info    : Done meshing 1D (Wall 0.000805054s, CPU 0.001134s)
Info    : Meshing 2D...
Info    : [  0%] Meshing surface 1 (Plane, Frontal-Delaunay)
Info    : [ 30%] Meshing surface 2 (Plane, Frontal-Delaunay)
Info    : [ 60%] Meshing surface 3 (Plane, Frontal-Delaunay)
Info    : [ 80%] Meshing surface 4 (Plane, Frontal-Delaunay)
Info    : Done meshing 2D (Wall 0.00281846s, CPU 0.002771s)
Info    : 40 nodes 82 elements
Info    : Writing 'physicals.msh'...
Info    : Done writing 'physicals.msh'
mesh = meshio.read("physicals.msh")
plot2D(mesh, ignore_lines=True)

_images/32e76d336ddccc8e8a507ed265529f7297a6c6a14f739485986e3a538f38707f.png

The sharp bits#

Conflicting entities#

When adding elementary entities, overlaps and interfaces are not “merged” by default: the entities will overlap and will be meshed separately. The resulting sub-meshes will not be connected.

Keeping track of integers#

Whenever entities are created / transformed (e.g. when healing interfaces), there can be reassignment of the integer tags used to label them. In the official tutorials, entity tags are re-identified based on some characteristic like bounding extent to later assign to physical groups.