前言本人是正在借助AI学习Lattice Boltzmann method 和 Palabos的小白。在此将理解的Palabos Tutorial的内容记录下来留做备忘,并与大家分享,希望得到各位大神指点。欢迎随时指出其中的错误,也欢迎有相同目标的同学一起交流。 2.5.0 介绍在这里这里我们第一次通过数值模拟还原了一个著名物理情形:Poiseuille流。该流动发生在一个有不可滑动壁的通道内,并且流速与通道壁平行。该流动的解析解由沿通道壁平行的速度分量的抛物线状轮廓表示。在模拟中,解析解用于在域的入口和出口上采取 Dirichlet边界条件。作为初始条件,采用了零速度场,并开始模拟直至在域内的每个点上收敛以得到Poiseuille解。
请注意,模拟最初在初始条件中的零速度和边界条件中的有限速度之间存在不连续性。如果在调整参数时模拟变得数值不稳定,请尝试增加晶格分辨率或减小晶格单位中的速度。 2.5.1 tutorial_1_5.cpp 代码思路
代码中的主要函数包括:
总体来说,这段代码演示了如何使用Lattice Boltzmann方法模拟Poiseuille流,并将结果输出为动画和数据文件。 2.5.2 tutorial_1_5.cpp 代码结构这段C++代码是基于Palabos库的一个简单的二维泊肃叶流(Poiseuille flow)模拟。 2.5.2.1 包含头文件包括C++标准库中的iomanip和iostream,以及Palabos库中的相关头文件。 #include <iomanip>#include <iostream> #include <vector> #include "palabos2D.h" #include "palabos2D.hh" 2.5.2.2 命名空间将全局命名空间简化为“plb”和“std”。 using namespace plb;using namespace std; 2.5.2.3 类型定义和描述符定义数据类型T为double,并设置DESCRIPTOR为Palabos库中的D2Q9Descriptor。 typedef double T;#define DESCRIPTOR plb::descriptors::D2Q9Descriptor 2.5.2.4 函数和类定义
{ T y = (T)iY / parameters.getResolution(); return 4. * parameters.getLatticeU() * (y - y * y); }
template <typename T> class PoiseuilleVelocity { public: PoiseuilleVelocity(IncomprFlowParam<T> parameters_) : parameters(parameters_) { } /// This version of the operator returns the velocity only, /// to instantiate the boundary condition. void operator()([[maybe_unused]] plint iX, plint iY, Array<T, 2> &u) const { u[0] = poiseuilleVelocity(iY, parameters); u[1] = T(); } /// This version of the operator returns also a constant value for /// the density, to create the initial condition. void operator()([[maybe_unused]] plint iX, plint iY, T &rho, Array<T, 2> &u) const { u[0] = poiseuilleVelocity(iY, parameters); u[1] = T(); rho = (T)1; } private: IncomprFlowParam<T> parameters; };
MultiBlockLattice2D<T, DESCRIPTOR> &lattice, IncomprFlowParam<T> const ¶meters, OnLatticeBoundaryCondition2D<T, DESCRIPTOR> &boundaryCondition) { // Create Velocity boundary conditions. boundaryCondition.setVelocityConditionOnBlockBoundaries(lattice); // Specify the boundary velocity. setBoundaryVelocity(lattice, lattice.getBoundingBox(), PoiseuilleVelocity<T>(parameters)); // Create the initial condition. initializeAtEquilibrium(lattice, lattice.getBoundingBox(), PoiseuilleVelocity<T>(parameters)); lattice.initialize(); }
{ const plint imSize = 600; ImageWriter<T> imageWriter("leeloo"); imageWriter.writeScaledGif( createFileName("u", iter, 6), *computeVelocityNorm(lattice), imSize, imSize); } 2.5.2.5 主函数 mainint main(int argc, char *argv[]){ plbInit(&argc, &argv); global::directories().setOutputDir("./tmp/"); // Use the class IncomprFlowParam to convert from // dimensionless variables to lattice units, in the // context of incompressible flows. IncomprFlowParam<T> parameters( (T)1e-2, // Reference velocity (the maximum velocity // in the Poiseuille profile) in lattice units. (T)100., // Reynolds number 100, // Resolution of the reference length (channel height). 2., // Channel length in dimensionless variables 1. // Channel height in dimensionless variables ); const T imSave = (T)0.1; // Time intervals at which to save GIF // images, in dimensionless time units. const T maxT = (T)3.1; // Total simulation time, in dimensionless // time units. writeLogFile(parameters, "Poiseuille flow"); MultiBlockLattice2D<T, DESCRIPTOR> lattice( parameters.getNx(), parameters.getNy(), new BGKdynamics<T, DESCRIPTOR>(parameters.getOmega())); OnLatticeBoundaryCondition2D<T, DESCRIPTOR> *boundaryCondition = createLocalBoundaryCondition2D<T, DESCRIPTOR>(); channelSetup(lattice, parameters, *boundaryCondition); // Main loop over time iterations. for (plint iT = 0; iT * parameters.getDeltaT() < maxT; ++iT) { if (iT % parameters.nStep(imSave) == 0 && iT > 0) { pcout << "Saving Gif at time step " << iT << endl; writeGifs(lattice, iT); } // Execute lattice Boltzmann iteration. lattice.collideAndStream(); } delete boundaryCondition; }
2.5.2.6 说明在这个模拟中,通道的宽度和高度都是有限的。速度边界条件设置为泊肃叶速度分布,初始条件根据泊肃叶速度分布设置。模拟的主循环执行格子玻尔兹曼迭代,并在特定时间间隔保存速度场的GIF图像。 这个例子展示了如何使用Palabos库设置一个简单的二维泊肃叶流模拟,并保存速度场的可视化结果。 2.5.3 分行代码详解2.5.3.1 包含头文件1 #include "palabos2D.h"2 #include "palabos2D.hh" 3 4 #include <vector> 5 #include <iostream> 6 #include <iomanip> 这段C++代码包含了四个头文件的引用:
其中, 通过引用这些头文件,可以使用相应的函数和类,方便进行二维流体模拟。 2.5.3.2 命名空间11 using namespace plb;12 using namespace std; 这一段C++代码使用了C++的命名空间(
因此,在代码中使用了 2.5.3.3 类型定义和描述符14 typedef double T;15 #define DESCRIPTOR plb::descriptors::D2Q9Descriptor 这段代码定义了两个常量
2.5.3.4 函数和类定义18 /// Velocity on the parabolic Poiseuille profile19 T poiseuilleVelocity(plint iY, IncomprFlowParam<T> const& parameters) { 20 T y = (T)iY / parameters.getResolution(); 21 return 4.*parameters.getLatticeU() * (y-y*y); 22 } 这段代码定义了一个名为 具体来说,这个函数接受两个参数:
第20行:将纵坐标 注意:该函数计算了给定高度的通道中的抛物线泊肃叶流速度剖面,并给出通道中心处的最大速度。对象** 第21行: 最后,该函数返回了当前位置的速度值。 24 /// A functional, used to initialize the velocity for the boundary conditions 25 template<typename T> 26 class PoiseuilleVelocity { 27 public: 28 PoiseuilleVelocity(IncomprFlowParam<T> parameters_) 29 : parameters(parameters_) 30 { } 31 /// This version of the operator returns the velocity only, 32 /// to instantiate the boundary condition. 33 void operator()(plint iX, plint iY, Array<T,2>& u) const { 34 u[0] = poiseuilleVelocity(iY, parameters); 35 u[1] = T(); 36 } 37 /// This version of the operator returns also a constant value for 38 /// the density, to create the initial condition. 39 void operator()(plint iX, plint iY, T& rho, Array<T,2>& u) const { 40 u[0] = poiseuilleVelocity(iY, parameters); 41 u[1] = T(); 42 rho = (T)1; 43 } 44 private: 45 IncomprFlowParam<T> parameters; 46 }; 这是所谓的函数对象或函数式的例子。它是一个类,重载了函数调用运算符operator()。该类的实例就像普通函数一样,但该对象比一个纯函数更智能。例如,它可以在构造函数中接受参数,并拥有可配置的行为。本函数对象封装了函数** 这是一个定义了一个Poiseuille边界条件的类
48 void channelSetup ( 49 MultiBlockLattice2D<T,DESCRIPTOR>& lattice, 50 IncomprFlowParam<T> const& parameters, 51 OnLatticeBoundaryCondition2D<T,DESCRIPTOR>& boundaryCondition ) 52 { 53 // Create Velocity boundary conditions. 54 boundaryCondition.setVelocityConditionOnBlockBoundaries(lattice); 55 56 // Specify the boundary velocity. 57 setBoundaryVelocity ( 58 lattice, lattice.getBoundingBox(), 59 PoiseuilleVelocity<T>(parameters) ); 60 61 // Create the initial condition. 62 initializeAtEquilibrium ( 63 lattice, lattice.getBoundingBox(), PoiseuilleVelocity<T>(parameters) ); 64 65 lattice.initialize(); 66 } 这段代码定义了一个名为
该函数的主要目的是为
68 void writeGifs(MultiBlockLattice2D<T,DESCRIPTOR>& lattice, plint iter) 69 { 70 const plint imSize = 600; 71 ImageWriter<T> imageWriter("leeloo"); 72 imageWriter.writeScaledGif(createFileName("u", iter, 6), 73 *computeVelocityNorm(lattice), 74 imSize, imSize ); 75 } 这段代码定义了一个名为
2.5.3.5 主函数 main77 int main(int argc, char* argv[]) {78 plbInit(&argc, &argv); 79 80 global::directories().setOutputDir("./tmp/"); 这段C++代码是整个程序的主函数,其作用是运行整个流体模拟的主要逻辑。
82 // Use the class IncomprFlowParam to convert from 83 // dimensionless variables to lattice units, in the 84 // context of incompressible flows. 85 IncomprFlowParam<T> parameters( 86 (T) 1e-2, // Reference velocity (the maximum velocity 87 // in the Poiseuille profile) in lattice units. 88 (T) 100., // Reynolds number 89 100, // Resolution of the reference length (channel height). 90 2., // Channel length in dimensionless variables 91 1. // Channel height in dimensionless variables 92 ); 这段C++代码定义了一个类型为
注意:类型为 93 const T imSave = (T)0.1; // Time intervals at which to save GIF 94 // images, in dimensionless time units. 95 const T maxT = (T)3.1; // Total simulation time, in dimensionless 96 // time units. 97 98 writeLogFile(parameters, "Poiseuille flow"); 这段代码定义了两个常量imSave和maxT,以及调用了一个函数writeLogFile。
100 MultiBlockLattice2D<T, DESCRIPTOR> lattice ( 101 parameters.getNx(), parameters.getNy(), 102 new BGKdynamics<T,DESCRIPTOR>(parameters.getOmega()) ); 这段代码定义了一个名为 104 OnLatticeBoundaryCondition2D<T,DESCRIPTOR>* 105 boundaryCondition = createLocalBoundaryCondition2D<T,DESCRIPTOR>(); 106 107 channelSetup(lattice, parameters, *boundaryCondition); 这段代码的作用是创建边界条件,并将边界条件应用到模拟的通道中。
109 // Main loop over time iterations. 110 for (plint iT=0; iT*parameters.getDeltaT()<maxT; ++iT) { 111 if (iT%parameters.nStep(imSave)==0 && iT>0) { 112 pcout << "Saving Gif at time step " << iT << endl; 113 writeGifs(lattice, iT); 114 } 115 // Execute lattice Boltzmann iteration. 116 lattice.collideAndStream(); 117 } 118 119 delete boundaryCondition; 120 } 这段代码实现了主要的计算过程。在这里,我们用一个for循环执行时间步长的迭代。每次迭代,程序都会检查是否需要将当前状态写入输出文件中,然后通过执行Lattice Boltzmann迭代来更新流场的状态。当完成所有迭代时,我们会释放创建的边界条件内存。
2.5.4 结果输出https://www.zhihu.com/video/1629604325491109888 2.5.5 附录2.5.5.1 tutorial_1_5.cpp 源码#include <iomanip>#include <iostream> #include <vector> #include "palabos2D.h" #include "palabos2D.hh" /* Code 1.5 in the Palabos tutorial */ using namespace plb; using namespace std; typedef double T; #define DESCRIPTOR plb::descriptors::D2Q9Descriptor /// Velocity on the parabolic Poiseuille profile T poiseuilleVelocity(plint iY, IncomprFlowParam<T> const ¶meters) { T y = (T)iY / parameters.getResolution(); return 4. * parameters.getLatticeU() * (y - y * y); } /// A functional, used to initialize the velocity for the boundary conditions template <typename T> class PoiseuilleVelocity { public: PoiseuilleVelocity(IncomprFlowParam<T> parameters_) : parameters(parameters_) { } /// This version of the operator returns the velocity only, /// to instantiate the boundary condition. void operator()([[maybe_unused]] plint iX, plint iY, Array<T, 2> &u) const { u[0] = poiseuilleVelocity(iY, parameters); u[1] = T(); } /// This version of the operator returns also a constant value for /// the density, to create the initial condition. void operator()([[maybe_unused]] plint iX, plint iY, T &rho, Array<T, 2> &u) const { u[0] = poiseuilleVelocity(iY, parameters); u[1] = T(); rho = (T)1; } private: IncomprFlowParam<T> parameters; }; void channelSetup( MultiBlockLattice2D<T, DESCRIPTOR> &lattice, IncomprFlowParam<T> const ¶meters, OnLatticeBoundaryCondition2D<T, DESCRIPTOR> &boundaryCondition) { // Create Velocity boundary conditions. boundaryCondition.setVelocityConditionOnBlockBoundaries(lattice); // Specify the boundary velocity. setBoundaryVelocity(lattice, lattice.getBoundingBox(), PoiseuilleVelocity<T>(parameters)); // Create the initial condition. initializeAtEquilibrium(lattice, lattice.getBoundingBox(), PoiseuilleVelocity<T>(parameters)); lattice.initialize(); } void writeGifs(MultiBlockLattice2D<T, DESCRIPTOR> &lattice, plint iter) { const plint imSize = 600; ImageWriter<T> imageWriter("leeloo"); imageWriter.writeScaledGif( createFileName("u", iter, 6), *computeVelocityNorm(lattice), imSize, imSize); } int main(int argc, char *argv[]) { plbInit(&argc, &argv); global::directories().setOutputDir("./tmp/"); // Use the class IncomprFlowParam to convert from // dimensionless variables to lattice units, in the // context of incompressible flows. IncomprFlowParam<T> parameters( (T)1e-2, // Reference velocity (the maximum velocity // in the Poiseuille profile) in lattice units. (T)100., // Reynolds number 100, // Resolution of the reference length (channel height). 2., // Channel length in dimensionless variables 1. // Channel height in dimensionless variables ); const T imSave = (T)0.1; // Time intervals at which to save GIF // images, in dimensionless time units. const T maxT = (T)3.1; // Total simulation time, in dimensionless // time units. writeLogFile(parameters, "Poiseuille flow"); MultiBlockLattice2D<T, DESCRIPTOR> lattice( parameters.getNx(), parameters.getNy(), new BGKdynamics<T, DESCRIPTOR>(parameters.getOmega())); OnLatticeBoundaryCondition2D<T, DESCRIPTOR> *boundaryCondition = createLocalBoundaryCondition2D<T, DESCRIPTOR>(); channelSetup(lattice, parameters, *boundaryCondition); // Main loop over time iterations. for (plint iT = 0; iT * parameters.getDeltaT() < maxT; ++iT) { if (iT % parameters.nStep(imSave) == 0 && iT > 0) { pcout << "Saving Gif at time step " << iT << endl; writeGifs(lattice, iT); } // Execute lattice Boltzmann iteration. lattice.collideAndStream(); } delete boundaryCondition; } 2.5.5.2 开头注释/* This file is part of the Palabos library.* * The Palabos softare is developed since 2011 by FlowKit-Numeca Group Sarl * (Switzerland) and the University of Geneva (Switzerland), which jointly * own the IP rights for most of the code base. Since October 2019, the * Palabos project is maintained by the University of Geneva and accepts * source code contributions from the community. * * Contact: * Jonas Latt * Computer Science Department * University of Geneva * 7 Route de Drize * 1227 Carouge, Switzerland * jonas.latt@unige.ch * * The most recent release of Palabos can be downloaded at * <https://palabos.unige.ch/> * * 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/>. */ |