Gmsh 极简入门

Gmsh logo Gmsh logo

Gmsh is a free 3D finite element mesh generator with a built-in CAD engine and post-processor. Its design goal is to provide a fast, light and user-friendly meshing tool with parametric input and advanced visualization capabilities. Gmsh is built around four modules: geometry, mesh, solver and post-processing. The specification of any input to these modules is done either interactively using the graphical user interface, in ASCII text files using Gmsh’s own scripting language (.geo files), or using the C++, Python or C API.

—-www.gmsh.info/

Gmsh 是一个开源的具有内置 CAD 引擎和后期处理子的三维有限元网格生成器.

  • 它的目的是提供一个快速的, 轻量级的, 用户友好的网格工具, 并且具有参数化输入和后期可视化能力. 目前 Gmsh 具有四个模块: 几何模块, 网格模块, 解子模块和后期处理模块, 且所有模块的使用都可以通过两种方式: 命令行和 GUI. 有限元网格生成工具有很多种, 比如 MeshGen, Hypermesh, 以及一个 MATLAB 小工具 DistMesh, 等等.

  • Gmsh 提供 Linux, Mac and Windows 版本, 并且在下载的安装包的 tutorial 目录中包含一个介绍关键概念和一些实例的教程. 这个教程里的实例都有非常详细的脚本注释, 易于入门, 是学习 Gmsh 的第一步.

为什么需要网格

网格生成 (Mesh generation or grid generation) 又可以称为网格剖分, 即将一个有界的连续的几何空间划分成由有限个单纯形, 矩形, 多边形或多面体构成的离散的几何空间, 是有限元方法求解偏微分方程的第一步. 高质量的网格往往依赖于求解区域及方程特性, 同时一般能够带来更精确的有限元解.

表示网格最简单的数据结构应该就是 单元-节点 结构, 例如 $[0,1]\times[0,1]$ 上一个简单的三角形网格可以由两个变量表示 pointselements:

points =        # 所有节点的坐标(x, y, z), 对二维空间 z=0
[[0.  0.  0. ]  # vertice 0
 [1.  0.  0. ]  # vertex 1
 [1.  1.  0. ]  # vertex 2
 [0.  1.  0. ]  # vertex 3
 [0.5 0.5 0. ]] # vertex 4
elements =      # 每一个网格单元由那些节点构成, 一般节点按逆时针排序
[[0 1 4]        # element 0
 [3 0 4]        # element 1
 [1 2 4]        # element 2
 [2 3 4]]       # element 3

对于简单的求解区域, 例如 [0,1]\times[0,1], 我们可以很容易划分一个三角形网格, 甚至直接写出 C 或者 Python 代码生成网格. 但是, 当求解区域是一个复杂多边形或边界是光滑曲线时, 手动生成网格就变得相当困难或者烦琐, 借助相关的网格生成软件或程序是必须的. 考虑到求解区域复杂度, 网格单元分类, 网格单元大小及形状正则性, 网格加细等等, 可以想象关于网格生成是相当有挑战性的研究方向, 有相当多的会议和研讨会将网格生成作为议题, 还有不少知名杂志持续地接收关于网格生成的研究成果, 更多介绍请参考维基百科: Mesh generation.

好消息是, 如果不是专门做网格生成的研究, 我们只需利用现成的网格生成工具, 如 Gmsh, 来生成需要的网格.

如何使用 Gmsh

如果你是第一次利用 Gmsh 生成网格, 可以参考如下几步:

  1. 官网下载并安装最新版 Gmsh. 无论你用 Win, MacOS 还是 Linux, Gmsh 均提供相应二进制文件;

  2. 正确编写脚本文件 .geo. tutorial 目录下有许多脚本示例, 非常适合入门; 或者以下面的脚本作为第一个例子. 新建空白文本文档 unitSquare.geo*, 拷贝如下内容并保存:

    // unit square
    //
    len = 1.;    //Mesh size
    
    Point(1) = {0,0,0,len}; Point(2) = {1,0,0,len};
    Point(3) = {1,1,0,len}; Point(4) = {0,1,0,len};
    Line(1) = {1,2}; Line(2) = {2,3};
    Line(3) = {3,4}; Line(4) = {4,1};
    
    Line Loop(5) = {1,2,3,4};
    Plane Surface(1) = {5};
    
    // 生成标准三角元,最后的参数可以是Left, Right, 或者Alternate
    //Transfinite Surface{1}={1,2,3,4} Alternate;
    
    // 通过合并三角元生成标准矩形元
    // Recombine Surface{1};
    
    Physical Line(1) = {2,4};
    Physical Surface(2) = {1};
  3. 在 Gmsh 中打开该文件, 界面左边找到 Modules->Mesh->2D 并点击; 然后点击 File-> Save mesh, 就可以在相同文件夹下生成同名网格文件 unitSquare.msh;

我们需要的网格 (pointselements) 就存储在文件 unitSquare.msh 中, 下一步就是提取这些数据 (后面有介绍) 并应用在有限元程序或其它程序中.

msh 文件

Gmsh 生成的 .msh 文件中保存了全部网格信息, 也就完成了网格生成的任务. .msh 文件的详细解释请参考官网: MSH 文件格式.

上面的 .geo 脚本生成的 MSH 文件 unitSquare.msh 如下 (Gmsh 4.4.1):

$MeshFormat
4.1 0 8
$EndMeshFormat
$Entities
4 4 1 0
1 0 0 0 0 
2 1 0 0 0 
3 1 1 0 0 
4 0 1 0 0 
1 0 0 0 1 0 0 0 2 1 -2 
2 1 0 0 1 1 0 1 1 2 2 -3 
3 0 1 0 1 1 0 0 2 3 -4 
4 0 0 0 0 1 0 1 1 2 4 -1 
1 0 0 0 1 1 0 1 2 4 1 2 3 4 
$EndEntities
$Nodes
7 5 1 5
0 1 0 1
1
0 0 0
0 2 0 1
2
1 0 0
0 3 0 1
3
1 1 0
0 4 0 1
4
0 1 0
1 2 0 0
1 4 0 0
2 1 0 1
5
0.5 0.5 0
$EndNodes
$Elements
3 6 1 6
1 2 1 1
1 2 3 
1 4 1 1
2 4 1 
2 1 2 4
3 1 2 5 
4 4 1 5 
5 2 3 5 
6 3 4 5 
$EndElements

Python 中使用 Gmsh

利用 meshio

Meshio 是 Python 下的标准模块, 可以读写许多网格文件格式, 例如:

Abaqus, ANSYS msh, AVS-UCD, CGNS, DOLFIN XML, Exodus, FLAC3D, H5M, Kratos/MDPA, Medit, MED/Salome, Nastran (bulk data), Neuroglancer precomputed format, Gmsh (format versions 2.2, 4.0, and 4.1), OBJ, OFF, PERMAS, PLY, STL, Tecplot .dat, TetGen .node/.ele, SVG (2D only, output only), SU2, UGRID, VTK, VTU, WKT (TIN), XDMF.
https://pypi.org/project/meshio/

Meshio 模块的使用非常简单, 官网有详细的说明: https://pypi.org/project/meshio/. 利用 meshio 读取上面的文件 unitSquare.msh 的程序如下:

import meshio 
import numpy as np

mesh = meshio.read('./unitSquare.msh', \
    file_format='gmsh')

# 保存数据或打印, 或者提供给其它使用网格的程序, 如有限元等等
# np.savetxt('points.txt', mesh.points)
# np.savetxt('cells.txt', mesh.cells['triangle'])

print(mesh.points,'\n', mesh.cells['triangle'])
print('\n')

# 输出如下:
# [[0.  0.  0. ]
#  [1.  0.  0. ]
#  [1.  1.  0. ]
#  [0.  1.  0. ]
#  [0.5 0.5 0. ]] 
#  [[0 1 4]
#  [3 0 4]
#  [1 2 4]
#  [2 3 4]]

当网格数据被提取并保存在文本文件里之后, 其它的程序语言如 Matlab, C/C++, Scilab 或者 R 等, 均可以利用这些网格数据.

利用 pygmsh

如果你以 Python 做为生产力编程语言, 并且需要用到网格, 那么就非常有必要了解一下 Pygmsh. 它的目的是提供 Gmsh 的 Python 接口, 并改进一些直接利用 Gmsh 的缺点.

  • 例如在 Gmsh 脚本中每一个几何实体 (点, 线, 面等) 必须手动分配一个编号 (1, 2, …), 并且必须检查这些编号是唯一的, 但是如果利用 Pygmsh, 这些几何实体可以变量名命名 (点可以用 p1, p2, …命名, 线为 l1, l2, …), 这样就避免了对编号是否唯一的检查.
#! /usr/bin/env python3
# 在矩形  [0,1]*[0,1] 上生成三角形网格
# 
import pygmsh
import meshio
import numpy as np
# 构造一个空的几何体数据结构
# 
geom = pygmsh.built_in.Geometry()
# 
# 添加矩形[0,1]*[0,1]
# 
geom.add_rectangle(
        0.0, 1.0,
        0.0, 1.0,
        0.0,
        1.
)
#
# 生成网格 mesh, 其具有数据结构:
#   |- mesh.points              # coordinates of nodes (N*3)
#   |- mesh.cells['line']       # mesh edges (N*2)
#   |- mesh.cells['triangle']   # triangles (N*3)
#
mesh = pygmsh.generate_mesh(geom)
#
# 保存数据或打印, 或者提供给其它使用网格的程序, 如有限元等等
# np.savetxt('points.txt', mesh.points)
# np.savetxt('cells.txt', mesh.cells['triangle'])
#
print(mesh.points)
print(mesh.cells['triangle'])

附录: 花瓣界面网格

给出一个生成花瓣界面网格的GMSH示例。花瓣界面的网格可用于验证求解二阶椭圆界面问题的算法的有效性,利用 Gmsh 内置的脚本文件可以轻易生成具有复杂形状的界面,这使得Gmsh成为生成各种网格的一个非常实用工具。

下面给出具体示例。目的是在区域 [-1,1]\times [-1,1] 上生成一个五叶花瓣界面,其表达式如下:

r = 1/2 + \sin( 5 \theta ) / 7,\quad \theta\in[0, 2\pi].

容易知道其外法向量为 n=(y'(\theta), -x'(\theta))/\sqrt{x'(\theta)^2+y'(\theta)^2}, 这里 x=r\cos(\theta),\ y=r\sin(\theta). 其图形如下图所示.
[图]
生成图2的脚本文件如下所示, 这里要注意两个地方: 1.生成五叶花瓣的循环;2. Coherence去掉重复的点.

/*
*   To generate petal interface in square [-1,1]^2, and the interface is 
* parametrized with the polar angle 'theta' [1]: 
*   r = 1/2 + Sin( 5* theta ) / 7, [0, 2*pi].
*   
*/
ls = .2;
nPoints = 9;    // Number of points in one petal
start = Pi/2-2*Pi/10;  end = Pi/2+2*Pi/10;
step = (end-start)/(nPoints-1);

Point(1) = {-1,-1,0,ls}; Point(2) = {1,-1,0,ls};
Point(3) = {1,1,0,ls};  Point(4) = {-1,1,0,ls};

Line(1) = {1,2}; Line(2)={2,3}; Line(3)={3,4}; Line(4)={4,1};

For k In {0:4}   // Five petal
    start = Pi/2-Pi/5 + k*2*Pi/5;    // the Position of each petal
    For i In {0:nPoints-1}
        theta = start + step*i;
        pList[i] = newp;
        Point(pList[i]) = {(0.5+Sin(5*theta)/7)*Cos(theta), 
                            (0.5+Sin(5*theta)/7)*Sin(theta),0,ls};
    EndFor
    petalLines[k] = newl;
    Spline(petalLines[k]) = pList[];  //Creates spline curve, i.e., a petal.
EndFor
Coherence; //Delete all duplicate elementary geometrical entities(Points here).

Line Loop(11) = petalLines[]; Plane Surface(12) = {11};
Line Loop(13) = {1,2,3,4};   Plane Surface(14) = {13,11};
Physical Surface(1)={14};   // Exterior part '1'
Physical Surface(2)={12};   // Interior part '2'

You may also like...

2 Responses

  1. 匿名说道:

    谢谢更新!

  2. 匿名说道:

    多谢!

发表评论

电子邮件地址不会被公开。