Skip to content

第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 注意事项

  1. 维度匹配:主从面必须是同一维度(都是2D面或都是1D线)
  2. 一对一:每个setPeriodic调用中,slave和master的标签数量应相同
  3. 变换方向:从面 = T(主面),即主面上的点通过T变换后与从面上的对应点重合
  4. 矩阵格式:16个double,按行排列(row-major order)
  5. 多周期可叠加:一个从面只能从属于一个周期约束;但不同面对可以使用不同周期
  6. OpenCASCADE:强烈建议使用occ内核做周期几何——手动构建匹配面对在geo内核下非常繁琐
  7. 验证方法:在GUI中放大到周期面边界,确认两侧节点完全重合