【gmshソースコード読み込み】エッジメッシュ処理

1. エッジ分割の役割

有限要素解析では、梁とトラスは線で分割された 1 次元の単位であると同時に、線メッシュは面メッシュの基礎でもあります。面を分割する前に、まずその境界を分割する必要があります。下図に示すように、長方形の板をメッシュする前に、4 つの境界を分割していくつかのノードを生成する必要があり、これらのノードを基に三角形または四角形の要素が生成されます。

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

2. 微分幾何学: 曲線理論

gmsh エッジ メッシュ コードを読む前に、微分幾何学に関する知識を理解しておく必要があります。微分幾何学では、曲線はパラメトリック方程式として表現されます。 

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

最初のガイド:\dot{p}=\frac{dp}{du}=[\frac{dx(u))}{du},\frac{dy(u))}{du}, \frac{dz(u)) }{あなた} ]

曲線の円弧の長さ:s(u)=\int_{u{0}}^{u}\left |  \dot{p}(u))\right |du

微分幾何学の曲線は一般にパラメトリック方程式で表されることを理解する必要があります。パラメータには値の範囲があり、0 は曲線の始点を表し、1 は終点を表します。曲線を微分してn次微分を求めることができ、1次微分のモジュールを積分することで円弧長を求めることができます。これらの概念を理解すると、基本的に gmsh コードを理解できます。

3. gmsh 曲線分割ソースコード

gmsh 分割曲線のコードはファイル MeshGEdge.cpp に実装されており、そのエントリは void MeshGEdge::operator()(GEdge *ge) 関数です。

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;
}

積分により線分の長さと内部ノードの数を求め、補間により内部ノードを生成する処理です。曲線の長さと内部点の数はどちらも積分関数によって数値積分されます。原則として、曲線の代わりに再帰直線が使用され、実際の曲線に近似します。再帰の最小深さは 7 層です。 . 直線でも 129 個の点を使用する必要があります。計算します。挿入する点の数を取得した後、線形補間を実行して内部ノードを生成します。

おすすめ

転載: blog.csdn.net/loveoobaby/article/details/131158922