文章目录

  • 前言
  • 一、点,线段,单元,多边形等的定义
  • 1. 点的定义
  • 2. 多边形的定义
  • 3. 线段的定义
  • 4. 拓扑关系的定义
  • 5. 单元的定义
  • 二、区域构造
  • 三、调用Gmsh的API剖网格并得到网格信息
  • 四、程序运行结果
  • 1. 程序输出
  • 2. 带有点标号的结果图
  • 3. 带有单元标号的结果图
  • 五、总结



前言

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


一、点,线段,单元,多边形等的定义

因为我们需要自己构造区域内的几何信息,所以对点,线段,单元,多边形定义如下。特别需要注意因为我们在剖网格过程中借助了C++的set和map容器,所以需要对定义的点和拓扑关系的类重载<运算符。

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 Polygon
{
public:
    Polygon() : _vertex(), _name() {}
    Polygon(const vector<Point>& vertex, const string& name = "poly") :_vertex(vertex), _name(name) {}
    ~Polygon() {}
    const vector<Point>& getVertex() const {return _vertex;}
    void clear() {_vertex.clear();}
    void append(const Point& p) {_vertex.push_back(p);}
    const string& getName() const {return _name;}
    void setPoints(const vector<Point>& vertex) {_vertex = vertex;}
    void setName(const string& name) {_name = name;}
  
private:
    vector<Point> _vertex;
    string _name;
};

3. 线段的定义

class Segment
{
public:
    Segment() : _startPt(), _endPt() {}
    Segment(const Point& startPt, const Point& endPt) : _startPt(startPt), _endPt(endPt) {}
    ~Segment() {}
    const Point& getStartPt() const {return _startPt;}
    const Point& getEndPt() const {return _endPt;}
    void setStartPt(const Point& startPt) {_startPt = startPt;}
    void setEndPt(const Point& endPt) {_endPt = endPt;}
  
protected:
    Point _startPt, _endPt;
};

4. 拓扑关系的定义

class Topology
{
public:
    Topology(const int firstId = -3, const int secondId = -3) : _firstId(firstId), _secondId(secondId) {}
    ~Topology() {}
    const int getFirstId() const {return _firstId;}
    const int getSecondId() const {return _secondId;}
    void setFirstId(const int firstId) {_firstId = firstId;}
    void setSecondId(const int secondId) {_secondId= secondId;}
    bool operator < (const Topology& topo) const;
    
private:
    int _firstId, _secondId;
};

bool Topology::operator < (const Topology& topo) const
{
    int tof = topo.getFirstId();
    int tos = topo.getSecondId();
    if(tof == _firstId) {
        return tos < _secondId;
    } else {
        return tof < _firstId;
    }
}

5. 单元的定义

class Element : public Segment
{
public:
    Element() : Segment(), _topo() {}
    Element(const Point& startPt, const Point& endPt, const Topology& topo) : Segment(startPt, endPt), _topo(topo) {}
    ~Element() {}
    
    const Topology& getTopology() const {return _topo;}
    void setTopology(const Topology& topo) {_topo = topo;}
    void setTopology(const int firstId, const int secondId) {_topo = Topology(firstId, secondId);}
    
private:
    Topology _topo;
};

其中拓扑关系表示此单元是连接着哪两个多边形区域,当然也能位于边界。

二、区域构造

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

void generatePolygons(vector<Polygon>& region)
{
    Polygon poly;
    double left, right, bot, top;
    double x, y, z = 0;
    Point p;
    
    poly.clear();
    p = Point(0, 0, z);
    poly.append(p);
    p = Point(8, 0, z);
    poly.append(p);
    p = Point(8, 1, z);
    poly.append(p);
    p = Point(6, 1, z);
    poly.append(p);
    p = Point(5, 1, z);
    poly.append(p);
    p = Point(3, 1, z);
    poly.append(p);
    p = Point(2, 1, z);
    poly.append(p);
    p = Point(0, 1, z);
    poly.append(p);
    poly.setName("poly_1");
    region.push_back(poly);
    
    poly.clear();
    p = Point(0, 1, z);
    poly.append(p);
    p = Point(2, 1, z);
    poly.append(p);
    p = Point(2, 2, z);
    poly.append(p);
    p = Point(0, 2, z);
    poly.append(p);
    poly.setName("poly_2");
    region.push_back(poly);
    
    poly.clear();
    p = Point(3, 1, z);
    poly.append(p);
    p = Point(5, 1, z);
    poly.append(p);
    p = Point(5, 2, z);
    poly.append(p);
    p = Point(3, 2, z);
    poly.append(p);
    poly.setName("poly_3");
    region.push_back(poly);

    poly.clear();
    p = Point(6, 1, z);
    poly.append(p);
    p = Point(8, 1, z);
    poly.append(p);
    p = Point(8, 2, z);
    poly.append(p);
    p = Point(6, 2, z);
    poly.append(p);
    poly.setName("poly_4");
    region.push_back(poly);

    poly.clear();
    p = Point(0, 2, z);
    poly.append(p);
    p = Point(2, 2, z);
    poly.append(p);
    p = Point(3, 2, z);
    poly.append(p);
    p = Point(5, 2, z);
    poly.append(p);
    p = Point(6, 2, z);
    poly.append(p);
    p = Point(8, 2, z);
    poly.append(p);
    p = Point(8, 3, z);
    poly.append(p);
    p = Point(0, 3, z);
    poly.append(p);
    poly.setName("poly_5");
    region.push_back(poly);
}

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

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

int main(int argc, char **argv)
{
	clock_t start, finish;
	start = clock();

	gmsh::initialize();
    gmsh::option::setNumber("General.Terminal", 1);
    gmsh::model::add("demo");
    
    double lc = 0.8;
    vector<Polygon> region;
    generatePolygons(region);
    
    int dim = 1;
    int tag = 1;
    double x, y, z;
    vector<int> loop, curveloop, planeSurface;
    
    int ct_point = 1;
    int ct_line = 1;
    
    // record the points where the previous polygon was inserted
    map<Point, int> recPoints;
    // record the line where the previous polygon wsa inserted
    map<Topology, int> recLines;
    vector<vector<int> > domElelIds(region.size());
    for(int i = 0; i < region.size(); i++)
    {
        loop.clear();
        Polygon poly = region[i];
        const vector<Point>& vertex = poly.getVertex();
        for(int j = 0; j < vertex.size(); j++)
        {
            Point p = vertex[j];
            auto iter = recPoints.find(p);
            if(iter == recPoints.end())
            {
                x = p.getX();
                y = p.getY();
                z = p.getZ();
                gmsh::model::geo::addPoint(x, y, z, lc, ct_point);
                recPoints[p] = ct_point;
                loop.push_back(ct_point);
                ct_point++;
            } else {
                loop.push_back(iter->second);
            }
        }
        curveloop.clear();
        for(int j = 0; j < loop.size(); j++)
        {
            int l1 = loop[j];
            int l2 = j == loop.size()-1 ? loop[0] : loop[j+1];
            Topology topo(l1, l2);
            Topology topo2(l2, l1);
            auto iterl = recLines.find(topo);
            auto iterl2 = recLines.find(topo2);
            bool flag = (iterl == recLines.end()) && (iterl2 == recLines.end());
            if(flag)
            {
                gmsh::model::geo::addLine(l1, l2, ct_line);
                recLines[topo] = ct_line;
                recLines[topo2] = -ct_line;
                curveloop.push_back(ct_line);
                ct_line++;
            } else {
                if(iterl != recLines.end())
                    curveloop.push_back(iterl->second);
                else
                    curveloop.push_back(iterl2->second);
            }
        }
        planeSurface.push_back(gmsh::model::geo::addCurveLoop(curveloop));
        tag = gmsh::model::addPhysicalGroup(dim, curveloop);
        string name = "Polygon_" + to_string(i);
        gmsh::model::setPhysicalName(dim, tag, name);
        domElelIds[i] = curveloop;
    }
    
    gmsh::model::geo::addPlaneSurface(planeSurface);
    
    dim = 1;
    gmsh::model::addPhysicalGroup(dim, planeSurface);
    
    gmsh::model::geo::synchronize();
    
    gmsh::model::mesh::generate(1);

#if 1
    clock_t info_s, info_e;
    info_s = clock();
    // get Mesh information
    
    // get 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);
    }
    
    // get Elemetnts
    dim = 1;
    tag = -1;
    vector<int> elementTypes;
    vector<vector<size_t> > elemetTags, nodeTagss;
    gmsh::model::mesh::getElements(elementTypes, elemetTags, nodeTagss, dim, tag);
    cout<<"The number of elemnets: "<<nodeTagss[0].size()/2<<endl;
    Topology topo;
    Element ele;
    Point p1, p2;
    unsigned int id1, id2;
    for(int i = 0; i < nodeTagss[0].size(); i+=2)
    {
        id1 = nodeTagss[0][i] - 1;
        id2 = nodeTagss[0][i+1] - 1;
        p1 = nodes[id1];
        p2 = nodes[id2];
        ele = Element(p1, p2, topo);
    }
    
    // get polygon nodes id and it's coordinate information
    // coord is coordinate information (x1, y1, z1, .......)
    dim = 1;
    vector<size_t> bouNodesIds;
    // boundaryTag = tag = gmsh::model::addPhysicalGroup(dim, curveloop)
    // then we can get one polygon points id on edges
    int boundaryTag = 1;
    gmsh::model::mesh::getNodesForPhysicalGroup(dim, boundaryTag, bouNodesIds, coord);
    vector<unsigned int> boundaryNodesIds(bouNodesIds.size());
    // just because index from star 0
    for(int i = 0; i < bouNodesIds.size(); i++)
    {
        boundaryNodesIds[i] = bouNodesIds[i]-1;
        //cout<<boundaryNodesIds[i]<<endl;
    }
    
    // get polygon arround elemnt id
    // bcurveLoop is line's tag(id) of one polygon
    // then we can get elemnts id of one polygon
    vector<int> bcurveLoop = domElelIds[0];
    vector<int> boundaryElementsIds;
    dim = 1;
    int dim2 = 1;
    bool strict = true;
    double xx, yy, zz;
    vector<size_t> elemetTag;
    for(int i = 0; i < bcurveLoop.size(); i++)
    {
        tag = abs(bcurveLoop[i]);
        gmsh::model::mesh::getElements(elementTypes, elemetTags, 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, elemetTag, dim2, strict);
            for(int kk= 0; kk < elemetTag.size(); kk++)
            {
                boundaryElementsIds.push_back(elemetTag[kk]-1);
            }
        }
        if(bcurveLoop[i] < 0)
        {
            reverse(boundaryElementsIds.begin(), boundaryElementsIds.end());
        }
    }
    info_e = clock();
#endif
    gmsh::write("demo.msh");
    gmsh::finalize();
    finish = clock();
    cout<<"The run time of get information: "<<(double)(info_e - info_s)/CLOCKS_PER_SEC<<"s"<<endl;
    cout<<"The run time is: "<<(double)(finish - start)/CLOCKS_PER_SEC<<"s"<<endl;
    return 0;
}

四、程序运行结果

1. 程序输出

mesh_core_cython模块_c++


如上图所示,我们可以很直观的看到点和单元的数量,以及程序各部分运行的时间。

程序也会在对应目录下产生一个“demo.msh”文件,用图形界面的gmsh软件打开,就能看到带有点标号的图和带有单元标号的图。

2. 带有点标号的结果图

注:下图中标注的数字为点的编号,从1开始。

mesh_core_cython模块_mesh_core_cython模块_02

3. 带有单元标号的结果图

注:下图中标注的数字为单元的编号,从1开始。

mesh_core_cython模块_mesh_core_cython模块_03

注:设置看带有不同编号的方法:Gmsh的菜单栏 -> Tools -> Options -> Mesh -> Visibility下面有Node lables,1D element lables等。

五、总结

剖网格也只是前处理,关键也是收集到想要的网格信息,一般是点,单元,以及单元的拓扑关系,在程序中也都有写,且都有注释。可以在程序中加入cout打印输出到终端,再结合图形界面,学习了解各函数API的功能。