第15章 周期性网格
15.1 学习目标
- 理解周期性网格(Periodic Mesh)的物理意义和有限元应用场景
- 掌握
mesh::setPeriodic()的API调用:4x4仿射变换矩阵的定义 - 学会对简单几何和复杂几何设置周期边界
- 了解自动匹配周期面的方法
15.2 核心概念说明
15.2.1 什么是周期性网格
周期性网格(Periodic Mesh)要求一对主从面(master/slave)上的节点完全一一对应:
从面节点坐标 = T(对应主面节点坐标)
其中 T 是一个仿射变换(平移+旋转+可能的缩放)。
15.2.2 有限元应用
| 场景 | 周期类型 | 说明 |
|---|---|---|
| 代表体元法(RVE) | 三轴周期 | 微观结构单胞,各对面节点匹配 |
| 周期性边界条件(PBC) | 指定方向周期 | 复合材料/晶体分析 |
| 旋转周期 | 旋转对称 | 叶轮机械单通道分析 |
| 电磁周期 | Floquet周期 | 天线阵列单元 |
15.2.3 实现原理
在网格生成时,Gmsh不独立对从面划分网格,而是: 1. 先对主面生成网格 2. 将主面的网格通过变换矩阵T映射到从面 3. 保证两个面的节点一一对应
15.2.4 仿射变换矩阵
setPeriodic 需要一个16元素向量,表示按行排列的4×4矩阵:
std::vector<double> translation = {
1, 0, 0, dx, // 第1行: (1, 0, 0, dx)
0, 1, 0, dy, // 第2行: (0, 1, 0, dy)
0, 0, 1, dz, // 第3行: (0, 0, 1, dz)
0, 0, 0, 1 // 第4行: (0, 0, 0, 1)
};
对于纯平移:dx/dy/dz 即为偏移量。矩阵形式可表达任意仿射变换。
15.3 C++代码逐段讲解
15.3.1 简单立方体周期(手动指定面)
#include <gmsh.h>
gmsh::initialize();
gmsh::model::add("ch15");
// 用OpenCASCADE创建立方体
gmsh::model::occ::addBox(0, 0, 0, 1, 1, 1, 1);
gmsh::model::occ::synchronize();
// 设置非均匀网格尺寸(便于视觉验证周期效果)
std::vector<std::pair<int,int>> out;
gmsh::model::getEntities(out, 0);
gmsh::model::mesh::setSize(out, 0.1);
gmsh::model::mesh::setSize({{0, 1}}, 0.02); // 点1附近密集
设了一个不均匀尺寸(角点1处密集),这样在GUI中可以清楚看到:主面的密集网格被精确复制到了从面。
15.3.2 定义周期约束
// 平移向量: dx=1, dy=0, dz=0 (右面=左面向右平移1个单位)
std::vector<double> translation({
1, 0, 0, 1, // 第1行
0, 1, 0, 0, // 第2行
0, 0, 1, 0, // 第3行
0, 0, 0, 1 // 第4行
});
// 面2(右面)的网格从面1(左面)复制,施加平移translation
gmsh::model::mesh::setPeriodic(
2, // 维度: 2=面
{2}, // 从面标签列表(slave)
{1}, // 主面标签列表(master)
translation // 4x4仿射变换矩阵(16个元素,按行)
);
要点:
- 第1参数 2 表示施加于2D实体(面)上
- 第2参数是从面(slave),其上的网格将被复制
- 第3参数是主面(master),提供网格模板
- 从面网格 = T × 主面网格
// 多方向周期:上下(y方向) + 前后(z方向)
gmsh::model::mesh::setPeriodic(
2, {6}, {5},
{1,0,0,0, 0,1,0,1, 0,0,1,0, 0,0,0,1}); // dy=1
gmsh::model::mesh::setPeriodic(
2, {4}, {3},
{1,0,0,0, 0,1,0,0, 0,0,1,1, 0,0,0,1}); // dz=1
gmsh::model::mesh::generate(3);
15.3.3 复杂几何自动匹配周期面
对于复杂几何(如球体嵌入立方体),手动查找对应面标签很困难。可以用包围盒搜索+比对来自动匹配:
// 创建立方体 + 嵌入球体 → fragment
gmsh::model::occ::addBox(2, 0, 0, 1, 1, 1, 10);
// ... 添加多个球体 ...
gmsh::model::occ::fragment({{3, 10}}, spheres, out, out_map);
gmsh::model::occ::synchronize();
// 自动匹配左面和右面:
// 1. 找到所有左面(x≈2的面)
double eps = 1e-3;
std::vector<std::pair<int,int>> sxmin;
gmsh::model::getEntitiesInBoundingBox(
2-eps, -eps, -eps, 2+eps, 1+eps, 1+eps, sxmin, 2);
// 2. 对每个左面,获取其包围盒并向右平移1单位
for(auto i : sxmin) {
double xmin, ymin, zmin, xmax, ymax, zmax;
gmsh::model::getBoundingBox(i.first, i.second,
xmin, ymin, zmin, xmax, ymax, zmax);
// 3. 在平移后的包围盒内寻找匹配面
std::vector<std::pair<int,int>> sxmax;
gmsh::model::getEntitiesInBoundingBox(
xmin-eps+1, ymin-eps, zmin-eps,
xmax+eps+1, ymax+eps, zmax+eps, sxmax, 2);
// 4. 验证包围盒是否匹配
for(auto j : sxmax) {
double xmin2, ymin2, zmin2, xmax2, ymax2, zmax2;
gmsh::model::getBoundingBox(j.first, j.second,
xmin2, ymin2, zmin2, xmax2, ymax2, zmax2);
xmin2 -= 1; xmax2 -= 1; // 平移回左边比对
if(std::abs(xmin2-xmin) < eps && std::abs(xmax2-xmax) < eps &&
std::abs(ymin2-ymin) < eps && std::abs(ymax2-ymax) < eps &&
std::abs(zmin2-zmin) < eps && std::abs(zmax2-zmax) < eps) {
gmsh::model::mesh::setPeriodic(2, {j.second}, {i.second}, translation);
}
}
}
15.4 完整可运行代码
// ch15_periodic.cpp — 周期性网格
#include <set>
#include <gmsh.h>
int main(int argc, char **argv)
{
gmsh::initialize(argc, argv);
gmsh::model::add("ch15");
// 创建立方体
gmsh::model::occ::addBox(0, 0, 0, 1, 1, 1, 1);
gmsh::model::occ::synchronize();
// 非均匀网格尺寸(验证周期效果)
std::vector<std::pair<int,int>> out;
gmsh::model::getEntities(out, 0);
gmsh::model::mesh::setSize(out, 0.1);
gmsh::model::mesh::setSize({{0, 1}}, 0.02);
// x方向周期:右面(2) ← 复制左面(1) + 平移(1,0,0)
std::vector<double> T({
1, 0, 0, 1,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
});
gmsh::model::mesh::setPeriodic(2, {2}, {1}, T);
// y方向周期:前面(6) ← 复制后面(5) + 平移(0,1,0)
gmsh::model::mesh::setPeriodic(
2, {6}, {5},
{1,0,0,0, 0,1,0,1, 0,0,1,0, 0,0,0,1});
// z方向周期:顶面(4) ← 复制底面(3) + 平移(0,0,1)
gmsh::model::mesh::setPeriodic(
2, {4}, {3},
{1,0,0,0, 0,1,0,0, 0,0,1,1, 0,0,0,1});
gmsh::model::mesh::generate(3);
gmsh::write("ch15_periodic.msh");
std::set<std::string> args(argv, argv + argc);
if(!args.count("-nopopup")) gmsh::fltk::run();
gmsh::finalize();
return 0;
}
15.5 关键API速查表
| API | 说明 |
|---|---|
mesh::setPeriodic(dim, {slaveTags}, {masterTags}, {matrix}) |
设置周期约束。dim为实体维度;slave为从面标签;master为主面标签;matrix为16元素4x4仿射变换(按行) |
model::getEntitiesInBoundingBox(xmin,ymin,zmin,xmax,ymax,zmax,out,dim) |
获取包围盒内的实体,用于自动匹配周期面 |
model::getBoundingBox(dim,tag,...) |
获取单个实体的精确包围盒 |
occ::fragment({objects}, {tools}, out, outMap) |
共形布尔分片,用于复杂周期几何 |
15.6 注意事项
- 维度匹配:主从面必须是同一维度(都是2D面或都是1D线)
- 一对一:每个setPeriodic调用中,slave和master的标签数量应相同
- 变换方向:从面 = T(主面),即主面上的点通过T变换后与从面上的对应点重合
- 矩阵格式:16个double,按行排列(row-major order)
- 多周期可叠加:一个从面只能从属于一个周期约束;但不同面对可以使用不同周期
- OpenCASCADE:强烈建议使用occ内核做周期几何——手动构建匹配面对在geo内核下非常繁琐
- 验证方法:在GUI中放大到周期面边界,确认两侧节点完全重合