Gmsh剖二维网格教程附代码


前言

Gmsh的安装与入门教程可以去看我的前一篇文章有限元剖网格之Gmsh安装与使用入门。本篇文章主要介绍如何借助Gmsh的C++ API剖二维网格(即三角形)。


一、点,三角形的定义

利用Gmsh剖的二维网格,一般是为了后续有限元计算的需要,后面也会给出一篇有限元的文章。此处主要是为了Gmsh前期的输入,由点连线构成多边形,以及最后利用Gmsh得到网格后对三角形信息的存储。

1. 点的定义

class Point
{
public:
    Point(const double x = 0, const double y = 0, const double z = 0):_x(x), _y(y), _z(z) { }
    ~Point() { }
    const double getX() const {return _x;}
    const double getY() const {return _y;}
    const double getZ() const {return _z;}
    void setX(const double x) {_x = x;}
    void setY(const double y) {_y = y;}
    void setZ(const double z) {_z = z;}
    bool operator < (const Point& p) const;
    
protected:
    double _x, _y, _z;
};

bool Point::operator < (const Point& p) const
{
    double px = p.getX();
    double py = p.getY();
    if(px == _x) {
        return py < _y;
    } else {
        return px < _x;
    }
}

2. 三角形的定义

class Triangle
{
  /**
   * Triangle vertex index
   * _id[0] ~ _id[2] : three vertices index of triangle
   * _id[3] is the midpoint between _id[0] and _id[1]
   * _id[4] is the midpoint between _id[1] and _id[2]
   * _id[5] is the midpoint between _id[2] and _id[0]
   */
  public:
    Triangle() {}
    ~Triangle() {}
    const vector<unsigned int>& getIds() const {return _ids;}
    void clear() {_ids.clear();}
    void append(unsigned int id) {_ids.push_back(id);}
    void resize(const unsigned int size) {_ids.resize(size);}

  private:
    vector<unsigned int> _ids;
};

注释也注明了,容器前三个空间就存储了三角形三个顶点的索引,后面存储的是三条边的中点的索引,是为了有限元(Lagrange 二次元)计算方便存储的。

二、区域构造

在此我们可以自己构造一个由多个多边形组成的区域。如下是一个简单的case:

void generateBoudaryAndHoles(vector<Point>& boundary, vector<vector<Point> >& holes)
{
  double left, right, bot, top;
  double x, y, z;
  left = 0;
  right = 5;
  bot = 0;
  top = 4;
  z = 0;
  Point p;
  p = Point(left, bot, z);
  boundary.push_back(p);
  p = Point(right, bot, z);
  boundary.push_back(p);
  p = Point(right, top, z);
  boundary.push_back(p);
  p = Point(left, top, z);
  boundary.push_back(p);
  p = Point(left, (bot+top)/2.0, z);
  boundary.push_back(p);


  vector<Point> hole;
  left = 1;
  right = 2;
  bot = 1;
  top = 2;
  p = Point(left, bot, z);
  hole.push_back(p);
  p = Point(right, bot, z);
  hole.push_back(p);
  p = Point(right, top, z);
  hole.push_back(p);
  p = Point(left, top, z);
  hole.push_back(p);
  p = Point(left, (bot+top)/2.0, z);
  hole.push_back(p);
  holes.push_back(hole);


  hole.clear();
  left = 3;
  right = 4;
  bot = 1;
  top = 2;
  p = Point(left, bot, z);
  hole.push_back(p);
  p = Point(right, bot, z);
  hole.push_back(p);
  p = Point(right, top, z);
  hole.push_back(p);
  p = Point(left, top, z);
  hole.push_back(p);
  holes.push_back(hole);
}

其中boundary存储的是区域的边界点信息,holes存储的是内部所有洞的点的信息。
注意:所有多边形点的信息都是逆时针顺序的

三、调用Gmsh的API剖网格并得到网格信息

以下为main函数中的主要代码 :调用Gmsh的API。相应位置都有注释,获取所有点的信息,获取单元信息,获取边界上三角形信息,获取洞边上三角形信息。
需要注意:程序中我设置gmsh内点标号即下标是从1开始的,此处我把点和单元信息存储在vector容器中,下标则从0开始,所以在代码的相应位置有-1操作。

1. 剖网格部分代码

int main(int argc, char **argv)
{
  gmsh::initialize();
  gmsh::option::setNumber("General.Terminal", 1);
    
  gmsh::model::add("demo");

  double boundartLc = 1, holeLc = 0.4;
  vector<Point> boundary;
  vector<vector<Point> > holes;
  generateBoudaryAndHoles(boundary, holes);

  int dim = 1;
  int tag = 1;
  double x, y, z;
  vector<int> loop, curveLoop, planeSurface;

  int boundaryTag;
  for(int i = 0; i < boundary.size(); i++)
  {
    x = boundary[i].getX();
    y = boundary[i].getY();
    z = boundary[i].getZ();
    loop.push_back(gmsh::model::geo::addPoint(x, y, z, boundartLc));
  }

  for(int i = 0; i < loop.size(); i++)
  {
    curveLoop.push_back(gmsh::model::geo::addLine(loop[i], i == loop.size()-1 ? loop[0] : loop[i+1]));
  }
  planeSurface.push_back(gmsh::model::geo::addCurveLoop(curveLoop));
  boundaryTag = gmsh::model::addPhysicalGroup(dim, curveLoop);
  gmsh::model::setPhysicalName(dim, boundaryTag, "Boundary");

  vector<int> bcurveLoop = curveLoop;

  vector<vector<int> > recCurveLoop;
  vector<int> holesTags;
  for(int i = 0; i < holes.size(); i++)
  {
    loop.clear();
    curveLoop.clear();
    for(int j = 0; j < holes[i].size(); j++)
    {
      x = holes[i][j].getX();
      y = holes[i][j].getY();
      z = holes[i][j].getZ();
      loop.push_back(gmsh::model::geo::addPoint(x, y, z, holeLc));
    }

    for(int j = 0; j < loop.size(); j++)
    {
      curveLoop.push_back(gmsh::model::geo::addLine(loop[j], j == loop.size()-1 ? loop[0] : loop[j+1]));
    }
    planeSurface.push_back(-gmsh::model::geo::addCurveLoop(curveLoop));
    
    tag = gmsh::model::addPhysicalGroup(dim, curveLoop);
    holesTags.push_back(tag);
    string name = "hole" + to_string(i);
    gmsh::model::setPhysicalName(dim, tag, name);
    recCurveLoop.push_back(curveLoop);
  }

  gmsh::model::geo::addPlaneSurface(planeSurface);

  dim = 2;
  gmsh::model::addPhysicalGroup(dim, planeSurface);
  gmsh::model::geo::synchronize();
  gmsh::model::mesh::generate(2);
  gmsh::write("demo.msh");  
  gmsh::finalize();
  return 0;
}


2. 获取网格的所有点的代码

  // Nodes
  dim = -1;
  tag = -1;
  bool includeBoundary = false;
  vector<double> coord, parametricCoord;
  vector<size_t> nodeTags;
  gmsh::model::mesh::getNodes(nodeTags, coord, parametricCoord, dim, tag, includeBoundary, true);
  cout<<"The number of nodes: "<<coord.size()/3<<endl;
  vector<Point> nodes;
  nodes.reserve(1000);
  Point p;
  for(int i = 0; i < coord.size(); i+=3)
  {
    x = coord[i];
    y = coord[i+1];
    z = coord[i+2];
    p = Point(x, y, z);
    nodes.push_back(p);
  }
  

3. 获取网格的所有三角形的代码

  //Elements
  dim = -1;
  tag = -1;
  vector<int> elementTypes;
  vector<vector<size_t> > elementTags, nodeTagss;
  gmsh::model::mesh::getElements(elementTypes, elementTags, nodeTagss, dim, tag);
  cout<<"The number of elements: "<<nodeTagss[1].size()/3<<endl;
  // boundaryNodesId include holes boundary nodes id
  // vector<size_t> boundaryNodesId = elementTags[0];
  vector<Triangle> triangles;
  triangles.reserve(1000);
  unsigned int id1, id2, id3;
  for(int i = 0; i < nodeTagss[1].size(); i += 3)
  {
    Triangle tri;
    id1 = nodeTagss[1][i]-1;
    id2 = nodeTagss[1][i+1]-1;
    id3 = nodeTagss[1][i+2]-1;
    tri.append(id1);
    tri.append(id2);
    tri.append(id3);
    triangles.push_back(tri);
  }
  

4. 获取最外围边界边上和内部洞的边上的点的代码

 // get boundary nodes id and it's coordinate information
  // coord is coordinate information (x1, y1, z1, .....)
  dim = 1;
  vector<size_t> bouNodesIds;
  gmsh::model::mesh::getNodesForPhysicalGroup(dim, boundaryTag, bouNodesIds, coord);
  vector<unsigned int> boundaryNodesIds(bouNodesIds.size());
  // just because index from start 0
  for(int i = 0; i < bouNodesIds.size(); i++)
  {
    boundaryNodesIds[i] = bouNodesIds[i]-1;
  }

  // get holes nodes id and it's coordinate information
  vector<vector<size_t> > hosNodesIds(holesTags.size());
  vector<vector<unsigned int> > holesNodesIds(holesTags.size());
  for(int i = 0; i < holesTags.size(); i++)
  {
    gmsh::model::mesh::getNodesForPhysicalGroup(dim, holesTags[i], hosNodesIds[i], coord);
  }
  for(int i = 0; i < hosNodesIds.size(); i++)
  {
    holesNodesIds[i].resize(hosNodesIds[i].size());
    for(int j = 0; j < hosNodesIds[i].size(); j++)
    {
      holesNodesIds[i][j] = hosNodesIds[i][j]-1;
    }
  }

#if 0
  for(int i = 0; i < holesNodesIds.size(); i++)
  {
    for(int j = 0; j < holesNodesIds[i].size(); j++)
    {
      cout<<holesNodesIds[i][j]<<" ";
    }
    cout<<endl;
  }
#endif


5. 获取内部洞的边上的三角形的代码

  // get holes arround triangle id
  vector<vector<int> > holesElementsIds(recCurveLoop.size());
  dim = 1;
  int dim2 = 2;
  vector<size_t> elementTag;
  bool strict = true;
  double xx, yy, zz;
  vector<int> holeElementsIds;
  for(int i = 0; i < recCurveLoop.size(); i++)
  {
    holeElementsIds.clear();
    for(int j = 0; j < recCurveLoop[i].size(); j++)
    {
      //elementTypes.clear();
      //elementTags.clear();
      //nodeTagss.clear();
      tag = recCurveLoop[i][j];
      gmsh::model::mesh::getElements(elementTypes, elementTags, nodeTagss, dim, tag);
      for(int k = 0; k < nodeTagss[0].size(); k += 2)
      {
        id1 = nodeTagss[0][k]-1;
        id2 = nodeTagss[0][k+1]-1;
        xx = (nodes[id1].getX() + nodes[id2].getX())/2.0;
        yy = (nodes[id1].getY() + nodes[id2].getY())/2.0;
        zz = (nodes[id1].getZ() + nodes[id2].getZ())/2.0;
        gmsh::model::mesh::getElementsByCoordinates(xx, yy, zz, elementTag, dim2, strict);
        for(int kk = 0; kk < elementTag.size(); kk++)
        {
          holeElementsIds.push_back(elementTag[kk]-1);
        }
      }
    }
    holesElementsIds.push_back(holeElementsIds);
  }
#if 0
  for(int i = 0; i < holesElementsIds.size(); i++)
  {
    for(int j = 0; j < holesElementsIds[i].size(); j++)
    {
      cout<<holesElementsIds[i][j]<<" ";
    }
    cout<<endl;
  }
#endif


注:这里指的三角形是,三角形的有一条边和内部洞的边重合(即三角形有两个顶点落于洞的边上)。

6. 获取最外围的边上的三角形的代码

  // get boundary arround triangle id
  vector<int> boundaryElementsIds;
  dim = 1;
  dim2 = 2;
  strict = true;
  for(int i = 0; i < bcurveLoop.size(); i++)
  {
    tag = bcurveLoop [i];
    gmsh::model::mesh::getElements(elementTypes, elementTags, nodeTagss, dim, tag);
    for(int k = 0; k < nodeTagss[0].size(); k += 2)
    {
      id1 = nodeTagss[0][k]-1;
      id2 = nodeTagss[0][k+1]-1;
      xx = (nodes[id1].getX() + nodes[id2].getX())/2.0;
      yy = (nodes[id1].getY() + nodes[id2].getY())/2.0;
      zz = (nodes[id1].getZ() + nodes[id2].getZ())/2.0;
      gmsh::model::mesh::getElementsByCoordinates(xx, yy, zz, elementTag, dim2, strict);
      for(int kk = 0; kk < elementTag.size(); kk++)
      {
        boundaryElementsIds.push_back(elementTag[kk]-1);
      }
    }
  }


注:这里指的三角形是,三角形的有一条边和最外围的边重合(即三角形有两个顶点落于最外围的边上)。

四、程序运行结果

1. 程序输出

Terminal
如上图所示,我们可以很直观的看到点和单元三角形的数量,共有95个点,149个三角形。其中gmsh的info的log信息提到的206个单元是因为他把一维的单元(线段)也加上了。
程序也会在对应目录下产生一个“demo.msh”文件,用图形界面的gmsh软件打开,就能看到带有点标号的图。

2. 带有点标号的结果图

下图中标注的数字为点的编号,从1开始。
node
因为程序中设置了洞周边三角形尺寸比边界设置的小,所以得到的网格也是在洞的附近网格比较密。

3. 没有点标号的结果图

在这里插入图片描述
***注:关于效果图是否有点单元编号以及阴影填充效果,相关设置可以看我Gmsh剖一维网格教程附代码这篇文章,里面有介绍,都是在软件上方的菜单栏中。

五、总结

剖网格也只是前处理,关键也是收集到想要的网格信息,给后面的有限元引擎进行计算。一般是点,单元,以及相应的边界条件信息,在程序中也都有写,且都有注释。可以在程序中加入cout打印输出到终端,再结合图形界面,学习了解各函数API的功能。

以上涉及的完整代码可以去我的GitHub网站下载查看。后续会整理用Gmsh剖三维四面体网格的教程,欢迎大家一起讨论学习交流。

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值