// =================================================================================== // Copyright ScalFmm 2011 INRIA, Olivier Coulaud, Bérenger Bramas, Matthias Messner // 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". // =================================================================================== #ifndef FTAYLORKERNEL_HPP #define FTAYLORKERNEL_HPP #include "../../Components/FAbstractKernels.hpp" #include "../../Utils/FMemUtils.hpp" #include "../../Utils/FLog.hpp" #include "../P2P/FP2PR.hpp" /** * @author Cyrille Piacibello * @class FTaylorKernel * * @brief This kernel is an implementation of the different operators * needed to compute the Fast Multipole Method using Taylor Expansion * for the Far fields interaction. */ //TODO spécifier les arguments. template< class CellClass, class ContainerClass, int P, int order> class FTaylorKernel : public FAbstractKernels { private: //Size of the multipole and local vectors static const int SizeVector = ((P+1)*(P+2)*(P+3))*order/6; //////////////////////////////////////////////////// // Object Attributes //////////////////////////////////////////////////// const FReal boxWidth; //< the box width at leaf level const int treeHeight; //< The height of the tree const FReal widthAtLeafLevel; //< width of box at leaf level const FReal widthAtLeafLevelDiv2; //< width of box at leaf leve div 2 const FPoint boxCorner; //< position of the box corner FReal factorials[2*P+1]; //< This contains the factorial until P FReal arrayDX[P+2],arrayDY[P+2],arrayDZ[P+2] ; //< Working arrays static const int sizeDerivative = (2*P+1)*(P+1)*(2*P+3)/3; FReal _PsiVector[sizeDerivative]; FReal _coeffPoly[SizeVector]; //////////////////////////////////////////////////// // Private method //////////////////////////////////////////////////// /////////////////////////////////////////////////////// // Precomputation /////////////////////////////////////////////////////// /** Compute the factorial from 0 to P * Then the data is accessible in factorials array: * factorials[n] = n! with n <= P */ void precomputeFactorials(){ factorials[0] = 1.0; FReal fidx = 1.0; for(int idx = 1 ; idx <= 2*P ; ++idx, ++fidx){ factorials[idx] = fidx * factorials[idx-1]; } } /** Return the position of a leaf from its tree coordinate */ FPoint getLeafCenter(const FTreeCoordinate coordinate) const { return FPoint( FReal(coordinate.getX()) * widthAtLeafLevel + widthAtLeafLevelDiv2 + boxCorner.getX(), FReal(coordinate.getY()) * widthAtLeafLevel + widthAtLeafLevelDiv2 + boxCorner.getY(), FReal(coordinate.getZ()) * widthAtLeafLevel + widthAtLeafLevelDiv2 + boxCorner.getZ()); } /** * @brief Return the position of the center of a cell from its tree * coordinate * @param FTreeCoordinate * @param inLevel the current level of Cell */ FPoint getCellCenter(const FTreeCoordinate coordinate, int inLevel) { //Set the boxes width needed FReal widthAtCurrentLevel = widthAtLeafLevel*FReal(1 << (treeHeight-(inLevel+1))); FReal widthAtCurrentLevelDiv2 = widthAtCurrentLevel/FReal(2); //Get the coordinate int a = coordinate.getX(); int b = coordinate.getY(); int c = coordinate.getZ(); //Set the center real coordinates from box corner and widths. FReal X = boxCorner.getX() + FReal(a)*widthAtCurrentLevel + widthAtCurrentLevelDiv2; FReal Y = boxCorner.getY() + FReal(b)*widthAtCurrentLevel + widthAtCurrentLevelDiv2; FReal Z = boxCorner.getZ() + FReal(c)*widthAtCurrentLevel + widthAtCurrentLevelDiv2; FPoint cCenter = FPoint(X,Y,Z); return cCenter; } /** * @brief Incrementation of powers in Taylor expansion * Result : ...,[2,0,0],[1,1,0],[1,0,1],[0,2,0]... 3-tuple are sorted * by size then alphabetical order. */ void incPowers(int * const FRestrict a, int *const FRestrict b, int *const FRestrict c) { int t = (*a)+(*b)+(*c); if(t==0) {a[0]=1;} else{ if(t==a[0]) {a[0]--; b[0]++;} else{ if(t==c[0]) {a[0]=t+1; c[0]=0;} else{ if(b[0]!=0) {b[0]--; c[0]++;} else{ b[0]=c[0]+1; a[0]--; c[0]=0; } } } } } /** * @brief Give the index of array from the corresponding 3-tuple * powers. */ int powerToIdx(const int a,const int b,const int c) { int t,res,p = a+b+c; res = p*(p+1)*(p+2)/6; t = p - a; res += t*(t+1)/2+c; return res; } /* Return the factorial of a number */ FReal fact(const int a){ if(a<0) { printf("fact :: Error factorial negative!! a=%d\n",a); return FReal(0); } FReal result = 1; for(int i = 1 ; i <= a ; ++i){ result *= FReal(i); } return result; } /* Return the product of factorial of 3 numbers */ FReal fact3int(const int a,const int b,const int c) { return ( factorials[a]*factorials[b]* factorials[c]) ; } /* Return the combine of a paire of number * \f[ C^{b}_{a} \f] */ FReal combin(const int& a, const int& b){ if(a= 2 // |x-y|^2*Psi_{a,b,c} + (2*a-1)*dx*Psi_{a-1,b,c} + a*(a-2)*Psi_{a-2,b,c} + 2*b*dy*Psi_{a,b-1,c} + b*(b-1)*Psi_{a,b-2,c} // + 2*c*dz*Psi_{a,b,c-1}} = 0 idxTarget = powerToIdx(a,b,c); idxSrc1 = powerToIdx(a-1,b,c); idxSrc2 = powerToIdx(a,b-1,c); idxSrc3 = powerToIdx(a,b,c-1); idxSrc4 = powerToIdx(a-2,b,c); idxSrc5 = powerToIdx(a,b-2,c); idxSrc6 = powerToIdx(a,b,c-2); yetComputed[idxTarget] = -(FReal(2*a-1)*dx*yetComputed[idxSrc1] + FReal((a-1)*(a-1))*yetComputed[idxSrc4] + FReal(2*b)*dy*yetComputed[idxSrc2] + FReal(b*(b-1))*yetComputed[idxSrc5] + FReal(2*c)*dz*yetComputed[idxSrc3] + FReal(c*(c-1))*yetComputed[idxSrc6])/dist2; } } } } } } ///////////////////////////////// ///////// Public Methods //////// ///////////////////////////////// public: /*Constructor, need system information*/ FTaylorKernel(const int inTreeHeight, const FReal inBoxWidth, const FPoint& inBoxCenter) : boxWidth(inBoxWidth), treeHeight(inTreeHeight), widthAtLeafLevel(inBoxWidth/FReal(1 << (inTreeHeight-1))), widthAtLeafLevelDiv2(widthAtLeafLevel/2), boxCorner(inBoxCenter.getX()-(inBoxWidth/2),inBoxCenter.getY()-(inBoxWidth/2),inBoxCenter.getZ()-(inBoxWidth/2)) { FReal facto; this->precomputeFactorials() ; for(int i=0, a = 0, b = 0, c = 0; i(fact3int(a,b,c)); _coeffPoly[i] = FReal(1.0)/(facto); #ifdef OC _coeffPoly[i] = static_cast(factorials[a+b+c])/(facto*facto); #endif this->incPowers(&a,&b,&c); //inc powers of expansion } } /* Default destructor */ virtual ~FTaylorKernel(){ } /**P2M * @brief Fill the Multipole with the field created by the cell * particles. * * Formula : * \f[ * M_{k} = \frac{|k|!}{k! k!} \sum_{j=0}^{N}{ q_j (x_c-x_j)^{k}} * \f] * where \f$x_c\f$ is the centre of the cell and \f$x_j\f$ the \f$j^{th}\f$ particles and \f$q_j\f$ its charge and \f$N\f$ the particle number. */ void P2M(CellClass* const pole, const ContainerClass* const particles) { //Copying cell center position once and for all const FPoint& cellCenter = getLeafCenter(pole->getCoordinate()); FReal * FRestrict multipole = pole->getMultipole(); FMemUtils::memset(multipole,0,SizeVector*sizeof(FReal(0.0))); FReal multipole2[SizeVector] ; // Iterator over Particles int nbPart = particles->getNbParticles(), i; const FReal* const * positions = particles->getPositions(); const FReal* posX = positions[0]; const FReal* posY = positions[1]; const FReal* posZ = positions[2]; const FReal* phyValue = particles->getPhysicalValues(); // Iterating over Particles FReal xc = cellCenter.getX(), yc = cellCenter.getY(), zc = cellCenter.getZ() ; FReal dx[3] ; for(int idPart=0 ; idPartgetCoordinate(),inLevel); FReal * FRestrict mult = pole->getMultipole(); //Iteration over the eight children int idxChild; FReal coeff; for(idxChild=0 ; idxChild<8 ; ++idxChild) { if(child[idxChild]){ //Test if child exists const FPoint& childCenter = getCellCenter(child[idxChild]->getCoordinate(),inLevel+1); const FReal * FRestrict multChild = child[idxChild]->getMultipole(); //Set the distance between centers of cells dx = cellCenter.getX() - childCenter.getX(); dy = cellCenter.getY() - childCenter.getY(); dz = cellCenter.getZ() - childCenter.getZ(); // Precompute the arrays of dx^i arrayDX[0] = 1.0 ; arrayDY[0] = 1.0 ; arrayDZ[0] = 1.0 ; for (int i = 1 ; i <= P ; ++i) { arrayDX[i] = dx * arrayDX[i-1] ; arrayDY[i] = dy * arrayDY[i-1] ; arrayDZ[i] = dz * arrayDZ[i-1] ; } a=0; b=0; c=0; FReal value; //Iteration over parent multipole array for(int idxMult = 0 ; idxMultgetCoordinate(),inLevel); FReal * FRestrict iterLocal = local->getLocal(); for(idxNeigh=0 ; idxNeigh<343 ; ++idxNeigh){ //Need to test if current neighbor is one of the interaction list if(distantNeighbors[idxNeigh]){ const FPoint curDistCenter = getCellCenter(distantNeighbors[idxNeigh]->getCoordinate(),inLevel); FMemUtils::memset(this->_PsiVector,0,sizeDerivative*sizeof(FReal(0.0))); // Compute derivatives on locCenter - curDistCenter // target source FReal dx = locCenter.getX() - curDistCenter.getX(); FReal dy = locCenter.getY() - curDistCenter.getY(); FReal dz = locCenter.getZ() - curDistCenter.getZ(); //Iterative Computation of all the derivatives needed this->computeFullDerivative(dx,dy,dz,this->_PsiVector); const FReal * multipole = distantNeighbors[idxNeigh]->getMultipole(); int al=0,bl=0 /*,cl=0*/ ; // For local array int am,bm,cm; // For distant array int id=0; // Index of iterLocal int i; //Case (al,bl,cl) == (0,0,0) for(int j = 0 ; j < SizeVector ; ++j){ //corresponding powers am,bm,cm iterLocal[0] += this->_PsiVector[j]*multipole[j]; } for(i=1, id=1 ; i<=P ; ++i){ //Case (i,0,0) : FReal fctl = factorials[i]; FReal coeffL = FReal(1.0)/(fctl); //Iterator over multipole array FReal tmp = 0.0; am=0; bm=0; cm=0; for(int j = 0 ; j < SizeVector ; ++j){ //corresponding powers am,bm,cm int idxPsi = powerToIdx(i+am,bm,cm); tmp += this->_PsiVector[idxPsi]*multipole[j]; incPowers(&am,&bm,&cm); } iterLocal[id] += tmp*coeffL ; //access to iterLocal[ (i,0,0) ] is inlined. //Case (i-1,1,0) : fctl = factorials[i-1]; coeffL = FReal(1.0)/(fctl); //Iterator over multipole array tmp = 0.0; am=0; bm=0; cm=0; for(int j = 0 ; j < SizeVector ; ++j){ //corresponding powers am,bm,cm int idxPsi = powerToIdx((i-1)+am,1+bm,cm); tmp += this->_PsiVector[idxPsi]*multipole[j]; //updating a,b,c incPowers(&am,&bm,&cm); } iterLocal[++id] += tmp*coeffL; //access to iterLocal[ (i-1,1,0) ] is inlined //Case (i-1,0,1) : //Values of fctl and coeffL are the same for (i-1,1,0) and (i-1,0,1) //Iterator over multipole array tmp = 0.0; am=0; bm=0; cm=0; for(int j = 0 ; j < SizeVector ; ++j){ //corresponding powers am,bm,cm int idxPsi = powerToIdx((i-1)+am,bm,1+cm); tmp += this->_PsiVector[idxPsi]*multipole[j]; //updating a,b,c incPowers(&am,&bm,&cm); } iterLocal[++id] += tmp*coeffL; //access to iterLocal[ (i-1,0,1) ] is inlined //End of specific case //Start of general case : ++id; for(al=i-2 ; al>= 0 ; --al) { for(bl=i-al ; bl>=0 ; --bl, ++id) { am = 0 ; bm = 0 ; cm = 0 ; tmp = FReal(0); fctl = fact3int(al,bl,i-(al+bl)); coeffL = FReal(1.0)/(fctl); for(int j = 0 ; j < SizeVector ; ++j){ //corresponding powers am,bm,cm int idxPsi = powerToIdx(al+am,bl+bm,(i-al-bl)+cm); tmp += this->_PsiVector[idxPsi]*multipole[j]; //updating a,b,c incPowers(&am,&bm,&cm); } iterLocal[id] += tmp*coeffL; } } } } } } /** *@brief Translate the local expansion of parent cell to child cell * * One need to translate the local expansion on a father cell * centered in \f$\mathbf{x}_p\f$ to its eight daughters centered in * \f$\mathbf{x}_p\f$ . * * Local expansion for the daughters will be : * \f[ \sum_{\mathbf{k}=0}^{|k|getCoordinate(),inLevel); // Get father local expansion const FReal* FRestrict fatherExpansion = fatherCell->getLocal() ; int idxFatherLoc; //index of Father local expansion to be read. FReal dx, dy, dz, coeff; int ap, bp, cp, af, bf, cf; //Indexes of expansion for father and child. // For all children for(int idxChild = 0 ; idxChild < 8 ; ++idxChild){ if(childCell[idxChild]){ //test if child exists FReal* FRestrict childExpansion = childCell[idxChild]->getLocal() ; const FPoint& childCenter = getCellCenter(childCell[idxChild]->getCoordinate(),inLevel+1); //Set the distance between centers of cells // Child - father dx = childCenter.getX()-locCenter.getX(); dy = childCenter.getY()-locCenter.getY(); dz = childCenter.getZ()-locCenter.getZ(); // Precompute the arrays of dx^i arrayDX[0] = 1.0 ; arrayDY[0] = 1.0 ; arrayDZ[0] = 1.0 ; for (int i = 1 ; i <= P ; ++i) { arrayDX[i] = dx * arrayDX[i-1] ; arrayDY[i] = dy * arrayDY[i-1] ; arrayDZ[i] = dz * arrayDZ[i-1] ; } //iterator over child's local expansion (to be filled) af=0; bf=0; cf=0; for(int k=0 ; kgetCoordinate()); //Iterator over particles int nbPart = particles->getNbParticles(); //Iteration over Local array // const FReal * iterLocal = local->getLocal(); const FReal * const * positions = particles->getPositions(); const FReal * posX = positions[0]; const FReal * posY = positions[1]; const FReal * posZ = positions[2]; FReal * forceX = particles->getForcesX(); FReal * forceY = particles->getForcesY(); FReal * forceZ = particles->getForcesZ(); FReal * targetsPotentials = particles->getPotentials(); FReal * phyValues = particles->getPhysicalValues(); //Iteration over particles for(int i=0 ; i::FullMutual(targets,directNeighborsParticles,14); } void P2PRemote(const FTreeCoordinate& /*inPosition*/, ContainerClass* const FRestrict inTargets, const ContainerClass* const FRestrict /*inSources*/, ContainerClass* const inNeighbors[27], const int /*inSize*/){ FP2PRT::FullRemote(inTargets,inNeighbors,27); } }; #endif