Tetgen剖分四面体网格学习
Tetgen安装
Windows系统下安装
1.下载好tetgen的源代码
从优快云链接或者官网连接直接下载即可。
2. 进行源代码编译
我使用的是Visual Studio 2013进行的编译,操作如下:
添加好下载的源码,如下图所示:
然后点击项目名称右键,选择如下图所示的操作,添加_CRT_SECURE_NO_WARNINGS
,就可以进行编译了,不然会报错。
编译好后,会生成tetgen.exe文件,如下图:
上面这些做好就可以使用了这个tetgen.exe 进行操作了。
Ubuntu操作系统下进行安装
在终端直接一条命令就可以:
sudo apt install tetgen
TetGen简单使用
1. Windows操作系统下使用
用管理员命令打开PowerShell:
运行如下两行代码:
cd D:\tetgen\
.\tetgen.exe -p example.poly
其结果如下:
Opening example.poly.
Delaunizing vertices...
Delaunay seconds: 0
Creating surface mesh ...
Surface mesh seconds: 0.001
Constrained Delaunay...
Constrained Delaunay seconds: 0
Removing exterior tetrahedra ...
Exterior tets removal seconds: 0
Optimizing mesh...
Optimization seconds: 0
Writing example.1.node.
Writing example.1.ele.
Writing example.1.face.
Writing example.1.edge.
Output seconds: 0.004
Total running seconds: 0.005
Statistics:
Input points: 28
Input facets: 23
Input segments: 44
Input holes: 2
Input regions: 2
Mesh points: 28
Mesh tetrahedra: 68
Mesh faces: 160
Mesh faces on facets: 50
Mesh edges on segments: 44
2. Ubuntu操作系统下使用
切换到example所在的目录,输入命令行:
tetgen -p example.poly
结果如下:
Opening example.poly.
Delaunizing vertices...
Delaunay seconds: 0
Creating surface mesh ...
Surface mesh seconds: 0
Constrained Delaunay...
Constrained Delaunay seconds: 0
Removing exterior tetrahedra ...
Exterior tets removal seconds: 0
Optimizing mesh...
Optimization seconds: 0
Writing example.1.node.
Writing example.1.ele.
Writing example.1.face.
Writing example.1.edge.
Output seconds: 0
Total running seconds: 0
Statistics:
Input points: 28
Input facets: 23
Input segments: 44
Input holes: 2
Input regions: 2
Mesh points: 28
Mesh tetrahedra: 68
Mesh faces: 160
Mesh faces on facets: 50
Mesh edges on segments: 44
3. 文件在Windows下用Tetview打开
命令行操作如下:
.\tetview-win.exe example.1.ele
效果是这样的:
TetGen详细操作
TetGen文件格式说明
下表列出TetGen使用的所有格式。所有文件都是ASCII码形式,可能包含有以#为前缀的
注释。点,四面体,面,洞和最小体积约束编号必须是连贯的,开始编号为1或者0。然而,
所有的输入文件必须是一致的。当读.node(.poly,.smesh)时,TetGen自动检测你选择的文
件。当其他程序调用TetGen时,如果你想要对象的编码从0开始,那么使用-z命令。
文件类型 | 输入或者输出 | 属性 |
---|---|---|
.node | input/output | 节点列表文件 |
.poly | input | PLC. |
.smesh | input/output | PLC. |
.ele | input/output | 四面体文件 |
.face | input/output | 三角形面文件 |
.edge | input/output | 边界 |
.vol | input | 最大体积列表 |
.var | input | 约束列表 |
.neigh | output | A list of neighbors |
1. .node
文件
第一行:<点个数><维数(3)><属性个数><边界标识(0或者1)>
剩下的行为点列表:
<点编号><x><y><z>[属性][边界标识]
例如:
158373 3 0 1 #表示有158373个点,维数是三维,属性个数为0,边界标识为1
1 -10000 -10000 -10000 1 # 表示点号为1,三维坐标是(-10000,-10000,-10000),边界标识是1
2 -10000 10000 -10000 1
3 10000 10000 -10000 1
4 10000 -10000 -10000 1
5 -10000 -10000 10000 1
6 -10000 10000 10000 1
7 10000 10000 10000 1
8 10000 -10000 10000 1
9 -10000 -10000 0 1
10 -10000 10000 0 1
- 属性为与点关联的浮点型物理量值(例如:质量、导电性等),并不加修改的复制到格网中。如果-p,-q,-a或者-i是选择的,每个Steiner点添加到格网中默认属性值为0。
- 如果指明了’-w’(权重Delaunay四面体剖分)项,第一个属性为相应点的权重。
- 如果第一行的第四部分值为1,文件中最后一列为边界标识。边界标识用于识别边界点(点在PLC面上),除了TetGen禁用了-B了开关,否则TetGen生成的.node文件包含边界标识。
边界标识在输出文件.node文件是选择如下:
- 如果在输入文件中点指定了非零边界标识,那么它在输出文件中将有相同的标识。
- 另外,如果节点在PLC表面上带有一个非零边界标识,那么这个表面上的节点将指定相同的标识。如果节点在若干的表面上,节点的边界标识在这些表面标识中任意选择。
- 如果节点在格网的边界上,那么节点的标识指定为1。
- 其他点分配标识为0
- TetGen可以决定那些点在边界上,输入边界标识为0(或者没有使用标识)将结果在输出所有点的边界标识为1。
2. .poly
文件
- 一个
.poly
文件表示一个PLC(piecewise linear complex [分段线性复合体])作为附加信息。尽管这儿没有限制PLC的表面TetGen需要PLC表示的完整的面边界格网范围。 .poly
文件格式包含了四部分,点列表,面列表,洞(体)点集列表和范围属性列表。前三部分是必须的,第四部分是可选的。
这四部分详细描述如下:
-
节点列表 所有节点的列表和它与.node具有完全相同的个数。<点个数>可以设置为0,指明这些点集存放在分离的.node文件中。
第一行: <点个数> <维数 (3)> <属性个数> <边界标识 (0 or 1)> 下面为点的列表: <点编号> <x> <y> <z> [属性] [边界标识] ...
-
面列表 每个面包含一个共面直线图(PSLG, planar straight line graph),它是一个可以包含线段,单点和洞的多边形。多边形(polygons)列表包含面。每个多边形有n个角点,n≥1。它可以退化,当n=1时表示点,当n=2时表示线段。格式如下:
某一行: <多边形个数s> [洞个数] [边界标识] 下面是多边形列表: <角点个数> <角点 1> <角点 2> ... <角点 #> ... 下面是洞列表 <洞编号> <x> <y> <z> ...
```shell
# poly文件示例
# Part 1 - node list 第一部分:点列表
# node count, 3 dim , no attribute, no boundary marker
256 3 0 0
1 36.0 0.0 0
2 35.95663642338621 1.7664362757870484 0
3 35.82665016019909 3.528617051864182 0
4 35.61035435873212 5.282297080393023 0
5 35.308270094516296 7.023251592580617 0
6 34.92112511500358 8.7472864765175 0
7 34.44985208635952 10.450248381160645 0
8 33.89558634658875 12.128034722119923 0
9 33.25966317040632 13.776603565143231 0
10 32.54361455244396 15.391983363490155 0
11 31.74916551654078 16.970282525735914 0
...
# Part 2 - face list 第二部分:面列表
130 1 # 面个数 有边界标识
# facet count, boundary marker
1 0 1 # 一个多边形,没有洞,边界标识1, 下面是这么多点构成的一个圆柱面
128 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
1 0 4 # 一个多边形,没有洞,边界标识4
4 1 2 130 129 # 4个点(1,2,130,129)构成的面
1 0 4
4 2 3 131 130
1 0 4
4 3 4 132 131
1 0 4
4 4 5 133 132
1 0 4
4 5 6 134 133
...
下图就是该文件的具体图例
3. .smesh
文件
4. .ele
文件
5. .face
文件
# face文件示例
8800 1 # 8800个面,没有洞,有边界标识
1 2595 1194 2593 4 # 第一个面 三个节点组成 边界标识为4
2 1908 1210 1906 4
3 1203 2595 2597 4
4 427 429 2335 4
5 57 4340 5196 1
6 3345 1714 3347 4
7 648 2622 3220 1
8 1338 582 581 4
9 471 3845 3846 4
...
6. .edge
文件
7. .vol
文件
8. mtr
文件
9. var
文件
10. .neigh
文件
Tetgen生成输入文件程序例子
- 看了前面这么多介绍,可能大家还是不怎么会生成自己的模型文件,这里给出一个生成自己模型文件的python程序,大家可以按照自己的需要来修改。
import os
import sys
import time
import math
import numpy as np
nn=4
dlz = [0 for col in range(nn)]
dlz=[20000, 2000, 10000, 20000]
dlx=40000
dly=40000
sumdlz=sum(dlz)
print('The length of the cuboid %0.3f' % dlx)
print('The width of the cuboid is %0.3f' % dly)
print('The height of the cuboid is %0.3f' % sumdlz)
boxname = 'cuboid' + '-' + str(dlx) + '-' + str(dly) + '-' + str(sumdlz) + '.poly'
boxf = open(boxname,'w')
nx=100
ny=50
nodenum_part=2*(nx+ny)
nodenum=(nn+1)*nodenum_part
dlz1=np.append(0,dlz)
for i in range(0, nn):
dlz1[i+1]=dlz1[i]+dlz1[i+1]
#save the information of nodes 储存所有节点信息
node = [[0 for col in range(4)] for row in range(nodenum)]
for i in range(0,nodenum):
node[i] = [i+1,0,0,0] # 第一个值储存节点编号
for i in range(0, len(dlz1)):
for j in range(0, int(ny/2)): # 底面上节点的三维坐标
node[i*nodenum_part+j][1:] = [dlx/2, j*dly/ny, dlz1[i]-sumdlz]
for j in range(int(ny/2), int(ny/2+nx/2)):
node[i*nodenum_part+j][1:] = [dlx/2-(j-int(ny/2))*dlx/nx, dly/2, dlz1[i]-sumdlz]
for j in range(int(ny/2+nx/2), int(ny/2+nx)):
node[i*nodenum_part+j][1:] = [-(j-int(nx/2+ny/2))*dlx/nx, dly/2, dlz1[i]-sumdlz]
for j in range(int(ny/2+nx), int(ny+nx)):
node[i*nodenum_part+j][1:] = [-dlx/2, dly/2-(j-int(nx+ny/2))*dly/ny, dlz1[i]-sumdlz]
for j in range(int(nx+ny), int(nx+ny+ny/2)):
node[i*nodenum_part+j][1:] = [-dlx/2, -(j-int(nx+ny))*dly/ny, dlz1[i]-sumdlz]
for j in range(int(nx+ny+ny/2), int(nx+ny+ny/2+nx/2)):
node[i*nodenum_part+j][1:] = [-dlx/2+(j-int(nx+ny+ny/2))*dlx/nx, -dly/2, dlz1[i]-sumdlz]
for j in range(int(nx+ny+ny/2+nx/2), int(nx+ny+ny/2+nx)):
node[i*nodenum_part+j][1:] = [(j-int(nx+ny+nx/2+ny/2))*dlx/nx, -dly/2, dlz1[i]-sumdlz]
for j in range(int(nx+ny+ny/2+nx), int(nx+ny+ny+nx)):
node[i*nodenum_part+j][1:] = [dlx/2, -dly/2+(j-int(nx+ny+nx+ny/2))*dly/ny, dlz1[i]-sumdlz]
#save the information of facets 存储面的信息
facenum = nn*nodenum_part + nn + 1 # 侧面的面单元和每个立方体的上底面和下底面的和
faceAll = [[0 for col in range(6)] for row in range(facenum-nn-1)] # 初始化面的数据
for j in range(0,nn):
for i in range(0, nodenum_part-1):
faceAll[j*nodenum_part+i] = [4, j*nodenum_part+i+1, j*nodenum_part+i+2, j*nodenum_part+i+2+nodenum_part, j*nodenum_part+i+1+nodenum_part, j+nn+2]
faceAll[(j+1)*nodenum_part-1] = [4, (j+1)*nodenum_part, j*nodenum_part+1, (j+1)*nodenum_part + 1, (j+2)*nodenum_part, j+nn+2]
face = [[0 for col in range(5)] for row in range(facenum-nn-1)] # 初始化面
faceMark = [[0 for col in range(3)] for row in range(facenum-nn-1)] # 初始化面标记
for i in range(0,facenum-nn-1):
face[i] = faceAll[i][0:5]
faceMark[i] = [1,0,faceAll[i][-1]]
meshsize = [-1] # the size of mesh
numVolumMark = nn # nn regions
SolventMark = [4, 0.1, 1, 10] # the region mark is 1
region = [[0 for col in range(6)] for row in range(numVolumMark)]
for i in range(0,len(region)):
region[i] = [i+1,0,0,0,0,0]
for i in range(0,len(region)):
region[i][1] = (node[i*nodenum_part][1] + node[int(nodenum_part/2+(i+1)*nodenum_part)][1])/2
region[i][2] = (node[i*nodenum_part][2] + node[int(nodenum_part/2+(i+1)*nodenum_part)][2])/2
region[i][3] = (node[i*nodenum_part][3] + node[int(nodenum_part/2+(i+1)*nodenum_part)][3])/2
region[i][4] = SolventMark[i]
region[i][5] = meshsize[0]
line = '# Part 1 - node list\n'
boxf.write(line)
line = '# <# of points> <dimension 3> <# of attributes> <boundary markers>\n'
boxf.write(line)
line = str(nodenum) + '\t' + '3' + '\t' + '0' + '\t' + '1' + '\n'
boxf.write(line)
for i in range(0, nodenum):
line = str(node[i][0]) + '\t' + str(node[i][1]) + '\t' + str(node[i][2]) + '\t' + str(node[i][3]) + '\t' + str(0) + '\n'
boxf.write(line)
line = '# Part 2 - face list\n'
boxf.write(line)
line = str(facenum) + '\t' + '1' + '\n'
boxf.write(line)
line = '# facet count, boundary marker\n'
boxf.write(line)
line = '#(Eg.) one polygon, no hole, boundary market is 1\n'
boxf.write(line)
for j in range(0,nn):
line = str(1) + '\t' + str(0) + '\t' + str(j+1) + '\n'
boxf.write(line)
line = str(nodenum_part) + '\t'
for i in range(j*nodenum_part, (j+1)*nodenum_part):
line += str(i+1) + '\t'
line += '\n'
boxf.write(line)
for k in range(j*nodenum_part, (j+1)*nodenum_part):
line = str(faceMark[k][0]) + '\t' + str(faceMark[k][1]) + '\t' + str(faceMark[k][2]) + '\n'
boxf.write(line)
line = str(face[k][0]) + '\t' + str(face[k][1]) + '\t' + str(face[k][2]) + '\t' + str(face[k][3]) + '\t' + str(face[k][4]) + '\n'
boxf.write(line)
# the boundary mark of the top facet is
line = str(1) + '\t' + str(0) + '\t' + str(nn+1) + '\n'
boxf.write(line)
line = str(nodenum_part) + '\t'
for i in range(nn*nodenum_part, (nn+1)*nodenum_part):
line += str(i+1) + '\t'
line += '\n'
boxf.write(line)
line = '# Part 3 - hole list\n'
boxf.write(line)
line = '# <# of polygons> <# of holes> <boundary marker>\n'
boxf.write(line)
line = '0' + '\n'
boxf.write(line)
line = '# Part 4 - region list\n'
boxf.write(line)
line = '#<region #> <x> <y> <z> <region attribute> <region volume constraint>\n'
boxf.write(line)
line = str(numVolumMark) + '\n'
boxf.write(line)
for i in range(0,len(region)):
line = str(region[i][0]) + '\t' + str(region[i][1]) + '\t' + str(region[i][2]) + '\t' + \
str(region[i][3]) + '\t' + str(region[i][4]) + '\t' + str(region[i][5]) + '\n'
boxf.write(line)
boxf.close()
#boxmeshfile = 'cylinder_' + str(dx) + '_' + str(dy) + '-' + 'refined1'+ '.mesh'
cmd = 'tetgen.exe -pq1.3iaAV -k ' + boxname
os.system(cmd)
该程序运行完成后会生成如下几个文件:
其效果图是这样:
- 大家在看懂程序后可以生成自己想要的任意层状介质的poly文件,和剖分文件。希望这个程序对大家学习和使用有所帮助。