/* Lattice Boltzmann sample, written in C++, using the OpenLB * library * * Copyright (C) 2011-2013 Mathias J. Krause, Thomas Henn, Tim Dornieden * 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. */ /* cylinder3d.cpp: * This example examines a steady flow past a cylinder placed in a channel. * The cylinder is offset somewhat from the center of the flow to make the * steady-state symmetrical flow unstable. At the inlet, a Poiseuille profile is * imposed on the velocity, whereas the outlet implements a Dirichlet pressure * condition set by p = 0. * Inspired by "Benchmark Computations of Laminar Flow Around * a Cylinder" by M.Schäfer and S.Turek. For high resolution, low * latticeU, and enough time to converge, the results for pressure drop, drag * and lift lie within the estimated intervals for the exact results. * An unsteady flow with Karman vortex street can be created by changing the * Reynolds number to Re=100. * It also shows the usage of the STL-reader and explains how * to set boundary conditions automatically. */ #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 olb::util; using namespace std; typedef double T; #define DESCRIPTOR D3Q19<> // Parameters for the simulation setup const int N = 10; // resolution of the model const T Re = 20.; // Reynolds number const T maxPhysT = 16.; // max. simulation time in s, SI unit // Stores data from stl file in geometry in form of material numbers void prepareGeometry( UnitConverter const& converter, IndicatorF3D& indicator, STLreader& stlReader, SuperGeometry3D& superGeometry ) { OstreamManager clout( std::cout,"prepareGeometry" ); clout << "Prepare Geometry ..." << std::endl; superGeometry.rename( 0,2,indicator ); superGeometry.rename( 2,1,stlReader ); superGeometry.clean(); Vector origin = superGeometry.getStatistics().getMinPhysR( 2 ); origin[1] += converter.getConversionFactorLength()/2.; origin[2] += converter.getConversionFactorLength()/2.; Vector extend = superGeometry.getStatistics().getMaxPhysR( 2 ); extend[1] = extend[1]-origin[1]-converter.getConversionFactorLength()/2.; extend[2] = extend[2]-origin[2]-converter.getConversionFactorLength()/2.; // Set material number for inflow origin[0] = superGeometry.getStatistics().getMinPhysR( 2 )[0]-converter.getConversionFactorLength(); extend[0] = 2*converter.getConversionFactorLength(); IndicatorCuboid3D inflow( extend,origin ); superGeometry.rename( 2,3,inflow ); // Set material number for outflow origin[0] = superGeometry.getStatistics().getMaxPhysR( 2 )[0]-converter.getConversionFactorLength(); extend[0] = 2*converter.getConversionFactorLength(); IndicatorCuboid3D outflow( extend,origin ); superGeometry.rename( 2,4,outflow ); // Set material number for cylinder origin[0] = superGeometry.getStatistics().getMinPhysR( 2 )[0]+converter.getConversionFactorLength(); extend[0] = ( superGeometry.getStatistics().getMaxPhysR( 2 )[0]-superGeometry.getStatistics().getMinPhysR( 2 )[0] )/2.; IndicatorCuboid3D cylinder( extend,origin ); superGeometry.rename( 2,5,cylinder ); // Removes all not needed boundary voxels outside the surface superGeometry.clean(); superGeometry.checkForErrors(); superGeometry.print(); clout << "Prepare Geometry ... OK" << std::endl; } // Set up the geometry of the simulation void prepareLattice( SuperLattice3D& sLattice, UnitConverter const& converter, Dynamics& bulkDynamics, sOnLatticeBoundaryCondition3D& bc, sOffLatticeBoundaryCondition3D& offBc, STLreader& stlReader, SuperGeometry3D& 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 // 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 ); // Material=5 -->bouzidi sLattice.defineDynamics( superGeometry, 5, &instances::getNoDynamics() ); offBc.addZeroVelocityBoundary( superGeometry, 5, stlReader ); // Initial conditions AnalyticalConst3D rhoF( 1 ); Vector velocityV; AnalyticalConst3D uF(velocityV); // Initialize all values of distribution functions to their local equilibrium sLattice.defineRhoU( bulkIndicator, rhoF, uF ); sLattice.iniEquilibrium( bulkIndicator, rhoF, uF ); // 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( SuperLattice3D& sLattice, UnitConverter const& converter, int iT, SuperGeometry3D& superGeometry ) { OstreamManager clout( std::cout,"setBoundaryValues" ); // No of time steps for smooth start-up int iTmaxStart = converter.getLatticeTime( maxPhysT*0.4 ); int iTupdate = 30; 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; } } // Computes the pressure drop between the voxels before and after the cylinder void getResults( SuperLattice3D& sLattice, UnitConverter const& converter, int iT, SuperGeometry3D& superGeometry, Timer& timer, STLreader& stlReader ) { OstreamManager clout( std::cout,"getResults" ); SuperVTMwriter3D vtmWriter( "cylinder3d" ); SuperLatticePhysVelocity3D velocity( sLattice, converter ); SuperLatticePhysPressure3D pressure( sLattice, converter ); SuperLatticeYplus3D yPlus( sLattice, converter, superGeometry, stlReader, 5 ); vtmWriter.addFunctor( velocity ); vtmWriter.addFunctor( pressure ); vtmWriter.addFunctor( yPlus ); const int vtkIter = converter.getLatticeTime( .3 ); const int statIter = 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 vtk files if ( iT%vtkIter == 0 ) { vtmWriter.write( iT ); SuperEuklidNorm3D normVel( velocity ); BlockReduction3D2D planeReduction( normVel, {0, 0, 1} ); // write output as JPEG heatmap::write(planeReduction, iT); } // Writes output on the console if ( iT%statIter == 0 ) { // Timer console output timer.update( iT ); timer.printStep(); // Lattice statistics console output sLattice.getStatistics().print( iT,converter.getPhysTime( iT ) ); // Drag, lift, pressure drop AnalyticalFfromSuperF3D intpolatePressure( pressure, true ); SuperLatticePhysDrag3D drag( sLattice, superGeometry, 5, converter ); std::vector point1V = superGeometry.getStatistics().getCenterPhysR( 5 ); std::vector point2V = superGeometry.getStatistics().getCenterPhysR( 5 ); T point1[3] = {}; T point2[3] = {}; for ( int i = 0; i<3; i++ ) { point1[i] = point1V[i]; point2[i] = point2V[i]; } point1[0] = superGeometry.getStatistics().getMinPhysR( 5 )[0] - converter.getConversionFactorLength(); point2[0] = superGeometry.getStatistics().getMaxPhysR( 5 )[0] + converter.getConversionFactorLength(); T p1, p2; intpolatePressure( &p1,point1 ); intpolatePressure( &p2,point2 ); clout << "pressure1=" << p1; clout << "; pressure2=" << p2; T pressureDrop = p1-p2; clout << "; pressureDrop=" << pressureDrop; T dragA[3]; int input1[0]; drag( dragA, input1 ); clout << "; drag=" << dragA[0] << "; lift=" << dragA[1] << endl; int input[4] = {}; SuperMax3D yPlusMaxF( yPlus, superGeometry, 1 ); T yPlusMax[1]; yPlusMaxF( yPlusMax,input ); clout << "yPlusMax=" << yPlusMax[0] << endl; } } 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); UnitConverterFromResolutionAndRelaxationTime const converter( int {N}, // resolution: number of voxels per charPhysL (T) 0.53, // latticeRelaxationTime: relaxation time, have to be greater than 0.5! (T) 0.1, // charPhysLength: reference length of simulation geometry (T) 0.2, // charPhysVelocity: maximal/highest expected velocity during simulation in __m / s__ (T) 0.2*2.*0.05/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("cylinder3d"); // === 2nd Step: Prepare Geometry === // Instantiation of the STLreader class // file name, voxel size in meter, stl unit in meter, outer voxel no., inner voxel no. STLreader stlReader( "cylinder3d.stl", converter.getConversionFactorLength(), 0.001 ); IndicatorLayer3D extendedDomain( stlReader, converter.getConversionFactorLength() ); // Instantiation of a cuboidGeometry with weights #ifdef PARALLEL_MODE_MPI const int noOfCuboids = singleton::mpi().getSize(); #else const int noOfCuboids = 7; #endif CuboidGeometry3D cuboidGeometry( extendedDomain, converter.getConversionFactorLength(), noOfCuboids ); // Instantiation of a loadBalancer HeuristicLoadBalancer loadBalancer( cuboidGeometry ); // Instantiation of a superGeometry SuperGeometry3D superGeometry( cuboidGeometry, loadBalancer, 2 ); prepareGeometry( converter, extendedDomain, stlReader, 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); sOffLatticeBoundaryCondition3D sOffBoundaryCondition( sLattice ); createBouzidiBoundaryCondition3D ( sOffBoundaryCondition ); prepareLattice( sLattice, converter, bulkDynamics, sBoundaryCondition, sOffBoundaryCondition, stlReader, superGeometry ); // === 4th Step: Main Loop with Timer === clout << "starting simulation..." << endl; Timer timer( converter.getLatticeTime( maxPhysT ), superGeometry.getStatistics().getNvoxel() ); timer.start(); for ( int iT = 0; iT < converter.getLatticeTime( maxPhysT ); ++iT ) { // === 5th Step: Definition of Initial and Boundary Conditions === setBoundaryValues( sLattice, converter, iT, superGeometry ); // === 6th Step: Collide and Stream Execution === sLattice.collideAndStream(); // === 7th Step: Computation and Output of the Results === getResults( sLattice, converter, iT, superGeometry, timer, stlReader ); } timer.stop(); timer.printSummary(); }