Commit 12fa0584 authored by BLANCHARD Pierre's avatar BLANCHARD Pierre

Introduced tensorial kernels for Uniform FMM (NB: Chebyshev UCB incompatible,...

Introduced tensorial kernels for Uniform FMM (NB: Chebyshev UCB incompatible, TODO Chebyshev Sym), tested for kernel R_{,ij} and its derivative, fixed multidimensionnal fft, + some cleanup.
parent 67c21775
......@@ -31,18 +31,18 @@
* This class defines a cell used in the Chebyshev based FMM.
* @param NVALS is the number of right hand side.
*/
template <int ORDER, int NVALS = 1>
template <int ORDER, int NMUL = 1, int NLOC = 1>
class FChebCell : public FExtendMortonIndex, public FExtendCoordinate
{
static const int VectorSize = TensorTraits<ORDER>::nnodes * 2;
FReal multipole_exp[NVALS * VectorSize]; //< Multipole expansion
FReal local_exp[NVALS * VectorSize]; //< Local expansion
FReal multipole_exp[NMUL * VectorSize]; //< Multipole expansion
FReal local_exp[NLOC * VectorSize]; //< Local expansion
public:
FChebCell(){
memset(multipole_exp, 0, sizeof(FReal) * NVALS * VectorSize);
memset(local_exp, 0, sizeof(FReal) * NVALS * VectorSize);
memset(multipole_exp, 0, sizeof(FReal) * NMUL * VectorSize);
memset(local_exp, 0, sizeof(FReal) * NLOC * VectorSize);
}
~FChebCell() {}
......@@ -72,26 +72,26 @@ public:
/** Make it like the begining */
void resetToInitialState(){
memset(multipole_exp, 0, sizeof(FReal) * NVALS * VectorSize);
memset(local_exp, 0, sizeof(FReal) * NVALS * VectorSize);
memset(multipole_exp, 0, sizeof(FReal) * NMUL * VectorSize);
memset(local_exp, 0, sizeof(FReal) * NLOC * VectorSize);
}
};
template <int ORDER, int NVALS = 1>
class FTypedChebCell : public FChebCell<ORDER,NVALS>, public FExtendCellType {
template <int ORDER, int NMUL = 1, int NLOC = 1>
class FTypedChebCell : public FChebCell<ORDER,NMUL,NLOC>, public FExtendCellType {
public:
template <class BufferWriterClass>
void save(BufferWriterClass& buffer) const{
FChebCell<ORDER,NVALS>::save(buffer);
FChebCell<ORDER,NMUL,NLOC>::save(buffer);
FExtendCellType::save(buffer);
}
template <class BufferReaderClass>
void restore(BufferReaderClass& buffer){
FChebCell<ORDER,NVALS>::restore(buffer);
FChebCell<ORDER,NMUL,NLOC>::restore(buffer);
FExtendCellType::restore(buffer);
}
void resetToInitialState(){
FChebCell<ORDER,NVALS>::resetToInitialState();
FChebCell<ORDER,NMUL,NLOC>::resetToInitialState();
FExtendCellType::resetToInitialState();
}
};
......
This diff is collapsed.
This diff is collapsed.
......@@ -48,6 +48,42 @@ struct DirectInteractionComputer<LENNARD_JONES_POTENTIAL, 1>
}
};
/*! Specialization for ID_OVER_R potential */
template <>
struct DirectInteractionComputer<ID_OVER_R, 1>
{
template <typename ContainerClass>
static void P2P( ContainerClass* const FRestrict TargetParticles,
ContainerClass* const NeighborSourceParticles[27]){
FP2P::FullMutualIOR(TargetParticles,NeighborSourceParticles,14);
}
template <typename ContainerClass>
static void P2PRemote( ContainerClass* const FRestrict inTargets,
ContainerClass* const inNeighbors[27],
const int inSize){
FP2P::FullRemoteIOR(inTargets,inNeighbors,inSize);
}
};
/*! Specialization for GradGradR potential */
template <>
struct DirectInteractionComputer<R_IJ, 1>
{
template <typename ContainerClass>
static void P2P( ContainerClass* const FRestrict TargetParticles,
ContainerClass* const NeighborSourceParticles[27]){
FP2P::FullMutualRIJ(TargetParticles,NeighborSourceParticles,14);
}
template <typename ContainerClass>
static void P2PRemote( ContainerClass* const FRestrict inTargets,
ContainerClass* const inNeighbors[27],
const int inSize){
FP2P::FullRemoteRIJ(inTargets,inNeighbors,inSize);
}
};
///////////////////////////////////////////////////////
// In case of multi right hand side
///////////////////////////////////////////////////////
......@@ -98,4 +134,48 @@ struct DirectInteractionComputer<LENNARD_JONES_POTENTIAL, NVALS>
}
};
/*! Specialization for ID_OVER_R potential */
template <int NVALS>
struct DirectInteractionComputer<ID_OVER_R, NVALS>
{
template <typename ContainerClass>
static void P2P( ContainerClass* const FRestrict TargetParticles,
ContainerClass* const NeighborSourceParticles[27]){
for(int idxRhs = 0 ; idxRhs < NVALS ; ++idxRhs){
FP2P::FullMutualIOR(TargetParticles,NeighborSourceParticles,14);
}
}
template <typename ContainerClass>
static void P2PRemote( ContainerClass* const FRestrict inTargets,
ContainerClass* const inNeighbors[27],
const int inSize){
for(int idxRhs = 0 ; idxRhs < NVALS ; ++idxRhs){
FP2P::FullRemoteIOR(inTargets,inNeighbors,inSize);
}
}
};
/*! Specialization for GradGradR potential */
template <int NVALS>
struct DirectInteractionComputer<R_IJ, NVALS>
{
template <typename ContainerClass>
static void P2P( ContainerClass* const FRestrict TargetParticles,
ContainerClass* const NeighborSourceParticles[27]){
for(int idxRhs = 0 ; idxRhs < NVALS ; ++idxRhs){
FP2P::FullMutualRIJ(TargetParticles,NeighborSourceParticles,14);
}
}
template <typename ContainerClass>
static void P2PRemote( ContainerClass* const FRestrict inTargets,
ContainerClass* const inNeighbors[27],
const int inSize){
for(int idxRhs = 0 ; idxRhs < NVALS ; ++idxRhs){
FP2P::FullRemoteRIJ(inTargets,inNeighbors,inSize);
}
}
};
#endif // FINTERPP2PKERNELS_HPP
This diff is collapsed.
......@@ -45,7 +45,7 @@ class FAbstractUnifKernel : public FAbstractKernels< CellClass, ContainerClass>
{
protected:
enum {nnodes = TensorTraits<ORDER>::nnodes};
typedef FUnifInterpolator<ORDER> InterpolatorClass;
typedef FUnifInterpolator<ORDER,MatrixKernelClass> InterpolatorClass;
/// Needed for P2M, M2M, L2L and L2P operators
const FSmartPointer<InterpolatorClass,FSmartPointerMemory> Interpolator;
......
......@@ -42,27 +42,27 @@
*
* @param NVALS is the number of right hand side.
*/
template <int ORDER, int NVALS = 1>
template <int ORDER, int NMUL = 1, int NLOC = 1>
class FUnifCell : public FExtendMortonIndex, public FExtendCoordinate
{
static const int VectorSize = TensorTraits<ORDER>::nnodes;
static const int TransformedVectorSize = (2*ORDER-1)*(2*ORDER-1)*(2*ORDER-1);
FReal multipole_exp[NVALS * VectorSize]; //< Multipole expansion
FReal local_exp[NVALS * VectorSize]; //< Local expansion
FReal multipole_exp[NMUL * VectorSize]; //< Multipole expansion
FReal local_exp[NLOC * VectorSize]; //< Local expansion
// PB: Store multipole and local expansion in Fourier space
FComplexe transformed_multipole_exp[NVALS * TransformedVectorSize];
FComplexe transformed_local_exp[NVALS * TransformedVectorSize];
FComplexe transformed_multipole_exp[NMUL * TransformedVectorSize];
FComplexe transformed_local_exp[NLOC * TransformedVectorSize];
public:
FUnifCell(){
memset(multipole_exp, 0, sizeof(FReal) * NVALS * VectorSize);
memset(local_exp, 0, sizeof(FReal) * NVALS * VectorSize);
memset(multipole_exp, 0, sizeof(FReal) * NMUL * VectorSize);
memset(local_exp, 0, sizeof(FReal) * NLOC * VectorSize);
memset(transformed_multipole_exp, 0,
sizeof(FComplexe) * NVALS * TransformedVectorSize);
sizeof(FComplexe) * NMUL * TransformedVectorSize);
memset(transformed_local_exp, 0,
sizeof(FComplexe) * NVALS * TransformedVectorSize);
sizeof(FComplexe) * NLOC * TransformedVectorSize);
}
~FUnifCell() {}
......@@ -92,12 +92,12 @@ public:
/** Make it like the begining */
void resetToInitialState(){
memset(multipole_exp, 0, sizeof(FReal) * NVALS * VectorSize);
memset(local_exp, 0, sizeof(FReal) * NVALS * VectorSize);
memset(multipole_exp, 0, sizeof(FReal) * NMUL * VectorSize);
memset(local_exp, 0, sizeof(FReal) * NLOC * VectorSize);
memset(transformed_multipole_exp, 0,
sizeof(FComplexe) * NVALS * TransformedVectorSize);
sizeof(FComplexe) * NMUL * TransformedVectorSize);
memset(transformed_local_exp, 0,
sizeof(FComplexe) * NVALS * TransformedVectorSize);
sizeof(FComplexe) * NLOC * TransformedVectorSize);
}
/** Get Transformed Multipole */
......@@ -120,21 +120,21 @@ public:
};
template <int ORDER, int NVALS = 1>
class FTypedUnifCell : public FUnifCell<ORDER,NVALS>, public FExtendCellType {
template <int ORDER, int NMUL = 1, int NLOC = 1>
class FTypedUnifCell : public FUnifCell<ORDER,NMUL,NLOC>, public FExtendCellType {
public:
template <class BufferWriterClass>
void save(BufferWriterClass& buffer) const{
FUnifCell<ORDER,NVALS>::save(buffer);
FUnifCell<ORDER,NMUL,NLOC>::save(buffer);
FExtendCellType::save(buffer);
}
template <class BufferReaderClass>
void restore(BufferReaderClass& buffer){
FUnifCell<ORDER,NVALS>::restore(buffer);
FUnifCell<ORDER,NMUL,NLOC>::restore(buffer);
FExtendCellType::restore(buffer);
}
void resetToInitialState(){
FUnifCell<ORDER,NVALS>::resetToInitialState();
FUnifCell<ORDER,NMUL,NLOC>::resetToInitialState();
FExtendCellType::resetToInitialState();
}
};
......
This diff is collapsed.
......@@ -54,20 +54,22 @@ class FUnifM2LHandler : FNoCopyable
{
enum {order = ORDER,
nnodes = TensorTraits<ORDER>::nnodes,
ninteractions = 316}; // 7^3 - 3^3 (max num cells in far-field)
ninteractions = 316, // 7^3 - 3^3 (max num cells in far-field)
rc = (2*ORDER-1)*(2*ORDER-1)*(2*ORDER-1)};
const MatrixKernelClass MatrixKernel;
FComplexe *FC;
unsigned int rc;
// for real valued kernel only n/2+1 complex values are stored
// after performing the DFT (the rest is deduced by conjugation)
unsigned int opt_rc;
typedef FUnifTensor<ORDER> TensorType;
unsigned int node_diff[nnodes*nnodes];
// FDft Dft; // Direct Discrete Fourier Transformator
FFft Dft; // Fast Discrete Fourier Transformator
FFft<1> Dft; // Fast Discrete Fourier Transformator
static const std::string getFileName()
{
......@@ -81,17 +83,12 @@ class FUnifM2LHandler : FNoCopyable
public:
FUnifM2LHandler()
: MatrixKernel(), FC(NULL),
rc((2*ORDER-1)*(2*ORDER-1)*(2*ORDER-1)),
: MatrixKernel(), FC(NULL),
opt_rc(rc/2+1),
Dft(rc) // initialize Discrete Fourier Transformator
{
// initialize root node ids
TensorType::setNodeIdsDiff(node_diff);
// for real valued kernel only n/2+1 complex values are stored
// after performing the DFT (the rest is deduced by conjugation)
}
~FUnifM2LHandler()
......@@ -271,22 +268,19 @@ FUnifM2LHandler<ORDER, MatrixKernelClass>::Compute(FComplexe* &FC)
_C = new FReal [rc];
_FC = new FComplexe [rc * ninteractions];
// initialize root node ids pairs
unsigned int node_ids_pairs[rc][2];
TensorType::setNodeIdsPairs(node_ids_pairs);
// init Discrete Fourier Transformator
// FDft Dft(rc);
FFft Dft(rc);
FFft<1> Dft(rc);
// get first column of K via permutation
unsigned int perm[rc];
for(unsigned int p=0; p<rc; ++p){
// permutation to order WHILE computing the entire 1st row
if(p<rc-1) perm[p]=p+1;
else perm[p]=p+1-rc;
// std::cout << "perm["<< p << "]="<< perm[p] << std::endl;
}
TensorType::setStoragePermutation(perm);
unsigned int counter = 0;
unsigned int li,lj, mi,mj, ni,nj;
unsigned int idi, idj, ido;
for (int i=-3; i<=3; ++i) {
for (int j=-3; j<=3; ++j) {
for (int k=-3; k<=3; ++k) {
......@@ -295,23 +289,11 @@ FUnifM2LHandler<ORDER, MatrixKernelClass>::Compute(FComplexe* &FC)
const FPoint cy(FReal(2.*i), FReal(2.*j), FReal(2.*k));
FUnifTensor<order>::setRoots(cy, FReal(2.), Y);
// evaluate m2l operator
ido=0;
unsigned int ido=0;
for(unsigned int l=0; l<2*order-1; ++l)
for(unsigned int m=0; m<2*order-1; ++m)
for(unsigned int n=0; n<2*order-1; ++n){
// l=0:(2*order-1) => li-lj=-(order-1):(order-1)
// Convention:
// lj=order-1 & li=0:order-1 => li-lj=1-order:0
// lj=1 & li=0:order-1 => li-lj=1:order-1
if(l<order-1) lj=order-1; else lj=0;
if(m<order-1) mj=order-1; else mj=0;
if(n<order-1) nj=order-1; else nj=0;
li=(l-(order-1))+lj; mi=(m-(order-1))+mj; ni=(n-(order-1))+nj;
// deduce corresponding index of K[nnodes x nnodes]
idi=li*order*order + mi*order + ni;
idj=lj*order*order + mj*order + nj;
// store value at current position in C
// use permutation if DFT is used because
// the storage of the first column is required
......@@ -319,7 +301,8 @@ FUnifM2LHandler<ORDER, MatrixKernelClass>::Compute(FComplexe* &FC)
// i.e. C[rc-1] C[0] C[1] .. C[rc-2] < RIGHT!
// _C[counter*rc + ido]
_C[perm[ido]]
= MatrixKernel.evaluate(X[idi], Y[idj]);
= MatrixKernel.evaluate(X[node_ids_pairs[ido][0]],
Y[node_ids_pairs[ido][1]]);
ido++;
}
......
......@@ -40,7 +40,8 @@
template <int ORDER>
class FUnifTensor : public FInterpTensor<ORDER,FUnifRoots<ORDER>>
{
enum {nnodes = TensorTraits<ORDER>::nnodes};
enum {nnodes = TensorTraits<ORDER>::nnodes,
rc = (2*ORDER-1)*(2*ORDER-1)*(2*ORDER-1)};
typedef FUnifRoots<ORDER> BasisType;
typedef FInterpTensor<ORDER,BasisType> ParentTensor;
......@@ -70,6 +71,68 @@ class FUnifTensor : public FInterpTensor<ORDER,FUnifRoots<ORDER>>
}
}
/**
* Compute roots index pairs for each of the (2\ell-1)^3 pairs
*
* @param[out] NodeIdsPairs diff of ids of coordinates of interpolation nodes
*/
static
void setNodeIdsPairs(unsigned int NodeIdsPairs[rc][2])
{
unsigned int li,lj,mi,mj,ni,nj;
unsigned int ido=0;
// If the diff i-j between indices is <0 (i.e. positive counter < ORDER-1)
// then source index is ORDER-1
// else it is the first
// we deduce the target index by simply considering i-j=counter-(ORDER-1)
for(unsigned int l=0; l<2*ORDER-1; ++l){
// l=0:(2*order-1) => li-lj=-(order-1):(order-1)
// Convention:
// lj=order-1 & li=0:order-1 => li-lj=1-order:0
// lj=1 & li=0:order-1 => li-lj=1:order-1
if(l<ORDER-1) lj=ORDER-1; else lj=0;
li=(l-(ORDER-1))+lj;
for(unsigned int m=0; m<2*ORDER-1; ++m){
if(m<ORDER-1) mj=ORDER-1; else mj=0;
mi=(m-(ORDER-1))+mj;
for(unsigned int n=0; n<2*ORDER-1; ++n){
if(n<ORDER-1) nj=ORDER-1; else nj=0;
ni=(n-(ORDER-1))+nj;
NodeIdsPairs[ido][0]=li*ORDER*ORDER + mi*ORDER + ni;
NodeIdsPairs[ido][1]=lj*ORDER*ORDER + mj*ORDER + nj;
ido++;
}
}
}
}
/**
* Compute permutation vector used to reorder first row of circulant matrix
*
* @param[out] perm
*/
static
void setStoragePermutation(unsigned int perm[rc])
{
for(unsigned int p=0; p<rc; ++p){
// permutation to order WHILE computing the entire 1st row
if(p<rc-1) perm[p]=p+1;
else perm[p]=p+1-rc;
// std::cout << "perm["<< p << "]="<< perm[p] << std::endl;
}
}
};
......
// ===================================================================================
// 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 FUNIFTENSORIALKERNEL_HPP
#define FUNIFTENSORIALKERNEL_HPP
#include "../../Utils/FGlobal.hpp"
#include "../../Utils/FTrace.hpp"
#include "../../Utils/FSmartPointer.hpp"
#include "./FAbstractUnifKernel.hpp"
#include "./FUnifTensorialM2LHandler.hpp"
class FTreeCoordinate;
/**
* @author Pierre Blanchard (pierre.blanchard@inria.fr)
* @class FUnifTensorialKernel
* @brief
* Please read the license
*
* This kernels implement the Lagrange interpolation based FMM operators. It
* implements all interfaces (P2P,P2M,M2M,M2L,L2L,L2P) which are required by
* the FFmmAlgorithm and FFmmAlgorithmThread.
*
* PB: 3 IMPORTANT remarks !!!
*
* 1) Handling tensorial kernels (NK,NRHS,NLHS) and having multiple rhs (NVALS)
* should be considered as 2 separate things and could even be combined (TODO).
*
* 2) The present tensorial version is the most naive one. All tensorial aspects
* are handled in the kernel. A more optimal version would be to consider looping
* over nRhs/nLhs inside Interpolator::applyP2M/L2P in order to avoid extra
* evaluation of the interpolating polynomials. When it comes to applying M2L it is
* not much faster to loop over NK inside applyM2L.
* 2-bis) The evaluation of the kernel matrix (see M2LHandler) should be done at once
* instead of compo-by-compo (TODO).
*
* 3) We currently use multiple 1D FFT instead of multidim FFT.
* TODO switch to multidim if relevant in considered range of size
* (see testFFTW and testFFTWMultidim).
*
* @tparam CellClass Type of cell
* @tparam ContainerClass Type of container to store particles
* @tparam MatrixKernelClass Type of matrix kernel function
* @tparam ORDER Lagrange interpolation order
*/
template < class CellClass, class ContainerClass, class MatrixKernelClass, int ORDER, int NVALS = 1>
class FUnifTensorialKernel
: public FAbstractUnifKernel< CellClass, ContainerClass, MatrixKernelClass, ORDER, NVALS>
{
enum {nK = MatrixKernelClass::NK,
nRhs = MatrixKernelClass::NRHS,
nLhs = MatrixKernelClass::NLHS};
// private types
typedef FUnifTensorialM2LHandler<ORDER,MatrixKernelClass> M2LHandlerClass;
// using from
typedef FAbstractUnifKernel< CellClass, ContainerClass, MatrixKernelClass, ORDER, NVALS>
AbstractBaseClass;
/// Needed for M2L operator
FSmartPointer< M2LHandlerClass,FSmartPointerMemory> M2LHandler;
public:
/**
* The constructor initializes all constant attributes and it reads the
* precomputed and compressed M2L operators from a binary file (an
* runtime_error is thrown if the required file is not valid).
*/
FUnifTensorialKernel(const int inTreeHeight,
const FPoint& inBoxCenter,
const FReal inBoxWidth)
: FAbstractUnifKernel< CellClass, ContainerClass, MatrixKernelClass, ORDER, NVALS>(inTreeHeight,
inBoxCenter,
inBoxWidth),
M2LHandler(new M2LHandlerClass())
{
// read precomputed compressed m2l operators from binary file
//M2LHandler->ReadFromBinaryFileAndSet(); // PB: TODO?
M2LHandler->ComputeAndSet();
}
void P2M(CellClass* const LeafCell,
const ContainerClass* const SourceParticles)
{
const FPoint LeafCellCenter(AbstractBaseClass::getLeafCellCenter(LeafCell->getCoordinate()));
// for(int idxV = 0 ; idxV < NVALS ; ++idxV){
for(int idxRhs = 0 ; idxRhs < nRhs ; ++idxRhs){
// 1) apply Sy
AbstractBaseClass::Interpolator->applyP2M(LeafCellCenter, AbstractBaseClass::BoxWidthLeaf,
LeafCell->getMultipole(idxRhs), SourceParticles);
// 2) apply Discrete Fourier Transform
M2LHandler->applyZeroPaddingAndDFT(LeafCell->getMultipole(idxRhs),
LeafCell->getTransformedMultipole(idxRhs));
}
// }// NVALS
}
void M2M(CellClass* const FRestrict ParentCell,
const CellClass*const FRestrict *const FRestrict ChildCells,
const int /*TreeLevel*/)
{
// for(int idxV = 0 ; idxV < NVALS ; ++idxV){
for(int idxRhs = 0 ; idxRhs < nRhs ; ++idxRhs){
// 1) apply Sy
FBlas::scal(AbstractBaseClass::nnodes, FReal(0.), ParentCell->getMultipole(idxRhs));
for (unsigned int ChildIndex=0; ChildIndex < 8; ++ChildIndex){
if (ChildCells[ChildIndex]){
AbstractBaseClass::Interpolator->applyM2M(ChildIndex, ChildCells[ChildIndex]->getMultipole(idxRhs),
ParentCell->getMultipole(idxRhs));
}
}
// 2) Apply Discete Fourier Transform
M2LHandler->applyZeroPaddingAndDFT(ParentCell->getMultipole(idxRhs),
ParentCell->getTransformedMultipole(idxRhs));
}
// }// NVALS
}
void M2L(CellClass* const FRestrict TargetCell,
const CellClass* SourceCells[343],
const int /*NumSourceCells*/,
const int TreeLevel)
{
// for(int idxV = 0 ; idxV < NVALS ; ++idxV){
for (unsigned int idxLhs=0; idxLhs < nLhs; ++idxLhs)
for (unsigned int idxRhs=0; idxRhs < nRhs; ++idxRhs){
unsigned int idxK = idxLhs*nRhs + idxRhs;
FComplexe *const TransformedLocalExpansion = TargetCell->getTransformedLocal(idxLhs);
const FReal CellWidth(AbstractBaseClass::BoxWidth / FReal(FMath::pow(2, TreeLevel)));
for (int idx=0; idx<343; ++idx){
if (SourceCells[idx]){
M2LHandler->applyFC(idx, CellWidth,
SourceCells[idx]->getTransformedMultipole(idxRhs),
TransformedLocalExpansion,idxK);
}
}
}
// }// NVALS
}
void L2L(const CellClass* const FRestrict ParentCell,
CellClass* FRestrict *const FRestrict ChildCells,
const int /*TreeLevel*/)
{
// for(int idxV = 0 ; idxV < NVALS ; ++idxV){
for(int idxLhs = 0 ; idxLhs < nLhs ; ++idxLhs){
// 1) Apply Inverse Discete Fourier Transform
M2LHandler->unapplyZeroPaddingAndDFT(ParentCell->getTransformedLocal(idxLhs),
const_cast<CellClass*>(ParentCell)->getLocal(idxLhs));
// 2) apply Sx
for (unsigned int ChildIndex=0; ChildIndex < 8; ++ChildIndex){
if (ChildCells[ChildIndex]){
AbstractBaseClass::Interpolator->applyL2L(ChildIndex, ParentCell->getLocal(idxLhs), ChildCells[ChildIndex]->getLocal(idxLhs));
}
}
}