/* Lattice Boltzmann sample, written in C++, using the OpenLB * library * * Copyright (C) 2006 - 2012 Mathias J. Krause, Jonas Fietz, * Jonas Latt, Jonas Kratzke * 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. */ /* cavity2d.cpp: * This example illustrates a flow in a cuboid, lid-driven cavity. * It also shows how to use the XML parameter files and has an * example description file for OpenGPI. This version is for parallel * use. A version for sequential use is also available. */ #include "olb2D.h" #ifndef OLB_PRECOMPILED // Unless precompiled version is used, #include "olb2D.hh" // include full template code #endif #include #include #include using namespace olb; using namespace olb::descriptors; using namespace olb::graphics; using namespace olb::util; using namespace std; typedef double T; #define DESCRIPTOR D2Q9<> 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 ); superGeometry.clean(); T eps = converter.getConversionFactorLength(); Vector extend( T( 1 ) + 2*eps, 2*eps ); Vector origin( T() - eps, T( 1 ) - eps ); IndicatorCuboid2D lid( extend, origin ); // Set material number for lid superGeometry.rename( 2,3,1,lid ); // 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.getStatistics().print(); clout << "Prepare Geometry ... OK" << std::endl; } 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(); // link lattice with dynamics for collision step // Material=0 -->do nothing sLattice.defineDynamics( superGeometry, 0, &instances::getNoDynamics() ); // Material=1 -->bulk dynamics sLattice.defineDynamics( superGeometry, 1, &bulkDynamics ); // Material=2,3 -->bulk dynamics, velocity boundary sLattice.defineDynamics( superGeometry, 2, &bulkDynamics ); sLattice.defineDynamics( superGeometry, 3, &bulkDynamics ); sBoundaryCondition.addVelocityBoundary( superGeometry, 2, omega ); sBoundaryCondition.addVelocityBoundary( superGeometry, 3, omega ); clout << "Prepare Lattice ... OK" << std::endl; } void setBoundaryValues( UnitConverter const& converter, SuperLattice2D& sLattice, int iT, SuperGeometry2D& superGeometry ) { if ( iT==0 ) { // set initial values: v = [0,0] AnalyticalConst2D rhoF( 1 ); std::vector velocity( 2,T() ); AnalyticalConst2D uF( velocity ); auto bulkIndicator = superGeometry.getMaterialIndicator({1, 2, 3}); sLattice.iniEquilibrium( bulkIndicator, rhoF, uF ); sLattice.defineRhoU( bulkIndicator, rhoF, uF ); // set non-zero velocity for upper boundary cells velocity[0] = converter.getCharLatticeVelocity(); AnalyticalConst2D u( velocity ); sLattice.defineU( superGeometry, 3, u ); // Make the lattice ready for simulation sLattice.initialize(); } } void getResults( SuperLattice2D& sLattice, UnitConverter const& converter, int iT, Timer* timer, const T logT, const T maxPhysT, const T imSave, const T vtkSave, std::string filenameGif, std::string filenameVtk, const int timerPrintMode, const int timerTimeSteps, SuperGeometry2D& superGeometry, bool converged ) { OstreamManager clout( std::cout,"getResults" ); SuperVTMwriter2D vtmWriter( filenameVtk ); 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 ); vtmWriter.write( geometry ); vtmWriter.write( cuboid ); vtmWriter.write( rank ); vtmWriter.createMasterFile(); } // Get statistics if ( iT%converter.getLatticeTime( logT )==0 || converged ) { sLattice.getStatistics().print( iT, converter.getPhysTime( iT ) ); } if ( iT%timerTimeSteps==0 || converged ) { timer->print( iT,timerPrintMode ); } // Writes the VTK files if ( ( iT%converter.getLatticeTime( vtkSave )==0 && iT>0 ) || converged ) { SuperLatticePhysVelocity2D velocity( sLattice, converter ); SuperLatticePhysPressure2D pressure( sLattice, converter ); vtmWriter.addFunctor( velocity ); vtmWriter.addFunctor( pressure ); vtmWriter.write( iT ); } // Writes the Gif files if ( ( iT%converter.getLatticeTime( imSave )==0 && iT>0 ) || converged ) { SuperLatticePhysVelocity2D velocity( sLattice, converter ); SuperEuklidNorm2D normVel( velocity ); BlockReduction2D2D planeReduction( normVel, 600, BlockDataSyncMode::ReduceOnly ); // write output of velocity as JPEG heatmap::write(planeReduction, iT); } // Output for x-velocity along y-position at the last time step if ( iT == converter.getLatticeTime( maxPhysT ) || converged ) { // Gives access to velocity information on lattice SuperLatticePhysVelocity2D velocityField( sLattice, converter ); // Interpolation functor with velocityField information AnalyticalFfromSuperF2D interpolation( velocityField, true, 1 ); Vector y_coord( {128, 125, 124, 123, 122, 109, 94, 79, 64, 58, 36, 22, 13, 9, 8, 7, 0} ); // Ghia, Ghia and Shin, 1982: "High-Re Solutions for Incompressible Flow Using the Navier-Stokes Equations and a Multigrid Method"; Table 1 Vector vel_ghia_RE1000( { 1.0, 0.65928, 0.57492, 0.51117, 0.46604, 0.33304, 0.18719, 0.05702,-0.06080,-0.10648, -0.27805,-0.38289,-0.29730,-0.22220,-0.20196, -0.18109, 0.0 } ); Vector vel_ghia_RE100( {1.0, 0.84123, 0.78871, 0.73722, 0.68717, 0.23151, 0.00332,-0.13641,-0.20581,-0.21090, -0.15662,-0.10150,-0.06434,-0.04775,-0.04192, -0.03717, 0.0 } ); Vector vel_simulation; // Gnuplot interface to create plots static Gnuplot gplot( "centerVelocityX" ); // Define comparison values Vector comparison = vel_ghia_RE1000; for ( int nY = 0; nY < 17; ++nY ) { // 17 data points evenly distributed between 0 and 1 (height) T position[2] = {0.5, y_coord[nY]/128.0}; T velocity[2] = {T(), T()}; // Interpolate velocityField at "position" and save it in "velocity" interpolation( velocity, position ); // Save value of velocity (in x-direction) in "vel_simulation" for every position "nY" vel_simulation[nY] = velocity[0]; // Set data for plot output gplot.setData( position[1], {vel_simulation[nY],comparison[nY]}, {"simulated","Ghia"} ); } // Create PNG file gplot.writePNG(); // Console output with results clout << "absoluteErrorL2(line)=" << ( vel_simulation - comparison ).norm() / 17. << "; relativeErrorL2(line)=" << ( vel_simulation - comparison ).norm() / comparison.norm() << std::endl; } } int main( int argc, char* argv[] ) { // === 1st Step: Initialization === olbInit( &argc, &argv ); OstreamManager clout( std::cout,"main" ); string fName( "cavity2d.xml" ); XMLreader config( fName ); std::string olbdir, outputdir; config["Application"]["OlbDir"].read( olbdir ); config["Output"]["OutputDir"].read( outputdir ); singleton::directories().setOlbDir( olbdir ); singleton::directories().setOutputDir( outputdir ); UnitConverter* converter = createUnitConverter( config ); // Prints the converter log as console output converter->print(); // Writes the converter log in a file converter->write("cavity2d"); int N = converter->getLatticeLength(1) + 1; // number of voxels in x,y,z direction Timer* timer = createTimer( config, *converter, N*N ); T logT = config["Output"]["Log"]["SaveTime"].get(); T imSave = config["Output"]["VisualizationImages"]["SaveTime"].get(); T vtkSave = config["Output"]["VisualizationVTK"]["SaveTime"].get(); T maxPhysT = config["Application"]["PhysParameters"]["PhysMaxTime"].get(); int timerSkipType = config["Output"]["Timer"]["SkipType"].get(); int timerPrintMode = config["Output"]["Timer"]["PrintMode"].get(); int timerTimeSteps = 1; if ( timerSkipType == 0 ) { timerTimeSteps = converter->getLatticeTime( 1. /*config["Output"]["Timer"]["PhysTime"].get()*/ ); } else { // config["Output"]["Timer"]["TimeSteps"].read( timerTimeSteps ); } std::string filenameGif = config["Output"]["VisualizationImages"]["Filename"].get(); std::string filenameVtk = config["Output"]["VisualizationVTK"]["Filename"].get(); // === 2rd Step: Prepare Geometry === Vector extend( 1,1 ); Vector origin( 0,0 ); IndicatorCuboid2D cuboid( extend, origin ); #ifdef PARALLEL_MODE_MPI CuboidGeometry2D cuboidGeometry( cuboid, converter->getConversionFactorLength(), singleton::mpi().getSize() ); #else CuboidGeometry2D cuboidGeometry( cuboid, converter->getConversionFactorLength(), 7 ); #endif cuboidGeometry.print(); HeuristicLoadBalancer loadBalancer( cuboidGeometry ); SuperGeometry2D superGeometry( cuboidGeometry, loadBalancer, 2 ); prepareGeometry( *converter, superGeometry ); // === 3rd Step: Prepare Lattice === SuperLattice2D sLattice( superGeometry ); ConstRhoBGKdynamics bulkDynamics ( converter->getLatticeRelaxationFrequency(), instances::getBulkMomenta() ); sOnLatticeBoundaryCondition2D sBoundaryCondition( sLattice ); createInterpBoundaryCondition2D > ( sBoundaryCondition ); prepareLattice( *converter, sLattice, bulkDynamics, sBoundaryCondition, superGeometry ); // === 4th Step: Main Loop with Timer === int interval = converter->getLatticeTime( 1 /*config["Application"]["ConvergenceCheck"]["interval"].get()*/ ); T epsilon = 1e-3; //config["Application"]["ConvergenceCheck"]["residuum"].get(); util::ValueTracer converge( interval, epsilon ); timer->start(); for ( int iT=0; iT <= converter->getLatticeTime( maxPhysT ); ++iT ) { if ( converge.hasConverged() ) { clout << "Simulation converged." << endl; getResults( sLattice, *converter, iT, timer, logT, maxPhysT, imSave, vtkSave, filenameGif, filenameVtk, timerPrintMode, timerTimeSteps, superGeometry, converge.hasConverged() ); break; } // === 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, iT, timer, logT, maxPhysT, imSave, vtkSave, filenameGif, filenameVtk, timerPrintMode, timerTimeSteps, superGeometry, converge.hasConverged() ); converge.takeValue( sLattice.getStatistics().getAverageEnergy(), true ); } timer->stop(); timer->printSummary(); delete converter; delete timer; }