diff --git a/Src/GroupTree/Core/FGroupTree.hpp b/Src/GroupTree/Core/FGroupTree.hpp index 1bcb7d7118ac972a75b99b516451a0042b679aeb..3639ef6b224784ab76db40ac0febd5dfebdee290 100644 --- a/Src/GroupTree/Core/FGroupTree.hpp +++ b/Src/GroupTree/Core/FGroupTree.hpp @@ -623,6 +623,187 @@ public: delete[] currentBlockIndexes; } + template<class ParticleContainer> + FGroupTree(const int inTreeHeight, const FReal inBoxWidth, const FPoint<FReal>& inBoxCenter, + const int inNbElementsPerBlock, ParticleContainer* inParticlesContainer, + std::vector<std::vector<int>> groupSizeAtEachLevel, + const bool particlesAreSorted = false): + treeHeight(inTreeHeight),nbElementsPerBlock(inNbElementsPerBlock),cellBlocksPerLevel(nullptr), + boxCenter(inBoxCenter), boxCorner(inBoxCenter,-(inBoxWidth/2)), boxWidth(inBoxWidth), + boxWidthAtLeafLevel(inBoxWidth/FReal(1<<(inTreeHeight-1))){ + + cellBlocksPerLevel = new std::vector<CellGroupClass*>[treeHeight]; + + MortonIndex* currentBlockIndexes = new MortonIndex[nbElementsPerBlock]; + // First we work at leaf level + { + // Build morton index for particles + struct ParticleSortingStruct{ + FSize originalIndex; + MortonIndex mindex; + }; + // Convert position to morton index + const FSize nbParticles = inParticlesContainer->getNbParticles(); + ParticleSortingStruct* particlesToSort = new ParticleSortingStruct[nbParticles]; + { + const FReal* xpos = inParticlesContainer->getPositions()[0]; + const FReal* ypos = inParticlesContainer->getPositions()[1]; + const FReal* zpos = inParticlesContainer->getPositions()[2]; + + for(FSize idxPart = 0 ; idxPart < nbParticles ; ++idxPart){ + const FTreeCoordinate host = FCoordinateComputer::GetCoordinateFromPositionAndCorner<FReal>(this->boxCorner, this->boxWidth, + treeHeight, + FPoint<FReal>(xpos[idxPart], ypos[idxPart], zpos[idxPart]) ); + const MortonIndex particleIndex = host.getMortonIndex(treeHeight-1); + particlesToSort[idxPart].mindex = particleIndex; + particlesToSort[idxPart].originalIndex = idxPart; + } + } + + // Sort if needed + if(particlesAreSorted == false){ + FQuickSort<ParticleSortingStruct, FSize>::QsOmp(particlesToSort, nbParticles, [](const ParticleSortingStruct& v1, const ParticleSortingStruct& v2){ + return v1.mindex <= v2.mindex; + }); + } + + + // Convert to block + const int idxLevel = (treeHeight - 1); + FSize* nbParticlesPerLeaf = new FSize[nbElementsPerBlock]; + FSize firstParticle = 0; + // We need to proceed each group in sub level + while(firstParticle != nbParticles){ + int sizeOfBlock = 0; + FSize lastParticle = firstParticle; + // Count until end of sub group is reached or we have enough cells + while(sizeOfBlock < nbElementsPerBlock && lastParticle < nbParticles){ + if(sizeOfBlock == 0 || currentBlockIndexes[sizeOfBlock-1] != particlesToSort[lastParticle].mindex){ + currentBlockIndexes[sizeOfBlock] = particlesToSort[lastParticle].mindex; + nbParticlesPerLeaf[sizeOfBlock] = 1; + sizeOfBlock += 1; + } + else{ + nbParticlesPerLeaf[sizeOfBlock-1] += 1; + } + lastParticle += 1; + } + while(lastParticle < nbParticles && currentBlockIndexes[sizeOfBlock-1] == particlesToSort[lastParticle].mindex){ + nbParticlesPerLeaf[sizeOfBlock-1] += 1; + lastParticle += 1; + } + + // Create a group + CellGroupClass*const newBlock = new CellGroupClass(currentBlockIndexes[0], + currentBlockIndexes[sizeOfBlock-1]+1, + sizeOfBlock); + FGroupOfParticles<FReal, NbSymbAttributes, NbAttributesPerParticle, AttributeClass>*const newParticleBlock = new FGroupOfParticles<FReal, NbSymbAttributes, NbAttributesPerParticle, AttributeClass>(currentBlockIndexes[0], + currentBlockIndexes[sizeOfBlock-1]+1, + sizeOfBlock, lastParticle-firstParticle); + + // Init cells + size_t nbParticlesOffsetBeforeLeaf = 0; + FSize offsetParticles = firstParticle; + for(int cellIdInBlock = 0; cellIdInBlock != sizeOfBlock ; ++cellIdInBlock){ + newBlock->newCell(currentBlockIndexes[cellIdInBlock], cellIdInBlock); + + CompositeCellClass newNode = newBlock->getCompleteCell(cellIdInBlock); + newNode.setMortonIndex(currentBlockIndexes[cellIdInBlock]); + FTreeCoordinate coord; + coord.setPositionFromMorton(currentBlockIndexes[cellIdInBlock], idxLevel); + newNode.setCoordinate(coord); + + // Add leaf + nbParticlesOffsetBeforeLeaf = newParticleBlock->newLeaf(currentBlockIndexes[cellIdInBlock], cellIdInBlock, + nbParticlesPerLeaf[cellIdInBlock], nbParticlesOffsetBeforeLeaf); + + BasicAttachedClass attachedLeaf = newParticleBlock->template getLeaf<BasicAttachedClass>(cellIdInBlock); + // Copy each particle from the original position + for(FSize idxPart = 0 ; idxPart < nbParticlesPerLeaf[cellIdInBlock] ; ++idxPart){ + attachedLeaf.setParticle(idxPart, particlesToSort[idxPart + offsetParticles].originalIndex, inParticlesContainer); + } + offsetParticles += nbParticlesPerLeaf[cellIdInBlock]; + } + + // Keep the block + cellBlocksPerLevel[idxLevel].push_back(newBlock); + particleBlocks.push_back(newParticleBlock); + + sizeOfBlock = 0; + firstParticle = lastParticle; + } + delete[] nbParticlesPerLeaf; + delete[] particlesToSort; + } + + + // For each level from heigth - 2 to 1 + for(int idxLevel = treeHeight-2; idxLevel > 0 ; --idxLevel){ + + CellGroupConstIterator iterChildCells = cellBlocksPerLevel[idxLevel+1].begin(); + const CellGroupConstIterator iterChildEndCells = cellBlocksPerLevel[idxLevel+1].end(); + + // Skip blocks that do not respect limit + while(iterChildCells != iterChildEndCells) + ++iterChildCells; + + // If lower level is empty or all blocks skiped stop here + if(iterChildCells == iterChildEndCells){ + break; + } + + MortonIndex currentCellIndex = (*iterChildCells)->getStartingIndex(); + int sizeOfBlock = 0; + + // We need to proceed each group in sub level + while(iterChildCells != iterChildEndCells){ + // Count until end of sub group is reached or we have enough cells + while(sizeOfBlock < nbElementsPerBlock && iterChildCells != iterChildEndCells ){ + if((sizeOfBlock == 0 || currentBlockIndexes[sizeOfBlock-1] != (currentCellIndex>>3)) + && (*iterChildCells)->exists(currentCellIndex)){ + currentBlockIndexes[sizeOfBlock] = (currentCellIndex>>3); + sizeOfBlock += 1; + currentCellIndex = (((currentCellIndex>>3)+1)<<3); + } + else{ + currentCellIndex += 1; + } + // If we are at the end of the sub group, move to next + while(iterChildCells != iterChildEndCells && (*iterChildCells)->getEndingIndex() <= currentCellIndex){ + ++iterChildCells; + // Update morton index + if(iterChildCells != iterChildEndCells && currentCellIndex < (*iterChildCells)->getStartingIndex()){ + currentCellIndex = (*iterChildCells)->getStartingIndex(); + } + } + } + + // If group is full + if(sizeOfBlock == nbElementsPerBlock || (sizeOfBlock && iterChildCells == iterChildEndCells)){ + // Create a group + CellGroupClass*const newBlock = new CellGroupClass(currentBlockIndexes[0], + currentBlockIndexes[sizeOfBlock-1]+1, + sizeOfBlock); + // Init cells + for(int cellIdInBlock = 0; cellIdInBlock != sizeOfBlock ; ++cellIdInBlock){ + newBlock->newCell(currentBlockIndexes[cellIdInBlock], cellIdInBlock); + + CompositeCellClass newNode = newBlock->getCompleteCell(cellIdInBlock); + newNode.setMortonIndex(currentBlockIndexes[cellIdInBlock]); + FTreeCoordinate coord; + coord.setPositionFromMorton(currentBlockIndexes[cellIdInBlock], idxLevel); + newNode.setCoordinate(coord); + } + + // Keep the block + cellBlocksPerLevel[idxLevel].push_back(newBlock); + + sizeOfBlock = 0; + } + } + } + delete[] currentBlockIndexes; + } /** This function dealloc the tree by deleting each block */ ~FGroupTree(){