// =================================================================================== // Copyright ScalFmm 2013 INRIA, // // This software is a computer program whose purpose is to compute the FMM. // // This software is governed by the CeCILL-C and LGPL licenses and // abiding by the rules of distribution of free software. // // 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 and CeCILL-C Licenses for more details. // "http://www.cecill.info". // "http://www.gnu.org/licenses". // =================================================================================== // // #author P. Blanchard // ==== CMAKE ===== // @FUSE_BLAS // ================ #include #include #include #include "Files/FFmaGenericLoader.hpp" #include "Kernels/Interpolation/FInterpMatrixKernel.hpp" #include "Kernels/Chebyshev/FChebCell.hpp" #include "Kernels/Chebyshev/FChebTensorialKernel.hpp" #include "Components/FSimpleLeaf.hpp" #include "Kernels/P2P/FP2PParticleContainerIndexed.hpp" #include "Utils/FParameters.hpp" #include "Utils/FMemUtils.hpp" #include "Containers/FOctree.hpp" #include "Containers/FVector.hpp" #include "Core/FFmmAlgorithm.hpp" #include "Core/FFmmAlgorithmThread.hpp" /** * This program runs the FMM Algorithm with the Chebyshev kernel and compares the results with a direct computation. */ // Simply create particles and try the kernels int main(int argc, char* argv[]) { const char* const filename = FParameters::getStr(argc,argv,"-f", "../Data/test20k.fma"); const unsigned int TreeHeight = FParameters::getValue(argc, argv, "-depth", 3); const unsigned int SubTreeHeight = FParameters::getValue(argc, argv, "-subdepth", 2); const unsigned int NbThreads = FParameters::getValue(argc, argv, "-t", 1); #ifdef _OPENMP omp_set_num_threads(NbThreads); std::cout << "\n>> Using " << omp_get_max_threads() << " threads.\n" << std::endl; #else std::cout << "\n>> Sequential version.\n" << std:: #endif // init timer FTic time; // typedefs typedef FInterpMatrixKernel_R_IJ MatrixKernelClass; const unsigned int NPV = MatrixKernelClass::NPV; const unsigned int NPOT = MatrixKernelClass::NPOT; const unsigned int NRHS = MatrixKernelClass::NRHS; const unsigned int NLHS = MatrixKernelClass::NLHS; const double CoreWidth = 0.1; const MatrixKernelClass MatrixKernel(CoreWidth); // init particles position and physical value struct TestParticle{ FPoint position; FReal forces[3][NPOT]; FReal physicalValue[NPV]; FReal potential[NPOT]; }; const FReal FRandMax = FReal(RAND_MAX); // open particle file FFmaGenericLoader loader(filename); if(!loader.isOpen()) throw std::runtime_error("Particle file couldn't be opened!"); TestParticle* const particles = new TestParticle[loader.getNumberOfParticles()]; for(int idxPart = 0 ; idxPart < loader.getNumberOfParticles() ; ++idxPart){ FPoint position; FReal physicalValue = 0.0; loader.fillParticle(&position,&physicalValue); // get copy particles[idxPart].position = position; // Set physical values for(unsigned idxPV = 0; idxPV ContainerClass; typedef FSimpleLeaf< ContainerClass > LeafClass; typedef FChebCell CellClass; typedef FOctree OctreeClass; typedef FChebTensorialKernel KernelClass; typedef FFmmAlgorithm FmmClass; // typedef FFmmAlgorithmThread FmmClass; // init oct-tree OctreeClass tree(TreeHeight, SubTreeHeight, loader.getBoxWidth(), loader.getCenterOfBox()); { // ----------------------------------------------------- std::cout << "Creating & Inserting " << loader.getNumberOfParticles() << " particles ..." << std::endl; std::cout << "\tHeight : " << TreeHeight << " \t sub-height : " << SubTreeHeight << std::endl; time.tic(); for(int idxPart = 0 ; idxPart < loader.getNumberOfParticles() ; ++idxPart){ // put in tree // PB: here we have to know NPV... if(NPV==1) tree.insert(particles[idxPart].position, idxPart, particles[idxPart].physicalValue[0]); else if(NPV==3) tree.insert(particles[idxPart].position, idxPart, particles[idxPart].physicalValue[0], particles[idxPart].physicalValue[1], particles[idxPart].physicalValue[2]); else std::runtime_error("Insert correct number of physical values!"); } time.tac(); std::cout << "Done " << "(@Creating and Inserting Particles = " << time.elapsed() << "s)." << std::endl; } // ----------------------------------------------------- { // ----------------------------------------------------- std::cout << "\nChebyshev FMM (ORDER="<< ORDER << ") ... " << std::endl; time.tic(); KernelClass kernels(TreeHeight, loader.getBoxWidth(), loader.getCenterOfBox(), &MatrixKernel, BoxWidthExtension, epsilon); FmmClass algorithm(&tree, &kernels); algorithm.execute(); time.tac(); std::cout << "Done " << "(@Algorithm = " << time.elapsed() << "s)." << std::endl; } // ----------------------------------------------------- { // ----------------------------------------------------- std::cout << "\nError computation ... " << std::endl; FMath::FAccurater potentialDiff[NPOT]; FMath::FAccurater fx[NPOT], fy[NPOT], fz[NPOT]; FReal checkPotential[20000][NPOT]; FReal checkfx[20000][NPOT]; { // Check that each particle has been summed with all other tree.forEachLeaf([&](LeafClass* leaf){ for(unsigned idxPot = 0; idxPotgetTargets()->getPotentials(idxPot); const FReal*const forcesX = leaf->getTargets()->getForcesX(idxPot); const FReal*const forcesY = leaf->getTargets()->getForcesY(idxPot); const FReal*const forcesZ = leaf->getTargets()->getForcesZ(idxPot); const int nbParticlesInLeaf = leaf->getTargets()->getNbParticles(); const FVector& indexes = leaf->getTargets()->getIndexes(); for(int idxPart = 0 ; idxPart < nbParticlesInLeaf ; ++idxPart){ const int indexPartOrig = indexes[idxPart]; //PB: store potential in array[nbParticles] checkPotential[indexPartOrig][idxPot]=potentials[idxPart]; checkfx[indexPartOrig][idxPot]=forcesX[idxPart]; // update accuracy potentialDiff[idxPot].add(particles[indexPartOrig].potential[idxPot],potentials[idxPart]); fx[idxPot].add(particles[indexPartOrig].forces[0][idxPot],forcesX[idxPart]); fy[idxPot].add(particles[indexPartOrig].forces[1][idxPot],forcesY[idxPart]); fz[idxPot].add(particles[indexPartOrig].forces[2][idxPot],forcesZ[idxPart]); } }// NPOT }); } // Print for information std::cout << "\nRelative Inf/L2 errors: " << std::endl; std::cout << " Potential: " << std::endl; for(unsigned idxPot = 0; idxPot