Palabos案例解析(一)permeability.cpp案例

Permeability案例

我輸入的結構數據 計算得到的流速雲圖

逐行註釋解析

文件位置:/palabos-v2.0r0/examples/tutorial/permeability.cpp

/* This file is part of the Palabos library.
 *
 * Copyright (C) 2011-2017 FlowKit Sarl
 * Route d'Oron 2
 * 1010 Lausanne, Switzerland
 * E-mail contact: [email protected]
 *
 * The most recent release of Palabos can be downloaded at 
 * <http://www.palabos.org/>
 *
 * The library Palabos is free software: you can redistribute it and/or
 * modify it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * The library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

/* Main author: Wim Degruyter */

#include "palabos3D.h"
#include "palabos3D.hh"

#include <vector>
#include <cmath>
#include <cstdlib>

using namespace plb;

typedef double T;
#define DESCRIPTOR descriptors::D3Q19Descriptor 
//格子描述符,用於規範計算域內每個元胞內的數據及偏移值,數據是以連續表的形式存儲於計算域當中

// This function object returns a zero velocity, and a pressure which decreases
//   linearly in x-direction. It is used to initialize the particle populations.

//提供一個壓力梯度類,後文以函數指針方式調用,運用設計模式中的命令模式(ps:初學者不用看這條解釋)
class PressureGradient {
public:
    PressureGradient(T deltaP_, plint nx_) : deltaP(deltaP_), nx(nx_)
    { }
    
    //運算符重載
    void operator() (plint iX, plint iY, plint iZ, T& density, Array<T,3>& velocity) const
    {
        velocity.resetToZero();
        density = (T)1 - deltaP*DESCRIPTOR<T>::invCs2 / (T)(nx-1) * (T)iX;
    }
private:
    T deltaP;
    plint nx;
};

//讀取結構的函數,傳入文件讀取名稱,傳出3D標量場數據
void readGeometry(std::string fNameIn, std::string fNameOut, MultiScalarField3D<int>& geometry)
{
    const plint nx = geometry.getNx();
    const plint ny = geometry.getNy();
    const plint nz = geometry.getNz();

    Box3D sliceBox(0,0, 0,ny-1, 0,nz-1); //詳情請見geometry3D.h文件
    //Box3D是用於規定操作執行區域,一般每個對數據操作都會用其作爲參數,此處選取yz面作爲輸入基準
    //但輸入數據有很多種其他的寫法,初學者按照這個仿寫就可以,C++學好了,再嘗試新方法
    
    std::auto_ptr<MultiScalarField3D<int> > slice = generateMultiScalarField<int>(geometry, sliceBox);
    //std::auto_ptr智能指針,用於在使用特定堆對象後,自動釋放內存防止內存泄漏
    //generateMultiScalarField函數詳見multiBlockGenerator文件系列,用於在堆區新建對象
    
    plb_ifstream geometryFile(fNameIn.c_str()); 
    //Palabos重載的輸入流函數,直接用即可
    
    //一片一片流輸入數據,因爲原案例是從圖來的,所以輸入寫成這樣
    //自己的數據如果是一個數據塊,寫可以直接寫一個三重循環的流輸入,不用那麼麻煩
    for (plint iX=0; iX<nx-1; ++iX) {
        if (!geometryFile.is_open()) {
            pcout << "Error: could not open geometry file " << fNameIn << std::endl;
            exit(EXIT_FAILURE);
        }
        //流輸入,slice此處是一個指針,也是一個數組,*ptr與ptr[]是同等操作(C++ Primer)
        geometryFile >> *slice;
        
        //copy函數,詳見copyData.cpp文件,用於將相同的場數據進行復制轉移
        //此處是用於轉移標量場數據
        copy(*slice, slice->getBoundingBox(), geometry, Box3D(iX,iX, 0,ny-1, 0,nz-1));
    }

    {
    	//vtk輸出部分,此處就是直接用,在別的地方以及數據輸出也都差不多
        VtkImageOutput3D<T> vtkOut("porousMedium", 1.0);
        vtkOut.writeData<float>(*copyConvert<int,T>(geometry, geometry.getBoundingBox()), "tag", 1.0);
    }

	//stl輸出部分,因爲我不涉及stl,所以我並沒有看,一般我會把這部分註釋掉,減少運行時間
    {
        std::auto_ptr<MultiScalarField3D<T> > floatTags = copyConvert<int,T>(geometry, geometry.getBoundingBox());
        std::vector<T> isoLevels;
        isoLevels.push_back(0.5);
        typedef TriangleSet<T>::Triangle Triangle;
        std::vector<Triangle> triangles;
        Box3D domain = floatTags->getBoundingBox().enlarge(-1);
        domain.x0++;
        domain.x1--;
        isoSurfaceMarchingCube(triangles, *floatTags, isoLevels, domain);
        TriangleSet<T> set(triangles);
        std::string outDir = fNameOut + "/";
        set.writeBinarySTL(outDir + "porousMedium.stl");
    }
}

void porousMediaSetup(MultiBlockLattice3D<T,DESCRIPTOR>& lattice,
        OnLatticeBoundaryCondition3D<T,DESCRIPTOR>* boundaryCondition,
        MultiScalarField3D<int>& geometry, T deltaP)
{
    const plint nx = lattice.getNx();
    const plint ny = lattice.getNy();
    const plint nz = lattice.getNz();

    pcout << "Definition of inlet/outlet." << std::endl;
    Box3D inlet (0,0, 1,ny-2, 1,nz-2);
    //使用OnLatticeBoundaryCondition3D類對象中的相應函數來設置邊界條件
    boundaryCondition->addPressureBoundary0N(inlet, lattice);
    setBoundaryDensity(lattice, inlet, (T) 1.);

    Box3D outlet(nx-1,nx-1, 1,ny-2, 1,nz-2);
    //使用OnLatticeBoundaryCondition3D類對象中的相應函數來設置邊界條件
    boundaryCondition->addPressureBoundary0P(outlet, lattice);
    setBoundaryDensity(lattice, outlet, (T) 1. - deltaP*DESCRIPTOR<T>::invCs2);

    pcout << "Definition of the geometry." << std::endl;
    // Where "geometry" evaluates to 1, use bounce-back.
    //通過掩碼的方式來爲特定節點重新設置動態類
    defineDynamics(lattice, geometry, new BounceBack<T,DESCRIPTOR>(), 1);
    // Where "geometry" evaluates to 2, use no-dynamics (which does nothing).
    defineDynamics(lattice, geometry, new NoDynamics<T,DESCRIPTOR>(), 2);

    pcout << "Initilization of rho and u." << std::endl;
    //平衡態初始化
    initializeAtEquilibrium( lattice, lattice.getBoundingBox(), PressureGradient(deltaP, nx) );
	
	//這個是在每個案例中都必須有的,此處爲調用MultiBlockLattice3D類對象內的initialize來對種羣進行初始化
    lattice.initialize();
    delete boundaryCondition;
}

void writeGifs(MultiBlockLattice3D<T,DESCRIPTOR>& lattice, plint iter)
{
    const plint nx = lattice.getNx();
    const plint ny = lattice.getNy();
    const plint nz = lattice.getNz();

    const plint imSize = 600;
    ImageWriter<T> imageWriter("leeloo");

    // Write velocity-norm at x=0.
    imageWriter.writeScaledGif(createFileName("ux_inlet", iter, 6),
            *computeVelocityNorm(lattice, Box3D(0,0, 0,ny-1, 0,nz-1)),
            imSize, imSize );

    // Write velocity-norm at x=nx/2.
    imageWriter.writeScaledGif(createFileName("ux_half", iter, 6),
            *computeVelocityNorm(lattice, Box3D(nx/2,nx/2, 0,ny-1, 0,nz-1)),
            imSize, imSize );
}

void writeVTK(MultiBlockLattice3D<T,DESCRIPTOR>& lattice, plint iter)
{
    VtkImageOutput3D<T> vtkOut(createFileName("vtk", iter, 6), 1.);
    vtkOut.writeData<float>(*computeVelocityNorm(lattice), "velocityNorm", 1.);
    vtkOut.writeData<3,float>(*computeVelocity(lattice), "velocity", 1.);
}

T computePermeability(MultiBlockLattice3D<T,DESCRIPTOR>& lattice, T nu, T deltaP, Box3D domain )
{
    pcout << "Computing the permeability." << std::endl;

    // Compute only the x-direction of the velocity (direction of the flow).
    plint xComponent = 0;
    plint nx = lattice.getNx();

    T meanU = computeAverage(*computeVelocityComponent(lattice, domain, xComponent));

    pcout << "Average velocity     = " << meanU                         << std::endl;
    pcout << "Lattice viscosity nu = " << nu                            << std::endl;
    pcout << "Grad P               = " << deltaP/(T)(nx-1)              << std::endl;
    pcout << "Permeability         = " << nu*meanU / (deltaP/(T)(nx-1)) << std::endl;

    return meanU;
}

int main(int argc, char **argv)
{
    plbInit(&argc, &argv);

    if (argc!=7) {
        pcout << "Error missing some input parameter\n";
        pcout << "The structure is :\n";
        pcout << "1. Input file name.\n";
        pcout << "2. Output directory name.\n";
        pcout << "3. number of cells in X direction.\n";
        pcout << "4. number of cells in Y direction.\n";
        pcout << "5. number of cells in Z direction.\n";
        pcout << "6. Delta P .\n";
        pcout << "Example: " << argv[0] << " twoSpheres.dat tmp/ 48 64 64 0.00005\n";
        exit (EXIT_FAILURE);
    }
    std::string fNameIn  = argv[1];
    std::string fNameOut = argv[2];

    const plint nx = atoi(argv[3]);
    const plint ny = atoi(argv[4]);
    const plint nz = atoi(argv[5]);
    const T deltaP = atof(argv[6]);

    global::directories().setOutputDir(fNameOut+"/");

    const T omega = 1.0;
    const T nu    = ((T)1/omega- (T)0.5)/DESCRIPTOR<T>::invCs2;

    pcout << "Creation of the lattice." << std::endl;
    
    //MultiBlockLattice3D類,是整個計算的計算主體,用於承載計算的全部操作,此處爲在棧區新建對象
    //詳情請見MultiBlockLattice系列文件
    //此處中的BGKdynamics爲在定義MultiBlockLattice3D同時,設置粒子輸運的背景動態類
    MultiBlockLattice3D<T,DESCRIPTOR> lattice(nx,ny,nz, new BGKdynamics<T,DESCRIPTOR>(omega));
    
    // Switch off periodicity.
    //週期性邊界條件的開關,用戶手冊裏有詳細說明
    lattice.periodicity().toggleAll(false);

    pcout << "Reading the geometry file." << std::endl;
    
    //MultiScalarField3D類,此處用於存儲結構數據
    //詳情請見multiDataField文件系列
    MultiScalarField3D<int> geometry(nx,ny,nz);
    readGeometry(fNameIn, fNameOut, geometry);

    pcout << "nu = " << nu << std::endl;
    pcout << "deltaP = " << deltaP << std::endl;
    pcout << "omega = " << omega << std::endl;
    pcout << "nx = " << lattice.getNx() << std::endl;
    pcout << "ny = " << lattice.getNy() << std::endl;
    pcout << "nz = " << lattice.getNz() << std::endl;
	
	//此處爲調用上面設置好的porousMediaSetup函數
    porousMediaSetup(lattice, createLocalBoundaryCondition3D<T,DESCRIPTOR>(), geometry, deltaP);

    // The value-tracer is used to stop the simulation once is has converged.
    // 1st parameter:velocity
    // 2nd parameter:size
    // 3rd parameters:threshold
    // 1st and second parameters ae used for the length of the time average (size/velocity)
    //原註釋寫的很清楚,用於停止計算,設置的追蹤函數
    util::ValueTracer<T> converge(1.0,1000.0,1.0e-4);

    pcout << "Simulation begins" << std::endl;
    plint iT=0;

    const plint maxT = 30000;
    for (;iT<maxT; ++iT) {
        if (iT % 20 == 0) {
            pcout << "Iteration " << iT << std::endl;
        }
        if (iT % 500 == 0 && iT>0) {
            writeGifs(lattice,iT);
        }
		
		//此處爲所有計算的開始點,調用MultiBlockLattice3D類對象內的collideAndStream來開啓演化計算
        lattice.collideAndStream();
        converge.takeValue(getStoredAverageEnergy(lattice),true);

        if (converge.hasConverged()) {
            break;
        }
    }

    pcout << "End of simulation at iteration " << iT << std::endl;

    pcout << "Permeability:" << std::endl << std::endl;
    computePermeability(lattice, nu, deltaP, lattice.getBoundingBox());
    pcout << std::endl;

    pcout << "Writing VTK file ..." << std::endl << std::endl;
    writeVTK(lattice,iT);
    pcout << "Finished!" << std::endl << std::endl;

    return 0;
}

輸入文件的形式

在不同的案例中結構相關的flag的值設置是不同的,此處的設置爲,空隙0,固體邊界1,固體實體2
可輸入的文件格式,數據之間由空白字符相分割(空白字符:換行\n,製表\t,空格等),數據點數量等同於設置的計算域格點數量,也就是Nx* Ny *Nz,如果按照當前案例的輸入來編寫,循環的順序是,x,y,z

for(int x=0;x<nx;x++){
	for(int y=0;y<ny;y++){
		for(int z=0;z<nz;z++){
			//此處放你要輸出的數據流
		}
	}
}

原始案例內matlab圖像處理程序

調用方式createDAT(48,‘twoSpheres’,‘twoSpheres’,‘twoSpheres.dat’)
即可輸出原始案例中的結構,注意此處僅僅是用來對一組圖片進行處理的程序。如果自己有結構,不要看這個。
補充,createDAT的參數,createDAT(numFiles,path,baseInput,baseOutput)
第一個,48,因爲原始案例給了48張圖片,第二個,‘twoSpheres’,因爲放在.m同級目錄下的twoSphere文件夾下,第三個,‘twoSpheres’,因爲文件名爲twoSphere00xx.bmp,第四個,‘twoSpheres.dat’,你要輸出的文件。

補丁

問題1:windows無法命令符運行。。。

方法一:

if (argc!=7) {
        pcout << "Error missing some input parameter\n";
        pcout << "The structure is :\n";
        pcout << "1. Input file name.\n";
        pcout << "2. Output directory name.\n";
        pcout << "3. number of cells in X direction.\n";
        pcout << "4. number of cells in Y direction.\n";
        pcout << "5. number of cells in Z direction.\n";
        pcout << "6. Delta P .\n";
        pcout << "Example: " << argv[0] << " twoSpheres.dat tmp/ 48 64 64 0.00005\n";
        exit (EXIT_FAILURE);
    }
    std::string fNameIn  = argv[1];
    std::string fNameOut = argv[2];

    const plint nx = atoi(argv[3]);
    const plint ny = atoi(argv[4]);
    const plint nz = atoi(argv[5]);
    const T deltaP = atof(argv[6]);

改成

	std::string fNameIn  = "twoSpheres.dat";
    std::string fNameOut = "tmp/";

    const plint nx = 48;
    const plint ny = 64;
    const plint nz = 64;
    const T deltaP = 0.00005;

我的辦法比較笨,這裏就是直接改代碼,把你想要的值直接寫進程序。。。

方法二:
此方法不用修改代碼。。。
此外,如果多dos比較熟悉,可以在運行輸入cmd,然後調出命令行模式,從而直接使用代碼對已生成的.exe執行文件進行運行操作,而不是雙擊執行,對於參數,你可以直接命令行使用你生成的.exe文件,參數輸入錯誤,他會給提示,按提示和標準的參數輸入格式輸入即可。注:請在執行文件位置新建tmp文件夾,然後對於那個tmp參數,如果不想改,需要cd到你執行文件的目錄下,然後./執行文件 輸參數,即可正常運行。或者直接將tmp參數改爲絕對路徑的對應位置,即你tmp文件夾,地址欄裏的東西,即可。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章