/* 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(); }