ofsp2025-09
参考:
- https://github.com/UnnamedMoose/BasicOpenFOAMProgrammingTutorials
- https://www.topcfd.cn/simulation/solve/openfoam/openfoam-program/
- https://www.tfd.chalmers.se/~hani/kurser/OS_CFD/
- https://github.com/ParticulateFlow/OSCCAR-doc/blob/master/openFoamUserManual_PFM.pdf
- https://www.youtube.com/watch?v=KB9HhggUi_E&ab_channel=UCLOpenFOAMWorkshop
- http://dyfluid.com/#
- https://ss1.xrea.com/penguinitis.g1.xrea.com/study/OpenFOAM/index.html
感谢原作者们的无私引路和宝贵工作。
前置: OpenFOAM基础算法 SIMPLE & PISO & PIMPLE | 𝓐𝓮𝓻𝓸𝓼𝓪𝓷𝓭 (aerosand.cn)
前面已经讨论了定常标量输运方程的求解、非定常波动方程的求解。如果我们想进一步学习,可能在网上很多教程都推荐从 icoFoam 开始学习,但是当我们打开 icoFoam 求解器的源代码,会发现手足无措,陌生的代码和算法把我们蒙在了窗户纸后面,阻碍我们前进。
事实上,初学者在看 OpenFOAM 标准求解器之前应该有两件事要做
- 理解算法 OpenFOAM算法 SIMPLE & PISO & PIMPLE | 𝓐𝓮𝓻𝓸𝓼𝓪𝓷𝓭 (aerosand.cn)
- 这篇算法讨论其实已经写了不少代码的实现部分
- 直观的 SIMPLE 算法实现
- 没有任何 SIMPLE 以外功能的代码
所以我们仍然不急于看 icoFoam
求解器或者其他标准求解器。下面我们不考虑任何的优化、也不考虑收敛检查等等额外的功能,单从 SIMPLE 算法出发,直观地在 OpenFOAM 中实现该算法的求解器。
控制方程如下
建立本文项目文件夹
// terminal
ofsp
mkdir 09_simple
cd 09_simple
应用准备
// terminal
foamNewApp 09_01_simple
cd 09_01_simple
cp -r $FOAM_TUTORIALS/incompressible/icoFoam/cavity/cavity debug_case
cp /home/aerosand{caserun,caseclean} .
脚本和说明
略。
求解器
createFields.H
文件 /userApp/createFields.H
内容如下
/*
* Contents
* # Basic fields
* # Transport properties
*/
/*
* # Basic fields
*/
Info<< "Reading field p\n" << endl;
volScalarField p
(
IOobject
(
"p",
runTime.timeName(),
mesh,
IOobject::MUST_READ,
IOobject::AUTO_WRITE
),
mesh
);
Info<< "Reading field U\n" << endl;
volVectorField U
(
IOobject
(
"U",
runTime.timeName(),
mesh,
IOobject::MUST_READ,
IOobject::AUTO_WRITE
),
mesh
);
Info<< "Creating old layer pressure field p_old\n" << endl;
volScalarField p_old
(
IOobject
(
"p_old",
runTime.timeName(),
mesh,
IOobject::NO_READ,
IOobject::NO_WRITE
),
p
);
// 因为压力场要做松弛处理,所以需要复制一份 p 场
#include "createPhi.H"
/*
* # Transport properties
*/
Info<< "Reading transportProperties\n" << endl;
IOdictionary transportProperties
(
IOobject
(
"transportProperties",
runTime.constant(),
mesh,
IOobject::MUST_READ_IF_MODIFIED,
IOobject::NO_WRITE
)
);
dimensionedScalar nu
(
"nu",
dimensionSet(0, 2, -1, 0, 0, 0, 0),
transportProperties
);
主源码
主源码中应用 SIMPLE 算法,实现如下
#include "fvCFD.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
int main(int argc, char *argv[])
{
#include "setRootCase.H"
#include "createTime.H"
#include "createMesh.H"
#include "createFields.H" // 创建场、输运参数
// 读取代数求解器参数
Info<< "Reading fvSolutino\n" << endl;
IOdictionary fvSolution
(
IOobject
(
"fvSolution",
runTime.system(),
mesh,
IOobject::MUST_READ_IF_MODIFIED,
IOobject::NO_WRITE
)
);
// scalar alpha(readScalar(fvSolution.lookup("alpha")));
dimensionedScalar alphaD("alpha",dimless,fvSolution);
scalar alpha(alphaD.value());
// scalar pRefCell(readScalar(fvSolution.lookup("pRefCell")));
dimensionedScalar pRefCellD("pRefCell",dimless,fvSolution);
scalar pRefCell(pRefCellD.value());
// scalar pRefValue(readScalar(fvSolution.lookup("pRefValue")));
dimensionedScalar pRefValueD("pRefValue",dimless,fvSolution);
scalar pRefValue(pRefValueD.value());
// 输出刚才读取的代数求解器参数
Info<< "fvSolution parameters" << nl
<< "\trelaxation factor \"alpha\": " << alpha << nl
<< "\tindex of cell containing reference pressure \"pRefCell\": " << pRefCell << nl
<< "\treference pressure value \"pRefValue\": " << pRefValue << nl
<< endl;
// 时间推进
Info<< "\nStarting time loop\n" << endl;
while (runTime.loop())
{
Info<< "Time = " << runTime.timeName() << endl;
// 组建动量方程
fvVectorMatrix UEqn
(
fvm::div(phi,U)
- fvm::laplacian(nu,U)
==
- fvc::grad(p)
);
// 求解动量方程
UEqn.solve();
// 组建压力方程
volScalarField A = UEqn.A();
volVectorField H = UEqn.H();
volScalarField rA = 1.0 / A;
volVectorField HbyA = H * rA;
fvScalarMatrix pEqn
(
fvm::laplacian(rA,p)
==
fvc::div(HbyA)
);
pEqn.setReference(pRefCell,pRefValue); // 为什么要设参考值?
// 求解压力方程
pEqn.solve();
// 欠松弛处理
p = alpha*p + (1.0-alpha)*p_old;
// 更新速度场
U = rA*H - rA*fvc::grad(p);
// 求解质量通量,可以直接使用 flux() 函数
phi = fvc::interpolate(U) & mesh.Sf();
// phi = fvc::flux(U);
// 边界条件修正,暂不深究
U.correctBoundaryConditions();
p.correctBoundaryConditions();
// 更新旧时间步场量,用于下一次欠松弛处理
p_old = p;
runTime.write();
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
Info<< nl;
runTime.printExecutionTime(Info);
Info<< "End\n" << endl;
return 0;
}
// ************************************************************************* //
编译
// terminal
wclean
wmake
调试算例
system/blockMeshDict
无需修改,我们仍然使用原几何模型和几何网格system/controlDict
无需修改,我们仍然使用求解控制的设置(步长步数等)decomposedParDict
和PDRblockMeshDict
不用管,本文用不到constant/transportProperties.H
无需修改,我们沿用粘度参数debug_case/0/
初始条件无需修改
fvSchemes
注意各个格式需要至少提供默认值,例如
divSchemes
{
default Gauss linear;
}
fvSolution
最后添加语句
alpha 0.01;
pRefCell 0;
pRefValue 0;
controlDict
自行尝试更改计算参数
计算
// terminal
./caseclean
./caserun
paraFoam -case _case
可以看到计算结果。
整理代码
我们可以把代码整理为如下文件结构
|- userApp/
|- debug_case/
|- Make/
|- files/
|- options/
|- 09_01_simple.C
|- createFields.H
|- readfvSolution.H
|- pEqn.H
|- UEqn.H
|- caserun
|- caseclean
主源码
#include "fvCFD.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
int main(int argc, char *argv[])
{
#include "setRootCase.H"
#include "createTime.H"
#include "createMesh.H"
#include "createFields.H"
#include "readfvSolution.H"
Info<< "\nStarting time loop\n" << endl;
while (runTime.loop())
{
Info<< "Time = " << runTime.timeName() << endl;
#include "UEqn.H"
#include "pEqn.H"
runTime.write();
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
Info<< nl;
runTime.printExecutionTime(Info);
Info<< "End\n" << endl;
return 0;
}
// ************************************************************************* //
readfvSolution.H
Info<< "Reading fvSolutino\n" << endl;
IOdictionary fvSolution
(
IOobject
(
"fvSolution",
runTime.system(),
mesh,
IOobject::MUST_READ_IF_MODIFIED,
IOobject::NO_WRITE
)
);
// scalar alpha(readScalar(fvSolution.lookup("alpha")));
dimensionedScalar alphaD("alpha",dimless,fvSolution);
scalar alpha(alphaD.value());
// scalar pRefCell(readScalar(fvSolution.lookup("pRefCell")));
dimensionedScalar pRefCellD("pRefCell",dimless,fvSolution);
scalar pRefCell(pRefCellD.value());
// scalar pRefValue(readScalar(fvSolution.lookup("pRefValue")));
dimensionedScalar pRefValueD("pRefValue",dimless,fvSolution);
scalar pRefValue(pRefValueD.value());
Info<< "fvSolution parameters" << nl
<< "\trelaxation factor \"alpha\": " << alpha << nl
<< "\tindex of cell containing reference pressure \"pRefCell\": " << pRefCell << nl
<< "\treference pressure value \"pRefValue\": " << pRefValue << nl
<< endl;
UEqn.H
fvVectorMatrix UEqn
(
fvm::div(phi,U)
- fvm::laplacian(nu,U)
==
- fvc::grad(p)
);
UEqn.solve();
pEqn.H
volScalarField A = UEqn.A();
volVectorField H = UEqn.H();
volScalarField rA = 1.0 / A;
volVectorField HbyA = H * rA;
fvScalarMatrix pEqn
(
fvm::laplacian(rA,p)
==
fvc::div(HbyA)
);
pEqn.setReference(pRefCell,pRefValue);
pEqn.solve();
p = alpha*p + (1.0-alpha)*p_old; // 压力松弛
U = rA*H - rA*fvc::grad(p);
phi = fvc::interpolate(U) & mesh.Sf();
U.correctBoundaryConditions();
p.correctBoundaryConditions();
p_old = p;
编译计算
仍然使用上面的调试算例,结果是相同的。
小结
我们通过百分百还原数学方程,没有加入其他任何功能,最后我们得到了一个单纯基于 SIMPLE
算法的求解器。
Last updated on