求解偏微分方程开源有限元软件deal.II学习--Step 10

来源:互联网 发布:nosql数据库的优势 编辑:程序博客网 时间:2024/06/02 05:23

求解偏微分方程开源有限元软件deal.II学习--Step 10

引子

这个小例子主要演示更高阶的映射。映射的意思是参考单元和真实单元之间的变换,之前的例子中默认使用线性或双线性映射。这里的线性映射不是形函数用线性近似,同时也不是Cn单元的概念,Cn单元代表解的最高阶导数的概念。如果边界是弯曲的话,用线性近似就可能不充分。如果使用分段二次抛物线来近似的话,就说这是二次或Q2近似。如果使用分段三次多项式近似的话,就称为Q3近似。依次类推。
这里是用逼近圆周率来说明映射问题。用两种方法:一种是对半径为1的圆进行三角剖分,然后积分,如果完全是圆,那么它的面积精确为圆周率,但因为是多项式近似,所以肯定不精确,这里展示不同映射下随着网格细化的收敛性。第二种不是计算面积,而是计算周长,那么圆周率就大约是周长的一半。

程序解析

123456789101112
#include <deal.II/base/quadrature_lib.h>#include <deal.II/base/convergence_table.h>#include <deal.II/grid/grid_generator.h>#include <deal.II/grid/tria_accessor.h>#include <deal.II/grid/tria_iterator.h>#include <deal.II/grid/manifold_lib.h>#include <deal.II/grid/tria.h>#include <deal.II/grid/grid_out.h>#include <deal.II/dofs/dof_handler.h>#include <deal.II/dofs/dof_accessor.h>#include <deal.II/fe/fe_q.h>#include <deal.II/fe/fe_values.h>

这是之前用过的头文件。

1
#include <deal.II/fe/mapping_q.h>

这是新的头文件。用来声明MappingQ类,进行任意阶数的多项式映射。

123
#include <iostream>#include <fstream>#include <cmath>

C++的标准头文件。
然后开始定义这个问题的命名空间。

123
namespace Step10{    using namespace dealii;

先定义一种非常精确的圆周率值用于后面比较:

1
const long double pi = 3.141592653589793238462643;

下面是输出。这里因为是个小程序,没有用类,而是用函数模板,参数是空间维度。

12345678910
template <int dim>void gnuplot_output(){    std::cout << "Output of grids into gnuplot files:" << std::endl        << "===================================" << std::endl;    Triangulation<dim> triangulation;    GridGenerator::hyper_ball (triangulation);    static const SphericalManifold<dim> boundary;    triangulation.set_all_manifold_ids_on_boundary(0);    triangulation.set_manifold (0, boundary);

首先产生一个粗的三角剖分,施加一个合适的边界描述。

12345678910
for (unsigned int refinement=0; refinement<2;        ++refinement, triangulation.refine_global(1)){    std::cout << "Refinement level: " << refinement << std::endl;    std::string filename_base = "ball";    filename_base += '0'+refinement;    for (unsigned int degree=1; degree<4; ++degree)    {        std::cout << "Degree = " << degree << std::endl;        const MappingQ<dim> mapping (degree);

然后使用不同的映射,分别是Q1、Q2和Q3。当分段线性映射,即Q1时,可以直接使用MappingQ1这个类。Attention!!MappingQ1也是很多函数和类默认使用的映射方式,如果不明确指定映射方式的话。

123456789101112
GridOut grid_out;GridOutFlags::Gnuplot gnuplot_flags(false, 30);grid_out.set_flags(gnuplot_flags);std::string filename = filename_base+"_mapping_q";filename += ('0'+degree);filename += ".dat";std::ofstream gnuplot_file (filename.c_str());grid_out.write_gnuplot (triangulation, gnuplot_file, &mapping);    }    std::cout << std::endl;}}

然后就是使用Gnuplot进行输出。
下面就进入该算例的主要部分:圆周率的计算。
因为这里的圆的半径是1,所以圆的面积的计算就是对常数1在整个计算域上积分:

K1dx=K^1 det J(x^)dx^idet J(x^i)w(x^i)


计算开始:

123456789101112131415
template <int dim>void compute_pi_by_area (){    std::cout << "Computation of Pi by the area:" << std::endl        << "==============================" << std::endl;    const QGauss<dim> quadrature(4);    for (unsigned int degree=1; degree<5; ++degree)    {        std::cout << "Degree = " << degree << std::endl;        Triangulation<dim> triangulation;        GridGenerator::hyper_ball (triangulation);        static const SphericalManifold<dim> boundary;        triangulation.set_all_manifold_ids_on_boundary (0);        triangulation.set_manifold(0, boundary);        const MappingQ<dim> mapping (degree);

这里选择的积分精度足够大,保证在这里任意映射下都能正确求解。

12
const FE_Q<dim> dummy_fe (1);DoFHandler<dim> dof_handler (triangulation);

这里创建了虚假的有限单元和自由度句柄,因为这里不需要知道在积分点上有限元上的值,只是为了知道那个积分权重。注意,这里有限单元的形函数次数是1,再次说明线性形函数跟线性映射不是一个概念。

12
FEValues<dim> fe_values (mapping, dummy_fe, quadrature,        update_JxW_values);

这里传递给FEValues的第一个参数是mapping对象,之前的例子中这个参数是省略的,默认使用MappingQ1这样的对象。

1
ConvergenceTable table;

这里再创建一个收敛性表格,来看收敛速率。
然后开始对细化步数循环:

123456
for (unsigned int refinement=0; refinement<6;        ++refinement, triangulation.refine_global (1)){    table.add_value("cells", triangulation.n_active_cells());    dof_handler.distribute_dofs (dummy_fe);    long double area = 0;

然后对所有单元进行循环:

123456789101112
typename DoFHandler<dim>::active_cell_iteratorcell = dof_handler.begin_active(),     endc = dof_handler.end();for (; cell!=endc; ++cell){    fe_values.reinit (cell);    for (unsigned int i=0; i<fe_values.n_quadrature_points; ++i)        area += fe_values.JxW (i);}table.add_value("eval.pi", static_cast<double> (area));table.add_value("error", static_cast<double> (std::fabs(area-pi)));}

并且存储进table中。
然后开始计算收敛性:

123456789101112131415161718
table.omit_column_from_convergence_rate_evaluation("cells");table.omit_column_from_convergence_rate_evaluation("eval.pi");table.evaluate_all_convergence_rates(ConvergenceTable::reduction_rate_log2);table.set_precision("eval.pi", 16);table.set_scientific("error", true);table.write_text(std::cout);std::cout << std::endl;}}下面的计算方法不是计算面积,而是计算周长:```cpptemplate <int dim>void compute_pi_by_perimeter (){    std::cout << "Computation of Pi by the perimeter:" << std::endl        << "===================================" << std::endl;    const QGauss<dim-1> quadrature(4);

注意,上面的QGauss的维度是dim-1,这是因为是对边积分,而不是对体单元。

1234567891011
for (unsigned int degree=1; degree<5; ++degree){    std::cout << "Degree = " << degree << std::endl;    Triangulation<dim> triangulation;    GridGenerator::hyper_ball (triangulation);    static const SphericalManifold<dim> boundary;    triangulation.set_all_manifold_ids_on_boundary (0);    triangulation.set_manifold (0, boundary);    const MappingQ<dim> mapping (degree);    const FE_Q<dim> fe (1);    DoFHandler<dim> dof_handler (triangulation);

这些跟上面的一样。

12345678
FEFaceValues<dim> fe_face_values (mapping, fe, quadrature,        update_JxW_values);ConvergenceTable table;for (unsigned int refinement=0; refinement<6;        ++refinement, triangulation.refine_global (1)){    table.add_value("cells", triangulation.n_active_cells());    dof_handler.distribute_dofs (fe);

Attention!这里是创建的FEFaceValues而不是FEValues对象。

12345678910111213141516171819202122232425
typename DoFHandler<dim>::active_cell_iteratorcell = dof_handler.begin_active(),     endc = dof_handler.end();long double perimeter = 0;for (; cell!=endc; ++cell)    for (unsigned int face_no=0; face_no<GeometryInfo<dim>::faces_per_cell; ++face_no)        if (cell->face(face_no)->at_boundary())        {            fe_face_values.reinit (cell, face_no);            for (unsigned int i=0; i<fe_face_values.n_quadrature_points; ++i)                perimeter += fe_face_values.JxW (i);        }table.add_value("eval.pi", static_cast<double> (perimeter/2.));table.add_value("error", static_cast<double> (std::fabs(perimeter/2.-pi)));}table.omit_column_from_convergence_rate_evaluation("cells");table.omit_column_from_convergence_rate_evaluation("eval.pi");table.evaluate_all_convergence_rates(ConvergenceTable::reduction_rate_log2);table.set_precision("eval.pi", 16);table.set_scientific("error", true);table.write_text(std::cout);std::cout << std::endl;}}}

Attention!!这里积分时,只有边界上的才能被叠加进去,所以需要有一个判断是否边界的语句。
然后是main函数:

12345678910111213141516171819202122232425262728293031323334
int main (){    try    {        std::cout.precision (16);        Step10::gnuplot_output<2>();        Step10::compute_pi_by_area<2> ();        Step10::compute_pi_by_perimeter<2> ();    }    catch (std::exception &exc)    {        std::cerr << std::endl << std::endl            << "----------------------------------------------------"            << std::endl;        std::cerr << "Exception on processing: " << std::endl            << exc.what() << std::endl            << "Aborting!" << std::endl            << "----------------------------------------------------"            << std::endl;        return 1;    }    catch (...)    {        std::cerr << std::endl << std::endl            << "----------------------------------------------------"            << std::endl;        std::cerr << "Unknown exception!" << std::endl            << "Aborting!" << std::endl            << "----------------------------------------------------"            << std::endl;        return 1;    }    return 0;}

计算结果




上面展示的是粗网格上的结果。实线是网格,虚线是精确的圆。
可以看出,即使在粗网格上,二次和三次映射都取得了很好的吻合结果。
然后是计算精度:

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768
Computation of Pi by the area:==============================Degree = 1cells eval.pi error5 1.9999999999999993 1.1416e+00 -20 2.8284271247461894 3.1317e-01 1.8780 3.0614674589207178 8.0125e-02 1.97320 3.1214451522580520 2.0148e-02 1.991280 3.1365484905459393 5.0442e-03 2.005120 3.1403311569547534 1.2615e-03 2.00Degree = 2cells eval.pi error5 3.1045694996615865 3.7023e-02 -20 3.1391475703122271 2.4451e-03 3.9280 3.1414377167038303 1.5494e-04 3.98320 3.1415829366419015 9.7169e-06 4.001280 3.1415920457576911 6.0783e-07 4.005120 3.1415926155921139 3.7998e-08 4.00Degree = 3cells eval.pi error5 3.1410033851499310 5.8927e-04 -20 3.1415830393583861 9.6142e-06 5.9480 3.1415925017363837 1.5185e-07 5.98320 3.1415926512106722 2.3791e-09 6.001280 3.1415926535525962 3.7197e-11 6.005120 3.1415926535892140 5.7923e-13 6.00Degree = 4cells eval.pi error5 3.1415871927401127 5.4608e-06 -20 3.1415926314742437 2.2116e-08 7.9580 3.1415926535026228 8.7170e-11 7.99320 3.1415926535894529 3.4036e-13 8.001280 3.1415926535897927 2.9187e-16 10.195120 3.1415926535897944 1.3509e-15 -2.21Computation of Pi by the perimeter:===================================Degree = 1cells eval.pi error5 2.8284271247461898 3.1317e-01 -20 3.0614674589207178 8.0125e-02 1.9780 3.1214451522580520 2.0148e-02 1.99320 3.1365484905459393 5.0442e-03 2.001280 3.1403311569547525 1.2615e-03 2.005120 3.1412772509327729 3.1540e-04 2.00Degree = 2cells eval.pi error5 3.1248930668550594 1.6700e-02 -20 3.1404050605605449 1.1876e-03 3.8180 3.1415157631807014 7.6890e-05 3.95320 3.1415878042798617 4.8493e-06 3.991280 3.1415923498174534 3.0377e-07 4.005120 3.1415926345932004 1.8997e-08 4.00Degree = 3cells eval.pi error5 3.1414940401456057 9.8613e-05 -20 3.1415913432549156 1.3103e-06 6.2380 3.1415926341726914 1.9417e-08 6.08320 3.1415926532906893 2.9910e-10 6.021280 3.1415926535851360 4.6571e-12 6.015120 3.1415926535897203 7.2845e-14 6.00Degree = 4cells eval.pi error5 3.1415921029432576 5.5065e-07 -20 3.1415926513737600 2.2160e-09 7.9680 3.1415926535810712 8.7218e-12 7.99320 3.1415926535897594 3.3668e-14 8.021280 3.1415926535897922 1.0617e-15 4.995120 3.1415926535897931 1.0061e-16 3.40

可以看出,随着映射次数提高,计算精度也在提高,而Q1映射在最细网格上的精度还不如Q3映射在最粗网格上的精度。最后一栏是收敛阶,可以看出阶数达到了h2p

0 0