• OpenFOAM类库介绍(三)隐式扩散项


    简介

    openFOAM中的扩散项(Laplacian)有显式和隐式两种,显式的Laplacian将返回一个场,而隐式的Laplacian将返回一个矩阵fvMatrix。在求解热传导问题时,我们采用隐式的Laplacian。
    ∇ ⋅ ( Γ ∇ T ) \nabla \cdot \left ( \Gamma \nabla T \right ) (Γ∇T)
    本文将根据笔者的理解,列出隐式的Laplacian所需的主要函数,并给出详细注释。

    fvmLaplacian函数

    隐式的Laplacian的主要代码是fvmLaplacian()函数,位于gaussLaplacianScheme.C文件内,该函数将完成所有计算流程,代码如下

    template<class Type, class GType>//泛型,Type是待求场vf的类型,GType是系数gamma的类型
     tmp<fvMatrix<Type>>//返回一个tmp临时对象,指向一个矩阵,如果求解这个矩阵就可以得到Laplacian方程的解
     gaussLaplacianScheme<Type, GType>::fvmLaplacian
     (
         const GeometricField<GType, fvsPatchField, surfaceMesh>& gamma,//gamma,是一个储存在单元面上的场
         const GeometricField<Type, fvPatchField, volMesh>& vf//待求场vf,是一个储存在单元中心的场
     )
     {
         const fvMesh& mesh = this->mesh();//获取网格
     
         const surfaceVectorField Sn(mesh.Sf()/mesh.magSf());//计算单元面单位法向量场,这个场储存在单元面上
     
         const surfaceVectorField SfGamma(mesh.Sf() & gamma);//计算单元面矢量与gamma的内积,这个场储存在单元面上
         const GeometricField<scalar, fvsPatchField, surfaceMesh> SfGammaSn
         (
             SfGamma & Sn//计算SfGamma与Sn的内积,得到标量场。事实上就是面积乘以gamma
         );
         const surfaceVectorField SfGammaCorr(SfGamma - SfGammaSn*Sn);//计算两个向量场的差值,但是根据实际计算,SfGammaCorr几乎处处为0,不知有何意义。如有知晓,请在评论区留言!
     
         tmp<fvMatrix<Type>> tfvm = fvmLaplacianUncorrected
         (//调用fvmLaplacianUncorrected函数完成矩阵构造,这个函数将在下文解释
             SfGammaSn,
             this->tsnGradScheme_().deltaCoeffs(vf),//deltaCoeffs是1/delta,作
             vf
         );
         fvMatrix<Type>& fvm = tfvm.ref();//拿取矩阵的引用
     
         tmp<GeometricField<Type, fvsPatchField, surfaceMesh>> tfaceFluxCorrection
             = gammaSnGradCorr(SfGammaCorr, vf);//调用gammaSnGradCorr函数来计算面修正量,由于SfGammaCorr是0,不知有何意义。如有知晓,请在评论区留言!
     
         if (this->tsnGradScheme_().corrected())//判断是否需要非正交修正
         {
             tfaceFluxCorrection.ref() +=
                 SfGammaSn*this->tsnGradScheme_().correction(vf);//使用correction函数完成非正交修正,并将修正量叠加在tfaceFluxCorrection上
         }
     
         fvm.source() -= mesh.V()*fvc::div(tfaceFluxCorrection())().primitiveField();//使用面积分的方式,将tfaceFluxCorrection转换到每个单元的源项
     
         if (mesh.fluxRequired(vf.name()))
         {
             fvm.faceFluxCorrectionPtr() = tfaceFluxCorrection.ptr();
         }
     
         return tfvm;//返回矩阵
     }
    
    • 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
    • 45

    openfoamwiki中给出了一些解释
    代码中的deltaCoeffs仅与网格有关,作为矩阵系数的一部分
    上述代码中调用了三个子函数:fvmLaplacianUncorrectedgammaSnGradCorrcorrected。接下来将依次介绍

    fvmLaplacianUncorrected函数

    fvmLaplacianUncorrected函数可以构造非正交修正前的矩阵,在阅读代码之前应该了解openFOAM矩阵的存储格式LDU,fvmLaplacianUncorrected的代码如下:

    template<class Type, class GType>
     tmp<fvMatrix<Type>>
     gaussLaplacianScheme<Type, GType>::fvmLaplacianUncorrected
     (
         const surfaceScalarField& gammaMagSf,//gamma*S的标量场,数据储存在面心
         const surfaceScalarField& deltaCoeffs,//系数,数据储存在面心
         const GeometricField<Type, fvPatchField, volMesh>& vf//待求场,数据储存在体心
     )
     {
         tmp<fvMatrix<Type>> tfvm//新建一个矩阵
         (
             new fvMatrix<Type>
             (
                 vf,
                 deltaCoeffs.dimensions()*gammaMagSf.dimensions()*vf.dimensions()
             )
         );
         fvMatrix<Type>& fvm = tfvm.ref();//获取矩阵的指针
         
         //内部面对矩阵的贡献
         fvm.upper() = deltaCoeffs.primitiveField()*gammaMagSf.primitiveField();//上三角元素,每个面上deltaCoeffs与gammaMagSf的乘积。上下三角是对称的。
         fvm.negSumDiag();//计算对角线元素,同一行非对角线元素的相反数
    
    	//边界面对矩阵的贡献
         forAll(vf.boundaryField(), patchi)//遍历每一个patch
         {
             const fvPatchField<Type>& pvf = vf.boundaryField()[patchi];//待求场的边界条件
             const fvsPatchScalarField& pGamma = gammaMagSf.boundaryField()[patchi];//在边界上gamma*S的标量场
             const fvsPatchScalarField& pDeltaCoeffs =
                 deltaCoeffs.boundaryField()[patchi];//在边界上的deltaCoeffs场
     
             if (pvf.coupled())//如果是耦合边界条件(特殊情况)
             {
                 fvm.internalCoeffs()[patchi] =
                     pGamma*pvf.gradientInternalCoeffs(pDeltaCoeffs);//计算边界条件给对角线元素的贡献
                 fvm.boundaryCoeffs()[patchi] =
                    -pGamma*pvf.gradientBoundaryCoeffs(pDeltaCoeffs);//计算边界条件给源项(右端项)的贡献
             }
             else//如果不是耦合边界条件(更一般的情况)
             {
                 fvm.internalCoeffs()[patchi] = pGamma*pvf.gradientInternalCoeffs();//计算边界条件给对角线元素的贡献
                 fvm.boundaryCoeffs()[patchi] = -pGamma*pvf.gradientBoundaryCoeffs();//计算边界条件给源项(右端项)的贡献
             }
         }
     
         return tfvm;//返回矩阵
     }
    
    • 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
    • 45
    • 46
    • 47

    需要指出的是,边界条件正式通过gradientInternalCoeffs()gradientBoundaryCoeffs()引入矩阵的。前者会在对角线元素的基础上添加边界贡献,后者会在右端项的基础上添加边界贡献。不同的边界条件计算公式不同,这里列出了Dirichlet边界条件的代码:

    template<class Type>
     Foam::tmp<Foam::Field<Type>>
     Foam::fixedValueFvPatchField<Type>::gradientInternalCoeffs() const
     {
         return -pTraits<Type>::one*this->patch().deltaCoeffs();
     }
     
     
     template<class Type>
     Foam::tmp<Foam::Field<Type>>
     Foam::fixedValueFvPatchField<Type>::gradientBoundaryCoeffs() const
     {
         return this->patch().deltaCoeffs()*(*this);
     }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    前者翻译成数学公式为(deltaCoeffs用 C o Co Co表示)
    − C o -Co Co
    后者翻译成数学公式为(边界自变量用 T b T_b Tb表示)
    C o ⋅ T b Co\cdot T_b CoTb

    gammaSnGradCorr函数

    调用gammaSnGradCorr函数来计算面修正量,由于SfGammaCorr是0,不知有何意义。如有知晓,请在评论区留言!

     template<class Type, class GType>
     tmp<GeometricField<Type, fvsPatchField, surfaceMesh>>//返回一个场,数据储存在面心
     gaussLaplacianScheme<Type, GType>::gammaSnGradCorr
     (
         const surfaceVectorField& SfGammaCorr,//一个矢量场,数据储存在面心
         const GeometricField<Type, fvPatchField, volMesh>& vf//待求的场
     )
     {
         const fvMesh& mesh = this->mesh();//提取网格
     
         tmp<GeometricField<Type, fvsPatchField, surfaceMesh>> tgammaSnGradCorr
         (//新建一个场,用于返回数据
             GeometricField<Type, fvsPatchField, surfaceMesh>::New
             (
                 "gammaSnGradCorr("+vf.name()+')',
                 mesh,
                 SfGammaCorr.dimensions()
                *vf.dimensions()*mesh.deltaCoeffs().dimensions()
             )
         );
     
         for (direction cmpt = 0; cmpt < pTraits<Type>::nComponents; cmpt++)
         {
             tgammaSnGradCorr.ref().replace
             (
                 cmpt,
                 fvc::dotInterpolate(SfGammaCorr, fvc::grad(vf.component(cmpt)))//将vf求梯度后插值到单元面上,再与SfGammaCorr点乘
             );
         }
     
         return tgammaSnGradCorr;//返回场
     }
    
    • 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

    corrected函数

    corrected并不在gaussLaplacianScheme.C文件内,而在correctedSnGrad.C文件内,用于计算非正交导致的通量,代码如下

    template<class Type>
     Foam::tmp<Foam::GeometricField<Type, Foam::fvsPatchField, Foam::surfaceMesh>>
     Foam::fv::correctedSnGrad<Type>::correction
     (
         const GeometricField<Type, fvPatchField, volMesh>& vf
     ) const
     {
         const fvMesh& mesh = this->mesh();//提取网格
     
         // 构造一个场,数据储存在面心
         tmp<GeometricField<Type, fvsPatchField, surfaceMesh>> tssf
         (
             GeometricField<Type, fvsPatchField, surfaceMesh>::New
             (
                 "snGradCorr("+vf.name()+')',
                 mesh,
                 vf.dimensions()*mesh.nonOrthDeltaCoeffs().dimensions()
             )
         );
         GeometricField<Type, fvsPatchField, surfaceMesh>& ssf = tssf.ref();
     
         for (direction cmpt = 0; cmpt < pTraits<Type>::nComponents; cmpt++)
         {
             ssf.replace
             (
                 cmpt,
                 correctedSnGrad<typename pTraits<Type>::cmptType>(mesh)
                .fullGradCorrection(vf.component(cmpt))//对vf的每个分量调用fullGradCorrection子函数
             );
         }
     
         return tssf;
     }
    
    • 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

    fullGradCorrection子函数的定义如下:

     template<class Type>
     Foam::tmp<Foam::GeometricField<Type, Foam::fvsPatchField, Foam::surfaceMesh>>
     Foam::fv::correctedSnGrad<Type>::fullGradCorrection
     (
         const GeometricField<Type, fvPatchField, volMesh>& vf
     ) const
     {
         const fvMesh& mesh = this->mesh();
     
         // 构造一个场,数据储存在面心
         tmp<GeometricField<Type, fvsPatchField, surfaceMesh>> tssf =
             linear<typename outerProduct<vector, Type>::type>(mesh).dotInterpolate
             (//对于每个面:将vf的梯度线性插值到面心后与网格的非正交矢量作乘积
                 mesh.nonOrthCorrectionVectors(),
                 gradScheme<Type>::New
                 (
                     mesh,
                     mesh.gradScheme("grad(" + vf.name() + ')')
                 )().grad(vf, "grad(" + vf.name() + ')')
             );
         tssf.ref().rename("snGradCorr(" + vf.name() + ')');
     
         return tssf;//返回场
     }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    最终,corrected得到了每个面上的通量 g r a d ( T ) ∗ t grad(T)*t grad(T)t,t是非正交修正矢量,仅与网格有关,在构建网格时已经生成。

    总结

    隐式的Laplacian主要流程为fvmLaplacian函数,而其中又包含三个子函数fvmLaplacianUncorrectedgammaSnGradCorrcorrected。最主要的计算工作(矩阵合成)由fvmLaplacianUncorrected承担,非正交修正由corrected承担。

  • 相关阅读:
    畅捷通T+任意文件上传(CNVD-2022-60632 )漏洞复现
    【毕业设计】单片机 火灾智能报警系统 - 嵌入式 物联网
    使用qrcode生成指定内容的二维码并在GUI界面显示
    MySQL日常使用记录
    收集灵感都有哪些网站推荐?
    promise详解
    【凸优化学习笔记2】仿射集、凸集
    关于JavaScript的Object所有方法
    如何辨认是否是高防服务器?
    Java 8 stream的详细用法
  • 原文地址:https://blog.csdn.net/weixin_43325228/article/details/126329672