TTK
Loading...
Searching...
No Matches
ttkPeriodicGhostsGeneration.cpp
Go to the documentation of this file.
2
3#include <ttkMacros.h>
4#include <ttkUtils.h>
5
7
9 this->setDebugMsgPrefix("PeriodicGhostsGeneration");
10 this->SetNumberOfInputPorts(1);
11 this->SetNumberOfOutputPorts(1);
12#ifdef TTK_ENABLE_MPI
13 hasMPISupport_ = true;
14#endif
15}
16
18 int port, vtkInformation *info) {
19 if(port == 0) {
20 info->Set(vtkDataObject::DATA_TYPE_NAME(), "vtkImageData");
21 return 1;
22 }
23 return 0;
24}
25
27 int port, vtkInformation *info) {
28 if(port == 0) {
29 info->Set(vtkDataObject::DATA_TYPE_NAME(), "vtkImageData");
30 return 1;
31 }
32 return 0;
33}
34
35#ifdef TTK_ENABLE_MPI
36
38 vtkInformation *ttkNotUsed(request),
39 vtkInformationVector **inputVector,
40 vtkInformationVector *ttkNotUsed(outputVector)) {
41 this->ComputeOutputExtent();
42 vtkInformation *inInfo = inputVector[0]->GetInformationObject(0);
43 std::array<int, 6> outExtentWithoutGhost{outExtent_};
44 for(int i = 0; i < 3; i++) {
45 if(std::abs(outExtent_[2 * i] - outExtent_[2 * i + 1]) > spacing_[i] / 10) {
46 if(!(localGlobalBounds_[2 * i].isBound == 1
47 && localGlobalBounds_[2 * i].isBound
48 == localGlobalBounds_[2 * i + 1].isBound)) {
49 outExtentWithoutGhost[2 * i] += 1;
50 outExtentWithoutGhost[2 * i + 1] -= 1;
51 }
52 }
53 }
54 inInfo->Set(vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT(),
55 outExtentWithoutGhost[0], outExtentWithoutGhost[1],
56 outExtentWithoutGhost[2], outExtentWithoutGhost[3],
57 outExtentWithoutGhost[4], outExtentWithoutGhost[5]);
58 return 1;
59}
60
62 vtkInformation *ttkNotUsed(request),
63 vtkInformationVector **inputVectors,
64 vtkInformationVector *outputVector) {
65 vtkInformation *inInfo = inputVectors[0]->GetInformationObject(0);
66 inInfo->Get(
67 vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT(), inExtent_.data());
68 inInfo->Get(vtkDataObject::SPACING(), this->spacing_.data());
69 inInfo->Get(vtkDataObject::ORIGIN(), this->origin_.data());
70 this->ComputeOutputExtent();
71 vtkInformation *outInfo = outputVector->GetInformationObject(0);
72 outInfo->Set(
73 vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT(), outExtent_.data(), 6);
74 outInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT());
75 return 1;
76}
77
78int ttkPeriodicGhostsGeneration::ComputeOutputExtent() {
79
80 // Retrieve the local extent for the current process
81 vtkNew<vtkExtentTranslator> translator;
82 translator->SetWholeExtent(inExtent_.data());
83 translator->SetNumberOfPieces(ttk::MPIsize_);
84 translator->SetPiece(ttk::MPIrank_);
85 translator->PieceToExtent();
86 int localExtent[6];
87 translator->GetExtent(localExtent);
88
89 // Compute the bounds of the local process
90 double bounds[6] = {
91 origin_[0] + localExtent[0] * spacing_[0],
92 origin_[0] + localExtent[1] * spacing_[0],
93 origin_[1] + localExtent[2] * spacing_[1],
94 origin_[1] + localExtent[3] * spacing_[1],
95 origin_[2] + localExtent[4] * spacing_[2],
96 origin_[2] + localExtent[5] * spacing_[2],
97 };
98 // If the extent has never been computed or has changed
99 if(!isOutputExtentComputed_
100 || std::abs(boundsWithoutGhosts_[0] - bounds[0]) > spacing_[0] / 10
101 || std::abs(boundsWithoutGhosts_[1] - bounds[1]) > spacing_[0] / 10
102 || std::abs(boundsWithoutGhosts_[2] - bounds[2]) > spacing_[1] / 10
103 || std::abs(boundsWithoutGhosts_[3] - bounds[3]) > spacing_[1] / 10
104 || std::abs(boundsWithoutGhosts_[4] - bounds[4]) > spacing_[2] / 10
105 || std::abs(boundsWithoutGhosts_[5] - bounds[5]) > spacing_[2] / 10) {
106 boundsWithoutGhosts_
107 = {bounds[0], bounds[1], bounds[2], bounds[3], bounds[4], bounds[5]};
108 // re-order tempGlobalBounds
109 globalBounds_ = {origin_[0] + inExtent_[0] * spacing_[0],
110 origin_[0] + inExtent_[1] * spacing_[0],
111 origin_[1] + inExtent_[2] * spacing_[1],
112 origin_[1] + inExtent_[3] * spacing_[1],
113 origin_[2] + inExtent_[4] * spacing_[2],
114 origin_[2] + inExtent_[5] * spacing_[2]};
115
116 std::map<int, int> neighborsToId{};
117 ttk::preconditionNeighborsUsingBoundingBox(
118 bounds, neighbors_, neighborsToId);
119
120 // Compute the 1D boundary matches of local process: if the process is on
121 // the global boundary, then the center of its other dimension is computed
122
123 for(int i = 0; i < 2; i++) {
124 if(std::abs(globalBounds_[i] - boundsWithoutGhosts_[i])
125 < spacing_[0] / 10) {
126 localGlobalBounds_[i].isBound = 1;
127 localGlobalBounds_[i].x = boundsWithoutGhosts_[i];
128 localGlobalBounds_[i].y
129 = (boundsWithoutGhosts_[2] + boundsWithoutGhosts_[3]) / 2;
130 localGlobalBounds_[i].z
131 = (boundsWithoutGhosts_[4] + boundsWithoutGhosts_[5]) / 2;
132 }
133 }
134
135 for(int i = 0; i < 2; i++) {
136 if(std::abs(globalBounds_[2 + i] - boundsWithoutGhosts_[2 + i])
137 < spacing_[1] / 10) {
138 localGlobalBounds_[2 + i].isBound = 1;
139 localGlobalBounds_[2 + i].x
140 = (boundsWithoutGhosts_[0] + boundsWithoutGhosts_[1]) / 2;
141 localGlobalBounds_[2 + i].y = boundsWithoutGhosts_[2 + i];
142 localGlobalBounds_[2 + i].z
143 = (boundsWithoutGhosts_[4] + boundsWithoutGhosts_[5]) / 2;
144 }
145 }
146
147 for(int i = 0; i < 2; i++) {
148 if(std::abs(globalBounds_[4 + i] - boundsWithoutGhosts_[4 + i])
149 < spacing_[2] / 10) {
150 localGlobalBounds_[4 + i].isBound = 1;
151 localGlobalBounds_[4 + i].x
152 = (boundsWithoutGhosts_[0] + boundsWithoutGhosts_[1]) / 2;
153 localGlobalBounds_[4 + i].y
154 = (boundsWithoutGhosts_[2] + boundsWithoutGhosts_[3]) / 2;
155 localGlobalBounds_[4 + i].z = boundsWithoutGhosts_[4 + i];
156 }
157 }
158
159 // The output extent is computed based on the previous results
160 // It is inflated or deflated for boundaries that will contain
161 // periodic ghosts
162 for(int i = 0; i < 3; i++) {
163 if(!(localGlobalBounds_[2 * i].isBound == 1
164 && localGlobalBounds_[2 * i + 1].isBound == 1)) {
165 outExtent_[2 * i] = inExtent_[2 * i] - 1;
166 outExtent_[2 * i + 1] = inExtent_[2 * i + 1] + 1;
167 } else {
168 outExtent_[2 * i] = inExtent_[2 * i];
169 outExtent_[2 * i + 1] = inExtent_[2 * i + 1];
170 }
171 }
172
173 // We compute here if the global boundary has periodic ghosts. In case a
174 // process has both a low and high (e.g. w low and x high) global boundary,
175 // the boundary does not have periodic ghosts
176 for(int i = 0; i < 3; i++) {
177 if(localGlobalBounds_[2 * i].isBound == 1
178 && localGlobalBounds_[2 * i].isBound
179 == localGlobalBounds_[2 * i + 1].isBound) {
180 isBoundaryPeriodic_[2 * i] = 0;
181 isBoundaryPeriodic_[2 * i + 1] = 0;
182 } else {
183 isBoundaryPeriodic_[2 * i] = localGlobalBounds_[2 * i].isBound;
184 isBoundaryPeriodic_[2 * i + 1] = localGlobalBounds_[2 * i + 1].isBound;
185 }
186 }
187 isOutputExtentComputed_ = true;
188 }
189 return 1;
190};
191
192template <int matchesSize, int metaDataSize>
193int ttkPeriodicGhostsGeneration::MarshalAndSendRecv(
194 vtkImageData *imageIn,
195 std::vector<std::vector<vtkSmartPointer<vtkCharArray>>> &charArrayBoundaries,
196 std::vector<std::vector<std::array<ttk::SimplexId, metaDataSize>>>
197 &charArrayBoundariesMetaData,
198 std::vector<std::array<ttk::SimplexId, matchesSize>> &matches,
199 std::vector<vtkSmartPointer<vtkCharArray>> &charArrayBoundariesReceived,
200 std::vector<std::array<ttk::SimplexId, metaDataSize>>
201 &charArrayBoundariesMetaDataReceived,
202 int dim) {
203
204 int *default_VOI = imageIn->GetExtent();
205 std::array<ttk::SimplexId, 6> VOI;
206 ttk::SimplexId matchesNumber = matches.size();
207 std::vector<std::vector<int>> additionalNeighbors(
208 ttk::MPIsize_, std::vector<int>{});
209 for(int i = 0; i < matchesNumber; i++) {
210 // Extent of the vtkImageData to extract
211 VOI = {default_VOI[0], default_VOI[1], default_VOI[2],
212 default_VOI[3], default_VOI[4], default_VOI[5]};
213 // It is modified based on the direction of the match
214 for(int k = 1; k <= dim; k++) {
215 if(matches[i][k] % 2 == 0) {
216 VOI[matches[i][k] + 1] = VOI[matches[i][k]];
217 } else {
218 VOI[matches[i][k] - 1] = VOI[matches[i][k]];
219 }
220 }
221 // The intersections between the extracted extent and the neighbors
222 // are computed to keep track of the new neighbors
223 for(const auto neigh : neighbors_) {
224 if(neigh != matches[i][0]) {
225 if(!(neighborVertexBBoxes_[neigh][0] == 0
226 && neighborVertexBBoxes_[neigh][1] == 0
227 && neighborVertexBBoxes_[neigh][2] == 0
228 && neighborVertexBBoxes_[neigh][3] == 0
229 && neighborVertexBBoxes_[neigh][4] == 0
230 && neighborVertexBBoxes_[neigh][5] == 0)) {
231 if(VOI[0] - inExtent_[0] <= neighborVertexBBoxes_[neigh][1]
232 && VOI[1] - inExtent_[0] >= neighborVertexBBoxes_[neigh][0]
233 && VOI[2] - inExtent_[2] <= neighborVertexBBoxes_[neigh][3]
234 && VOI[3] - inExtent_[2] >= neighborVertexBBoxes_[neigh][2]
235 && VOI[4] - inExtent_[4] <= neighborVertexBBoxes_[neigh][5]
236 && VOI[5] - inExtent_[4] >= neighborVertexBBoxes_[neigh][4]) {
237 if(std::find(additionalNeighbors[matches[i][0]].begin(),
238 additionalNeighbors[matches[i][0]].end(), neigh)
239 == additionalNeighbors[matches[i][0]].end()) {
240 additionalNeighbors[matches[i][0]].push_back(neigh);
241 }
242 }
243 }
244 }
245 }
246 // The vtkImageData is extracted
249 extractVOI->SetInputData(imageIn);
250 extractVOI->SetVOI(VOI[0], VOI[1], VOI[2], VOI[3], VOI[4], VOI[5]);
251 extractVOI->Update();
252 vtkSmartPointer<vtkImageData> extracted = extractVOI->GetOutput();
254 // The vtkImageData is converted to a vtkCharArray (for sending)
255 if(vtkCommunicator::MarshalDataObject(extracted, buffer) == 0) {
256 printErr("Marshalling failed!");
257 };
258 charArrayBoundaries[matches[i][0]].emplace_back(buffer);
259 std::array<ttk::SimplexId, metaDataSize> metaData;
260 for(int j = 0; j < metaDataSize; j++) {
261 metaData.at(j) = matches[i][j + 1];
262 }
263 // The type of match is stored as meta data
264 charArrayBoundariesMetaData[matches[i][0]].emplace_back(metaData);
265 }
266
267 ttk::SimplexId recv_size{0};
268 ttk::SimplexId send_size{0};
269 std::array<ttk::SimplexId, metaDataSize + 1> sendMetadata;
270 std::array<ttk::SimplexId, metaDataSize + 1> recvMetaData;
271 // All the extracted image data are exchanged between processes along with
272 // their meta data and their new neighbors
273 for(int i = 0; i < ttk::MPIsize_; i++) {
274 for(int j = 0; j < static_cast<int>(charArrayBoundaries[i].size()); j++) {
275 send_size = charArrayBoundaries[i][j]->GetNumberOfTuples();
276 MPI_Sendrecv(&send_size, 1, ttk::getMPIType(send_size), i, 0, &recv_size,
277 1, ttk::getMPIType(recv_size), i, 0, ttk::MPIcomm_,
278 MPI_STATUS_IGNORE);
281 buffer->SetNumberOfTuples(recv_size);
282 buffer->SetNumberOfComponents(1);
283 char *sendPointer = ttkUtils::GetPointer<char>(charArrayBoundaries[i][j]);
284 char *recvPointer = ttkUtils::GetPointer<char>(buffer);
285 MPI_Sendrecv(sendPointer, send_size, MPI_CHAR, i, 0, recvPointer,
286 recv_size, MPI_CHAR, i, 0, ttk::MPIcomm_, MPI_STATUS_IGNORE);
287 charArrayBoundariesReceived.emplace_back(buffer);
288 std::copy(charArrayBoundariesMetaData[i][j].begin(),
289 charArrayBoundariesMetaData[i][j].begin() + metaDataSize,
290 sendMetadata.begin());
291 sendMetadata.at(metaDataSize) = additionalNeighbors[i].size();
292 MPI_Sendrecv(sendMetadata.data(), metaDataSize + 1,
293 ttk::getMPIType(recv_size), i, 0, recvMetaData.data(),
294 metaDataSize + 1, ttk::getMPIType(recv_size), i, 0,
295 ttk::MPIcomm_, MPI_STATUS_IGNORE);
296 std::array<ttk::SimplexId, metaDataSize> aux;
297 std::copy(recvMetaData.begin(), recvMetaData.end() - 1, aux.begin());
298 charArrayBoundariesMetaDataReceived.emplace_back(aux);
299 std::vector<int> receivedNeighbors(recvMetaData.back());
300 MPI_Sendrecv(additionalNeighbors[i].data(), additionalNeighbors[i].size(),
301 MPI_INTEGER, i, 0, receivedNeighbors.data(),
302 recvMetaData.back(), MPI_INTEGER, i, 0, ttk::MPIcomm_,
303 MPI_STATUS_IGNORE);
304 for(const int neigh : receivedNeighbors) {
305 if(std::find(neighbors_.begin(), neighbors_.end(), neigh)
306 == neighbors_.end()) {
307 neighbors_.push_back(neigh);
308 }
309 }
310 }
311 }
312 return 1;
313}
314
315template <typename boundaryType>
316int ttkPeriodicGhostsGeneration::UnMarshalAndMerge(
317 std::vector<boundaryType> &metaDataReceived,
318 std::vector<vtkSmartPointer<vtkCharArray>> &boundariesReceived,
319 boundaryType direction,
320 int mergeDirection,
321 vtkImageData *mergedImage) {
322 // Search for the right received extracted image data using the meta
323 // data and the boundary direction
324 auto it
325 = std::find(metaDataReceived.begin(), metaDataReceived.end(), direction);
326 if(it != metaDataReceived.end()) {
327 vtkNew<vtkStructuredPoints> id;
328 vtkNew<vtkImageData> aux;
329 // If the right extracted image is found, it is converted back to a
330 // vtkImageData
331 if(vtkCommunicator::UnMarshalDataObject(
332 boundariesReceived[std::distance(metaDataReceived.begin(), it)], id)
333 == 0) {
334 printErr("UnMarshaling failed!");
335 return 0;
336 };
337 // Merged into mergedImage
338 this->MergeImageAndSlice(mergedImage, id, aux, mergeDirection);
339 mergedImage->DeepCopy(aux);
340 }
341 return 1;
342}
343
344template <typename boundaryType>
345int ttkPeriodicGhostsGeneration::UnMarshalAndCopy(
346 std::vector<boundaryType> &metaDataReceived,
347 std::vector<vtkSmartPointer<vtkCharArray>> &boundariesReceived,
348 boundaryType direction,
349 vtkImageData *mergedImage) {
350 // Search for the right received extracted image data using the meta
351 // data and the boundary direction
352 auto it
353 = std::find(metaDataReceived.begin(), metaDataReceived.end(), direction);
354 if(it != metaDataReceived.end()) {
355 vtkNew<vtkStructuredPoints> id;
356 // If the right extracted image is found, it is converted back to a
357 // vtkImageData
358 if(vtkCommunicator::UnMarshalDataObject(
359 boundariesReceived[std::distance(metaDataReceived.begin(), it)], id)
360 == 0) {
361 printErr("UnMarshaling failed!");
362 return 0;
363 };
364 // Copied into mergedImage
365 mergedImage->DeepCopy(id);
366 }
367 return 1;
368}
369
370int ttkPeriodicGhostsGeneration::MergeDataArrays(
371 vtkDataArray *imageArray,
372 vtkDataArray *sliceArray,
373 vtkSmartPointer<vtkDataArray> &currentArray,
374 int direction,
375 int dims[3],
376 unsigned char ghostValue,
377 ttk::SimplexId numberOfSimplices,
378 ttk::SimplexId numberOfTuples) {
379 std::string arrayName(imageArray->GetName());
380
381#ifndef TTK_ENABLE_KAMIKAZE
382 if(!sliceArray) {
383 printErr("Array " + arrayName
384 + " is not present in the Data of the second vtkImageData");
385 return 0;
386 }
387#endif
388 currentArray->SetNumberOfComponents(1);
389 currentArray->SetNumberOfTuples(numberOfSimplices);
390 currentArray->SetName(arrayName.c_str());
391 // Special treatment for ghost arrays
392 if(std::strcmp(currentArray->GetName(), "vtkGhostType") == 0) {
393 if(ghostValue != 0) {
394 sliceArray->SetNumberOfTuples(numberOfTuples);
395 sliceArray->Fill(ghostValue);
396 } else {
397 for(int i = 0; i < numberOfTuples; i++) {
398 if(sliceArray->GetTuple1(i) == 1) {
399 sliceArray->SetTuple1(i, 16);
400 }
401 }
402 }
403 }
404 int sliceCounter = 0;
405 int imageCounter = 0;
406 int counter = 0;
407 // We define the function addSlice based on the direction of the merge
408 std::function<bool(int, int, int, int[3])> addSlice;
409 switch(direction) {
410 case 0:
411 addSlice = [](int x, int ttkNotUsed(y), int ttkNotUsed(z),
412 int ttkNotUsed(dimensions)[3]) { return x == 0; };
413 break;
414 case 1:
415 addSlice = [](int x, int ttkNotUsed(y), int ttkNotUsed(z),
416 int dimensions[3]) { return x == dimensions[0] - 1; };
417 break;
418 case 2:
419 addSlice = [](int ttkNotUsed(x), int y, int ttkNotUsed(z),
420 int ttkNotUsed(dimensions)[3]) { return y == 0; };
421 break;
422 case 3:
423 addSlice = [](int ttkNotUsed(x), int y, int ttkNotUsed(z),
424 int dimensions[3]) { return y == dimensions[1] - 1; };
425 break;
426 case 4:
427 addSlice = [](int ttkNotUsed(x), int ttkNotUsed(y), int z,
428 int ttkNotUsed(dimensions)[3]) { return z == 0; };
429 break;
430 case 5:
431 addSlice = [](int ttkNotUsed(x), int ttkNotUsed(y), int z,
432 int dimensions[3]) { return z == dimensions[2] - 1; };
433 break;
434 }
435
436 // Do the actual merging
437 for(int z = 0; z < dims[2]; z++) {
438 for(int y = 0; y < dims[1]; y++) {
439 for(int x = 0; x < dims[0]; x++) {
440 if(addSlice(x, y, z, dims)) {
441 currentArray->SetTuple1(counter, sliceArray->GetTuple1(sliceCounter));
442 sliceCounter++;
443 } else {
444 currentArray->SetTuple1(counter, imageArray->GetTuple1(imageCounter));
445 imageCounter++;
446 }
447 counter++;
448 }
449 }
450 }
451 return 1;
452}
453
454int ttkPeriodicGhostsGeneration::MergeImageAndSlice(vtkImageData *image,
455 vtkImageData *slice,
456 vtkImageData *mergedImage,
457 int direction) {
458 vtkDataArray *arrayImage = image->GetCellData()->GetArray("Cell Type");
459 vtkDataArray *arraySlice = slice->GetCellData()->GetArray("Cell Type");
460 if(!(arrayImage && arraySlice) && arrayImage) {
461 image->GetCellData()->RemoveArray("Cell Type");
462 }
463 if(!(arrayImage && arraySlice) && arraySlice) {
464 slice->GetCellData()->RemoveArray("Cell Type");
465 }
466
467#ifndef TTK_ENABLE_KAMIKAZE
468 if(image->GetPointData()->GetNumberOfArrays()
469 != slice->GetPointData()->GetNumberOfArrays()) {
470 printErr("The two vtkImageData objects to merge don't have the same number "
471 "of arrays for their Point Data");
472 return 0;
473 }
474
475 if(image->GetCellData()->GetNumberOfArrays()
476 != slice->GetCellData()->GetNumberOfArrays()) {
477 printErr("The two vtkImageData objects to merge don't have the same number "
478 "of arrays for their Cell Data");
479 return 0;
480 }
481#endif
482 mergedImage->SetSpacing(image->GetSpacing());
483 mergedImage->Initialize();
484 int extentImage[6];
485 image->GetExtent(extentImage);
486 if(direction % 2 == 0) {
487 extentImage[direction] -= 1;
488 } else {
489 extentImage[direction] += 1;
490 }
491 mergedImage->SetExtent(extentImage);
492 int dims[3];
493 mergedImage->GetDimensions(dims);
494 int cellDims[3] = {std::max(dims[0] - 1, 1), std::max(dims[1] - 1, 1),
495 std::max(dims[2] - 1, 1)};
496 int numberOfPoints = mergedImage->GetNumberOfPoints();
497 int imageDims[3];
498 image->GetDimensions(imageDims);
499 int sliceDims[3];
500 slice->GetDimensions(sliceDims);
501
502 for(int array = 0; array < image->GetPointData()->GetNumberOfArrays();
503 array++) {
504 vtkDataArray *imageArray = image->GetPointData()->GetArray(array);
505 vtkDataArray *sliceArray
506 = slice->GetPointData()->GetArray(imageArray->GetName());
508 = vtkSmartPointer<vtkDataArray>::Take(imageArray->NewInstance());
509 this->MergeDataArrays(imageArray, sliceArray, currentArray, direction, dims,
510 vtkDataSetAttributes::DUPLICATEPOINT, numberOfPoints,
511 slice->GetNumberOfPoints());
512 mergedImage->GetPointData()->AddArray(currentArray);
513 }
514 int numberOfCells = mergedImage->GetNumberOfCells();
515
516 for(int array = 0; array < image->GetCellData()->GetNumberOfArrays();
517 array++) {
518 vtkDataArray *imageArray = image->GetCellData()->GetArray(array);
519 std::string arrayName(imageArray->GetName());
520 vtkDataArray *sliceArray
521 = slice->GetCellData()->GetArray(arrayName.c_str());
522 if(std::strcmp(arrayName.c_str(), "Cell Type") == 0) {
523 continue;
524 }
526 = vtkSmartPointer<vtkDataArray>::Take(imageArray->NewInstance());
527 // If the direction is low, the new cells created belong to the process
528 if(direction == 0 || direction == 2 || direction == 4) {
529 this->MergeDataArrays(imageArray, sliceArray, currentArray, direction,
530 cellDims, 0, numberOfCells,
531 slice->GetNumberOfCells());
532 } else {
533 // The value of ghost is not 1, as it would make the cell disappear in
534 // Paraview
535 this->MergeDataArrays(imageArray, sliceArray, currentArray, direction,
536 cellDims, vtkDataSetAttributes::EXTERIORCELL,
537 numberOfCells, slice->GetNumberOfCells());
538 }
539
540 mergedImage->GetCellData()->AddArray(currentArray);
541 }
542
543 vtkNew<vtkCharArray> cellTypes;
544 cellTypes->SetName("Cell Type");
545 cellTypes->SetNumberOfTuples(numberOfCells);
546 cellTypes->Fill(image->GetCellType(0));
547 mergedImage->GetCellData()->AddArray(cellTypes);
548
549 return 1;
550};
551
552int ttkPeriodicGhostsGeneration::MPIPeriodicGhostPipelinePreconditioning(
553 vtkImageData *imageIn, vtkImageData *imageOut) {
554
555 if(!ttk::isRunningWithMPI()) {
556 return 0;
557 }
558
559 auto other = [](ttk::SimplexId i) {
560 if(i % 2 == 1) {
561 return i - 1;
562 }
563 return i + 1;
564 };
565
566 int dimensionality = imageIn->GetDataDimension();
567
568 ttk::RegularGridTriangulation *triangulation
570 neighborVertexBBoxes_ = triangulation->getNeighborVertexBBoxes();
571
572 // Computation of the order in which the merging of the ghosts should be done
573 // The first dimension is the longest
574 std::map<int, ttk::SimplexId> dimensionOrder;
575 dimensionOrder[0] = inExtent_[1] - inExtent_[0];
576 dimensionOrder[2] = inExtent_[3] - inExtent_[2];
577 dimensionOrder[4] = inExtent_[5] - inExtent_[4];
578
579 int firstDim{-1};
580 int secondDim{-1};
581 int thirdDim{-1};
582
583 auto maxMap = [](const std::pair<int, ttk::SimplexId> &p1,
584 const std::pair<int, ttk::SimplexId> &p2) {
585 return p1.second <= p2.second;
586 };
587 auto pr = std::max_element(
588 std::begin(dimensionOrder), std::end(dimensionOrder), maxMap);
589 firstDim = pr->first;
590 dimensionOrder[pr->first] = std::round(pr->second / 2);
591 pr = std::max_element(
592 std::begin(dimensionOrder), std::end(dimensionOrder), maxMap);
593 if(firstDim != pr->first) {
594 secondDim = pr->first;
595 }
596 dimensionOrder[pr->first] = std::round(pr->second / 2);
597
598 pr = std::max_element(
599 std::begin(dimensionOrder), std::end(dimensionOrder), maxMap);
600 if(secondDim == -1) {
601 secondDim = pr->first;
602 }
603 thirdDim = 6 - (firstDim + secondDim);
604
605 this->ComputeOutputExtent();
606
607 // Preparation of the MPI type for the matches
608 MPI_Datatype partialGlobalBoundMPI;
609 std::vector<ttk::periodicGhosts::partialGlobalBound> allLocalGlobalBounds(
611 MPI_Datatype types[]
612 = {MPI_UNSIGNED_CHAR, MPI_DOUBLE, MPI_DOUBLE, MPI_DOUBLE};
613 int lengths[] = {1, 1, 1, 1};
614 const long int mpi_offsets[]
615 = {offsetof(ttk::periodicGhosts::partialGlobalBound, isBound),
619 MPI_Type_create_struct(
620 4, lengths, mpi_offsets, types, &partialGlobalBoundMPI);
621 MPI_Type_commit(&partialGlobalBoundMPI);
622
623 MPI_Allgather(localGlobalBounds_.data(), 6, partialGlobalBoundMPI,
624 allLocalGlobalBounds.data(), 6, partialGlobalBoundMPI,
625 ttk::MPIcomm_);
626
627 // Computation of the 1D matches
628 std::vector<std::array<ttk::SimplexId, 3>> matches;
629 for(int i = 0; i < ttk::MPIsize_; i++) {
630 if(i != ttk::MPIrank_) {
631 for(int j = 0; j < 6; j++) {
632 bool isIn = false;
633 if(!(localGlobalBounds_[other(j)].isBound != 0
634 && localGlobalBounds_[j].isBound != 0)) {
635 if(localGlobalBounds_[other(j)].isBound != 0
636 && allLocalGlobalBounds[i * 6 + j].isBound != 0) {
637 if(0 <= j && j <= 1) {
638 isIn
639 = (boundsWithoutGhosts_[2] <= allLocalGlobalBounds[i * 6 + j].y
640 && boundsWithoutGhosts_[3]
641 >= allLocalGlobalBounds[i * 6 + j].y)
642 && (boundsWithoutGhosts_[4]
643 <= allLocalGlobalBounds[i * 6 + j].z
644 && boundsWithoutGhosts_[5]
645 >= allLocalGlobalBounds[i * 6 + j].z);
646 }
647 if(2 <= j && j <= 3) {
648 isIn
649 = (boundsWithoutGhosts_[0] <= allLocalGlobalBounds[i * 6 + j].x
650 && boundsWithoutGhosts_[1]
651 >= allLocalGlobalBounds[i * 6 + j].x)
652 && (boundsWithoutGhosts_[4]
653 <= allLocalGlobalBounds[i * 6 + j].z
654 && boundsWithoutGhosts_[5]
655 >= allLocalGlobalBounds[i * 6 + j].z);
656 }
657 if(4 <= j && j <= 5) {
658 isIn
659 = (boundsWithoutGhosts_[0] <= allLocalGlobalBounds[i * 6 + j].x
660 && boundsWithoutGhosts_[1]
661 >= allLocalGlobalBounds[i * 6 + j].x)
662 && (boundsWithoutGhosts_[2]
663 <= allLocalGlobalBounds[i * 6 + j].y
664 && boundsWithoutGhosts_[3]
665 >= allLocalGlobalBounds[i * 6 + j].y);
666 }
667 if(isIn) {
668 matches.emplace_back(
669 std::array<ttk::SimplexId, 3>{i, other(j), j});
670 if(std::find(neighbors_.begin(), neighbors_.end(), i)
671 == neighbors_.end()) {
672 neighbors_.push_back(i);
673 }
674 }
675 }
676 }
677 }
678 }
679 }
680 // Computation of the 2D matches
681 std::vector<std::array<ttk::SimplexId, 2>> local2DBounds;
682 std::vector<std::array<ttk::SimplexId, 3>> matches_2D;
683 if(dimensionality >= 2) {
684 for(int i = 0; i < 4; i++) {
685 for(int j = i + 1; j < 6; j++) {
686 if((abs(i - j) == 1 && i % 2 == 1) || abs(i - j) >= 2) {
687 if((localGlobalBounds_[i].isBound != 0
688 && localGlobalBounds_[j].isBound != 0)
689 && !(localGlobalBounds_[other(i)].isBound != 0
690 && localGlobalBounds_[other(j)].isBound != 0)) {
691 local2DBounds.emplace_back(std::array<ttk::SimplexId, 2>{i, j});
692 }
693 }
694 }
695 }
696
697 for(int i = 0; i < static_cast<int>(local2DBounds.size()); i++) {
698 for(int j = 0; j < ttk::MPIsize_; j++) {
699 if(j != ttk::MPIrank_) {
700 bool isIn = false;
701 if((allLocalGlobalBounds[j * 6 + other(local2DBounds[i][0])].isBound
702 != 0)
703 && (allLocalGlobalBounds[j * 6 + other(local2DBounds[i][1])]
704 .isBound
705 != 0)) {
706 ttk::SimplexId dirs[2]
707 = {other(local2DBounds[i][0]), other(local2DBounds[i][1])};
708 std::sort(dirs, dirs + 2);
709 if((dirs[0] < 2 && dirs[1] >= 2 && dirs[1] < 4)) {
710 isIn = (boundsWithoutGhosts_[4]
711 <= allLocalGlobalBounds[j * 6 + dirs[0]].z
712 && boundsWithoutGhosts_[5]
713 >= allLocalGlobalBounds[j * 6 + dirs[0]].z);
714 }
715 if((dirs[0] < 2 && dirs[1] >= 4)) {
716 isIn = (boundsWithoutGhosts_[2]
717 <= allLocalGlobalBounds[j * 6 + dirs[0]].y
718 && boundsWithoutGhosts_[3]
719 >= allLocalGlobalBounds[j * 6 + dirs[0]].y);
720 }
721 if((dirs[0] >= 2 && dirs[0] < 4 && dirs[1] >= 4)) {
722 isIn = (boundsWithoutGhosts_[0]
723 <= allLocalGlobalBounds[j * 6 + dirs[0]].x
724 && boundsWithoutGhosts_[1]
725 >= allLocalGlobalBounds[j * 6 + dirs[0]].x);
726 }
727 if(isIn) {
728 matches_2D.emplace_back(std::array<ttk::SimplexId, 3>{
729 j, local2DBounds[i][0], local2DBounds[i][1]});
730 if(std::find(neighbors_.begin(), neighbors_.end(), j)
731 == neighbors_.end()) {
732 neighbors_.push_back(j);
733 }
734 }
735 }
736 }
737 }
738 }
739 }
740 // Computation of the 3D matches
741 std::vector<std::array<ttk::SimplexId, 3>> local3DBounds;
742 std::vector<std::array<ttk::SimplexId, 4>> matches_3D;
743 if(dimensionality == 3) {
744 for(int i = 0; i < 2; i++) {
745 for(int j = 0; j < 2; j++) {
746 for(int k = 0; k < 2; k++) {
747 if(localGlobalBounds_[i].isBound != 0
748 && localGlobalBounds_[2 + j].isBound != 0
749 && localGlobalBounds_[4 + k].isBound != 0) {
750 local3DBounds.emplace_back(
751 std::array<ttk::SimplexId, 3>{i, 2 + j, 4 + k});
752 }
753 }
754 }
755 }
756 for(int i = 0; i < static_cast<int>(local3DBounds.size()); i++) {
757 for(int j = 0; j < ttk::MPIsize_; j++) {
758 if(j != ttk::MPIrank_) {
759 if((allLocalGlobalBounds[j * 6 + other(local3DBounds[i][0])].isBound
760 != 0)
761 && (allLocalGlobalBounds[j * 6 + other(local3DBounds[i][1])]
762 .isBound
763 != 0)
764 && (allLocalGlobalBounds[j * 6 + other(local3DBounds[i][2])]
765 .isBound
766 != 0)) {
767 matches_3D.emplace_back(std::array<ttk::SimplexId, 4>{
768 j, local3DBounds[i][0], local3DBounds[i][1],
769 local3DBounds[i][2]});
770 if(std::find(neighbors_.begin(), neighbors_.end(), j)
771 == neighbors_.end()) {
772 neighbors_.push_back(j);
773 }
774 }
775 }
776 }
777 }
778 }
779 // Now, extract ImageData for 1D boundaries
780 std::vector<std::vector<vtkSmartPointer<vtkCharArray>>> charArrayBoundaries(
782 std::vector<std::vector<std::array<ttk::SimplexId, 1>>>
783 charArrayBoundariesMetaData(ttk::MPIsize_);
784 std::vector<vtkSmartPointer<vtkCharArray>> charArrayBoundariesReceived;
785 std::vector<std::array<ttk::SimplexId, 1>>
786 charArrayBoundariesMetaDataReceived;
787 if(this->MarshalAndSendRecv<3, 1>(
788 imageIn, charArrayBoundaries, charArrayBoundariesMetaData, matches,
789 charArrayBoundariesReceived, charArrayBoundariesMetaDataReceived, 1)
790 == 0) {
791 printErr("Error occurred when marshalling messages");
792 return 0;
793 }
794
795 // Extract 2D boundaries
796 std::vector<std::vector<vtkSmartPointer<vtkCharArray>>> charArray2DBoundaries(
798 std::vector<std::vector<std::array<ttk::SimplexId, 2>>>
799 charArray2DBoundariesMetaData(ttk::MPIsize_);
800 std::vector<vtkSmartPointer<vtkCharArray>> charArray2DBoundariesReceived;
801 std::vector<std::array<ttk::SimplexId, 2>>
802 charArray2DBoundariesMetaDataReceived;
803 if(dimensionality >= 2) {
804 if(this->MarshalAndSendRecv<3, 2>(imageIn, charArray2DBoundaries,
805 charArray2DBoundariesMetaData, matches_2D,
806 charArray2DBoundariesReceived,
807 charArray2DBoundariesMetaDataReceived, 2)
808 == 0) {
809 return 0;
810 }
811 }
812 // Now, same for 3D boundaries
813 std::vector<std::vector<vtkSmartPointer<vtkCharArray>>> charArray3DBoundaries(
815 std::vector<std::vector<std::array<ttk::SimplexId, 3>>>
816 charArray3DBoundariesMetaData(ttk::MPIsize_);
817 std::vector<vtkSmartPointer<vtkCharArray>> charArray3DBoundariesReceived;
818 std::vector<std::array<ttk::SimplexId, 3>>
819 charArray3DBoundariesMetaDataReceived;
820 if(dimensionality == 3) {
821 if(this->MarshalAndSendRecv<4, 3>(imageIn, charArray3DBoundaries,
822 charArray3DBoundariesMetaData, matches_3D,
823 charArray3DBoundariesReceived,
824 charArray3DBoundariesMetaDataReceived, 3)
825 == 0) {
826 return 0;
827 }
828 }
829 imageOut->DeepCopy(imageIn);
830
831 // Merge in the first direction (low and high)
832 for(int dir = firstDim; dir < firstDim + 2; dir++) {
833 if(this->UnMarshalAndMerge<std::array<ttk::SimplexId, 1>>(
834 charArrayBoundariesMetaDataReceived, charArrayBoundariesReceived,
835 std::array<ttk::SimplexId, 1>{other(dir)}, dir, imageOut)
836 == 0) {
837 return 0;
838 }
839 }
840 if(dimensionality >= 2) {
841 // Merge in the second direction
842 for(int dir = secondDim; dir < secondDim + 2; dir++) {
845 if(this->UnMarshalAndCopy<std::array<ttk::SimplexId, 1>>(
846 charArrayBoundariesMetaDataReceived, charArrayBoundariesReceived,
847 std::array<ttk::SimplexId, 1>{other(dir)}, mergedImage)
848 == 0) {
849 return 0;
850 }
851 if(mergedImage->GetNumberOfPoints() > 0) {
852 for(int dir_2D = firstDim; dir_2D < firstDim + 2; dir_2D++) {
853 std::array<ttk::SimplexId, 2> merge_dir{
854 std::min(other(dir), other(dir_2D)),
855 std::max(other(dir_2D), other(dir))};
856 if(this->UnMarshalAndMerge<std::array<ttk::SimplexId, 2>>(
857 charArray2DBoundariesMetaDataReceived,
858 charArray2DBoundariesReceived, merge_dir, dir_2D, mergedImage)
859 == 0) {
860 return 0;
861 }
862 }
865 if(this->MergeImageAndSlice(imageOut, mergedImage, aux, dir) == 0) {
866 return 0;
867 }
868 imageOut->DeepCopy(aux);
869 }
870 }
871 }
872 if(dimensionality == 3) {
873 // Merge in the third direction
874 for(int dir = thirdDim; dir < thirdDim + 2; dir++) {
875 vtkNew<vtkImageData> mergedImage1;
876 if(this->UnMarshalAndCopy<std::array<ttk::SimplexId, 1>>(
877 charArrayBoundariesMetaDataReceived, charArrayBoundariesReceived,
878 std::array<ttk::SimplexId, 1>{other(dir)}, mergedImage1)
879 == 0) {
880 return 0;
881 }
882 if(mergedImage1->GetNumberOfPoints() > 0) {
883 for(int dir_2D = firstDim; dir_2D < firstDim + 2; dir_2D++) {
884 std::array<ttk::SimplexId, 2> merge_dir{
885 std::min(other(dir), other(dir_2D)),
886 std::max(other(dir_2D), other(dir))};
887 if(this->UnMarshalAndMerge<std::array<ttk::SimplexId, 2>>(
888 charArray2DBoundariesMetaDataReceived,
889 charArray2DBoundariesReceived, merge_dir, dir_2D, mergedImage1)
890 == 0) {
891 return 0;
892 }
893 }
894 for(int dir_2D = secondDim; dir_2D < secondDim + 2; dir_2D++) {
895 vtkNew<vtkImageData> mergedImage2;
896 std::array<ttk::SimplexId, 2> merge_dir_2D{
897 std::min(other(dir), other(dir_2D)),
898 std::max(other(dir_2D), other(dir))};
899 if(this->UnMarshalAndCopy<std::array<ttk::SimplexId, 2>>(
900 charArray2DBoundariesMetaDataReceived,
901 charArray2DBoundariesReceived, merge_dir_2D, mergedImage2)
902 == 0) {
903 return 0;
904 }
905 if(mergedImage2->GetNumberOfPoints() > 0) {
906 for(int dir_3D = firstDim; dir_3D < firstDim + 2; dir_3D++) {
907 std::array<ttk::SimplexId, 3> merge_dir_3D{
908 other(dir), other(dir_2D), other(dir_3D)};
909 std::sort(merge_dir_3D.begin(), merge_dir_3D.end());
910 if(this->UnMarshalAndMerge<std::array<ttk::SimplexId, 3>>(
911 charArray3DBoundariesMetaDataReceived,
912 charArray3DBoundariesReceived, merge_dir_3D, dir_3D,
913 mergedImage2)
914 == 0) {
915 return 0;
916 }
917 }
918 vtkNew<vtkImageData> aux;
919 if(this->MergeImageAndSlice(mergedImage1, mergedImage2, aux, dir_2D)
920 == 0) {
921 return 0;
922 }
923 mergedImage1->DeepCopy(aux);
924 }
925 }
926 if(mergedImage1->GetNumberOfPoints() > 0) {
927 vtkNew<vtkImageData> aux;
928 if(this->MergeImageAndSlice(imageOut, mergedImage1, aux, dir) == 0) {
929 return 0;
930 }
931 imageOut->DeepCopy(aux);
932 }
933 }
934 }
935 }
936
937 int nbArrays = imageOut->GetPointData()->GetNumberOfArrays();
938 for(int i = nbArrays - 1; i >= 0; i--) {
939 vtkDataArray *array = imageOut->GetPointData()->GetArray(i);
940 std::string arrayName = std::string(array->GetName());
941 if(arrayName.rfind("_Order") == (arrayName.size() - 6)) {
942 imageOut->GetPointData()->RemoveArray(i);
943 }
944 }
945 return 1;
946};
947
948#endif
950 vtkInformation *ttkNotUsed(request),
951 vtkInformationVector **inputVector,
952 vtkInformationVector *outputVector) {
953
954 vtkImageData *imageIn = vtkImageData::GetData(inputVector[0]);
955 vtkImageData *imageOut = vtkImageData::GetData(outputVector);
956#ifdef TTK_ENABLE_MPI
957 this->MPIPeriodicGhostPipelinePreconditioning(imageIn, imageOut);
958#else
959 imageOut->ShallowCopy(imageIn);
960#endif
961
962 // return success
963 return 1;
964};
#define ttkNotUsed(x)
Mark function/method parameters that are not used in the function body at all.
Definition BaseClass.h:47
virtual int RequestInformation(vtkInformation *ttkNotUsed(request), vtkInformationVector **ttkNotUsed(inputVectors), vtkInformationVector *ttkNotUsed(outputVector))
ttk::Triangulation * GetTriangulation(vtkDataSet *dataSet)
virtual int RequestUpdateExtent(vtkInformation *ttkNotUsed(request), vtkInformationVector **ttkNotUsed(inputVectors), vtkInformationVector *ttkNotUsed(outputVector))
TTK VTK-filter that generates an outside ghost layer for periodic implicit grids when used in a distr...
int FillOutputPortInformation(int port, vtkInformation *info) override
int RequestData(vtkInformation *request, vtkInformationVector **inputVector, vtkInformationVector *outputVector) override
int FillInputPortInformation(int port, vtkInformation *info) override
void setDebugMsgPrefix(const std::string &prefix)
Definition Debug.h:364
int printErr(const std::string &msg, const debug::LineMode &lineMode=debug::LineMode::NEW, std::ostream &stream=std::cerr) const
Definition Debug.h:149
RegularGridTriangulation is an abstract subclass of ttk::AbstractTriangulation that exposes a common ...
COMMON_EXPORTS int MPIsize_
Definition BaseClass.cpp:10
COMMON_EXPORTS int MPIrank_
Definition BaseClass.cpp:9
int SimplexId
Identifier type for simplices of any dimension.
Definition DataTypes.h:22
T end(std::pair< T, T > &p)
Definition ripserpy.cpp:472
T begin(std::pair< T, T > &p)
Definition ripserpy.cpp:468
vtkStandardNewMacro(ttkPeriodicGhostsGeneration)