OpenFoam中的Simple系列算法

简介

Simple系列算法是求解不可压缩流的主要工具。OpenFoam中提供了SimpleFoam求解器、PisoFoam求解器以完成不可压缩流的求解。

但是OpenFoam中的代码与流体力学教材中的讲述并不一致,导致初学者难以理解代码的含义。笔者推荐一篇Martínez撰写的文章《Influence of momentum interpolation methods on the accuracy and convergence of pressure–velocity coupling algorithms in OpenFOAM》,该文章详细地推导了Simple算法、SimpleC算法和Piso算法,更重要的是其最终形式就是OpenFoam所采用的形式

本文将直接使用Martínez的推导结果,将结果与OpenFoam中的代码对应,方便初学者理解代码含义。

Simple算法

文中公式(40)表示使用Rhie-Chow插值得到的面心速度
u e m = α u ( ∑ N B A N B u N B ∗ + B P ) e ( A P ) e − α u Δ y ( p E m − p P m ) ( A P ) e u_{e}^{m}=\frac{\alpha_{u}\left(\sum_{N B} A_{N B} u_{N B}^{*}+B_{P}\right)_{e}}{\left(A_{P}\right)_{e}}-\frac{\alpha_{u} \Delta y\left(p_{E}^{m}-p_{P}^{m}\right)}{\left(A_{P}\right)_{e}} uem=(AP)eαu(NBANBuNB+BP)e(AP)eαuΔy(pEmpPm)面心速度是用来构造连续方程的。考虑不可压缩流,对于任意一个单元,进出各面心的流量之和必然为0。所以针对一个单元,对上式求和(面积加权),即可得到压力方程
∑ S f α u Δ y ( p E m − p P m ) ( A P ) f = ∑ S f α u ( ∑ N B A N B u N B ∗ + B P ) f ( A P ) f \sum {{S_f}{{{\alpha _u}\Delta y\left( {p_E^m - p_P^m} \right)} \over {{{\left( {{A_P}} \right)}_f}}}} = \sum {{S_f}{{{\alpha _u}{{\left( {\sum\limits_{NB} {{A_{NB}}} u_{NB}^* + {B_P}} \right)}_f}} \over {{{\left( {{A_P}} \right)}_f}}}} Sf(AP)fαuΔy(pEmpPm)=Sf(AP)fαu(NBANBuNB+BP)f
现在看看SimpleFoam求解器中的代码

fvm::laplacian(rAtU(), p) == fvc::div(phiHbyA)

代码与公式是严格对应的,rAtU是动量方程对角矩阵Ap的倒数。phiHbyA是H矩阵与Ap之商在面心插值后的结果。总之,代码与公式是严格对应的。

SimpleC算法

文中公式(48)是使用SimpleC算法和Rhie-Chow插值得到的面心速度
u e m = α u ( ∑ N B A N B u N B ∗ + B P ) e ( A P ) e − α u ( 1 ( A P ) e − 1 ( A P − ∑ N B A N B ) e ) Δ y ( p E m − 1 − p P m − 1 ) − α u Δ y ( p E m − p P m ) ( A P − ∑ N B A N B ) e u_{e}^{m}=\frac{\alpha_{u}\left(\sum_{N B} A_{N B} u_{N B}^{*}+B_{P}\right)_{e}}{\left(A_{P}\right)_{e}}-\alpha_{u}\left(\frac{1}{\left(A_{P}\right)_{e}}-\frac{1}{\left(A_{P}-\sum_{N B} A_{N B}\right)_{e}}\right) \Delta y\left(p_{E}^{m-1}-p_{P}^{m-1}\right)-\frac{\alpha_{u} \Delta y\left(p_{E}^{m}-p_{P}^{m}\right)}{\left(A_{P}-\sum_{N B} A_{N B}\right)_{e}} uem=(AP)eαu(NBANBuNB+BP)eαu((AP)e1(APNBANB)e1)Δy(pEm1pPm1)(APNBANB)eαuΔy(pEmpPm)
同样对面求和之后有
∑ S f α u Δ y ( p F m − p P m ) ( A P − ∑ N B A N B ) f = ∑ S f ( α u ( ∑ N B A N B u N B ∗ + B P ) f ( A P ) f − α u ( 1 ( A P ) f − 1 ( A P − ∑ N B A N B ) f ) Δ y ( p F m − 1 − p P m − 1 ) ) \sum {{S_f}{{{\alpha _u}\Delta y\left( {p_F^m - p_P^m} \right)} \over {{{\left( {{A_P} - \sum\limits_{NB} {{A_{NB}}} } \right)}_f}}}} = \sum {{S_f}\left( {{{{\alpha _u}{{\left( {\sum\limits_{NB} {{A_{NB}}} u_{NB}^* + {B_P}} \right)}_f}} \over {{{\left( {{A_P}} \right)}_f}}} - {\alpha _u}\left( {{1 \over {{{\left( {{A_P}} \right)}_f}}} - {1 \over {{{\left( {{A_P} - \sum\limits_{NB} {{A_{NB}}} } \right)}_f}}}} \right)\Delta y\left( {p_F^{m - 1} - p_P^{m - 1}} \right)} \right)} Sf(APNBANB)fαuΔy(pFmpPm)=Sf (AP)fαu(NBANBuNB+BP)fαu (AP)f1(APNBANB)f1 Δy(pFm1pPm1)
现在看看SimpleFoam求解器中的代码
SimpleC在Simple算法的基础上对一些系数做了修改,如下

if (simple.consistent())
{
    rAtU = 1.0/(1.0/rAU - UEqn.H1());
    phiHbyA +=
        fvc::interpolate(rAtU() - rAU)*fvc::snGrad(p)*mesh.magSf();
    HbyA -= (rAU - rAtU())*fvc::grad(p);
}

rAtU 变成了 1 ( A P − ∑ N B A N B ) f {1 \over {{{\left( {{A_P} - \sum\limits_{NB} {{A_{NB}}} } \right)}_f}}} (APNBANB)f1,phiHbyA变成了更复杂的形式。
压力泊松方程依然没有变化

fvm::laplacian(rAtU(), p) == fvc::div(phiHbyA)

代码与公式是严格对应的。

Piso算法

文中公式(55)是使用Piso算法和Rhie-Chow插值得到的面心速度
u e m = α u ( ∑ N B A N B u N B ∗ ∗ + B P ) e ( A P ) e − α u Δ y ( p E m − p P m ) ( A P ) e u_{e}^{m}=\frac{\alpha_{u}\left(\sum_{N B} A_{N B} u_{N B}^{* *}+B_{P}\right)_{e}}{\left(A_{P}\right)_{e}}-\frac{\alpha_{u} \Delta y\left(p_{E}^{m}-p_{P}^{m}\right)}{\left(A_{P}\right)_{e}} uem=(AP)eαu(NBANBuNB∗∗+BP)e(AP)eαuΔy(pEmpPm)
代码与公式之间是严格对应的。

事实上,Piso算法与Simple算法的压力修正方程并无区别。在OpenFoam中,Piso算法是为了算瞬态,而Simple算法只能算稳态。二者之间的区别主要在压力修正的计算次序上

Piso算法的流程如下

while (runTime.loop())
     {
         Info<< "Time = " << runTime.userTimeName() << nl << endl;
 
         #include "CourantNo.H"
 
         // Pressure-velocity PISO corrector
         {
             fvModels.correct();
 
             #include "UEqn.H"
 
             // --- PISO loop
             while (piso.correct())
             {
                 #include "pEqn.H"
             }
         }
 
         viscosity->correct();
         turbulence->correct();
 
         runTime.write();
 
         Info<< "ExecutionTime = " << runTime.elapsedCpuTime() << " s"
             << "  ClockTime = " << runTime.elapsedClockTime() << " s"
             << nl << endl;
     }

代码中的runTime是物理时间,在每个物理时间步下完成速度预测与若干次压力修正。收敛之后,迈向下一个时间步。

Simple算法的流程如下

while (simple.loop(runTime))
     {
         Info<< "Time = " << runTime.userTimeName() << nl << endl;
 
         fvModels.correct();
 
         // --- Pressure-velocity SIMPLE corrector
         {
             #include "UEqn.H"
             #include "pEqn.H"
         }
 
         viscosity->correct();
         turbulence->correct();
 
         runTime.write();
 
         Info<< "ExecutionTime = " << runTime.elapsedCpuTime() << " s"
             << "  ClockTime = " << runTime.elapsedClockTime() << " s"
             << nl << endl;
     }

代码中的runTime不是物理时间,而是一个递增的整数,代表迭代次数。每一步迭代都需要求解速度预测方程与压力修正方程。收敛后得到的是稳态解。

你可能感兴趣的:(流体力学,openFoam类库,算法,c++,学习,经验分享)