Source code for torch_fem.dataset.mesh.meshgen

import gmsh 
import os
from ...mesh import Mesh
from ...shape import element_type2dimension, element_type2order


[docs]class MeshGen: """ Parameters ---------- element_type : str, optional If ``element_type`` is ``None``, then it will generate a mix mesh. Otherwise, the order and element will be determined by ``element_type``. dimension : int, optional The dimension of the mesh, e.g., :math:`2` or :math:`3`. order : int, optional The order of the element, e.g., :math:`1`, :math:`2`. chara_length : float, optional The characteristic length of the mesh. The smaller the value, the more dense the mesh. Default is :math:`0.1`. Examples -------- 1. generate rectangle mesh with triangle elements: .. code-block:: python from torch_fem import MeshGen generator = MeshGen(element_type="tri") # triangle mesh for 2d generator.addRectangle(0,0,1,1) # add a rectangle mesh = generator.gen().plot() # generate and visualize the mesh 2. generate mixed mesh with left triangle and right rectangle .. code-block:: python from torch_fem import MeshGen mesh_gen = MeshGen(element_type=None, chara_length=0.1, order=2) mesh_gen.add_rectangle(0,0,0.5,1, element="tri") mesh_gen.add_rectangle(0.5,0,0.5,1, element="quad") mesh_gen.remove_circle(0.5,0.5,0.1) mesh_gen.gen().plot() """ def __init__(self, element_type=None, dimension=2, order=1, chara_length=0.1, cache_path="./tmp.msh"): if element_type is not None: order = element_type2order[element_type] dimension = element_type2dimension[element_type] self.dimension = dimension self.order = order self.chara_length = chara_length self.element_type = element_type self.cache_path = cache_path gmsh.initialize() gmsh.model.add("geometry") self.objects = {} self.default_objects = [] self.quad_objects = []
[docs] def add_rectangle(self, left, bottom, width, height, element="tri"): """add a rectangle to the geometry Parameters ---------- left: float the left boundary of the rectangle bottom: float the bottom boundary of the rectangle width: float the width of the rectangle height: float the height of the rectangle element: str, optional the type of the element, can be "tri" or "quad" default: "tri" Returns ------- MeshGen the mesh generator itself """ if self.element_type is not None: element = "tri" if self.element_type.startswith("triangle") else "quad" assert element in ["tri", "quad"] assert self.dimension == 2, f"dimension must be 2, but got {self.dimension}" rectangle = gmsh.model.occ.addRectangle(left, bottom, 0, width, height) gmsh.model.occ.synchronize() name = f"[{len(self.objects)}]rectangle({left},{bottom},{width},{height})" self.default_objects.append(name) self.objects[name] = (2,rectangle) if element == "quad": # gmsh.model.mesh.setTransfiniteSurface(rectangle) self.quad_objects.append(name) return self
[docs] def remove_rectangle(self, left, bottom, width, height): """remove the rectangle from the geometry Parameters ---------- left: float the left boundary of the rectangle bottom: float the bottom boundary of the rectangle width: float the width of the rectangle height: float the height of the rectangle Returns ------- MeshGen the mesh generator itself """ assert self.dimension == 2, f"dimension must be 2, but got {self.dimension}" rectangle = gmsh.model.occ.addRectangle(left, bottom, 0, width, height) difference, _ = gmsh.model.occ.cut([self.objects[i] for i in self.default_objects], [(2,rectangle)]) gmsh.model.occ.synchronize() name = f"[{len(self.objects)}]rectangle({left},{bottom},{width},{height})" self.objects[name] = (2,rectangle) return self
[docs] def add_circle(self, cx, cy, r, element="tri"): """add a circle to the geometry Parameters ---------- cx: float the x coordinate of the center cy: float the y coordinate of the center r: float the radius of the circle element: str, optional the type of the element, can be "tri" or "quad" default: "tri" Returns ------- MeshGen the mesh generator itself """ if self.element_type is not None: element = "tri" if self.element_type.startswith("triangle") else "quad" assert element in ["tri", "quad"] assert self.dimension == 2, f"dimension must be 2, but got {self.dimension}" circle = gmsh.model.occ.addDisk(cx, cy, 0, r, r) gmsh.model.occ.synchronize() name = f"[{len(self.objects)}]circle({cx},{cy},{r})" self.default_objects.append(name) self.objects[name] = (2,circle) if element == "quad": gmsh.model.mesh.setRecombine(2, circle) return self
[docs] def remove_circle(self, cx, cy, r): """remove the cirlce from the geometry Parameters ---------- cx: float the x coordinate of the center cy: float the y coordinate of the center r: float the radius of the circle Returns ------- MeshGen the mesh generator itself """ assert self.dimension == 2, f"dimension must be 2, but got {self.dimension}" circle = gmsh.model.occ.addDisk(cx, cy, 0, r, r) difference, _ = gmsh.model.occ.cut([self.objects[i] for i in self.default_objects], [(2,circle)]) gmsh.model.occ.synchronize() name = f"[{len(self.objects)}]circle({cx},{cy},{r})" self.objects[name] = (2,circle) return self
[docs] def add_cube(self, x, y, z, dx, dy, dz): """add a cube to the geometry, only works for 3d Parameters ---------- x: float the x coordinate of the center y: float the y coordinate of the center z: float the z coordinate of the center dx: float the width of the cube dy: float the height of the cube dz: float the depth of the cube Returns ------- MeshGen the mesh generator itself """ assert self.dimension == 3, f"dimension must be 3, but got {self.dimension}" cube = gmsh.model.occ.addBox(x, y, z, dx, dy, dz) name = f"[{len(self.objects)}]cube({x},{y},{z},{dx},{dy},{dz})" self.default_objects.append(name) self.objects[name] = (3,cube) return self
[docs] def remove_cube(self, x, y, z, dx, dy, dz): """remove the cube from the geometry, only works for 3d Parameters ---------- x: float the x coordinate of the center y: float the y coordinate of the center z: float the z coordinate of the center dx: float the width of the cube dy: float the height of the cube dz: float the depth of the cube Returns ------- MeshGen the mesh generator itself """ assert self.dimension == 3, f"dimension must be 3, but got {self.dimension}" cube = gmsh.model.occ.addBox(x, y, z, dx, dy, dz) difference, _ = gmsh.model.occ.cut([self.objects[i] for i in self.default_objects], [(3,cube)]) gmsh.model.occ.synchronize() name = f"[{len(self.objects)}]cube({x},{y},{z},{dx},{dy},{dz})" self.objects[name] = (3,cube) return self
[docs] def add_sphere(self, x, y, z, r): """add a sphere to the geometry, only works for 3d Parameters ---------- x: float the x coordinate of the center y: float the y coordinate of the center z: float the z coordinate of the center r: float the radius of the sphere Returns ------- MeshGen the mesh generator itself """ assert self.dimension == 3, f"dimension must be 3, but got {self.dimension}" sphere = gmsh.model.occ.addSphere(x, y, z, r) name = f"[{len(self.objects)}]sphere({x},{y},{z},{r})" self.default_objects.append(name) self.objects[name] = (3,sphere) return self
[docs] def remove_sphere(self, x, y, z, r): """remove the sphere from the geometry, only works for 3d Parameters ---------- x: float the x coordinate of the center y: float the y coordinate of the center z: float the z coordinate of the center r: float the radius of the sphere Returns ------- MeshGen the mesh generator itself """ assert self.dimension == 3, f"dimension must be 3, but got {self.dimension}" sphere = gmsh.model.occ.addSphere(x, y, z, r) difference, _ = gmsh.model.occ.cut([self.objects[i] for i in self.default_objects], [(3,sphere)]) gmsh.model.occ.synchronize() name = f"[{len(self.objects)}]sphere({x},{y},{z},{r})" self.objects[name] = (3,sphere) return self
[docs] def gen(self, show=False): """generate the mesh from the geometry Parameters ---------- show: bool, optional whether to show the mesh in the gmsh gui default: :obj:`False` Returns ------- torch_fem.mesh.Mesh the generated mesh """ if self.element_type is None: for obj in self.quad_objects: gmsh.model.mesh.setRecombine(*self.objects[obj]) elif self.element_type.startswith("quad"): for obj in self.default_objects: gmsh.model.mesh.setRecombine(*self.objects[obj]) gmsh.option.setNumber("Mesh.ElementOrder", self.order) gmsh.model.mesh.setSize(gmsh.model.getEntities(0), self.chara_length) gmsh.model.addPhysicalGroup(self.dimension, [self.objects[i][1] for i in self.default_objects]) gmsh.model.setPhysicalName(self.dimension, 1, "domain") # Generate the mesh gmsh.model.mesh.generate(self.dimension) if show: gmsh.fltk.run() # Save the mesh gmsh.write(self.cache_path) # Finalize Gmsh gmsh.finalize() mesh = Mesh.from_file(self.cache_path) os.remove(self.cache_path) return mesh