OpenFOAM開發(fā)編程基礎(chǔ)01 輸入輸出

和參考:

  • 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/#

感謝原作者們的無私引路和寶貴工作。

前置:OpenFOAM開發(fā)編程基礎(chǔ)00 基本實(shí)現(xiàn)和開發(fā) | ???????????????? (aerosand.cn)

為了消除間斷感保持學(xué)習(xí)連續(xù)性,上一節(jié)的內(nèi)容有所更新和勘誤。微信無法大量修正,詳情移步 aerosand.cn

這一次我們依然從C++開始,了解最基本的文件流輸出輸出。

這里需要重申OpenFOAM初學(xué)者的C++學(xué)習(xí)的個(gè)人建議:目前階段,需要系統(tǒng)學(xué)習(xí)C++,需要對C++有基本和完整的認(rèn)知,暫時(shí)不需要深入學(xué)習(xí)C++,暫時(shí)不需要學(xué)習(xí)編程算法,不需要等到學(xué)完再開始OpenFOAM。需要長期學(xué)習(xí)C++,需要慢慢深入學(xué)習(xí)C++,需要在不斷的實(shí)踐中積累C++經(jīng)驗(yàn)。

在做計(jì)算或者其他交互工作的時(shí)候,信息流的寫入寫出是不可避免的。比如從文件中讀取計(jì)算參數(shù)、讀取材料性質(zhì),又或者是向文件寫入計(jì)算后的物理場。

所以,在了解應(yīng)用的基本實(shí)現(xiàn)和開發(fā)后,我們討論一下應(yīng)用的輸出輸出方法。

本文依然基于 ubuntu22.04 系統(tǒng),OpenFOAM 2306 版本(和 2212 版本,2206 版本幾乎沒有什么差別,不用擔(dān)心)。OpenFOAM 11 版本改動(dòng)較大,會再其他系列討論。此系列以后不再贅述。

建立本文的項(xiàng)目文件夾并進(jìn)入

// terminalcd /home/aerosand/aerosand/ofspmkdir 01_IOcd 01_IO

ofsp: OpenFOAM Sharing Programming

本文的后續(xù)更新和勘誤參見 aerosand.cn,或點(diǎn)擊最后的【閱讀原文】。

C++ 實(shí)現(xiàn)

C++ 通過輸入輸出流來實(shí)現(xiàn)從文件中讀取或者向文件中寫入。

文件流

在初學(xué)的時(shí)候我們就知道 C++ 提供 iostream 標(biāo)準(zhǔn)庫,包含 cincout 方法,用于從標(biāo)準(zhǔn)輸入中讀取信息流,或者從標(biāo)準(zhǔn)輸出中寫入信息流。除此之外,C++ 還提供 fstream 標(biāo)準(zhǔn)庫,用于外部文件和信息流之間的交互。

  • ofstream 表示輸出文件流,用于創(chuàng)建文件并向文件寫入信息
  • ifstream 表示輸入文件流,用于從文件讀取信息
  • fstream 表示通用文件流,同時(shí)具有寫入寫出的方法

項(xiàng)目實(shí)現(xiàn)

通過 vscode 的 C/C++ Project Generater01_IO/ 路徑下新建項(xiàng)目 01_01_IO/

此插件的簡單介紹已更新到上一文章,詳見 aerosand.cn

主代碼 src/main.cpp 如下所示

#include <iostream> // IO標(biāo)準(zhǔn)庫#include <string> // 字符串庫#include <fstream> // 文件流庫#include <cassert> // 異常判斷庫
int main(int argc, char *argv[]){    std::string name;    int year;    std::string var[3];
   std::fstream infile; // 定義一個(gè)文件流    infile.open("input.dat",std::fstream::in); // 打開文件并準(zhǔn)備讀取    if (!infile) { // 異常判斷,如果打開失敗,則執(zhí)行下面        std::cout << "# WARNING: NO input!" << std::endl;        assert(infile); // 終止程序并拋出錯(cuò)誤信息(頻繁調(diào)用影響性能)    }    infile >> var[0] >> name;    infile >> var[1] >> year;    infile.close(); // 必須關(guān)閉文件
   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); // 打開文件并準(zhǔn)備寫入    outfile << var[0] <<"\t" << name << std::endl;    outfile << var[1] << "\t" << year << std::endl;    outfile.close(); // 必須關(guān)閉文件
// 現(xiàn)代 C++ 可以不寫 return 0;}

為了保證文件讀取正常,我們需要提供相應(yīng)的 input.dat 文件,放在項(xiàng)目根目錄下即可。文件內(nèi)容如下

arg0    Aerosandarg1    2023

終端編譯并運(yùn)行此項(xiàng)目

// terminalmake run

運(yùn)行結(jié)果如下

g++ -std=c++17 -Wall -Wextra -g -Iinclude -c -MMD src/main.cpp  -o src/main.osrc/main.cpp: In function ‘int main(int, char**)’:src/main.cpp:6:14: warning: unused parameter ‘a(chǎn)rgc’ [-Wunused-parameter]    6 | int main(int argc, char *argv[])      |          ~~~~^~~~src/main.cpp:6:26: warning: unused parameter ‘a(chǎn)rgv’ [-Wunused-parameter]    6 | int main(int argc, char *argv[])      |                    ~~~~~~^~~~~~g++ -std=c++17 -Wall -Wextra -g -Iinclude -o output/main src/main.o  -LlibExecuting all complete!./output/mainarg0		@	arg1Aerosand	@	2023Executing run: all complete!

我們找到輸出結(jié)果(除了編譯語句,還有其他信息提醒有未使用變量,我們確實(shí)沒有使用,不用在意,以后不再贅述),

arg0		@	arg1Aerosand	@	2023

同時(shí)發(fā)現(xiàn)項(xiàng)目根目錄下生成了 output.dat 文件,打開看到其中內(nèi)容為

arg0	Aerosandarg1	2023

該項(xiàng)目滿足我們的要求。

OpenFOAM 實(shí)現(xiàn)

OpenFOAM 的應(yīng)用一般需要從 case 中讀取字典、從邊界條件庫中讀取邊界數(shù)據(jù),向 case 中輸出計(jì)算結(jié)果等等。

OpenFOAM 是怎么實(shí)現(xiàn)從文件夾讀取和寫入的呢?OpenFOAM 的讀取和寫入更加抽象和高級,按關(guān)鍵詞進(jìn)行索引查找的方法直接封裝在了相關(guān)的類中,直接使用方法即可,暫時(shí)不用深究到實(shí)現(xiàn)的代碼層面。

應(yīng)用準(zhǔn)備

// terminalcd /home/aerosand/aerosand/ofsp/01_IO/foamNewApp 01_02_IOcd 01_02_IOcp -r $FOAM_TUTORIALS/incompressible/icoFoam/cavity/cavity debug_casecode .

文件結(jié)構(gòu)如下

|- 01_02_IO/	|- debug_case/		|- 0/		|- constant/		|- system/	|- Make/ 		|- files		|- options	|- 01_02_IO.C

腳本和說明

新建腳本

// terminalcode _appmake.sh _appclean.sh _caserun.sh _caseclean.sh README.md

_appmake.sh 腳本主要負(fù)責(zé)應(yīng)用的編譯,暫時(shí)寫入如下內(nèi)容

#!/bin/bash
wmake

_appclean.sh 腳本主要負(fù)責(zé)應(yīng)用的清理,暫時(shí)寫如下內(nèi)容

#!/bin/bash
wclean

_caserun.sh 腳本主要是負(fù)責(zé)應(yīng)用編譯成功后,調(diào)試算例的運(yùn)行,暫時(shí)寫入如下內(nèi)容

#!/bin/bash
blockMesh -case debug_case | tee debug_case/log.meshecho "Meshing done."
01_02_IO -case debug_case | tee debug_case/log.run

_caseclean.sh 腳本主要是負(fù)責(zé)清理應(yīng)用到到編譯前狀態(tài),如果應(yīng)用要修改,那么測試算例也要還原到運(yùn)行前的狀態(tài),所以暫時(shí)寫入如下內(nèi)容

#!/bin/bash
wcleanrm -rf debug_case/log.*foamCleanTutorials debug_caseecho "Cleaning done."

README.md 寫入需要說明的內(nèi)容。

以后除非有特別情況,不再贅述腳本和說明

主源碼

主源碼如下

#include "fvCFD.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
int main(int argc, char *argv[]){    #include "setRootCase.H"    #include "createTime.H"
   #include "createMesh.H"

   /*    * Reading from the dictionary    */    const word dictName("customProperties"); // 創(chuàng)建word類型變量保存字典名稱    IOobject dictIO // 創(chuàng)建IOobject類型變量,從參數(shù)列表初始化    (        dictName, // 字典名稱        runTime.constant(), // 字典位置        mesh, // 和mesh相關(guān)        IOobject::MUST_READ, // 必須從字典讀取        IOobject::NO_WRITE // 不向字典寫入    );
   if (!dictIO.typeHeaderOk<dictionary>(true))    {        FatalErrorIn(args.executable()) << "Cannot open specified dictionary"            << dictName << exit(FatalError);    }    // 如果字典文件在文件頭中指定的不是 dictionary 類型,則報(bào)錯(cuò)
   dictionary myDictionary;    myDictionary = IOdictionary(dictIO);    // 從IOobject變量中創(chuàng)建字典對象
   // 一般使用下面這種緊湊寫法    Info<< "Reading myProperties\n" << endl;    IOdictionary myProperties // 字典變量名和字典文件名取相同    (        IOobject        (            "myProperties",            runTime.constant(),            mesh,            IOobject::MUST_READ,            IOobject::NO_WRITE        )    );
   word solver; // 創(chuàng)建word類型變量    myProperties.lookup("application") >> solver;    // 從myProperties文件中查找到關(guān)鍵詞,并取值賦給solver
   word format(myProperties.lookup("writeFormat"));    // 或者寫成更緊湊的形式
   scalar timeStep(myProperties.lookupOrDefault("deltaT", scalar(0.01)));    // 也可以寫成    // scalar timeStep(myProperties.lookupOrDefault<scalar>("deltaT", 0.01));    // 如果字典中沒有提供這一關(guān)鍵詞,則使用此句提供的默認(rèn)值
   bool ifPurgeWrite(myProperties.lookupOrDefault<Switch>("purgeWrite",0));    // bool類型也可以按關(guān)鍵詞查找并讀取
   List<scalar> pointList(myProperties.lookup("point"));    // 列表也可以讀取
   HashTable<vector,word> sourceField(myProperties.lookup("source"));    // 哈希表也可以讀取(無所謂什么是哈希表,沒有關(guān)系)
// 輸出讀取的內(nèi)容    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        << endl;

   /*    * Writing to files    */    fileName outputDir = runTime.path()/"processing"; // 創(chuàng)建outputDir變量并賦值路徑    mkDir(outputDir); // 創(chuàng)建上句路徑的文件夾
   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,該字典沒有讀取寫入操作,只需要寫上正確的文件頭,內(nèi)容留空處理。

FoamFile{    version     2.0;    format      ascii;    class       dictionary;    location    "constant";    object      customProperties;}

字典文件 debug_case/constant/myProperties ,內(nèi)容如下

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));

編譯運(yùn)行

終端運(yùn)行

// terminalsh _appmake.shsh _caserun.sh

以后不再贅述簡單的執(zhí)行命令

終端顯示結(jié)果如下

// 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))


ExecutionTime = 0.01 s  ClockTime = 0 s
End

另外算例文件夾下有了一個(gè)新建文件夾 debug_case/processing/,路徑下的 myOutPut.dat 內(nèi)容如下

processing/myOutPut.dat0 1 2 3 ...
3(U1 (0 0 0)U3 (1 0 0)U2 (1 0 0))

小結(jié)

我們同樣從最一般的 C++ 基礎(chǔ)情況切入,了解了基于文件流的輸入輸出,也討論了 OpenFOAM 設(shè)計(jì)的文件流輸入輸出方法,以后會不斷地使用文件流輸入輸出。


文章來源: Aerosand 

登錄后免費(fèi)查看全文
立即登錄
App下載
技術(shù)鄰APP
工程師必備
  • 項(xiàng)目客服
  • 培訓(xùn)客服
  • 平臺客服

TOP