Skip to content

第9章 四边形网格重组 (Unstructured Quad Meshing via Recombination)


9.1 学习目标

  • 理解 Gmsh 中 Recombination(重组)的基本原理:将三角形网格的相邻三角对合并为四边形,以及其背后的 Blossom 完美匹配算法
  • 掌握全局重组 (Mesh.RecombineAll) 与局部重组 (mesh::setRecombine) 两种控制方式,能针对多面几何选择性生成四边形网格
  • 了解三种重组算法(Simple / Blossom / Full-Quad)的区别与适用场景,能根据几何复杂度选择合适的算法
  • 理解 Mesh.Algorithm = 8(Frontal-Delaunay for Quads)作为替代四边形网格生成路径的工作机制
  • 掌握四边形网格质量评估方法,以及处理混合网格(四边形+三角形过渡区)的策略

9.2 核心概念说明

9.2.1 为什么需要四边形网格

在有限元分析中,四边形(Quadrilateral,简称 Quad)和六面体(Hexahedron)网格相比三角形/四面体网格具有显著优势:

维度 三角形/四面体 四边形/六面体
精度 常应变单元,弯曲变形下"过硬"(shear locking) 双线性插值,弯曲表现更优
单元数量 同等精度需要更多单元 1 个四边形等效于约 2 个三角形
收敛速度 较慢 较快(h/p-refinement 表现更好)
边界层 难以构建规则排列 天然适合边界层网格(结构化排列)
计算效率 单元数多,矩阵规模大 单元数少,矩阵规模小,求解快

在 CFD 边界层网格、结构分析的应力集中区、以及复合材料层合板建模等场景中,四边形网格是必不可少的。

9.2.2 Recombination(重组)原理

Gmsh 采用间接法生成非结构化四边形网格:首先生成全三角形网格,然后通过完美匹配算法(Perfect Matching)将相邻三角形两两合并为四边形。这一过程称为 Recombination(重组)。

生成三角形网格 (Delaunay / Frontal)
        |
        v
构建无向图: 节点=三角形, 边=相邻关系, 权值=合并后的四边形质量
        |
        v
求解最小代价完美匹配 (Blossom 算法)
        |
        v
将匹配成功的三角形对合并为四边形
未匹配的三角形保留(若允许混合网格)或通过细分消除

核心概念: - 图模型:每个三角形是一个节点,两个相邻三角形之间存在一条边。边的权值(cost)反映合并后四边形的质量——质量越好,权值越低。 - 完美匹配:选取一组边,使得每个节点恰好被连接一次,且被选边的总权值最小。 - Blossom 算法:解决一般图(非二部图)上最小代价完美匹配问题的经典组合优化算法,由 Jack Edmonds 于 1965 年提出。Gmsh 使用的 Blossom-Quad 变体针对四边形网格生成做了专门优化(参见 Remacle 等人 2012 年的论文)。

9.2.3 三种重组算法

Gmsh 提供三种重组算法,通过 Mesh.RecombinationAlgorithm 选项控制:

算法 ID 名称 行为 混合网格 适用场景
0 Simple (简单) 暴力贪心合并:遍历所有相邻三角形对,检查是否满足角度准则,满足即合并 自然产生 简单几何,速度优先,质量要求不高
1 (默认) Blossom (开花) 先 Delaunay 三角剖分,再通过最小代价完美匹配合并为四边形 允许保留三角形 通用场景,平衡质量与自动化程度
2 Full-Quad (全四边形) 在 Blossom 基础上增加自动粗化-重组-光滑-细分级联 不允许(全部转为四边形) 严格要求全四边形网格的场合
3 Full-Quad (全四边形) 与算法 2 类似,但采用不同的细分策略 不允许 算法 2 的替代选项

Simple 算法的角度准则mesh::setRecombine(dim, tag, angle)angle 参数的默认值为 45 度。当两个三角形的公共边两侧内角之和超过此阈值时,Simple 算法拒绝合并这对三角形。这是因为合并后四边形的内角偏离 90 度太远会导致严重的单元畸变。

9.2.4 全局 vs 局部重组

Gmsh 支持两种粒度的重组控制:

方式 API 作用范围
全局 option::setNumber("Mesh.RecombineAll", 1) 模型中所有 2D 面(PlaneSurface / Surface)
局部 mesh::setRecombine(2, surfaceTag, angle) 指定标签的单个面,可单独控制 angle 阈值

两者的典型使用策略:

全局 RecombineAll = 1
    + 在不需要四边形的面上显式关闭(或不设置 mesh::setRecombine)
    → 适合大多数面需要四边形,少数面保留三角形的场景

局部 mesh::setRecombine(2, tag)
    + 仅对关心的面设置
    → 适合大多数面需要三角形,少数关键面需要四边形的场景

9.2.5 Frontal-Delaunay for Quads (算法 8)

除了三角形后重组的方式,Gmsh 还提供实验性的 Mesh.Algorithm = 8:直接生成适应于四边形重组的直角三角形网格。该算法基于 L-inf 范数的 Frontal-Delaunay 方法,刻意生成直角占主导的三角形网格,使得后续重组几乎不产生低质量四边形。论文参考:Remacle 等人,"A Frontal Delaunay Quad Mesh Generator Using the L-inf Norm",IJNME, 2013。

gmsh::option::setNumber("Mesh.Algorithm", 8);  // 实验性四边形专用算法

注意:该算法消耗内存较多,且可能在某些复杂几何上不够稳定,适合对四边形质量要求极高的场景。

9.2.6 混合网格与细分

重组过程不可避免地会留下一些"无法重组"的三角形——这些三角形的相邻三角形已被剔除或合并后会导致极差形状的四边形。Gmsh 提供两种处理策略:

  1. 保留混合网格(默认):允许最终网格包含三角形。对于大多数工程分析,三角形-四边形混合网格(Hybrid Mesh)完全可接受,且避免了为强行消除三角形而引入劣质四边形。
  2. 全四边形细分Mesh.SubdivisionAlgorithm = 1):对混合网格中所有单元(包括四边形和三角形)进行 Catmull-Clark 式细分,将每个三角形细分为 3 个四边形,每个四边形细分为 4 个四边形。结果是一个全四边形网格,但单元数会显著增加。
// 先剖分,再重组(显式两步法)
gmsh::model::mesh::generate(2);
gmsh::model::mesh::recombine();                // 显式执行重组

// 然后细分以获得全四边形网格
gmsh::option::setNumber("Mesh.SubdivisionAlgorithm", 1);
gmsh::model::mesh::refine();                   // 执行细分

9.3 C++ 代码逐段讲解

本章示例构建一个带圆孔的矩形板(Plate with Hole),这是 CAE 中经典的应力集中问题。我们先将整块板剖分为三角形,再通过重组生成主导四边形网格。此外,代码还比较了不同重组算法在孔周围三角形过渡区的表现。

9.3.1 头文件与初始化

#include <set>
#include <cmath>
#include <gmsh.h>

int main(int argc, char **argv) {
  gmsh::initialize(argc, argv);
  gmsh::model::add("ch09");

与之前章节一致:gmsh::initialize 初始化 Gmsh 引擎,gmsh::model::add 创建命名模型。

9.3.2 带孔矩形板几何建模

用几何内核构建一个 10 x 6 的矩形板,中心开一个半径 1 的圆孔。这需要在矩形四边上各插入一个点以分段构建闭合环:

  // ---- 几何参数 ----
  double L = 10.0, H = 6.0;          // 板的长和宽
  double R = 1.0;                    // 中心圆孔的半径
  double lc = 0.5;                   // 基础网格尺寸

  // ---- 外边界点(矩形四角)----
  int p1 = gmsh::model::geo::addPoint(0,    0,    0, lc, 1);
  int p2 = gmsh::model::geo::addPoint(L,    0,    0, lc, 2);
  int p3 = gmsh::model::geo::addPoint(L,    H,    0, lc, 3);
  int p4 = gmsh::model::geo::addPoint(0,    H,    0, lc, 4);

  // ---- 圆孔上的四个控制点(顺时针方向,用于构建内边界环)----
  double cx = L / 2.0, cy = H / 2.0;  // 圆心坐标
  int pc1 = gmsh::model::geo::addPoint(cx - R, cy,     0, lc, 5);
  int pc2 = gmsh::model::geo::addPoint(cx,     cy + R, 0, lc, 6);
  int pc3 = gmsh::model::geo::addPoint(cx + R, cy,     0, lc, 7);
  int pc4 = gmsh::model::geo::addPoint(cx,     cy - R, 0, lc, 8);

  // ---- 圆心点(可选,用作细化控制)----
  int pc  = gmsh::model::geo::addPoint(cx, cy, 0, lc * 0.5, 9);

CAE 要点:孔周围布置更多几何控制点是常见做法。这里用 4 个点控制圆孔弧线,并在圆心额外放置一个点(pc),其 lc 为基础值的一半,使得孔周围网格自然细化——应力集中区需要更高网格分辨率。

  // ---- 外边界直线 ----
  int l1 = gmsh::model::geo::addLine(p1, p2, 1);
  int l2 = gmsh::model::geo::addLine(p2, p3, 2);
  int l3 = gmsh::model::geo::addLine(p3, p4, 3);
  int l4 = gmsh::model::geo::addLine(p4, p1, 4);

  // ---- 内孔圆弧(圆心角各 90 度)----
  int a1 = gmsh::model::geo::addCircleArc(pc1, pc, pc2, 5);
  int a2 = gmsh::model::geo::addCircleArc(pc2, pc, pc3, 6);
  int a3 = gmsh::model::geo::addCircleArc(pc3, pc, pc4, 7);
  int a4 = gmsh::model::geo::addCircleArc(pc4, pc, pc1, 8);

addCircleArc(start, center, end, tag) 创建从 startend 的逆时针圆弧,圆心为 center。四条圆弧拼成一个完整的圆孔边界。

  // ---- 外边界闭环和内孔闭环 ----
  int outerLoop = gmsh::model::geo::addCurveLoop({l1, l2, l3, l4}, 11);
  int innerLoop = gmsh::model::geo::addCurveLoop({a1, a2, a3, a4}, 12);

  // ---- 带孔平面面(plane surface with hole)----
  int plate = gmsh::model::geo::addPlaneSurface({outerLoop, innerLoop}, 100);

addPlaneSurface({outerLoop, innerLoop}, ...) 中传入两个闭环——第一个定义外边界(所有点围成的区域),第二个定义内边界(从该区域中挖去)。Gmsh 自动处理孔洞拓扑。

  gmsh::model::geo::synchronize();

9.3.3 全局重组(RecombineAll)方案

最简方式——一行代码使所有 2D 面尝试三角形转四边形重组:

  // ---- 方案 A:全局重组(适用于所有面)----
  bool useGlobalRecombine = true;  // 切换此变量对比效果

  if (useGlobalRecombine) {
    gmsh::option::setNumber("Mesh.RecombineAll", 1);
    // RecombineAll 对所有面开启重组,使用默认 Blossom 算法 (algorithm=1)
  }

Mesh.RecombineAll = 1 是全局布尔开关。不需要逐个面调用 mesh::setRecombine。这是最快捷的方式,适合所有面都需要四边形的场景。

9.3.4 局部重组(setRecombine)方案

如果只希望在特定面上生成四边形(例如仅关心主板应力分布,而夹具面可以用三角形),则应使用局部方式:

  // ---- 方案 B:局部重组(仅对指定面生效)----
  if (!useGlobalRecombine) {
    gmsh::model::mesh::setRecombine(2, plate, 45.0);
    // 参数: dim=2 (面), tag=100 (plate面), angle=45° (Simple算法阈值)
  }

mesh::setRecombine(dim, tag, angle): - dim:实体维度。目前仅支持 dim=2(将三角形重组为四边形) - tag:目标面实体的标签。在多面模型中,可以对不同面使用不同标签 - angle:阈值角度(仅对 Simple 算法有效)。对于 Blossom 算法(默认),此参数被忽略

三种典型的局部重组组合策略:

// 策略 1:板面重组为四边形,孔周围保留三角形过渡
gmsh::model::mesh::setRecombine(2, plate, 45.0);
// plate 之外的区域不设置重组,保留三角形

// 策略 2:多个面分别控制
gmsh::model::mesh::setRecombine(2, surfaceA, 30.0); // 严格角度阈值
gmsh::model::mesh::setRecombine(2, surfaceB, 60.0); // 宽松角度阈值

// 策略 3:全局开启后对个别面关闭
gmsh::option::setNumber("Mesh.RecombineAll", 1);
// 不支持直接"关闭";要豁免某个面,不应使用全局开关,改用逐面 setRecombine

9.3.5 重组算法选择

  // ---- 选择重组算法 ----
  // 0 = Simple (贪心)   : 速度最快,质量一般,适合预览
  // 1 = Blossom (默认)  : 最小代价完美匹配,质量最好,推荐日常使用
  // 2 = Full-Quad       : Blossom + 粗化-重组-光滑-细分级联
  // 3 = Full-Quad 变体  : 算法 2 的替代细分策略
  gmsh::option::setNumber("Mesh.RecombinationAlgorithm", 1);

9.3.6 可选:Frontal-Delaunay for Quads (算法 8)

如果想绕开三角形-重组的两步路径,可以直接使用四边形专用的 Frontal-Delaunay 算法:

  // ---- 若需尝试实验性算法 8,取消以下注释 ----
  // gmsh::option::setNumber("Mesh.Algorithm", 8);
  // 注意:算法 8 会在内部自动调用重组逻辑,不需 setRecombine

当同时设置 Mesh.Algorithm = 8mesh::setRecombine 时,算法 8 拥有优先权——Gmsh 直接使用该算法生成适合重组的三角形网格,然后内部重组。

9.3.7 网格生成与显式重组

  // ---- 生成 2D 网格 ----
  gmsh::model::mesh::generate(2);

generate(2) 执行完整的 2D 网格生成流水线:1D 边界网格 -> 2D 面网格 -> 重组(如果已设置重组标志)。这是最常见的一体化调用方式。

也可以拆分执行,以便在中间步骤检查或修改:

  // ---- 显式两步法(等价于上面的一体化过程)----
  // gmsh::model::mesh::generate(2);
  // gmsh::model::mesh::recombine();  // 显式执行重组步骤

  // ---- 进一步:细分获得全四边形网格 ----
  // gmsh::option::setNumber("Mesh.SubdivisionAlgorithm", 1);
  // gmsh::model::mesh::refine();

mesh::recombine() 对已生成网格的所有设置了重组标志的面执行重组。mesh::refine() 在设置了 Mesh.SubdivisionAlgorithm 后对混合网格进行细分以消除所有三角形。

9.3.8 网格质量统计输出

四边形网格生成后,评估其质量是 CAE 前处理中不可或缺的步骤。Gmsh 不直接提供质量统计 API,但可以通过遍历单元坐标手动计算。这里展示一种简化的四边形质量评估方法——基于雅可比行列式(Scaled Jacobian):

  // ---- 统计四边形/三角形数量 ----
  std::vector<std::size_t> elemTypes;
  std::vector<std::vector<std::size_t>> elemTags;
  std::vector<std::vector<std::size_t>> nodeTags;
  gmsh::model::mesh::getElements(elemTypes, elemTags, nodeTags, 2);
  // getElements 返回二维单元的类型列表、标签列表和节点列表

  int nTri = 0, nQuad = 0;
  for (std::size_t i = 0; i < elemTypes.size(); ++i) {
    if (elemTypes[i] == 2)       // 2 节点三角形
      nTri += elemTags[i].size();
    else if (elemTypes[i] == 3)  // 3 节点四边形
      nQuad += elemTags[i].size();
  }
  gmsh::logger::write("=== 网格统计 ===");
  gmsh::logger::write("三角形数量: " + std::to_string(nTri));
  gmsh::logger::write("四边形数量: " + std::to_string(nQuad));
  if (nTri + nQuad > 0)
    gmsh::logger::write("四边形占比: " +
      std::to_string(100.0 * nQuad / (nTri + nQuad)) + "%");

Gmsh 单元类型编号(2D): | 类型编号 | 含义 | |----------|------| | 2 | 3 节点三角形(TRI3) | | 3 | 4 节点四边形(QUAD4) | | 9 | 6 节点三角形(TRI6,二阶) | | 16 | 8 节点四边形(QUAD8,二阶) |

9.3.9 孔周围细化控制

CAE 分析中孔周围应力梯度大,需要局部细化。此处使用第 8 章学过的尺寸场技术——通过 Distance + Threshold 场在孔周围施加更小的网格尺寸:

  // ---- 孔周围尺寸场(可选,若需要更精细的孔周网格)----
  bool useSizeField = true;
  if (useSizeField) {
    gmsh::model::mesh::field::add("Distance", 1);
    gmsh::model::mesh::field::setNumbers(1, "CurvesList", {5, 6, 7, 8});
    // 计算到四条孔圆弧的距离 (曲线标签 5-8)

    gmsh::model::mesh::field::add("Threshold", 2);
    gmsh::model::mesh::field::setNumber(2, "InField", 1);
    gmsh::model::mesh::field::setNumber(2, "SizeMin", lc * 0.2);
    gmsh::model::mesh::field::setNumber(2, "SizeMax", lc);
    gmsh::model::mesh::field::setNumber(2, "DistMin", 0.5);
    gmsh::model::mesh::field::setNumber(2, "DistMax", 2.0);
    // 距孔 0.5 以内: 尺寸 = 0.1 ; 距孔 2.0 以外: 尺寸 = 0.5

    gmsh::model::mesh::field::setAsBackgroundMesh(2);

    // 关闭点级 lc 和曲率自适应,使尺寸完全由场控制
    gmsh::option::setNumber("Mesh.MeshSizeFromPoints", 0);
    gmsh::option::setNumber("Mesh.MeshSizeFromCurvature", 0);
    gmsh::option::setNumber("Mesh.MeshSizeExtendFromBoundary", 0);
  }

尺寸场与重组独立运作——场控制单元大小,重组控制单元形状(三角转四边形)。两者结合可在孔周围同时获得高密度四边形主导的网格,这是高质量 CAE 前处理的典型目标。

9.3.10 输出与可视化

  // ---- 保存网格 ----
  gmsh::write("ch09_plate.msh");

  // ---- 启动 GUI ----
  std::set<std::string> args(argv, argv + argc);
  if (!args.count("-nopopup")) gmsh::fltk::run();

  gmsh::finalize();
  return 0;
}

在 GUI 中可以直观对比三角形网格和重组后的四边形网格:菜单 Tools -> Options -> Mesh -> Visibility 中切换 2D 元素显示,或在命令行模式下使用 gmsh ch09_plate.msh 打开查看。


9.4 完整可运行代码

// ============================================================================
// 第 9 章完整示例:四边形网格重组 (Quad Mesh via Recombination)
//
// 几何: 带中心圆孔的矩形板 (Plate with Hole) — 经典 CAE 应力集中案例
//
// 编译:
//   g++ -o ch09 ch09.cpp -I/path/to/gmsh/include -L/path/to/gmsh/lib -lgmsh
//
// 运行:
//   ./ch09              # 打开 GUI 查看结果
//   ./ch09 -nopopup     # 仅生成 ch09_plate.msh,不打开 GUI
// ============================================================================

#include <set>
#include <cmath>
#include <string>
#include <gmsh.h>

int main(int argc, char **argv) {
  gmsh::initialize(argc, argv);
  gmsh::model::add("ch09");

  // ======================== 1. 几何参数 ========================
  double L = 10.0, H = 6.0;
  double R = 1.0;
  double lc = 0.5;
  double cx = L / 2.0, cy = H / 2.0;          // 圆心

  // ======================== 2. 构建几何点 ========================
  // 外边界四角
  int p1 = gmsh::model::geo::addPoint(0,    0,    0, lc, 1);
  int p2 = gmsh::model::geo::addPoint(L,    0,    0, lc, 2);
  int p3 = gmsh::model::geo::addPoint(L,    H,    0, lc, 3);
  int p4 = gmsh::model::geo::addPoint(0,    H,    0, lc, 4);

  // 圆孔控制点 (顺时针)
  int pc1 = gmsh::model::geo::addPoint(cx - R, cy,     0, lc, 5);
  int pc2 = gmsh::model::geo::addPoint(cx,     cy + R, 0, lc, 6);
  int pc3 = gmsh::model::geo::addPoint(cx + R, cy,     0, lc, 7);
  int pc4 = gmsh::model::geo::addPoint(cx,     cy - R, 0, lc, 8);

  // 圆心 (细化用)
  int pc  = gmsh::model::geo::addPoint(cx, cy, 0, lc * 0.5, 9);

  // ======================== 3. 构建曲线 ========================
  // 外边界直线
  int l1 = gmsh::model::geo::addLine(p1, p2, 1);
  int l2 = gmsh::model::geo::addLine(p2, p3, 2);
  int l3 = gmsh::model::geo::addLine(p3, p4, 3);
  int l4 = gmsh::model::geo::addLine(p4, p1, 4);

  // 内孔圆弧 (四个 90 度圆弧)
  int a1 = gmsh::model::geo::addCircleArc(pc1, pc, pc2, 5);
  int a2 = gmsh::model::geo::addCircleArc(pc2, pc, pc3, 6);
  int a3 = gmsh::model::geo::addCircleArc(pc3, pc, pc4, 7);
  int a4 = gmsh::model::geo::addCircleArc(pc4, pc, pc1, 8);

  // ======================== 4. 构建带孔面 ========================
  int outerLoop = gmsh::model::geo::addCurveLoop({l1, l2, l3, l4}, 11);
  int innerLoop = gmsh::model::geo::addCurveLoop({a1, a2, a3, a4}, 12);
  int plate = gmsh::model::geo::addPlaneSurface({outerLoop, innerLoop}, 100);
  gmsh::model::geo::synchronize();

  // ======================== 5. 重组设置 ========================
  // ---- 选择重组方案 (二选一) ----
  bool useGlobal = true;  // true: 全局重组 ; false: 局部重组
  if (useGlobal) {
    gmsh::option::setNumber("Mesh.RecombineAll", 1);
  } else {
    gmsh::model::mesh::setRecombine(2, plate, 45.0);
  }

  // ---- 选择重组算法 ----
  // 0=Simple, 1=Blossom(默认), 2=Full-Quad, 3=Full-Quad变体
  gmsh::option::setNumber("Mesh.RecombinationAlgorithm", 1);

  // ======================== 6. 孔周围尺寸场(可选) ========================
  bool useSizeField = true;
  if (useSizeField) {
    // Distance 场: 到孔圆弧的距离
    gmsh::model::mesh::field::add("Distance", 1);
    gmsh::model::mesh::field::setNumbers(1, "CurvesList", {5, 6, 7, 8});

    // Threshold 场: 将距离映射为尺寸
    gmsh::model::mesh::field::add("Threshold", 2);
    gmsh::model::mesh::field::setNumber(2, "InField", 1);
    gmsh::model::mesh::field::setNumber(2, "SizeMin", lc * 0.2);
    gmsh::model::mesh::field::setNumber(2, "SizeMax", lc);
    gmsh::model::mesh::field::setNumber(2, "DistMin", 0.5);
    gmsh::model::mesh::field::setNumber(2, "DistMax", 2.0);

    gmsh::model::mesh::field::setAsBackgroundMesh(2);

    // 关闭干扰项,尺寸完全由场决定
    gmsh::option::setNumber("Mesh.MeshSizeFromPoints", 0);
    gmsh::option::setNumber("Mesh.MeshSizeFromCurvature", 0);
    gmsh::option::setNumber("Mesh.MeshSizeExtendFromBoundary", 0);
  }

  // ======================== 7. 生成网格 ========================
  gmsh::model::mesh::generate(2);

  // ======================== 8. 网格统计 ========================
  {
    std::vector<std::size_t> elemTypes;
    std::vector<std::vector<std::size_t>> elemTags;
    std::vector<std::vector<std::size_t>> nodeTags;
    gmsh::model::mesh::getElements(elemTypes, elemTags, nodeTags, 2);

    int nTri = 0, nQuad = 0;
    for (std::size_t i = 0; i < elemTypes.size(); ++i) {
      if (elemTypes[i] == 2)       // 3 节点三角形
        nTri += elemTags[i].size();
      else if (elemTypes[i] == 3)  // 4 节点四边形
        nQuad += elemTags[i].size();
    }
    gmsh::logger::write("=== 网格统计 ===");
    gmsh::logger::write("三角形数量: " + std::to_string(nTri));
    gmsh::logger::write("四边形数量: " + std::to_string(nQuad));
    if (nTri + nQuad > 0) {
      gmsh::logger::write("四边形占比: " +
        std::to_string(100.0 * nQuad / (nTri + nQuad)) + "%");
      gmsh::logger::write("总单元数: " + std::to_string(nTri + nQuad));
    }
  }

  // ======================== 9. 输出与 GUI ========================
  gmsh::write("ch09_plate.msh");

  std::set<std::string> cmdArgs(argv, argv + argc);
  if (!cmdArgs.count("-nopopup")) gmsh::fltk::run();

  gmsh::finalize();
  return 0;
}

运行方式

# 编译
g++ -o ch09 ch09.cpp -I/path/to/gmsh/include -L/path/to/gmsh/lib -lgmsh

# 运行(打开 GUI)
./ch09

# 仅生成 .msh 文件
./ch09 -nopopup

关键变量切换建议

变量 默认值 切换后效果
useGlobal true 改为 false 使用局部重组(仅对 plate 面生效)
Mesh.RecombinationAlgorithm 1 改为 0 体验 Simple 贪心算法速度;改为 2 获得全四边形网格
useSizeField true 改为 false 关闭孔周细化,观察均匀尺寸下的四边形质量

9.5 关键 API 速查表

重组控制

函数 / 选项 说明
gmsh::option::setNumber("Mesh.RecombineAll", 1) 全局开启重组(对所有 2D 面生效)
gmsh::model::mesh::setRecombine(2, tag, angle) 局部重组,仅对 dim=2 且指定 tag 的面生效。angle 为 Simple 算法的角度阈值(默认 45 度)
gmsh::option::setNumber("Mesh.RecombinationAlgorithm", algo) 选择重组算法:0=Simple, 1=Blossom, 2/3=Full-Quad
gmsh::option::setNumber("Mesh.SubdivisionAlgorithm", 1) 开启全四边形细分(与 mesh::refine() 配合使用)

网格操作

函数 说明
gmsh::model::mesh::generate(dim) 生成 dim 维网格(内部含重组步骤,若有重组标志)
gmsh::model::mesh::recombine() 显式执行三角形->四边形重组
gmsh::model::mesh::refine() Mesh.SubdivisionAlgorithm 执行网格细分

相关选项

选项名 类型 默认值 说明
Mesh.RecombineAll integer 0 全局重组开关(0=关闭,1=开启)
Mesh.RecombinationAlgorithm integer 1 重组算法:0=Simple, 1=Blossom, 2/3=Full-Quad
Mesh.SubdivisionAlgorithm integer 0 细分算法:0=无,1=全四边形细分
Mesh.Algorithm integer 6 2D 网格算法:8=Frontal-Delaunay for Quads(实验性)
Mesh.Smoothing integer 网格光滑迭代次数(重组后光滑可改善四边形质量)

核心几何建模(本章使用)

函数 说明
gmsh::model::geo::addCircleArc(start, center, end, tag) 创建圆弧(圆心角由三点位置隐式决定)
gmsh::model::geo::addPlaneSurface({outerLoop, innerLoop}, tag) 创建带孔平面面(多个闭环:第一个=外边界,后续=内孔)
gmsh::model::geo::synchronize() 将几何从 geo 模块同步到核心模型

网格查询

函数 说明
gmsh::model::mesh::getElements(types, tags, nodes, dim) 按维度获取单元信息(类型编号:2=TRI3, 3=QUAD4, 9=TRI6, 16=QUAD8)
gmsh::model::mesh::getElementTypes(types) 获取模型中所有单元类型

9.6 常见问题与注意事项

  1. Simple 算法的 angle 参数仅在 algorithm=0 时生效:当 Mesh.RecombinationAlgorithm 设为 1(Blossom,默认)或 2/3 时,setRecombine(dim, tag, angle) 中的 angle 参数被忽略。此时 Gmsh 由 Blossom 算法内部的四边形质量代价函数自行决定是否合并一对三角形。

  2. Blossom 算法可能保留三角形:这是设计行为,不是 bug。Blossom 的核心思想是"合并不如不合并好时就保留三角形"。如果需要 100% 四边形,请使用算法 2(Full-Quad)或开启 Mesh.SubdivisionAlgorithm = 1 后执行 mesh::refine()

  3. 几何复杂度对重组成功率的影响:当几何包含极锐角、极短边或复杂拓扑时,Blossom 算法的匹配成功率下降。对此类几何,建议先用 Mesh.Algorithm = 8 或减小 lc 增加三角形数量,为重组提供更优质的输入三角网格。

  4. 重组不可逆mesh::recombine() 修改的是网格数据(将三角形替换为四边形),调用后无法恢复原三角形网格。如需保留原始三角剖分,请在重组前通过 gmsh::write() 保存副本。

  5. 与 transfinite 的关系:第 3、6 章中的 transfinite + Recombine 生成的是结构化四边形网格;本章的 setRecombine + 非结构化剖分生成的是非结构化四边形网格。两种方式截然不同——transfinite 通过映射生成,recombination 通过对三角形网格的后处理生成。

  6. 性能提示:Blossom 算法的最小代价完美匹配在单元数极大(>100K 三角形)时可能消耗较多时间。Simple 算法的 O(N) 贪心策略在此规模下明显更快,但质量稍低。对于超大规模模型,建议先在部分区域用 Simple 算法做快速预览,再以 Blossom 做最终网格。

  7. 二阶单元的重组:如果先剖分一阶三角形网格再将其提升为二阶(通过 Mesh.SecondOrderLinear 等选项),重组操作建议在提升至二阶之前完成。二阶三角形(TRI6, 6 节点)的相邻拓扑关系与一阶一致,但 Blossom 算法的代价函数是为一阶单元设计的。