第23章 离散实体(Discrete Entity)与混合模型:地形网格实战
23.1 学习目标
- 理解离散实体(Discrete Entity)的概念:完全由网格定义的几何体(无 CAD 参数化曲面)
- 掌握
addDiscreteEntity(dim, tag, {boundaryTags})创建离散点/线/面/体,以及带符号边界标签的方向约定 - 学会程序化注入网格数据:
mesh::addNodes()写入节点坐标 +mesh::addElementsByType()注入单元拓扑 - 掌握常用单元类型编号:15=点(1节点), 1=线段(2节点), 2=三角形(3节点), 3=四边形(4节点), 4=四面体(4节点), 5=六面体(8节点) 等
- 理解
mesh::reclassifyNodes()将节点从聚合实体重新分类到正确的边界实体(边/点) - 理解
mesh::createGeometry()从网格生成几何参数化,使离散面可重新划分网格 - 掌握混合模型(Hybrid Model):将离散面与内置 CAD 实体组合为完整计算域(仅
geo模块支持,OpenCASCADE 不支持) - 完整案例:从 NxN 高度数据点程序化构建三角面离散曲面并组合 CAD 下卧层体
23.2 核心概念说明
23.2.1 离散实体 vs CAD 实体
Gmsh 中有两类几何实体:
| 特性 | CAD 实体(Built-in / OCC) | 离散实体(Discrete Entity) |
|---|---|---|
| 几何表示 | 参数化曲面/曲线方程(NURBS) | 完全由网格数据定义 |
| 创建方式 | geo::addPoint/Line/Surface 或 occ::addBox |
addDiscreteEntity(dim, tag, boundaryTags) |
| 网格划分 | 自动生成网格 | 网格需程序化注入 |
| 可重划网格 | 天然支持 | 需先调用 createGeometry() 生成参数化 |
| 与 CAD 实体混合 | 可以(geo 模块) | 可以(作为 CAD 实体的边界) |
离散实体的本质:它是一种"只存储网格、无解析几何"的实体——类似 STL 文件的三角形面片不包含底层 NURBS 曲面。Gmsh 的离散实体机制让你可以直接操作这样的纯网格几何,并与其他 CAD 实体组合。
23.2.2 单元类型编号速览
Gmsh 使用整型编号标识单元类型,常用值如下:
| 编号 | 名称 | 维度 | 节点数 | 典型用途 |
|---|---|---|---|---|
| 15 | Point | 0 | 1 | 离散点 |
| 1 | Line (2-node) | 1 | 2 | 离散边 / 2D 边界 |
| 2 | Triangle (3-node) | 2 | 3 | 离散面 / 地形表面 |
| 3 | Quadrangle (4-node) | 2 | 4 | 结构网格面 |
| 4 | Tetrahedron (4-node) | 3 | 4 | 三维体网格 |
| 5 | Hexahedron (8-node) | 3 | 8 | 结构体网格 |
| 6 | Prism (6-node) | 3 | 6 | 三棱柱(楔形) |
| 7 | Pyramid (5-node) | 3 | 5 | 金字塔过渡单元 |
| 8 | Line3 (3-node) | 1 | 3 | 二阶线 |
| 9 | Triangle6 (6-node) | 2 | 6 | 二阶三角形 |
23.2.3 混合模型原理
混合模型(Hybrid Model)是指同一计算域中同时包含离散实体和 CAD 实体,两者边界相互联结形成完整 B-rep(Boundary Representation,边界表示)。
典型案例 -- 地形网格:
┌──────────────────────────────┐
│ 离散面 (Discrete Surface) │ ← 程序化注入的三角网格(地形)
│ 标签: (2, 1) │
├──────┬──────┬──────┬──────┤
│ 侧面3│ 侧面4│ 侧面5│ 侧面6│ ← CAD 面(geo::addPlaneSurface)
├──────┴──────┴──────┴──────┤
│ 底面 (s1) │ ← CAD 面
├──────────────────────────────┤
│ 体 v1 = {s1, s3, s4, │
│ s5, s6, 离散面1} │ ← 混合体:5个CAD面 + 1个离散面
└──────────────────────────────┘
关键约束:
- 只有 gmsh::model::geo 模块(内置 CAD 内核)支持混合模型
- gmsh::model::occ 模块(OpenCASCADE)不支持——其面边界必须全是 OCC 曲线
- 混合时 CAD 曲线的端点可以直接连接到离散点标签
23.2.4 边界方向约定与工作流
addDiscreteEntity 对于二维面,边界曲线标签通过带符号的列表指定:
// 边界 = {1, 2, -3, -4} 的含义:
// +1: 沿曲线1正方向(起点→终点) +2: 沿曲线2正方向
// -3: 沿曲线3负方向(终点→起点) -4: 沿曲线4负方向
符号确保边界曲线形成一致的逆时针(CCW)环绕方向。Gmsh B-rep 约定:二维面外边界为逆时针,内边界(孔洞)为顺时针。
完整工作流:
程序化生成节点坐标
│
▼
addDiscreteEntity(0/1/2, ...) ← 创建离散点、线、面(拓扑骨架)
│
▼
mesh::addNodes(2, 1, ...) ← 节点集中注入离散面
│
▼
mesh::addElementsByType(...) ← 注入点、线、三角单元
│
▼
mesh::reclassifyNodes() ← 节点从面重分类到正确的边/点
│
▼
mesh::createGeometry() ← 从网格生成参数化(可选,但推荐)
│
▼
geo::addPoint/Line/Surface... ← 创建 CAD 下卧层(混合模型)
│
▼
mesh::generate(3) ← 三维网格生成
23.3 C++ 代码逐段讲解
23.3.1 头文件、初始化与数据准备
#include <set>
#include <iostream>
#include <gmsh.h>
int main(int argc, char **argv)
{
gmsh::initialize();
gmsh::model::add("ch23");
int N = 100; // N x N 控制点网格
// lambda: 二维索引 (i,j) → 唯一节点标签 (从1开始)
auto tag = [N](std::size_t i, std::size_t j) { return (N + 1) * i + j + 1; };
std::vector<double> coords; // {x0,y0,z0, x1,y1,z1, ...}
std::vector<std::size_t> nodes; // 节点标签(与 coords 对应)
std::vector<std::size_t> tris; // 三角形连接(每3个标签1个三角)
std::vector<std::size_t> lin[4]; // 4条边界上的线段连接
std::size_t pnt[4] = {tag(0,0), tag(N,0), tag(N,N), tag(0,N)}; // 角点
解释:tag(i,j) 将二维控制点索引扁平化为一维节点标签。N=100 定义了 101x101 的节点网格。lin[4] :lin[0]=底边(j=0)、lin[1]=右边(i=N)、lin[2]=顶边(j=N)、lin[3]=左边(i=0)。pnt[4] 为 4 个角点标签。
for(std::size_t i = 0; i < N + 1; i++) {
for(std::size_t j = 0; j < N + 1; j++) {
nodes.push_back(tag(i, j));
coords.insert(coords.end(), {
(double)i / N, // x ∈ [0, 1]
(double)j / N, // y ∈ [0, 1]
0.05 * sin(10 * (double)(i + j) / N) // z = 起伏高度
});
解释:遍历 101x101 控制点网格。X/Y 均匀分布,Z 由正弦函数生成模拟地形起伏(振幅 0.05)。实际工程中可替换为 DEM(数字高程模型)数据。
// 每个单元格被对角线切分为 2 个三角形
if(i > 0 && j > 0) {
tris.insert(tris.end(),
{tag(i-1,j-1), tag(i,j-1), tag(i-1,j)}); // 左下三角
tris.insert(tris.end(),
{tag(i,j-1), tag(i,j), tag(i-1,j)}); // 右上三角
}
// 边界线段:左右边界 (i==0 或 i==N) 和上下边界 (j==0 或 j==N)
if((i == 0 || i == N) && j > 0) {
std::size_t s = (i == 0) ? 3 : 1;
lin[s].insert(lin[s].end(), {tag(i, j - 1), tag(i, j)});
}
if((j == 0 || j == N) && i > 0) {
std::size_t s = (j == 0) ? 0 : 2;
lin[s].insert(lin[s].end(), {tag(i - 1, j), tag(i, j)});
}
}
}
解释:每个控制网格单元 [i-1,i] x [j-1,j] 沿对角线 (i,j-1)-(i-1,j) 分为两个三角形。所有三角形的顶点顺序一致保持 CCW。边界线段在 i==0(右)、i==N(左)、j==0(底)、j==N(顶) 四条边上生成,后续作为离散曲线的网格数据注入。
23.3.2 创建离散实体拓扑
// 4 个离散点(维度 0,标签 1-4,无边界)
for(int i = 0; i < 4; i++) gmsh::model::addDiscreteEntity(0, i + 1);
// 设置角点模型坐标(使 GUI 显示 & CAD 连接可用)
gmsh::model::setCoordinates(1, 0, 0, coords[3 * tag(0, 0) - 1]);
gmsh::model::setCoordinates(2, 1, 0, coords[3 * tag(N, 0) - 1]);
gmsh::model::setCoordinates(3, 1, 1, coords[3 * tag(N, N) - 1]);
gmsh::model::setCoordinates(4, 0, 1, coords[3 * tag(0, N) - 1]);
解释:addDiscreteEntity(0, tag) 创建离散点。标签 1-4 分别对应四个角点 (0,0)、(1,0)、(1,1)、(0,1)。setCoordinates() 设置模型坐标——离散点没有默认坐标,必须显式设置才能用于 GUI 显示和与 CAD 实体的连接。坐标索引 3*tag-1 为 Z 分量(因为 coords 中每个节点 3 个 double,从索引 0 开始)。
// 4 条离散曲线(维度 1,标签 1-4,以角点为端点)
for(int i = 0; i < 4; i++)
gmsh::model::addDiscreteEntity(1, i + 1, {i + 1, (i < 3) ? (i + 2) : 1});
// 1 张离散面(维度 2,标签 1,4条曲线为边界,CCW 定向)
gmsh::model::addDiscreteEntity(2, 1, {1, 2, -3, -4});
解释:addDiscreteEntity(1, tag, {startPoint, endPoint}) 创建离散曲线——曲线1(点1→点2,底边),曲线2(点2→点3,右边),曲线3(点3→点4,顶边),曲线4(点4→点1,左边)。离散面的边界 {1, 2, -3, -4} 中,正号表示沿曲线正方向,负号表示逆方向,形成逆时针环绕。
23.3.3 注入网格数据
// 集中注入所有节点到离散面(后续由 reclassifyNodes 纠正归属)
gmsh::model::mesh::addNodes(2, 1, nodes, coords);
// 注入点单元(类型15, 1节点)、线段单元(类型1, 2节点)、三角形单元(类型2, 3节点)
for(int i = 0; i < 4; i++) {
gmsh::model::mesh::addElementsByType(i + 1, 15, {}, {pnt[i]});
gmsh::model::mesh::addElementsByType(i + 1, 1, {}, lin[i]);
}
gmsh::model::mesh::addElementsByType(1, 2, {}, tris);
解释:mesh::addNodes(dim, tag, nodeTags, coords) 将节点数据绑定到实体——这里为方便起见将所有节点(内部+边界+角点)全部注入离散面 (2,1)。mesh::addElementsByType(entityTag, elemType, elemTags, nodeTags) 注入单元:elemTags={} 表示自动编号单元标签,nodeTags 为连续排列的节点连接列表。
23.3.4 节点重分类与几何参数化
// 重分类节点:根据单元归属,将节点从面重新分配到正确的子实体
gmsh::model::mesh::reclassifyNodes();
// 从网格生成参数化几何:使离散面可重划网格
gmsh::model::mesh::createGeometry();
解释:reclassifyNodes() 是离散实体工作流的关键。它将节点扫描所有单元的所属实体——如果某节点的全部单元都在同一条边上,该节点被重新归类到那条边;若全部单元都在同一角点上,则归类到那个点。如果跳过此步骤,后续对离散曲线的 getNodes() 查询将返回空。
createGeometry() 从离散实体的网格构建参数化几何:对曲线拟合 B-spline,对面构建 2D 参数域到 3D 空间的映射。执行后离散面可像普通 CAD 面一样调用 mesh::setSize() + mesh::generate() 重划网格。典型应用:STL 导入 → 离散实体 → 参数化 → 重新用四边形或优化三角形剖分。注意:对质量差的网格(极度畸变、自交),此函数可能失败。
23.3.5 构建 CAD 下卧层(混合模型)
// 仅 geo 模块支持混合模型,OpenCASCADE 不支持
// 在地形面下方创建 4 个 CAD 点(底面角点,z = -0.5)
int p1 = gmsh::model::geo::addPoint(0, 0, -0.5);
int p2 = gmsh::model::geo::addPoint(1, 0, -0.5);
int p3 = gmsh::model::geo::addPoint(1, 1, -0.5);
int p4 = gmsh::model::geo::addPoint(0, 1, -0.5);
// 底面 4 条边 + 4 条垂直边(连接底面 CAD 点到地表离散角点)
int c1 = gmsh::model::geo::addLine(p1, p2);
int c2 = gmsh::model::geo::addLine(p2, p3);
int c3 = gmsh::model::geo::addLine(p3, p4);
int c4 = gmsh::model::geo::addLine(p4, p1);
int c10 = gmsh::model::geo::addLine(p1, 1); // CAD点p1 → 离散点1
int c11 = gmsh::model::geo::addLine(p2, 2); // CAD点p2 → 离散点2
int c12 = gmsh::model::geo::addLine(p3, 3); // CAD点p3 → 离散点3
int c13 = gmsh::model::geo::addLine(p4, 4); // CAD点p4 → 离散点4
解释:geo::addLine(startPoint, endPoint) 创建 CAD 直线。垂直边 c10-c13 的第二个参数是离散点标签(1-4)——这展示了混合模型的核心机制:CAD 曲线以离散点为端点。
// 底面
int ll1 = gmsh::model::geo::addCurveLoop({c1, c2, c3, c4});
int s1 = gmsh::model::geo::addPlaneSurface({ll1});
// 4 个侧面:每条环 = 1底边 + 2垂直边 + 1离散曲线
int ll3 = gmsh::model::geo::addCurveLoop({c1, c11, -1, -c10});
int s3 = gmsh::model::geo::addPlaneSurface({ll3});
int ll4 = gmsh::model::geo::addCurveLoop({c2, c12, -2, -c11});
int s4 = gmsh::model::geo::addPlaneSurface({ll4});
int ll5 = gmsh::model::geo::addCurveLoop({c3, c13, 3, -c12});
int s5 = gmsh::model::geo::addPlaneSurface({ll5});
int ll6 = gmsh::model::geo::addCurveLoop({c4, c10, 4, -c13});
int s6 = gmsh::model::geo::addPlaneSurface({ll6});
解释:以前侧面 s3 的曲线环 {c1, c11, -1, -c10} 为例:+c1=底边 p1→p2,+c11=垂直边 p2→2,-1=离散曲线1反向(点2→点1,沿地表),-c10=垂直边 p1→1 反向(点1→p1)。离散曲线1正方向是点1→点2,但侧面需要从点2回到点1,因此取负。
// 6 张面围成封闭空间 → 体
int sl1 = gmsh::model::geo::addSurfaceLoop({s1, s3, s4, s5, s6, 1});
int v1 = gmsh::model::geo::addVolume({sl1});
gmsh::model::geo::synchronize();
解释:addSurfaceLoop({faces}) 的面列表中,1 是离散面标签。它与 5 个 CAD 面共同构成封闭面环。geo::synchronize() 将所有 geo 模块实体同步到全局模型,并将 CAD 实体与离散实体的拓扑关联起来。
23.3.6 网格剖分策略
bool transfinite = false; // 全六面体结构网格
bool transfiniteAuto = false; // 自动 Transfinite
if(transfinite) {
int NN = 30;
std::vector<std::pair<int, int>> tmp;
gmsh::model::getEntities(tmp, 1);
for(auto c : tmp) gmsh::model::mesh::setTransfiniteCurve(c.second, NN);
gmsh::model::getEntities(tmp, 2);
for(auto s : tmp) {
gmsh::model::mesh::setTransfiniteSurface(s.second);
gmsh::model::mesh::setRecombine(s.first, s.second);
gmsh::model::mesh::setSmoothing(s.first, s.second, 100);
}
gmsh::model::mesh::setTransfiniteVolume(v1);
}
else if(transfiniteAuto) {
gmsh::option::setNumber("Mesh.MeshSizeMin", 0.5);
gmsh::option::setNumber("Mesh.MeshSizeMax", 0.5);
gmsh::model::mesh::setTransfiniteAutomatic();
}
else {
gmsh::option::setNumber("Mesh.MeshSizeMin", 0.05);
gmsh::option::setNumber("Mesh.MeshSizeMax", 0.05);
}
gmsh::model::mesh::generate(3);
gmsh::write("ch23_terrain.msh");
解释:三种网格策略:
- 自由四面体(默认):通用,任意几何可剖
- 手动 Transfinite:所有曲线统一节点数 → 全六面体,适用于地下层结构网格
- 自动 Transfinite:setTransfiniteAutomatic() 由尺寸约束反推每条边的节点数,自动应用 Transfinite + Recombine
mesh::generate(3) 生成三维网格。因离散面已参数化,它将被作为计算域顶面重新剖分。
std::set<std::string> args(argv, argv + argc);
if(!args.count("-nopopup")) gmsh::fltk::run();
gmsh::finalize();
return 0;
}
23.4 完整可运行代码
// =============================================================================
// Chapter 23: Discrete Entities & Hybrid Models — Terrain Meshing
//
// 功能: 演示离散实体完整工作流 + 混合模型地形网格
// 编译: g++ -o ch23_terrain ch23_terrain.cpp -lgmsh -std=c++11
// 运行: ./ch23_terrain (GUI) 或 ./ch23_terrain -nopopup (批处理)
// =============================================================================
#include <set>
#include <iostream>
#include <gmsh.h>
int main(int argc, char **argv)
{
gmsh::initialize(argc, argv);
gmsh::model::add("ch23");
// ===== 1. 数据准备:N x N 地形控制点 =====
int N = 100;
auto tag = [N](std::size_t i, std::size_t j) {
return (N + 1) * i + j + 1;
};
std::vector<double> coords;
std::vector<std::size_t> nodes;
std::vector<std::size_t> tris;
std::vector<std::size_t> lin[4];
std::size_t pnt[4] = {tag(0,0), tag(N,0), tag(N,N), tag(0,N)};
for(std::size_t i = 0; i < N + 1; i++) {
for(std::size_t j = 0; j < N + 1; j++) {
nodes.push_back(tag(i, j));
coords.insert(coords.end(), {
(double)i / N,
(double)j / N,
0.05 * sin(10 * (double)(i + j) / N)
});
if(i > 0 && j > 0) {
tris.insert(tris.end(),
{tag(i-1,j-1), tag(i,j-1), tag(i-1,j)});
tris.insert(tris.end(),
{tag(i,j-1), tag(i,j), tag(i-1,j)});
}
if((i == 0 || i == N) && j > 0) {
std::size_t s = (i == 0) ? 3 : 1;
lin[s].insert(lin[s].end(), {tag(i, j - 1), tag(i, j)});
}
if((j == 0 || j == N) && i > 0) {
std::size_t s = (j == 0) ? 0 : 2;
lin[s].insert(lin[s].end(), {tag(i - 1, j), tag(i, j)});
}
}
}
// ===== 2. 创建离散实体拓扑 =====
for(int i = 0; i < 4; i++)
gmsh::model::addDiscreteEntity(0, i + 1);
gmsh::model::setCoordinates(1, 0, 0, coords[3 * tag(0, 0) - 1]);
gmsh::model::setCoordinates(2, 1, 0, coords[3 * tag(N, 0) - 1]);
gmsh::model::setCoordinates(3, 1, 1, coords[3 * tag(N, N) - 1]);
gmsh::model::setCoordinates(4, 0, 1, coords[3 * tag(0, N) - 1]);
for(int i = 0; i < 4; i++)
gmsh::model::addDiscreteEntity(1, i + 1,
{i + 1, (i < 3) ? (i + 2) : 1});
gmsh::model::addDiscreteEntity(2, 1, {1, 2, -3, -4});
// ===== 3. 注入网格数据 =====
gmsh::model::mesh::addNodes(2, 1, nodes, coords);
for(int i = 0; i < 4; i++) {
gmsh::model::mesh::addElementsByType(i + 1, 15, {}, {pnt[i]});
gmsh::model::mesh::addElementsByType(i + 1, 1, {}, lin[i]);
}
gmsh::model::mesh::addElementsByType(1, 2, {}, tris);
// ===== 4. 重分类节点 & 生成几何参数化 =====
gmsh::model::mesh::reclassifyNodes();
gmsh::model::mesh::createGeometry();
// ===== 5. 构建 CAD 下卧层(混合模型) =====
int p1 = gmsh::model::geo::addPoint(0, 0, -0.5);
int p2 = gmsh::model::geo::addPoint(1, 0, -0.5);
int p3 = gmsh::model::geo::addPoint(1, 1, -0.5);
int p4 = gmsh::model::geo::addPoint(0, 1, -0.5);
int c1 = gmsh::model::geo::addLine(p1, p2);
int c2 = gmsh::model::geo::addLine(p2, p3);
int c3 = gmsh::model::geo::addLine(p3, p4);
int c4 = gmsh::model::geo::addLine(p4, p1);
int c10 = gmsh::model::geo::addLine(p1, 1);
int c11 = gmsh::model::geo::addLine(p2, 2);
int c12 = gmsh::model::geo::addLine(p3, 3);
int c13 = gmsh::model::geo::addLine(p4, 4);
int ll1 = gmsh::model::geo::addCurveLoop({c1, c2, c3, c4});
int s1 = gmsh::model::geo::addPlaneSurface({ll1});
int ll3 = gmsh::model::geo::addCurveLoop({c1, c11, -1, -c10});
int s3 = gmsh::model::geo::addPlaneSurface({ll3});
int ll4 = gmsh::model::geo::addCurveLoop({c2, c12, -2, -c11});
int s4 = gmsh::model::geo::addPlaneSurface({ll4});
int ll5 = gmsh::model::geo::addCurveLoop({c3, c13, 3, -c12});
int s5 = gmsh::model::geo::addPlaneSurface({ll5});
int ll6 = gmsh::model::geo::addCurveLoop({c4, c10, 4, -c13});
int s6 = gmsh::model::geo::addPlaneSurface({ll6});
int sl1 = gmsh::model::geo::addSurfaceLoop({s1, s3, s4, s5, s6, 1});
int v1 = gmsh::model::geo::addVolume({sl1});
gmsh::model::geo::synchronize();
// ===== 6. 网格剖分 =====
bool transfinite = false;
bool transfiniteAuto = false;
if(transfinite) {
int NN = 30;
std::vector<std::pair<int, int>> tmp;
gmsh::model::getEntities(tmp, 1);
for(auto c : tmp)
gmsh::model::mesh::setTransfiniteCurve(c.second, NN);
gmsh::model::getEntities(tmp, 2);
for(auto s : tmp) {
gmsh::model::mesh::setTransfiniteSurface(s.second);
gmsh::model::mesh::setRecombine(s.first, s.second);
gmsh::model::mesh::setSmoothing(s.first, s.second, 100);
}
gmsh::model::mesh::setTransfiniteVolume(v1);
}
else if(transfiniteAuto) {
gmsh::option::setNumber("Mesh.MeshSizeMin", 0.5);
gmsh::option::setNumber("Mesh.MeshSizeMax", 0.5);
gmsh::model::mesh::setTransfiniteAutomatic();
}
else {
gmsh::option::setNumber("Mesh.MeshSizeMin", 0.05);
gmsh::option::setNumber("Mesh.MeshSizeMax", 0.05);
}
gmsh::model::mesh::generate(3);
gmsh::write("ch23_terrain.msh");
// ===== 7. 启动 GUI =====
std::set<std::string> args(argv, argv + argc);
if(!args.count("-nopopup")) gmsh::fltk::run();
gmsh::finalize();
return 0;
}
23.5 关键 API 速查表
23.5.1 离散实体创建与管理
| API | 说明 |
|---|---|
gmsh::model::addDiscreteEntity(dim, tag) |
创建无边界离散实体(用于 dim=0 的点) |
gmsh::model::addDiscreteEntity(dim, tag, boundaryTags) |
创建带边界的离散实体:线(dim=1,端点列表)、面(dim=2,带符号曲线列表)、体(dim=3,面列表) |
gmsh::model::setCoordinates(tag, x, y, z) |
设置离散点的模型坐标(用于 GUI 显示和 CAD 连接) |
gmsh::model::getType(dim, tag, type) |
获取实体类型字符串(如 "Discrete Surface") |
23.5.2 网格数据注入
| API | 说明 |
|---|---|
gmsh::model::mesh::addNodes(dim, tag, nodeTags, coords) |
向实体注入节点;coords 为 {x0,y0,z0, x1,y1,z1,...} 交错格式 |
gmsh::model::mesh::addElementsByType(entityTag, elemType, elemTags, nodeTags) |
向实体注入同类型单元;elemTags={} 表示自动编号 |
gmsh::model::mesh::addElements(entityTag, elemTypes, elemTags, nodeTags) |
向实体注入混合类型单元 |
gmsh::model::mesh::reclassifyNodes() |
将节点从聚合实体重分类到正确的边界实体(基于单元归属) |
gmsh::model::mesh::createGeometry() |
从离散实体网格生成参数化几何(使离散面可重划网格) |
23.5.3 常用单元类型编号
| 编号 | 名称 | 维度 | 节点数 | 编号 | 名称 | 维度 | 节点数 |
|---|---|---|---|---|---|---|---|
| 15 | Point | 0 | 1 | 1 | Line (2-node) | 1 | 2 |
| 8 | Line3 (3-node) | 1 | 3 | 2 | Triangle (3-node) | 2 | 3 |
| 9 | Triangle6 (6-node) | 2 | 6 | 3 | Quadrangle (4-node) | 2 | 4 |
| 4 | Tetrahedron (4-node) | 3 | 4 | 5 | Hexahedron (8-node) | 3 | 8 |
| 6 | Prism (6-node) | 3 | 6 | 7 | Pyramid (5-node) | 3 | 5 |
23.5.4 混合模型相关 geo API
| API | 说明 |
|---|---|
gmsh::model::geo::addPoint(x, y, z) |
创建 CAD 点,返回标签(端点可连接离散点) |
gmsh::model::geo::addLine(startTag, endTag) |
创建 CAD 直线,端点可为离散点标签 |
gmsh::model::geo::addCurveLoop(curveTags) |
创建闭合曲线环(有向边界) |
gmsh::model::geo::addPlaneSurface(wireTags) |
以闭合环为边界创建平面面 |
gmsh::model::geo::addSurfaceLoop(faceTags) |
以封闭面集合创建面环 |
gmsh::model::geo::addVolume(surfaceLoopTags) |
以封闭面环创建体 |
gmsh::model::geo::synchronize() |
同步 geo 模块实体到 Gmsh 全局模型 |
23.5.5 Transfinite 网格控制
| API | 说明 |
|---|---|
gmsh::model::mesh::setTransfiniteCurve(tag, nPoints) |
设置曲线结构网格点数 |
gmsh::model::mesh::setTransfiniteSurface(tag) |
对面应用 Transfinite 剖分 |
gmsh::model::mesh::setTransfiniteVolume(tag) |
对体应用 Transfinite 剖分 |
gmsh::model::mesh::setRecombine(dim, tag) |
对面/体启用四边形/六面体重组 |
gmsh::model::mesh::setTransfiniteAutomatic() |
自动 Transfinite(由尺寸约束推断分割数,全重组) |
23.6 注意事项与最佳实践
-
仅
geo模块支持混合模型:gmsh::model::occ(OpenCASCADE)不支持其边界实体为离散实体。如果已通过 OCC 构建几何,需转用geo模块重新构建或仅用geo完成混合部分。 -
reclassifyNodes()不可省略:跳过此步骤,所有节点将保留在首次addNodes()指定的实体上,后续对离散曲线/点的节点查询返回空,createGeometry()可能产生不正确结果。 -
createGeometry()的质量依赖:参数化几何的质量取决于输入网格质量。极度狭长或反转的三角形可能导致参数化失败。建议调用前对网格进行质量检查。 -
边界标签的符号约定:二维面边界中正号=沿曲线正方向,负号=逆方向。错误符号导致面法向反转,影响体定义和网格生成。CCW 环绕是基本原则。
-
离散实体与分区实体的区别:两者都存储为 "Discrete *" 类型,但分区实体(
mesh::partition()生成)额外携带分区索引和父实体信息(通过getPartitions()/getParent()查询)。 -
节点和单元标签全局唯一:
addNodes()和addElementsByType()中的标签必须在整个模型中唯一。 -
从外部文件导入网格的替代方案:除程序化注入外,可通过
gmsh::merge("file.stl")导入 STL,然后调用mesh::classifySurfaces(angle, boundary, curveAngle)自动创建离散实体和拓扑。 -
地形数据真实来源:实际工程中地形数据通常来自 GIS/DEM 文件,只需将
coords的 Z 分量替换为真实海拔,其余流程完全一致。