天天看點

AFEPack 使用 Tutorial(二):解帶系數二維泊松方程AFEPack Step by Step Tutorial 2: solve Coefficient Poisson Equation

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∣∂Ω​=ub​ub​(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≥y​2.0​1.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;
};
           

這樣,我們就完成了帶系數二維泊松方程。