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

Posted on 2016-08-28   |   In computational material science   |   暂无评论

引子

此例没有介绍革命性的功能,但有很多对前面例子的“微创新”,包括:

  • 在不断细化的网格上的计算。数值计算通常要在不同的网格上进行,这样才能感受到精度。而且deal.II支持自适应网格,虽然这个例子中没有用到,但基础在这
  • 读入非规则网格数据
  • 计算优化
  • debug模式,使用Assert宏
  • 变系数Possion方程,使用预条件迭代求解器

这里要求解的方程是:

−∇⋅a(x)∇u(x)u=1inΩ,=0on∂Ω

如果a(x) 是常系数,那么就成了Possion方程,如果它是空间相关的变系数,方程就复杂一些了。
还是得先写出方程的弱形式:

(a∇ϕ,∇u)=(ϕ,1)∀ϕ

程序解析

以下是头文件们:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <deal.II/base/quadrature_lib.h>
#include <deal.II/base/function.h>
#include <deal.II/base/logstream.h>
#include <deal.II/lac/vector.h>
#include <deal.II/lac/full_matrix.h>
#include <deal.II/lac/sparse_matrix.h>
#include <deal.II/lac/dynamic_sparsity_pattern.h>
#include <deal.II/lac/solver_cg.h>
#include <deal.II/lac/precondition.h>
#include <deal.II/grid/tria.h>
#include <deal.II/dofs/dof_handler.h>
#include <deal.II/grid/tria_accessor.h>
#include <deal.II/grid/tria_iterator.h>
#include <deal.II/dofs/dof_accessor.h>
#include <deal.II/dofs/dof_tools.h>
#include <deal.II/fe/fe_q.h>
#include <deal.II/fe/fe_values.h>
#include <deal.II/numerics/vector_tools.h>
#include <deal.II/numerics/matrix_tools.h>
#include <deal.II/numerics/data_out.h>
#include <deal.II/grid/grid_in.h>
#include <deal.II/grid/manifold_lib.h>
#include <fstream>
#include <iostream>
#include <sstream>
using namespace dealii;

新增的是grid_in.h,是为了从硬盘中读入一个网格文件。manifold_lib.h是为了描述环形边界上的对象。

step5类模板:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
template <int dim>
class Step5
{    public:
        Step5 ();
        void run ();
    private:
        void setup_system ();
        void assemble_system ();
        void solve ();
        void output_results (const unsigned int cycle) const;
        Triangulation<dim> triangulation;
        FE_Q<dim> fe;
        DoFHandler<dim> dof_handler;
        SparsityPattern sparsity_pattern;
        SparseMatrix<double> system_matrix;
        Vector<double> solution;
        Vector<double> system_rhs;
};

因为这里处理的是变系数椭圆问题,使用的对象类型还是Function,不过这里没用value,而是用的value_list,它不再接收单个点,而是接收一系列的点,然后返回这些点上的函数值:

1
2
3
4
5
6
7
8
9
10
11
template <int dim>
class Coefficient : public Function<dim>
{    public:
        Coefficient () : Function<dim>() {}
        virtual double value (const Point<dim> &p,
                const unsigned int component = 0) const;
        virtual void value_list (const std::vector<Point<dim> > &points,
                std::vector<double> &values,
                const unsigned int component = 0) const;
};

下面是单个点上的函数值:

1
2
3
4
5
6
7
8
9
template <int dim>
double Coefficient<dim>::value (const Point<dim> &p,
        const unsigned int / *component* /) const
{    if (p.square() < 0.5*0.5)
        return 20;
    else
        return 1;
}

那么下面就是一下计算很多点的函数值:

1
2
3
4
5
6
7
template <int dim>
void Coefficient<dim>::value_list (const std::vector<Point<dim> > &points,
        std::vector<double> &values,
        const unsigned int component) const
{    Assert (values.size() == points.size(),
            ExcDimensionMismatch (values.size(), points.size()));

这个函数接收三个参数:一个是坐标点的列表,一个是存储这些点上的函数值的列表,一个是矢量component,这里应该是0,因为此处是标量函数。
很明显,输出列表values的大小跟输入列表points应该相同,但事实上90%的编程错误都是输入了无效参数,因此应该保证输入参数是valid的。此处,Assert宏是个好方法,因为它保证它的第一个参数,即条件,是有效的,如果无效,就抛出一个exception,即它的第二个参数,通常是终止程序。这将极快地定位错误,方便调试。另一方面,这些检查也不会明显地拖慢程序,而且,Assert宏可仅存在于debug模式,在优化模式中可以将其完全去掉。
事实上,如果将deal.II中的所有check都关掉,可以提速4倍,但同时有引入大量调试错误的问题。
所以,最好是程序稳定后,再将debug关闭。
上面代码就是Assert一下两者的尺寸是否相同,第一个参数是是否相同的条件,第二个参数是调用内置的一个函数来输出两者维度不匹配的信息。该算例的最后就是给出了一个触发这种不匹配的情形,可以发现很快就能定位错误,同时,如果程序是在一个调试器中运行,可以通过调用堆栈直接跳转到出错位置。

1
2
Assert (component == 0,
        ExcIndexRange (component, 0, 1));

这里还检查了是不是标量函数,因为标量可视为只有一个分量的矢量,所以Assert一下是否component=0,如果越界了,就调用ExcIndexRange函数。
下面就是具体赋值代码:

1
2
3
4
5
6
7
8
9
const unsigned int n_points = points.size();
for (unsigned int i=0; i<n_points; ++i)
{    if (points[i].square() < 0.5*0.5)
        values[i] = 20;
    else
        values[i] = 1;
}
}

构造函数:

1
2
3
4
5
template <int dim>
Step5<dim>::Step5 () :
    fe (1),
    dof_handler (triangulation)
{}

建立系统,跟之前不同的是没有生成网格这一步:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
template <int dim>
void Step5<dim>::setup_system ()
{    dof_handler.distribute_dofs (fe);
    std::cout << " Number of degrees of freedom: "
        << dof_handler.n_dofs()
        << std::endl;
    DynamicSparsityPattern dsp(dof_handler.n_dofs());
    DoFTools::make_sparsity_pattern (dof_handler, dsp);
    sparsity_pattern.copy_from(dsp);
    system_matrix.reinit (sparsity_pattern);
    solution.reinit (dof_handler.n_dofs());
    system_rhs.reinit (dof_handler.n_dofs());
}

组装系统:

1
2
3
4
5
6
7
8
9
10
11
12
template <int dim>
void Step5<dim>::assemble_system ()
{    QGauss<dim> quadrature_formula(2);
    FEValues<dim> fe_values (fe, quadrature_formula,
            update_values | update_gradients |
            update_quadrature_points | update_JxW_values);
    const unsigned int dofs_per_cell = fe.dofs_per_cell;
    const unsigned int n_q_points = quadrature_formula.size();
    FullMatrix<double> cell_matrix (dofs_per_cell, dofs_per_cell);
    Vector<double> cell_rhs (dofs_per_cell);
    std::vector<types::global_dof_index> local_dof_indices (dofs_per_cell);

这一步的前面部分跟之前的相同,不解释。
下面不同之处在于这里用的是变系数,所以先根据之前的系数模板创建这么一个对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
const Coefficient<dim> coefficient;
std::vector<double> coefficient_values (n_q_points);
typename DoFHandler<dim>::active_cell_iterator
cell = dof_handler.begin_active(),
     endc = dof_handler.end();
for (; cell!=endc; ++cell)
{    cell_matrix = 0;
    cell_rhs = 0;
    fe_values.reinit (cell);
    coefficient.value_list (fe_values.get_quadrature_points(),
            coefficient_values);
    for (unsigned int q_index=0; q_index<n_q_points; ++q_index)
        for (unsigned int i=0; i<dofs_per_cell; ++i)
        {            for (unsigned int j=0; j<dofs_per_cell; ++j)
                cell_matrix(i,j) += (coefficient_values[q_index] *
                        fe_values.shape_grad(i,q_index) *
                        fe_values.shape_grad(j,q_index) *
                        fe_values.JxW(q_index));
            cell_rhs(i) += (fe_values.shape_value(i,q_index) *
                    1.0 *
                    fe_values.JxW(q_index));
        }
    cell->get_dof_indices (local_dof_indices);
    for (unsigned int i=0; i<dofs_per_cell; ++i)
    {        for (unsigned int j=0; j<dofs_per_cell; ++j)
            system_matrix.add (local_dof_indices[i],
                    local_dof_indices[j],
                    cell_matrix(i,j));
        system_rhs(local_dof_indices[i]) += cell_rhs(i);
    }
}
std::map<types::global_dof_index,double> boundary_values;
VectorTools::interpolate_boundary_values (dof_handler,
        0,
        ZeroFunction<dim>(),
        boundary_values);
MatrixTools::apply_boundary_values (boundary_values,
        system_matrix,
        solution,
        system_rhs);
}

比起step4做的优化是:在计算系数函数的值时,是在每个单位上一下计算了所有积分点上的值。因为从step4可以看出,在那里计算右端项时,计算了$dofs_per_celln_q_points times次,如果还是这样计算,对应到这里计算系数时,就需要计算

dofs_per_celldofs_per_cell*n_q_points$次,但实际上相同积分点对应的这些函数值相同,没必要在自由度的循环中重复计算,同时这里还涉及虚函数调用,开销很大。综上,一次性计算完毕,优化了计算效率。
求解步如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
template <int dim>
void Step5<dim>::solve ()
{    SolverControl solver_control (1000, 1e-12);
    SolverCG<> solver (solver_control);
    PreconditionSSOR<> preconditioner;
    preconditioner.initialize(system_matrix, 1.2);
    solver.solve (system_matrix, solution, system_rhs,
            preconditioner);
    std::cout << " " << solver_control.last_step()
        << " CG iterations needed to obtain convergence."
        << std::endl;
}

这里使用了对称超松弛迭代算法作为预条件子。
输出部分如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
template <int dim>
void Step5<dim>::output_results (const unsigned int cycle) const
{    DataOut<dim> data_out;
    data_out.attach_dof_handler (dof_handler);
    data_out.add_data_vector (solution, "solution");
    data_out.build_patches ();
    DataOutBase::EpsFlags eps_flags;
    eps_flags.z_scaling = 4;
    eps_flags.azimut_angle = 40;
    eps_flags.turn_angle = 10;
    data_out.set_flags (eps_flags);
    std::ostringstream filename;
    filename << "solution-"
        << cycle
        << ".eps";
    std::ofstream output (filename.str().c_str());
    data_out.write_eps (output);
}

这里将输出格式改成了EPS。因为EPS是一个打印格式,它不像其他格式的数据那样能输入到图像工具进行编辑,所以需要事先定义好,源码中就定义了多种flag,不详述。

1
2
3
4
5
6
template <int dim>
void Step5<dim>::run ()
{    GridIn<dim> grid_in;
    grid_in.attach_triangulation (triangulation);
    std::ifstream input_file("circle-grid.inp");

run函数中直接读入网格文件,这里是个inp后缀的。因为该网格是二维的,所以Assert一下如果不是二维的,就抛出一个异常:

1
Assert (dim==2, ExcInternalError());

这是一个非规则网格文件UCD:

1
grid_in.read_ucd (input_file);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
static const SphericalManifold<dim> boundary;
triangulation.set_all_manifold_ids_on_boundary(0);
triangulation.set_manifold (0, boundary);
for (unsigned int cycle=0; cycle<6; ++cycle)
{    std::cout << "Cycle " << cycle << ':' << std::endl;
    if (cycle != 0)
        triangulation.refine_global (1);
    std::cout << " Number of active cells: "
        << triangulation.n_active_cells()
        << std::endl
        << " Total number of cells: "
        << triangulation.n_cells()
        << std::endl;
    setup_system ();
    assemble_system ();
    solve ();
    output_results (cycle);
}
}

然后创建一个流形,来告诉triangulation在网格细化后如何在边界上加点。
然后是main函数:

1
2
3
4
5
6
7
8
9
10
11
12
int main ()
{    Step5<2> laplace_problem_2d;
    laplace_problem_2d.run ();
    /*
       Coefficient<2> coefficient;
       std::vector<Point<2> > points (2);
       std::vector<double> coefficient_values (1);
       coefficient.value_list (points, coefficient_values);
       */
    return 0;
}

注释起来的代码是为了得到Assert的异常信息。

计算结果

每次细化结果为:





#deal.II

求解偏微分方程开源有限元软件deal.II学习--Step 5相关推荐

  1. 求解二阶偏微分方程c语言,科学网—求解偏微分方程开源有限元软件deal.II学习--Step 3 - 亓欣波的博文...

    完整版见:qixinbo.info. deal.II的程序结构 deal.II采用面向对象编程,其中包含了很多的Modules,各自实现不同的功能,并有机地结合起来.如上图所示.具体为:Triangu ...

  2. 流固耦合开源软件precice安装笔记(包括开源CFD软件OpenFOAM、插件swak4Foam,开源有限元软件CalculiX、deal.II)

    安装环境:Ubuntu 20.04 LTS 1. 安装Python发行版Anaconda 可在Anaconda官网下载安装包,下载完成后在下载目录中鼠标右键打开终端,键入: bash Anaconda ...

  3. 开源的python有限元软件_想从Abaqus转用开源有限元软件有什么好的推荐吗?

    推荐FreeCAD,Code-Aster,Calculix. (1)FreeCAD据说华为在尝试于FreeCAD基础上进行CAE开发,FreeCAD本是一款开源的CAD软件,具备一些简单的有限元分析功 ...

  4. 编译开源有限元软件calculix

    calculix的编译过程 第一步:下载安装cygwin,选择gcc-core gcc-g++ gfortran perl注意版本一致,且为最低版本 第二步:修改makefile文件,因为最后没有生成 ...

  5. 开源的python有限元软件_python有限元

    广告关闭 腾讯云11.11云上盛惠 ,精选热门产品助力上云,云服务器首年88元起,买的越多返的越多,最高返5000元! 节点坐标和单元的拓扑信息暂由有限元前处理软件导出后由python读入#nodes ...

  6. 【机器人学】机器人开源项目KDL源码学习:(5)KDL如何求解几何雅克比矩阵

    这篇文章试图说清楚两件事:1. 几何雅克比矩阵的本质:2. KDL如何求解机械臂的几何雅克比矩阵. 一.几何雅克比矩阵的本质 机械臂的关节空间的速度可以映射到执行器末端在操作空间的速度,这种映射可以通 ...

  7. 免费开源Scada软件 RapidScada学习记录

    免费开源Scada软件 RapidScada学习记录 由于工作需要,需要采集污水处理厂的数据,比如溶解氧,ph等数据,并且要求远程控制功能,设备数据异常手机推送功能,能通过网页查看摄像头当前设备及工作 ...

  8. 深度学习(Deep Ritz,Galerkin,PINN)求解偏微分方程(PDE)解读

    深度学习在求解偏微分方程(PDE)中有三种主要方法: Deep Ritz, Galerkin 和 PINN. Deep Ritz 方法是通过使用深度神经网络来逼近解析解来求解 PDE 的. 它使用了 ...

  9. 深度学习(Deep Ritz,Galerkin,PINN)求解偏微分方程(PDE)实现代码地址

    深度学习求解偏微分方程的实现代码可以在GitHub上查找.下面是几个可能有用的项目地址: Deep Ritz: https://github.com/yseop/DeepRitz Galerkin: ...

  10. 深度学习求解偏微分方程

    深度学习求解偏微分方程 1. 稀疏回归解偏微分方程 2. 离散连续方程解偏微分方程 3. 物理神经网络解偏微分方程(PINN:物理激发的神经网络) 1. 稀疏回归解偏微分方程 论文:<Data- ...

最新文章

  1. mysql 查看当前连接数 和 最大连接数
  2. 中国城市人口分布区域分析
  3. TP框架表单验证 【包含ajax方法】
  4. C# 图片画矩形,添加文字
  5. win11天气小组件如何开启 Windows11开启天气组件的设置方法
  6. 伯克利与微软联合发布Blink:使GPU计算实现高达2倍加速
  7. 常用的ajax的代码
  8. 服务器r软硬件配置,软硬件配置要求 - eSight V300R007C00 产品描述 11 - 华为
  9. nginx跨域配置及压缩配置
  10. cobar mysql_mysql分布式中间件cobar
  11. xp系统怎么更改计算机用户名和密码,xp系统怎么取消开机密码?
  12. 【Android控件】呼吸效果的动画
  13. 美团红包饿了么红包CPS小程序+ H5 +推出外卖红包应用,带有后台代码,安装超级简单-源码
  14. Excel如何实现两个工作表数据的对比,比较两个Excel表,两个表格对比 的绿色工具
  15. 【this,super关键字使用】经典习题
  16. 《亲密关系》——[美] 罗兰·米勒 (Rowland S. Miller)
  17. CondaHTTPError: HTTP 000 CONNECTION FAILED for url <https://repo.anaconda.co
  18. 政府的工作流千变万化怎么办(2)
  19. 华为中国大学生ICT大赛2021实践赛网络赛道晋级赛试题解析(答案版)
  20. 通信学习笔记--5G SA和NSA的技术要点和优劣对比

热门文章

  1. 新华三模拟器IRF配置
  2. 游戏开发之STL库的基础使用(string、vector、list、map、unordered_map)(C++基础)
  3. C++ 实数和二进制操作入门
  4. yum源配置文件解释
  5. call_user_func_array
  6. 解决scrollView上subView下移20point问题的一种方式
  7. 盘点欧洲五大智慧城市典范
  8. vsftp启用root用户
  9. window.location.href的使用方法
  10. nginx 负载均衡的五中不同配置方式