1 /*=========================================================================
2 
3   Program:   Visualization Toolkit
4   Module:    TestDistributedPointCloudFilter.cxx
5 
6   Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
7   All rights reserved.
8   See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
9 
10      This software is distributed WITHOUT ANY WARRANTY; without even
11      the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
12      PURPOSE.  See the above copyright notice for more information.
13 
14 =========================================================================*/
15 
16 #include "vtkBoundingBox.h"
17 #include "vtkDistributedPointCloudFilter.h"
18 #include "vtkDoubleArray.h"
19 #include "vtkIdFilter.h"
20 #include "vtkMinimalStandardRandomSequence.h"
21 #include "vtkMPICommunicator.h"
22 #include "vtkMPIController.h"
23 #include "vtkNew.h"
24 #include "vtkPointData.h"
25 #include "vtkPoints.h"
26 #include "vtkPolyData.h"
27 #include "vtkProcessIdScalars.h"
28 #include "vtkStringArray.h"
29 
30 //#define DEBUG_ON
31 
32 #ifdef DEBUG_ON
33 #include "vtkXMLPPolyDataWriter.h"
34 #endif
35 
36 #include <sstream>
37 
TestDistributedPointCloudFilter(int argc,char * argv[])38 int TestDistributedPointCloudFilter(int argc, char* argv[])
39 {
40   vtkNew<vtkMPIController> controller;
41   controller->Initialize(&argc, &argv, 0);
42   assert("pre: Controller should not be nullptr" && (controller != nullptr));
43   vtkMultiProcessController::SetGlobalController(controller);
44 
45   const int rank = controller->GetLocalProcessId();
46   const int numberOfProcessors = controller->GetNumberOfProcesses();
47   assert("pre: NumberOfProcessors >= 1" && (numberOfProcessors >= 1));
48   assert("pre: Rank is out-of-bounds" && (rank >= 0));
49 
50   const int finalNumberOfPointsPerRank = 40;
51   const int totalNumberOfPoints = numberOfProcessors * finalNumberOfPointsPerRank;
52   const int initialNumberOfPoints = totalNumberOfPoints / (numberOfProcessors > 1 ? 2 : 1);
53   vtkNew<vtkPolyData> inputPoly;
54   // Create random set of points on the two first ranks only
55   if (rank == 0 || rank == 1)
56   {
57     vtkNew<vtkMinimalStandardRandomSequence> random;
58     random->Initialize(rank);
59 
60     vtkNew<vtkPoints> points;
61     points->SetNumberOfPoints(initialNumberOfPoints);
62     inputPoly->SetPoints(points);
63 
64     vtkNew<vtkDoubleArray> data;
65     data->SetNumberOfValues(initialNumberOfPoints);
66     data->SetName("ReverseOrder");
67     inputPoly->GetPointData()->AddArray(data);
68 
69     vtkNew<vtkStringArray> sdata;
70     sdata->SetNumberOfValues(initialNumberOfPoints);
71     sdata->SetName("RankString");
72     inputPoly->GetPointData()->AddArray(sdata);
73 
74     std::stringstream ss;
75     ss << "Rank_" << rank;
76 
77     for (vtkIdType i = 0; i < initialNumberOfPoints; i++)
78     {
79       double coords[3];
80       coords[0] = random->GetValue(); random->Next();
81       coords[1] = random->GetValue(); random->Next();
82       coords[2] = random->GetValue(); random->Next();
83       points->SetPoint(i, coords);
84       data->SetValue(i, totalNumberOfPoints - i - 1);
85       sdata->SetValue(i, ss.str());
86     }
87   }
88 
89   // attach initial ids and process ids
90   vtkNew<vtkIdFilter> idFilter;
91   idFilter->SetInputData(inputPoly);
92   idFilter->SetIdsArrayName("OriginalId");
93   vtkNew<vtkProcessIdScalars> procIdScalars;
94   procIdScalars->SetInputConnection(idFilter->GetOutputPort());
95   procIdScalars->Update();
96   procIdScalars->GetOutput()->GetPointData()->GetArray("ProcessId")->SetName("OriginalProcessId");
97 
98   // distribute the points over the processors
99   vtkNew<vtkDistributedPointCloudFilter> filter;
100   filter->SetInputConnection(procIdScalars->GetOutputPort());
101 
102   // attach new process ids
103   vtkNew<vtkProcessIdScalars> outProcIdScalars;
104   outProcIdScalars->SetInputConnection(filter->GetOutputPort());
105   outProcIdScalars->Update();
106   vtkPolyData *outputPoly = vtkPolyData::SafeDownCast(outProcIdScalars->GetOutput());
107 
108   bool error = false;
109   int nbOfLocallyReceivedPoints = outputPoly->GetNumberOfPoints();
110   if (nbOfLocallyReceivedPoints != finalNumberOfPointsPerRank)
111   {
112     cerr << "No point on the node " << rank << "\n";
113     // do not exit here so MPI can end correctly
114     error = true;
115   }
116 
117   if (outputPoly->GetPointData()->GetNumberOfArrays() != 5)
118   {
119     cerr << "Incorrect number of point data arrays on rank " << rank << "\n";
120     error = true;
121   }
122 
123   double bounds[6];
124   outputPoly->GetBounds(bounds);
125   vtkBoundingBox bbox(bounds);
126   if (!bbox.IsValid() || bbox.GetLength(0) == 0. || bbox.GetLength(1) == 0. || bbox.GetLength(2) == 0.)
127   {
128     cerr << "Incorrect bounding box of output points on rank " << rank << "\n";
129     error = true;
130   }
131 
132   vtkMPICommunicator* com = vtkMPICommunicator::SafeDownCast(controller->GetCommunicator());
133   std::vector<int> nbOfReceivedPoints(numberOfProcessors);
134   com->AllGather(&nbOfLocallyReceivedPoints, nbOfReceivedPoints.data(), 1);
135 
136   int totalNumberOfReceivedPoints = 0;
137   for (vtkIdType i = 0; i < numberOfProcessors; i++)
138   {
139     totalNumberOfReceivedPoints += nbOfReceivedPoints[i];
140   }
141 
142   if (totalNumberOfReceivedPoints != totalNumberOfPoints)
143   {
144     cerr << "Wrong total of points: " << totalNumberOfReceivedPoints << " instead of "
145          << totalNumberOfPoints << "\n";
146     cerr << "Rank " << rank << ":";
147     for (int i = 0; i < nbOfLocallyReceivedPoints; i++)
148     {
149       cout << " " << outputPoly->GetPoints()->GetPoint(i)[0];
150     }
151     cerr << endl;
152     error = true;
153   }
154 
155 #ifdef DEBUG_ON
156   vtkNew<vtkXMLPPolyDataWriter> writer;
157   std::stringstream ss;
158   ss << "TestDistributedPointCloudFilter-" << numberOfProcessors << "ranks.pvtp";
159   writer->SetFileName(ss.str().c_str());
160   writer->SetInputData(inputPoly);
161   writer->SetNumberOfPieces(numberOfProcessors);
162   writer->SetStartPiece(rank);
163   writer->SetEndPiece(rank);
164   writer->SetWriteSummaryFile(1);
165   writer->Update();
166 #endif
167 
168   controller->Finalize();
169 
170   return error ? EXIT_FAILURE : EXIT_SUCCESS;
171 }
172