/* Lattice Boltzmann sample, written in C++, using the OpenLB
* library
*
* Copyright (C) 2007, 2012 Jonas Latt, Mathias J. Krause
* Vojtech Cvrcek, Peter Weisbrod
* E-mail contact: info@openlb.net
* The most recent release of OpenLB can be downloaded at
*
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
/* poiseuille2d.cpp:
* This example examines a 2D Poseuille flow
* It illustrates the computation of error norms.
*/
#include "olb2D.h"
#include "olb2D.hh" // use only generic version!
#include
#include
#include
#include
#include
using namespace olb;
using namespace olb::descriptors;
using namespace olb::graphics;
using namespace std;
typedef double T;
//#define MRT
#ifdef MRT
#define DESCRIPTOR ForcedMRTD2Q9Descriptor
#else
#define DESCRIPTOR D2Q9
#endif
typedef enum {forced, nonForced} FlowType;
typedef enum {bounceBack, local, interpolated, freeSlip, partialSlip} BoundaryType;
// Parameters for the simulation setup
FlowType flowType = forced;
BoundaryType boundaryType = interpolated;
const T lx = 2.; // length of the channel
const T ly = 1.; // height of the channel
int N = 50; // resolution of the model
const T Re = 10.; // Reynolds number
const T maxPhysT = 20.; // max. simulation time in s, SI unit
const T physInterval = 0.25; // interval for the convergence check in s
const T residuum = 1e-5; // residuum for the convergence check
const T tuner = 0.99; // for partialSlip only: 0->bounceBack, 1->freeSlip
// Stores geometry information in form of material numbers
void prepareGeometry( UnitConverter const& converter,
SuperGeometry2D& superGeometry ) {
OstreamManager clout( std::cout,"prepareGeometry" );
clout << "Prepare Geometry ..." << std::endl;
superGeometry.rename( 0,2 );
superGeometry.rename( 2,1,1,1 );
if (flowType == nonForced) {
Vector extend;
Vector origin;
T physSpacing = converter.getPhysDeltaX();
// Set material number for inflow
extend[1] = ly;
extend[0] = physSpacing / 2;
origin[0] -= physSpacing / 4;
IndicatorCuboid2D inflow( extend, origin );
superGeometry.rename( 2,3,1,inflow );
// Set material number for outflow
origin[0] = lx - physSpacing / 4;
IndicatorCuboid2D outflow( extend, origin );
superGeometry.rename( 2,4,1,outflow );
}
// Removes all not needed boundary voxels outside the surface
superGeometry.clean();
// Removes all not needed boundary voxels inside the surface
superGeometry.innerClean();
superGeometry.checkForErrors();
superGeometry.print();
clout << "Prepare Geometry ... OK" << std::endl;
}
// Set up the geometry of the simulation
void prepareLattice( UnitConverter const& converter,
SuperLattice2D& sLattice,
Dynamics& bulkDynamics,
sOnLatticeBoundaryCondition2D& sBoundaryCondition,
SuperGeometry2D& superGeometry ) {
OstreamManager clout( std::cout,"prepareLattice" );
clout << "Prepare Lattice ..." << std::endl;
const T omega = converter.getLatticeRelaxationFrequency();
// Material=0 -->do nothing
sLattice.defineDynamics( superGeometry, 0, &instances::getNoDynamics() );
// Material=1 -->bulk dynamics
sLattice.defineDynamics( superGeometry, 1, &bulkDynamics );
if (boundaryType == bounceBack) {
sLattice.defineDynamics( superGeometry, 2, &instances::getBounceBack() );
} else if (boundaryType == freeSlip) {
sLattice.defineDynamics(superGeometry, 2, &instances::getNoDynamics());
sBoundaryCondition.addSlipBoundary( superGeometry, 2 );
} else if (boundaryType == partialSlip) {
sLattice.defineDynamics(superGeometry, 2, &instances::getNoDynamics());
sBoundaryCondition.addPartialSlipBoundary(tuner, superGeometry, 2 );
} else {
sLattice.defineDynamics( superGeometry, 2, &bulkDynamics );
sBoundaryCondition.addVelocityBoundary( superGeometry, 2, omega );
}
if (flowType == nonForced) {
// Material=3 -->bulk dynamics
sLattice.defineDynamics( superGeometry, 3, &bulkDynamics );
// Material=4 -->bulk dynamics
sLattice.defineDynamics( superGeometry, 4, &bulkDynamics );
sBoundaryCondition.addVelocityBoundary( superGeometry, 3, omega );
sBoundaryCondition.addPressureBoundary( superGeometry, 4, omega );
}
// Initial conditions
T Lx = converter.getLatticeLength( lx );
T Ly = converter.getLatticeLength( ly );
if (flowType == forced) {
std::vector poiseuilleForce( 2,T() );
poiseuilleForce[0] = 8.*converter.getLatticeViscosity()*converter.getCharLatticeVelocity() / ( Ly*Ly );
AnalyticalConst2D force( poiseuilleForce );
// Initialize force
sLattice.defineField(superGeometry, 1, force);
sLattice.defineField(superGeometry, 2, force);
} else {
T p0 =8.*converter.getLatticeViscosity()*converter.getCharLatticeVelocity()*Lx/( Ly*Ly );
AnalyticalLinear2D rho( -p0/lx*invCs2(), 0 , p0*invCs2()+1 );
T maxVelocity = converter.getCharLatticeVelocity();
T distance2Wall = converter.getConversionFactorLength();
Poiseuille2D u( superGeometry, 3, maxVelocity, distance2Wall );
// Initialize all values of distribution functions to their local equilibrium
sLattice.defineRhoU( superGeometry, 1, rho, u );
sLattice.iniEquilibrium( superGeometry, 1, rho, u );
sLattice.defineRhoU( superGeometry, 2, rho, u );
sLattice.iniEquilibrium( superGeometry, 2, rho, u );
sLattice.defineRhoU( superGeometry, 3, rho, u );
sLattice.iniEquilibrium( superGeometry, 3, rho, u );
sLattice.defineRhoU( superGeometry, 4, rho, u );
sLattice.iniEquilibrium( superGeometry, 4, rho, u );
}
// Make the lattice ready for simulation
sLattice.initialize();
clout << "Prepare Lattice ... OK" << std::endl;
}
// Compute error norms
void error( SuperGeometry2D& superGeometry,
SuperLattice2D& sLattice,
UnitConverter const& converter,
Dynamics& bulkDynamics ) {
OstreamManager clout( std::cout,"error" );
int tmp[]= { };
T result[2]= { };
// velocity error
const T maxVelocity = converter.getCharPhysVelocity();
const T radius = ly/2.;
std::vector axisPoint( 2,T() );
axisPoint[0] = lx/2.;
axisPoint[1] = ly/2.;
std::vector axisDirection( 2,T() );
axisDirection[0] = 1;
axisDirection[1] = 0;
Poiseuille2D uSol( axisPoint, axisDirection, maxVelocity, radius );
SuperLatticePhysVelocity2D u( sLattice,converter );
auto indicatorF = superGeometry.getMaterialIndicator(1);
SuperAbsoluteErrorL1Norm2D absVelocityErrorNormL1(u, uSol, indicatorF);
absVelocityErrorNormL1(result, tmp);
clout << "velocity-L1-error(abs)=" << result[0];
SuperRelativeErrorL1Norm2D relVelocityErrorNormL1(u, uSol, indicatorF);
relVelocityErrorNormL1(result, tmp);
clout << "; velocity-L1-error(rel)=" << result[0] << std::endl;
SuperAbsoluteErrorL2Norm2D absVelocityErrorNormL2(u, uSol, indicatorF);
absVelocityErrorNormL2(result, tmp);
clout << "velocity-L2-error(abs)=" << result[0];
SuperRelativeErrorL2Norm2D relVelocityErrorNormL2(u, uSol, indicatorF);
relVelocityErrorNormL2(result, tmp);
clout << "; velocity-L2-error(rel)=" << result[0] << std::endl;
SuperAbsoluteErrorLinfNorm2D absVelocityErrorNormLinf(u, uSol, indicatorF);
absVelocityErrorNormLinf(result, tmp);
clout << "velocity-Linf-error(abs)=" << result[0];
SuperRelativeErrorLinfNorm2D relVelocityErrorNormLinf(u, uSol, indicatorF);
relVelocityErrorNormLinf(result, tmp);
clout << "; velocity-Linf-error(rel)=" << result[0] << std::endl;
// strainRate error
PoiseuilleStrainRate2D sSol( converter, ly );
SuperLatticePhysStrainRate2D s( sLattice,converter );
SuperAbsoluteErrorL1Norm2D absStrainRateErrorNormL1(s, sSol, indicatorF);
absStrainRateErrorNormL1(result, tmp);
clout << "strainRate-L1-error(abs)=" << result[0];
SuperRelativeErrorL1Norm2D relStrainRateErrorNormL1(s, sSol, indicatorF);
relStrainRateErrorNormL1(result, tmp);
clout << "; strainRate-L1-error(rel)=" << result[0] << std::endl;
SuperAbsoluteErrorL2Norm2D absStrainRateErrorNormL2(s, sSol, indicatorF);
absStrainRateErrorNormL2(result, tmp);
clout << "strainRate-L2-error(abs)=" << result[0];
SuperRelativeErrorL2Norm2D relStrainRateErrorNormL2(s, sSol, indicatorF);
relStrainRateErrorNormL2(result, tmp);
clout << "; strainRate-L2-error(rel)=" << result[0] << std::endl;
SuperAbsoluteErrorLinfNorm2D absStrainRateErrorNormLinf(s, sSol, indicatorF);
absStrainRateErrorNormLinf(result, tmp);
clout << "strainRate-Linf-error(abs)=" << result[0];
SuperRelativeErrorLinfNorm2D relStrainRateErrorNormLinf(s, sSol, indicatorF);
relStrainRateErrorNormLinf(result, tmp);
clout << "; strainRate-Linf-error(rel)=" << result[0] << std::endl;
if (flowType == nonForced) {
// pressure error
int Lx = converter.getLatticeLength( lx );
int Ly = converter.getLatticeLength( ly );
T p0 = 8.*converter.getLatticeViscosity()*converter.getCharLatticeVelocity()*Lx/T( Ly*Ly );
AnalyticalLinear2D pressureSol( -converter.getPhysPressure( p0 )/lx , 0 , converter.getPhysPressure( p0 ) );
SuperLatticePhysPressure2D pressure( sLattice,converter );
SuperAbsoluteErrorL1Norm2D absPressureErrorNormL1(pressure, pressureSol, indicatorF);
absPressureErrorNormL1(result, tmp);
clout << "pressure-L1-error(abs)=" << result[0];
SuperRelativeErrorL1Norm2D relPressureErrorNormL1(pressure, pressureSol, indicatorF);
relPressureErrorNormL1(result, tmp);
clout << "; pressure-L1-error(rel)=" << result[0] << std::endl;
SuperAbsoluteErrorL2Norm2D absPressureErrorNormL2(pressure, pressureSol, indicatorF);
absPressureErrorNormL2(result, tmp);
clout << "pressure-L2-error(abs)=" << result[0];
SuperRelativeErrorL2Norm2D relPressureErrorNormL2(pressure, pressureSol, indicatorF);
relPressureErrorNormL2(result, tmp);
clout << "; pressure-L2-error(rel)=" << result[0] << std::endl;
SuperAbsoluteErrorLinfNorm2D absPressureErrorNormLinf(pressure, pressureSol, indicatorF);
absPressureErrorNormLinf(result, tmp);
clout << "pressure-Linf-error(abs)=" << result[0];
SuperRelativeErrorLinfNorm2D relPressureErrorNormLinf(pressure, pressureSol, indicatorF);
relPressureErrorNormLinf(result, tmp);
clout << "; pressure-Linf-error(rel)=" << result[0] << std::endl;
}
}
// Output to console and files
void getResults( SuperLattice2D& sLattice, Dynamics& bulkDynamics,
UnitConverter const& converter, int iT,
SuperGeometry2D& superGeometry, Timer& timer, bool hasConverged ) {
OstreamManager clout( std::cout,"getResults" );
SuperVTMwriter2D vtmWriter( "forcedPoiseuille2d" );
SuperLatticePhysVelocity2D velocity( sLattice, converter );
SuperLatticePhysPressure2D pressure( sLattice, converter );
vtmWriter.addFunctor( velocity );
vtmWriter.addFunctor( pressure );
const int vtmIter = converter.getLatticeTime( maxPhysT/20. );
const int statIter = converter.getLatticeTime( maxPhysT/20. );
if ( iT==0 ) {
// Writes the geometry, cuboid no. and rank no. as vti file for visualization
SuperLatticeGeometry2D geometry( sLattice, superGeometry );
SuperLatticeCuboid2D cuboid( sLattice );
SuperLatticeRank2D rank( sLattice );
superGeometry.rename( 0,2 );
vtmWriter.write( geometry );
vtmWriter.write( cuboid );
vtmWriter.write( rank );
vtmWriter.createMasterFile();
}
// Writes the vtm files and profile text file
if ( iT%vtmIter==0 || hasConverged ) {
vtmWriter.write( iT );
SuperEuklidNorm2D normVel( velocity );
BlockReduction2D2D planeReduction( normVel, 600, BlockDataSyncMode::ReduceOnly );
// write output as JPEG
heatmap::write(planeReduction, iT);
}
if ( hasConverged ) {
Gnuplot gplot( "centerVelocity" );
T Ly = converter.getLatticeLength( ly );
for ( int iY=0; iY<=Ly; ++iY ) {
T dx = 1. / T(converter.getResolution());
const T maxVelocity = converter.getPhysVelocity( converter.getCharLatticeVelocity() );
T point[2]= {T(),T()};
point[0] = lx/2.;
point[1] = ( T )iY/Ly;
const T radius = ly/2.;
std::vector axisPoint( 2,T() );
axisPoint[0] = lx/2.;
axisPoint[1] = ly/2.;
std::vector axisDirection( 2,T() );
axisDirection[0] = 1;
axisDirection[1] = 0;
Poiseuille2D uSol( axisPoint, axisDirection, maxVelocity, radius );
T analytical[2] = {T(),T()};
uSol( analytical,point );
SuperLatticePhysVelocity2D velocity( sLattice, converter );
AnalyticalFfromSuperF2D intpolateVelocity( velocity, true );
T numerical[2] = {T(),T()};
intpolateVelocity( numerical,point );
gplot.setData( iY*dx, {analytical[0],numerical[0]}, {"analytical","numerical"} );
}
// Create PNG file
gplot.writePNG();
}
// Writes output on the console
if ( iT%statIter==0 || hasConverged ) {
// Timer console output
timer.update( iT );
timer.printStep();
// Lattice statistics console output
sLattice.getStatistics().print( iT,converter.getPhysTime( iT ) );
// Error norms
error( superGeometry, sLattice, converter, bulkDynamics );
}
}
int main( int argc, char* argv[] ) {
// === 1st Step: Initialization ===
olbInit( &argc, &argv );
singleton::directories().setOutputDir( "./tmp/" );
OstreamManager clout( std::cout,"main" );
if (argc > 1) {
if (argv[1][0]=='-'&&argv[1][1]=='h') {
OstreamManager clout( std::cout,"help" );
clout<<"Usage: program [Resolution] [FlowType] [BoundaryType]"< 1) {
N = atoi(argv[1]);
if (N < 1) {
std::cerr << "Fluid domain is too small" << std::endl;
return 1;
}
}
if (argc > 2) {
int flowTypeNumber = atoi(argv[2]);
if (flowTypeNumber < 0 || flowTypeNumber > (int)nonForced) {
std::cerr << "Unknown fluid flow type" << std::endl;
return 2;
}
flowType = (FlowType) flowTypeNumber;
}
if (argc > 3) {
int boundaryTypeNumber = atoi(argv[3]);
if (boundaryTypeNumber < 0 || boundaryTypeNumber > (int) partialSlip) {
std::cerr << "Unknown boundary type" << std::endl;
return 3;
}
boundaryType = (BoundaryType) boundaryTypeNumber;
}
UnitConverterFromResolutionAndRelaxationTime const converter(
int {N}, // resolution: number of voxels per charPhysL
(T) 0.8, // latticeRelaxationTime: relaxation time, have to be greater than 0.5!
(T) 1, // charPhysLength: reference length of simulation geometry
(T) 1, // charPhysVelocity: maximal/highest expected velocity during simulation in __m / s__
(T) 1./Re, // physViscosity: physical kinematic viscosity in __m^2 / s__
(T) 1.0 // physDensity: physical density in __kg / m^3__
);
// Prints the converter log as console output
converter.print();
// Writes the converter log in a file
converter.write("forcedPoiseuille2d");
// === 2nd Step: Prepare Geometry ===
Vector extend( lx, ly );
Vector origin;
IndicatorCuboid2D cuboid( extend, origin );
// Instantiation of a cuboidGeometry with weights
#ifdef PARALLEL_MODE_MPI
const int noOfCuboids = singleton::mpi().getSize();
#else
const int noOfCuboids = 1;
#endif
CuboidGeometry2D cuboidGeometry( cuboid, converter.getConversionFactorLength(), noOfCuboids );
if (flowType == forced) {
// Periodic boundaries in x-direction
cuboidGeometry.setPeriodicity( true, false );
}
// Instantiation of a loadBalancer
HeuristicLoadBalancer loadBalancer( cuboidGeometry );
// Instantiation of a superGeometry
SuperGeometry2D superGeometry( cuboidGeometry, loadBalancer, 2 );
prepareGeometry( converter, superGeometry );
// === 3rd Step: Prepare Lattice ===
SuperLattice2D sLattice( superGeometry );
Dynamics* bulkDynamics;
#if defined(MRT)
if (flowType == forced) {
bulkDynamics = new ForcedMRTdynamics( converter.getLatticeRelaxationFrequency(), instances::getBulkMomenta() );
} else {
bulkDynamics = new MRTdynamics( converter.getLatticeRelaxationFrequency(), instances::getBulkMomenta() );
}
#else
if (flowType == forced) {
bulkDynamics = new ForcedBGKdynamics( converter.getLatticeRelaxationFrequency(), instances::getBulkMomenta() );
} else {
bulkDynamics = new BGKdynamics( converter.getLatticeRelaxationFrequency(), instances::getBulkMomenta() );
}
#endif
// choose between local and non-local boundary condition
sOnLatticeBoundaryCondition2D sBoundaryCondition( sLattice );
if (boundaryType == local) {
createLocalBoundaryCondition2D (sBoundaryCondition);
} else {
createInterpBoundaryCondition2D ( sBoundaryCondition );
}
prepareLattice( converter, sLattice, *bulkDynamics, sBoundaryCondition, superGeometry );
// === 4th Step: Main Loop with Timer ===
clout << "starting simulation..." << endl;
Timer timer( converter.getLatticeTime( maxPhysT ), superGeometry.getStatistics().getNvoxel() );
util::ValueTracer converge( converter.getLatticeTime( physInterval ), residuum );
timer.start();
for ( int iT = 0; iT < converter.getLatticeTime( maxPhysT ); ++iT ) {
if ( converge.hasConverged() ) {
clout << "Simulation converged." << endl;
getResults( sLattice, *bulkDynamics, converter, iT, superGeometry, timer, converge.hasConverged() );
break;
}
// === 5th Step: Definition of Initial and Boundary Conditions ===
// in this application no boundary conditions have to be adjusted
// === 6th Step: Collide and Stream Execution ===
sLattice.collideAndStream();
// === 7th Step: Computation and Output of the Results ===
getResults( sLattice, *bulkDynamics, converter, iT, superGeometry, timer, converge.hasConverged() );
converge.takeValue( sLattice.getStatistics().getAverageEnergy(), true );
}
timer.stop();
timer.printSummary();
}