Skip to content
  • 最新
  • 版块
  • 东岳流体
  • 随机看[请狂点我]
皮肤
  • Light
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • 默认(不使用皮肤)
  • 不使用皮肤
折叠
CFD中文网

CFD中文网

  1. CFD中文网
  2. OpenFOAM
  3. 关于negSumDiag()的一个问题

关于negSumDiag()的一个问题

已定时 已固定 已锁定 已移动 OpenFOAM
11 帖子 4 发布者 10.2k 浏览
  • 从旧到新
  • 从新到旧
  • 最多赞同
回复
  • 在新帖中回复
登录后回复
此主题已被删除。只有拥有主题管理权限的用户可以查看。
  • M 离线
    M 离线
    mengweilm425
    写于2016年10月5日 17:53 最后由 李东岳 编辑 2020年3月21日 06:08
    #1

    在看关于negSumDiag()的代码的时候遇到一个问题。diag上面的项是由该“行”上面的的off-diag的项相加后取负值得到。为什么下面的代码感觉像是把该“列”的数相加了呢?

    template<class Type, class DType, class LUType>
    void Foam::LduMatrix<Type, DType, LUType>::negSumDiag()
    {
    const Field<LUType>& Lower = const_cast<const LduMatrix&>(*this).lower();
    const Field<LUType>& Upper = const_cast<const LduMatrix&>(*this).upper();
    Field<DType>& Diag = diag();
    const unallocLabelList& l = lduAddr().lowerAddr();
    const unallocLabelList& u = lduAddr().upperAddr();
    for (label face=0; face<l.size(); face++)
    {
    Diag[l[face]] -= Lower[face];
    Diag[u[face]] -= Upper[face];
    }
    }

    0_1475689903884_Capture.JPG

    以这个网格为例

    0_1475690047966_Capture.JPG
    lowerAdd 为 (0 1 2 1 2 3 4 3 )
    upperAdd 为 (2 2 3 6 4 5 6 5 )

    矩阵的形式为

    0_1475689800365_Capture2.JPG

    那么Diag[0] 应该是由 Upper[1]得到。这个和上面的代码不符。如果矩阵是对称的,这个计算没有问题。而如果是 非对称的,是不是就有问题了?

    谢谢大家!

    1 条回复 最后回复
  • M 离线
    M 离线
    mengweilm425
    写于2016年10月5日 19:34 最后由 李东岳 编辑 2020年3月21日 06:09
    #2

    为了进一步测试,建立一个3X3的网格,并创建一个fvMatrix。

            fvVectorMatrix UEqn_lap
            (
              fvm::laplacian(nu, U)
            );
    

    网格编号如下:

    |6|7|8|
    |3|4|5|
    |0|1|2|
    

    输出 upper 和lower 的address,没有问题

            Info<< "UEqn_lap upper add before sum   " << UEqn_lap.lduAddr().upperAddr() << nl << endl;
            Info<< "UEqn_lap lower add before sum   " << UEqn_lap.lduAddr().lowerAddr() << nl << endl;
    UEqn_lap upper add before sum
    12
    (
    1
    3
    2
    4
    5
    4
    6
    5
    7
    8
    7
    8
    )
    
    
    UEqn_lap lower add before sum
    12
    (
    0
    0
    1
    1
    2
    3
    3
    4
    4
    5
    6
    7
    )
    

    下面把upper lower 和diag重新赋值 并输出

            UEqn_lap.upper() = 100;
            UEqn_lap.diag() = 0;
            UEqn_lap.lower() = 1;
    
            Info<< "UEqn_lap diag after change   " << UEqn_lap.diag() << nl << endl;
            Info<< "UEqn_lap upper after change   " << UEqn_lap.upper() << nl << endl;
            Info<< "UEqn_lap lower after change   " << UEqn_lap.lower() << nl << endl;
    

    输出为:

    UEqn_lap diag after change   9{0}
    
    UEqn_lap upper after change   12{100}
    
    UEqn_lap lower after change   12{1}
    

    调用

            UEqn_lap.negSumDiag();
    
            Info<< "UEqn_lap diag after negSumDiag   " << UEqn_lap.diag() << nl << endl;
    

    输出结果为:

    UEqn_lap diag after negSumDiag   9(-2 -102 -101 -102 -202 -201 -101 -201 -200)
    

    我特意把upper里面的数值设的很大,从输出的结果看,diag 是把lower里面的数加和了,也就是把列中的数加和了。

    这个结果让我有点儿弄不明白。

    W 1 条回复 最后回复 2016年10月6日 01:58
  • W 离线
    W 离线
    wwzhao 超神
    在 2016年10月6日 01:58 中回复了 mengweilm425 最后由 编辑
    #3

    @mengweilm425 这个问题cfd-online上也有人讨论过,不过没有结果。我个人觉得这确实是OpenFOAM的bug,不过由于fvm离散后的系数矩阵的特殊性质,列元素相加和行元素相加得到的结果是一样的,因此这个bug不影响计算。有机会细说。

    M 1 条回复 最后回复 2016年10月6日 12:02
  • M 离线
    M 离线
    mengweilm425
    在 2016年10月6日 12:02 中回复了 wwzhao 最后由 编辑
    #4

    @wwzhao 谢谢您的回复!

    我找到了这个帖子

    http://www.cfd-online.com/Forums/openfoam-programming-development/72456-matrix-storage-diagonal-coeffients-calculation.html

    但是也没说清楚。不过系数矩阵是对称的话,确实不影响结果。

    1 条回复 最后回复
  • G 离线
    G 离线
    gyzhangqm 神
    写于2020年3月20日 10:13 最后由 李东岳 编辑 2020年3月21日 06:09
    #5

    这个问题困扰我了一天,但我解决了这个问题。
    首先,我们应该明白OpenFOAM中并没有存A[][]这样的系数矩阵,存的是diag,lower和upper这些非零的系数。
    其次,我们在看一些教程的时候,为了解释lduAddressing中的lower(), upper(), lowerAddr(), and upperAddr(),可能把lower解释成了
    the coefficient multiplying ϕowner in the algebraic equation for element neighbour,把upper解释成了
    the coefficient multiplying ϕneighbour in the algebraic equation for element owner.
    甚至有些教程给出了下面的解释

    A[upperAddr[k]][lowerAddr[k]]=lower[k]
    A[lowerAddr[k]][upperAddr[k]]=upper[k]
    

    如果这样理解的话,当然negSumDiag是对列求和的,这和我们认知里矩阵的一行代表一个方程相违背。有人强行解释对于扩散方程因为矩阵对称,虽有对行求和对列求和结果一致。

    但事实上出现这样认知的原因在于我们对lower和upper的理解出现的错误。Hrvoje Jasak在帖子中也说明了这点
    https://www.cfd-online.com/Forums/openfoam-programming-development/72456-matrix-storage-diagonal-coeffients-calculation.html

    经过对guassConvectionScheme的探究及对对流方程离散化的推导(原谅我尚未掌握这个论坛上使用公式排版的方法),正确的理解方式应该是:

    1. lower存的系数代表了以Neighbour为单元的代数方程中,Owner的物理量的贡献
    2. upper 存的系数代表了以Owner为单元的代数方程中,Neighbour的物理量的贡献

    并且根据对流方程离散化的推导,我们其实可以知道diag的系数,并非为该行off-diagonal系数的和。而应该为
    aC=−∑f:nb(C)aN+∑f:nb(C)m˙f
    基于规则正交网格的推导可以参考
    《The Finite Volume Method in Computational Fluid Dynamics An Advanced Introduction with OpenFOAM and Matlab》

    如果有时间,我会重新排版这条回复。有任何问题可以和我联系讨论。

    W 1 条回复 最后回复 2020年3月21日 08:00
  • 李 在线
    李 在线
    李东岳 管理员
    写于2020年3月20日 22:10 最后由 编辑
    #6

    感谢大佬们分享 :huahua:

    http://dyfluid.com/index.html
    需要帮助debug算例的看这个 https://cfd-china.com/topic/8018

    1 条回复 最后回复
  • W 离线
    W 离线
    wwzhao 超神
    在 2020年3月21日 08:00 中回复了 gyzhangqm 最后由 编辑
    #7

    @gyzhangqm

    aC=−∑f:nb(C)aN+∑f:nb(C)m˙f

    上面这个式子是不是漏了 ϕC 和 ϕN?m˙f 是不是指的 Ax=b 中的 b?

    我的理解应该是

    aCϕC=−∑f:nb(C)aNϕN+bC

    以上公式为计入边界条件后的表达式, bC 不为 0,且 aC 的值被边界条件影响。

    negSumDiag 只对 diag, lower 和 upper 做了操作,并没有计入边界条件的影响,所以其过程并不对应以上公式。

    1 条回复 最后回复
  • G 离线
    G 离线
    gyzhangqm 神
    写于2020年3月21日 14:33 最后由 编辑
    #8

    @wwzhao

    扩散方程比较容易理解。这里仅以对流方程为例,不考虑边界条件,不考虑网格扭曲的影响。下面是我的理解。

    仅考虑对流项
    ∇⋅(ρuϕ)=0
    将对控制单元O的体积分转化为面积分,f∼nb(O)为控制单元O的控制面,N∼NB(O)为控制单元O的的邻居单元
    ∑f∼nb(O)(ρuϕ)f⋅Sf=0

    定义
    m˙f=(ρu)f⋅Sf

    面心的物理量通过两侧体心物理量加权平均得到:
    ϕf=ϖϕO+(1−ϖ)ϕN
    上面公式整理后为:
    ∑f∼nb(O)(ρuϕ)f⋅Sf=∑f−nb(O)m˙fϕf=aOϕO+∑N∼NB(O)aNϕN=0

    其中
    aN=m˙f(1−ϖ)f
    aO=∑f∼nb(O)m˙fϖ=−∑N∼NB(O)aN+∑f∼nb(O)m˙f

    在gaussConvectionScheme<Type>::fvmDiv中

        fvm.lower() = -weights.primitiveField()*faceFlux.primitiveField();
        fvm.upper() = fvm.lower() + faceFlux.primitiveField();
        fvm.negSumDiag();
    

    可以看出,lower存的是 −ϖfm˙f,代表了以Neighbour为单元的代数方程中,Owner的物理量ϕO的贡献 ,upper 存的是m˙f(1−ϖ)f,代表了以Owner为单元的代数方程中,Neighbour的物理量ϕN的贡献 。这里所说的Neighbour和Owner,其实是立足于面的,Owner是该面左侧的单元,Neighbour是该面右侧的单元,Owner编号小于Neighbour编号。

    至于weights的具体计算,则根据具体的插值格式进行计算,对于Upwind、FROMM、QUICK,TVD等不同格式,分别有不同的计算方法,这里暂不讨论。

    这里有个trick,aN=m˙f(1−ϖ)f,为什么对于编号大于Owner的邻居单元,upper 存的是m˙f(1−ϖ)f,而对有编号小于Owner的邻居单元,lower存的是 −ϖfm˙f呢?这是因为计算面心的系数ϖ时在总是认为面左侧是Owner,右侧是Neighbour.

    最后是 fvm.negSumDiag()求和计算对角项的系数aO。这里我们并非按照公式aO=∑f∼nb(O)m˙fϖ,先对所有的cell循环,然后对每个cell的所有面循环,这样计算效率底下,非结构网格程序一般都是对所有的面进行循环,将其贡献分别计入两侧的控制体中。

        for (label face=0; face<l.size(); face++)
        {
            Diag[l[face]] -= Lower[face];
            Diag[u[face]] -= Upper[face];
        }
    

    这里是对列求和,而非对行求和。如果是对行求和的话,得到的是−∑N∼NB(O)aN。而对角项的系数aO=−∑N∼NB(O)aN+∑f∼nb(O)m˙f.

    那么最后的问题就是为什么对列求和得到的是正确的aO=−∑N∼NB(O)aN+∑f∼nb(O)m˙f,这是巧合么?并不是巧合,而是必然。仔细想想这些系数所代表的含义,

    1. lower存的系数代表了控制面右侧的单元(Neighbour)的代数方程中,控制面左侧单元(Owner)的物理量的贡献
    2. upper 存的系数代表了控制面左侧单元(Owner)的代数方程中,控制面右侧的单元(Neighbour)的物理量的贡献

    另一方面在Aϕ=b中

    1. 矩阵A中的第i行的非对角项系数,其实代表了与编号为i的单元相邻的单元物理量对i的单元控制方程的贡献。
    2. 矩阵A中的第j列的非对角项系数,其实代表了编号为j的单元对其相邻的单元控制方程的贡献。

    对于对流项,单元i对单元j的影响不等于单元j对单元i的影响,但是,编号为j的单元对所有与其相邻的单元的贡献的总和,正应该是j的单元自身系数的相反数。也正是矩阵A中的第j列的非对角项系数之和的相反数。

    以上理解也只是我个人根据guassConvectionScheme.C文件的自我解读。如果哪里有理解错的地方,还请指正。

    W 1 条回复 最后回复 2020年3月23日 19:27
  • W 离线
    W 离线
    wwzhao 超神
    在 2020年3月23日 19:27 中回复了 gyzhangqm 最后由 编辑
    #9

    @gyzhangqm

    很精彩的推导!解开了我一直以来的疑惑。

    不过有一点我不认同:

    这里有个trick,aN=m˙f(1−ϖ)f,为什么对于编号大于Owner的邻居单元,upper 存的是m˙f(1−ϖ)f,而对有编号小于Owner的邻居单元,lower存的是 −ϖfm˙f呢?这是因为计算面心的系数ϖ时在总是认为面左侧是Owner,右侧是Neighbour.

    这里的trick(正负号)应该是求flux的时候设置的,而不是求weight。

    我换个说法理一下思路:

    对于单元 P,有 k 个面,也就有 k 个邻居单元,记为 N1 ... Nk。

    每个面心的量通过面的 owner 和 neighbour 单元体心插值得到(注意这里的 owner、neighour 和上面的 P、N 完全不相关,为了避免歧义,上面的单元用 P 而不是用 O 表示)

    ϕf=ϖϕown+(1−ϖ)ϕnei

    于是对流项的积分变为

    ∑f=1k(ρuϕ)f⋅Sf=∑f=1km˙fϕf

    上式中的 m˙ 为质量通量,这个是由速度点乘面法向得到的。具体操作为 fvc::flux(U),也就是 mesh.Sf() 和 U 的点乘。Sf() 是一个矢量场,它的方向很有讲究。它被规定为从 own 指向 nei。而高斯定理的 Sf 规定方向指向封闭单元外。这两个定义是由矛盾的。因此需要上面的trick来调整正负号。

    先看非对角元素。遍历 P 的所有面单元:

    • 若该面单元的 own 为 P, nei 为 N,Sf() 与 Sf 同方向
      这种情况下,N 的系数在 upper,因此 upper 需要加上 m˙(1−ϖ)。同时 P 的主对角元素加上 m˙ϖ。
    • 若该面单元的 nei 为 P,own 为 N,Sf() 与 Sf 反方向
      这种情况下,N 的系数在 lower,原本 lower 需要加上 m˙ϖ,调整负号后,最终加上的是 −m˙ϖ。同时 P 的主对角元素加上 −m˙(1−ϖ)。

    以上过程对应以下两行代码:

        fvm.lower() = -weights.primitiveField()*faceFlux.primitiveField();
        fvm.upper() = fvm.lower() + faceFlux.primitiveField();
    

    再看主对角元素。

    对于邻居单元 N ,在计算其对应行的非对角系数时,所用插值公式和 P 相同,即还是

    ϕf=ϖϕown+(1−ϖ)ϕnei

    也就是说以上公式要用两次,分别是在 P 和 N 作为主对角元素的行上使用。

    在循环到 N 与 P 共享的面单元时:

    • 若该面单元的 own 为 P, nei 为 N,Sf()方向为 P->N ,Sf 方向为 N->P,二者反方向
      这种情况下,P 的系数为 −m˙ϖ。
    • 若该面单元的 nei 为 P,own 为 N,Sf() 方向为 N->P, Sf 方向也为 N->P,二者同方向
      这种情况下,P 的系数为 m˙(1−ϖ) 。

    注意以上两种情况,P 作为 N 的非对角系数和作为其本身的主对角系数正好是相反数,且处于同一列上。由于遍历一次面单元将所有网格单元(包括P和其所有邻居单元)的非对角元素都计算好了,为了避免重复计算,所以直接用该列的非对角元素加和并取相反数得到主对角元素的值。

    即对应代码:

        fvm.negSumDiag();
    

    也就是说 negSumDiag 计算的确实是列,而不是行。我之前会由系数矩阵对称的错觉是因为采用的验证算例都是采用线性插值计算面心物理量,这使得插值系数 ϖ=0.5,正好造成系数矩阵反对称的假象。

    G 1 条回复 最后回复 2020年3月24日 05:22
  • 李 在线
    李 在线
    李东岳 管理员
    写于2020年3月23日 23:50 最后由 李东岳 编辑 2020年3月24日 08:12
    #10

    插一句:我觉得CFD教材应该把结构网格简化,着重讨论非结构网格。另外, @wwzhao 的讨论把P,N省略的替换为通量正负看起来更简洁,因为已经有own与nei的标识了。

    不管怎样,:papa: 惊为天人 大佬们继续!!

    http://dyfluid.com/index.html
    需要帮助debug算例的看这个 https://cfd-china.com/topic/8018

    1 条回复 最后回复
  • G 离线
    G 离线
    gyzhangqm 神
    在 2020年3月24日 05:22 中回复了 wwzhao 最后由 编辑
    #11

    @wwzhao

    是的,OpenFOAM中面的方向定义永远是从小编号指向大编号,Gauss定理中面的方向指向外。也许把这些面分为大面和小面区分一下更容易理解。

    对于编号大于P的单元U,他们之间面上的量为:

    ϕf=ϖϕP+(1−ϖ)ϕU

    m˙f=(ρu)f⋅Sf

    对于编号小于P的单元L,他们之间面上的量为:

    ϕf=(1−ϖ)ϕP+ϖϕL

    m˙f=−(ρu)f⋅Sf

    ∑f∼nb(P)(ρuϕ)f⋅Sf=∑N∈L(P)(−m˙fϕf)+∑N∈U(P)m˙fϕf
    =∑N∈L(P)−m˙f[(1−ϖ)ϕP+ϖϕN]+∑N∈U(P)m˙f[ϖϕP+(1−ϖ)ϕN]

    =(∑N∈L(P)−m˙f(1−ϖ)+∑N∈U(P)m˙fϖ)ϕP+∑N∈L(P)−m˙fϖϕN+∑N∈U(P)m˙f(1−ϖ)ϕN

    这样可以看出lower存的是 −ϖfm˙f, upper存的是m˙f(1−ϖ)f.并且diag存的是
    aP=−[∑N∈L(P)m˙f(1−ϖ)+∑N∈U(P)(−m˙fϖ)]

    1 条回复 最后回复
2016年10月5日 17:53

5/11

2020年3月20日 10:13

未读 6
2020年3月24日 05:22
  • 登录

  • 登录或注册以进行搜索。
5 / 11
  • 第一个帖子
    5/11
    最后一个帖子
0
  • 最新
  • 版块
  • 东岳流体
  • 随机看[请狂点我]