第7章 背景网格(Background Mesh):用已有网格指导网格尺寸
7.1 学习目标
- 理解背景网格(Background Mesh)的核心原理:用已有网格的单元尺寸信息指导新几何体的网格生成
- 掌握通过
merge()加载.pos后处理视图文件作为背景网格的方法 - 学会使用
PostView尺寸场(Size Field)将视图数据绑定为背景网格,并关闭其他尺寸约束 - 理解背景网格、点级
lc、尺寸场(Size Field)三者之间的优先级关系 - 了解自适应细化中的级联网格策略:用初次粗网格作为第二轮的背景网格
7.2 核心概念说明
7.2.1 什么是背景网格(Background Mesh)
在实际 CAE 前处理中,经常会遇到这样的需求:某一区域的网格需要根据另一个已存在网格的信息来确定尺寸。例如:
- 自适应网格细化(Adaptive Mesh Refinement):先在粗网格上求解,根据误差估计对网格进行细化,此时"含误差分布的粗网格"可以作为细化阶段的输入
- 多物理场耦合:一个物理场的网格信息指导另一个物理场的网格生成
- 几何修复重剖:从旧的网格中提取尺寸分布,用于重新划分新几何
Gmsh 通过背景网格机制支持这类工作流。背景网格本质上是一个后处理视图(Post-Processing View),其中每个单元(三角形、四面体等)携带一个标量值——目标网格尺寸(target mesh size)。在网格生成时,Gmsh 查询该视图中任意空间位置处的插值尺寸,替代或补充传统的点级 lc 约束。
7.2.2 两种 Post-Processing View 的区别
Gmsh 的后处理视图分为两类,它们在作为背景网格时的使用方式有所不同:
| 视图类型 | 数据存储方式 | 作为背景网格时的要求 |
|---|---|---|
| 基于列表的视图(List-based view) | 数据以独立列表形式存在于 .pos 文件中,不依赖任何模型实体 |
可直接使用,无需额外操作 |
| 基于模型的视图(Model-based view) | 数据依附于某个具体模型的网格实体 | 必须创建新模型(gmsh::model::add)来容纳新几何体,否则剖分新网格会破坏背景网格数据 |
官方 t7.cpp 中使用的是基于列表的视图,因此较为简单。本章聚焦于此场景。
7.2.3 .pos 文件格式简介
.pos(Post-processing data)是 Gmsh 的后处理数据文件格式。一个典型的三维标量三角形视图如下:
View "background mesh" {
ST(0.078,0.235,0, 0.069,0.238,0, 0.070,0.229,0){0.012,0.012,0.008};
ST(0.011,0.100,0, 0.017,0.104,0, 0.007,0.106,0){0.004,0.005,0.003};
...
};
ST表示标量三角形(Scalar Triangle),三个括号为三角形三顶点坐标,花括号内为每个顶点上的标量值(即目标网格尺寸)- 类似地还有
SS(标量线段)、SQ(标量四边形)、VT(矢量三角形)等单元类型 - 该文件由
merge()直接加载,Gmsh 自动解析为后处理视图
7.2.4 背景网格的尺寸优先级
当同时存在多种尺寸约束时,Gmsh 按以下机制处理:
- 默认行为:Gmsh 综合点级
lc、边界曲率、背景网格等多种来源计算最终尺寸 - 仅用背景网格:通过以下三个选项全部设为 0,可令 Gmsh 仅依赖背景网格决定尺寸:
cpp
gmsh::option::setNumber("Mesh.MeshSizeExtendFromBoundary", 0); // 禁用边界延伸尺寸
gmsh::option::setNumber("Mesh.MeshSizeFromPoints", 0); // 禁用点级 lc
gmsh::option::setNumber("Mesh.MeshSizeFromCurvature", 0); // 禁用曲率自适应尺寸
- 尺寸因子:
Mesh.MeshSizeFactor选项对所有尺寸来源(包括背景网格)施加全局缩放因子,默认值为 1。
这一优先级机制使得背景网格既可独立决定网格尺寸,也可与点级 lc 协同工作。
7.2.5 背景网格 vs 尺寸场(Size Field)
背景网格实际上是尺寸场(Size Field, 将在第10章详细介绍)的一种特例。当调用 gmsh::model::mesh::field::setAsBackgroundMesh() 时,Gmsh 内部会将指定的 PostView 字段注册为一个通用的尺寸场。理解这一关系有助于后续学习更复杂的自适应尺寸控制策略。
7.3 C++ 代码逐段讲解
本章代码演示两种场景:先使用背景网格生成网格,再关闭背景网格对比仅有 lc 值时的网格密度。
7.3.1 头部与初始化
#include <set>
#include <gmsh.h>
int main(int argc, char **argv) {
gmsh::initialize();
// 加载基于列表的后处理视图,包含目标网格尺寸
try {
gmsh::merge("../t7_bgmesh.pos");
} catch (...) {
gmsh::logger::write("Could not load background mesh: bye!");
gmsh::finalize();
return 0;
}
gmsh::merge()加载外部文件到当前模型,支持.geo、.msh、.pos、.stl等多种格式- 此处加载的是
.pos文件(列表型后处理视图),Gmsh 将其解析为一个或多个View - 使用
try-catch包裹文件加载是良好的工程实践——文件缺失时给出明确错误信息并优雅退出
7.3.2 创建新模型与几何
// 创建新模型容纳几何体。虽然列表型视图不会被剖分破坏,
// 但显式创建新模型是好习惯,确保数据隔离
gmsh::model::add("t7");
// 定义一个简单矩形区域
double lc = 1e-2;
gmsh::model::geo::addPoint(0.0, 0.0, 0, lc, 1);
gmsh::model::geo::addPoint(0.1, 0.0, 0, lc, 2);
gmsh::model::geo::addPoint(0.1, 0.3, 0, lc, 3);
gmsh::model::geo::addPoint(0.0, 0.3, 0, lc, 4);
gmsh::model::geo::addLine(1, 2, 1);
gmsh::model::geo::addLine(3, 2, 2);
gmsh::model::geo::addLine(3, 4, 3);
gmsh::model::geo::addLine(4, 1, 4);
gmsh::model::geo::addCurveLoop({4, 1, -2, 3}, 1);
gmsh::model::geo::addPlaneSurface({1}, 1);
gmsh::model::geo::synchronize();
- 矩形尺寸为 0.1 x 0.3,点级
lc = 1e-2(约 0.01) addCurveLoop({4, 1, -2, 3}, 1)中,曲线 2 使用了负号——因为曲线 2 的原始定义是(3 -> 2),而外环需要逆时针方向,因此需要翻转其方向synchronize()将几何从geo模块同步到 Gmsh 核心模型
7.3.3 将视图绑定为背景网格尺寸场
// 新增一个基于 PostView 的尺寸场(Size Field)
int bg_field = gmsh::model::mesh::field::add("PostView");
gmsh::model::mesh::field::setNumber(bg_field, "ViewIndex", 0);
// 将该字段设为当前模型的背景网格
gmsh::model::mesh::field::setAsBackgroundMesh(bg_field);
关键步骤解析:
1. gmsh::model::mesh::field::add("PostView") 创建一个类型为 "PostView" 的尺寸场,返回其整数句柄 bg_field
2. setNumber(bg_field, "ViewIndex", 0) 指定该尺寸场绑定到索引为 0 的视图(即 merge 加载的第一个视图)
3. setAsBackgroundMesh(bg_field) 将该尺寸场激活为当前模型的背景网格
7.3.4 禁用其他尺寸约束(可选)
// 仅从背景网格计算尺寸,忽略其他约束
gmsh::option::setNumber("Mesh.MeshSizeExtendFromBoundary", 0);
gmsh::option::setNumber("Mesh.MeshSizeFromPoints", 0);
gmsh::option::setNumber("Mesh.MeshSizeFromCurvature", 0);
如果不设置这三个选项,Gmsh 会将背景网格与点级 lc、几何曲率等综合计算(取最小值为准)。设为零后,尺寸完全由背景网格决定。在实际工程中应根据需求选择是否关闭这些选项。
7.3.5 网格生成与结果输出
gmsh::model::mesh::generate(2);
gmsh::write("t7.msh");
// 启动 GUI 查看结果
std::set<std::string> args(argv, argv + argc);
if (!args.count("-nopopup")) gmsh::fltk::run();
gmsh::finalize();
return 0;
}
7.3.6 对比实验:有/无背景网格的网格密度差异
为直观展示背景网格的作用,以下扩展代码在同一个矩形几何上分别按"仅点级 lc"和"仅背景网格"两种方式生成网格,输出两份 .msh 文件供对比:
// ===== 方案 A:仅点级 lc(不启用背景网格) =====
gmsh::model::add("t7_no_bgm");
// ... 创建相同的矩形几何 ...
gmsh::model::geo::synchronize();
gmsh::model::mesh::generate(2);
gmsh::write("t7_no_bgm.msh");
// ===== 方案 B:仅背景网格(关闭点级 lc) =====
gmsh::model::add("t7_with_bgm");
// ... 创建相同的矩形几何 ...
int bg = gmsh::model::mesh::field::add("PostView");
gmsh::model::mesh::field::setNumber(bg, "ViewIndex", 0);
gmsh::model::mesh::field::setAsBackgroundMesh(bg);
gmsh::option::setNumber("Mesh.MeshSizeFromPoints", 0);
gmsh::model::geo::synchronize();
gmsh::model::mesh::generate(2);
gmsh::write("t7_with_bgm.msh");
对比两个文件可以发现:方案 A 的网格尺寸由统一的 lc = 1e-2 控制(密度均匀);方案 B 的网格尺寸由背景网格 .pos 文件中各三角形的标量值决定(密度随空间变化)。
7.4 完整可运行代码
// -----------------------------------------------------------------------------
// Gmsh C++ 教程 第7章 —— 背景网格(Background Mesh)
//
// 本程序演示如何使用后处理视图作为背景网格来指导网格尺寸生成。
//
// 运行方式:
// 编译并运行,或在 Gmsh GUI 中加载 .pos 文件并选择 "Apply as background mesh"
// 命令行等效:gmsh t1.geo -bgm t7_bgmesh.pos
// -----------------------------------------------------------------------------
#include <set>
#include <gmsh.h>
int main(int argc, char **argv) {
gmsh::initialize();
// --------------------------------------------------------------------
// 步骤 1:加载背景网格数据(列表型后处理视图)
// --------------------------------------------------------------------
try {
gmsh::merge("../t7_bgmesh.pos");
} catch (...) {
gmsh::logger::write("Could not load background mesh: bye!");
gmsh::finalize();
return 0;
}
// --------------------------------------------------------------------
// 步骤 2:创建新模型并定义几何
// --------------------------------------------------------------------
gmsh::model::add("t7");
double lc = 1e-2;
gmsh::model::geo::addPoint(0.0, 0.0, 0, lc, 1);
gmsh::model::geo::addPoint(0.1, 0.0, 0, lc, 2);
gmsh::model::geo::addPoint(0.1, 0.3, 0, lc, 3);
gmsh::model::geo::addPoint(0.0, 0.3, 0, lc, 4);
gmsh::model::geo::addLine(1, 2, 1);
gmsh::model::geo::addLine(3, 2, 2);
gmsh::model::geo::addLine(3, 4, 3);
gmsh::model::geo::addLine(4, 1, 4);
gmsh::model::geo::addCurveLoop({4, 1, -2, 3}, 1);
gmsh::model::geo::addPlaneSurface({1}, 1);
gmsh::model::geo::synchronize();
// --------------------------------------------------------------------
// 步骤 3:将视图注册为 PostView 尺寸场,并激活为背景网格
// --------------------------------------------------------------------
int bg_field = gmsh::model::mesh::field::add("PostView");
gmsh::model::mesh::field::setNumber(bg_field, "ViewIndex", 0);
gmsh::model::mesh::field::setAsBackgroundMesh(bg_field);
// --------------------------------------------------------------------
// 步骤 4(可选):仅从背景网格计算尺寸,关闭其他约束
// --------------------------------------------------------------------
gmsh::option::setNumber("Mesh.MeshSizeExtendFromBoundary", 0);
gmsh::option::setNumber("Mesh.MeshSizeFromPoints", 0);
gmsh::option::setNumber("Mesh.MeshSizeFromCurvature", 0);
// --------------------------------------------------------------------
// 步骤 5:生成网格并输出
// --------------------------------------------------------------------
gmsh::model::mesh::generate(2);
gmsh::write("t7.msh");
// 启动 GUI 查看结果(传 -nopopup 可跳过)
std::set<std::string> args(argv, argv + argc);
if (!args.count("-nopopup")) gmsh::fltk::run();
gmsh::finalize();
return 0;
}
7.5 关键 API 速查表
文件合并
| 函数 | 签名 | 说明 |
|---|---|---|
gmsh::merge |
merge(filename) |
将外部文件(.geo / .msh / .pos / .stl 等)合并到当前模型 |
尺寸场(Size Field)——背景网格子集
| 函数 | 说明 |
|---|---|
gmsh::model::mesh::field::add("PostView") |
创建基于后处理视图的尺寸场,返回字段句柄 |
gmsh::model::mesh::field::setNumber(field, "ViewIndex", idx) |
将尺寸场绑定到第 idx 号视图(0-based) |
gmsh::model::mesh::field::setAsBackgroundMesh(field) |
将指定尺寸场激活为当前模型的背景网格 |
网格尺寸控制选项
| 选项名 | 默认值 | 说明 |
|---|---|---|
Mesh.MeshSizeExtendFromBoundary |
1 | 是否从边界曲线延伸计算尺寸约束 |
Mesh.MeshSizeFromPoints |
1 | 是否使用几何点的 lc 值作为尺寸约束 |
Mesh.MeshSizeFromCurvature |
1 | 是否根据几何曲率自适应调整尺寸 |
Mesh.MeshSizeFactor |
1 | 全局尺寸缩放因子(对所有来源生效) |
标准网格流程
| 函数 | 说明 |
|---|---|
gmsh::model::geo::addPoint(x, y, z, lc, tag) |
创建几何点,lc 为该点局部尺寸(可被背景网格覆盖) |
gmsh::model::geo::addLine(start, end, tag) |
创建直线 |
gmsh::model::geo::addCurveLoop({curves...}, tag) |
由曲线列表创建闭合环(负号=反向) |
gmsh::model::geo::addPlaneSurface({loops...}, tag) |
由曲线环创建平面面 |
gmsh::model::geo::synchronize() |
将几何数据同步到 Gmsh 核心模型 |
gmsh::model::mesh::generate(dim) |
生成 dim 维网格 |
gmsh::write(filename) |
将模型+网格写出到文件 |
7.6 注意事项
-
view-based vs list-based:如果使用的
.pos文件是基于模型的视图(依附于某个网格),务必将新几何定义在另一个模型(通过gmsh::model::add创建)中,否则mesh::generate()的剖分过程会破坏背景网格数据。 -
ViewIndex 从 0 开始:
setNumber(field, "ViewIndex", 0)绑定的是merge加载的第一个视图。如果.pos文件包含多个View定义块,需要指定对应的索引。 -
关闭选项的作用域:
Mesh.MeshSizeFromPoints、Mesh.MeshSizeExtendFromBoundary、Mesh.MeshSizeFromCurvature均为全局选项(gmsh::option::setNumber),会影响该进程内后续的所有网格生成操作。在多模型场景下如需恢复默认行为,应显式设回 1。 -
与命令行方式的对应:本章的 C++ 逻辑等价于命令行
gmsh geometry.geo -bgm background_mesh.pos,其中-bgm标志自动完成 "加载视图 + 注册 PostView 字段 + 设为背景网格" 三步操作。 -
学习路线:背景网格是尺寸场(Size Field)的特例。掌握本章后,建议继续阅读第10章,了解更通用、灵活的尺寸场系统(如
MathEval、Box、Min、Max等复合尺寸场)。 -
自适应细化应用:在实战中,典型的级联工作流为:第一步用粗网格做求解器计算;第二步提取误差估计写入
.pos文件(每个单元携带一个误差标量);第三步将该.pos作为背景网格加载,生成误差大处密集、误差小处稀疏的细化网格。此方法可显著提升 CAE 求解的精度-成本比。