openFOAM C++代码的一些特性

目录

数值耗散和数值频散

对非线性化项的处理

OpenFOAM程序最基本的架构

OpenFOAM里的代码和C++的区别

 关于#include的文件

fvCFD.H

using namespace std

主函数main函数

编译

Make/options文件内容指定

报错咋办

OpenFOAM中的内置类型非常多

OpenFOAM中包含了一些自定义的宏

C++代码例子

OpenFOAM实例

代码封装


数值耗散和数值频散

由于cfd旨在用差分方程代替微分方程,所以差分方程是微分方程的逼近,但二者之间总有误差。误差由阶次不同,可造成解的耗散和频散,其中耗散就如给流场添加了人为的粘性一样,使得本来尖锐的突越变得平滑,分辨率降低。

简单的说截断项中偶数阶微分的存在使得解具有耗散性,奇数阶微分的存在使得解具有频散性。比如一道正弦曲线,耗散使之幅值变低,而频散使之相位和周期发生变化。

https://zhidao.baidu.com/question/399291950.html

对非线性化项的处理

由于非线性求解器非常复杂,因此在OpenFOAM均采用线性化处理。

OpenFOAM程序最基本的架构

每个程序会通过单独一个文件夹来包含(如myFirstFoam文件夹),在这个文件夹下,存在一个Make文件夹,Make中包含files和options文件。同时在myFirstFoam文件夹中,需要包含程序文件(如myFirstFoam.C)。

  • 目录文件夹下的.C文件是OpenFOAM/C++的主程序(注意大写);

  • Make文件夹是OpenFOAM特有的,里面主要包含两个文件files和options;

  • files用来指定OpenFOAM顺序进行编译的文件名称以及路径

  • options用来指定OpenFOAM需要调用的外挂库的路径以及名称

OpenFOAM里的代码和C++的区别

输出语句中的coutInfo替代,Info是OpenFOAM中用来输出的语句。不仅仅可以输出字符串,也可以输出其他各种的场(比如体向量场等)。后面的nl在OpenFOAM中用来表示换行,等同于C++中的endl。 

$(LIB_SRC)是一个环境变量,在OpenFOAM中等同于$(FOAM_SRC),其真实的位置可以通过键入echo $FOAM_SRC来查看。

 关于#include的文件

后期编程的时候,则是用户需要自己去OpenFOAM源文件里面找的。需要什么头文件,就写什么头文件。举例,用户可能需要在编程环境中调用volScalarField类型,这样,就需要在程序最开始,将volScalarField这个类型进行包含。在OpenFOAM中,可以通过#include "volFields.H"来处理。其中的volFields.H,只能用户自己去找,没有规律。

fvCFD.H

OpenFOAM中fvCFD.H文件是一个非常大的框架,它把OpenFOAM中大部分的基本的类型都包含了,在初学不知道应该调用OpenFOAM具体的什么类型的时候,建议直接把fvCFD.H文件进行包含省去寻找具体头文件的过程。

using namespace std

using namespace std之后,就省去了std::,可以直接引用std空间里面的函数和变量。

一个中大型软件往往由多名程序员共同开发,会使用大量的变量和函数,不可避免地会出现变量或函数的命名冲突。当所有人的代码都测试通过,没有问题时,将它们结合到一起就有可能会出现命名冲突。

例如小李和小韩都参与了一个文件管理系统的开发,它们都定义了一个全局变量 fp,用来指明当前打开的文件,将他们的代码整合在一起编译时,很明显编译器会提示 fp 重复定义(Redefinition)错误。

为了解决合作开发时的命名冲突问题,C++ 引入了命名空间(Namespace)的概念。请看下面的例子:

namespace Li{  //小李的变量定义
    FILE fp = NULL;
}
namespace Han{  //小韩的变量定义
    FILE fp = NULL
}

小李与小韩各自定义了以自己姓氏为名的命名空间,此时再将他们的 fp 变量放在一起编译就不会有任何问题。

namespace 是C++中的关键字,用来定义一个命名空间,语法格式为:

namespace name{
    //variables, functions, classes
}

 ::是一个新符号,称为域解析操作符,在C++中用来指明要使用的命名空间。

http://c.biancheng.net/view/2192.html

主函数main函数

main函数是一个特殊的函数,在可执行的OpenFOAM/C++程序中,它总是最先运行的代码。int main()声明了一个叫做main的函数,它没有参数。如果有参数的话,它们的名称应该出现在小括号()。但具有一个返回值,类型为int,因此在程序的最后,会存在一行代码return 0;。最简单的理解方法是,将所有的主程序函数头都写成int main()(当然你可以写成void main(),它并不需要返回值)。但其中的main是不能变的,大小写也不能变,必须这样写。

编译

OpenFOAM中所有的程序(库)的编译器调用命令为:wmake,用户应该切换到程序目录下,运行wmake

Make/options文件内容指定

options用来指定OpenFOAM需要调用的外挂库的路径以及名称

报错咋办

同时注意,在提示missing separator错误之后,即使添加好\也依然会报错。这时候需要用wclean命令清空一下,再运行wmake。提醒:某些情况下如果Gcc编译器不匹配你的OpenFOAM版本号,也会出现类似问题。这也解释了,为什么比较高版本的OpenFOAM需要比较高版本的Gcc编译器。

OpenFOAM中的内置类型非常多

比如:

  • volScalarField 体标量场:比如CFD中定义在网格上的温度场;

  • volVectirField 体矢量场:比如CFD中定义在网格上的速度场;

  • fvScalarMatrix 标量矩阵:比如CFD中离散湍流动能生成的稀疏线性系统;

  • autoPtr OpenFOAM智能指针:OpenFOAM自定义的智能指针;

  • processor 处理器边界类型:并行计算的处理器边界类型;

在这里并不能够全部列举。这需要同学们在以后使用OpenFOAM的过程中持续的摸索。

常用类总结:http://blog.sina.com.cn/s/blog_5333fbde0100dagt.html

对于程序,最常用到的,也是最底层的就是数据,在OpenFOAM中引入了三类基础数据类型:标量scalar, 向量vector, 张量tensor.这三个中数据类型,也是FOAM中最基础的三个类。(还有一个比较重要的就是bool和label,前者就是是非型,及对错型,只不过是更扩展一些,后者是标签型数据,相当于c中的整型。关于更多的其它数据类型可以参看目录..\src\OpenFOAM\primitives里面)

在上述数据类的基础上,增加场(field)的概念,就引入了标量场scalarField, 向量场vectorField, 张量场tensorField.实际上这三个类又是field类的typedef,如typedef field<scalar> saclarField。这些场类中都有对应的成员函数进行加减乘除运算,还有复杂的点积叉积等。说到这field class,其实他就像是一个数据存放的区域一样,存放上scalar,那它成了标量场scalarField。这些类中可以有接口实现数据的计算。从field类中又派生出了FieldField类,这个就是说场中场类,其实这个主要用于边界条件类的一个基类。因为边界条件算是网格类场中的一个特殊的场,后面会介绍。

比field类高一点的就是几何场类 GeometricField class,其相比field class多了纪录场位置的相关信息。说到这里请大家注意他和polyMesh class的区别,后者只是纪录网格的结构,如点的位置、面的组成、体的组成等等,polyMesh class中对应有pointMesh,surfaceMesh,volMesh等类,从字面上很容易理解其处理和记录网格点、网格面、网格体等信息。而GeometricField类,其则是记录了在什么样的网格上有量a的相关信息或数据。它包括了内部区域、边界区域(GeometricBoundaryField class)、网格、尺度单位、计算的先前时间阶的值等。在该类中有常用的三种(实际上还有其他的许多,可以参看OpenFOAM网上说明):volScalarField体标量场,volVectorField体向量场,volTensorField体张量场。这里说的场与field有所不同,这里指的是网格区域上所对应的数据信息。上述的vol就是指ployMesh中的volMesh,如volscalarField类来说:

    volScalarField p
    (
        IOobject
        (
            "p",
            runTime.timeName(),
            mesh,
            IOobject::MUST_READ,
            IOobject::AUTO_WRITE
        ),
        mesh
    );

这是读入标量压力场文件,把压力值存储到网格体中心。为加深对GeometricField类的理解,贴张PG中的图片:

除了体的向量标量张量场外,还有面标量场surfaceScalarField、面向量场surfaceVectorField、面张量场surfaceTensorField。看下面的例子:

    surfaceScalarField phi
    (
        IOobject
        (
            "phi",
            runTime.timeName(),
            mesh
        ),
        fvc::interpolate(alpha)*phia
      + fvc::interpolate(beta)*phib
    );

这里的phi既是一个面向量场对象,他用来是纪录单元体面上流过的通量值

除了常用到的标量向量张量的几何场外,还有一些特殊量的场:surfaceSymmTensor面对称张量几何场、体球面张量场等等。几何场里面还有一个比较重要的类就是GeometricBoundaryField,用来专门对边界进行处理的一个类。

 

OpenFOAM中包含了一些自定义的宏

其中一些语句可用来执行CFD专属的循环。比如forAll()语句是非常常用的OpenFOAM语句。看下面的代码:

forAll(U, cellI)
{
    U[cellI] = vector(1, 0, 0);
}

其中forAll()为一种遍历,其需要传入两个参数,在上面的例子中,第一个参数为U(一个volScalarField),则此forAll()表示对整个计算域的网格进行循环。括号内的U[cellI] = vector(1, 0, 0);表示对每个网格的U[cellI]进行赋值。需要注意的是,上面的例子并不对U的边界进行处理。

如果forAll()内传入surfaceScalarField,则对内部的面进行循环,例如:

forAll(phi, index)
{
    phi[index] = 0.0;
}

上面的代码将phi内部的面赋值为0。同样的,本例子不对phi的边界进行处理。

C++代码例子

double shit(double a, int b);//函数返回double类型,名称为shit(),传入一个double类型a,一个int类型b

int main()
{
    double whatName;
    double a = 2.1;
    int b = 0;
    whatName = shit(a, b);//调用shit()函数并将返回值赋给whatName
    return 0;
}

double shit(double a, int b)//函数定义
{
//函数具体计算代码略
}
//函数返回double类型,名称为shit(),传入一个double类型a,一个int类型b
double shit(double a, int b);
{
//函数具体计算代码略
}

int main()
{
    double whatName;
    double a = 2.1;
    int b = 0;
    whatName = shit(a, b);//调用shit()函数并将返回值赋给whatName
    return 0;
}

OpenFOAM实例

//在main()函数之前的函数定义,返回volScalarField类型,函数名为Function()
volScalarField Function
(
    //传入一个volVectorField类型
    volVectorField U,
    //传入一个dimensionedScalar类型
    dimensionedScalar nu
)
{
//略    
}

int main()
{
    volScalarField p = Function(velocity, nu);
    //声明一个volScalarField类型叫做p
    //将Function(velocity, nu)函数返回的volScalarField赋值给p
}

代码封装

我们把类的声明放在了.H文件中,类的实现放在了.C文件中,对这个.C文件进行编译,可以形成一个库(后缀为.so)。同时,在主程序内,将类的.H文件进行包含,同时在options文件中嵌入库文件,即可将程序成功编译。如果同学们想把自己的库函数封装,需要进行下面的步骤:

  • 在自己的电脑上编写类的.H文件和.C文件;

  • 编译出库文件;

  • 拷贝.H文件和库文件给其他用户,其他用户的程序文件中即可调用这个库,并调用其中的库函数(类接口);

需要注意的是,你并不需要把.C文件拷贝给其他人,其他人即可调用你的库(你的函数实现)。这,就是非常典型的类封装过程。因为你的.C文件包含了所有函数实现,且没有被外传。

很多软件都是进行的类似的操作,很有可能同学们下载了一款软件,其中包含了大量的.so文件(在windows下后缀为.dll的文件),然后包含若干的文件头。很明显,这就是封装后的代码。

同时提醒的是,OpenFOAM为一款开源CFD求解器,并不支持闭源行为。

  • 3
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值