diff --git a/Src/GroupTree/FGroupTaskDepAlgorithm.hpp b/Src/GroupTree/FGroupTaskDepAlgorithm.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..6d5b83b02181658e1419bd761fe1acaeca5990c4
--- /dev/null
+++ b/Src/GroupTree/FGroupTaskDepAlgorithm.hpp
@@ -0,0 +1,692 @@
+#ifndef FGROUPTASKDEPALGORITHM_HPP
+#define FGROUPTASKDEPALGORITHM_HPP
+
+
+#include "../Utils/FGlobal.hpp"
+#include "../Core/FCoreCommon.hpp"
+#include "../Utils/FQuickSort.hpp"
+#include "../Containers/FTreeCoordinate.hpp"
+#include "../Utils/FLog.hpp"
+#include "../Utils/FTic.hpp"
+
+#include <vector>
+#include <vector>
+
+#include <omp.h>
+
+template <class OctreeClass, class CellContainerClass, class CellClass, class KernelClass, class ParticleGroupClass, class ParticleContainerClass>
+class FGroupTaskDepAlgorithm {
+protected:
+    struct OutOfBlockInteraction{
+        MortonIndex outIndex;
+        MortonIndex insideIndex;
+        int outPosition;
+        // To sort
+        bool operator <=(const OutOfBlockInteraction& other) const{
+            return outIndex <= other.outIndex;
+        }
+    };
+
+    template <class OtherBlockClass>
+    struct BlockInteractions{
+        OtherBlockClass* otherBlock;
+        std::vector<OutOfBlockInteraction> interactions;
+    };
+
+    std::vector< std::vector< std::vector<BlockInteractions<CellContainerClass>>>> externalInteractionsAllLevel;
+    std::vector< std::vector<BlockInteractions<ParticleGroupClass>>> externalInteractionsLeafLevel;
+
+    const int MaxThreads;         //< The number of threads
+    OctreeClass*const tree;       //< The Tree
+    KernelClass** kernels;        //< The kernels
+
+public:
+    FGroupTaskDepAlgorithm(OctreeClass*const inTree, KernelClass* inKernels, const int inMaxThreads = -1)
+        : MaxThreads(inMaxThreads==-1?omp_get_max_threads():inMaxThreads), tree(inTree), kernels(nullptr){
+        FAssertLF(tree, "tree cannot be null");
+        FAssertLF(inKernels, "kernels cannot be null");
+
+        kernels = new KernelClass*[MaxThreads];
+        for(int idxThread = 0 ; idxThread < MaxThreads ; ++idxThread){
+            this->kernels[idxThread] = new KernelClass(*inKernels);
+        }
+
+        FLOG(FLog::Controller << "FGroupTaskDepAlgorithm (Max Thread " << MaxThreads << ")\n");
+    }
+
+    ~FGroupTaskDepAlgorithm(){
+        for(int idxThread = 0 ; idxThread < MaxThreads ; ++idxThread){
+            delete this->kernels[idxThread];
+        }
+        delete[] kernels;
+    }
+
+    void execute(const unsigned operationsToProceed = FFmmNearAndFarFields){
+        FLOG( FLog::Controller << "\tStart FGroupTaskDepAlgorithm\n" );
+
+        //#pragma omp parallel
+        {
+            //#pragma omp single nowait
+            {
+                // For now rebuild all external interaction
+                buildExternalInteractionVecs();
+            }
+        }
+
+        #pragma omp parallel
+        {
+            #pragma omp single nowait
+            {
+                if( operationsToProceed & FFmmP2P ) directPass();
+
+                if(operationsToProceed & FFmmP2M) bottomPass();
+
+                if(operationsToProceed & FFmmM2M) upwardPass();
+
+                if(operationsToProceed & FFmmM2L) transferPass();
+
+                if(operationsToProceed & FFmmL2L) downardPass();
+
+                if( operationsToProceed & FFmmL2P ) mergePass();
+            }
+        }
+    }
+
+protected:
+    /**
+     * This function is creating the interactions vector between blocks.
+     * It fills externalInteractionsAllLevel and externalInteractionsLeafLevel.
+     * Warning, the omp task for now are using the class attributes!
+     *
+     */
+    void buildExternalInteractionVecs(){
+        FLOG( FTic timer; FTic leafTimer; FTic cellTimer; );
+        // Reset interactions
+        externalInteractionsAllLevel.clear();
+        externalInteractionsLeafLevel.clear();
+        // One per level + leaf level
+        externalInteractionsAllLevel.resize(tree->getHeight());
+
+        // First leaf level
+        {
+            // We create one big vector per block
+            typename std::vector< std::vector<OutOfBlockInteraction> > allOutsideInteractions;
+
+            for(int idxGroup = 0 ; idxGroup < tree->getNbParticleGroup() ; ++idxGroup){
+                // Create the vector
+                allOutsideInteractions.push_back( std::vector<OutOfBlockInteraction>() );
+                typename std::vector<OutOfBlockInteraction>* outsideInteractions = &allOutsideInteractions.back();
+                ParticleGroupClass* containers = tree->getParticleGroup(idxGroup);
+
+                externalInteractionsLeafLevel.emplace_back();
+                std::vector<BlockInteractions<ParticleGroupClass>>* externalInteractions = &externalInteractionsLeafLevel.back();
+
+                #pragma omp task default(none) firstprivate(idxGroup, containers, outsideInteractions, externalInteractions)
+                { // Can be a task(inout:iterCells, out:outsideInteractions)
+                    const MortonIndex blockStartIdx = containers->getStartingIndex();
+                    const MortonIndex blockEndIdx   = containers->getEndingIndex();
+
+                    for(MortonIndex mindex = blockStartIdx ; mindex < blockEndIdx ; ++mindex){
+                        ParticleContainerClass particles = containers->template getLeaf<ParticleContainerClass>(mindex);
+                        if(particles.isAttachedToSomething()){
+                            MortonIndex interactionsIndexes[26];
+                            int interactionsPosition[26];
+                            FTreeCoordinate coord(mindex, tree->getHeight()-1);
+                            int counter = coord.getNeighborsIndexes(tree->getHeight(),interactionsIndexes,interactionsPosition);
+
+                            for(int idxInter = 0 ; idxInter < counter ; ++idxInter){
+                                if( blockStartIdx <= interactionsIndexes[idxInter] && interactionsIndexes[idxInter] < blockEndIdx ){
+                                    // Inside block interaction, do nothing
+                                }
+                                else if(interactionsIndexes[idxInter] < mindex){
+                                    OutOfBlockInteraction property;
+                                    property.insideIndex = mindex;
+                                    property.outIndex    = interactionsIndexes[idxInter];
+                                    property.outPosition = interactionsPosition[idxInter];
+                                    (*outsideInteractions).push_back(property);
+                                }
+                            }
+                        }
+                    }
+
+                    // Sort to match external order
+                    FQuickSort<OutOfBlockInteraction, int>::QsSequential((*outsideInteractions).data(),int((*outsideInteractions).size()));
+
+                    int currentOutInteraction = 0;
+                    for(int idxLeftGroup = 0 ; idxLeftGroup < idxGroup && currentOutInteraction < int((*outsideInteractions).size()) ; ++idxLeftGroup){
+                        ParticleGroupClass* leftContainers = tree->getParticleGroup(idxLeftGroup);
+                        const MortonIndex blockStartIdx    = leftContainers->getStartingIndex();
+                        const MortonIndex blockEndIdx      = leftContainers->getEndingIndex();
+
+                        while(currentOutInteraction < int((*outsideInteractions).size()) && (*outsideInteractions)[currentOutInteraction].outIndex < blockStartIdx){
+                            currentOutInteraction += 1;
+                        }
+
+                        int lastOutInteraction = currentOutInteraction;
+                        while(lastOutInteraction < int((*outsideInteractions).size()) && (*outsideInteractions)[lastOutInteraction].outIndex < blockEndIdx){
+                            lastOutInteraction += 1;
+                        }
+
+                        const int nbInteractionsBetweenBlocks = (lastOutInteraction-currentOutInteraction);
+                        if(nbInteractionsBetweenBlocks){
+                            externalInteractions->emplace_back();
+                            BlockInteractions<ParticleGroupClass>* interactions = &externalInteractions->back();
+                            interactions->otherBlock = leftContainers;
+                            interactions->interactions.resize(nbInteractionsBetweenBlocks);
+                            std::copy((*outsideInteractions).begin() + currentOutInteraction,
+                                      (*outsideInteractions).begin() + lastOutInteraction,
+                                      interactions->interactions.begin());
+                        }
+
+                        currentOutInteraction = lastOutInteraction;
+                    }
+                }
+            }
+            #pragma omp taskwait
+        }
+        FLOG( leafTimer.tac(); );
+        FLOG( cellTimer.tic(); );
+        {
+            for(int idxLevel = tree->getHeight()-1 ; idxLevel >= 2 ; --idxLevel){
+                std::vector<std::vector<OutOfBlockInteraction> > allOutsideInteractions;
+
+                for(int idxGroup = 0 ; idxGroup < tree->getNbCellGroupAtLevel(idxLevel) ; ++idxGroup){
+                    allOutsideInteractions.push_back(std::vector<OutOfBlockInteraction>());
+                    std::vector<OutOfBlockInteraction>* outsideInteractions = &allOutsideInteractions.back();
+
+                    const CellContainerClass* currentCells = tree->getCellGroup(idxLevel, idxGroup);
+
+                    externalInteractionsAllLevel[idxLevel].emplace_back();
+                    std::vector<BlockInteractions<CellContainerClass>>* externalInteractions = &externalInteractionsAllLevel[idxLevel].back();
+
+                    #pragma omp task default(none) firstprivate(idxGroup, currentCells, outsideInteractions, idxLevel, externalInteractions)
+                    {
+                        const MortonIndex blockStartIdx = currentCells->getStartingIndex();
+                        const MortonIndex blockEndIdx   = currentCells->getEndingIndex();
+
+                        for(MortonIndex mindex = blockStartIdx ; mindex < blockEndIdx ; ++mindex){
+                            const CellClass* cell = currentCells->getCell(mindex);
+                            if(cell){
+                                FAssertLF(cell->getMortonIndex() == mindex);
+                                MortonIndex interactionsIndexes[189];
+                                int interactionsPosition[189];
+                                const FTreeCoordinate coord(cell->getCoordinate());
+                                int counter = coord.getInteractionNeighbors(idxLevel,interactionsIndexes,interactionsPosition);
+
+                                for(int idxInter = 0 ; idxInter < counter ; ++idxInter){
+                                    if( blockStartIdx <= interactionsIndexes[idxInter] && interactionsIndexes[idxInter] < blockEndIdx ){
+                                        // Nothing to do
+                                    }
+                                    else if(interactionsIndexes[idxInter] < mindex){
+                                        OutOfBlockInteraction property;
+                                        property.insideIndex = mindex;
+                                        property.outIndex    = interactionsIndexes[idxInter];
+                                        property.outPosition = interactionsPosition[idxInter];
+                                        (*outsideInteractions).push_back(property);
+                                    }
+                                }
+                            }
+                        }
+
+                        // Manage outofblock interaction
+                        FQuickSort<OutOfBlockInteraction, int>::QsSequential((*outsideInteractions).data(),int((*outsideInteractions).size()));
+
+                        int currentOutInteraction = 0;
+                        for(int idxLeftGroup = 0 ; idxLeftGroup < idxGroup && currentOutInteraction < int((*outsideInteractions).size()) ; ++idxLeftGroup){
+                            CellContainerClass* leftCells   = tree->getCellGroup(idxLevel, idxLeftGroup);
+                            const MortonIndex blockStartIdx = leftCells->getStartingIndex();
+                            const MortonIndex blockEndIdx   = leftCells->getEndingIndex();
+
+                            while(currentOutInteraction < int((*outsideInteractions).size()) && (*outsideInteractions)[currentOutInteraction].outIndex < blockStartIdx){
+                                currentOutInteraction += 1;
+                            }
+
+                            int lastOutInteraction = currentOutInteraction;
+                            while(lastOutInteraction < int((*outsideInteractions).size()) && (*outsideInteractions)[lastOutInteraction].outIndex < blockEndIdx){
+                                lastOutInteraction += 1;
+                            }
+
+                            // Create interactions
+                            const int nbInteractionsBetweenBlocks = (lastOutInteraction-currentOutInteraction);
+                            if(nbInteractionsBetweenBlocks){
+                                externalInteractions->emplace_back();
+                                BlockInteractions<CellContainerClass>* interactions = &externalInteractions->back();
+                                interactions->otherBlock = leftCells;
+                                interactions->interactions.resize(nbInteractionsBetweenBlocks);
+                                std::copy((*outsideInteractions).begin() + currentOutInteraction,
+                                          (*outsideInteractions).begin() + lastOutInteraction,
+                                          interactions->interactions.begin());
+                            }
+
+                            currentOutInteraction = lastOutInteraction;
+                        }
+                    }
+                }
+            }
+            #pragma omp taskwait
+        }
+        FLOG( cellTimer.tac(); );
+
+        FLOG( FLog::Controller << "\t\t Prepare in " << timer.tacAndElapsed() << "s\n" );
+        FLOG( FLog::Controller << "\t\t\t Prepare at leaf level in   " << leafTimer.elapsed() << "s\n" );
+        FLOG( FLog::Controller << "\t\t\t Prepare at other levels in " << cellTimer.elapsed() << "s\n" );
+    }
+
+
+    void bottomPass(){
+        FLOG( FTic timer; );
+
+        for(int idxGroup = 0 ; idxGroup < tree->getNbParticleGroup() ; ++idxGroup){
+            CellContainerClass* leafCells  = tree->getCellGroup(tree->getHeight()-1, idxGroup);
+            ParticleGroupClass* containers = tree->getParticleGroup(idxGroup);
+            #pragma omp task default(shared) firstprivate(leafCells, containers) depend(inout: leafCells[0]) depend(in: containers[0])
+            {
+                const MortonIndex blockStartIdx = leafCells->getStartingIndex();
+                const MortonIndex blockEndIdx = leafCells->getEndingIndex();
+                KernelClass*const kernel = kernels[omp_get_thread_num()];
+
+                for(MortonIndex mindex = blockStartIdx ; mindex < blockEndIdx ; ++mindex){
+                    CellClass* cell = leafCells->getCell(mindex);
+                    if(cell){
+                        FAssertLF(cell->getMortonIndex() == mindex);
+                        ParticleContainerClass particles = containers->template getLeaf<ParticleContainerClass>(mindex);
+                        FAssertLF(particles.isAttachedToSomething());
+                        kernel->P2M(cell, &particles);
+                    }
+                }
+            }
+        }
+
+        FLOG( FLog::Controller << "\t\t bottomPass in " << timer.tacAndElapsed() << "s\n" );
+    }
+
+    void upwardPass(){
+        FLOG( FTic timer; );
+        for(int idxLevel = tree->getHeight()-2 ; idxLevel >= 2 ; --idxLevel){
+            typename OctreeClass::CellGroupIterator iterCells = tree->cellsBegin(idxLevel);
+            const typename OctreeClass::CellGroupIterator endCells = tree->cellsEnd(idxLevel);
+
+            typename OctreeClass::CellGroupIterator iterChildCells = tree->cellsBegin(idxLevel+1);
+            const typename OctreeClass::CellGroupIterator endChildCells = tree->cellsEnd(idxLevel+1);
+
+            while(iterCells != endCells && iterChildCells != endChildCells){
+                CellContainerClass*const currentCells = (*iterCells);
+
+                CellContainerClass* subCellGroups[9];
+                memset(subCellGroups, 0, sizeof(CellContainerClass*) * 9);
+
+                // Skip current group if needed
+                if( (*iterChildCells)->getEndingIndex() <= (currentCells->getStartingIndex()<<3) ){
+                    ++iterChildCells;
+                    FAssertLF( iterChildCells != endChildCells );
+                    FAssertLF( ((*iterChildCells)->getStartingIndex()>>3) == currentCells->getStartingIndex() );
+                }
+                // Copy at max 8 groups
+                int nbSubCellGroups = 0;
+                subCellGroups[nbSubCellGroups] = (*iterChildCells);
+                nbSubCellGroups += 1;
+                while((*iterChildCells)->getEndingIndex() <= ((currentCells->getEndingIndex()<<3)+7)
+                      && (++iterChildCells) != endChildCells
+                      && (*iterChildCells)->getStartingIndex() <= (currentCells->getEndingIndex()<<3)+7 ){
+                    subCellGroups[nbSubCellGroups] = (*iterChildCells);
+                    nbSubCellGroups += 1;
+                    FAssertLF( nbSubCellGroups <= 9 );
+                }
+
+                #pragma omp task default(none) firstprivate(idxLevel, currentCells, subCellGroups, nbSubCellGroups) depend(inout: currentCells[0]) depend(in: subCellGroups[0])
+                {
+                    const MortonIndex blockStartIdx = currentCells->getStartingIndex();
+                    const MortonIndex blockEndIdx   = currentCells->getEndingIndex();
+                    KernelClass*const kernel = kernels[omp_get_thread_num()];
+                    int idxSubCellGroup = 0;
+
+                    for(MortonIndex mindex = blockStartIdx ; mindex < blockEndIdx && idxSubCellGroup != nbSubCellGroups; ++mindex){
+                        CellClass* cell = currentCells->getCell(mindex);
+                        if(cell){
+                            FAssertLF(cell->getMortonIndex() == mindex);
+                            CellClass* child[8] = {nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr};
+
+                            for(int idxChild = 0 ; idxChild < 8 ; ++idxChild){
+                                if( subCellGroups[idxSubCellGroup]->getEndingIndex() <= ((mindex<<3)+idxChild) ){
+                                    idxSubCellGroup += 1;
+                                }
+                                if( idxSubCellGroup == nbSubCellGroups ){
+                                    break;
+                                }
+                                child[idxChild] = subCellGroups[idxSubCellGroup]->getCell((mindex<<3)+idxChild);
+                                FAssertLF(child[idxChild] == nullptr || child[idxChild]->getMortonIndex() == ((mindex<<3)+idxChild));
+                            }
+
+                            kernel->M2M(cell, child, idxLevel);
+                        }
+                    }
+                }
+
+                ++iterCells;
+            }
+
+            FAssertLF(iterCells == endCells);
+            FAssertLF((iterChildCells == endChildCells || (++iterChildCells) == endChildCells));
+            FAssertLF(iterCells == endCells && (iterChildCells == endChildCells || (++iterChildCells) == endChildCells));
+        }
+        FLOG( FLog::Controller << "\t\t upwardPass in " << timer.tacAndElapsed() << "s\n" );
+    }
+
+    void transferPass(){
+        FLOG( FTic timer; );
+        FLOG( FTic timerInBlock; FTic timerOutBlock; );
+        for(int idxLevel = tree->getHeight()-1 ; idxLevel >= 2 ; --idxLevel){
+            FLOG( timerInBlock.tic() );
+            {
+                typename OctreeClass::CellGroupIterator iterCells = tree->cellsBegin(idxLevel);
+                const typename OctreeClass::CellGroupIterator endCells = tree->cellsEnd(idxLevel);
+
+                while(iterCells != endCells){
+                    CellContainerClass* currentCells = (*iterCells);
+
+                    #pragma omp task default(none) firstprivate(currentCells, idxLevel) depend(inout: currentCells[0])
+                    {
+                        const MortonIndex blockStartIdx = currentCells->getStartingIndex();
+                        const MortonIndex blockEndIdx = currentCells->getEndingIndex();
+                        KernelClass*const kernel = kernels[omp_get_thread_num()];
+
+                        for(MortonIndex mindex = blockStartIdx ; mindex < blockEndIdx ; ++mindex){
+                            CellClass* cell = currentCells->getCell(mindex);
+                            if(cell){
+                                FAssertLF(cell->getMortonIndex() == mindex);
+                                MortonIndex interactionsIndexes[189];
+                                int interactionsPosition[189];
+                                const FTreeCoordinate coord(cell->getCoordinate());
+                                int counter = coord.getInteractionNeighbors(idxLevel,interactionsIndexes,interactionsPosition);
+
+                                const CellClass* interactions[343];
+                                memset(interactions, 0, 343*sizeof(CellClass*));
+                                int counterExistingCell = 0;
+
+                                for(int idxInter = 0 ; idxInter < counter ; ++idxInter){
+                                    if( blockStartIdx <= interactionsIndexes[idxInter] && interactionsIndexes[idxInter] < blockEndIdx ){
+                                        CellClass* interCell = currentCells->getCell(interactionsIndexes[idxInter]);
+                                        if(interCell){
+                                            FAssertLF(interCell->getMortonIndex() == interactionsIndexes[idxInter]);
+                                            FAssertLF(interactions[interactionsPosition[idxInter]] == nullptr);
+                                            interactions[interactionsPosition[idxInter]] = interCell;
+                                            counterExistingCell += 1;
+                                        }
+                                    }
+                                }
+
+                                kernel->M2L( cell , interactions, counterExistingCell, idxLevel);
+                            }
+                        }
+                    }
+                    ++iterCells;
+                }
+            }
+            FLOG( timerInBlock.tac() );
+            FLOG( timerOutBlock.tic() );
+            {
+                typename OctreeClass::CellGroupIterator iterCells = tree->cellsBegin(idxLevel);
+                const typename OctreeClass::CellGroupIterator endCells = tree->cellsEnd(idxLevel);
+
+                typename std::vector<std::vector<BlockInteractions<CellContainerClass>>>::iterator externalInteractionsIter = externalInteractionsAllLevel[idxLevel].begin();
+
+                while(iterCells != endCells){
+                    CellContainerClass* currentCells = (*iterCells);
+
+                    typename std::vector<BlockInteractions<CellContainerClass>>::iterator currentInteractions = (*externalInteractionsIter).begin();
+                    const typename std::vector<BlockInteractions<CellContainerClass>>::iterator currentInteractionsEnd = (*externalInteractionsIter).end();
+
+                    while(currentInteractions != currentInteractionsEnd){
+                        CellContainerClass* cellsOther = (*currentInteractions).otherBlock;
+                        const std::vector<OutOfBlockInteraction>* outsideInteractions = &(*currentInteractions).interactions;
+
+                        #pragma omp task default(none) firstprivate(currentCells, outsideInteractions, cellsOther, idxLevel) depend(inout: currentCells[0], cellsOther[0])
+                        {
+                            KernelClass*const kernel = kernels[omp_get_thread_num()];
+
+                            for(int outInterIdx = 0 ; outInterIdx < int(outsideInteractions->size()) ; ++outInterIdx){
+                                CellClass* interCell = cellsOther->getCell((*outsideInteractions)[outInterIdx].outIndex);
+                                if(interCell){
+                                    FAssertLF(interCell->getMortonIndex() == (*outsideInteractions)[outInterIdx].outIndex);
+                                    CellClass* cell = currentCells->getCell((*outsideInteractions)[outInterIdx].insideIndex);
+                                    FAssertLF(cell);
+                                    FAssertLF(cell->getMortonIndex() == (*outsideInteractions)[outInterIdx].insideIndex);
+
+                                    const CellClass* interactions[343];
+                                    memset(interactions, 0, 343*sizeof(CellClass*));
+                                    interactions[(*outsideInteractions)[outInterIdx].outPosition] = interCell;
+                                    const int counter = 1;
+                                    kernel->M2L( cell , interactions, counter, idxLevel);
+
+                                    interactions[(*outsideInteractions)[outInterIdx].outPosition] = nullptr;
+                                    interactions[getOppositeInterIndex((*outsideInteractions)[outInterIdx].outPosition)] = cell;
+                                    kernel->M2L( interCell , interactions, counter, idxLevel);
+                                }
+                            }
+                        }
+
+                        ++currentInteractions;
+                    }
+
+                    ++iterCells;
+                    ++externalInteractionsIter;
+                }
+            }
+            FLOG( timerOutBlock.tac() );
+        }
+        FLOG( FLog::Controller << "\t\t transferPass in " << timer.tacAndElapsed() << "s\n" );
+        FLOG( FLog::Controller << "\t\t\t inblock in  " << timerInBlock.elapsed() << "s\n" );
+        FLOG( FLog::Controller << "\t\t\t outblock in " << timerOutBlock.elapsed() << "s\n" );
+    }
+
+    void downardPass(){
+        FLOG( FTic timer; );
+        for(int idxLevel = 2 ; idxLevel <= tree->getHeight()-2 ; ++idxLevel){
+            typename OctreeClass::CellGroupIterator iterCells = tree->cellsBegin(idxLevel);
+            const typename OctreeClass::CellGroupIterator endCells = tree->cellsEnd(idxLevel);
+
+            typename OctreeClass::CellGroupIterator iterChildCells = tree->cellsBegin(idxLevel+1);
+            const typename OctreeClass::CellGroupIterator endChildCells = tree->cellsEnd(idxLevel+1);
+
+            while(iterCells != endCells && iterChildCells != endChildCells){
+                CellContainerClass*const currentCells = (*iterCells);
+
+                CellContainerClass* subCellGroups[9];
+                memset(subCellGroups, 0, sizeof(CellContainerClass*) * 9);
+
+                // Skip current group if needed
+                if( (*iterChildCells)->getEndingIndex() <= (currentCells->getStartingIndex()<<3) ){
+                    ++iterChildCells;
+                    FAssertLF( iterChildCells != endChildCells );
+                    FAssertLF( ((*iterChildCells)->getStartingIndex()>>3) == currentCells->getStartingIndex() );
+                }
+                // Copy at max 8 groups
+                int nbSubCellGroups = 0;
+                subCellGroups[nbSubCellGroups] = (*iterChildCells);
+                nbSubCellGroups += 1;
+                while((*iterChildCells)->getEndingIndex() <= ((currentCells->getEndingIndex()<<3)+7)
+                      && (++iterChildCells) != endChildCells
+                      && (*iterChildCells)->getStartingIndex() <= (currentCells->getEndingIndex()<<3)+7 ){
+                    subCellGroups[nbSubCellGroups] = (*iterChildCells);
+                    nbSubCellGroups += 1;
+                    FAssertLF( nbSubCellGroups <= 9 );
+                }
+
+                #pragma omp task default(none) firstprivate(idxLevel, currentCells, subCellGroups, nbSubCellGroups) depend(inout: subCellGroups[0]) depend(in: currentCells[0])
+                {
+                    const MortonIndex blockStartIdx = currentCells->getStartingIndex();
+                    const MortonIndex blockEndIdx = currentCells->getEndingIndex();
+                    KernelClass*const kernel = kernels[omp_get_thread_num()];
+                    int idxSubCellGroup = 0;
+
+                    for(MortonIndex mindex = blockStartIdx ; mindex < blockEndIdx && idxSubCellGroup != nbSubCellGroups; ++mindex){
+                        CellClass* cell = currentCells->getCell(mindex);
+                        if(cell){
+                            FAssertLF(cell->getMortonIndex() == mindex);
+                            CellClass* child[8] = {nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr};
+
+                            for(int idxChild = 0 ; idxChild < 8 ; ++idxChild){
+                                if( subCellGroups[idxSubCellGroup]->getEndingIndex() <= ((mindex<<3)+idxChild) ){
+                                    idxSubCellGroup += 1;
+                                }
+                                if( idxSubCellGroup == nbSubCellGroups ){
+                                    break;
+                                }
+                                child[idxChild] = subCellGroups[idxSubCellGroup]->getCell((mindex<<3)+idxChild);
+                                FAssertLF(child[idxChild] == nullptr || child[idxChild]->getMortonIndex() == ((mindex<<3)+idxChild));
+                            }
+
+                            kernel->L2L(cell, child, idxLevel);
+                        }
+                    }
+                }
+
+                ++iterCells;
+            }
+
+            FAssertLF(iterCells == endCells && (iterChildCells == endChildCells || (++iterChildCells) == endChildCells));
+        }
+        FLOG( FLog::Controller << "\t\t downardPass in " << timer.tacAndElapsed() << "s\n" );
+    }
+
+    void directPass(){
+        FLOG( FTic timer; );
+        FLOG( FTic timerInBlock; FTic timerOutBlock; );
+
+        FLOG( timerInBlock.tic() );
+        {
+            typename OctreeClass::ParticleGroupIterator iterParticles = tree->leavesBegin();
+            const typename OctreeClass::ParticleGroupIterator endParticles = tree->leavesEnd();
+
+            while(iterParticles != endParticles){
+                ParticleGroupClass* containers = (*iterParticles);
+
+                #pragma omp task default(none) firstprivate(containers) depend(inout: containers[0])
+                {
+                    const MortonIndex blockStartIdx = containers->getStartingIndex();
+                    const MortonIndex blockEndIdx = containers->getEndingIndex();
+                    KernelClass*const kernel = kernels[omp_get_thread_num()];
+
+                    for(MortonIndex mindex = blockStartIdx ; mindex < blockEndIdx ; ++mindex){
+                        ParticleContainerClass particles = containers->template getLeaf<ParticleContainerClass>(mindex);
+                        if(particles.isAttachedToSomething()){
+                            MortonIndex interactionsIndexes[26];
+                            int interactionsPosition[26];
+                            FTreeCoordinate coord(mindex, tree->getHeight()-1);
+                            int counter = coord.getNeighborsIndexes(tree->getHeight(),interactionsIndexes,interactionsPosition);
+
+                            ParticleContainerClass interactionsObjects[27];
+                            ParticleContainerClass* interactions[27];
+                            memset(interactions, 0, 27*sizeof(ParticleContainerClass*));
+                            int counterExistingCell = 0;
+
+                            for(int idxInter = 0 ; idxInter < counter ; ++idxInter){
+                                if( blockStartIdx <= interactionsIndexes[idxInter] && interactionsIndexes[idxInter] < blockEndIdx ){
+                                    interactionsObjects[counterExistingCell] = containers->template getLeaf<ParticleContainerClass>(interactionsIndexes[idxInter]);
+                                    if(interactionsObjects[counterExistingCell].isAttachedToSomething()){
+                                        FAssertLF(interactions[interactionsPosition[idxInter]] == nullptr);
+                                        interactions[interactionsPosition[idxInter]] = &interactionsObjects[counterExistingCell];
+                                        counterExistingCell += 1;
+                                    }
+                                }
+                            }
+
+                            kernel->P2P( coord, &particles, &particles , interactions, counterExistingCell);
+                        }
+                    }
+                }
+                ++iterParticles;
+            }
+        }
+        FLOG( timerInBlock.tac() );
+        FLOG( timerOutBlock.tic() );
+        {
+            typename OctreeClass::ParticleGroupIterator iterParticles = tree->leavesBegin();
+            const typename OctreeClass::ParticleGroupIterator endParticles = tree->leavesEnd();
+
+            typename std::vector<std::vector<BlockInteractions<ParticleGroupClass>>>::iterator externalInteractionsIter = externalInteractionsLeafLevel.begin();
+
+            while(iterParticles != endParticles){
+                typename std::vector<BlockInteractions<ParticleGroupClass>>::iterator currentInteractions = (*externalInteractionsIter).begin();
+                const typename std::vector<BlockInteractions<ParticleGroupClass>>::iterator currentInteractionsEnd = (*externalInteractionsIter).end();
+
+                ParticleGroupClass* containers = (*iterParticles);
+
+                while(currentInteractions != currentInteractionsEnd){
+                    ParticleGroupClass* containersOther = (*currentInteractions).otherBlock;
+                    const std::vector<OutOfBlockInteraction>* outsideInteractions = &(*currentInteractions).interactions;
+
+                    #pragma omp task default(none) firstprivate(containers, containersOther, outsideInteractions) depend(inout: containersOther[0], containers[0])
+                    {
+                        KernelClass*const kernel = kernels[omp_get_thread_num()];
+                        for(int outInterIdx = 0 ; outInterIdx < int(outsideInteractions->size()) ; ++outInterIdx){
+                            ParticleContainerClass interParticles = containersOther->template getLeaf<ParticleContainerClass>((*outsideInteractions)[outInterIdx].outIndex);
+                            if(interParticles.isAttachedToSomething()){
+                                ParticleContainerClass particles = containers->template getLeaf<ParticleContainerClass>((*outsideInteractions)[outInterIdx].insideIndex);
+                                FAssertLF(particles.isAttachedToSomething());
+                                ParticleContainerClass* interactions[27];
+                                memset(interactions, 0, 27*sizeof(ParticleContainerClass*));
+                                interactions[(*outsideInteractions)[outInterIdx].outPosition] = &interParticles;
+                                const int counter = 1;
+                                kernel->P2PRemote( FTreeCoordinate((*outsideInteractions)[outInterIdx].insideIndex, tree->getHeight()-1), &particles, &particles , interactions, counter);
+
+                                interactions[(*outsideInteractions)[outInterIdx].outPosition] = nullptr;
+                                interactions[getOppositeNeighIndex((*outsideInteractions)[outInterIdx].outPosition)] = &particles;
+                                kernel->P2PRemote( FTreeCoordinate((*outsideInteractions)[outInterIdx].outIndex, tree->getHeight()-1), &interParticles, &interParticles , interactions, counter);
+                            }
+                        }
+                    }
+
+                    ++currentInteractions;
+                }
+
+                ++iterParticles;
+                ++externalInteractionsIter;
+            }
+        }
+        FLOG( timerOutBlock.tac() );
+
+        FLOG( FLog::Controller << "\t\t directPass in " << timer.tacAndElapsed() << "s\n" );
+        FLOG( FLog::Controller << "\t\t\t inblock  in " << timerInBlock.elapsed() << "s\n" );
+        FLOG( FLog::Controller << "\t\t\t outblock in " << timerOutBlock.elapsed() << "s\n" );
+    }
+
+    void mergePass(){
+        FLOG( FTic timer; );
+
+        for(int idxGroup = 0 ; idxGroup < tree->getNbParticleGroup() ; ++idxGroup){
+            CellContainerClass* leafCells  = tree->getCellGroup(tree->getHeight()-1, idxGroup);
+            ParticleGroupClass* containers = tree->getParticleGroup(idxGroup);
+            #pragma omp task default(shared) firstprivate(leafCells, containers) depend(inout: containers[0]) depend(in: leafCells[0])
+            {
+                const MortonIndex blockStartIdx = leafCells->getStartingIndex();
+                const MortonIndex blockEndIdx = leafCells->getEndingIndex();
+                KernelClass*const kernel = kernels[omp_get_thread_num()];
+
+                for(MortonIndex mindex = blockStartIdx ; mindex < blockEndIdx ; ++mindex){
+                    CellClass* cell = leafCells->getCell(mindex);
+                    if(cell){
+                        FAssertLF(cell->getMortonIndex() == mindex);
+                        ParticleContainerClass particles = containers->template getLeaf<ParticleContainerClass>(mindex);
+                        FAssertLF(particles.isAttachedToSomething());
+                        kernel->L2P(cell, &particles);
+                    }
+                }
+            }
+        }
+
+        FLOG( FLog::Controller << "\t\t L2P in " << timer.tacAndElapsed() << "s\n" );
+    }
+
+    int getOppositeNeighIndex(const int index) const {
+        // ((idxX+1)*3 + (idxY+1)) * 3 + (idxZ+1)
+        return 27-index-1;
+    }
+
+    int getOppositeInterIndex(const int index) const {
+        // ((( (xdiff+3) * 7) + (ydiff+3))) * 7 + zdiff + 3
+        return 343-index-1;
+    }
+};
+
+#endif // FGROUPTASKDEPALGORITHM_HPP