utestMpiTreeBuilder.cpp 8.62 KB
Newer Older
1
// See LICENCE file at project root
2

PIACIBELLO Cyrille's avatar
PIACIBELLO Cyrille committed
3 4 5 6
// ==== CMAKE =====
// @FUSE_MPI
// ================

7 8 9 10 11 12 13 14 15
#include "ScalFmmConfig.h"
#include <cstdlib>
#include <string.h>
#include <stdexcept>
#include <algorithm>
#include <vector>

#include "FUTester.hpp"

BRAMAS Berenger's avatar
BRAMAS Berenger committed
16 17
#include "Utils/FMpi.hpp"
#include "Containers/FVector.hpp"
18

BRAMAS Berenger's avatar
BRAMAS Berenger committed
19 20
#include "Files/FFmaGenericLoader.hpp"
#include "Files/FMpiFmaGenericLoader.hpp"
21
#include "Utils/FLeafBalance.hpp"
BRAMAS Berenger's avatar
BRAMAS Berenger committed
22
#include "Containers/FTreeCoordinate.hpp"
23
#include "Containers/FCoordinateComputer.hpp"
24

BRAMAS Berenger's avatar
BRAMAS Berenger committed
25 26 27 28
#include "Utils/FQuickSortMpi.hpp"
#include "Utils/FBitonicSort.hpp"
#include "Files/FMpiTreeBuilder.hpp"
#include "Core/FCoreCommon.hpp"
29

BRAMAS Berenger's avatar
BRAMAS Berenger committed
30 31
#include "Utils/FPoint.hpp"
#include "Utils/FMath.hpp"
32 33 34


class TestMpiTreeBuilder :  public FUTesterMpi< class TestMpiTreeBuilder> {
35

36
    template <class FReal>
37
    struct TestParticle{
38
        MortonIndex mindex;
39 40 41
        FSize indexInFile;
        FPoint<FReal> position;
        FReal physicalValue;
42

43 44 45
        const FPoint<FReal>& getPosition()const{
            return position;
        }
46

47
        bool operator<(const TestParticle& rhs)const{
48
            return rhs.mindex < this->mindex || (rhs.mindex == this->mindex && this->indexInFile < rhs.indexInFile);
49
        }
50

51
    };
52

53
    void RunTest(){
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
        typedef double FReal;
        //
        // Load particles
        //
        if(sizeof(FReal) == sizeof(float) ) {
            std::cerr << "No input data available for Float "<< std::endl;
            exit(EXIT_FAILURE);
        }
        const std::string parFile( (sizeof(FReal) == sizeof(float))?
                                       "Test/DirectFloatbfma":
                                       "UTest/DirectDouble.bfma");
        //Let the choice there to test
        std::string filename(SCALFMMDataPath+parFile);
        //std::string filename("../Data/unitCubeXYZQ100.bfma");

69
        int TreeHeight = 3;
70

71 72
        FMpiFmaGenericLoader<FReal> loader(filename,app.global());
        if(!loader.isOpen()) throw std::runtime_error("Particle file couldn't be opened!") ;
73 74

        //Get the needed informations
75 76
        const FReal boxWidth = loader.getBoxWidth();
        const FReal boxWidthAtLeafLevel = boxWidth/FReal(1 << (TreeHeight - 1));
77

78 79 80 81 82
        const FPoint<FReal> centerOfBox = loader.getCenterOfBox();
        const FPoint<FReal> boxCorner   = centerOfBox - boxWidth/2;
        //Now, we sort again the particles with MPI QuickSort
        const FSize idxStart = loader.getStart();
        FAssertLF(idxStart + loader.getMyNumberOfParticles() <= loader.getNumberOfParticles());
83

84 85 86
        FMpiTreeBuilder<FReal,TestParticle<FReal>>::IndexedParticle * arrayToBeSorted = new FMpiTreeBuilder<FReal,TestParticle<FReal>>::IndexedParticle[loader.getMyNumberOfParticles()];
        //Copy the TestParticles into an array of indexedParticle
        for(FSize i=0 ; i<loader.getMyNumberOfParticles() ; ++i){
87
            //Fill automatically position AND physicalValue attributes
88
            loader.fillParticle(&(arrayToBeSorted[i].particle.position),&(arrayToBeSorted[i].particle.physicalValue));
89 90

            //We store the index in the file
91
            arrayToBeSorted[i].particle.indexInFile = i + idxStart;
92 93

            //Build temporary TreeCoordinate
94 95 96 97
            FTreeCoordinate host;
            host.setX( FCoordinateComputer::GetTreeCoordinate<FReal>( arrayToBeSorted[i].particle.getPosition().getX() - boxCorner.getX(), boxWidth, boxWidthAtLeafLevel, TreeHeight ));
            host.setY( FCoordinateComputer::GetTreeCoordinate<FReal>( arrayToBeSorted[i].particle.getPosition().getY() - boxCorner.getY(), boxWidth, boxWidthAtLeafLevel, TreeHeight ));
            host.setZ( FCoordinateComputer::GetTreeCoordinate<FReal>( arrayToBeSorted[i].particle.getPosition().getZ() - boxCorner.getZ(), boxWidth, boxWidthAtLeafLevel, TreeHeight ));
98 99

            //Set Morton index from Tree Coordinate
100 101
            arrayToBeSorted[i].particle.mindex = host.getMortonIndex();
            arrayToBeSorted[i].index = arrayToBeSorted[i].particle.mindex;
102
        }
103
        FMpiTreeBuilder<FReal,TestParticle<FReal>>::IndexedParticle* outputArray = nullptr;
104
        FSize outputSize;
105
        FQuickSortMpi<FMpiTreeBuilder<FReal,TestParticle<FReal>>::IndexedParticle,MortonIndex,FSize>::QsMpi(arrayToBeSorted,loader.getMyNumberOfParticles(),&outputArray,&outputSize,app.global());
106 107

        if(app.global().processId() == 0){
108 109 110
            FSize allparts;
            MPI_Reduce(&outputSize, &allparts, 1, FMpi::GetType(outputSize), MPI_SUM, 0, app.global().getComm());
            FAssertLF(allparts == loader.getNumberOfParticles());
111 112
        }
        else{
113
            MPI_Reduce(&outputSize, nullptr, 1, FMpi::GetType(outputSize), MPI_SUM, 0, app.global().getComm());
114 115
        }

116 117
        int* allPartsCount = new int[loader.getNumberOfParticles()];
        memset(allPartsCount, 0, sizeof(int)*loader.getNumberOfParticles());
118

119 120 121
        for(int idxPart = 0 ; idxPart < outputSize ; ++idxPart){
            FAssertLF(allPartsCount[outputArray[idxPart].particle.indexInFile] == 0);
            allPartsCount[outputArray[idxPart].particle.indexInFile] += 1;
122 123
        }

124 125 126
        if(app.global().processId() == 0){
            int* allPartsCountReduced = new int[loader.getNumberOfParticles()];
            MPI_Reduce(allPartsCount, allPartsCountReduced, int(loader.getNumberOfParticles()), FMpi::GetType(*allPartsCount), MPI_SUM, 0, app.global().getComm());
127

128 129 130
            for(int idxPart = 0 ; idxPart < loader.getNumberOfParticles() ; ++idxPart){
                FAssertLF(allPartsCountReduced[idxPart] == 1);
            }
131

132 133 134 135 136
            delete[] allPartsCountReduced;
        }
        else{
            MPI_Reduce(allPartsCount, nullptr, int(loader.getNumberOfParticles()), FMpi::GetType(*allPartsCount), MPI_SUM, 0, app.global().getComm());
        }
137

138
        MortonIndex leftright[2] = {-1, -1};
139

140 141 142
        if(outputSize){
            leftright[0] = outputArray[0].particle.mindex;
            leftright[1] = outputArray[0].particle.mindex;
143

144 145 146
            for(int idxPart = 1 ; idxPart < outputSize ; ++idxPart){
                leftright[0] = std::min(leftright[0], outputArray[idxPart].particle.mindex);
                leftright[1] = std::max(leftright[1], outputArray[idxPart].particle.mindex);
147 148
            }
        }
149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171

        if(app.global().processId() == 0){
            MortonIndex* allintervals = new MortonIndex[app.global().processCount()*2];
            MPI_Gather(leftright, 2, FMpi::GetType(*leftright),
                       allintervals, 2, FMpi::GetType(*allintervals), 0, app.global().getComm());

            MortonIndex currentLimit = -1;

            for(int idxProc = 0 ; idxProc < app.global().processCount() ; ++idxProc){
                FAssertLF(allintervals[idxProc*2] != -1 || allintervals[idxProc*2+1] == -1);
                if(allintervals[idxProc*2] != -1){
                    FAssertLF(allintervals[idxProc*2] <= allintervals[idxProc*2+1]);

                    if(idxProc && allintervals[idxProc*2-1] != -1){
                        FAssertLF(allintervals[idxProc*2-1] < allintervals[idxProc*2]);
                    }
                    if(idxProc != app.global().processCount()-1 && allintervals[idxProc*2+1] != -1){
                        FAssertLF(allintervals[idxProc*2] < allintervals[idxProc*2+1]);
                    }

                    FAssertLF(currentLimit < allintervals[idxProc*2]);
                    currentLimit = allintervals[idxProc*2+1];
                }
172 173
            }

174 175 176 177 178 179
            delete[] allintervals;
        }
        else{
            MPI_Gather(leftright, 2, FMpi::GetType(*leftright),
                       nullptr, 2, FMpi::GetType(*leftright), 0, app.global().getComm());
        }
180

181

182 183
        delete[] allPartsCount;
        delete[] outputArray;
184
    }
185

186 187
    /** If memstas is running print the memory used */
    void PostTest() {
188 189 190 191 192 193 194 195
        if( FMemStats::controler.isUsed() ){
            std::cout << app.global().processId() << "-> Memory used at the end " << FMemStats::controler.getCurrentAllocated()
                      << " Bytes (" << FMemStats::controler.getCurrentAllocatedMB() << "MB)\n";
            std::cout << app.global().processId() << "-> Max memory used " << FMemStats::controler.getMaxAllocated()
                      << " Bytes (" << FMemStats::controler.getMaxAllocatedMB() << "MB)\n";
            std::cout << app.global().processId() << "-> Total memory used " << FMemStats::controler.getTotalAllocated()
                      << " Bytes (" << FMemStats::controler.getTotalAllocatedMB() << "MB)\n";
        }
196
    }
197

198
    void SetTests(){
199
        AddTest(&TestMpiTreeBuilder::RunTest,"Load a File, sort it, merge it, and Equalize it (4 steps)");
200
    }
201

202
public:
203 204
    TestMpiTreeBuilder(int argc,char ** argv) : FUTesterMpi(argc,argv){
    }
205 206 207 208 209


};

TestClassMpi(TestMpiTreeBuilder);