文件位置:/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: contact@flowkit.com
*
* The most recent release of Palabos can be downloaded at
*
*
* 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 .
*/
/* 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 isoLevels;
isoLevels.push_back((T) 0.5);
typedef TriangleSet::Triangle Triangle;
std::vector triangles;
isoSurfaceMarchingCube(triangles, volumeFraction, isoLevels, volumeFraction.getBoundingBox());
TriangleSet(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, 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的自由表面流模型仿复杂多孔介质中的液滴渗透