参考:
感谢原作者们的无私引路和宝贵工作。
前置:
OpenFOAM开发编程基础00 基本实现和开发 | 𝓐𝓮𝓻𝓸𝓼𝓪𝓷𝓭 (aerosand.cn)
这里需要重申 OpenFOAM 初学者的 C++ 学习的个人建议:目前阶段,需要系统学习 C++,需要对 C++ 有基本和完整的认知,暂时不需要深入学习 C++,暂时不需要学习编程算法,不需要等到学完再开始 OpenFOAM。需要不断学习 C++,需要慢慢深入学习 C++,需要在不断的实践中积累 C++ 经验。
在做计算或者其他交互工作的时候,信息流的写入写出是不可避免的。比如从文件中读取计算参数、读取材料性质,又或者是向文件写入计算后的物理场。
所以,在了解应用的基本实现和开发后,我们讨论一下应用的输出输出方法。
本文依然基于 ubuntu22.04 系统,OpenFOAM 2306 版本(和 2212 版本,2206 版本几乎没有什么差别,不用担心)。此系列以后不再赘述。
建立本文的项目文件夹并进入
1 2 3 4 // terminal cd /home/aerosand/aerosand/ofsp mkdir 01_IO cd 01_IO
C++ 实现
C++ 通过输入输出流来实现从文件中读取或者向文件中写入。
文件流
在初学的时候我们就知道 C++ 提供 iostream
标准库,包含 cin
和 cout
方法,用于从标准输入中读取信息流,或者从标准输出中写入信息流。除此之外,C++ 还提供 fstream
标准库,用于外部文件和信息流之间的交互。
ofstream
表示输出文件流,用于创建文件并向文件写入信息
ifstream
表示输入文件流,用于从文件读取信息
fstream
表示通用文件流,同时具有写入写出的方法
项目实现
通过 vscode 的 C/C++ Project Generater
在 01_IO/
路径下新建项目 01_01_IO/
。
主源码 src/main.cpp
如下所示
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 #include <iostream> #include <string> #include <fstream> #include <cassert> int main (int argc, char *argv[]) { std::string name; int year; std::string var[3 ]; std::fstream infile; infile.open ("input.dat" ,std::fstream::in); if (!infile) { std::cout << "# WARNING: NO input!" << std::endl; assert (infile); } infile >> var[0 ] >> name; infile >> var[1 ] >> year; infile.close (); std::cout << var[0 ] << "\t\t@\t" << var[1 ] << std::endl; std::cout << name << "\t@\t" << year << std::endl; std::fstream outfile; outfile.open ("output.dat" ,std::fstream::out); outfile << var[0 ] <<"\t" << name << std::endl; outfile << var[1 ] << "\t" << year << std::endl; outfile.close (); }
为了保证文件读取正常,我们需要提供相应的 input.dat
文件,放在项目根目录下即可。文件内容如下
终端编译并运行此项目
运行结果如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 g++ -std=c++17 -Wall -Wextra -g -Iinclude -c -MMD src/main.cpp -o src/main.o src/main.cpp: In function ‘int main(int, char**)’: src/main.cpp:6:14: warning: unused parameter ‘argc’ [-Wunused-parameter] 6 | int main(int argc, char *argv[]) | ~~~~^~~~ src/main.cpp:6:26: warning: unused parameter ‘argv’ [-Wunused-parameter] 6 | int main(int argc, char *argv[]) | ~~~~~~^~~~~~ g++ -std=c++17 -Wall -Wextra -g -Iinclude -o output/main src/main.o -Llib Executing all complete! ./output/main arg0 @ arg1 Aerosand @ 2023 Executing run: all complete!
我们找到输出结果(除了编译语句,还有其他信息提醒有未使用变量,我们确实没有使用,不用在意,以后不再赘述),
1 2 arg0 @ arg1 Aerosand @ 2023
同时发现项目根目录下生成了 output.dat
文件,打开看到其中内容为
1 2 3 arg0 Aerosand arg1 2023
该项目满足我们的要求。
OpenFOAM 实现
OpenFOAM 的应用一般需要从 case 中读取字典、从边界条件库中读取边界数据,向 case 中输出计算结果等等。
OpenFOAM 是怎么实现从文件夹读取和写入的呢?OpenFOAM 的读取和写入更加高级,按关键词进行索引查找的方法直接封装在了相关的类中,直接使用方法即可,暂时不用深究到实现的代码层面。
应用准备
1 2 3 4 5 // terminal foamNewApp 01_02_IO cd 01_02_IO cp -r $FOAM_TUTORIALS/incompressible/icoFoam/cavity/cavity debug_case code .
文件结构如下
1 2 3 4 5 6 7 8 9 |- 01_02_IO/ |- debug_case/ |- 0/ |- constant/ |- system/ |- Make/ |- files |- options |- 01_02_IO.C
脚本和说明
新建脚本
1 2 // terminal code _appmake.sh _appclean.sh _caserun.sh _caseclean.sh README.md
_appmake.sh
脚本主要负责应用的编译,暂时写入如下内容
_appclean.sh
脚本主要负责应用的清理,暂时写如下内容
_caserun.sh
脚本主要是负责应用编译成功后,调试算例的运行,暂时写入如下内容
1 2 3 4 5 6 #!/bin/bash blockMesh -case debug_case | tee debug_case/log.mesh echo "Meshing done." 01_02_IO -case debug_case | tee debug_case/log.run
_caseclean.sh
脚本主要是负责清理应用到到编译前状态,如果应用要修改,那么测试算例也要还原到运行前的状态,所以暂时写入如下内容
1 2 3 4 5 6 #!/bin/bash wclean rm -rf debug_case/log.* foamCleanTutorials debug_case echo "Cleaning done."
README.md
写入需要说明的内容。
以后除非有特别情况,不再赘述脚本和说明
主源码
主源码如下
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 115 #include "fvCFD.H" int main (int argc, char *argv[]) { #include "setRootCase.H" #include "createTime.H" #include "createMesh.H" const word dictName ("customProperties" ) ; IOobject dictIO ( dictName, runTime.constant (), mesh, IOobject::MUST_READ, IOobject::NO_WRITE ); if (!dictIO.typeHeaderOk <dictionary>(true )) { FatalErrorIn (args.executable ()) << "Cannot open specified dictionary" << dictName << exit (FatalError); } dictionary myDictionary; myDictionary = IOdictionary (dictIO); Info<< "Reading myProperties\n" << endl; IOdictionary myProperties ( IOobject ( "myProperties" , runTime.constant (), mesh, IOobject::MUST_READ, IOobject::NO_WRITE ) ); word solver; myProperties.lookup ("application" ) >> solver; word format (myProperties.lookup("writeFormat" )) ; scalar timeStep (myProperties.lookupOrDefault("deltaT" , scalar(0.01 ))) ; bool ifPurgeWrite (myProperties.lookupOrDefault<Switch>("purgeWrite" ,0 )) ; List<scalar> pointList (myProperties.lookup("point" )) ; HashTable<vector,word> sourceField (myProperties.lookup("source" )) ; vector myVec = vector (myProperties.subDict ("subDict" ).lookup ("myVec" )); Info<< nl << "application: " << solver << nl << nl << "writeFormat: " << format << nl << nl << "deltaT: " << timeStep << nl << nl << "purgeWrite: " << ifPurgeWrite << nl << nl << "point: " << pointList << nl << nl << "source: " << sourceField << nl << nl << "myVec: " << myVec << nl << nl << endl; fileName outputDir = runTime.path ()/"processing" ; mkDir (outputDir); autoPtr<OFstream> outputFilePtr; outputFilePtr.reset (new OFstream (outputDir/"myOutPut.dat" )); outputFilePtr () << "processing/myOutPut.dat" << endl; outputFilePtr () << "0 1 2 3 ..." << endl; sourceField.insert ("U3" , vector (1 , 0.0 , 0.0 )); outputFilePtr () << sourceField << endl; Info<< nl; runTime.printExecutionTime (Info); Info<< "End\n" << endl; return 0 ; }
字典文件
提供字典文件 debug_case/constant/customProperties
,该字典没有读取写入操作,只需要写上正确的文件头,内容留空处理。
1 2 3 4 5 6 7 8 9 FoamFile { version 2.0 ; format ascii; class dictionary ; location "constant" ; object customProperties; }
字典文件 debug_case/constant/myProperties
,内容如下
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 FoamFile { version 2.0 ; format ascii; class dictionary ; location "constant" ; object myProperties; } application icoFoam; writeFormat ascii; purgeWrite 1 ; point ( 0 1 2 ); source ( U1 (0 0 0 ) U2 (1 0 0 ) ); subDict { myVec (0.0 0.0 1.0 ); }
编译运行
终端运行
1 2 3 // terminal sh _appmake.sh sh _caserun.sh
以后不再赘述简单的执行命令
终端显示结果如下
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 // terminal Create time Create mesh for time = 0 Reading myProperties application: icoFoam writeFormat: ascii deltaT: 0.01 purgeWrite: 1 point: 3(0 1 2) source: 2 ( U1 (0 0 0) U2 (1 0 0) ) myVec: (0 0 1) ExecutionTime = 0.01 s ClockTime = 0 s End
另外算例文件夹下有了一个新建文件夹 debug_case/processing/
,路径下的 myOutPut.dat
内容如下
1 2 3 4 5 6 7 8 9 10 processing/myOutPut.dat 0 1 2 3 ...3 ( U1 (0 0 0 )U3 (1 0 0 )U2 (1 0 0 ))
小结
我们同样从最一般的 C++ 基础情况切入,了解了基于文件流的输入输出,也讨论了 OpenFOAM 设计的文件流输入输出方法,以后会不断地使用文件流输入输出。
Copyright Notice: 此文章版权归aerosand.cn所有,如有转载,请注明来自原作者