[lectura del código fuente de gmsh] Proceso de mallado de bordes

1. El papel de la división de bordes

En el análisis de elementos finitos, las vigas y las armaduras son unidades unidimensionales divididas por líneas. Al mismo tiempo, el mallado de líneas es también la base del mallado de superficies. Antes de dividir una cara, primero se deben dividir sus límites. Como se muestra en la figura siguiente, antes de mallar una placa rectangular, es necesario dividir los cuatro límites y generar varios nodos, a partir de estos nodos se generan elementos triangulares o cuadriláteros.

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

2. Geometría diferencial: teoría de curvas

Antes de leer el código de mallado de bordes de gmsh, es necesario tener algunos conocimientos de geometría diferencial. En geometría diferencial, las curvas se expresan como ecuaciones paramétricas: 

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

Guía de primer orden:\dot{p}=\frac{dp}{du}=[\frac{dx(u))}{du},\frac{dy(u))}{du}, \frac{dz(u)) }{tú} ]

Longitud del arco de la curva:s(u)=\int_{u{0}}^{u}\izquierda |  \dot{p}(u))\right |du

Necesitamos entender que una curva en geometría diferencial generalmente está representada por una ecuación paramétrica, los parámetros tienen un rango de valores, 0 representa el punto inicial de la curva y 1 representa el punto final. La curva se puede diferenciar para obtener la derivada de orden n y la longitud del arco se puede obtener integrando el módulo de la derivada de primer orden. Comprender estos conceptos básicamente puede comprender el código gmsh.

3. código fuente de división de curva gmsh

El código para las curvas divisorias gmsh se implementa en el archivo meshGEdge.cpp, y su entrada es la función 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;
}

El proceso consiste en obtener la longitud de la línea y el número de nodos internos mediante integración, y generar nodos internos mediante interpolación. La longitud de la curva y el número de puntos internos son integraciones numéricas realizadas a través de la función de integración. El principio es que se utiliza la línea recursiva en lugar de la curva, que se aproxima a la curva real. La profundidad mínima de la recursión es de 7 capas. Incluso una línea recta debe utilizar 129 puntos. Luego de obtener el número de puntos a insertar, se realiza una interpolación lineal para generar nodos internos.

Supongo que te gusta

Origin blog.csdn.net/loveoobaby/article/details/131158922
Recomendado
Clasificación