def create_square_mesh(filename, recombine=False, structured=False, nx=13, ny=9, lc=0.12):
if gmsh.isInitialized():
gmsh.finalize()
gmsh.initialize()
gmsh.model.add(Path(filename).stem)
p1 = gmsh.model.geo.addPoint(0.0, 0.0, 0.0, lc)
p2 = gmsh.model.geo.addPoint(1.4, 0.0, 0.0, lc)
p3 = gmsh.model.geo.addPoint(1.4, 0.8, 0.0, lc)
p4 = gmsh.model.geo.addPoint(0.0, 0.8, 0.0, lc)
l1 = gmsh.model.geo.addLine(p1, p2)
l2 = gmsh.model.geo.addLine(p2, p3)
l3 = gmsh.model.geo.addLine(p3, p4)
l4 = gmsh.model.geo.addLine(p4, p1)
loop = gmsh.model.geo.addCurveLoop([l1, l2, l3, l4])
surface = gmsh.model.geo.addPlaneSurface([loop])
if structured:
gmsh.model.geo.mesh.setTransfiniteCurve(l1, nx)
gmsh.model.geo.mesh.setTransfiniteCurve(l3, nx)
gmsh.model.geo.mesh.setTransfiniteCurve(l2, ny)
gmsh.model.geo.mesh.setTransfiniteCurve(l4, ny)
gmsh.model.geo.mesh.setTransfiniteSurface(surface)
if recombine:
gmsh.model.geo.mesh.setRecombine(2, surface)
gmsh.model.geo.synchronize()
gmsh.model.addPhysicalGroup(2, [surface], tag=1)
gmsh.model.setPhysicalName(2, 1, "Omega")
gmsh.model.addPhysicalGroup(1, [l1, l2, l3, l4], tag=2)
gmsh.model.setPhysicalName(1, 2, "boundary")
gmsh.option.setNumber("Mesh.RecombinationAlgorithm", 1)
gmsh.model.mesh.generate(2)
gmsh.write(filename)
gmsh.finalize()
return filename
def read_triangles_and_quads_from_msh(filename):
if gmsh.isInitialized():
gmsh.finalize()
gmsh.initialize()
gmsh.model.add("view_quads")
gmsh.merge(filename)
node_tags, node_coords, _ = gmsh.model.mesh.getNodes()
xy = node_coords.reshape(-1, 3)[:, :2]
node_to_local = {int(tag): i for i, tag in enumerate(node_tags)}
cells = []
counts = {"triangles": 0, "quads": 0}
for _, entity_tag in gmsh.model.getEntities(2):
element_types, _, element_nodes = gmsh.model.mesh.getElements(2, entity_tag)
for e_type, e_nodes in zip(element_types, element_nodes):
if e_type == 2:
conn = np.array(e_nodes, dtype=np.int64).reshape(-1, 3)
counts["triangles"] += len(conn)
elif e_type == 3:
conn = np.array(e_nodes, dtype=np.int64).reshape(-1, 4)
counts["quads"] += len(conn)
else:
continue
cells.extend(np.vectorize(node_to_local.get)(conn))
gmsh.finalize()
return xy, cells, counts
def plot_cells(ax, filename, title):
xy, cells, counts = read_triangles_and_quads_from_msh(filename)
for cell in cells:
polygon = xy[np.r_[cell, cell[0]]]
ax.plot(polygon[:, 0], polygon[:, 1], lw=0.55, color="0.2")
ax.set_aspect("equal")
ax.set_title(f"{title}\n{counts['triangles']} triangles, {counts['quads']} quads")
ax.set_xlabel("x")
ax.set_ylabel("y")
tri_square = create_square_mesh("square_triangles.msh")
quad_square = create_square_mesh("square_recombined_quads.msh", recombine=True)
structured_square = create_square_mesh("square_structured_quads.msh", structured=True, recombine=True)
structured_triangles = create_square_mesh("square_structured_triangles.msh", structured=True)
fig, axes = plt.subplots(1, 4, figsize=(14, 4.2), constrained_layout=True)
plot_cells(axes[0], tri_square, "Unstructured triangles")
plot_cells(axes[1], quad_square, "Recombined quads")
plot_cells(axes[2], structured_square, "Structured quads")
plot_cells(axes[3], structured_triangles, "Structured triangles")
plt.show()