内容可能存在错误,欢迎大家批评指正
一、例子介绍
本文使用的是官网上提供的第二例子( MOOSE|EX02),这个例子的求解的方程是在扩散方程(泊松方程)之上添加平流项,平流速度在方向上为,否则为零。其边界条件采用Dirichlet边界。在求解域底部上 ,在顶部 ,在剩余的其他边界上,其中是边界法向向量。
二、FEM
1、生成弱形式
a.等式两侧同时乘测试函数
这里
b.对整个求解域积分
c.分部积分
由于求导的性质有,带入(2.2)式左侧第一项得:
d.应用散度定理
散度定理可以理解成:考虑在流动得液体中取出体积为的部分,则流过该部分表面的液体通量等于散度在整个体积上的积分。
左侧是在整个体积上的积分,右侧是在的表面边界上的积分,是边界上外法线方向。
对(2.3)式的左侧第二项应用散度定理,(2.3)式可以写成
e.写成内积的形式
可以把(2.4)式用内积形式表示,其中每项都能在MOOSE中找到现有类进行继承
2、等参单元
其中
这样刚度矩阵的
3、高斯积分
进行离散后的积分求解起来很麻烦,为了方便就采用了高斯积分的形式
是正交点的位置,是它的相关权重。
最终我们只需求解:
MOOSE平台关于高斯积分的介绍Quadrature,目前还没有太多说明。
三、输入文件
MOOSE会把输入文件和编译的app一块编译成而执行文件,然后运行输出,MOOSE的输入文件采用的是“分层输入文本”(hit)格式,基本的MOOSE输入文件包含六个部分
[Mesh]:定义求解的区域及剖分
[Variables]:定义需要求解的变量
[Kernels]:定义要解决的方程
[BCs]:定义边界条件
[Executioner]:定义解决问题的类型和方式
[Outputs]:定义结果如何输出
当然根据求解问题的需要也可以添加AuxVariables、AuxKernels、preconditioners、Postprocessors、Functions等更多的层。
[Kernels] #最外层名称不能随意更改,要使用MOOSE支持的层名
[Kernel_1] #内存名称可以用户自定义,方便阅读
type = Kernel_name #要用的物理模块,必须是编译的app包含在内的
variable = variable1 #该物理模块需要求解的变量名称
#根据Kernel的类型设置其他参数
[]
[Kernel_2] #需要多个物理模块,可以并列添加内部层
type = Kernel_name #同一个物理模块可以多次调用求解不同的变量
variable = variable2 #
[]
[] #内外层的开始和结尾都需要中括号
1、网格[mesh]
本例使用的网格是已经生成的ExodusII格式的网格文件,所以直接导入就行。共有3774个节点,2476个四边形剖分。
[Mesh]
file = mug.e
[]
MOOSE支持很多类型的网格文件导入,具体可以查看官网介绍(FileMesh)。
2、变量[Variables]
对于改问题要求解的方程
其中只包含一个变量。
[Variables]
[convected] #变量的名称
order = FIRST #基函数插值类型
family = LAGRANGE #阶数
[] #网格是QUAD4剖分,所以该变量采用拉格朗日一次插值
[]
3、物理模块[Kernels]
控制方程的弱形式除去边界边界项外还有两项:扩散项和对流项,所以物理模块应该选用两个。
[Kernels]
[diff]
type = Diffusion
variable = convected
[]
[conv]
type = ExampleConvection
variable = convected
velocity = '0.0 0.0 1.0'
[]
[]
物理模块由两个文件构成,存放在./include中的.h类型的头文件和存放在./src中.C类型的源文件
对于Diffusion模块,其头文件Diffusion.h
#pragma once //避免多次编译
#include "Kernel.h" //调用
class Diffusion : public Kernel //继承Kernel类
{
public:
//InputParameters:主要的MOOSE类负责处理几乎每个MOOSE系统中的用户定义参数。
static InputParameters validParams();
Diffusion(const InputParameters & parameters);//构造函数
protected:
virtual Real computeQpResidual() override;//残差求解函数,必须重载
virtual Real computeQpJacobian() override;//雅可比矩阵求解函数,必须重载
};
源文件Diffusion.C
#include "Diffusion.h"
//注册类名,通过这个将类编译进程序使用
registerMooseObject("MooseApp", Diffusion);
InputParameters
Diffusion::validParams() //构造Diffusion模块获取物理变量的函数
{
InputParameters params = Kernel::validParams();
//addClassDescription添加类描述
params.addClassDescription("The Laplacian operator ($-\\nabla \\cdot \\nabla u$), with the weak "
"form of $(\\nabla \\phi_i, \\nabla u_h)$.");
return params;
}
Diffusion::Diffusion(const InputParameters & parameters) : Kernel(parameters) {}
Real
Diffusion::computeQpResidual()
{
return _grad_u[_qp] * _grad_test[_i][_qp];//继承的是Kernel,所以要使用_grad_test或_test
}
Real
Diffusion::computeQpJacobian()
{
return _grad_phi[_j][_qp] * _grad_test[_i][_qp];
}
平流项模块的头文件ExampleConvection.h
#pragma once
#include "Kernel.h"
class ExampleConvection : public Kernel
{
public:
/**这是构造函数的声明。 这个函数接受一个字符串和一个InputParameters对象,
就像其他Kernel-derived类。
*/
ExampleConvection(const InputParameters & parameters);
/**
validParams返回这个内核接受/需要的参数
*/
static InputParameters validParams();
protected:
/**
计算一个积分点(节点)上的残差
*/
virtual Real computeQpResidual() override;
/**
* Responsible for computing the diagonal block of the preconditioning matrix.
* This is essentially the partial derivative of the residual with respect to
* the variable this kernel operates on ("u").
*
* Note that this can be an approximation or linearization. In this case it's
* not because the Jacobian of this operator is easy to calculate.
*
* This function should always be defined in the .C file.
*/
virtual Real computeQpJacobian() override;
private:
/**
用来储存向量,便于计算内积
*/
RealVectorValue _velocity;
};
源文件ExampleConvection.C
#include "ExampleConvection.h"
//注册
registerMooseObject("ExampleApp", ExampleConvection);
//这个函数定义了这个内核的有效参数和它们的默认值
InputParameters
ExampleConvection::validParams()
{
InputParameters params = Kernel::validParams();
//这里使用了RealVectorValue所以velocity的传入值是向量
params.addRequiredParam<RealVectorValue>("velocity", "Velocity Vector");
return params;
}
ExampleConvection::ExampleConvection(const InputParameters & parameters)
: // 必须先调用基类的构造函数
Kernel(parameters),
_velocity(getParam<RealVectorValue>("velocity"))
{
}
Real
ExampleConvection::computeQpResidual()
{
// velocity * _grad_u[_qp] is actually doing a dot product
return _test[_i][_qp] * (_velocity * _grad_u[_qp]);
}
Real
ExampleConvection::computeQpJacobian()
{
// the partial derivative of _grad_u is just _grad_phi[_j]
return _test[_i][_qp] * (_velocity * _grad_phi[_j][_qp]);
}
4、边界条件[BCs]
[BCs]
[bottom]
type = DirichletBC
variable = convected
boundary = 'bottom'
value = 1
[]
[top]
type = DirichletBC
variable = convected
boundary = 'top'
value = 0
[]
[]
5、求解方式[Executioner]
[Executioner]
type = Steady #稳态,不含时间
solve_type = 'PJFNK' #带有预处理的JFNK
[]
6、输出[Outputs]
[Outputs]
execute_on = 'timestep_end' #时间结束执行该对象
exodus = true
[]
7、ex02_oversample.i
[Mesh]
type = GeneratedMesh #常用网格生成类
dim = 2 #网格维度是2
# x的最大坐标xmax为可选参数默认为1
# x的最小坐标xmin为可选参数默认为0
nx = 2 # x方向剖分的数目,默认为1
ny = 2 # y方向剖分的数目,默认为1
#这里选择的维度是2,所以不写nz,zmax应该也行,但是官网写了
nz = 0
zmax = 0 # z方向的最大坐标
elem_type = QUAD9 #网格剖分类型,默认是QUAD4,四个自由度的四边形网格
[]
[Variables]
[./diffused]
order = SECOND #网格9有个自由度,所以要采用二阶插值,默认是拉格朗日类型
[../]
[]
[Kernels]
[./diff]
type = Diffusion
variable = diffused
[../]
[]
[DiracKernels]
[./foo]
variable = diffused #被施加负载的变量
type = ConstantPointSource #在网格中的单个位置施加负载
value = 1 # 负载
point = '0.3 0.3 0.0' #施加负载的位置
[../]
[]
[BCs]
[./all]
type = DirichletBC
variable = diffused
boundary = 'bottom left right top'
value = 0.0
[../]
[]
[Executioner]
type = Steady
solve_type = 'PJFNK'
[]
[Outputs]
execute_on = 'timestep_end'
exodus = true
[./refine_2]
type = Exodus #输出文件类型
file_base = oversample_2 #输出文件名
refinements = 2
[../]
[./refine_4]
type = Exodus
file_base = oversample_4
#Number of uniform refinements for oversampling
#(refinement levels beyond any uniform refinements)
refinements = 4
[../]
[]
四、MOOSE常见代码
MOOSE的内核AD开头的是自动计算微分,如果选用AD类型,那么模型所用的所有模块都要调用AD开头的。
1、常见内置变量
-
_u,_grad_u 被操作变量的值和梯度。
-
_test,_grad_test 值()的测试函数 和梯度
-
_i 测试功能组件的当前索引。
-
_q_point 当前节点的坐标。
-
_qp 当前节点索引。
2、InputParameters
“InputParameters”是一个C++类模板,声明和填充所有的参数,InputParameters 对象是一组参数,每个参数都有单独的属性,可用于精细控制底层对象的行为。例如,参数可以标记为必需或可选,提供或不提供默认值。输入参数的完整属性列表(InputParameters)。
每个类里包含的InputParameters都是使用静态方法validParams创建的,使用InputParameters方法创建一个名为Object的Kernel,其定义了两个参数,一个带有默认值1980的int类型的可选参数year和类型为int的必选参数month,以及相应的参数描述。
#include "Object.h"
//创建的所有基于MOOSE的对象类都必须使用这个宏把要创建的类在app内注册
//第一个参数是带有"App"后缀应用程序名称。
//第二个参数是创建的c++类的名称。
registerMooseObject("MOOSEApp", Object);
InputParameters
Object::validParams()
{
InputParameters params = Kernel::validParams(); //从父类开始
params.addParam<int>("year", 1980, "Provide the year you were born.")
params.addRequiredParam<int>("month", "Provide the month you were born.")
return params;
}
输入文件的Kernel层
[Kernel]
[date_object]
type = Object #使用已经在app内注册的Kernel
year = 2000 #此为选填参数,不填就用默认值1980
month = 6 #此为必选参数,不填程序报错
[]
[]
3、computeQpResidual
这个函数会出现在每个Kernel的末尾,用来计算残差。最好去看官网说明,说的比较清楚。
父类 | 覆盖 | 用法 |
---|---|---|
computeQpResidual | 当PDE中的项同时乘以测试函数和测试函数的梯度时使用(_test和_grad_test必须使用) | |
precomputeQpResidual | 当PDE中计算的项仅乘以测试函数时使用(不用写_test参数,会自动应用) | |
precomputeQpResidual | 当PDE中计算的项仅乘以测试函数的梯度时使用(不用写_grad_test参数,会自动应用) |
使用实例对于弱形式:
仅用到了测试函数的梯度,所以计算残差要继承KernelGrad或ADKernelGrad的precomputeQpResidual ,代码形式:
#pragma once
//调用ADKernelGrad基类
#include "ADKernelGrad.h"
//继承
class DarcyPressure : public ADKernelGrad
{
public:
static InputParameters validParams();
DarcyPressure(const InputParameters & parameters);
protected:
/// 静态函数,必须重载
virtual ADRealVectorValue precomputeQpResidual() override;
///变量的值
const Real _permeability;
const Real _viscosity;
};
//源文件计算残差,不用写_grad_test参数,会自动应用
ADRealVectorValue
DarcyPressure::precomputeQpResidual()
{
return (_permeability / _viscosity) * _grad_u[_qp];//这里的u就是求解变量p
}
4、
参考资料
、Gockenbach M S . Understanding and Implementing the Finite Element Method[M]. Society for Industrial and Applied Mathematics, 2006.
、https://mooseframework.inl.gov/
文章出处登录后可见!