void writeResults(MultiBlockLattice3D<T,DESCRIPTOR>& lattice, MultiScalarField3D<T>& volumeFraction, plint iT) { static const plint nx = lattice.getNx(); static const plint ny = lattice.getNy(); static const plint nz = lattice.getNz(); Box3D slice(0, nx-1, ny/2, ny/2, 0, nz-1); ImageWriter<T> imageWriter("leeloo"); imageWriter.writeScaledPpm(createFileName("u", iT, 6), *computeVelocityNorm(lattice, slice)); imageWriter.writeScaledPpm(createFileName("rho", iT, 6), *computeDensity(lattice, slice)); imageWriter.writeScaledPpm(createFileName("volumeFraction", iT, 6), *extractSubDomain(volumeFraction, slice)); // Use a marching-cube algorithm to reconstruct the free surface and write an STL file. std::vector<T> isoLevels; isoLevels.push_back((T) 0.5); typedef TriangleSet<T>::Triangle Triangle; std::vector<Triangle> triangles; isoSurfaceMarchingCube(triangles, volumeFraction, isoLevels, volumeFraction.getBoundingBox()); TriangleSet<T>(triangles).writeBinarySTL(createFileName(outDir+"/interface", iT, 6)+".stl"); VtkImageOutput3D<T> vtkOut(createFileName("volumeFraction", iT, 6), 1.); vtkOut.writeData<float>(volumeFraction, "vf", 1.); }
void cavitySetup( MultiBlockLattice3D<T,DESCRIPTOR>& lattice, IncomprFlowParam<T> const& parameters, OnLatticeBoundaryCondition3D<T,DESCRIPTOR>& boundaryCondition ) { const plint nx = parameters.getNx(); const plint ny = parameters.getNy(); const plint nz = parameters.getNz(); Box3D topLid = Box3D(0, nx-1, ny-1, ny-1, 0, nz-1); Box3D bottomLid = Box3D(0, nx-1, 0, 0, 0, nz-1); Box3D everythingButTopLid = Box3D(0, nx-1, 0, ny-2, 0, nz-1); /* instantiateOuterNLDboundary(lattice, lattice.getBoundingBox()); setOuterNLDboundaryDynamics(lattice, lattice.getBackgroundDynamics().clone(), lattice.getBoundingBox(), boundary::dirichlet); setOuterNLDboundaryDynamics(lattice, lattice.getBackgroundDynamics().clone(), bottomLid, boundary::neumann); defineDynamics(lattice, bottomLid, lattice.getBackgroundDynamics().clone()); */ boundaryCondition.setVelocityConditionOnBlockBoundaries(lattice, lattice.getBoundingBox(), boundary::dirichlet); T u = sqrt((T)2)/(T)2 * parameters.getLatticeU(); initializeAtEquilibrium(lattice, everythingButTopLid, (T) 1., Array<T,3>(0.,0.,0.) ); initializeAtEquilibrium(lattice, topLid, (T) 1., Array<T,3>(u,0.,u) ); setBoundaryVelocity(lattice, topLid, Array<T,3>(u,0.,u) ); lattice.initialize(); }
void iniLattice( MultiBlockLattice3D<T,DESCRIPTOR>& lattice, VoxelizedDomain3D<T>& voxelizedDomain ) { // Switch all remaining outer cells to no-dynamics, except the outer // boundary layer, and keep the rest as BGKdynamics. defineDynamics(lattice, voxelizedDomain.getVoxelMatrix(), lattice.getBoundingBox(), new NoDynamics<T,DESCRIPTOR>, voxelFlag::outside); initializeAtEquilibrium(lattice, lattice.getBoundingBox(), (T) 1., Array<T,3>((T)0.,(T)0.,(T)0.)); lattice.initialize(); }
void writeGifs(MultiBlockLattice3D<T,DESCRIPTOR>& lattice, plint iter) { const plint imSize = 600; const plint nx = lattice.getNx(); const plint ny = lattice.getNy(); const plint nz = lattice.getNz(); Box3D slice(0, nx-1, 0, ny-1, nz/2, nz/2); ImageWriter<T> imageWriter("leeloo"); imageWriter.writeScaledGif(createFileName("u", iter, 6), *computeVelocityNorm(lattice, slice), imSize, imSize ); }
void channelSetup( MultiBlockLattice3D<T,DESCRIPTOR>& lattice, IncomprFlowParam<T> const& parameters, OnLatticeBoundaryCondition3D<T,DESCRIPTOR>& boundaryCondition ) { // Create Velocity boundary conditions boundaryCondition.setVelocityConditionOnBlockBoundaries(lattice); setBoundaryVelocity ( lattice, lattice.getBoundingBox(), PoiseuilleVelocity<T>(parameters) ); lattice.initialize(); }
void writeGifs(MultiBlockLattice3D<T,DESCRIPTOR>& lattice, plint iter){ const plint nx = lattice.getNx(); const plint ny = lattice.getNy(); const plint nz = lattice.getNz(); const plint imSize = 600; ImageWriter<T> imageWriter("leeloo"); Box3D slice(0, nx-1, 0, ny-1, nz/2, nz/2); //imageWriter.writeGif(createFileName("u", iT, 6), //*computeDensity(lattice), ); imageWriter.writeGif( createFileName("rho", iter, 6), *computeDensity(lattice, slice), (T) rho0 - drho/1000000, (T) rho0 + drho/1000000, imSize, imSize); }
void writeVTK(MultiBlockLattice3D<T,DESCRIPTOR>& lattice, IncomprFlowParam<T> const& parameters, PhysUnits3D<T> const& units, plint iter) { T p_fact = units.getPhysForce(1)/pow(units.getPhysLength(1),2)/3.; std::string fname(createFileName("vtk", iter, 6)); VtkImageOutput3D<T> vtkOut(fname, units.getPhysLength(1)); vtkOut.writeData<3,float>(*computeVelocity(lattice), "velocity", units.getPhysVel(1)); vtkOut.writeData<float>(*computeDensity(lattice), "density",units.getPhysDensity(1)); MultiScalarField3D<T> p(*computeDensity(lattice)); subtractInPlace(p,1.); vtkOut.writeData<float>(p,"pressure",p_fact ); IBscalarQuantity sf = SolidFraction; applyProcessingFunctional(new GetScalarQuantityFromDynamicsFunctional<T,DESCRIPTOR,T>(sf), lattice.getBoundingBox(),lattice,p); vtkOut.writeData<float>(p,"solidfraction",1. ); pcout << "wrote " << fname << std::endl; }
int main(int argc, char* argv[]) { plbInit(&argc, &argv); global::directories().setOutputDir("./tmp/"); // Use the class IncomprFlowParam to convert from // dimensionless variables to lattice units, in the // context of incompressible flows. IncomprFlowParam<T> parameters( (T) 1e-2, // Reference velocity (the maximum velocity // in the Poiseuille profile) in lattice units. (T) 100., // Reynolds number 30, // Resolution of the reference length (channel height). 3., // Channel length in dimensionless variables 1., // Channel height in dimensionless variables 1. // Channel depth in dimensionless variables ); const T imSave = (T)0.02; // Time intervals at which to save GIF // images, in dimensionless time units. const T maxT = (T)2.5; // Total simulation time, in dimensionless // time units. writeLogFile(parameters, "3D Poiseuille flow"); MultiBlockLattice3D<T, DESCRIPTOR> lattice ( parameters.getNx(), parameters.getNy(), parameters.getNz(), new BGKdynamics<T,DESCRIPTOR>(parameters.getOmega()) ); OnLatticeBoundaryCondition3D<T,DESCRIPTOR>* //boundaryCondition = createInterpBoundaryCondition3D<T,DESCRIPTOR>(); boundaryCondition = createLocalBoundaryCondition3D<T,DESCRIPTOR>(); channelSetup(lattice, parameters, *boundaryCondition); // Main loop over time iterations. for (plint iT=0; iT*parameters.getDeltaT()<maxT; ++iT) { if (iT%parameters.nStep(imSave)==0) { pcout << "Saving Gif at time step " << iT << endl; writeGifs(lattice, iT); } // Execute lattice Boltzmann iteration. lattice.collideAndStream(); } delete boundaryCondition; }
void cavitySetup( MultiBlockLattice3D<T,DESCRIPTOR>& lattice, IncomprFlowParam<T> const& parameters, OnLatticeBoundaryCondition3D<T,DESCRIPTOR>& boundaryCondition ) { const plint nx = parameters.getNx(); const plint ny = parameters.getNy(); const plint nz = parameters.getNz(); Box3D topLid = Box3D(0, nx-1, ny-1, ny-1, 0, nz-1); Box3D everythingButTopLid = Box3D(0, nx-1, 0, ny-2, 0, nz-1); boundaryCondition.setVelocityConditionOnBlockBoundaries(lattice); T u = std::sqrt((T)2)/(T)2 * parameters.getLatticeU(); initializeAtEquilibrium(lattice, everythingButTopLid, (T)1., Array<T,3>((T)0.,(T)0.,(T)0.) ); initializeAtEquilibrium(lattice, topLid, (T)1., Array<T,3>(u,(T)0.,u) ); setBoundaryVelocity(lattice, topLid, Array<T,3>(u,(T)0.,u) ); lattice.initialize(); }
int main(int argc, char* argv[]) { plbInit(&argc, &argv); global::directories().setOutputDir("./tmp/"); IncomprFlowParam<T> parameters( (T) 1e-2, // uMax (T) 100., // Re 100, // N 1., // lx 1., // ly 1. // lz ); const plint logIter = 10; const plint imageIter = 60; const plint checkPointIter = 200; plint iniT, endT; if (argc != 3) { pcout << "Error; the syntax is \"" << argv[0] << " start-iter end-iter\"," << endl; return -1; } stringstream iniTstr, endTstr; iniTstr << argv[1]; iniTstr >> iniT; endTstr << argv[2]; endTstr >> endT; writeLogFile(parameters, "3D diagonal cavity"); MultiBlockLattice3D<T, DESCRIPTOR> lattice ( parameters.getNx(), parameters.getNy(), parameters.getNz(), new BGKdynamics<T,DESCRIPTOR>(parameters.getOmega()) ); OnLatticeBoundaryCondition3D<T,DESCRIPTOR>* boundaryCondition //= createInterpBoundaryCondition3D<T,DESCRIPTOR>(); = createLocalBoundaryCondition3D<T,DESCRIPTOR>(); cavitySetup(lattice, parameters, *boundaryCondition); delete boundaryCondition; // Load saved data from a previous simulation, if the initial time step // is larger than zero. if (iniT>0) { //loadRawMultiBlock(lattice, "checkpoint.dat"); loadBinaryBlock(lattice, "checkpoint.dat"); } // Main loop over time iterations. for (plint iT=iniT; iT<endT; ++iT) { if (iT%imageIter==0) { pcout << "Writing Gif ..." << endl; writeGifs(lattice, parameters, iT); } if (iT%checkPointIter==0 && iT>iniT) { pcout << "Saving the state of the simulation ..." << endl; //saveRawMultiBlock(lattice, "checkpoint.dat"); saveBinaryBlock(lattice, "checkpoint.dat"); } // Lattice Boltzmann iteration step. lattice.collideAndStream(); if (iT%logIter==0) { pcout << "step " << iT << "; t=" << iT*parameters.getDeltaT() << "; av energy=" << setprecision(10) << getStoredAverageEnergy<T>(lattice) << "; av rho=" << setprecision(10) << getStoredAverageDensity<T>(lattice) << endl; } } }
int main(int argc, char* argv[]) { plbInit(&argc, &argv); //defaultMultiBlockPolicy3D().toggleBlockingCommunication(true); plint N; try { global::argv(1).read(N); } catch(...) { pcout << "Wrong parameters. The syntax is " << std::endl; pcout << argv[0] << " N" << std::endl; pcout << "where N is the resolution. The benchmark cases published " << std::endl; pcout << "on the Palabos Wiki use N=100, N=400, N=1000, or N=4000." << std::endl; exit(1); } pcout << "Starting benchmark with " << N+1 << "x" << N+1 << "x" << N+1 << " grid points " << "(approx. 2 minutes on modern processors)." << std::endl; IncomprFlowParam<T> parameters( (T) 1e-2, // uMax (T) 1., // Re N, // N 1., // lx 1., // ly 1. // lz ); MultiBlockLattice3D<T, DESCRIPTOR> lattice ( parameters.getNx(), parameters.getNy(), parameters.getNz(), new BGKdynamics<T,DESCRIPTOR>(parameters.getOmega()) ); plint numCores = global::mpi().getSize(); pcout << "Number of MPI threads: " << numCores << std::endl; // Current cores run approximately at 5 Mega Sus. T estimateSus= 5.e6*numCores; // The benchmark should run for approximately two minutes // (2*60 seconds). T wishNumSeconds = 60.; plint numCells = lattice.getBoundingBox().nCells(); // Run at least three iterations. plint numIter = std::max( (plint)3, (plint)(estimateSus*wishNumSeconds/numCells+0.5)); OnLatticeBoundaryCondition3D<T,DESCRIPTOR>* boundaryCondition = createLocalBoundaryCondition3D<T,DESCRIPTOR>(); cavitySetup(lattice, parameters, *boundaryCondition); // Run the benchmark once "to warm up the machine". for (plint iT=0; iT<numIter; ++iT) { lattice.collideAndStream(); } // Run the benchmark for good. global::timer("benchmark").start(); global::profiler().turnOn(); for (plint iT=0; iT<numIter; ++iT) { lattice.collideAndStream(); } pcout << "After " << numIter << " iterations: " << (T) (numCells*numIter) / global::timer("benchmark").getTime() / 1.e6 << " Mega site updates per second." << std::endl << std::endl; global::profiler().writeReport(); delete boundaryCondition; }
int main(int argc, char* argv[]) { plbInit(&argc, &argv); global::directories().setOutputDir("./tmp/"); IncomprFlowParam<T> parameters( (T) 1e-2, // uMax (T) 10., // Re 30, // N 1., // lx 1., // ly 1. // lz ); const T logT = (T)1/(T)100; const T imSave = (T)1/(T)10; const T vtkSave = (T)1; const T maxT = (T)10.1; pcout << "omega= " << parameters.getOmega() << std::endl; writeLogFile(parameters, "3D diagonal cavity"); T omega = parameters.getOmega(); #ifdef USE_MRT plint mrtId = 0; mrtParam<T,DESCRIPTOR>().set(mrtId,MRTparam<T,DESCRIPTOR>(omega)); #endif MultiBlockLattice3D<T, DESCRIPTOR> lattice ( parameters.getNx(), parameters.getNy(), parameters.getNz(), DYNAMICS ); #ifdef USE_ASINARI integrateProcessingFunctional(new AsinariPostCollide3D<T,DESCRIPTOR>, lattice.getBoundingBox(), lattice, 0); #endif OnLatticeBoundaryCondition3D<T,DESCRIPTOR>* boundaryCondition //= createInterpBoundaryCondition3D<T,DESCRIPTOR>(); = createLocalBoundaryCondition3D<T,DESCRIPTOR>(); cavitySetup(lattice, parameters, *boundaryCondition); T previousIterationTime = T(); // Loop over main time iteration. for (plint iT=0; iT<parameters.nStep(maxT); ++iT) { global::timer("mainLoop").restart(); if (iT%parameters.nStep(imSave)==0) { pcout << "Writing Gif ..." << endl; writeGifs(lattice, parameters, iT); } if (iT%parameters.nStep(vtkSave)==0 && iT>0) { pcout << "Saving VTK file ..." << endl; writeVTK(lattice, parameters, iT); } if (iT%parameters.nStep(logT)==0) { pcout << "step " << iT << "; t=" << iT*parameters.getDeltaT(); } // Execute a time iteration. lattice.collideAndStream(); // Access averages from internal statistics ( their value is defined // only after the call to lattice.collideAndStream() ) if (iT%parameters.nStep(logT)==0) { pcout << "; av energy=" << setprecision(10) << getStoredAverageEnergy<T>(lattice) << "; av rho=" << setprecision(10) << getStoredAverageDensity<T>(lattice) << endl; pcout << "Time spent during previous iteration: " << previousIterationTime << endl; } previousIterationTime = global::timer("mainLoop").stop(); } delete boundaryCondition; }
int main(int argc, char* argv[]) { /// === 1st Step: Initialization === olbInit(&argc, &argv); singleton::directories().setOutputDir("./tmp/"); OstreamManager clout(std::cout,"main"); LBconverter<T> converter( (int) 3, // dim (T) 1./20., // latticeL_ (T) 4e-2, // latticeU_ (T) 1./100., // charNu_ (T) 1. // charL_ = 1, ); writeLogFile(converter, "backwardFacingStep2d"); /// === 3rd Step: Prepare Lattice === //const T maxT = (T)100.1; const T maxT = (T)0.21; writeLogFile(converter, "3D backward facing step"); #ifndef PARALLEL_MODE_MPI // sequential program execution BlockLattice3D<T, DESCRIPTOR> lattice( converter.numNodes(18), converter.numNodes(1.5), converter.numNodes(1.5) ); #else // parallel program execution MultiBlockLattice3D<T, DESCRIPTOR> lattice ( createRegularDataDistribution( //converter.numNodes(18), converter.numNodes(1.5), converter.numNodes(1.5) ) );//401*31*31 converter.numNodes(36), converter.numNodes(12), converter.numNodes(12) ) ); #endif ConstRhoBGKdynamics<T, DESCRIPTOR> bulkDynamics ( converter.getOmega(), instances::getBulkMomenta<T,DESCRIPTOR>() ); // choose between local and non-local boundary condition OnLatticeBoundaryCondition3D<T,DESCRIPTOR>* //boundaryCondition = createInterpBoundaryCondition3D(lattice); boundaryCondition = createLocalBoundaryCondition3D(lattice); prepareLattice(converter, lattice, bulkDynamics, *boundaryCondition); /// === 4th Step: Main Loop with Timer === T st, et; int iT=0; st=time_sec(); // setBoundaryValues(converter, lattice, iT); et=time_sec(); cout << "set boundary: " << et - st << endl; st=time_sec(); lattice.collideAndStream(); getResults(lattice, converter, iT, maxT); //for (iT=0; iT < converter.numTimeSteps(maxT); ++iT) { //for (iT=1; iT < converter.numTimeSteps(maxT); ++iT) { for (iT=1; iT < 100; ++iT) { /// === 5th Step: Definition of Initial and Boundary Conditions === setBoundaryValues(converter, lattice, iT); /// === 6th Step: Collide and Stream Execution === lattice.collideAndStream(); /// === 7th Step: Computation and Output of the Results === getResults(lattice, converter, iT, maxT); } et=time_sec(); cout << "steps:"<<converter.numTimeSteps(maxT)<<"kernel time: " << et - st << endl; std::cout<<"nx:"<<converter.numNodes(36)<<"ny:"<<converter.numNodes(12)<<"nz:"<<converter.numNodes(12)<<std::endl; delete boundaryCondition; }
void writeVTK(MultiBlockLattice3D<T,DESCRIPTOR>& lattice, plint iter){ VtkImageOutput3D<T> vtkOut(createFileName("vtk", iter, 6), 1.); vtkOut.writeData<float>(*computeDensity(lattice), "density", 1.); std::auto_ptr<MultiScalarField3D<T> > velocity(plb::computeVelocityComponent(lattice, lattice.getBoundingBox(), 2)); vtkOut.writeData<T>(*velocity, "velocity", 1.); }
int main(int argc, char* argv[]) { plbInit(&argc, &argv); T uMax; plint N; T nu_f,d_part,v_frac, v_inf; std::string outDir; try { global::argv(1).read(d_part); global::argv(2).read(N); global::argv(3).read(v_frac); global::argv(4).read(nu_f); global::argv(5).read(v_inf); global::argv(6).read(uMax); global::argv(7).read(outDir); } catch(PlbIOException& exception) { pcout << exception.what() << endl; pcout << "Command line arguments:\n"; pcout << "1 : d_part\n"; pcout << "2 : N per particle diameter\n"; pcout << "3 : particle volume fraction\n"; pcout << "4 : nu_fluid\n"; pcout << "5 : estimated v_inf\n"; pcout << "6 : uMax\n"; pcout << "7 : outDir\n"; exit(1); } std::string lbOutDir(outDir), demOutDir(outDir); lbOutDir.append("tmp/"); demOutDir.append("post/"); global::directories().setOutputDir(lbOutDir); const T rho_f = 1000; LiggghtsCouplingWrapper wrapper(argv,global::mpi().getGlobalCommunicator()); // particle size and volume fraction are handed over to LIGGGHTS // as variables (see LIGGGHTS docu for details) wrapper.setVariable("r_part",d_part/2); wrapper.setVariable("v_frac",v_frac); wrapper.execFile("in.lbdem"); T g = 9.81; const T lx = 1., ly = 1., lz = 2.; T r_ = d_part/2.; T rho_s = 1100.; T m = r_*r_*r_*4./3.*3.14*rho_s; PhysUnits3D<T> units(2.*r_,v_inf,nu_f,lx,ly,lz,N,uMax,rho_f); IncomprFlowParam<T> parameters(units.getLbParam()); plint nx = parameters.getNx(), ny = parameters.getNy(), nz = parameters.getNz(); // get lattice decomposition from LIGGGHTS and create lattice according to parallelization // given in the LIGGGHTS input script LatticeDecomposition lDec(parameters.getNx(),parameters.getNy(),parameters.getNz(), wrapper.lmp); SparseBlockStructure3D blockStructure = lDec.getBlockDistribution(); ExplicitThreadAttribution* threadAttribution = lDec.getThreadAttribution(); plint envelopeWidth = 1; MultiBlockLattice3D<T, DESCRIPTOR> lattice (MultiBlockManagement3D (blockStructure, threadAttribution, envelopeWidth ), defaultMultiBlockPolicy3D().getBlockCommunicator(), defaultMultiBlockPolicy3D().getCombinedStatistics(), defaultMultiBlockPolicy3D().getMultiCellAccess<T,DESCRIPTOR>(), new DYNAMICS ); defineDynamics(lattice,lattice.getBoundingBox(),new DYNAMICS); const T maxT = ceil(3.*lz/v_inf); const T vtkT = 0.1; const T logT = 0.0000001; const plint maxSteps = units.getLbSteps(maxT); const plint vtkSteps = max<plint>(units.getLbSteps(vtkT),1); const plint logSteps = max<plint>(units.getLbSteps(logT),1); writeLogFile(parameters, "sedimenting spheres benchmark"); lattice.initialize(); T dt_phys = units.getPhysTime(1); plint demSubsteps = 10; T dt_dem = dt_phys/(T)demSubsteps; pcout << "------------------------------\n" << "omega: " << parameters.getOmega() << "\n" << "dt_phys: " << dt_phys << "\n" << "maxT: " << maxT << " | maxSteps: " << maxSteps << "\n" << "v_inf: " << v_inf << "\n" << "Re : " << parameters.getRe() << "\n" << "vtkT: " << vtkT << " | vtkSteps: " << vtkSteps << "\n" << "grid size: " << nx << " " << ny << " " << nz << "\n" << "------------------------------" << std::endl; // set timestep and output directory wrapper.setVariable("t_step",dt_dem); wrapper.setVariable("dmp_stp",vtkSteps*demSubsteps); wrapper.setVariable("dmp_dir",demOutDir); wrapper.execFile("in2.lbdem"); wrapper.runUpto(demSubsteps-1); clock_t start = clock(); clock_t loop = clock(); clock_t end = clock(); // Loop over main time iteration. for (plint iT=0; iT<=maxSteps; ++iT) { bool initWithVel = false; setSpheresOnLattice(lattice,wrapper,units,initWithVel); if(iT%vtkSteps == 0 && iT > 0) // LIGGGHTS does not write at timestep 0 writeVTK(lattice,parameters,units,iT); lattice.collideAndStream(); getForcesFromLattice(lattice,wrapper,units); wrapper.run(demSubsteps); if(iT%logSteps == 0) { end = clock(); T time = difftime(end,loop)/((T)CLOCKS_PER_SEC); T totaltime = difftime(end,start)/((T)CLOCKS_PER_SEC); T mlups = ((T) (lattice.getNx()*lattice.getNy()*lattice.getNz()*logSteps))/time/1e6; pcout << "time: " << time << " " ; pcout << "calculating at " << mlups << " MLU/s" << " | total time running: " << totaltime << std::endl; loop = clock(); } } T totaltime = difftime(end,start)/((T)CLOCKS_PER_SEC); T totalmlups = ((T) (lattice.getNx()*lattice.getNy()*lattice.getNz()*(maxSteps+1)))/totaltime/1e6; pcout << " ********************** \n" << "total time: " << totaltime << " calculating at " << totalmlups << " MLU/s" << std::endl; }
void runProgram() { /* * Read the obstacle geometry. */ pcout << std::endl << "Reading STL data for the obstacle geometry." << std::endl; Array<T,3> center(param.cx, param.cy, param.cz); Array<T,3> centerLB(param.cxLB, param.cyLB, param.czLB); // The triangle-set defines the surface of the geometry. TriangleSet<T> triangleSet(param.geometry_fname, DBL); // Place the obstacle in the correct place in the simulation domain. // Here the "geometric center" of the obstacle is computed manually, // by computing first its bounding cuboid. In cases that the STL // file with the geometry of the obstacle contains its center as // the point, say (0, 0, 0), then the following variable // "obstacleCenter" must be set to (0, 0, 0) manually. Cuboid<T> bCuboid = triangleSet.getBoundingCuboid(); Array<T,3> obstacleCenter = 0.5 * (bCuboid.lowerLeftCorner + bCuboid.upperRightCorner); triangleSet.translate(-obstacleCenter); triangleSet.scale(1.0/param.dx); // In lattice units from now on... triangleSet.translate(centerLB); triangleSet.writeBinarySTL(outputDir+"obstacle_LB.stl"); // The DEFscaledMesh, and the triangle-boundary are more sophisticated data // structures used internally by Palabos to treat the boundary. plint xDirection = 0; plint borderWidth = 1; // Because Guo acts in a one-cell layer. // Requirement: margin>=borderWidth. plint margin = 1; // Extra margin of allocated cells around the obstacle, for the case of moving walls. plint blockSize = 0; // Size of blocks in the sparse/parallel representation. // Zero means: don't use sparse representation. DEFscaledMesh<T> defMesh(triangleSet, 0, xDirection, margin, Dot3D(0, 0, 0)); TriangleBoundary3D<T> boundary(defMesh); //boundary.getMesh().inflate(); pcout << "tau = " << 1.0/param.omega << std::endl; pcout << "dx = " << param.dx << std::endl; pcout << "dt = " << param.dt << std::endl; pcout << "Number of iterations in an integral time scale: " << (plint) (1.0/param.dt) << std::endl; /* * Voxelize the domain. */ // Voxelize the domain means: decide which lattice nodes are inside the obstacle and which are outside. pcout << std::endl << "Voxelizing the domain." << std::endl; plint extendedEnvelopeWidth = 2; // Extrapolated off-lattice BCs. const int flowType = voxelFlag::outside; VoxelizedDomain3D<T> voxelizedDomain ( boundary, flowType, param.boundingBox(), borderWidth, extendedEnvelopeWidth, blockSize ); pcout << getMultiBlockInfo(voxelizedDomain.getVoxelMatrix()) << std::endl; /* * Generate the lattice, the density and momentum blocks. */ pcout << "Generating the lattice, the rhoBar and j fields." << std::endl; MultiBlockLattice3D<T,DESCRIPTOR> *lattice = new MultiBlockLattice3D<T,DESCRIPTOR>(voxelizedDomain.getVoxelMatrix()); if (param.useSmago) { defineDynamics(*lattice, lattice->getBoundingBox(), new SmagorinskyBGKdynamics<T,DESCRIPTOR>(param.omega, param.cSmago)); pcout << "Using Smagorinsky BGK dynamics." << std::endl; } else { defineDynamics(*lattice, lattice->getBoundingBox(), new BGKdynamics<T,DESCRIPTOR>(param.omega)); pcout << "Using BGK dynamics." << std::endl; } bool velIsJ = false; defineDynamics(*lattice, voxelizedDomain.getVoxelMatrix(), lattice->getBoundingBox(), new NoDynamics<T,DESCRIPTOR>(), voxelFlag::inside); lattice->toggleInternalStatistics(false); MultiBlockManagement3D sparseBlockManagement(lattice->getMultiBlockManagement()); // The rhoBar and j fields are used at both the collision and at the implementation of the // outflow boundary condition. plint envelopeWidth = 1; MultiScalarField3D<T> *rhoBar = new MultiScalarField3D<T> ( MultiBlockManagement3D ( sparseBlockManagement.getSparseBlockStructure(), sparseBlockManagement.getThreadAttribution().clone(), envelopeWidth ), defaultMultiBlockPolicy3D().getBlockCommunicator(), defaultMultiBlockPolicy3D().getCombinedStatistics(), defaultMultiBlockPolicy3D().getMultiScalarAccess<T>() ); rhoBar->toggleInternalStatistics(false); MultiTensorField3D<T,3> *j = new MultiTensorField3D<T,3> ( MultiBlockManagement3D ( sparseBlockManagement.getSparseBlockStructure(), sparseBlockManagement.getThreadAttribution().clone(), envelopeWidth ), defaultMultiBlockPolicy3D().getBlockCommunicator(), defaultMultiBlockPolicy3D().getCombinedStatistics(), defaultMultiBlockPolicy3D().getMultiTensorAccess<T,3>() ); j->toggleInternalStatistics(false); std::vector<MultiBlock3D*> lattice_rho_bar_j_arg; lattice_rho_bar_j_arg.push_back(lattice); lattice_rho_bar_j_arg.push_back(rhoBar); lattice_rho_bar_j_arg.push_back(j); integrateProcessingFunctional( new ExternalRhoJcollideAndStream3D<T,DESCRIPTOR>(), lattice->getBoundingBox(), lattice_rho_bar_j_arg, 0); integrateProcessingFunctional( new BoxRhoBarJfunctional3D<T,DESCRIPTOR>(), lattice->getBoundingBox(), lattice_rho_bar_j_arg, 3); // rhoBar and j are computed at level 3 because // the boundary conditions are on levels 1 and 2. /* * Generate the off-lattice boundary condition on the obstacle and the outer-domain boundary conditions. */ pcout << "Generating boundary conditions." << std::endl; OffLatticeBoundaryCondition3D<T,DESCRIPTOR,Velocity> *boundaryCondition; BoundaryProfiles3D<T,Velocity> profiles; bool useAllDirections=true; OffLatticeModel3D<T,Velocity>* offLatticeModel=0; if (param.freeSlipWall) { profiles.setWallProfile(new FreeSlipProfile3D<T>); } else { profiles.setWallProfile(new NoSlipProfile3D<T>); } offLatticeModel = new GuoOffLatticeModel3D<T,DESCRIPTOR> ( new TriangleFlowShape3D<T,Array<T,3> >(voxelizedDomain.getBoundary(), profiles), flowType, useAllDirections ); offLatticeModel->setVelIsJ(velIsJ); boundaryCondition = new OffLatticeBoundaryCondition3D<T,DESCRIPTOR,Velocity>( offLatticeModel, voxelizedDomain, *lattice); boundaryCondition->insert(); // The boundary condition algorithm or the outer domain. OnLatticeBoundaryCondition3D<T,DESCRIPTOR>* outerBoundaryCondition = createLocalBoundaryCondition3D<T,DESCRIPTOR>(); outerDomainBoundaries(lattice, rhoBar, j, outerBoundaryCondition); /* * Implement the outlet sponge zone. */ if (param.numOutletSpongeCells > 0) { T bulkValue; Array<plint,6> numSpongeCells; if (param.outletSpongeZoneType == 0) { pcout << "Generating an outlet viscosity sponge zone." << std::endl; bulkValue = param.omega; } else if (param.outletSpongeZoneType == 1) { pcout << "Generating an outlet Smagorinsky sponge zone." << std::endl; bulkValue = param.cSmago; } else { pcout << "Error: unknown type of sponge zone." << std::endl; exit(-1); } // Number of sponge zone lattice nodes at all the outer domain boundaries. // So: 0 means the boundary at x = 0 // 1 means the boundary at x = nx-1 // 2 means the boundary at y = 0 // and so on... numSpongeCells[0] = 0; numSpongeCells[1] = param.numOutletSpongeCells; numSpongeCells[2] = 0; numSpongeCells[3] = 0; numSpongeCells[4] = 0; numSpongeCells[5] = 0; std::vector<MultiBlock3D*> args; args.push_back(lattice); if (param.outletSpongeZoneType == 0) { applyProcessingFunctional(new ViscositySpongeZone<T,DESCRIPTOR>( param.nx, param.ny, param.nz, bulkValue, numSpongeCells), lattice->getBoundingBox(), args); } else { applyProcessingFunctional(new SmagorinskySpongeZone<T,DESCRIPTOR>( param.nx, param.ny, param.nz, bulkValue, param.targetSpongeCSmago, numSpongeCells), lattice->getBoundingBox(), args); } } /* * Setting the initial conditions. */ // Initial condition: Constant pressure and velocity-at-infinity everywhere. Array<T,3> uBoundary(param.getInletVelocity(0), 0.0, 0.0); initializeAtEquilibrium(*lattice, lattice->getBoundingBox(), 1.0, uBoundary); applyProcessingFunctional( new BoxRhoBarJfunctional3D<T,DESCRIPTOR>(), lattice->getBoundingBox(), lattice_rho_bar_j_arg); // Compute rhoBar and j before VirtualOutlet is executed. lattice->executeInternalProcessors(1); // Execute all processors except the ones at level 0. lattice->executeInternalProcessors(2); lattice->executeInternalProcessors(3); /* * Particles (streamlines). */ // This part of the code that relates to particles, is purely for visualization // purposes. Particles are used to compute streamlines essentially. // Definition of a particle field. MultiParticleField3D<ParticleFieldT>* particles = 0; if (param.useParticles) { particles = new MultiParticleField3D<ParticleFieldT> ( lattice->getMultiBlockManagement(), defaultMultiBlockPolicy3D().getCombinedStatistics() ); std::vector<MultiBlock3D*> particleArg; particleArg.push_back(particles); std::vector<MultiBlock3D*> particleFluidArg; particleFluidArg.push_back(particles); particleFluidArg.push_back(lattice); // Functional that advances the particles to their new position at each predefined time step. integrateProcessingFunctional ( new AdvanceParticlesEveryWhereFunctional3D<T,DESCRIPTOR>(param.cutOffSpeedSqr), lattice->getBoundingBox(), particleArg, 0); // Functional that assigns the particle velocity according to the particle's position in the fluid. integrateProcessingFunctional ( new FluidToParticleCoupling3D<T,DESCRIPTOR>((T) param.particleTimeFactor), lattice->getBoundingBox(), particleFluidArg, 1 ); // Definition of a domain from which particles will be injected in the flow field. Box3D injectionDomain(0, 0, centerLB[1]-0.25*param.ny, centerLB[1]+0.25*param.ny, centerLB[2]-0.25*param.nz, centerLB[2]+0.25*param.nz); // Definition of simple mass-less particles. Particle3D<T,DESCRIPTOR>* particleTemplate=0; particleTemplate = new PointParticle3D<T,DESCRIPTOR>(0, Array<T,3>(0.,0.,0.), Array<T,3>(0.,0.,0.)); // Functional which injects particles with predefined probability from the specified injection domain. std::vector<MultiBlock3D*> particleInjectionArg; particleInjectionArg.push_back(particles); integrateProcessingFunctional ( new InjectRandomParticlesFunctional3D<T,DESCRIPTOR>(particleTemplate, param.particleProbabilityPerCell), injectionDomain, particleInjectionArg, 0 ); // Definition of an absorbtion domain for the particles. Box3D absorbtionDomain(param.outlet); // Functional which absorbs the particles which reach the specified absorbtion domain. integrateProcessingFunctional ( new AbsorbParticlesFunctional3D<T,DESCRIPTOR>, absorbtionDomain, particleArg, 0 ); particles->executeInternalProcessors(); } /* * Starting the simulation. */ plb_ofstream energyFile((outputDir+"average_energy.dat").c_str()); pcout << std::endl; pcout << "Starting simulation." << std::endl; for (plint i = 0; i < param.maxIter; ++i) { if (i <= param.initialIter) { Array<T,3> uBoundary(param.getInletVelocity(i), 0.0, 0.0); setBoundaryVelocity(*lattice, param.inlet, uBoundary); } if (i % param.statIter == 0) { pcout << "At iteration " << i << ", t = " << i*param.dt << std::endl; Array<T,3> force(boundaryCondition->getForceOnObject()); T factor = util::sqr(util::sqr(param.dx)) / util::sqr(param.dt); pcout << "Force on object over fluid density: F[x] = " << force[0]*factor << ", F[y] = " << force[1]*factor << ", F[z] = " << force[2]*factor << std::endl; T avEnergy = boundaryCondition->computeAverageEnergy() * util::sqr(param.dx) / util::sqr(param.dt); pcout << "Average kinetic energy over fluid density: E = " << avEnergy << std::endl; energyFile << i*param.dt << " " << avEnergy << std::endl; pcout << std::endl; } if (i % param.vtkIter == 0) { pcout << "Writing VTK at time t = " << i*param.dt << endl; writeVTK(*boundaryCondition, i); if (param.useParticles) { writeParticleVtk<T,DESCRIPTOR> ( *particles, createFileName(outputDir+"particles_", i, PADDING) + ".vtk", param.maxNumParticlesToWrite ); } } if (i % param.imageIter == 0) { pcout << "Writing PPM image at time t = " << i*param.dt << endl; writePPM(*boundaryCondition, i); } lattice->executeInternalProcessors(); lattice->incrementTime(); if (param.useParticles && i % param.particleTimeFactor == 0) { particles->executeInternalProcessors(); } } energyFile.close(); delete outerBoundaryCondition; delete boundaryCondition; if (param.useParticles) { delete particles; } delete j; delete rhoBar; delete lattice; }