Palabos案例解析(三)damBreak3d.cpp案例

damBreak案例

逐行註釋解析

文件位置:/palabos-v2.0r0/examples/showCases/vofMultiPhase/damBreak3d.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/>.
*/

/* The breaking dam free surface problem. This code demonstrates the basic usage of the
 * free surface module in Palabos. Surface tension and contact angles are optional.
 */

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

using namespace plb;

// 此處爲帶外力項的格子描述符,
// 嗯,其實這個那個不帶外力項的,區別就是,這個格子描述符爲每一個元胞,
// 多分配了一個表示外力的張量,也就是一個長度爲3的數組
#define DESCRIPTOR descriptors::ForcedD3Q19Descriptor

typedef double T;

// Smagorinsky constant for LES model.
const T cSmago = 0.14;

// Physical dimensions of the system (in meters).
const T lx = 3.22;
const T ly = 1.0;
const T lz = 1.0;

const T rhoEmpty = T(1);

plint writeImagesIter   = 10;
plint getStatisticsIter = 20;

plint maxIter;
plint N;
plint nx, ny, nz;
T delta_t, delta_x;
Array<T,3> externalForce;
T nuPhys, nuLB, tau, omega, Bo, surfaceTensionLB, contactAngle;

std::string outDir;
plint obstacleCenterXYplane, obstacleLength, obstacleWidth, obstacleHeight, beginWaterReservoir, waterReservoirHeight;
plint waterLevelOne, waterLevelTwo, waterLevelThree, waterLevelFour;

void setupParameters() {
    delta_x = lz / N;
    // roundToInt是Palabos公用工程提供的對數字進行向下取整的函數,直接用即可,使用方法如下
    nx = util::roundToInt(lx / delta_x);
    ny = util::roundToInt(ly / delta_x);
    nz = util::roundToInt(lz / delta_x);

    // Gravity in lattice units.
    T gLB = 9.8 * delta_t * delta_t/delta_x;
    externalForce = Array<T,3>(0., 0., -gLB);
    tau            = (nuPhys*DESCRIPTOR<T>::invCs2*delta_t)/(delta_x*delta_x) + 0.5;
    omega          = 1./tau;
    nuLB           = (tau-0.5)*DESCRIPTOR<T>::cs2; // Viscosity in lattice units.

    surfaceTensionLB = rhoEmpty * gLB * N * N / Bo;

    obstacleCenterXYplane = util::roundToInt(0.744*N);
    obstacleLength        = util::roundToInt(0.403*N);
    obstacleWidth         = util::roundToInt(0.161*N);
    obstacleHeight        = util::roundToInt(0.161*N);
    beginWaterReservoir   = util::roundToInt((0.744+1.248)*N);
    waterReservoirHeight  = util::roundToInt(0.55*N);

    waterLevelOne   = util::roundToInt(0.496*N);
    waterLevelTwo   = util::roundToInt(2.*0.496*N);
    waterLevelThree = util::roundToInt(3.*0.496*N);
    waterLevelFour  = util::roundToInt((3.*0.496 + 1.150)*N);
}

// Specifies the initial condition for the fluid (each cell is assigned the
// flag "fluid", "empty", or "wall").
// 指定流體的初始條件(每個單元格被分配爲“流體”、“空”或“牆”)
// 初始化流體域,與其他案例不同,對於自由表面流和與此案例同文件的vof兩相流模型,其邊界是封裝爲特定值的
// freeSurfaceFlag::wall,freeSurfaceFlag::fluid,freeSurfaceFlag::empty分別爲對應的枚舉值,
// 其具體值,請看src中multiPhysics中freeSurface開頭的源代碼文件中的註釋說明
int initialFluidFlags(plint iX, plint iY, plint iZ) {
    // Place an obstacle on the left end, which is hit by the fluid.
    bool insideObstacle =
        iX >= obstacleCenterXYplane-obstacleWidth/2 &&
        iX <= obstacleCenterXYplane+obstacleWidth/2 &&
        iY >= ny/2-obstacleLength/2 &&
        iY <= ny/2+obstacleLength/2 &&
        iZ <= obstacleHeight+1;

    if (insideObstacle) {
        return freeSurfaceFlag::wall;
    }
    else if (iX >= beginWaterReservoir && iZ <= waterReservoirHeight) {
        return freeSurfaceFlag::fluid;
    }
    else {
        return freeSurfaceFlag::empty;
    }
}

void writeResults(MultiBlockLattice3D<T,DESCRIPTOR>& lattice, MultiScalarField3D<T>& volumeFraction, plint iT)
{
    static const plint nx = lattice.getNx();
    static const plint ny = lattice.getNy();
    static const plint nz = lattice.getNz();
    Box3D slice(0, nx-1, ny/2, ny/2, 0, nz-1);
    ImageWriter<T> imageWriter("leeloo");

	/*
	在這個地方,原始案例會以ppm的格式被輸出出來,爲了能得到可以直接看的gif格式的圖片,修改如下
	imageWriter.writeScaledGif(createFileName("u", iT, 6),
                               *computeVelocityNorm(lattice, slice),600,600); 

    imageWriter.writeScaledGif(createFileName("rho", iT, 6),
                               *computeDensity(lattice, slice),600,600);
                   
    imageWriter.writeScaledGif(createFileName("volumeFraction", iT, 6), 
    						   *extractSubDomain(volumeFraction, slice),600,600);
	*/
    imageWriter.writeScaledPpm(createFileName("u", iT, 6),
                               *computeVelocityNorm(lattice, slice));

    imageWriter.writeScaledPpm(createFileName("rho", iT, 6),
                               *computeDensity(lattice, slice));

    imageWriter.writeScaledPpm(createFileName("volumeFraction", iT, 6), 
    						   *extractSubDomain(volumeFraction, slice));
    						   
	/* 這部分是輸出stl的,一般我都會註釋掉
    // Use a marching-cube algorithm to reconstruct the free surface and write an STL file.
    std::vector<T> isoLevels;
    isoLevels.push_back((T) 0.5);
    typedef TriangleSet<T>::Triangle Triangle;
    std::vector<Triangle> triangles;
    isoSurfaceMarchingCube(triangles, volumeFraction, isoLevels, volumeFraction.getBoundingBox());
    TriangleSet<T>(triangles).writeBinarySTL(createFileName(outDir+"/interface", iT, 6)+".stl");
    */

    VtkImageOutput3D<T> vtkOut(createFileName("volumeFraction", iT, 6), 1.);
    vtkOut.writeData<float>(volumeFraction, "vf", 1.);
}

// 此函數是用來計算每時間步的統計值的,所使用的以FreeSurface開頭的函數,
// freeSurfaceAverageMass,freeSurfaceAverageDensity,
// freeSurfaceAverageDensity,freeSurfaceAverageVolumeFraction
// 是freeSurface模型自己封裝的函數,如果想看,
// 可以看src文件夾中multiPhysics中freeSurface開頭的源代碼文件中的註釋說明
void writeStatistics(FreeSurfaceFields3D<T,DESCRIPTOR>& fields) {
    pcout << " -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*- " << std::endl;
    T averageMass = freeSurfaceAverageMass<T,DESCRIPTOR>(fields.freeSurfaceArgs, fields.lattice.getBoundingBox());
    pcout << "Average Mass: " << averageMass  << std::endl;
    T averageDensity = freeSurfaceAverageDensity<T,DESCRIPTOR>(fields.freeSurfaceArgs, fields.lattice.getBoundingBox());
    pcout << "Average Density: " << std::setprecision(12) << averageDensity  << std::endl;

    T averageVolumeFraction = freeSurfaceAverageVolumeFraction<T,DESCRIPTOR>(fields.freeSurfaceArgs, fields.lattice.getBoundingBox());
    pcout << "Average Volume-Fraction: " << std::setprecision(12) << averageVolumeFraction  << std::endl;

    pcout << " -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*- " << std::endl;
}


int main(int argc, char **argv)
{
    plbInit(&argc, &argv);
    global::directories().setInputDir("./");

    if (global::argc() != 8) {
        pcout << "Error missing some input parameter\n";
    }

    try {
        global::argv(1).read(outDir);
        global::directories().setOutputDir(outDir+"/");

        global::argv(2).read(nuPhys);
        global::argv(3).read(Bo);
        global::argv(4).read(contactAngle);
        global::argv(5).read(N);
        global::argv(6).read(delta_t);
        global::argv(7).read(maxIter);
    }
    catch(PlbIOException& except) {
        pcout << except.what() << std::endl;
        pcout << "The parameters for this program are :\n";
        pcout << "1. Output directory name.\n";
        pcout << "2. kinematic viscosity in physical Units (m^2/s) .\n";
        pcout << "3. Bond number (Bo = rho * g * L^2 / gamma).\n";
        pcout << "4. Contact angle (in degrees).\n";
        pcout << "5. number of lattice nodes for lz .\n";
        pcout << "6. delta_t .\n";
        pcout << "7. maxIter .\n";
        pcout << "Reasonable parameters on a desktop computer are: " << (std::string)global::argv(0) << " tmp 1.e-5 100 80.0 40 1.e-3 80000\n";
        pcout << "Reasonable parameters on a parallel machine are: " << (std::string)global::argv(0) << " tmp 1.e-6 100 80.0 100 1.e-4 80000\n";
        exit (EXIT_FAILURE);
    }
    
    setupParameters();

    pcout << "delta_t= " << delta_t << std::endl;
    pcout << "delta_x= " << delta_x << std::endl;
    pcout << "delta_t*delta_t/delta_x= " << delta_t*delta_t/delta_x << std::endl;
    pcout << "externalForce= " << externalForce[2] << std::endl;
    pcout << "relaxation time= " << tau << std::endl;
    pcout << "omega= " << omega << std::endl;
    pcout << "kinematic viscosity physical units = " << nuPhys << std::endl;
    pcout << "kinematic viscosity lattice units= " << nuLB << std::endl;

    global::timer("initialization").start();

	// 離散塊結構,其實也就是MultiBlockLattice的基礎數據結構,
	// 基本上,不太需要自己寫這個,直接用MultiBlockLattice類的默認版即可,
	// 在這裏寫這個,是爲了作爲MultiBlockLattice的參數而新建的對象
    SparseBlockStructure3D blockStructure(createRegularDistribution3D(nx, ny, nz));
	
	// 動態類對象,此處使用的是大渦BGK
    Dynamics<T,DESCRIPTOR>* dynamics
        = new SmagorinskyBGKdynamics<T,DESCRIPTOR>(omega, cSmago);

    // If surfaceTensionLB is 0, then the surface tension algorithm is deactivated.
    // If contactAngle is less than 0, then the contact angle algorithm is deactivated.
    // FreeSurfaceFields是模型撰寫者自己寫的基礎數據存儲結構,
    // 你可以簡化理解爲裏面包含了一個MultiBlockLattice對象
    // 所以需要按照模型的要求輸入相關參數,參數分別爲,離散塊結構,上面定義的大渦BGK動態類,空的空間對應的密度,
    // 格子單位表面張力,接觸角,外力;
    FreeSurfaceFields3D<T,DESCRIPTOR> fields( blockStructure, dynamics->clone(), rhoEmpty,
                                              surfaceTensionLB, contactAngle, externalForce );
                                              
    // 這句註釋是,作者本來就註釋的,涉及更深層次的數據處理器設計,
    // 如果想了解,去看我有關於手冊中數據處理器的相關解釋
    //integrateProcessingFunctional(new ShortenBounceBack3D<T,DESCRIPTOR>, fields.lattice.getBoundingBox(), fields.freeSurfaceArgs, 0);

    // Set all outer-wall cells to "wall" (here, bulk-cells are also set to "wall", but it
    // doesn't matter, because they are overwritten on the next line).
    // 將所有格點的freesurfaceflag設置爲枚舉值wall(對於此處設計,因爲flag可覆寫,並不是此處就是最終設置)
    setToConstant(fields.flag, fields.flag.getBoundingBox(), (int)freeSurfaceFlag::wall);
    // In the bulk (all except outer wall layer), initialize the flags as specified by
    // the function "initialFluidFlags".
    // 因爲已經寫好了initialFluidFlags函數,這裏使用setToFunction,對所有的freesurfaceflag進行設置,
    // 函數最後的參數initialFluidFlags,就是向setToFunction傳遞的函數指針
    setToFunction(fields.flag, fields.flag.getBoundingBox().enlarge(-1), initialFluidFlags);
	
	// 同樣初始化,使用默認初始化
    fields.defaultInitialize();

    pcout << "Time spent for setting up lattices: "
          << global::timer("initialization").stop() << std::endl;
    T lastIterationTime = T();

    for (plint iT = 0; iT <= maxIter; ++iT) {
        global::timer("iteration").restart();

        T sum_of_mass_matrix = T();
        T lost_mass = T();
        if (iT % getStatisticsIter==0) {
            pcout << std::endl;
            pcout << "ITERATION = " << iT << std::endl;
            pcout << "Time of last iteration is " << lastIterationTime << " seconds" << std::endl;
            writeStatistics(fields);
            sum_of_mass_matrix = fields.lattice.getInternalStatistics().getSum(0);
            pcout << "Sum of mass matrix: " << sum_of_mass_matrix << std::endl;
            lost_mass = fields.lattice.getInternalStatistics().getSum(1);
            pcout << "Lost mass: " << lost_mass << std::endl;
            pcout << "Total mass: " << sum_of_mass_matrix + lost_mass << std::endl;
            pcout << "Interface cells: " << fields.lattice.getInternalStatistics().getIntSum(0) << std::endl;
        }

        if (iT % writeImagesIter == 0) {
            global::timer("images").start();
            writeResults(fields.lattice, fields.volumeFraction, iT);
            pcout << "Total time spent for writing images: "
                << global::timer("images").stop() << std::endl;
        }

        // This includes the collision-streaming cycle, plus all free-surface operations.
        // 此處的lattice其實是上面定義的離散塊結構,調用其函數executeInternalProcessors,
        // 其中包含了碰撞與遷移步驟,之後執行內部處理器
        fields.lattice.executeInternalProcessors();
        // 然後將本時間步的結果進行統計,因爲是MPI多進程並行,需要在計算末尾對所有的結果進行整合
        fields.lattice.evaluateStatistics();
        // 好像是對所有離散塊的計數器進行統一加一
        fields.lattice.incrementTime();

        lastIterationTime = global::timer("iteration").stop();
    }
}

說明

這個案例的基礎結構與原始的單相流和shanchen兩相流以及其他高階模型是不一樣的,唯一相近的是同文件夾中的fallingDroplet.cpp,可以對比着看,比這個還複雜一些,但是如果致力於自己寫高階數學模型,拿着個的一組源代碼進行學習,非常有幫助
個人認爲小朋友們不要太過於深究內部的代碼結構,非常複雜,可能你看完了,都要畢業了,這是前車之鑑,這個案例好在可以很簡單的研究大密度比的兩相流,但是必須捨棄其中密度比較小的一相,同時這個案例中的自由表面流模型,封裝了表面張力,接觸角等在兩相研究中非常難以寫代碼的部分,所以,爲了可以畢業,我選擇了這個模型,來堆數據,對於是否符合科學研究的需求,還需要小朋友們自己斟酌
以這個案例爲基礎編寫的代碼爲
畢業了——課題代碼開源(三)使用Palabos的自由表面流模型仿複雜多孔介質中的液滴滲透

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