第24章 后处理视图(View):List-based 与 Model-based 数据导入
24.1 学习目标
- 理解 Gmsh 后处理的两大数据类型:基于列表的视图(List-based View)和基于模型的视图(Model-based View),以及各自的适用场景
- 掌握
view::add()创建视图、view::addListData()用展平数组导入自定义坐标+场值数据,理解元素形状字符串("ST"、"VL"、"SQ"等)的命名规则 - 掌握
view::addHomogeneousModelData()与view::addModelData()将求解器输出绑定到模型网格节点/单元 - 理解 step(时间步)参数在多步动画和跨模型切换中的作用
- 学会
view::addListDataString()添加文字标注、view::probe()探针采样、高阶场的自适应可视化配置
24.2 核心概念说明
24.2.1 View 与 Plugin 的分工
后处理(Post-Processing)由两个组件协作完成:
View (视图) ─── 本章内容 ─── 数据的容器
├── List-based: 独立于任何模型与网格,自包含坐标+场值
└── Model-based: 绑定到特定模型实体与网格节点
Plugin (插件) ─── 见第20章 ─── 对 View 进行运算与变换
View 存储标量场(Scalar Field)、矢量场(Vector Field)或张量场(Tensor Field)的数据及渲染选项。GUI 可同时显示多个视图,每个独立控制可见性、时间步和显示模式。
24.2.2 两种视图对比
| 特性 | List-based | Model-based |
|---|---|---|
| 数据来源 | 用户自定义坐标+场值 | 绑定模型网格节点/单元 |
| 独立性 | 完全独立,无需模型 | 必须依赖已有模型 |
| 数据结构 | 逐元素展平列表 | 逐节点/逐单元数组 |
| 多步支持 | 定长,各步结构相同 | 每步可绑定不同模型 |
| 典型用途 | 传感器读数、解析解可视化 | 求解器输出(应力/位移/温度) |
| 文件格式 | .pos |
.msh |
24.2.3 元素形状字符串速查
addListData() 第二个参数为两字符字符串:首字符=场类型,次字符=元素形状。
场类型(首字符):S=标量(1分量)、V=矢量(3分量)、T=张量(9分量)
元素形状(次字符):P=点(1节点)、L=线(2)、T=三角形(3)、Q=四边形(4)、S=四面体(4)、H=六面体(8)、I=三棱柱(6)、Y=金字塔(5)
示例:"ST"=三角形标量场(每单元3值),"VL"=线矢量场(每节点3分量,共6值)。
24.2.4 Model-based 的三种数据类型
| dataType | 含义 | 场连续性 |
|---|---|---|
"NodeData" |
节点数据,每节点一个值 | 连续场(节点间插值) |
"ElementData" |
单元数据,每单元一个常数值 | 分片常值场 |
"ElementNodeData" |
单元节点数据,每单元的每节点一个值 | 间断场(单元边界可跳变) |
24.3 List-based 视图编程(基于 x3.cpp)
24.3.1 创建视图并导入三角形标量场
#include <set>
#include <iostream>
#include <gmsh.h>
int main(int argc, char **argv)
{
gmsh::initialize(argc, argv);
// 创建视图: view::add(name) 返回视图的整数 tag
int t1 = gmsh::view::add("A list-based view");
List-based 数据组织方式:每个元素的坐标(先全部 x、再全部 y、最后全部 z),随后追加各时间步的场值。以下构建两个一阶三角形(各3节点),并填充10个时间步的标量数据:
// 三角形1和三角形2的坐标(x集合 + y集合 + z集合,各3个值)
std::vector<double> triangle1 = {
0., 1., 1., 0., 0., 1., 0., 0., 0.};
std::vector<double> triangle2 = {
0., 1., 0., 0., 1., 1., 0., 0., 0.};
// 为每个三角形追加 10 个时间步的场值(每步3个标量值)
for(int step = 0; step < 10; step++) {
triangle1.insert(triangle1.end(), {10., 11. - step, 12.});
triangle2.insert(triangle2.end(), {11., 12., 13. + step});
}
// 拼接两个三角形数据并导入
std::vector<double> triangles(triangle1);
triangles.insert(triangles.end(), triangle2.begin(), triangle2.end());
// 参数: viewTag, "ST"=标量三角形, 2个元素, 展平数据
gmsh::view::addListData(t1, "ST", 2, triangles);
总数据量 = 233(坐标) + 1023(场值) = 78 个 double。addListData 根据元素类型和数量自动推断数据长度。
24.3.2 导入线段矢量场
矢量场每节点需3个分量 (vx, vy, vz)。以一条线段("VL")为例:
// 线段: 2节点坐标(x集合 + y集合 + z集合)
std::vector<double> line = {0., 1., 1.2, 1.2, 0., 0.};
for(int step = 0; step < 10; step++)
// 每步: 2节点 x 3分量 = 6个值
line.insert(line.end(), {10. + step, 0., 0., 10. + step, 0., 0.});
gmsh::view::addListData(t1, "VL", 1, line);
24.3.3 添加文字标注与选项配置
// 2D 标注(屏幕坐标,原点=窗口中心,正y朝上)和 3D 标注(世界坐标)
gmsh::view::addListDataString(t1, {20., -20.}, {"Created with Gmsh"});
gmsh::view::addListDataString(t1, {0.5, 1.5, 0.},
{"A multi-step list-based view"},
{"Align", "Center", "Font", "Helvetica"});
// 视图显示选项
gmsh::view::option::setNumber(t1, "TimeStep", 5); // 显示第5步
gmsh::view::option::setNumber(t1, "IntervalsType", 3); // 3=连续色谱
// 探针采样: 在 (0.9, 0.1, 0) 获取插值后的场值
std::vector<double> val;
double dist;
gmsh::view::probe(t1, 0.9, 0.1, 0, val, dist);
std::cout << "Value at (0.9, 0.1, 0):";
for(auto v : val) std::cout << " " << v;
std::cout << std::endl;
常用视图选项:"TimeStep"(当前步)、"IntervalsType"(1=等值线,2=填充,3=连续色谱)、"NbIso"(等值线数量)、"RangeType"(1=按步,2=跨步,3=自定义)、"ShowScale"(显示色条)。
24.3.4 高阶场与自适应可视化
高阶场需通过 setInterpolationMatrices() 定义基函数的系数矩阵 c 和指数矩阵 e:
int t2 = gmsh::view::add("Second order quad");
// 4节点四边形的坐标 + 9个场值(支持二阶9节点插值)
std::vector<double> quad = {
0.,1.,1.,0., -1.2,-1.2,-0.2,-0.2, 0.,0.,0.,0.};
quad.insert(quad.end(), {1.,1.,1.,1., 3.,3.,3.,3., -3.});
// 基函数: f[i](u,v,w) = sum_j c[i][j] * u^e[j][0] * v^e[j][1] * w^e[j][2]
gmsh::view::setInterpolationMatrices(
t2, "Quadrangle", 9,
{ /* 9x9 系数矩阵 c, 行优先 */ },
{ /* 9x3 指数矩阵 e, 行优先 */ });
gmsh::view::addListData(t2, "SQ", 1, quad);
// 启用自适应可视化网格(AMR驱动渲染)
gmsh::view::option::setNumber(t2, "AdaptVisualizationGrid", 1);
gmsh::view::option::setNumber(t2, "TargetError", 1e-2);
gmsh::view::option::setNumber(t2, "MaxRecursionLevel", 5);
Gmsh 根据 TargetError 自动细分单元直到插值误差达标或达到 MaxRecursionLevel。细分后的数据可通过 view::getListData() 取回(设置 returnAdaptive=true)。
24.3.5 保存与启动
gmsh::view::write(t1, "x3.pos"); // 保存为 .pos 文件
std::set<std::string> args(argv, argv + argc);
if(!args.count("-nopopup")) gmsh::fltk::run();
gmsh::finalize();
return 0;
}
24.4 Model-based 视图编程(基于 x4.cpp)
24.4.1 创建模型与手动网格
Model-based 视图必须绑定到模型。首先创建模型并手动构建网格:
#include <set>
#include <gmsh.h>
int main(int argc, char **argv)
{
gmsh::initialize(argc, argv);
gmsh::model::add("simple model");
int surf = gmsh::model::addDiscreteEntity(2); // 离散面,无几何描述
// 添加4个节点: {标签}, 展平坐标(xyz xyz xyz xyz)
gmsh::model::mesh::addNodes(2, surf, {1,2,3,4},
{0.,0.,0., 1.,0.,0., 1.,1.,0., 0.,1.,0.});
// 添加2个三角形(element type=2): {单元标签}, {连通性}
gmsh::model::mesh::addElementsByType(surf, 2, {1,2}, {1,2,3, 1,3,4});
addDiscreteEntity(2) 创建维度2的离散实体,无需 OCC 几何,纯粹由用户定义的网格填充,适用于只需后处理的"裸网格"场景。
24.4.2 添加 NodeData(连续场)
int t1 = gmsh::view::add("Continuous");
for(int step = 0; step < 10; step++) {
gmsh::view::addHomogeneousModelData(
t1, step, "simple model", "NodeData",
{1, 2, 3, 4}, // 节点标签列表
{10., 10., 12. + step, 13. + step}); // 逐节点场值(4个)
}
addHomogeneousModelData() 参数:viewTag、step(0-based时间步)、modelName(须与 model::add() 一致)、dataType、tags 列表、data 展平数组。"NodeData" 产生连续场,Gmsh 在节点间线性插值。
24.4.3 添加 ElementNodeData(间断场)
int t2 = gmsh::view::add("Discontinuous");
for(int step = 0; step < 10; step++) {
gmsh::view::addHomogeneousModelData(
t2, step, "simple model", "ElementNodeData",
{1, 2}, // 单元标签列表
{10., 10., 12. + step, // 单元1的3个节点值
14., 15., 13. + step}); // 单元2的3个节点值
}
对于2个三角形(各3节点),每步需 2*3=6 个值。"ElementNodeData" 容许单元边界上值不连续(如节点3处单元1值为12+step、单元2值为14),呈现颜色跳变。"ElementData" 更简单:每单元仅一个常量值。
24.4.4 跨模型多步动画
Model-based 视图的核心优势是不同时间步可绑定到不同模型,支持网格重划分后的无缝可视化:
// 创建第二个模型: OCC 立方体 + 自动四面体剖分
gmsh::model::add("another model");
gmsh::model::occ::addBox(0, 0, 0, 1, 1, 1);
gmsh::model::occ::synchronize();
gmsh::model::mesh::generate(3); // 三维网格
// 获取新模型的所有节点
std::vector<std::size_t> nodes;
std::vector<double> coord, coordParam;
gmsh::model::mesh::getNodes(nodes, coord, coordParam);
// 在同一个视图 t1 中添加 step 11-19,绑定到新模型
for(int step = 11; step < 20; step++) {
std::vector<double> val;
for(std::size_t i = 0; i < coord.size(); i += 3)
val.push_back(step * coord[i]); // 场值 = step * x
gmsh::view::addHomogeneousModelData(t1, step, "another model",
"NodeData", nodes, val);
}
视图 t1 的 step 0-9 基于粗网格(4节点2三角形),step 11-19 基于细网格(OCC立方体)。在 GUI 中播放动画时自动切换。这对动态裂纹扩展等需要网格重划分的时变求解器尤为重要。
24.4.5 addModelData() 处理非均匀数据
当需要混合不同类型元素时(如三角形+四边形混合网格),应使用更通用的 addModelData():
gmsh::view::addModelData(viewTag, step, modelName, dataType,
{tag1, tag2, ...}, // 实体标签列表
data, // 展平数据 (double*)
numComponents, // 每实体分量数(如标量=1,矢量=3)
numEntities); // 实体总数
// 可选参数 partitionSize[]: 指定每个实体各自的数据长度
它通过 partitionSize 数组支持每个实体拥有不同的节点/值数量,适用于混合网格场景。
24.4.6 保存
gmsh::view::write(t1, "x4_t1.msh");
gmsh::view::write(t2, "x4_t2.msh");
// 跨模型视图会自动生成多个文件
std::set<std::string> args(argv, argv + argc);
if(!args.count("-nopopup")) gmsh::fltk::run();
gmsh::finalize();
return 0;
}
若设置 gmsh::option::setNumber("PostProcessing.SaveMesh", 1),write() 将同时保存网格几何;否则仅保存场值。
24.5 完整可运行代码
示例一:List-based 视图
// ch24_list_based.cpp
#include <set>
#include <iostream>
#include <gmsh.h>
int main(int argc, char **argv)
{
gmsh::initialize(argc, argv);
// ---- 1. 三角形标量场 (10步) ----
int t1 = gmsh::view::add("List-based triangles + lines");
std::vector<double> tri1 = {0.,1.,1., 0.,0.,1., 0.,0.,0.};
std::vector<double> tri2 = {0.,1.,0., 0.,1.,1., 0.,0.,0.};
for(int s = 0; s < 10; s++) {
tri1.insert(tri1.end(), {10., 11.-s, 12.});
tri2.insert(tri2.end(), {11., 12., 13.+s});
}
tri1.insert(tri1.end(), tri2.begin(), tri2.end());
gmsh::view::addListData(t1, "ST", 2, tri1);
// ---- 2. 线段矢量场 (10步) ----
std::vector<double> line = {0.,1., 1.2,1.2, 0.,0.};
for(int s = 0; s < 10; s++)
line.insert(line.end(), {10.+s,0.,0., 10.+s,0.,0.});
gmsh::view::addListData(t1, "VL", 1, line);
// ---- 3. 文字标注 ----
gmsh::view::addListDataString(t1, {20.,-20.}, {"Created with Gmsh"});
gmsh::view::addListDataString(t1, {0.5,1.5,0.},
{"A multi-step list-based view"},
{"Align","Center","Font","Helvetica"});
// ---- 4. 选项与探针 ----
gmsh::view::option::setNumber(t1, "TimeStep", 5);
gmsh::view::option::setNumber(t1, "IntervalsType", 3);
std::vector<double> val;
double dist;
gmsh::view::probe(t1, 0.9, 0.1, 0, val, dist);
std::cout << "Probe at (0.9,0.1,0):";
for(auto v : val) std::cout << " " << v;
std::cout << std::endl;
// ---- 5. 二阶四边形 + 自适应可视化 ----
int t2 = gmsh::view::add("Second order quad");
std::vector<double> quad = {
0.,1.,1.,0., -1.2,-1.2,-0.2,-0.2, 0.,0.,0.,0.};
quad.insert(quad.end(), {1.,1.,1.,1., 3.,3.,3.,3., -3.});
gmsh::view::setInterpolationMatrices(
t2, "Quadrangle", 9,
{0,0,0.25,0,0,-0.25,-0.25,0,0.25, 0,0,0.25,0,0,-0.25,0.25,0,-0.25,
0,0,0.25,0,0,0.25,0,0,0.25, -0.25,0,-0.25,0,0,-0.5,0.5,0,0.5,
0,-0.5,0,0,0.5,-0.5,0,0.5,-0.5, 0,0.5,0,-0.5,0,0,0,0,-0.5,
0.5,0,-0.5,0,0.5,0,0,0.5,-0.5, 0,0.5,0,0,1,-1,1,-1,0,0,0,0,0},
{0,0,0,2,0,0,2,2,0, 0,2,0,1,0,0,2,1,0,1,
2,0,0,1,0,1,1,0});
gmsh::view::addListData(t2, "SQ", 1, quad);
gmsh::view::option::setNumber(t2, "AdaptVisualizationGrid", 1);
gmsh::view::option::setNumber(t2, "TargetError", 1e-2);
gmsh::view::option::setNumber(t2, "MaxRecursionLevel", 5);
std::set<std::string> args(argv, argv + argc);
if(!args.count("-nopopup")) gmsh::fltk::run();
gmsh::finalize();
return 0;
}
示例二:Model-based 视图
// ch24_model_based.cpp
#include <set>
#include <gmsh.h>
int main(int argc, char **argv)
{
gmsh::initialize(argc, argv);
// ---- 1. 构建手动网格(离散面 + 4节点 + 2个三角形) ----
gmsh::model::add("simple model");
int surf = gmsh::model::addDiscreteEntity(2);
gmsh::model::mesh::addNodes(2, surf, {1,2,3,4},
{0.,0.,0., 1.,0.,0., 1.,1.,0., 0.,1.,0.});
gmsh::model::mesh::addElementsByType(surf, 2, {1,2}, {1,2,3, 1,3,4});
// ---- 2. NodeData(连续场) ----
int t1 = gmsh::view::add("Continuous NodeData");
for(int s = 0; s < 10; s++)
gmsh::view::addHomogeneousModelData(
t1, s, "simple model", "NodeData",
{1,2,3,4}, {10., 10., 12.+s, 13.+s});
// ---- 3. ElementNodeData(间断场) ----
int t2 = gmsh::view::add("Discontinuous ElementNodeData");
for(int s = 0; s < 10; s++)
gmsh::view::addHomogeneousModelData(
t2, s, "simple model", "ElementNodeData",
{1,2}, {10., 10., 12.+s, 14., 15., 13.+s});
// ---- 4. 第二个模型(OCC立方体)跨模型追加步 ----
gmsh::model::add("another model");
gmsh::model::occ::addBox(0,0,0, 1,1,1);
gmsh::model::occ::synchronize();
gmsh::model::mesh::generate(3);
std::vector<std::size_t> nodes;
std::vector<double> coords, params;
gmsh::model::mesh::getNodes(nodes, coords, params);
for(int s = 11; s < 20; s++) {
std::vector<double> v;
for(std::size_t i = 0; i < coords.size(); i += 3)
v.push_back(s * coords[i]);
gmsh::view::addHomogeneousModelData(t1, s, "another model",
"NodeData", nodes, v);
}
// ---- 5. 保存 ----
gmsh::view::write(t1, "ch24_t1.msh");
gmsh::view::write(t2, "ch24_t2.msh");
std::set<std::string> args(argv, argv + argc);
if(!args.count("-nopopup")) gmsh::fltk::run();
gmsh::finalize();
return 0;
}
24.6 关键 API 速查表
视图创建与管理
| API | 说明 |
|---|---|
int gmsh::view::add(name) |
创建新视图,返回 tag |
void gmsh::view::remove(tag) |
删除指定视图 |
void gmsh::view::write(tag, filename) |
保存视图(list-based为.pos,model-based为.msh) |
void gmsh::view::probe(tag, x, y, z, val, dist) |
在(x,y,z)处采样场值 |
List-based 数据导入
| API | 说明 |
|---|---|
void gmsh::view::addListData(tag, type, numElems, data) |
导入展平列表数据 |
void gmsh::view::addListDataString(tag, coords, strings, options) |
添加2D/3D文字标注 |
void gmsh::view::setInterpolationMatrices(tag, type, d, c, e) |
设置高阶场插值矩阵 |
void gmsh::view::getListData(tag, type, numElems, data) |
读取视图中的列表数据 |
Model-based 数据导入
| API | 说明 |
|---|---|
void gmsh::view::addHomogeneousModelData(tag, step, model, type, tags, data) |
同构数据导入(简化版) |
void gmsh::view::addModelData(tag, step, model, type, tags, data, comp, nEnts) |
通用数据导入(支持非均匀数据) |
void gmsh::view::getModelData(tag, step, model, type, tags, data) |
取回模型绑定数据 |
视图选项(view::option 命名空间)
| API | 说明 |
|---|---|
void gmsh::view::option::setNumber(tag, option, value) |
设置数值选项 |
void gmsh::view::option::getNumber(tag, option, value) |
获取数值选项 |
void gmsh::view::option::setString(tag, option, value) |
设置字符串选项 |
常用视图选项名称
| 选项名 | 类型 | 含义 |
|---|---|---|
"TimeStep" |
Number | 当前显示时间步 |
"NbTimeStep" |
Number | 总时间步数(只读) |
"IntervalsType" |
Number | 1=等值线, 2=填充, 3=连续色谱 |
"NbIso" |
Number | 等值线/填充数量 |
"RangeType" |
Number | 1=按步, 2=跨步, 3=自定义 |
"CustomMin" / "CustomMax" |
Number | 自定义色条范围 |
"ShowScale" |
Number | 0=隐藏色条, 1=显示 |
"ArrowSizeMax" / "ArrowSizeMin" |
Number | 矢量箭头尺寸范围 |
"AdaptVisualizationGrid" |
Number | 0=关闭, 1=开启自适应可视化 |
"TargetError" |
Number | 自适应细分误差阈值 |
"MaxRecursionLevel" |
Number | 自适应细分最大层级 |
24.7 小结
- List-based 视图:数据与网格解耦,通过
view::addListData()直接提供坐标和场值。元素类型由两字符标识符决定(如 "ST"、"VL")。适合外部数据、传感器读数、解析解的可视化。 - Model-based 视图:数据绑定到模型网格实体,三种数据类型覆盖有限元结果输出:
"NodeData"(连续插值云图)、"ElementData"(分片常值)、"ElementNodeData"(单元间间断场)。跨模型步功能使网格重划分动画成为可能。 - 高阶可视化:通过
setInterpolationMatrices()定义基函数,配合AdaptVisualizationGrid等选项实现 AMR 驱动的自适应渲染。 - 视图管理:
view::option控制显示参数,view::probe()支持任意点采样,view::write()支持持久化输出(.pos / .msh)。