/* Lattice Boltzmann sample, written in C++, using the OpenLB
* library
*
* Copyright (C) 2006, 2007, 2012 Jonas Latt, Mathias J. Krause
* 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.
*/
/* bstep3d.cpp:
* The implementation of a backward facing step. It is furthermore
* shown how to use checkpointing to save the state of the
* simulation regularly.
*/
#include "olb3D.h"
#ifndef OLB_PRECOMPILED // Unless precompiled version is used,
#include "olb3D.hh" // include full template code
#endif
#include
#include
#include
#include
using namespace olb;
using namespace olb::descriptors;
using namespace olb::graphics;
using namespace std;
typedef double T;
#define DESCRIPTOR D3Q19<>
// Parameters for the simulation setup
const T lx1 = 5.0; // length of step
const T ly1 = 0.75; // height of step
const T lx0 = 18.0; // length of channel
const T ly0 = 1.5; // height of channel
const T lz0 = 1.5; // width of channel
const int N = 20; // resolution of the model
const int M = 25; // resolution of the model
const T maxPhysT = 40.; // max. simulation time in s, SI unit
// Stores geometry information in form of material numbers
void prepareGeometry( UnitConverter const& converter,
SuperGeometry3D& superGeometry )
{
OstreamManager clout( std::cout,"prepareGeometry" );
clout << "Prepare Geometry ..." << std::endl;
superGeometry.rename( 0,2 );
superGeometry.rename( 2,1,1,1,1 );
Vector extend( lx1, ly1, lz0 );
Vector origin;
IndicatorCuboid3D cuboid2( extend, origin );
superGeometry.rename( 1,2,cuboid2 );
// Set material number for inflow
extend = {2*converter.getConversionFactorLength(), ly0, lz0};
origin[0] -= converter.getConversionFactorLength()/2.;
IndicatorCuboid3D inflow( extend, origin );
superGeometry.rename( 2,3,1,inflow );
// Set material number for outflow
origin[0] = lx0 - converter.getConversionFactorLength()*1.5;
IndicatorCuboid3D 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,
SuperLattice3D& sLattice,
Dynamics& bulkDynamics,
sOnLatticeBoundaryCondition3D& bc,
SuperGeometry3D& superGeometry )
{
OstreamManager clout( std::cout,"prepareLattice" );
clout << "Prepare Lattice ..." << endl;
const T omega = converter.getLatticeRelaxationFrequency();
// Material=0 -->do nothing
sLattice.defineDynamics( superGeometry, 0, &instances::getNoDynamics() );
// Material=1 -->bulk dynamics
// Material=3 -->bulk dynamics (inflow)
// Material=4 -->bulk dynamics (outflow)
auto bulkIndicator = superGeometry.getMaterialIndicator({1, 3, 4});
sLattice.defineDynamics( bulkIndicator, &bulkDynamics );
// Material=2 -->bounce back
sLattice.defineDynamics( superGeometry, 2, &instances::getBounceBack() );
// Setting of the boundary conditions
bc.addVelocityBoundary( superGeometry, 3, omega );
bc.addPressureBoundary( superGeometry, 4, omega );
// Initial conditions
AnalyticalConst3D ux( 0. );
AnalyticalConst3D uy( 0. );
AnalyticalConst3D uz( 0. );
AnalyticalConst3D rho( 1. );
AnalyticalComposed3D u( ux,uy,uz );
//Initialize all values of distribution functions to their local equilibrium
sLattice.defineRhoU( bulkIndicator, rho, u );
sLattice.iniEquilibrium( bulkIndicator, rho, u );
// Make the lattice ready for simulation
sLattice.initialize();
clout << "Prepare Lattice ... OK" << std::endl;
}
// Generates a slowly increasing inflow for the first iTMaxStart timesteps
void setBoundaryValues( UnitConverter const& converter,
SuperLattice3D& sLattice, int iT,
SuperGeometry3D& superGeometry )
{
OstreamManager clout( std::cout,"setBoundaryValues" );
// No of time steps for smooth start-up
int iTmaxStart = converter.getLatticeTime( maxPhysT*0.2 );
int iTupdate = 5;
if ( iT%iTupdate==0 && iT<= iTmaxStart ) {
// Smooth start curve, sinus
// SinusStartScale StartScale(iTmaxStart, T(1));
// Smooth start curve, polynomial
PolynomialStartScale startScale( iTmaxStart, T( 1 ) );
// Creates and sets the Poiseuille inflow profile using functors
int iTvec[1]= {iT};
T frac[1]= {};
startScale( frac,iTvec );
std::vector maxVelocity( 3,0 );
maxVelocity[0] = 2.25*frac[0]*converter.getCharLatticeVelocity();
T distance2Wall = converter.getConversionFactorLength()/2.;
RectanglePoiseuille3D poiseuilleU( superGeometry, 3, maxVelocity, distance2Wall, distance2Wall, distance2Wall );
sLattice.defineU( superGeometry, 3, poiseuilleU );
clout << "step=" << iT << "; maxVel=" << maxVelocity[0] << std::endl;
}
}
// Output to console and files
void getResults( SuperLattice3D& sLattice,
UnitConverter const& converter,
BlockReduction3D2D& planeReduction,
int iT,
SuperGeometry3D& superGeometry, Timer& timer)
{
OstreamManager clout( std::cout,"getResults" );
SuperVTMwriter3D vtmWriter( "bstep3d" );
SuperLatticePhysVelocity3D velocity( sLattice, converter );
SuperLatticePhysPressure3D pressure( sLattice, converter );
vtmWriter.addFunctor( velocity );
vtmWriter.addFunctor( pressure );
const int vtkIter = converter.getLatticeTime( 0.2 );
const int statIter = converter.getLatticeTime( 0.1 );
const int saveIter = converter.getLatticeTime( 1. );
if ( iT==0 ) {
// Writes the geometry, cuboid no. and rank no. as vti file for visualization
SuperLatticeGeometry3D geometry( sLattice, superGeometry );
SuperLatticeCuboid3D cuboid( sLattice );
SuperLatticeRank3D rank( sLattice );
vtmWriter.write( geometry );
vtmWriter.write( cuboid );
vtmWriter.write( rank );
vtmWriter.createMasterFile();
}
// Writes the ppm files
if ( iT%vtkIter==0 ) {
vtmWriter.write( iT );
planeReduction.update();
// write output as JPEG
heatmap::write(planeReduction, iT);
}
// Writes output on the console
if ( iT%statIter==0 && iT>=0 ) {
// Timer console output
timer.update( iT );
timer.printStep();
// Lattice statistics console output
sLattice.getStatistics().print( iT,converter.getPhysTime( iT ) );
}
// Saves lattice data
if ( iT%( saveIter/2 )==0 && iT>0 ) {
clout << "Checkpointing the system at t=" << iT << endl;
sLattice.save( "bstep3d.checkpoint" );
// The data can be reloaded using
// sLattice.load("bstep3d.checkpoint");
}
}
int main( int argc, char* argv[] )
{
// === 1st Step: Initialization ===
olbInit( &argc, &argv );
singleton::directories().setOutputDir( "./tmp/" );
OstreamManager clout( std::cout,"main" );
// display messages from every single mpi process
//clout.setMultiOutput(true);
UnitConverter converter(
(T) 1./N, // physDeltaX: spacing between two lattice cells in __m__
(T) 1./(M*N), // physDeltaT: time step in __s__
(T) 1., // charPhysLength: reference length of simulation geometry
(T) 1., // charPhysVelocity: maximal/highest expected velocity during simulation in __m / s__
(T) 1./100., // physViscosity: physical kinematic viscosity in __m^2 / s__
(T) 1. // 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("bstep3d");
// === 2nd Step: Prepare Geometry ===
Vector extend( lx0, ly0, lz0 );
Vector origin;
IndicatorCuboid3D cuboid( extend, origin );
// Instantiation of a cuboidGeometry with weights
#ifdef PARALLEL_MODE_MPI
const int noOfCuboids = singleton::mpi().getSize();
#else
const int noOfCuboids = 7;
#endif
CuboidGeometry3D cuboidGeometry( cuboid, converter.getConversionFactorLength(), noOfCuboids );
// Instantiation of a loadBalancer
HeuristicLoadBalancer loadBalancer( cuboidGeometry );
// Instantiation of a superGeometry
SuperGeometry3D superGeometry( cuboidGeometry, loadBalancer, 2 );
prepareGeometry( converter, superGeometry );
// === 3rd Step: Prepare Lattice ===
SuperLattice3D sLattice( superGeometry );
BGKdynamics bulkDynamics (
converter.getLatticeRelaxationFrequency(),
instances::getBulkMomenta()
);
// choose between local and non-local boundary condition
sOnLatticeBoundaryCondition3D sBoundaryCondition( sLattice );
// createInterpBoundaryCondition3D(sBoundaryCondition);
createLocalBoundaryCondition3D( 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() );
timer.start();
// Set up persistent measuring functors for result extraction
SuperLatticePhysVelocity3D velocity( sLattice, converter );
SuperEuklidNorm3D normVel( velocity );
BlockReduction3D2D planeReduction(
normVel,
Hyperplane3D().centeredIn(cuboidGeometry.getMotherCuboid()).normalTo({0,0,1}),
600,
BlockDataSyncMode::ReduceOnly);
for ( int iT = 0; iT < converter.getLatticeTime( maxPhysT ); ++iT ) {
// === 5th Step: Definition of Initial and Boundary Conditions ===
setBoundaryValues( converter, sLattice, iT, superGeometry );
// === 6th Step: Collide and Stream Execution ===
sLattice.collideAndStream();
// === 7th Step: Computation and Output of the Results ===
getResults( sLattice, converter, planeReduction, iT, superGeometry, timer );
}
timer.stop();
timer.printSummary();
}