IO
IO的基本特征是硬盘读写,耗时间。
后处理里面的IO
目前我见过的OF版本里面后处理的编程范式,仍旧是读一个时间步进行操作再读下一个时间步,这样一段IO之后一点操作操作完了输出一个时间步的结果,然后下一个时间步再一段IO……可能会效率很低:也许来得不如先集中读入所有数据(内存足够大),然后再处理后集中输出。不过要能够这样处理的前提除去内存,对OpenFOAM里field类读写操作的类需要在时间维度上变成动态数组。
我的认识:读入field的时候确实是动态的数组,但在时间维度不是。
Serial or parallel ?
早期OpenFOAM
进行并行计算时要decomposePar
将计算域分为多个部分,网格文件实际上也分开了。在写入的时候也是写入到每个processor
文件中,这是早期OpenFOAM版本的做法,弊端是产生的文件量特别大。有些机器上面,比如occigen就有文件个数的限制,只允许200000个文件。如果500个processor,一个processor里面最多只能400个文件,而一个时间步里面至少有U, p, phi
,所以这个限制还挺严格。
如何解决?要么定期reconstructPar
然后把那些processor里面对应的U, p, phi
删掉,需要注意的有两点:
1.第一个操作完成,第二步删除再进行
2.不要对OpenFOAM正在读写的文件进行操作
第2点比较好规避,选择所有时间步,避开倒数2个,剩下的一般都写好了(因为一般输出数据间隔都不会很小);而第1点,就需要保证第一步的返回值为0,经由判断后再进行相应删除
较新的OpenFOAM
openfoam-plus_1712和openfoam5提供了一种直接输出一个整体流场U, p, phi
的方式。(吐槽一下:这应该是一个并行CFD的基本功能才对)
经尝试想要在早期版本中编译并行输出的模块很复杂,弃。
数据格式
binary or ascii
模拟的数据输出可以选择,各有优劣:
- binary数据存储体积更小,推荐在simu中间使用,而且在paraview读取数据的时候(20M算例,两个时间步)至少比ascii要快7倍
- 在初始场中推荐使用ascii,因为万一要改边界条件binary格式不是那么好下手(vimdiff或者meld都对ascii支持更好)
- 再者OpenFOAM支持一个simu里面两种格式文件共存,utility和solver在读数据的时候都是以文件头上
format
相应格式读取
binary或者ascii,如何在二者中转换?
复杂解且不完全:比如从binary转为ascii,可以用decomposePar
然后reconstructPar
,第二步的时候把输出格式改为ascii,internalField
的读写应该没问题,但是BC呢还是得check一下
简单解且官方:foamFormatConvert,用system/controlDict里面的格式来写输出(不过这会overwrite原数据),默认会将constant/polyMesh里面的数据也按照格式重写
foamFormatConvert
也不是一直都行得通:ascii格式的文件是OF-2.3.x/OF-3.0.1兼容的,但binary就不是,遇到以下报错需考虑版本问题(另外一点窍门是转换的时候如果方便,把constant
和system
移动到与时间步分离的目录下运行foamFormatConvert -constant
,这样可以避免时间步的数据被影响)
1 2 3 4 5 6 7 8 9
| --> FOAM FATAL IO ERROR: Expected a ')' while reading binaryBlock, found on line 20 an error
file: synthetic_phasedStepFrom_test_from_0/From0p3_3_of3/constant/polyMesh/faces at line 20.
From function Istream::readEnd(const char*) in file db/IOstreams/IOstreams/Istream.C at line 111.
FOAM exiting
|
相关的类
UListIO.C
里有底层的operator<<
,判断了是否输出的是ascii后判断是不是uniform
的UList,如果是uniform就用{}
,如果不是就用()
,这就是为什么OpenFOAM里面只要输出List就一定带有大括号或者小括号.
典型读写范例
bug
据测试…读的时候文件可以用绝对路径来且可以较长,但写的时候得注意绝对路径不一定奏效(即使确定了路径存在),为啥要说这一点,因为“不奏效”指无warning且不报错(如果debug不开的话)但就是找不到输出文件,写的时候用constant
作为输出路径比较保险(当下目录.
也需质疑)
读写范例
通过IOobject读入写场,注意,OpenFOAM里面场是*Field
而Field
是一个List
,所以其实是List
读写(上面写到还跟UList有关)
读
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
| volVectorField velocityField ( IOobject ( velocityFieldName, runTime.timeName(), mesh, IOobject::MUST_READ ), mesh );
IOList<label> labelGroup ( IOobject ( "labelGroup", position, runTime, IOobject::MUST_READ, IOobject::NO_WRITE ) );
FoamFile { version 2.0; format ascii; class labelList; location "constant"; object labelGroup; }
21 ( 26524615 25273885 24395085 23786685 23330385 22975485 22671285 22417785 22198085 21995285 21826285 21657285 21657285 21031985 20355985 19865885 19494085 19189885 18919485 18699785 18496984 )
|
Note : topoSet
好像可以做labelList
的操作,它的功能还很多!包括{label/zone/face/point/box/rotatedBox/cylinder/sphere/...}toCell
写
此例用runTime而没用用mesh作为objectRegistry,但需要system/controlDict
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
|
#include "Time.H" #include "argList.H"
#include "IOList.H" #include "OFstream.H"
using namespace Foam;
int main( int argc, char *argv[]) { #include "setRootCase.H" #include "createTime.H"
IOList<scalar> IOScalarList ( IOobject ( "IOSList", "constant", runTime, IOobject::NO_READ, IOobject::AUTO_WRITE ) );
List<scalar> sList(8); sList[0] = 7.0; sList[1] = 9.0; sList[2] = 1.0; sList[3] = 2.1; sList[4] = 4.0; sList[5] = 7.0; sList[6] = 4.0; sList[7] = 0.0; IOList<scalar> IOScalarList1 ( IOobject ( "IOSList1", "constant", runTime, IOobject::NO_READ, IOobject::AUTO_WRITE ), sList );
Info<< "Writing " << IOScalarList.name() << " to " << IOScalarList.objectPath() << endl;
OFstream os(IOScalarList.objectPath()); OFstream os1(IOScalarList1.objectPath());
IOScalarList.writeHeader(os); IOScalarList1.writeHeader(os1);
Info<< "\nEnd\n" << endl; }
IOobjectWriter.C
EXE = $(FOAM_USER_APPBIN)/IOobjectWriter
EXE_INC = \ -I$(LIB_SRC)/OpenFOAM/lnInclude
EXE_LIBS = \ -lOpenFOAM
|
这里没有用到fvCFD.H
这个容量超级大的*.H
文件,也没有用到class fvMesh
,仅仅是List
的读入,算是个minimum code.但如果要加入读volScalarField
呢?下面是上面代码加入volScalarField
的变种,但如果想要保持尽量少的头文件并不容易.下面这段代码编译错误.
1 2 3 4 5 6 7 8 9 10 11 12 13
| $ wmake SOURCE=userProbeByLabel.C ; OMPI_CXX="g++" mpicxx -Dlinux64 -DWM_DP -Wall -Wextra -Wno-unused-parameter -Wold-style-cast -Wnon-virtual-dtor -O2 -march=native -fuse-ld=bfd -DNoRepository -ftemplate-depth-100 -I/home/hluo/.local/easybuild/software/OpenFOAM/2.3.1-foss-2016a/OpenFOAM-2.3.1/src/OpenFOAM/lnInclude -I/home/hluo/.local/easybuild/software/OpenFOAM/2.3.1-foss-2016a/OpenFOAM-2.3.1/src/finiteVolume/lnInclude -I/home/hluo/.local/easybuild/software/OpenFOAM/2.3.1-foss-2016a/OpenFOAM-2.3.1/src/meshTools/lnInclude -IlnInclude -I. -I/home/hluo/.local/easybuild/software/OpenFOAM/2.3.1-foss-2016a/OpenFOAM-2.3.1/src/OpenFOAM/lnInclude -I/home/hluo/.local/easybuild/software/OpenFOAM/2.3.1-foss-2016a/OpenFOAM-2.3.1/src/OSspecific/POSIX/lnInclude -fPIC -c $SOURCE -o Make/linux64GccDPOpt/userProbeByLabel.o userProbeByLabel.C: In function ‘int main(int, char**)’: userProbeByLabel.C:59:7: error: variable ‘Foam::volScalarField mean’ has initializer but incomplete type scalarFieldName+"_mean", ^ userProbeByLabel.C:76:14: error: variable ‘Foam::volScalarField scalarField’ has initializer but incomplete type scalarFieldName, ^ userProbeByLabel.C:84:18: error: variable ‘Foam::volScalarField sPrime’ has initializer but incomplete type volScalarField sPrime = scalarField - mean; ^ make: *** [Make/linux64GccDPOpt/userProbeByLabel.o] Error 1
|
说明volScalarField
并没有正确地被初始化,查userProbeByLabel.dep
发现没有对应的模板类GeometricField
的身影,但加入#include "GeometricField.H"
的最终结果也是莫名其妙一堆错.当然一个肯定可行的解法是头文件加上fvCFD.H
,基于测试我发现#include "fvc.H"
或者#include "fvm.H"
均可通过编译,而且能够正常运行.代码如下,关键行#include "fvc.H"
被注释.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
| #include "Time.H" #include "argList.H" #include "fvMesh.H" #include "timeSelector.H"
#include <fstream>
using namespace Foam;
int main( int argc, char *argv[]) { timeSelector::addOptions(); argList::validArgs.append("scalarFieldName"); argList::validArgs.append("timeOfAverageField"); argList::validArgs.append("position");
#include "setRootCase.H" #include "createTime.H"
word scalarFieldName(args.additionalArgs()[0]); word timeOfAverageField(args.additionalArgs()[1]); word position(args.additionalArgs()[2]);
instantList timeDirs = timeSelector::select0(runTime, args);
IOList<label> labelGroup ( IOobject ( "labelGroup", position, runTime, IOobject::MUST_READ, IOobject::NO_WRITE ) );
Info<< labelGroup << endl;
mkDir("userDefinedLog"); std::ofstream fluctuationLog ( fileName(string("userDefinedLog")/string("fluctuation_labelGroup_"+scalarFieldName)).c_str(), ios_base::app );
#include "createMesh.H"
volScalarField mean ( IOobject ( scalarFieldName+"_mean", timeOfAverageField, mesh, IOobject::MUST_READ ), mesh );
forAll(timeDirs, timeI) { runTime.setTime(timeDirs[timeI], timeI); Info<< "Time = " << runTime.timeName() << endl;
volScalarField scalarField ( IOobject ( scalarFieldName, runTime.timeName(), mesh, IOobject::MUST_READ ), mesh );
volScalarField sPrime = scalarField - mean;
fluctuationLog << runTime.timeName() << " "; forAll(labelGroup, i) { fluctuationLog << sPrime.internalField()[labelGroup[i]] << " " ; } fluctuationLog << std::endl; } Info<< "\nEnd\n" << endl; }
userProbeByLabel.C
EXE = $(FOAM_USER_APPBIN)/userProbeByLabel
EXE_INC = \ -I$(LIB_SRC)/OpenFOAM/lnInclude \ -I$(LIB_SRC)/finiteVolume/lnInclude \ -I$(LIB_SRC)/meshTools/lnInclude
EXE_LIBS = \ -lOpenFOAM \ -lfiniteVolume \ -lmeshTools
|
计算和写(标准)
1 2 3 4 5 6 7 8 9 10 11 12 13
| volScalarField strainRate ( IOobject ( "strainRate_"+velocityFieldName, runTime.timeName(), mesh, IOobject::NO_READ, IOobject::NO_WRITE ), Foam::sqrt(2.0)*mag(symm(fvc::grad(velocityField))) );
|
计算和写(避开量纲)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| scalarField userCalcNu(scalarField strainRate) { scalar nuInf_ = 2e-6; scalar nu0_ = 3e-4; scalar k_ = 1; scalar n_ = 0.326; scalar a_ = 2.0;
return nuInf_ + (nu0_ - nuInf_) *pow(scalar(1) + pow(k_*strainRate, a_), (n_ - 1.0)/a_); }
volScalarField nu ( IOobject ( outputFieldName, runTime.timeName(), mesh, IOobject::NO_READ, IOobject::AUTO_WRITE ), mesh, dimensionedScalar ( outputFieldName, dimless, scalar(0.) ) );
nu.internalField()=userCalcNu(strainRate);
nu.write();
|
计算一个跟壁面相关的场(也就是仅在边界上才有值)并输出
可以参照wallShearStress,这样输出的场(yPlus也是类似)可以在paraview可视化,也许需要去掉internal mesh
的勾选
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
|
volVectorField wallShearStress ( IOobject ( "wallShearStress", runTime.timeName(), mesh, IOobject::NO_READ, IOobject::AUTO_WRITE ), mesh, dimensionedVector ( "wallShearStress", sqr(dimLength)/sqr(dimTime), vector::zero ) );
wallShearStress.write();
|
初始化一个单位张量场
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| volTensorField identity ( IOobject ( "identity", runTime.timeName(), mesh, IOobject::NO_READ, IOobject::AUTO_WRITE ), mesh, dimensionedTensor ( "identity", dimless, tensor::I ) );
|
编写边界条件
fixedValueFvPatchVectorField
Dirichlet边界(可以随时间变化),通常继承自fixedValueFvPatchVectorField
,编写边界条件的时候要注意:
- data member 申明(
*.H
)和初始化(*.C
)的顺序要一致
- 一定要要让所有constructor里面data member都完成初始化(
*.C
)
*.C
里面的virtual void write(Ostream&) const
务必要包含所有必要的data member,因为在decomposePar
时写入processor*
里边界条件用的就是这个write
*.C
里面的makePatchTypeField
似乎是个宏函数,一定要修改成makePatchTypeField(fvPatchVectorField, SameAsClassName);
- 可以写一些non-member function用来做简单的计算
窍门
所在边界的名字:patch().name()
当前时间步的时间参数:this->db().time().timeOutputValue()
.在这个类runTime
不可见
常用Tips
object
timeSelector : 通常以 -time 起,后接 ':500,1200:1300,3000:'
args : argList类 —— argument list. 构造函数对应 #include "setRootCase.H"
runTime : 可以用write()方法,将所有注册的object都写出来(如果不是NO_WRITE),但一般的流场也可以自己用write(),例如U.write()
. 构造函数对应 #include "createTime.H"
mesh : fvMesh类,可以用来遍历:forAll(mesh.C(), celli)
,也可以通过mesh.V()
来找网格体积
函数
书写文件头
OpenFOAM里面文件头怎么来的? : writeHeader
max/min
场(GeometricField,一般是某volScalarField)的最大值,最小值:(虽然我不确认并行的时候对不对)
1 2 3 4 5 6 7 8 9 10
| max(someField); max(someField.internalField()); max(someField.boundaryField());
forAll(mesh.boundaryMesh(), patchI) { max(someField.boundaryField()[patchI]) }
|
findMax/findMin
这个紧接前面,要注意的是findMax返回的index是所操作数组的index而非全局index
面积元的全局index在哪里呢?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| forAll(mesh.boundaryMesh().names(), patchName) { polyPatch cPatch = mesh.boundaryMesh()[patchName]; forAll(cPatch, faceI) { label faceId = cPatch.start() + faceI; Info << faceId << " -> " << endl; Info << " cPatch[faceI] = " << cPatch[faceI] << endl; Info << " mesh.faces()[faceId] = " << mesh.faces()[faceId] << endl; } }
|
findPatchID
通过字符串来找到一个patchList里面对应的patchID
1
| label patchID = mesh.boundaryMesh().findPatchID("walls");
|
boundary value还是离网格最近的cell value?
U.boundaryField()[patchi]
这个是boundary value.U.patchInternalField()
这是离boundary最近的cell些的U的cell value
faceCells
cell到face是很自然的,因为:cell是什么?多个face闭合起来成为cell。那怎么反过来找呢?
1 2 3
| const fvPatchList& patches = mesh.boundary();
patches[somePatch].faceCells[facei];
|
类
VectorSpace, Vector, Tensor: mag
VectorSpace 显然是这里最具有一般性最底层的模板类了,Vector和Tensor在它眼里仅是3个元素和9个元素的差别,求mag
都一视同仁。
mag
在VectorSpace/Vector里面就是所有元素平方和magSqr
然后开平方,在Tensor变成9个元素平方和然后开平方。因此一般的Tensor不同重写magSqr
,SymmTensor因为其形式特别就重写了magSqr
,如此替换掉最一般的magSqr
,对SymmTensor仍然可以进行mag
操作,为什么呢?因为SymmTensor是Tensor是VectorSpace里面的元素,因此一定就有mag
,此时母类mag
调用的是SymmTensor版本的magSqr
。
外积
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| #include "vector.H" #include "tensor.H" #include "IOstreams.H"
using namespace Foam;
int main() {
vector a(1, 2, 3); Info << "a = " << a << endl; Info << "a*a = " << a*a << endl;
return 0; }
|
如果将#include "tensor.H"
注释掉会报一个比较长的错:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| $ wmake Making dependency list for source file Test-vector.C SOURCE=Test-vector.C ; OMPI_CXX="g++" mpicxx -Dlinux64 -DWM_DP -Wall -Wextra -Wno-unused-parameter -Wold-style-cast -Wnon-virtual-dtor -O2 -march=native -fuse-ld=bfd -DNoRepository -ftemplate-depth-100 -IlnInclude -I. -I/home/hluo/.local/easybuild/software/OpenFOAM/2.3.1-foss-2016a/OpenFOAM-2.3.1/src/OpenFOAM/lnInclude -I/home/hluo/.local/easybuild/software/OpenFOAM/2.3.1-foss-2016a/OpenFOAM-2.3.1/src/OSspecific/POSIX/lnInclude -fPIC -c $SOURCE -o Make/linux64GccDPOpt/Test-vector.o OMPI_CXX="g++" mpicxx -Dlinux64 -DWM_DP -Wall -Wextra -Wno-unused-parameter -Wold-style-cast -Wnon-virtual-dtor -O2 -march=native -fuse-ld=bfd -DNoRepository -ftemplate-depth-100 -IlnInclude -I. -I/home/hluo/.local/easybuild/software/OpenFOAM/2.3.1-foss-2016a/OpenFOAM-2.3.1/src/OpenFOAM/lnInclude -I/home/hluo/.local/easybuild/software/OpenFOAM/2.3.1-foss-2016a/OpenFOAM-2.3.1/src/OSspecific/POSIX/lnInclude -fPIC -Xlinker --add-needed -Xlinker --no-as-needed Make/linux64GccDPOpt/Test-vector.o -L/home/hluo/.local/easybuild/software/OpenFOAM/2.3.1-foss-2016a/OpenFOAM-2.3.1/platforms/linux64GccDPOpt/lib \ -lOpenFOAM -ldl -lm -o /home/hluo/OpenFOAM/hluo-2.3.1/platforms/linux64GccDPOpt/bin/Test-vector [hluo@zaurak userVector]$ vim Test-vector.C [hluo@zaurak userVector]$ wmake Making dependency list for source file Test-vector.C SOURCE=Test-vector.C ; OMPI_CXX="g++" mpicxx -Dlinux64 -DWM_DP -Wall -Wextra -Wno-unused-parameter -Wold-style-cast -Wnon-virtual-dtor -O2 -march=native -fuse-ld=bfd -DNoRepository -ftemplate-depth-100 -IlnInclude -I. -I/home/hluo/.local/easybuild/software/OpenFOAM/2.3.1-foss-2016a/OpenFOAM-2.3.1/src/OpenFOAM/lnInclude -I/home/hluo/.local/easybuild/software/OpenFOAM/2.3.1-foss-2016a/OpenFOAM-2.3.1/src/OSspecific/POSIX/lnInclude -fPIC -c $SOURCE -o Make/linux64GccDPOpt/Test-vector.o Test-vector.C: In function ‘int main()’: Test-vector.C:18:23: error: no match for ‘operator*’ (operand types are ‘Foam::vector {aka Foam::Vector<double>}’ and ‘Foam::vector {aka Foam::Vector<double>}’) Info << "a*a = " << a*a << endl; ^ Test-vector.C:18:23: note: candidates are: In file included from /home/hluo/.local/easybuild/software/OpenFOAM/2.3.1-foss-2016a/OpenFOAM-2.3.1/src/OpenFOAM/lnInclude/VectorSpace.H:168:0, from /home/hluo/.local/easybuild/software/OpenFOAM/2.3.1-foss-2016a/OpenFOAM-2.3.1/src/OpenFOAM/lnInclude/Vector.H:44, from /home/hluo/.local/easybuild/software/OpenFOAM/2.3.1-foss-2016a/OpenFOAM-2.3.1/src/OpenFOAM/lnInclude/vector.H:39, from Test-vector.C:1: /home/hluo/.local/easybuild/software/OpenFOAM/2.3.1-foss-2016a/OpenFOAM-2.3.1/src/OpenFOAM/lnInclude/VectorSpaceI.H:552:13: note: template<class Form, class Cmpt, int nCmpt> Form Foam::operator*(Foam::scalar, const Foam::VectorSpace<Form, Cmpt, nCmpt>&) inline Form operator* ^ /home/hluo/.local/easybuild/software/OpenFOAM/2.3.1-foss-2016a/OpenFOAM-2.3.1/src/OpenFOAM/lnInclude/VectorSpaceI.H:552:13: note: template argument deduction/substitution failed: Test-vector.C:18:24: note: cannot convert ‘a’ (type ‘Foam::vector {aka Foam::Vector<double>}’) to type ‘Foam::scalar {aka double}’ Info << "a*a = " << a*a << endl; ^ In file included from /home/hluo/.local/easybuild/software/OpenFOAM/2.3.1-foss-2016a/OpenFOAM-2.3.1/src/OpenFOAM/lnInclude/VectorSpace.H:168:0, from /home/hluo/.local/easybuild/software/OpenFOAM/2.3.1-foss-2016a/OpenFOAM-2.3.1/src/OpenFOAM/lnInclude/Vector.H:44, from /home/hluo/.local/easybuild/software/OpenFOAM/2.3.1-foss-2016a/OpenFOAM-2.3.1/src/OpenFOAM/lnInclude/vector.H:39, from Test-vector.C:1: /home/hluo/.local/easybuild/software/OpenFOAM/2.3.1-foss-2016a/OpenFOAM-2.3.1/src/OpenFOAM/lnInclude/VectorSpaceI.H:565:13: note: template<class Form, class Cmpt, int nCmpt> Form Foam::operator*(const Foam::VectorSpace<Form, Cmpt, nCmpt>&, Foam::scalar) inline Form operator* ^ /home/hluo/.local/easybuild/software/OpenFOAM/2.3.1-foss-2016a/OpenFOAM-2.3.1/src/OpenFOAM/lnInclude/VectorSpaceI.H:565:13: note: template argument deduction/substitution failed: Test-vector.C:18:24: note: cannot convert ‘a’ (type ‘Foam::vector {aka Foam::Vector<double>}’) to type ‘Foam::scalar {aka double}’ Info << "a*a = " << a*a << endl; ^ make: *** [Make/linux64GccDPOpt/Test-vector.o] Error 1
|
报错的意思是我们找Foam::Vector<double>
与Foam::Vector<double>
之间的operator*
找不到,只能有后面vector前面scalar的operator*
(分别在VectorSpaceI.H:552和VectorSpaceI.H:565),但因为vector,tensor和VectorSpace是由一种复杂的方式耦合在一起的,所以并没有报一个找不到俩vector的外积。但如果加上”tensor.H”一切都对了,输出结果:
1 2 3
| $ Test-vector a = (1 2 3) a*a = (1 2 3 2 4 6 3 6 9)
|
List
List
and UList
are different. List a(10)
then a.append(something)
will append to the tail of the initialized a
resulting a a[11]=something
相互引用的数据
1 2 3 4 5 6 7 8 9 10
| const fvMesh & mesh = U.mesh();
const surfaceScalarField& phi = runTime.db().lookupObject<surfaceScalarField>("phi");
this->db()
const scalar t = this->db().time().timeOutputValue()
|
几个坑
scalarExplicitSetValue : 源fvOptions/constraints/general/explicitSetValue
,居然连field
不设置都可以不报错..
fixedTemperatureConstraint : 是针对能量方程的constraint,即compressible solvers
一些继承关系疏理
singlePhaseTransportModel : nonNewtonianIcoFoam里面通过singlePhaseTransportModel来初始化流变模型,与viscosityModel
没有继承关系,因此singlePhaseTransportModel
不能用Foam::tmp<Foam::volScalarField> Foam::viscosityModel::strainRate()
这个方法。然而BirdCarreau
是viscosityModel
的子类,它是如何被singlePhaseTransportModel
包装起来的呢?
Pstream : 继承自UPstream
(内有方法:parRun(), master(), procNo()
…),不过通常用的时候这样用Pstream::master()
,用于比如输出到文件而不想因为mpi多次重复输出
Pirating
timeDir
如果小心输出时间步全部刚好差了0.01怎么办,例如[5,7]变成了[5.01,7.01],timeDir里面的U,p
都有个冠冕堂皇的location
的值对应时间。但其实可以把目录5.01
改成5
,7.01
改成7
,这时比如要续算,solver便认为对应的时间步其实是5
和7
。似乎sample这些utility也可以这样骗[待确认]。
一句话:外面的目录名是个“货真价实”的幌子,可以欺骗solver和utility
setFields
setFields用于在internalField里面写一个值。选择区域最常用的估计是boxToCell
,但定义box
有格式
1 2 3
| box (x1 y1 z1) (x2 y2 z2);
|
如果不符合上述规范,不会有报错,但不会在internalField里面有写任何的值
这里给出一个符合规范的setFieldsDict
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| FoamFile { version 2.0; format ascii; class dictionary; location "system"; object setFieldsDict; }
defaultFieldValues ( volScalarFieldValue T 0 );
regions ( boxToCell { box (-0.004 -0.08 -0.004) (0.004 -0.004 0.004); fieldValues ( volScalarFieldValue T 1 ); } );
|
probes
OpenFOAM本身的probes如果刚好碰上网格边上会有warning
属于functionObjects,在simu的同时运行,如果想要postProcssing,用execFlow…,但是:需要在constrolDict里面加入,自己写的BC会有奇怪报错…
对策:用wyldckat写的,摘干净的最通用的ExecFunctionObjects,把所有的functionObject都放在system/system/controlDict.functions,这样就可以分开了
reconstructPar与decomposePar
这俩不互为反函数,如果要用这种方式完成binary和ascii的转换,一定double check BC,因为BC可能被改写
一句话:reconstructPar/decomposePar 一定要看好BC
reconstructed case or decomposed case
paraview中有这个选项,然而,经历reconstructPar或者decomposePar后BC可能被篡改,所以单纯看边界的话有可能会被吓倒在地,怎么可能!同一个时间步decompose情况下用paraview看BC上的值好好的,结果reconstructPar之后paraview一看……面目全非!这种差异在numberOfSubdomains更容易出现(例如120),而设置成4就有可能没问题,可能可以算作bug。当然也许是我写出来的bug,毕竟有的边界value要生成随机数,我随机种子又没变。
一句话:当你看到reconstruct之后的BC不对的时候,即使processor*已经被清空了,此时也不用慌,再一次decomposePar帮你恢复BC(至于numberOfSubdomains设置为多少,最好跟并行算的时候一样吧)。即使又出了什么幺蛾子BC的值不能恢复,至少internalField的值不会被动到
上图,这个就是我并行120个核算出来之后想都没想就reconstructPar的效果,一脸懵

但把原数据也就是放在processor*里面的数据用paraview来看的时候就变成了下图,用numberOfSubdomains=120
再重新decomposePar也是一样的效果

同一个类的不同变种编译为不同名字的lib
如上,如果想要将多个lib混用在一个case里面,在system/controlDict
里面都加入lib是必须的,但其实这样不行,做不到混用。如何做到呢?需要修改类的名字,并与makePatchTypeField(fvPatchVectorField, pVFvPatchVectorField2Dpf_Port1)这个macro function对应,这样不仅lib名字不同其实里面的类也不同,这样就可以混用了。
cyclic
U,p
设置周期性条件后,在生成的数据里type为cyclic
你会发现U,p
都没有value,仅有一个type (phi
却有,也是cyclic
类型).那为啥我用patchIntegrate U inlet
又不是零呢?经过仔细地看源码和比对
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
|
Info << U.boundaryField()[patchLabel] << endl;
vectorField& Ub_cyclic = U.boundaryField()[patchLabel] Info << Ub_cyclic << endl;
Info << sum(U.boundaryField()[patchLabel]) << endl; Info << sum(Ub_cyclic) << endl;
|
这里得出的结论是:U
和p
在数据文件里面,boundaryField项的其中一个patch上有可能仅有type(例如cyclic),但没有value,这不意味着U.boundaryField()[cyclic patch]
上面没有值。简单地来说:当你看数据文件里面边界上没有值,并不是真的没有值,边界上还可以算通量呢!
PS : phi
同为cyclic
类型,但边界上却有值,且value写在数据文件里