/* Lattice Boltzmann sample, written in C++, using the OpenLB * library * * Copyright (C) 2011-2016 Robin Trunk, 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. */ /* bifurcation3d.cpp: * This example examines a steady particulate flow past a bifurcation. At the inlet, * an inflow condition with grad_n u = 0 and rho = 1 is implemented. * At both outlets, a Poiseuille profile is imposed on the velocity. * After a start time, particles are put into the bifurcation by imposing * a inflow condition with rho = 1 on the second euler phase at the inlet. * The particles are simulated as a continuum with a advection-diffusion equation * and experience a stokes drag force. * * A publication using the same geometry can be found here: * http://link.springer.com/chapter/10.1007/978-3-642-36961-2_5 * * */ #include "olb3D.h" #include "olb3D.hh" // use only generic version! using namespace std; using namespace olb; using namespace olb::descriptors; using namespace olb::graphics; using namespace olb::util; typedef double T; #define NSDESCRIPTOR D3Q19<> #define ADDESCRIPTOR D3Q7 const T Re = 50; // Reynolds number const int N = 19; // resolution of the model const int iTperiod = 100; // amount of timesteps when new boundary conditions are reset and results are visualized const T diffusion = 1.e-6; // diffusion coefficient for advection-diffusion equation const T radius = 1.5e-04; // particles radius const T partRho = 998.2; // particles density const T maxPhysT = 10.; // max. simulation time in s, SI unit // center of inflow and outflow regions [m] Vector inletCenter( T(), T(), 0.0786395 ); Vector outletCenter0( -0.0235929682287551, -0.000052820468762797, -0.021445708949909 ); Vector outletCenter1( 0.0233643529416147, 0.00000212439067050152, -0.0211994104877918 ); // radii of inflow and outflow regions [m] T inletRadius = 0.00999839; T outletRadius0 = 0.007927; T outletRadius1 = 0.00787134; // normals of inflow and outflow regions Vector inletNormal( T(), T(), T( -1 ) ); Vector outletNormal0( 0.505126, -0.04177, 0.862034 ); Vector outletNormal1( -0.483331, -0.0102764, 0.875377 ); 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(); // rename the material at the inlet IndicatorCircle3D inletCircle( inletCenter, inletNormal, inletRadius ); IndicatorCylinder3D inlet( inletCircle, 2 * converter.getConversionFactorLength() ); superGeometry.rename( 2, 3, 1, inlet ); // rename the material at the outlet0 IndicatorCircle3D outletCircle0( outletCenter0, outletNormal0, 0.95*outletRadius0 ); IndicatorCylinder3D outlet0( outletCircle0, 4 * converter.getConversionFactorLength() ); superGeometry.rename( 2, 4, outlet0 ); // rename the material at the outlet1 IndicatorCircle3D outletCircle1( outletCenter1, outletNormal1, 0.95*outletRadius1 ); IndicatorCylinder3D outlet1( outletCircle1, 4 * converter.getConversionFactorLength() ); superGeometry.rename( 2, 5, outlet1 ); superGeometry.clean(); superGeometry.innerClean( true ); superGeometry.checkForErrors(); superGeometry.print(); clout << "Prepare Geometry ... OK" << std::endl; return; } void prepareLattice( SuperLattice3D& sLatticeNS, SuperLattice3D& sLatticeAD, UnitConverter const& converter, Dynamics& bulkDynamics, Dynamics& bulkDynamicsAD, sOnLatticeBoundaryCondition3D& bc, sOnLatticeBoundaryCondition3D& bcAD, SuperGeometry3D& superGeometry, T omegaAD ) { OstreamManager clout( std::cout, "prepareLattice" ); clout << "Prepare Lattice ..." << std::endl; const T omega = converter.getLatticeRelaxationFrequency(); // Material=0 --> do nothing sLatticeNS.defineDynamics( superGeometry, 0, &instances::getNoDynamics() ); sLatticeAD.defineDynamics( superGeometry, 0, &instances::getNoDynamics() ); // Material=1 --> bulk dynamics // Material=3 --> bulk dynamics (inflow) auto inflowIndicator = superGeometry.getMaterialIndicator({1, 3}); sLatticeNS.defineDynamics( inflowIndicator, &bulkDynamics ); sLatticeAD.defineDynamics( inflowIndicator, &bulkDynamicsAD ); // Material=2 --> bounce-back / sLatticeNS.defineDynamics( superGeometry, 2, &instances::getBounceBack() ); sLatticeAD.defineDynamics( superGeometry, 2, &instances::getZeroDistributionDynamics() ); // Material=4,5 -->bulk dynamics / do-nothing (outflow) auto outflowIndicator = superGeometry.getMaterialIndicator({4, 5}); sLatticeNS.defineDynamics( outflowIndicator, &bulkDynamics ); sLatticeAD.defineDynamics( outflowIndicator, &instances::getNoDynamics() ); // Setting of the boundary conditions bc.addPressureBoundary( superGeometry, 3, omega ); bc.addVelocityBoundary( outflowIndicator, omega ); bcAD.addZeroDistributionBoundary( superGeometry, 2 ); bcAD.addTemperatureBoundary( superGeometry, 3, omegaAD ); bcAD.addConvectionBoundary( outflowIndicator ); bcAD.addExtFieldBoundary( superGeometry.getMaterialIndicator({2, 3, 4, 5}), ADDESCRIPTOR::index() ); // Initial conditions AnalyticalConst3D rho1( 1. ); AnalyticalConst3D rho0( 1.e-8 ); std::vector velocity( 3,T() ); AnalyticalConst3D u0( velocity ); // Initialize all values of distribution functions to their local equilibrium sLatticeNS.defineRhoU( superGeometry.getMaterialIndicator({1, 2, 3, 4, 5}), rho1, u0 ); sLatticeNS.iniEquilibrium( superGeometry.getMaterialIndicator({1, 2, 3, 4, 5}), rho1, u0 ); sLatticeAD.defineRho( superGeometry, 3, rho1 ); sLatticeAD.iniEquilibrium( superGeometry.getMaterialIndicator({1, 2, 4, 5}), rho0, u0 ); // Lattice initialize sLatticeNS.initialize(); sLatticeAD.initialize(); clout << "Prepare Lattice ... OK" << std::endl; return; } void setBoundaryValues( SuperLattice3D& sLatticeNS, UnitConverter const& converter, int iT, T maxPhysT, SuperGeometry3D& superGeometry ) { OstreamManager clout( std::cout, "setBoundaryValues" ); // No of time steps for smooth start-up int iTmaxStart = converter.getLatticeTime( 0.8*maxPhysT ); // Set inflow velocity T maxVelocity = converter.getCharLatticeVelocity() * 3. / 4. * std::pow( inletRadius, 2 ) / std::pow( outletRadius0, 2 ); if ( iT % iTperiod == 0 ) { if ( iT <= iTmaxStart ) { SinusStartScale startScale( iTmaxStart, T( 1 ) ); int iTvec[1] = { iT }; T frac[1] = { T( 0 ) }; startScale( frac, iTvec ); maxVelocity *= frac[0]; } CirclePoiseuille3D poiseuilleU4( outletCenter0[0], outletCenter0[1], outletCenter0[2], outletNormal0[0], outletNormal0[1], outletNormal0[2], outletRadius0 * 0.95, -maxVelocity ); CirclePoiseuille3D poiseuilleU5( outletCenter1[0], outletCenter1[1], outletCenter1[2], outletNormal1[0], outletNormal1[1], outletNormal1[2], outletRadius1 * 0.95, -maxVelocity ); sLatticeNS.defineU( superGeometry, 4, poiseuilleU4 ); sLatticeNS.defineU( superGeometry, 5, poiseuilleU5 ); } } void getResults( SuperLattice3D& sLatticeNS, SuperLattice3D& sLatticeAD, UnitConverter const& converter, int iT, SuperGeometry3D& superGeometry, Timer& timer ) { OstreamManager clout( std::cout, "getResults" ); SuperVTMwriter3D vtmWriter( "bifurcation3d_fluid" ); SuperVTMwriter3D vtmWriterAD( "bifurcation3d_particle" ); SuperLatticePhysVelocity3D velocity( sLatticeNS, converter ); SuperLatticeVelocity3D latticeVelocity( sLatticeNS); SuperLatticePhysPressure3D pressure( sLatticeNS, converter ); SuperLatticeDensity3D particles( sLatticeAD ); SuperLatticePhysExternal3D extField(sLatticeAD, converter.getConversionFactorVelocity(), ADDESCRIPTOR::index(), ADDESCRIPTOR::size()); vtmWriter.addFunctor( velocity ); vtmWriter.addFunctor( pressure ); vtmWriterAD.addFunctor( particles ); vtmWriterAD.addFunctor( extField ); if ( iT == 0 ) { SuperLatticeGeometry3D geometry( sLatticeNS, superGeometry ); SuperLatticeCuboid3D cuboid( sLatticeNS ); SuperLatticeRank3D rank( sLatticeNS ); vtmWriter.write( geometry ); vtmWriter.write( cuboid ); vtmWriter.write( rank ); vtmWriter.createMasterFile(); vtmWriterAD.createMasterFile(); // Print some output of the chosen simulation setup clout << "N=" << N << "; maxTimeSteps=" << converter.getLatticeTime( maxPhysT ) << "; noOfCuboid=" << superGeometry.getCuboidGeometry().getNc() << "; Re=" << Re << "; St=" << ( 2.*partRho*radius*radius*converter.getCharPhysVelocity() ) / ( 9.*converter.getPhysViscosity()*converter.getPhysDensity()*converter.getCharPhysLength() ) << std::endl; } if ( iT % iTperiod == 0 ) { // Writes the vtk files vtmWriter.write( iT ); vtmWriterAD.write( iT ); // GIF Writer SuperEuklidNorm3D normVel( velocity ); HyperplaneLattice3D gifLattice( superGeometry.getCuboidGeometry(), Hyperplane3D() .centeredIn(superGeometry.getCuboidGeometry().getMotherCuboid()) .normalTo({0, -1, 0}), 600); BlockReduction3D2D planeReductionVelocity( normVel, gifLattice, BlockDataSyncMode::ReduceOnly ); BlockReduction3D2D planeReductionParticles( particles, gifLattice, BlockDataSyncMode::ReduceOnly ); // write output as JPEG heatmap::write(planeReductionVelocity, iT); heatmap::write(planeReductionParticles, iT); // Writes output on the console timer.update( iT ); timer.printStep(); sLatticeNS.getStatistics().print( iT, iT * converter.getCharLatticeVelocity() / T(converter.getResolution()) ); // preparation for flux computations const std::vector materials = { 1, 3, 4, 5 }; IndicatorCircle3D inlet( inletCenter + 2. * converter.getConversionFactorLength() * inletNormal, inletNormal, inletRadius + 2. * converter.getConversionFactorLength() ); IndicatorCircle3D outlet0( outletCenter0 + 2. * converter.getConversionFactorLength() * outletNormal0, outletNormal0, outletRadius0 + 2. * converter.getConversionFactorLength() ); IndicatorCircle3D outlet1( outletCenter1 + 2. * converter.getConversionFactorLength() * outletNormal1, outletNormal1, outletRadius1 + 2. * converter.getConversionFactorLength() ); // Flux of the fluid at the inlet and outlet regions SuperPlaneIntegralFluxVelocity3D vFluxInflow( sLatticeNS, converter, superGeometry, inlet, materials ); vFluxInflow.print( "inflow", "ml/s" ); SuperPlaneIntegralFluxPressure3D pFluxInflow( sLatticeNS, converter, superGeometry, inlet, materials ); pFluxInflow.print( "inflow", "N", "Pa" ); SuperPlaneIntegralFluxVelocity3D vFluxOutflow0( sLatticeNS, converter, superGeometry, outlet0, materials ); vFluxOutflow0.print( "outflow0", "ml/s" ); SuperPlaneIntegralFluxPressure3D pFluxOutflow0( sLatticeNS, converter, superGeometry, outlet0, materials ); pFluxOutflow0.print( "outflow0", "N", "Pa" ); SuperPlaneIntegralFluxVelocity3D vFluxOutflow1( sLatticeNS, converter, superGeometry, outlet1, materials ); vFluxOutflow1.print( "outflow1", "ml/s" ); SuperPlaneIntegralFluxPressure3D pFluxOutflow1( sLatticeNS, converter, superGeometry, outlet1, materials ); pFluxOutflow1.print( "outflow1", "N", "Pa" ); int input[4] = {0}; T mFlux[5] = {0.}, mFlux0[5] = {0.}, mFlux1[5] = {0.}; // Flux of particles at the inlet and outlet regions: Inflow, Outflow0 and Outlfow1 SuperPlaneIntegralFluxMass3D mFluxInflow(latticeVelocity,particles, superGeometry, converter.getConversionFactorMass(), converter.getConversionFactorTime(), inlet, materials); SuperPlaneIntegralFluxMass3D mFluxOutflow0(latticeVelocity, particles, superGeometry, converter.getConversionFactorMass(), converter.getConversionFactorTime(),outlet0, materials); SuperPlaneIntegralFluxMass3D mFluxOutflow1(latticeVelocity, particles, superGeometry, converter.getConversionFactorMass(), converter.getConversionFactorTime(), outlet1, materials); mFluxInflow( mFlux,input ); mFluxOutflow0( mFlux0,input ); mFluxOutflow1( mFlux1,input ); // Since more diffusion is added to ensure stability the computed escaperate falls short of the real value, // therefore it is scaled by the factor 1.4 computed by a simulation without drag force. This value is computed // for this specific setup. For further information see R.Trunk, T.Henn, W.Dörfler, H.Nirschl, M.J.Krause, // "Inertial Dilute Particulate Fluid Flow Simulations with an Euler-Euler Lattice Boltzmann Method" T escr = -( mFlux0[0]+mFlux1[0] )/mFlux[0]*1.4; clout << "escapeRate=" << escr << "; captureRate="<< 1-escr << std::endl; } } int main( int argc, char* argv[] ) { // === 1st Step: Initialization === olbInit( &argc, &argv ); singleton::directories().setOutputDir( "./tmp/" ); OstreamManager clout( std::cout, "main" ); UnitConverterFromResolutionAndRelaxationTime const converter( int {N}, // resolution: number of voxels per charPhysL (T) 0.557646, // latticeRelaxationTime: relaxation time, have to be greater than 0.5! (T) inletRadius*2., // charPhysLength: reference length of simulation geometry (T) Re*1.5e-5/( inletRadius*2 ), // charPhysVelocity: maximal/highest expected velocity during simulation in __m / s__ (T) 1.5e-5, // physViscosity: physical kinematic viscosity in __m^2 / s__ (T) 1.225 // 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("bifurcation3d"); // compute relaxation parameter to solve the advection-diffusion equation in the lattice Boltzmann context T omegaAD = converter.getLatticeRelaxationFrequencyFromDiffusivity( diffusion ); // === 2nd Step: Prepare Geometry === STLreader stlReader( "../bifurcation3d.stl",converter.getConversionFactorLength() ); IndicatorLayer3D extendedDomain( stlReader,converter.getConversionFactorLength() ); // Instantiation of an empty cuboidGeometry int noOfCuboids = std::max( 20, singleton::mpi().getSize() ); CuboidGeometry3D cuboidGeometry( extendedDomain, converter.getConversionFactorLength(), noOfCuboids, "weight" ); cuboidGeometry.printExtended(); clout << "min / max ratio (volume) = " << (T) cuboidGeometry.getMinLatticeVolume() / cuboidGeometry.getMaxLatticeVolume() << endl; clout << "min / max ratio (weight) = " << (T) cuboidGeometry.getMinLatticeWeight() / cuboidGeometry.getMaxLatticeWeight() << endl; // Instantiation of an empty loadBalancer HeuristicLoadBalancer loadBalancer( cuboidGeometry ); // Default instantiation of superGeometry SuperGeometry3D superGeometry( cuboidGeometry, loadBalancer, 2 ); prepareGeometry( converter, extendedDomain, stlReader, superGeometry ); // === 3rd Step: Prepare Lattice === SuperLattice3D sLatticeNS( superGeometry ); SuperLattice3D sLatticeAD( superGeometry ); SuperExternal3D sExternal( superGeometry, sLatticeAD, sLatticeAD.getOverlap() ); BGKdynamics bulkDynamics( converter.getLatticeRelaxationFrequency(), instances::getBulkMomenta() ); ParticleAdvectionDiffusionBGKdynamics bulkDynamicsAD ( omegaAD, instances::getBulkMomenta() ); sOnLatticeBoundaryCondition3D sBoundaryCondition( sLatticeNS ); createInterpBoundaryCondition3D( sBoundaryCondition ); sOnLatticeBoundaryCondition3D sBoundaryConditionAD( sLatticeAD ); createAdvectionDiffusionBoundaryCondition3D( sBoundaryConditionAD ); prepareLattice( sLatticeNS, sLatticeAD, converter, bulkDynamics, bulkDynamicsAD, sBoundaryCondition, sBoundaryConditionAD, superGeometry, omegaAD ); AdvectionDiffusionParticleCouplingGenerator3D coupling( ADDESCRIPTOR::index() ); AdvDiffDragForce3D dragForce( converter,radius,partRho ); coupling.addForce( dragForce ); sLatticeNS.addLatticeCoupling( superGeometry, 1, coupling, sLatticeAD ); // === 4th Step: Main Loop with Timer === Timer timer( converter.getLatticeTime( maxPhysT ), superGeometry.getStatistics().getNvoxel() ); timer.start(); for ( int iT = 0; iT <= converter.getLatticeTime( maxPhysT ); ++iT ) { getResults( sLatticeNS, sLatticeAD, converter, iT, superGeometry, timer ); setBoundaryValues( sLatticeNS, converter, iT, maxPhysT, superGeometry ); sLatticeNS.executeCoupling(); sExternal.communicate(); sLatticeNS.collideAndStream(); sLatticeAD.collideAndStream(); } timer.stop(); timer.printSummary(); }