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