[gmsh source code reading] Edge meshing process

1. The role of edge division

In finite element analysis, beams and trusses are one-dimensional units divided by lines. At the same time, line meshing is also the basis for surface meshing. Before dividing a face, its boundaries need to be divided first. As shown in the figure below, before meshing a rectangular plate, it is necessary to divide the four boundaries and generate several nodes. Triangular or quadrilateral elements are generated based on these nodes.

p=p(u)=[x(u), y(u), z(u)], u\in [0, 1]

2. Differential geometry: Curve theory

Before reading the gmsh edge meshing code, you need to understand some knowledge of differential geometry. In differential geometry, curves are expressed as parametric equations: 

p=p(u)=[x(u), y(u), z(u)] , u\in[0, 1]

First-order guide:\dot{p}=\frac{dp}{du}=[\frac{dx(u))}{du},\frac{dy(u))}{du}, \frac{dz(u)) }{you} ]

Arc length of curve:s(u)=\int_{u{0}}^{u}\left | \dot{p}(u))\right |du

We need to understand that a curve in differential geometry is generally represented by a parametric equation. The parameters have a value range, 0 represents the starting point of the curve, and 1 represents the end point. The curve can be differentiated to obtain the n-order derivative, and the arc length can be obtained by integrating the module of the first-order derivative. Understanding these concepts can basically understand the gmsh code.

3. gmsh curve division source code

The code for gmsh dividing curves is implemented in the file meshGEdge.cpp, and its entry is the void meshGEdge::operator()(GEdge *ge) function.

void meshGEdge::operator()(GEdge *ge)
{

  ge->model()->setCurrentMeshEntity(ge); //设置当前mesh的对象

  if(ge->degenerate(1)) { // 如果是退化边,直接标记完成
    ge->meshStatistics.status = GEdge::DONE;
    return;
  }

  // compute bounds
  Range<double> bounds = ge->parBounds(0); // 范围边的uv坐标范围0-1
  double t_begin = bounds.low(); // 0
  double t_end = bounds.high();  // 1

  // if a BL is ending at one of the ends, then create specific points
  std::vector<MVertex *> _addBegin, _addEnd;

  // integration to get length, number of points, etc
  int N;
  std::vector<IntPoint> Points;
  double a;
  int filterMinimumN;
  meshGEdgeProcessing(ge, t_begin, t_end, N, Points, a, filterMinimumN); // 计算需要划分的点数量N

  // look if we are doing the STL triangulation
  std::vector<MVertex *> &mesh_vertices = ge->mesh_vertices;

  // 如果不存在起始点就退出
  GPoint beg_p, end_p;
  if(!ge->getBeginVertex() || !ge->getEndVertex()) {
    Msg::Warning("Skipping curve with no begin or end point");
    return;
  }
  // 获取曲线的起点、终点
  else if(ge->getBeginVertex() == ge->getEndVertex() &&
          ge->getBeginVertex()->edges().size() == 1) {
    end_p = beg_p = ge->point(t_begin);
    Msg::Debug("Meshing periodic closed curve (tag %i, %i points)", ge->tag(),
               N);
  }
  else {
    // 多数情况下曲线有两个端点
    MVertex *v0 = ge->getBeginVertex()->mesh_vertices[0];
    MVertex *v1 = ge->getEndVertex()->mesh_vertices[0];
    beg_p = GPoint(v0->x(), v0->y(), v0->z());
    end_p = GPoint(v1->x(), v1->y(), v1->z());
  }

  // do not consider the first and the last vertex (those are not
  // classified on this mesh edge)
  if(N > 1) {
    const double b = a / static_cast<double>(N - 1);
    int count = 1, NUMP = 1;
    IntPoint P1, P2;
    mesh_vertices.resize(N - 2); // 内部点的数量是N-2

      // 通过插值方式插入中间节点
    while(NUMP < N - 1) {
      P1 = Points[count - 1];
      P2 = Points[count];
      const double d = (double)NUMP * b;
      if((std::abs(P2.p) >= std::abs(d)) && (std::abs(P1.p) < std::abs(d))) {
        double const dt = P2.t - P1.t;
        double const dlc = P2.lc - P1.lc;
        double const dp = P2.p - P1.p;
        double const t = P1.t + dt / dp * (d - P1.p);
        SVector3 der = ge->firstDer(t);
        const double d = norm(der);
        double lc = d / (P1.lc + dlc / dp * (d - P1.p));
        GPoint V = ge->point(t);
        // 新生成的点保存下来
        mesh_vertices[NUMP - 1] =
          new MEdgeVertex(V.x(), V.y(), V.z(), ge, t, 0, lc);
        NUMP++;
      }
      else {
        count++;
      }
    }
    mesh_vertices.resize(NUMP - 1);
  }


  if(CTX::instance()->mesh.algo2d != ALGO_2D_BAMG &&
     CTX::instance()->mesh.algo2d != ALGO_2D_QUAD_QUASI_STRUCT &&
     CTX::instance()->mesh.algo2d != ALGO_2D_PACK_PRLGRMS)
    if(_addBegin.empty() && _addEnd.empty())
      filterPoints(ge, filterMinimumN - 2);

  std::vector<MLine *> &lines = ge->lines;

  // 将新生成的边保存下来
  for(std::size_t i = 0; i <= mesh_vertices.size(); i++) {
    MVertex *v0 =
      (i == 0) ? ge->getBeginVertex()->mesh_vertices[0] : mesh_vertices[i - 1];

    MVertex *v1 = (i == mesh_vertices.size()) ?
                    ge->getEndVertex()->mesh_vertices[0] :
                    mesh_vertices[i];
    lines.push_back(new MLine(v0, v1));
  }

  if(ge->getBeginVertex() == ge->getEndVertex() &&
     ge->getBeginVertex()->edges().size() == 1) {
    MVertex *v0 = ge->getBeginVertex()->mesh_vertices[0];
    v0->x() = beg_p.x();
    v0->y() = beg_p.y();
    v0->z() = beg_p.z();
  }

  Msg::Info("Meshing curve %d (%s): %li interior vertices", ge->tag(),
             ge->getTypeString().c_str(), ge->mesh_vertices.size());

  // 标记划分结束
  ge->meshStatistics.status = GEdge::DONE;
}

The process is to obtain the length of the line and the number of internal nodes through integration, and generate internal nodes through interpolation. The length of the curve and the number of internal points are both numerical integrations performed through the Integration function. The principle is that the recursive line is used instead of the curve, which approximates the real curve. The minimum depth of the recursion is 7 layers. Even a straight line must use 129 points. calculate. After obtaining the number of points to be inserted, linear interpolation is performed to generate internal nodes.

Guess you like

Origin blog.csdn.net/loveoobaby/article/details/131158922