// =================================================================================== // Copyright ScalFmm 2011 INRIA, // olivier.coulaud@inria.fr, berenger.bramas@inria.fr // 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. Banchard // Modifs // O. Coulaud // ==== CMAKE ===== // @FUSE_FFT // ================ // Keep in private GIT #include #include #include #include "Files/FFmaGenericLoader.hpp" #include "Kernels/Interpolation/FInterpMatrixKernel_TensorialInteractions.hpp" #include "Kernels/Uniform/FUnifCell.hpp" #include "Kernels/Uniform/FUnifTensorialKernel.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" #include "Utils/FParameterNames.hpp" // For std::array<> (i.e. for Tensorial kernels purpose) #include /** * This program runs the FMM Algorithm with the Uniform kernel and compares the results with a direct computation. */ // Simply create particles and try the kernels int main(int argc, char* argv[]) { FHelpDescribeAndExit(argc, argv, "Test Uniform Tensorial kernel and compare it with the direct computation.", FParameterDefinitions::OctreeHeight,FParameterDefinitions::NbThreads, FParameterDefinitions::OctreeSubHeight, FParameterDefinitions::InputFile); typedef double FReal; const char* const filename = FParameters::getStr(argc,argv,FParameterDefinitions::InputFile.options, "../Data/test20k.fma"); const unsigned int TreeHeight = FParameters::getValue(argc, argv, FParameterDefinitions::OctreeHeight.options, 3); const unsigned int SubTreeHeight = FParameters::getValue(argc, argv, FParameterDefinitions::OctreeSubHeight.options, 2); const unsigned int NbThreads = FParameters::getValue(argc, argv, FParameterDefinitions::NbThreads.options, 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 and infos typedef FInterpMatrixKernel_R_IJ MatrixKernelClass; MatrixKernelClass::printInfo(); // useful features of matrix kernel 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 FReal CoreWidth = 0.; std::cout << "Core width: a=" << CoreWidth << std::endl; std::cout << std::endl; // Build matrix kernel const MatrixKernelClass MatrixKernel(CoreWidth); // init particles position and physical value struct TestParticle{ FPoint position; FReal forces[3][NPOT]; FReal physicalValue[NPV]; FReal potential[NPOT]; }; // 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(FSize 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 LeafClass; typedef FUnifCell CellClass; typedef FOctree OctreeClass; typedef FUnifTensorialKernel 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(FSize idxPart = 0 ; idxPart < loader.getNumberOfParticles() ; ++idxPart){ // put in tree if(NPV==1) // scalar kernels like ONE_OVER_R tree.insert(particles[idxPart].position, idxPart, particles[idxPart].physicalValue[0]); else if(NPV==3) // R_IJ or IOR tree.insert(particles[idxPart].position, idxPart, particles[idxPart].physicalValue[0], particles[idxPart].physicalValue[1], particles[idxPart].physicalValue[2]); else std::runtime_error("NPV not yet supported in test! Add new case."); // // [TODO] Fix insertion of multiple physical values using std::array !! // //// Convert FReal[NVALS] to std::array //std::array physicalState; //for(int idxVals = 0 ; idxVals < NVALS ; ++idxVals){ // for(int idxPV = 0 ; idxPV < NPV ; ++idxPV) // physicalState[idxPV*NVALS+idxVals]=particles[idxPart].physicalValue[idxPV]; // physicalState[(NPV+0)*NVALS+idxVals]=0.0; // physicalState[(NPV+1)*NVALS+idxVals]=0.0; // physicalState[(NPV+2)*NVALS+idxVals]=0.0; // physicalState[(NPV+3)*NVALS+idxVals]=0.0; //} //tree.insert(particles[idxPart].position, idxPart,physicalState); } time.tac(); std::cout << "Done " << "(@Creating and Inserting Particles = " << time.elapsed() << "s)." << std::endl; } // ----------------------------------------------------- { // ----------------------------------------------------- std::cout << "\nLagrange/Uniform grid FMM (ORDER="<< ORDER << ") ... " << std::endl; time.tic(); KernelClass* kernels = new KernelClass(TreeHeight, loader.getBoxWidth(), loader.getCenterOfBox(),&MatrixKernel,BoxWidthExtension); 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 FSize nbParticlesInLeaf = leaf->getTargets()->getNbParticles(); const FVector& indexes = leaf->getTargets()->getIndexes(); for(FSize idxPart = 0 ; idxPart < nbParticlesInLeaf ; ++idxPart){ const FSize indexPartOrig = indexes[idxPart]; //PB: store potential in array[nbParticles] checkPotential[indexPartOrig][idxPot]=potentials[idxPart]; checkfx[indexPartOrig][idxPot]=forcesX[idxPart]; //if(idxPart<10) // std::cout << " FMM potentials[idxPartOrigin="<< indexPartOrig <<"]=" << potentials[idxPart] << " DIRECT potentials[idxPartOrigin="<< indexPartOrig <<"]=" << particles[indexPartOrig].potential[idxPot] << std::endl; 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