AFEPack Step by Step Tutorial 2: solve Coefficient Poisson Equation
Coefficient Poisson Equation 的源码在 example/coefficient_possion_equation 目录下。
运行样例
编译 EasyMesh 网格文件
~/AFEPack/example/coefficient_possion_equation$ easymesh D
Working. Please wait !
Improving the grid quality. Please wait !
Renumerating nodes, elements and sides. Please wait !
Processing material marks. Please wait !
编译程序
使用 make 进行编译,具体编译信息可以自己查看 Makefile。
~/AFEPack/example/coefficient_possion_equation$ make
mpicxx -c -o coefficient_possion_equation.o coefficient_possion_equation.cpp -I. -O2 -fno-delete-null-pointer-checks -I/usr/include/trilinos -fPIC -D__SERIALIZATION__ -DMULTITHREAD -pthread -ftemplate-depth-256 -I/home/yizhou/AFEPack/library/include -I/home/yizhou/local/include -I/home/yizhou/local/include/deal.II -I/usr/include/trilinos
mpicxx -o main coefficient_possion_equation.o -L/home/yizhou/local/lib -L../../library/lib -lAFEPack -ldeal_II -ltbb -ldl -lm -pthread
运行结果
~/AFEPack/example/coefficient_possion_equation$ ./run D
Reading easymesh data file ...
reading node data ... OK!
reading side data ... OK!
reading element data ... OK!
AFEPack library file found: /home/yizhou/AFEPack/template/triangle/triangle.tmp_geo
AFEPack library file opened: /home/yizhou/AFEPack/template/triangle/triangle.tmp_geo
Opening shared library /home/yizhou/AFEPack/template/triangle/./triangle.geometry.so ...
tried /home/yizhou/AFEPack/template/triangle/./triangle.geometry.so: success
AFEPack library file found: /home/yizhou/AFEPack/template/triangle/triangle.crd_trs
AFEPack library file opened: /home/yizhou/AFEPack/template/triangle/triangle.crd_trs
Opening shared library /home/yizhou/AFEPack/template/triangle/./triangle.geometry.so ...
tried /home/yizhou/AFEPack/template/triangle/./triangle.geometry.so: success
AFEPack library file found: /home/yizhou/AFEPack/template/triangle/triangle.1.tmp_dof
AFEPack library file opened: /home/yizhou/AFEPack/template/triangle/triangle.1.tmp_dof
AFEPack library file found: /home/yizhou/AFEPack/template/triangle/triangle.1.bas_fun
AFEPack library file opened: /home/yizhou/AFEPack/template/triangle/triangle.1.bas_fun
Opening shared library /home/yizhou/AFEPack/template/triangle/./triangle.1.bas_fun.so ...
tried /home/yizhou/AFEPack/template/triangle/./triangle.1.bas_fun.so: success
Opening shared library /home/yizhou/AFEPack/template/triangle/./triangle.1.bas_fun.so ...
tried /home/yizhou/AFEPack/template/triangle/./triangle.1.bas_fun.so: success
Opening shared library /home/yizhou/AFEPack/template/triangle/./triangle.1.bas_fun.so ...
tried /home/yizhou/AFEPack/template/triangle/./triangle.1.bas_fun.so: success
Building degree of freedom for the FEM space ...
total 3009 degree of freedom found.
AMGSolver initializing ... OK! grid levels: 5
AMGSolver begin with initial residual 880.211 ...
failed to converge with residual 0.633937 at step 21.
从上面可以看出运行结果出错了,failed to converge,第 21 步没法收敛。问题我看看是不是出在网格。
Coefficient Poisson Equation
这里,我们的二维带系数泊松方程为
{ − K ⋅ Δ u = f u ∣ ∂ Ω = u b u b ( x , y ) = s i n ( x ) ∗ s i n ( y ) , i f ( x , y ) ∈ ∂ Ω f ( x , y ) = 1 , f o r ( x , y ) ∈ Ω \left\{\begin{matrix} -K \cdot \Delta u = f\\ u |_{\partial \Omega} = u_b\\ u_b(x,y)=sin(x)*sin(y), \ if \ (x,y) \in \partial \Omega\\ f(x,y) = 1, \ for \ (x,y) \in \Omega \end{matrix}\right. ⎩⎪⎪⎨⎪⎪⎧−K⋅Δu=fu∣∂Ω=ubub(x,y)=sin(x)∗sin(y), if (x,y)∈∂Ωf(x,y)=1, for (x,y)∈Ω
其中 u b u_b ub 是边界条件, f f f 是右端项, K K K 是一个 2 × 2 2 \times 2 2×2 矩阵,定义如下:
K = [ k 11 ( x , y ) k 12 ( x , y ) k 21 ( x , y ) k 22 ( x , y ) ] = [ { 10.0 + x ∀ x < y 2.0 + y ∀ x ≥ y 1.0 2.0 { 10.0 + x ∀ x + y > 1 1.0 + y ∀ x + y ≤ 1 ] K=\begin{bmatrix} k_{11}(x,y) & k_{12}(x,y)\\ k_{21}(x,y) & k_{22}(x,y) \end{bmatrix}= \begin{bmatrix} \left\{\begin{matrix} 10.0+x \ \ \forall x<y\\ 2.0+y \ \ \forall x \geq y \end{matrix}\right. & 1.0 \\ 2.0 & \left\{\begin{matrix} 10.0+x \ \ \forall x+y>1\\ 1.0+y \ \ \forall x+y \leq 1 \end{matrix}\right. \end{bmatrix} K=[k11(x,y)k21(x,y)k12(x,y)k22(x,y)]=⎣⎢⎢⎡{10.0+x ∀x<y2.0+y ∀x≥y2.01.0{10.0+x ∀x+y>11.0+y ∀x+y≤1⎦⎥⎥⎤ 矩阵 K K K 是 positive definite。
核心代码分析
Coefficient Poisson Equation 代码大部分和 2D Poisson Equation 代码是一样的。差别只是在构造基函数方面。2D Poisson Equation 在构造刚度矩阵(Stiff Matrix)的时候,直接使用 AFEPack 提供的模板类 StiffMatrix 即可。Coefficient Poisson Equation 需要自己构造基函数,所以我们需要从 StiffMatrix 类中派出一个新类。
BilinearOperator 类
BilinearOperator is the discretized version of the bilinear form on two linear space
f [ a ( u , v ) , u ∈ U , v ∈ V ] f[a(u,v), \qquad u \in U, v \in V] f[a(u,v),u∈U,v∈V], thus for U U U and V V V to be finite dimensional space with basis function as ϕ i , 1 ≤ i ≤ M \phi^i, 1\leq i\leq M ϕi,1≤i≤M and ψ j , 1 ≤ j ≤ N \psi^j, 1\leq j \leq N ψj,1≤j≤N, respectively, the bilinear form can be given as a matrix with its entry as f [ a i j = a ( ϕ i , ψ j ) ] f[a^{ij} = a(\phi^i, \psi^j)] f[aij=a(ϕi,ψj)].
The linear finite dimensional spaces here are two finite element space and the resulted matrix is a sparse matrix. The method getElementMatrix() is a pure virtual function that you must specify this method in derived class to contruct an object.
从上面的代码注释中可以看到,我们需要实现该类的纯虚函数 getElementMatrix()。该模板类在 AFEPack/library/include/BilinearOperator.h 文件中。定义如下:
template <int DIM, class value_type0, class value_type1=value_type0,
int DOW=DIM, int TDIM0=DIM, int TDIM1 = DIM>
class BilinearOperator : public SparseMatrix<double>
StiffMatrix 类
头文件位置
该模板类在 AFEPack/library/include/BilinearOperator.h 文件中。定义如下:
template <int DIM, class value_type, int DOW=DIM, int TDIM=DIM>
class StiffMatrix : public BilinearOperator<DIM, value_type, value_type, DOW, TDIM, TDIM>
{
public:
StiffMatrix() {};
StiffMatrix(FEMSpace<typename StiffMatrix::value_type,DIM,DOW,TDIM>& sp) :
BilinearOperator<DIM, typename StiffMatrix::value_type,
typename StiffMatrix::value_type,DOW,TDIM,TDIM>(sp, sp) {};
virtual ~StiffMatrix() {};
public:
void reinit(FEMSpace<typename StiffMatrix::value_type,DIM,DOW,TDIM>& sp);
virtual void getElementMatrix(const Element<typename StiffMatrix::value_type,DIM,DOW,TDIM>&,
const Element<typename StiffMatrix::value_type,DIM,DOW,TDIM>&,
const typename ActiveElementPairIterator<DIM>::State state = ActiveElementPairIterator<DIM>::EQUAL);
};
关键函数
getElementMatrix()
如上小节所示,我们需要实现 StiffMatrix 类中的虚函数 getElementMatrix()。这个虚函数将在父类 BilinearOperator 的 buildSparseMatrix() 函数中调用。函数 buildSparseMatrix() 将在父类 BilinearOperator 的 build() 中调用。
具体参看文件 AFEPack/library/include/BilinearOperator.templates.h。
类实现
我们从 StiffMatrix 类中派生,并实现纯虚函数 getElementMatrix() 即可。具体类实现如下:
class Matrix : public StiffMatrix<2, double>
{
public:
Matrix(FEMSpace<double,2>& sp) :
StiffMatrix<2,double>(sp) {};
virtual ~Matrix() {};
public:
virtual void getElementMatrix(const Element<double,2>& element0,
const Element<double,2>& element1,
const ActiveElementPairIterator<2>::State state);
};
getElementMatrix() 实现
根据带系数二维泊松方程定义,我们函数实现如下:
void Matrix::getElementMatrix(
const Element<double,2>& element0,
const Element<double,2>& element1,
const ActiveElementPairIterator<2>::State state)
{
int n_element_dof0 = elementDof0().size();
int n_element_dof1 = elementDof1().size();
double volume = element0.templateElement().volume();
const QuadratureInfo<2>& quad_info = element0.findQuadratureInfo(algebricAccuracy());
std::vector<double> jacobian = element0.local_to_global_jacobian(quad_info.quadraturePoint());
int n_quadrature_point = quad_info.n_quadraturePoint();
std::vector<AFEPack::Point<2> > q_point = element0.local_to_global(quad_info.quadraturePoint());
std::vector<std::vector<std::vector<double> > > basis_gradient = element0.basis_function_gradient(q_point);
for (int l = 0;l < n_quadrature_point;l ++) {
double Jxw = quad_info.weight(l)*jacobian[l]*volume;
AFEPack::Point<2> q_point = element0.local_to_global(quad_info.quadraturePoint(l));
double a11 = a11_(q_point);
double a12 = a12_(q_point);
double a21 = a21_(q_point);
double a22 = a22_(q_point);
for (int j = 0; j < n_element_dof0; j++) {
for (int k = 0; k < n_element_dof1; k++) {
elementMatrix(j,k) += Jxw*(
a11 * basis_gradient[j][l][0] * basis_gradient[k][l][0]
+ a12 * basis_gradient[j][l][0] * basis_gradient[k][l][1]
+ a21 * basis_gradient[j][l][1] * basis_gradient[k][l][0]
+ a22 * basis_gradient[j][l][1] * basis_gradient[k][l][1]
);
}
}
}
};
这样,我们就完成了核心代码实现。其他和二维泊松方程就一样了。
完整实现
Step 1: 网格文件
EasyMesh mesh;
mesh.readData("D");
Step 2: 构建模板单元
// construct template geometry
TemplateGeometry<2> triangle_template_geometry;
triangle_template_geometry.readData("triangle.tmp_geo");
// construct coordinate transformation
CoordTransform<2,2> triangle_coord_transform;
triangle_coord_transform.readData("triangle.crd_trs");
// construct template degree of freedom
TemplateDOF<2> triangle_template_dof(triangle_template_geometry);
triangle_template_dof.readData("triangle.1.tmp_dof");
// construct basis functions
BasisFunctionAdmin<double,2,2> triangle_basis_function(triangle_template_dof);
triangle_basis_function.readData("triangle.1.bas_fun");
// construct template element,
// because to construct a finite element space, we general need a list of
// template elements that we store the template elements in a vector and
// provide it to a finite element space
std::vector<TemplateElement<double,2,2> > template_element(1);
template_element.reinit(triangle_template_geometry,
triangle_template_dof,
triangle_coord_transform,
triangle_basis_function);
Step 3: 构建有限元空间
// construct the finite element space on the mesh and the template element
FEMSpace<double,2> fem_space(mesh, template_element);
// this is the number of the geometry elements
int n_element = mesh.n_geometry(2);
// and set the number of elements in the finite element space the same as geometry elements
fem_space.element().resize(n_element);
// appoint that for every element, its template element is the 0-th one in the vector template_element
for (int i = 0;i < n_element;i ++)
fem_space.element(i).reinit(fem_space,i,0);
// build the element information in the finite element space
fem_space.buildElement();
fem_space.buildDof();
fem_space.buildDofBoundaryMark();
Step 4: 构建刚度矩阵
前面三步和二维泊松方程是一样的,这里和二维泊松方程不同。
Matrix stiff_matrix(fem_space);
stiff_matrix.algebricAccuracy() = 3;
stiff_matrix.build();
Step 5: 解拉普拉斯方程
FEMFunction<double,2> solution(fem_space);
Vector<double> right_hand_side;
Operator::L2Discretize(&f, fem_space, right_hand_side, 3);
BoundaryFunction<double,2> boundary(BoundaryConditionInfo::DIRICHLET, 1, &u);
BoundaryConditionAdmin<double,2> boundary_admin(fem_space);
boundary_admin.add(boundary);
boundary_admin.apply(stiff_matrix, solution, right_hand_side);
AMGSolver solver(stiff_matrix);
solver.solve(solution, right_hand_side);
solution.writeOpenDXData("u.dx");
定义 u b u_b ub 函数
double u(const double * p)
{
return sin(p[0]) * sin(p[1]);
};
定义 f f f 函数
double f(const double * p)
{
return 1.0;
};
这样,我们就完成了带系数二维泊松方程。