这个 Tutorial 和前面两个不同。前面两个 Tutorial 都是 AFEPack 自带的例子,这个 Tutorial 可以认为是在 2D Poisson Equation 的基础上改进,将原来的 2D 方程变成 3D。
这里,三维泊松方程定义
{ − Δ 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))
头文件
#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();
我们以三维四面体线性有限单元为例,展示构造有限单元需要的代码和步骤。事实上所有的模板单元信息都保存在文本信息中,我们只需要对信息做初始化以及读入相对应文件。
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);
注意:这里的代码和二维方程是不一样的,我们要选择三维对应的模板单元。
有了网格数据和模板单元我们就可以构造有限元空间。事实上实现起来很简单,只需要对每一个几何单元指定一个模板单元。这里我们只考虑线性(或二次)有限单元,因此一个循环即可。
/// 构造有限单元
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();
在有限元空间上构造稀疏矩阵。这里的稀疏矩阵其实就是刚度矩阵 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();
根据定义,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);
我们还是使用 AMG 求解器。AMG 参数设置为 t o l = 1.0 e − 8 tol=1.0e^{-8} tol=1.0e−8,最大迭代步数 200 200 200。
/// AMG 求解
AMGSolver solver(stiff_matrix);
solver.solve(solution, right_hand_side, 1.0e-08, 200);
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