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. pRefCell的选择将修改压力修正方程矩阵对应的系数

pRefCell的选择将修改压力修正方程矩阵对应的系数

已定时 已固定 已锁定 已移动 OpenFOAM
3 帖子 2 发布者 66 浏览
  • 从旧到新
  • 从新到旧
  • 最多赞同
回复
  • 在新帖中回复
登录后回复
此主题已被删除。只有拥有主题管理权限的用户可以查看。
  • A 在线
    A 在线
    AppleKiller
    写于 最后由 编辑
    #1

    各位Foamer好,在探究压力修正方程矩阵系数的过程中,发现一个有意思的点,从而产生一个疑问。
    10d5ab8a-53bf-4f2d-a902-8d5f756fb23d-image.png
    本人使用SimpleFoam计算方腔流,网格为3*3(这样好看矩阵的系数),如下加入的代码为压力修正方程系数的输出:

            fvScalarMatrix pEqn
            (
                fvm::laplacian(rAtU(), p)  == fvc::div(phiHbyA)
            );
    
            pEqn.setReference(pRefCell, pRefValue);
    
    /////////////////////////////////////////////////////////////////////////////////////   
    /* */
    	std::vector<std::vector<double>> P(mesh.nCells(), std::vector<double>(mesh.nCells(), 0.0));
    
        for(label i=0; i<mesh.nCells(); i++)
        {
           P[i][i] = pEqn.diag()[i];
        }
    
        for(label faceI=0; faceI<pEqn.lduAddr().lowerAddr().size(); faceI++)
        {
            label l = pEqn.lduAddr().lowerAddr()[faceI];
            label u = pEqn.lduAddr().upperAddr()[faceI];
            P[l][u] = pEqn.upper()[faceI];
            P[u][l] = pEqn.lower()[faceI];
        }
    
    
        Info << "P before boundary influence:\n" << endl;
    
        for (int i = 0; i < mesh.nCells(); i++)
        {
            Info << "[";
            for (int j = 0; j < mesh.nCells(); j++)
            {
                Info << P[i][j] << " ";
            }
            Info <<"]"<< endl;
        }
        Info << endl;
    

    有意思的现象是 当我的pRefCell取网格单元0时,其矩阵系数如下
    70086477-6f19-4825-bb45-9d1e85ced442-image.png
    0号网格单元的对应的对角矩阵系数等于其余影响的单元系数负数之和的两倍(-5.7037 = -2*(1.42593+1.42593))
    但是其余的网格单元对应的对角矩阵系数等于其余影响的单元系数之负和

    如果将pRefCell选为1时,那么矩阵系数如下
    9adff1ca-4dd1-4994-813d-f64b5ee96906-image.png
    有意思的点来了,1号网格单元的对应的对角矩阵系数等于其余影响的单元系数负数之和的两倍(-9.2037 = -2*(1.42593+1.42593+1.75))
    其余的网格单元对应的对角矩阵系数等于其余影响的单元系数之负和

    有大佬知道为什么这么处理吗?

    1 条回复 最后回复
  • X 在线
    X 在线
    xpqiu 超神
    写于 最后由 编辑
    #2

    首先,对于压力方程,因为其只有 laplacian项,所以可以推导出来这样的压力方程的系数满足 “网格单元对应的对角矩阵系数等于其余影响的单元系数之负和” 这个特点。
    其次,我们来看 setReference 这个函数做了什么。查看 fvMatrix 类中 setReference 函数的实现,如下:

    template<class Type>
    void Foam::fvMatrix<Type>::setReference
    (
        const label celli,
        const Type& value,
        const bool forceReference
    )
    {
        if ((forceReference || psi_.needReference()) && celli >= 0)
        {
            source()[celli] += diag()[celli]*value;
            diag()[celli] += diag()[celli];
        }
    }
    

    可以看到,这个函数确实是把对应网格的对角元素值翻倍了。 setReference 函数的目的是把指定网格(对应参数 celli)的值设置为指定的值(对应参数 value),可以为什么这样实现就可以达到这个目的呢?原理如下:

    在不可压缩流体计算中,我们求解的是压力泊松方程(Pressure Poisson Equation),它通常形如:
    $$ \nabla \cdot (\frac{1}{A_p} \nabla p) = \nabla \cdot (\frac{H(\mathbf{U})}{A_p}) $$
    这里 p 是压力,A_p 是速度方程离散后中心网格的系数,H(U) 是一个与速度相关的项。

    当我们将这个方程离散化后,会得到一个大型的线性方程组:
    $$ \mathbf{A} \mathbf{p} = \mathbf{b} $$
    其中 A 是系数矩阵,p 是我们要求解的、包含所有网格压力的向量,b 是源项向量。

    关键问题:如果计算域的所有边界都是第二类边界条件(Neumann BC),比如 zeroGradient(法向梯度为零),这意味着我们只规定了压力的梯度,而没有规定压力的绝对值。

    从物理上讲,不可压缩流的压力是相对的。如果 p 是一个有效的解,那么 p + C(其中 C 是任意常数)也同样是一个有效的解,因为它不会改变压力的梯度(∇(p+C) = ∇p)。

    因此,我们必须通过指定一个点的压力值来消除这种不确定性,为整个压力场提供一个“锚点”或“基准”。
    setReference 的目标是强制让某个特定网格 celli 的压力 p[celli] 在求解后等于我们设定的参考值 value。
    也就是说,我们想让方程组的解满足:
    $$ p_{\text{celli}} = \text{value} $$

    3. 代码实现与数学原理剖析

    我们再来看这两行代码,并结合线性方程组 Ap=b 在第 celli 行的具体形式来分析。

    对于网格 celli,其对应的线性方程(未修改前)是:
    $$ A_{ii} p_i + \sum_{j \in N(i)} A_{ij} p_j = b_i $$
    其中:

    • i 就是 celli
    • p_i 是网格 i 的压力
    • A_{ii} 是对角线元素,对应 OpenFOAM 代码中的 diag()[celli]
    • A_{ij} 是非对角线元素,表示网格 i 与其邻居网格 j 的相互影响
    • b_i 是源项,对应 source()[celli]

    现在我们来分析这两行代码对这个方程做了什么修改:

    核心代码:

    source()[celli] += diag()[celli]*value;
    diag()[celli] += diag()[celli];
    

    第一步:source()[celli] += diag()[celli]*value;

    这行代码修改了源项 b_i。修改后的新源项 b'_i 为:
    $$ b_i^{'} = b_i + A_{ii} \cdot \text{value} $$
    此时,方程变为:
    $$ A_{ii} p_i + \sum_{j \in N(i)} A_{ij} p_j = b_i + A_{ii} \cdot \text{value} $$

    第二步:diag()[celli] += diag()[celli];

    这行代码修改了对角线元素 A_{ii}。修改后的新对角线元素 A'_{ii} 为:
    $$ A_{ii}^{\prime} = A_{ii} + A_{ii} = 2A_{ii} $$
    现在,第 i 行的方程最终变成了:
    $$ (2A_{ii}) p_i + \sum_{j \in N(i)} A_{ij} p_j = b_i + A_{ii} \cdot \text{value} $$

    让我们来整理一下这个修改后的方程。我们可以把 (2A_{ii}) p_i 拆开:
    $$ (A_{ii} p_i + \sum_{j \in N(i)} A_{ij} p_j) + A_{ii} p_i = b_i + A_{ii} \cdot \text{value} $$

    也就是说,当调用了 setReference 函数后,求解的线性方程组发生了变化。通过求解这个修改后的方程,能让方程趋于收敛到 $p_i = \text{value}$ 这个解,此时
    $$ A_{ii} p_i = A_{ii} \cdot \text{value} $$
    因此也会使得修改前的
    $$ A_{ii} p_i + \sum_{j \in N(i)} A_{ij} p_j \approx b_i $$
    也同时成立。

    通过这样就实现了让指定的网格的解等于指定值,同时又不违反从 Laplacian 项离散后得到的方程。

    A 1 条回复 最后回复
  • A 在线
    A 在线
    AppleKiller
    在 中回复了 xpqiu 最后由 编辑
    #3

    @xpqiu 谢谢老哥

    1 条回复 最后回复

  • 登录

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