AFEPack 使用 Tutorial(三):解三维泊松方程

AFEPack Step by Step Tutorial 3: solve 3D Poisson Equation

这个 Tutorial 和前面两个不同。前面两个 Tutorial 都是 AFEPack 自带的例子,这个 Tutorial 可以认为是在 2D Poisson Equation 的基础上改进,将原来的 2D 方程变成 3D。

3D Poisson Equation

这里,三维泊松方程定义
{ − Δ u ( x , y , z ) = f ( x , y , z ) , ( x , y , z ) ∈ Ω u ( x , y , z ) ∣ ∂ Ω = u 0 ( x , y , z ) , ( x , y , z ) ∈ ∂ Ω \left\{\begin{matrix} -\Delta u(x,y,z) = f(x,y,z), \quad (x,y,z) \in \Omega\\ u(x,y,z) |_{\partial \Omega} = u_{0}(x,y,z), \quad (x,y,z) \in \partial \Omega \end{matrix}\right. { Δu(x,y,z)=f(x,y,z),(x,y,z)Ωu(x,y,z)Ω=u0(x,y,z),(x,y,z)Ω
其中,方程的左端项为 u ( x , y , z ) = s i n ( π x ) s i n ( 2 π y ) s i n ( π z ) u(x,y,z)=sin(\pi x)sin(2 \pi y)sin(\pi z) u(x,y,z)=sin(πx)sin(2πy)sin(πz),方程的右端项为 f ( x , y , z ) = 6 π 2 u ( x , y , z ) f(x,y,z)=6 \pi^{2} u(x,y,z) f(x,y,z)=6π2u(x,y,z),计算区域 Ω = [ 0 , 1 ] × [ 0 , 1 ] × [ 0 , 1 ] \Omega = [0,1] \times [0,1] \times [0,1] Ω=[0,1]×[0,1]×[0,1],边界条件 ∂ Ω \partial \Omega Ω 可以由解析解给出。

代码实现

常量定义

这里我们需要定义维度、 π \pi π

#define DIM (3)
#define PI (4.0*atan(1.0))

Step 1: 网格文件

头文件

#include    /// step 1: Read the mesh
#include   /// step 1: Read the mesh

C++实现

	//初始化
	HGeometryTree<DIM> htree;
	htree.readMesh("D.mesh");  /// it will find D.n, D.s, D.e

	/// 加密网格
	int refineRound = 5;
	IrregularMesh<DIM> ir_mesh; 
	ir_mesh.reinit(htree);
	ir_mesh.globalRefine(refineRound);
	ir_mesh.semiregularize();
	ir_mesh.regularize(false);
	Mesh<DIM,DIM> mesh = ir_mesh.regularMesh();

Step 2: 构建模板单元

我们以三维四面体线性有限单元为例,展示构造有限单元需要的代码和步骤。事实上所有的模板单元信息都保存在文本信息中,我们只需要对信息做初始化以及读入相对应文件。
Geometry Information 几何信息包括四面体的形状、面积、数值积分信息。
Coordinate Transformation 坐标变换包含模板单元和真实单元之间的映射信息。
Degree of Freedom 几何上的自由度信息。
Basis Function 基函数的信息。
注:线性元改成二次元只需要修改degree的值

	/// 选择单元次数:线性元degree = 1; 二次元degree = 2
	int degree =1;

	/// 构造模板单元
	std::stringstream tmp_dof, bas_fun;
	tmp_dof << "tetrahedron/tetrahedron." << degree << ".tmp_dof"; /// 1 stands for piecewise linear polynomial
	bas_fun << "tetrahedron/tetrahedron." << degree << ".bas_fun"; 

	TemplateGeometry<DIM>   triangle_template_geometry;
	triangle_template_geometry.readData("tetrahedron/tetrahedron.tmp_geo");
  
	CoordTransform<DIM,DIM>   triangle_coord_transform;
	triangle_coord_transform.readData("tetrahedron/tetrahedron.crd_trs");
  
	TemplateDOF<DIM>        triangle_template_dof(triangle_template_geometry);
	triangle_template_dof.readData(tmp_dof.str());
  
	BasisFunctionAdmin<double,DIM,DIM> triangle_basis_function(triangle_template_dof);
	triangle_basis_function.readData(bas_fun.str());
 
	std::vector<TemplateElement<double,DIM,DIM> > template_element(1);
	template_element[0].reinit(triangle_template_geometry,
                             triangle_template_dof,
                             triangle_coord_transform,
                             triangle_basis_function);

注意:这里的代码和二维方程是不一样的,我们要选择三维对应的模板单元。

Step 3: 构建有限元空间

有了网格数据和模板单元我们就可以构造有限元空间。事实上实现起来很简单,只需要对每一个几何单元指定一个模板单元。这里我们只考虑线性(或二次)有限单元,因此一个循环即可。

	/// 构造有限单元
	FEMSpace<double,DIM> fem_space(mesh, template_element);

	int n_element = mesh.n_geometry(DIM);
	fem_space.element().resize(n_element);
	for (int i = 0;i < n_element;i ++)
	fem_space.element(i).reinit(fem_space,i,0);

	fem_space.buildElement();
	fem_space.buildDof();
	fem_space.buildDofBoundaryMark();

Step 4: 构建刚度矩阵

在有限元空间上构造稀疏矩阵。这里的稀疏矩阵其实就是刚度矩阵 A A A,它的每个元素的值由下面积分给出
A i , j = ∫ Ω ∇ ψ j ⋅ ∇ ψ i d x , x ∈ Ω , i , j = 1 , … , N A_{i,j} = \int_{\Omega} \nabla \psi_j \cdot \nabla \psi_i dx, \quad x\in \Omega, i,j = 1,\ldots,N Ai,j=Ωψjψidx,xΩ,i,j=1,,N
实现中上述值是以数值积分的方式求出。数值积分的精度设置为2。

	/// 构造稀疏矩阵
	StiffMatrix<DIM,double> stiff_matrix(fem_space);
	stiff_matrix.algebricAccuracy() = 2;
	stiff_matrix.build();

Step 5: 解拉普拉斯方程

构造右端项

根据定义,RHS 为 f ( x , y ) = 6 π 2 sin ⁡ ( π x ) sin ⁡ ( 2 π y ) sin ⁡ ( π z ) f(x,y) = 6\pi^2\sin(\pi x) \sin(2\pi y) \sin(\pi z) f(x,y)=6π2sin(πx)sin(2πy)sin(πz)

/// 右端项函数
double f ( const double * p) 
{
     
      return 5*PI*PI*sin(1*PI*p[0]) * sin(2*PI*p[1])*sin(PI*p[2]);
}

右端项离散化
b i = ∫ Ω f ψ i , i = 1 , … , N b_i = \int_{\Omega} f \psi_i, \quad i = 1,\ldots,N bi=Ωfψi,i=1,,N

	/// 构造右端项
	FEMFunction<double,DIM> solution(fem_space);
	Vector<double> right_hand_side;

添加边界条件

根据定义,边界条件 ∂ Ω \partial \Omega Ω u ( x , y ) = sin ⁡ ( π x ) sin ⁡ ( 2 π y ) sin ⁡ ( π z ) u(x,y) = \sin (\pi x) \sin (2\pi y) \sin (\pi z) u(x,y)=sin(πx)sin(2πy)sin(πz)

/// 边界条件函数
double u ( const double * p) 
{
     
  return sin(PI*p[0]) * sin(2*PI*p[1])*sin (PI*p[2]);
}

下面我们再添加边界条件。这里,我们使用迪氏边界。

	/// 添加边界条件  
	BoundaryFunction<double,DIM> boundary(BoundaryConditionInfo::DIRICHLET, 1, &u);
	BoundaryConditionAdmin<double,DIM> boundary_admin(fem_space);
	boundary_admin.add(boundary);
	boundary_admin.apply(stiff_matrix, solution, right_hand_side);

设计线性求解系统 A x = b Ax=b Ax=b

我们还是使用 AMG 求解器。AMG 参数设置为 t o l = 1.0 e − 8 tol=1.0e^{-8} tol=1.0e8,最大迭代步数 200 200 200

	/// AMG 求解 
	AMGSolver solver(stiff_matrix);
	solver.solve(solution, right_hand_side, 1.0e-08, 200); 

Step 6: 输出结果

AFEPack 使用 OpenDX 数据格式。

	/// 结果输出
	// 输出 OpenDX 格式数据
	solution.writeOpenDXData("u.dx");
  
	// 计算 L2 错误
	double error = Functional::L2Error(solution, FunctionFunction<double>(&u), 3);
	std::cerr << "\nL2 error = " << error << std::endl;

这样,我们就完成了三维泊松方程的求解。

完整代码


// 3D Poisson Equation.cpp :
//

#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define PI (4.0*atan(1.0))
#define DIM (3)

double u(const double *);
double f(const double *);

int main(int argc, char * argv[]) {
     
	//初始化
	HGeometryTree<DIM> htree;
	htree.readMesh("D.mesh");  /// it will find D.n, D.s, D.e

	/// 加密网格
	int refineRound = 5;
	IrregularMesh<DIM> ir_mesh; 
	ir_mesh.reinit(htree);
	ir_mesh.globalRefine(refineRound);
	ir_mesh.semiregularize();
	ir_mesh.regularize(false);
	Mesh<DIM,DIM> mesh = ir_mesh.regularMesh();

	/// 选择单元次数:线性元degree = 1; 二次元degree = 2
	int degree =1;

	/// 构造模板单元
	std::stringstream tmp_dof, bas_fun;
	tmp_dof << "tetrahedron/tetrahedron." << degree << ".tmp_dof"; /// 1 stands for piecewise linear polynomial
	bas_fun << "tetrahedron/tetrahedron." << degree << ".bas_fun"; 

	TemplateGeometry<DIM>   triangle_template_geometry;
	triangle_template_geometry.readData("tetrahedron/tetrahedron.tmp_geo");
  
	CoordTransform<DIM,DIM>   triangle_coord_transform;
	triangle_coord_transform.readData("tetrahedron/tetrahedron.crd_trs");
  
	TemplateDOF<DIM>        triangle_template_dof(triangle_template_geometry);
	triangle_template_dof.readData(tmp_dof.str());
  
	BasisFunctionAdmin<double,DIM,DIM> triangle_basis_function(triangle_template_dof);
	triangle_basis_function.readData(bas_fun.str());
 
	std::vector<TemplateElement<double,DIM,DIM> > template_element(1);
	template_element[0].reinit(triangle_template_geometry,
                             triangle_template_dof,
                             triangle_coord_transform,
                             triangle_basis_function);  

	/// 构造有限单元
	FEMSpace<double,DIM> fem_space(mesh, template_element);

	int n_element = mesh.n_geometry(DIM);
	fem_space.element().resize(n_element);
	for (int i = 0;i < n_element;i ++)
	fem_space.element(i).reinit(fem_space,i,0);

	fem_space.buildElement();
	fem_space.buildDof();
	fem_space.buildDofBoundaryMark();

	/// 构造稀疏矩阵
	StiffMatrix<DIM,double> stiff_matrix(fem_space);
	stiff_matrix.algebricAccuracy() = 2;
	stiff_matrix.build();

	/// 构造右端项
	FEMFunction<double,DIM> solution(fem_space);
	Vector<double> right_hand_side;
	Operator::L2Discretize(&f, fem_space, right_hand_side, 2);

	/// 添加边界条件  
	BoundaryFunction<double,DIM> boundary(BoundaryConditionInfo::DIRICHLET, 1, &u);
	BoundaryConditionAdmin<double,DIM> boundary_admin(fem_space);
	boundary_admin.add(boundary);
	boundary_admin.apply(stiff_matrix, solution, right_hand_side);
	
	/// AMG 求解 
	AMGSolver solver(stiff_matrix);
	solver.solve(solution, right_hand_side, 1.0e-08, 200); 

	/// 结果输出
	// 输出 OpenDX 格式数据
	solution.writeOpenDXData("u.dx");
	
	// 计算 L2 错误
	double error = Functional::L2Error(solution, FunctionFunction<double>(&u), 3);
	std::cerr << "\nL2 error = " << error << std::endl;

  return 0;
};

double u(const double * p)
{
     
	return sin(PI*p[0]) * sin(2*PI*p[1])*sin (PI*p[2]);
};

double f(const double * p)
{
     
	return 5*PI*PI*sin(1*PI*p[0]) * sin(2*PI*p[1])*sin(PI*p[2]);
};

//
// end of file


你可能感兴趣的:(AFEPack,AFEPack,泊松方程,PDE)