美文网首页
libmesh自适应例子ex1

libmesh自适应例子ex1

作者: 不想当社畜 | 来源:发表于2019-12-17 21:51 被阅读0次

libmesh中自适应求解的有限元例子

阅读libmesh官方自带的自适应的例子,主要是学习libmeshAPI的使用。

阅读example/adaptivity/ex1中的例子。分析如下:

求解问题

- \epsilon u''(x)+u(x)=1; x\in [0,1]\\ \begin{cases} u(0) = u(1) = 0;\\ \epsilon << 1; \end{cases}

其实不知道整个方程是如何离散化的,也就是说对应的形函数和高斯积分是如何确定的.

因为在力学上只是针对特定的PDE方程进行离散化,所以整个形函数的确定,高斯点的布置都流程都一模一样的,然后想确定PDE方程的离散化,是学习这类程序的关键,不仅仅只适用于固体的有限元程序。

代码阅读

把整个程序都会使用中文注释的方式,进行解释和说明。其实也有好多目前不太清楚的。相信了解的越多对框架的理解就越深。

// 使用自适应mesh对象进行问题求解 (求解一维问题偏微分方程问题 使用自适应单元类型)

//该问题是求解如下的一维问题:
// 问题的公式:
//  - \epsilon * u''(x) + u(x) =  1 ;  (0<= x <=1)
// 边界条件 u(0) = u(1) = 0

// 需要的头文件
// Libmesh includes
#include "libmesh/mesh.h"
#include "libmesh/mesh_generation.h"
#include "libmesh/edge_edge3.h"
#include "libmesh/gnuplot_io.h"
#include "libmesh/equation_systems.h"
#include "libmesh/linear_implicit_system.h"
#include "libmesh/fe.h"
#include "libmesh/getpot.h"
#include "libmesh/quadrature_gauss.h"
#include "libmesh/sparse_matrix.h"
#include "libmesh/dof_map.h"
#include "libmesh/numeric_vector.h"
#include "libmesh/dense_matrix.h"
#include "libmesh/dense_vector.h"
#include "libmesh/error_vector.h"
#include "libmesh/kelly_error_estimator.h"
#include "libmesh/mesh_refinement.h"
#include "libmesh/enum_solver_package.h"
// 自行添加的头文件 目的后面输出VTK格式的文件
#include "libmesh/vtk_io.h"

using namespace libMesh;

// 一维问题的整体单元的组装
void assemble_1D(EquationSystems & es,
                 const std::string & system_name);
// 主函数入口
int main(int argc, char ** argv)
{
  // 初始化libmesh 环境 其中包括MPI环境,petsc环境 等其他环境
  LibMeshInit init (argc, argv);
    // 这个例子是需要线性方程求解的,所以必须libMesh默认的求解包不是为空的就可以.(当在配置LIBEMSH使用了petsc时,默认使用的求解器包为petsc)
  libmesh_example_requires(libMesh::default_solver_package() != INVALID_SOLVER_PACKAGE,
                           "--enable-petsc, --enable-trilinos, or --enable-eigen");

  // 要求libmesh库支持自适应 (需要在配置libmesh的过程中指定,由于是自适应的程序例子 所以必须支持自适应)
#ifndef LIBMESH_ENABLE_AMR
  libmesh_example_requires(false, "--enable-amr");
#else
  
  // 其中有一个初始化 特别是并行计算时
  // 创建mesh对象 
  Mesh mesh(init.comm());

  GetPot command_line (argc, argv);// 解析命令行参数

  int n = 4;
  if (command_line.search(1, "-n"))// 查看命令行参数是否有-n 获得-n后面的值 不然默认n的值为4
    n = command_line.next(n);

  
  // 创建1d单元 在0-1范围之间创建N个单元 (单元类型为EDG3,二次型单元)
  MeshTools::Generation::build_line(mesh, n, 0., 1., EDGE3);
  // 自己添加的函数 查看mesh对象的基本属性
  // 使用一个进程运行程序 并且没有输入参数:  ./example-dbg  
  // 输入信息为:
  /*
  Mesh Information:
  elem_dimensions()={1}  # mesh对象的维度 1维
  spatial_dimension()=1  # mesh对象的空间维度 1维
  n_nodes()=9            # mesh 对象一共有 9个节点  4个单元每个单元3个节点
    n_local_nodes()=9    # mesh 对象中 当前进程 存放的节点 (由于只有一个进程)
  n_elem()=4             # mesh 对象中 总共的单元数 4 个  (参与计算一般都是使用激活的单元)
    n_local_elem()=4     # mesh 对象中 当前进程的单元数
    n_active_elem()=4    # mesh 对象中 激活的单元  (激活的单元不一定等于总共的单元)
  n_subdomains()=1       # mesh 对象中 总的子域个数 (这个干什么用 我也不知道)
  n_partitions()=1       # mesh 对象中 整个数据被切分成多少份 (分布式存放需要统计 (DistributeMesh))
  n_processors()=1       # mesh 对象中 整个程序运行的进程个数 
  n_threads()=1          # mesh 对象中 环境是否开启了多线程 线程的个数
  processor_id()=0       # mesh 对象中 输出当前信息 属于第几个进程id编号
  */
  mesh.print_info();

  //针对mesh对象定义一个方程系统 
  // 整个方程系统中,可以包括多个系统,而且这些系统可以随机被激活 ( 固体系统,温度场系统 流体场系统等)
  // 系统之间可以独立 也可以耦合
  EquationSystems equation_systems(mesh);
  
  // 在方程系统中,添加一个隐式线性求解的系统 (目的为了求解上述的方程) 并且给这个系统名称为'1D'
  LinearImplicitSystem & system = equation_systems.add_system
    <LinearImplicitSystem>("1D");

  // 上述创建了线性隐式系统 给系统添加一个新的变量 (只有有变量最后才能形成方程组)
  // ('U' 而且变量使用二阶的方程进行迭代  二阶什么之类的 目前没有搞懂)
  system.add_variable("u", SECOND);

  // 由于系统中,不同边界条件之类的情况,需要指定整体矩阵矩阵函数 同时边界条件的处理 (最终才能求解方程)
  system.attach_assemble_function(assemble_1D);

  // 定义自适应mesh对象 (参数为mesh对象)表示给该对象进行自适应
  MeshRefinement mesh_refinement(mesh);
  
  //这些参数决定了将被精炼和粗化的单元的比例。
  // 任何单元的最大误差的30%以内的任何单元都将被细化,
  // 并且任何单元上的最小误差的30%以内的任何元素都可能被粗化。 
  mesh_refinement.refine_fraction()  = 0.7;
  mesh_refinement.coarsen_fraction() = 0.3;
  // 保证所有单元都不会被细化5次
  mesh_refinement.max_h_level()      = 5;

  // 初始化方程系统 (当存在多个系统时,对里面每个系统都需要初始化)
  equation_systems.init();
  // 自己添加的函数 查看方程系统的基本信息
  // equation_systems.print_info();// 输出方程系统的基本信息

  // Refinement parameters 自适应网格参数  最大迭代步数为5次
  const unsigned int max_r_steps = 5; 

  // 开始自适应 每次根据误差决定哪些部分进行网格加密 一共求解5次
  for (unsigned int r_step=0; r_step<=max_r_steps; r_step++)
    {
      /// 求解名称为‘1D’的系统方程 中间应该会调用分配整体矩阵
      equation_systems.get_system("1D").solve();

      // 确保mesh对象在最后一次迭代的过程中,不会被细化. 因为当最后一次迭代的过程中,不需要进行网格加密了。
      if (r_step != max_r_steps)
        {
          // 误差检测向量
          ErrorVector error;// 该类包含各种统计方法的实现 实际上就是std::vector数据加上一些统计函数
          KellyErrorEstimator error_estimator;// 误差检测器

          // 这个误差向量的值 是什么值???
          error_estimator.estimate_error(system, error);

          // 输出误差向量的数据
          libMesh::out << "Error estimate\nl2 norm = "
                       << error.l2_norm()
                       << "\nmaximum = "
                       << error.maximum()
                       << std::endl;

          // 根据计算出来的误差向量 进行mesh对象的单元自适应
          mesh_refinement.flag_elements_by_error_fraction (error);

          // 对mesh对象进行粗化和细化
          mesh_refinement.refine_and_coarsen_elements();

          // 针对新的mesh 对象,方程系统需要重新进行初始化
          equation_systems.reinit();
        }
    }

  // 使用GnuPlot进行输出即可
  GnuPlotIO plot(mesh, "Adaptivity Example 1", GnuPlotIO::GRID_ON);

  plot.write_equation_systems("gnuplot_script", equation_systems);
  
  // 自己定义的VTK格式输出
  //VTKIO vtk(mesh);
  //vtk.set_compression(false);
  //vtk.write("out1put.pvtu");
#endif // #ifndef LIBMESH_ENABLE_AMR

  return 0;
}

// 定义一维问题的整体矩阵分配函数
void assemble_1D(EquationSystems & es,
                 const std::string & system_name)
{
  // Ignore unused parameter warnings when !LIBMESH_ENABLE_AMR.
  libmesh_ignore(es, system_name);

#ifdef LIBMESH_ENABLE_AMR

  // It is a good idea to check we are solving the correct system
  libmesh_assert_equal_to (system_name, "1D");

  // Get a reference to the mesh object
  const MeshBase & mesh = es.get_mesh();

  // The dimension we are using, i.e. dim==1 获得mesh对象的维度
  const unsigned int dim = mesh.mesh_dimension();

  // Get a reference to the system we are solving 获得方程对象的系统
  LinearImplicitSystem & system = es.get_system<LinearImplicitSystem>("1D");

  // Get a reference to the DofMap object for this system. The DofMap object
  // handles the index translation from node and element numbers to degree of
  // freedom numbers. DofMap's are discussed in more detail in future examples.
  // 获得这个系统中自由度管理的引用, DOfMap对象是用来处理从节点和单元编号到自由度编号的索引转换。DofMap将在以后的示例中进行更详细的讨论。
  const DofMap & dof_map = system.get_dof_map();

  // Get a constant reference to the Finite Element type for the first
  // (and only) variable in the system.
  // 获得这个系统中,第一个变量的有限元类型
  FEType fe_type = dof_map.variable_type(0);

  // Build a finite element object of the specified type. The build
  // function dynamically allocates memory so we use a std::unique_ptr in this case.
  // A std::unique_ptr is a pointer that cleans up after itself. See examples 3 and 4
  // for more details on std::unique_ptr.

  // 生成指定类型的有限元对象。build函数动态分配内存,因此在本例中使用std::unique_ptr。
  // 一个std::unique_ptr是一个指针,它在自身之后进行清理。有关std::unique\u ptr的更多详细信息,请参见示例3和4。
  std::unique_ptr<FEBase> fe(FEBase::build(dim, fe_type));

  // Tell the finite element object to use fifth order Gaussian quadrature
  // 告诉有限元对象使用五阶高斯求积分
  QGauss qrule(dim, FIFTH);
  fe->attach_quadrature_rule(&qrule);

  // 在这里,我们定义了一些对单元格特定数据的引用,这些数据将用于分配线性系统
  // Here we define some references to cell-specific data that will be used to
  // assemble the linear system.

  // The element Jacobian * quadrature weight at each integration point.
  //  单元雅克比*积分点上的权重(每个积分点)
  const std::vector<Real> & JxW = fe->get_JxW();

  // The element shape functions evaluated at the quadrature points.
  // 单元的形函数 在高斯点上的值
  const std::vector<std::vector<Real>> & phi = fe->get_phi();

  // The element shape function gradients evaluated at the quadrature points.
  // 单元形函数的梯度在高斯点上的值
  const std::vector<std::vector<RealGradient>> & dphi = fe->get_dphi();

  // Declare a dense matrix and dense vector to hold the element matrix
  // and right-hand-side contribution
  // 声明单元的刚度矩阵 和 右手向量
  DenseMatrix<Number> Ke;
  DenseVector<Number> Fe;

  // This vector will hold the degree of freedom indices for the element.
  // These define where in the global system the element degrees of freedom
  // get mapped.
  // 此向量将保留单元的自由度索引。这些定义了单元自由度在全局系统中的映射位置。
  std::vector<dof_id_type> dof_indices;// 自由度到全局之间的映射关系

  // 对每个进程而言,开始对所有本地单元进行遍历为了计算单元的刚度矩阵和右手项
  // We now loop over all the active elements in the mesh in order to calculate
  // the matrix and right-hand-side contribution from each element. Use a
  // const_element_iterator to loop over the elements. We make
  // el_end const as it is used only for the stopping condition of the loop.
  for (const auto & elem : mesh.active_local_element_ptr_range())
    {
      // 获取当前单元的自由度索引。这些定义了这个单元在全局矩阵和右手项。
      // Get the degree of freedom indices for the current element.
      // These define where in the global matrix and right-hand-side this
      // element will contribute to.
      dof_map.dof_indices(elem, dof_indices);// 获得单元的映射关系 (单元节点第一个节点 对应 整体矩阵的位置 )
      
      // 计算当前单元特定的单元数据,这其中包括计算当前单元积分点上和形函数的值

      // Compute the element-specific data for the current element. This
      // involves computing the location of the quadrature points (q_point)
      // and the shape functions (phi, dphi) for the current element.
      fe->reinit(elem);

      // Store the number of local degrees of freedom contained in this element
      //存储此单元中包含的局部自由度的个数
      const unsigned int n_dofs =
        cast_int<unsigned int>(dof_indices.size());
      libmesh_assert_equal_to (n_dofs, phi.size());

      // We resize and zero out Ke and Fe (resize() also clears the matrix and
      // vector). In this example, all elements in the mesh are EDGE3's, so
      // Ke will always be 3x3, and Fe will always be 3x1. If the mesh contained
      // different element types, then the size of Ke and Fe would change.
      Ke.resize(n_dofs, n_dofs);
      Fe.resize(n_dofs);


      // Now loop over quadrature points to handle numerical integration
      // 对单元的高斯积分点进行循环遍历
      for (unsigned int qp=0; qp<qrule.n_points(); qp++)
        {
          // Now build the element matrix and right-hand-side using loops to
          // integrate the test functions (i) against the trial functions (j).
          for (unsigned int i=0; i != n_dofs; i++)
            {
              Fe(i) += JxW[qp]*phi[i][qp];

              for (unsigned int j=0; j != n_dofs; j++)
                {
                  Ke(i,j) += JxW[qp]*(1.e-3*dphi[i][qp]*dphi[j][qp] +
                                      phi[i][qp]*phi[j][qp]);
                }
            }
        }
      // 此时我们已经完成了单元的刚度矩阵和右手项的高斯积分点上求和。最后一步是应用边界条件,在本例中是u(0)=u(1)=0的简单Dirichlet条件。

      // At this point we have completed the matrix and RHS summation. The
      // final step is to apply boundary conditions, which in this case are
      // simple Dirichlet conditions with u(0) = u(1) = 0.

      // Define the penalty parameter used to enforce the BC's
      double penalty = 1.e10;// 大数法进行处理固定边界条件

      // Loop over the sides of this element. For a 1D element, the "sides"
      // are defined as the nodes on each edge of the element, i.e. 1D elements
      // have 2 sides.
      // 循环单元的边界,对于1维单元而言,边界定义为每条边上的节点 1D单元有两个边界
      for (auto s : elem->side_index_range())
        {
          // If this element has a nullptr neighbor, then it is on the edge of the
          // mesh and we need to enforce a boundary condition using the penalty
          // method.
          // 如果这个单元有一个nullptr邻居,那么说明它就在网格的边缘,我们需要使用惩罚方法来强制一个边界条件。
          if (elem->neighbor_ptr(s) == nullptr)
            {
              Ke(s,s) += penalty;
              Fe(s)   += 0*penalty;
            }
        }

      // This is a function call that is necessary when using adaptive
      // mesh refinement. See Adaptivity Example 2 for more details.
      // 这是使用自适应网格细化时必需的函数调用。有关详细信息,请参见自适应示例2。
      dof_map.constrain_element_matrix_and_vector (Ke, Fe, dof_indices);

      // Add Ke and Fe to the global matrix and right-hand-side.
     //将单元刚度矩阵 和 右手项 组装到整体矩阵中去
      system.matrix->add_matrix(Ke, dof_indices);
      system.rhs->add_vector(Fe, dof_indices);
    }
#endif // #ifdef LIBMESH_ENABLE_AMR
}

该例子目前还有不清楚的问题有:

  1. 不知道一维整体矩阵函数分配和组装过程中,如何对该问题进行离散化的,因为这个不是传统的有限元问题,所有在离散后,不是简单的 B^TDB 进行计算而已。
  2. 装配函数具体调用发生在哪个过程中。

相关文章

  • libmesh自适应例子ex1

    libmesh中自适应求解的有限元例子 阅读libmesh官方自带的自适应的例子,主要是学习libmesh中API...

  • libmesh

    阅读libmesh中整个清单过程 整个文档主要是包括之后要做的事,总体要做的事。 最终要达到的效果. 目的说明 阅...

  • libmesh迭代器的实现和方法(1)

    libmesh 迭代器的实现和方法(1) 在阅读libmesh源码的过程中,会接触到两个类:Distributed...

  • iOS学习笔记(6)-自适应高度的Table View

    这篇笔记主要记录了完成一个自适应高度的TableView的例子。例子来自https://www.raywender...

  • flex自适应横向滚动区域

    效果如图 使用时只需要把scroll-view移动到需要自适应滚动撑满的地方就可以这是纵向例子:flex自适应滚动...

  • 控件自适应frame-根据字符串和字体大小

    ** 一.封装** 例子: ** 二.分类中封装** ** 三.部分可能会出现自适应高度后文字无法显示...

  • ex1

    嵌入式ex1 1、What are the most important extensions of the St...

  • ex1

    //说明文件夹来源 //导入类Scanner // public class所定义的类为共用类,可以被所有用户调...

  • Chapter 3 Strings, Vectors and A

    1 Ex1: For int ia[3][4], please print every value of the ...

  • 第二章-例题

    EX1 求12345 include int main(){int a=1;for(int ...

网友评论

      本文标题:libmesh自适应例子ex1

      本文链接:https://www.haomeiwen.com/subject/dowenctx.html