FFmmAlgorithmPeriodic.hpp 22.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
// ===================================================================================
// 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".
// ===================================================================================
16 17
#ifndef FFMMALGORITHMPERIODIC_HPP
#define FFMMALGORITHMPERIODIC_HPP
18

19 20

#include "../Utils/FGlobal.hpp"
21
#include "../Utils/FGlobalPeriodic.hpp"
22
#include "../Utils/FAssert.hpp"
23
#include "../Utils/FLog.hpp"
24

25
#include "../Utils/FTic.hpp"
26
#include "../Utils/FMemUtils.hpp"
27 28 29 30

#include "../Containers/FOctree.hpp"
#include "../Containers/FVector.hpp"

31
#include "FCoreCommon.hpp"
32 33 34 35 36 37 38

/**
* @author Berenger Bramas (berenger.bramas@inria.fr)
* @class FFmmAlgorithmPeriodic
* @brief
* Please read the license
*
39
* This class is a basic FMM algorithm with periodic behavior
40 41
* It just iterates on a tree and call the kernels with good arguments.
*
COULAUD Olivier's avatar
COULAUD Olivier committed
42
* Of course this class does not deallocate pointer given in arguments.
43
*/
44
template<class OctreeClass, class CellClass, class ContainerClass, class KernelClass, class LeafClass>
45
class FFmmAlgorithmPeriodic : public FAbstractAlgorithm{
46

47 48
    OctreeClass* const tree;        //< The octree to work on
    KernelClass* kernels;           //< The kernels
49

COULAUD Olivier's avatar
COULAUD Olivier committed
50
    const int OctreeHeight;         //< The height of the octree (real height)
51 52 53
    const int nbLevelsAboveRoot;    //< The nb of level the user ask to go above the tree (>= -1)
    const int offsetRealTree;       //< nbLevelsAboveRoot GetFackLevel

54 55 56 57 58 59

public:
    /** The constructor need the octree and the kernels used for computation
      * @param inTree the octree to work on
      * @param inKernels the kernels to call
      * An assert is launched if one of the arguments is null
COULAUD Olivier's avatar
COULAUD Olivier committed
60
      * @param inUpperLevel this parameter defines the behavior of the periodicity refer to the main doc
61
      *
62
      */
BRAMAS Berenger's avatar
BRAMAS Berenger committed
63
    FFmmAlgorithmPeriodic(OctreeClass* const inTree, const int inUpperLevel = 0)
64
        : tree(inTree) , kernels(nullptr), OctreeHeight(tree->getHeight()),
BRAMAS Berenger's avatar
BRAMAS Berenger committed
65
          nbLevelsAboveRoot(inUpperLevel), offsetRealTree(inUpperLevel + 3) {
66

67 68
        FAssertLF(tree, "tree cannot be null");
        FAssertLF(-1 <= inUpperLevel, "inUpperLevel cannot be < -1");
69

70
        FLOG(FLog::Controller << "FFmmAlgorithmPeriodic\n");
71 72 73 74 75 76
    }

    /** Default destructor */
    virtual ~FFmmAlgorithmPeriodic(){
    }

77 78 79 80
    void setKernel(KernelClass*const inKernel){
        kernels = inKernel;
    }

81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146



    long long int theoricalRepetition() const {
        if( nbLevelsAboveRoot == -1 ){
            // we know it is 3 (-1;+1)
            return 3;
        }
        // Else we find the repetition in one dir and double it
        const long long int oneDirectionRepetition = (1<<(nbLevelsAboveRoot+2)); // 2^nbLevelsAboveRoot in each dim
        const long long int fullRepetition = 2 * oneDirectionRepetition;
        return fullRepetition;
    }


    void repetitionsIntervals(FTreeCoordinate*const min, FTreeCoordinate*const max) const {
        if( nbLevelsAboveRoot == -1 ){
            // We know it is (-1;1)
            min->setPosition(-1,-1,-1);
            max->setPosition(1,1,1);
        }
        else{
            const int halfRepeated = int(theoricalRepetition()/2);
            min->setPosition(-halfRepeated,-halfRepeated,-halfRepeated);
            // if we repeat the box 8 times, we go from [-4 to 3]
            max->setPosition(halfRepeated-1,halfRepeated-1,halfRepeated-1);
        }
    }


    FReal extendedBoxWidth() const {
        // The simulation box is repeated is repeated 4 times if nbLevelsAboveRoot==-1
        // And then it doubles by two
        return tree->getBoxWidth() * FReal(1<<(nbLevelsAboveRoot+3));
    }

    /** This function has to be used to init the kernel with correct args
      * it return the box cneter seen from a kernel point of view from the periodicity the user ask for
      * this is computed using the originalBoxWidth and originalBoxCenter given in parameter
      * @param originalBoxCenter the real system center
      * @param originalBoxWidth the real system size
      * @return the center the kernel should use
      */
    FPoint extendedBoxCenter() const {
        const FReal originalBoxWidth     = tree->getBoxWidth();
        const FReal originalBoxWidthDiv2 = originalBoxWidth/2.0;
        const FPoint originalBoxCenter   = tree->getBoxCenter();

        const FReal offset = extendedBoxWidth()/FReal(2.0);
        return FPoint( originalBoxCenter.getX() - originalBoxWidthDiv2 + offset,
                       originalBoxCenter.getY() - originalBoxWidthDiv2 + offset,
                       originalBoxCenter.getZ() - originalBoxWidthDiv2 + offset);
    }

    /** This function has to be used to init the kernel with correct args
      * it return the tree heigh seen from a kernel point of view from the periodicity the user ask for
      * this is computed using the originalTreeHeight given in parameter
      * @param originalTreeHeight the real tree heigh
      * @return the heigh the kernel should use
      */
    int extendedTreeHeight() const {
        // The real height
        return OctreeHeight + offsetRealTree;
    }

protected:
147 148 149 150
    /**
      * To execute the fmm algorithm
      * Call this function to run the complete algorithm
      */
151
    void executeCore(const unsigned operationsToProceed) override {
152
        FAssertLF(kernels, "kernels cannot be null");
153

154
        if(operationsToProceed & FFmmP2M) bottomPass();
155

156
        if(operationsToProceed & FFmmM2M) upwardPass();
157

158 159 160 161 162
        if(operationsToProceed & FFmmM2L){
            transferPass();
            // before downward pass we have to perform the periodicity
            processPeriodicLevels();
        }
163

164
        if(operationsToProceed & FFmmL2L) downardPass();
165

166
        if((operationsToProceed & FFmmP2P) || (operationsToProceed & FFmmL2P)) directPass((operationsToProceed & FFmmP2P),(operationsToProceed & FFmmL2P));
167 168
    }

169

170 171 172 173 174 175
    /////////////////////////////////////////////////////////////////////////////
    // P2M
    /////////////////////////////////////////////////////////////////////////////

    /** P2M */
    void bottomPass(){
176
        FLOG( FLog::Controller.write("\tStart Bottom Pass\n").write(FLog::Flush) );
177 178
        FLOG(FTic counterTime);
        FLOG(FTic computationCounter);
179 180 181 182 183 184 185 186

        typename OctreeClass::Iterator octreeIterator(tree);

        // Iterate on leafs
        octreeIterator.gotoBottomLeft();
        do{
            // We need the current cell that represent the leaf
            // and the list of particles
187
            FLOG(computationCounter.tic());
188
            kernels->P2M( octreeIterator.getCurrentCell() , octreeIterator.getCurrentListSrc());
189
            FLOG(computationCounter.tac());
190 191
        } while(octreeIterator.moveRight());

192 193
        FLOG( FLog::Controller << "\tFinished (@Bottom Pass (P2M) = "  << counterTime.tacAndElapsed() << "s)\n" );
        FLOG( FLog::Controller << "\t\t Computation : " << computationCounter.cumulated() << " s\n" );
194 195 196 197 198 199 200 201
    }

    /////////////////////////////////////////////////////////////////////////////
    // Upward
    /////////////////////////////////////////////////////////////////////////////

    /** M2M */
    void upwardPass(){
202
        FLOG( FLog::Controller.write("\tStart Upward Pass\n").write(FLog::Flush); );
203 204
        FLOG(FTic counterTime);
        FLOG(FTic computationCounter);
205 206 207 208 209 210 211 212 213 214

        // Start from leal level - 1
        typename OctreeClass::Iterator octreeIterator(tree);
        octreeIterator.gotoBottomLeft();
        octreeIterator.moveUp();

        typename OctreeClass::Iterator avoidGotoLeftIterator(octreeIterator);

        // for each levels
        for(int idxLevel = OctreeHeight - 2 ; idxLevel > 0 ; --idxLevel ){
215
            FLOG(FTic counterTimeLevel);
216
            const int fackLevel = idxLevel + offsetRealTree;
217 218 219 220
            // for each cells
            do{
                // We need the current cell and the child
                // child is an array (of 8 child) that may be null
221
                FLOG(computationCounter.tic());
222
                kernels->M2M( octreeIterator.getCurrentCell() , octreeIterator.getCurrentChild(), fackLevel);
223
                FLOG(computationCounter.tac());
224 225 226 227
            } while(octreeIterator.moveRight());

            avoidGotoLeftIterator.moveUp();
            octreeIterator = avoidGotoLeftIterator;// equal octreeIterator.moveUp(); octreeIterator.gotoLeft();
228
            FLOG( FLog::Controller << "\t\t>> Level " << idxLevel << "(" << fackLevel << ") = "  << counterTimeLevel.tacAndElapsed() << "s\n" );
229 230 231
        }


232 233
        FLOG( FLog::Controller << "\tFinished (@Upward Pass (M2M) = "  << counterTime.tacAndElapsed() << "s)\n" );
        FLOG( FLog::Controller << "\t\t Computation : " << computationCounter.cumulated() << " s\n" );
234 235 236
    }

    /////////////////////////////////////////////////////////////////////////////
237
    // Transfer
238 239 240
    /////////////////////////////////////////////////////////////////////////////

    /** M2L L2L */
241
    void transferPass(){
242
        FLOG( FLog::Controller.write("\tStart Downward Pass (M2L)\n").write(FLog::Flush); );
243 244
        FLOG(FTic counterTime);
        FLOG(FTic computationCounter);
245 246 247 248

        typename OctreeClass::Iterator octreeIterator(tree);
        typename OctreeClass::Iterator avoidGotoLeftIterator(octreeIterator);

249
        const CellClass* neighbors[343];
250 251 252

        // for each levels
        for(int idxLevel = 1 ; idxLevel < OctreeHeight ; ++idxLevel ){
253
            FLOG(FTic counterTimeLevel);
254
            const int fackLevel = idxLevel + offsetRealTree;
255 256
            // for each cells
            do{
BRAMAS Berenger's avatar
BRAMAS Berenger committed
257
                const int counter = tree->getPeriodicInteractionNeighbors(neighbors, octreeIterator.getCurrentGlobalCoordinate(), idxLevel, AllDirs);
258
                FLOG(computationCounter.tic());
259
                if(counter) kernels->M2L( octreeIterator.getCurrentCell() , neighbors, counter, fackLevel);
260
                FLOG(computationCounter.tac());
261 262 263
            } while(octreeIterator.moveRight());
            avoidGotoLeftIterator.moveDown();
            octreeIterator = avoidGotoLeftIterator;
264

265
            FLOG(computationCounter.tic());
266
            kernels->finishedLevelM2L(fackLevel);
267
            FLOG(computationCounter.tac());
268
            FLOG( FLog::Controller << "\t\t>> Level " << idxLevel << "(" << fackLevel << ") = "  << counterTimeLevel.tacAndElapsed() << "s\n" );
269
        }
270 271
        FLOG( FLog::Controller << "\tFinished (@Downward Pass (M2L) = "  << counterTime.tacAndElapsed() << "s)\n" );
        FLOG( FLog::Controller << "\t\t Computation : " << computationCounter.cumulated() << " s\n" );
272
    }
273

274 275 276 277 278 279
    /////////////////////////////////////////////////////////////////////////////
    // Downward
    /////////////////////////////////////////////////////////////////////////////


    void downardPass(){ // second L2L
280
        FLOG( FLog::Controller.write("\tStart Downward Pass (L2L)\n").write(FLog::Flush); );
281 282
        FLOG(FTic counterTime);
        FLOG(FTic computationCounter );
283 284 285

        typename OctreeClass::Iterator octreeIterator(tree);
        typename OctreeClass::Iterator avoidGotoLeftIterator(octreeIterator);
286

287 288 289
        const int heightMinusOne = OctreeHeight - 1;
        // for each levels exepted leaf level
        for(int idxLevel = 1 ; idxLevel < heightMinusOne ; ++idxLevel ){
290
            FLOG(FTic counterTimeLevel);
291 292
            const int fackLevel = idxLevel + offsetRealTree;

293 294
            // for each cells
            do{
295
                FLOG(computationCounter.tic());
296
                kernels->L2L( octreeIterator.getCurrentCell() , octreeIterator.getCurrentChild(), fackLevel);
297
                FLOG(computationCounter.tac());
298 299 300 301
            } while(octreeIterator.moveRight());

            avoidGotoLeftIterator.moveDown();
            octreeIterator = avoidGotoLeftIterator;
302
            FLOG( FLog::Controller << "\t\t>> Level " << idxLevel << "(" << fackLevel << ") = "  << counterTimeLevel.tacAndElapsed() << "s\n" );
303 304
        }

305 306
        FLOG( FLog::Controller << "\tFinished (@Downward Pass (L2L) = "  << counterTime.tacAndElapsed() << "s)\n" );
        FLOG( FLog::Controller << "\t\t Computation : " << computationCounter.cumulated() << " s\n" );
307

308 309 310 311 312 313 314 315

    }

    /////////////////////////////////////////////////////////////////////////////
    // Direct
    /////////////////////////////////////////////////////////////////////////////

    /** P2P */
316
    void directPass(const bool p2pEnabled, const bool l2pEnabled){
317
        FLOG( FLog::Controller.write("\tStart Direct Pass\n").write(FLog::Flush); );
318 319 320
        FLOG(FTic counterTime);
        FLOG(FTic computationCounterL2P);
        FLOG(FTic computationCounterP2P);
321 322

        const int heightMinusOne = OctreeHeight - 1;
323
        const FReal boxWidth = tree->getBoxWidth();
324 325 326 327

        typename OctreeClass::Iterator octreeIterator(tree);
        octreeIterator.gotoBottomLeft();
        // There is a maximum of 26 neighbors
berenger-bramas's avatar
berenger-bramas committed
328
        ContainerClass* neighbors[27];
329
        FTreeCoordinate offsets[27];
330
        bool hasPeriodicLeaves;
331 332
        // for each leafs
        do{
333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363
            if(l2pEnabled){
                FLOG(computationCounterL2P.tic());
                kernels->L2P(octreeIterator.getCurrentCell(), octreeIterator.getCurrentListTargets());
                FLOG(computationCounterL2P.tac());
            }
            if(p2pEnabled){
                // need the current particles and neighbors particles
                const FTreeCoordinate centerOfLeaf = octreeIterator.getCurrentGlobalCoordinate();
                const int counter = tree->getPeriodicLeafsNeighbors( neighbors, offsets, &hasPeriodicLeaves, centerOfLeaf, heightMinusOne, AllDirs);
                int periodicNeighborsCounter = 0;

                if(hasPeriodicLeaves){
                    ContainerClass* periodicNeighbors[27];
                    memset(periodicNeighbors, 0, 27 * sizeof(ContainerClass*));

                    for(int idxNeig = 0 ; idxNeig < 27 ; ++idxNeig){
                        if( neighbors[idxNeig] && !offsets[idxNeig].equals(0,0,0) ){
                            // Put periodic neighbors into other array
                            periodicNeighbors[idxNeig] = neighbors[idxNeig];
                            neighbors[idxNeig] = nullptr;
                            ++periodicNeighborsCounter;

                            FReal*const positionsX = periodicNeighbors[idxNeig]->getWPositions()[0];
                            FReal*const positionsY = periodicNeighbors[idxNeig]->getWPositions()[1];
                            FReal*const positionsZ = periodicNeighbors[idxNeig]->getWPositions()[2];

                            for(int idxPart = 0; idxPart < periodicNeighbors[idxNeig]->getNbParticles() ; ++idxPart){
                                positionsX[idxPart] += boxWidth * FReal(offsets[idxNeig].getX());
                                positionsY[idxPart] += boxWidth * FReal(offsets[idxNeig].getY());
                                positionsZ[idxPart] += boxWidth * FReal(offsets[idxNeig].getZ());
                            }
364 365
                        }
                    }
366

367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382
                    FLOG(computationCounterP2P.tic());
                    kernels->P2PRemote(octreeIterator.getCurrentGlobalCoordinate(),octreeIterator.getCurrentListTargets(),
                                 octreeIterator.getCurrentListSrc(), periodicNeighbors, periodicNeighborsCounter);
                    FLOG(computationCounterP2P.tac());

                    for(int idxNeig = 0 ; idxNeig < 27 ; ++idxNeig){
                        if( periodicNeighbors[idxNeig] ){
                            FReal*const positionsX = periodicNeighbors[idxNeig]->getWPositions()[0];
                            FReal*const positionsY = periodicNeighbors[idxNeig]->getWPositions()[1];
                            FReal*const positionsZ = periodicNeighbors[idxNeig]->getWPositions()[2];

                            for(int idxPart = 0; idxPart < periodicNeighbors[idxNeig]->getNbParticles() ; ++idxPart){
                                positionsX[idxPart] -= boxWidth * FReal(offsets[idxNeig].getX());
                                positionsY[idxPart] -= boxWidth * FReal(offsets[idxNeig].getY());
                                positionsZ[idxPart] -= boxWidth * FReal(offsets[idxNeig].getZ());
                            }
383 384 385
                        }
                    }
                }
386

387 388 389 390 391
                FLOG(computationCounterP2P.tic());
                kernels->P2P(octreeIterator.getCurrentGlobalCoordinate(),octreeIterator.getCurrentListTargets(),
                             octreeIterator.getCurrentListSrc(), neighbors, counter - periodicNeighborsCounter);
                FLOG(computationCounterP2P.tac());
            }
392 393 394
        } while(octreeIterator.moveRight());


395 396 397
        FLOG( FLog::Controller << "\tFinished (@Direct Pass (L2P + P2P) = "  << counterTime.tacAndElapsed() << "s)\n" );
        FLOG( FLog::Controller << "\t\t Computation L2P : " << computationCounterL2P.cumulated() << " s\n" );
        FLOG( FLog::Controller << "\t\t Computation P2P : " << computationCounterP2P.cumulated() << " s\n" );
398 399 400 401 402 403 404

    }

    /////////////////////////////////////////////////////////////////////////////
    // Periodic levels = levels <= 0
    /////////////////////////////////////////////////////////////////////////////

405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425
    /** Get the index of a interaction neighbors (for M2L)
      * @param x the x position in the interactions (from -3 to +3)
      * @param y the y position in the interactions (from -3 to +3)
      * @param z the z position in the interactions (from -3 to +3)
      * @return the index (from 0 to 342)
      */
    int neighIndex(const int x, const int y, const int z) const {
        return (((x+3)*7) + (y+3))*7 + (z + 3);
    }

    /** Periodicity Core
      * This function is split in several part:
      * 1 - special case managment
      * There is nothing to do if nbLevelsAboveRoot == -1 and only
      * a M2L if nbLevelsAboveRoot == 0
      * 2 - if nbLevelsAboveRoot > 0
      * First we compute M2M and special M2M if needed for the border
      * Then the M2L by taking into account the periodicity directions
      * Then the border by using the precomputed M2M
      * Finally the L2L
      */
426
    void processPeriodicLevels(){
427
        FLOG( FLog::Controller.write("\tStart Periodic Pass\n").write(FLog::Flush); );
428
        FLOG(FTic counterTime);
429

BRAMAS Berenger's avatar
BRAMAS Berenger committed
430 431 432 433 434 435 436 437
        if( nbLevelsAboveRoot != -1 ){
            // we will use offsetRealTree-1 cells but for simplicity allocate offsetRealTree
            // upperCells[offsetRealTree-1] is root cell
            CellClass*const upperCells = new CellClass[offsetRealTree];
            {
                typename OctreeClass::Iterator octreeIterator(tree);
                octreeIterator.gotoLeft();
                kernels->M2M( &upperCells[offsetRealTree-1], octreeIterator.getCurrentBox(), offsetRealTree);
438
            }
BRAMAS Berenger's avatar
BRAMAS Berenger committed
439 440 441 442 443
            {
                CellClass* virtualChild[8];
                for(int idxLevel = offsetRealTree-1 ; idxLevel > 1  ; --idxLevel){
                    FMemUtils::setall(virtualChild,&upperCells[idxLevel],8);
                    kernels->M2M( &upperCells[idxLevel-1], virtualChild, idxLevel);
444
                }
445
            }
BRAMAS Berenger's avatar
BRAMAS Berenger committed
446
            CellClass*const downerCells = new CellClass[offsetRealTree];
447

BRAMAS Berenger's avatar
BRAMAS Berenger committed
448 449
            {
                const int idxUpperLevel = 2;
450 451 452 453

                const CellClass* neighbors[343];
                memset(neighbors, 0, sizeof(CellClass*) * 343);
                int counter = 0;
454 455 456
                for(int idxX = -2 ; idxX <= 1 ; ++idxX){
                    for(int idxY = -2 ; idxY <= 1 ; ++idxY){
                        for(int idxZ = -2 ; idxZ <= 1 ; ++idxZ){
BRAMAS Berenger's avatar
BRAMAS Berenger committed
457 458 459
                            if( FMath::Abs(idxX) > 1 || FMath::Abs(idxY) > 1 || FMath::Abs(idxZ) > 1){
                                neighbors[neighIndex(idxX,idxY,idxZ)] = &upperCells[idxUpperLevel-1];
                                ++counter;
460
                            }
461 462 463
                        }
                    }
                }
BRAMAS Berenger's avatar
BRAMAS Berenger committed
464 465 466
                // compute M2L
                kernels->M2L( &downerCells[idxUpperLevel-1] , neighbors, counter, idxUpperLevel);
            }
467

BRAMAS Berenger's avatar
BRAMAS Berenger committed
468 469 470 471 472 473 474 475 476 477
            for(int idxUpperLevel = 3 ; idxUpperLevel <= offsetRealTree ; ++idxUpperLevel){
                const CellClass* neighbors[343];
                memset(neighbors, 0, sizeof(CellClass*) * 343);
                int counter = 0;
                for(int idxX = -2 ; idxX <= 3 ; ++idxX){
                    for(int idxY = -2 ; idxY <= 3 ; ++idxY){
                        for(int idxZ = -2 ; idxZ <= 3 ; ++idxZ){
                            if( FMath::Abs(idxX) > 1 || FMath::Abs(idxY) > 1 || FMath::Abs(idxZ) > 1){
                                neighbors[neighIndex(idxX,idxY,idxZ)] = &upperCells[idxUpperLevel-1];
                                ++counter;
478 479 480
                            }
                        }
                    }
481 482
                }

BRAMAS Berenger's avatar
BRAMAS Berenger committed
483 484 485
                // compute M2L
                kernels->M2L( &downerCells[idxUpperLevel-1] , neighbors, counter, idxUpperLevel);
            }
486

BRAMAS Berenger's avatar
BRAMAS Berenger committed
487
            {
488 489
                CellClass* virtualChild[8];
                memset(virtualChild, 0, sizeof(CellClass*) * 8);
BRAMAS Berenger's avatar
BRAMAS Berenger committed
490 491 492 493
                for(int idxLevel = 2 ; idxLevel <= offsetRealTree-1  ; ++idxLevel){
                    virtualChild[0] = &downerCells[idxLevel];
                    kernels->L2L( &downerCells[idxLevel-1], virtualChild, idxLevel);
                }
494
            }
495

BRAMAS Berenger's avatar
BRAMAS Berenger committed
496 497 498 499 500
            // L2L from 0 to level 1
            {
                typename OctreeClass::Iterator octreeIterator(tree);
                octreeIterator.gotoLeft();
                kernels->L2L( &downerCells[offsetRealTree-1], octreeIterator.getCurrentBox(), offsetRealTree);
501 502
            }

BRAMAS Berenger's avatar
BRAMAS Berenger committed
503 504
            delete[] upperCells;
            delete[] downerCells;
505
        }
506

507
        FLOG( FLog::Controller << "\tFinished (@Periodic = "  << counterTime.tacAndElapsed() << "s)\n" );
508 509 510
    }


511 512 513 514
};


#endif // FFMMALGORITHMPERIODIC_HPP