1 /*=========================================================================
2 
3   Program:   Visualization Toolkit
4   Module:    vtkOverlappingCellsDetector.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 // Hide VTK_DEPRECATED_IN_9_1_0() warning for this class
17 #define VTK_DEPRECATION_LEVEL 0
18 
19 #include "vtkOverlappingCellsDetector.h"
20 
21 #include "vtkAbstractPointLocator.h"
22 #include "vtkBoundingBox.h"
23 #include "vtkCellData.h"
24 #include "vtkCompositeDataIterator.h"
25 #include "vtkCompositeDataSet.h"
26 #include "vtkDIYExplicitAssigner.h"
27 #include "vtkDIYUtilities.h"
28 #include "vtkDataArrayRange.h"
29 #include "vtkDataSet.h"
30 #include "vtkDoubleArray.h"
31 #include "vtkGenericCell.h"
32 #include "vtkIdList.h"
33 #include "vtkIdTypeArray.h"
34 #include "vtkInformation.h"
35 #include "vtkInformationVector.h"
36 #include "vtkKdTreePointLocator.h"
37 #include "vtkLogger.h"
38 #include "vtkMultiBlockDataSet.h"
39 #include "vtkMultiProcessController.h"
40 #include "vtkNew.h"
41 #include "vtkObjectFactory.h"
42 #include "vtkPartitionedDataSet.h"
43 #include "vtkPointData.h"
44 #include "vtkPointSet.h"
45 #include "vtkPoints.h"
46 #include "vtkPolyData.h"
47 #include "vtkSmartPointer.h"
48 #include "vtkStaticPointLocator2D.h"
49 #include "vtkStreamingDemandDrivenPipeline.h"
50 #include "vtkUnsignedCharArray.h"
51 #include "vtkUnstructuredGrid.h"
52 
53 #include <algorithm>
54 #include <cassert>
55 #include <map>
56 #include <set>
57 #include <tuple>
58 #include <unordered_map>
59 #include <vector>
60 
61 // clang-format off
62 #include "vtk_diy2.h"
63 #include VTK_DIY2(diy/mpi.hpp)
64 #include VTK_DIY2(diy/master.hpp)
65 #include VTK_DIY2(diy/link.hpp)
66 #include VTK_DIY2(diy/reduce-operations.hpp)
67 #include VTK_DIY2(diy/assigner.hpp)
68 // clang-format on
69 
70 namespace
71 {
72 constexpr char SPHERE_RADIUS_ARRAY_NAME[21] = "SphereRadius";
73 constexpr char ID_MAP_TO_ORIGIN_DATASET_IDS_NAME[32] = "IdMapToOriginDataSetIds";
74 
75 //----------------------------------------------------------------------------
ComputeEpsilon(const vtkBoundingBox & boundingBox)76 double ComputeEpsilon(const vtkBoundingBox& boundingBox)
77 {
78   const double *minPoint = boundingBox.GetMinPoint(), *maxPoint = boundingBox.GetMaxPoint();
79   double max = std::max({ std::fabs(minPoint[0]), std::fabs(minPoint[1]), std::fabs(minPoint[2]),
80     std::fabs(maxPoint[0]), std::fabs(maxPoint[1]), std::fabs(maxPoint[2]) });
81 
82   // Factor 100.0 controls the angular resolution w.r.t. world axis. With this
83   // set up, angles between the skrinking direction and a world axis that are
84   // below asin(0.01) = 0.6 degrees do not deviate from the axis.
85   return 100.0 * std::max(std::sqrt(VTK_DBL_MIN), VTK_DBL_EPSILON * max);
86 }
87 
88 //----------------------------------------------------------------------------
89 // For each cell of the input dataset, returns a point cloud such that each
90 // point of this point cloud maps to the center of the bounding box of the cell
91 // of same id in the input dataset.
92 // Bounding boxes are also computed and returned by reference so they are
93 // computed only once in this filter.
ConvertCellsToBoundingSpheres(vtkDataSet * ds,std::vector<vtkBoundingBox> & bboxes)94 vtkPointSet* ConvertCellsToBoundingSpheres(vtkDataSet* ds, std::vector<vtkBoundingBox>& bboxes)
95 {
96   vtkIdType size = ds->GetNumberOfCells();
97 
98   auto pointCloud = vtkPolyData::New();
99   vtkNew<vtkPoints> points;
100   points->SetNumberOfPoints(size);
101   pointCloud->SetPoints(points);
102 
103   bboxes.reserve(size);
104 
105   vtkNew<vtkDoubleArray> SphereRadiusArray;
106   SphereRadiusArray->SetName(SPHERE_RADIUS_ARRAY_NAME);
107   SphereRadiusArray->SetNumberOfComponents(1);
108   SphereRadiusArray->SetNumberOfTuples(size);
109 
110   for (vtkIdType id = 0; id < size; ++id)
111   {
112     vtkCell* cell = ds->GetCell(id);
113     bboxes.emplace_back(ds->GetBounds());
114     double center[3];
115     double radius2 = cell->ComputeBoundingSphere(center);
116     SphereRadiusArray->SetValue(id, std::sqrt(radius2));
117     points->SetPoint(id, center);
118   }
119 
120   pointCloud->GetPointData()->AddArray(SphereRadiusArray);
121 
122   return pointCloud;
123 }
124 
125 //----------------------------------------------------------------------------
126 // Given an input pointCloud computed using ConvertCellsToBoundingSpheres,
127 // shared boundingBoxes of each input block, and the input source of local block,
128 // this method creates one vtkUnstructuredGrid for each block containing cells
129 // from source overlapping the bounding box of corresponding block.
130 // If no cells intersect, there is no allocation at corresponding global id
131 // in the returned map.
132 // The output vtkUnstructuredGrid holds the original cell id from source.
133 // This information is used later to figure out who intersected who in the last
134 // step of this filter.
ExtractOverlappingCellCandidateByProcess(vtkPointSet * pointCloud,const std::map<int,vtkBoundingBox> & boundingBoxes,vtkPointSet * source,const std::vector<vtkBoundingBox> & cellBoundingBoxes)135 std::map<int, vtkSmartPointer<vtkUnstructuredGrid>> ExtractOverlappingCellCandidateByProcess(
136   vtkPointSet* pointCloud, const std::map<int, vtkBoundingBox>& boundingBoxes, vtkPointSet* source,
137   const std::vector<vtkBoundingBox>& cellBoundingBoxes)
138 {
139   // Each output vtkUnstructuredGrid needs those data objects in order to be constructed.
140   // We tie them together in one tuple to facilitate iterating over blocks.
141   using CellArrayCellTypePointsIdTuple =
142     std::tuple<vtkSmartPointer<vtkCellArray>, vtkSmartPointer<vtkUnsignedCharArray>,
143       vtkSmartPointer<vtkPoints>, vtkSmartPointer<vtkIdTypeArray>>;
144   std::map<int, CellArrayCellTypePointsIdTuple> cactptidList;
145 
146   // Creating the output
147   std::map<int, vtkSmartPointer<vtkUnstructuredGrid>> ugList;
148 
149   if (!source->GetNumberOfCells())
150   {
151     return ugList;
152   }
153 
154   int pointsType = source->GetPoints()->GetDataType();
155 
156   // Initializing new pointers.
157   std::transform(boundingBoxes.cbegin(), boundingBoxes.cend(),
158     std::inserter(cactptidList, cactptidList.begin()),
159     [pointsType](
160       const std::pair<int, vtkBoundingBox> pair) -> std::pair<int, CellArrayCellTypePointsIdTuple> {
161       return std::pair<int, CellArrayCellTypePointsIdTuple>(pair.first,
162         CellArrayCellTypePointsIdTuple(vtkSmartPointer<vtkCellArray>::Take(vtkCellArray::New()),
163           vtkSmartPointer<vtkUnsignedCharArray>::Take(vtkUnsignedCharArray::New()),
164           vtkSmartPointer<vtkPoints>::Take(vtkPoints::New(pointsType)),
165           vtkSmartPointer<vtkIdTypeArray>::Take(vtkIdTypeArray::New())));
166     });
167 
168   vtkDataArray* radiusArray = pointCloud->GetPointData()->GetArray(SPHERE_RADIUS_ARRAY_NAME);
169 
170   // We store new point ids in of the cells in pointIdsList. It is necessary to construct
171   // valid vtkUnstructuredGrid.
172   std::map<int, std::set<vtkIdType>> pointIdsList;
173 
174   for (auto& pair : cactptidList)
175   {
176     int globalId = pair.first;
177     CellArrayCellTypePointsIdTuple& cactptid = pair.second;
178     std::set<vtkIdType>& pointIds = pointIdsList[globalId];
179     vtkBoundingBox bb = boundingBoxes.find(globalId)->second;
180     bb.Inflate(-ComputeEpsilon(bb));
181     for (vtkIdType id = 0; id < pointCloud->GetNumberOfPoints(); ++id)
182     {
183       // For each point in our pointCloud, add corresponding cell from source
184       // if the bounding sphere intersects neighbor's bounding box
185       // and if the cell bounding box intersects with neighbor'bounding box as well.
186       // We test both intersection to narrow candidates and limit MPI communication when possible.
187       double radius = radiusArray->GetTuple1(id);
188       if (bb.IntersectsSphere(pointCloud->GetPoint(id), radius - radius * VTK_DBL_EPSILON) &&
189         bb.Intersects(cellBoundingBoxes[id]))
190       {
191         vtkCell* cell = source->GetCell(id);
192         vtkIdList* idList = cell->GetPointIds();
193         for (vtkIdType i = 0; i < idList->GetNumberOfIds(); ++i)
194         {
195           pointIds.insert(idList->GetId(i));
196         }
197         std::get<0>(cactptid)->InsertNextCell(cell);
198         std::get<1>(cactptid)->InsertNextTuple1(cell->GetCellType());
199         std::get<3>(cactptid)->InsertNextTuple1(id);
200       }
201     }
202   }
203 
204   // For each block, we set correct point ids in each extracted cell, using pointIdsList
205   for (auto& pair : cactptidList)
206   {
207     int globalId = pair.first;
208     CellArrayCellTypePointsIdTuple& cactptid = pair.second;
209     std::set<vtkIdType>& pointIds = pointIdsList[globalId];
210     vtkIdType currentId = 0;
211     vtkPoints* points = std::get<2>(cactptid);
212     points->SetNumberOfPoints(static_cast<vtkIdType>(pointIds.size()));
213 
214     // We create an id inverse map so one can go from new cells to their homologue
215     // in source
216     std::unordered_map<vtkIdType, vtkIdType> inversePointIdsMap;
217     for (vtkIdType pointId : pointIds)
218     {
219       points->SetPoint(currentId, source->GetPoint(pointId));
220       double p[3];
221       points->GetPoint(currentId, p);
222       inversePointIdsMap[pointId] = currentId++;
223     }
224 
225     // We can now replace cell point ids
226     vtkCellArray* ca = std::get<0>(cactptid);
227     vtkNew<vtkIdList> idList;
228     for (vtkIdType cellId = 0; cellId < ca->GetNumberOfCells(); ++cellId)
229     {
230       ca->GetCellAtId(cellId, idList);
231       for (vtkIdType i = 0; i < idList->GetNumberOfIds(); ++i)
232       {
233         idList->SetId(i, inversePointIdsMap[idList->GetId(i)]);
234       }
235       ca->ReplaceCellAtId(cellId, idList);
236     }
237   }
238 
239   for (const auto& pair : cactptidList)
240   {
241     int globalId = pair.first;
242     const CellArrayCellTypePointsIdTuple& cactptid = pair.second;
243     vtkCellArray* ca = std::get<0>(cactptid);
244     if (ca->GetNumberOfCells())
245     {
246       vtkUnsignedCharArray* ct = std::get<1>(cactptid);
247       vtkPoints* pt = std::get<2>(cactptid);
248       vtkIdTypeArray* idArray = std::get<3>(cactptid);
249 
250       vtkNew<vtkUnstructuredGrid> ug;
251       ug->SetCells(ct, ca);
252       ug->SetPoints(pt);
253       idArray->SetName(ID_MAP_TO_ORIGIN_DATASET_IDS_NAME);
254       ug->GetCellData()->AddArray(idArray);
255 
256       ugList[globalId] = ug;
257     }
258   }
259 
260   return ugList;
261 }
262 
263 //----------------------------------------------------------------------------
264 // Block structure used for diy communication
265 struct Block : public diy::Serialization<vtkPointSet*>
266 {
267   /**
268    * Bouding boxes of all spatial neighbor blocks
269    */
270   std::map<int, vtkBoundingBox> BoundingBoxes;
271 
272   /**
273    * DataSets containing potentially intersecting cells sent by other blocks
274    */
275   std::map<int, vtkSmartPointer<vtkDataSet>> DataSets;
276 
277   /**
278    * Map from local dataset cell id to a list of cell ids from other blocks that intersect
279    * local cell.
280    * In other words, if CollisionListMaps[globalId][localCellId][cellId] exists, then
281    * local cell of id localCellId intersects the cell from block globalId of id cellId.
282    */
283   std::map<int, std::unordered_map<vtkIdType, std::set<vtkIdType>>> CollisionListMaps;
284 };
285 } // anonymous namespace
286 
287 vtkStandardNewMacro(vtkOverlappingCellsDetector);
288 vtkCxxSetObjectMacro(vtkOverlappingCellsDetector, Controller, vtkMultiProcessController);
289 
290 //----------------------------------------------------------------------------
vtkOverlappingCellsDetector()291 vtkOverlappingCellsDetector::vtkOverlappingCellsDetector()
292   : Controller(nullptr)
293   , NumberOfOverlapsPerCellArrayName(nullptr)
294   , Tolerance(0.0)
295 {
296   this->SetController(vtkMultiProcessController::GetGlobalController());
297   this->SetNumberOfOverlapsPerCellArrayName("NumberOfOverlapsPerCell");
298 }
299 
300 //----------------------------------------------------------------------------
~vtkOverlappingCellsDetector()301 vtkOverlappingCellsDetector::~vtkOverlappingCellsDetector()
302 {
303   this->SetController(nullptr);
304   this->SetNumberOfOverlapsPerCellArrayName(nullptr);
305 }
306 
307 //------------------------------------------------------------------------------
FillInputPortInformation(int vtkNotUsed (port),vtkInformation * info)308 int vtkOverlappingCellsDetector::FillInputPortInformation(
309   int vtkNotUsed(port), vtkInformation* info)
310 {
311   info->Append(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkCompositeDataSet");
312   info->Append(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkPointSet");
313   return 1;
314 }
315 
316 //----------------------------------------------------------------------------
RequestData(vtkInformation *,vtkInformationVector ** inputVector,vtkInformationVector * outputVector)317 int vtkOverlappingCellsDetector::RequestData(
318   vtkInformation*, vtkInformationVector** inputVector, vtkInformationVector* outputVector)
319 {
320   vtkDataObject* inputDO = vtkDataObject::GetData(inputVector[0], 0);
321   vtkDataObject* outputDO = vtkDataObject::GetData(outputVector, 0);
322 
323   bool error = false;
324   if (auto outputDS = vtkDataSet::SafeDownCast(outputDO))
325   {
326     if (auto inputDS = vtkDataSet::SafeDownCast(inputDO))
327     {
328       outputDS->ShallowCopy(inputDS);
329     }
330     else
331     {
332       error = true;
333     }
334   }
335   else if (auto outputCDS = vtkCompositeDataSet::SafeDownCast(outputDO))
336   {
337     if (auto inputCDS = vtkCompositeDataSet::SafeDownCast(inputDO))
338     {
339       outputCDS->CopyStructure(inputCDS);
340       auto iter = inputCDS->NewIterator();
341       for (iter->InitTraversal(); !iter->IsDoneWithTraversal(); iter->GoToNextItem())
342       {
343         auto subInputDO = iter->GetCurrentDataObject();
344         auto clone = subInputDO->NewInstance();
345         clone->ShallowCopy(subInputDO);
346         outputCDS->SetDataSet(iter, clone);
347         clone->FastDelete();
348       }
349       iter->Delete();
350     }
351     else
352     {
353       error = true;
354     }
355   }
356   else
357   {
358     error = true;
359   }
360   if (error)
361   {
362     vtkErrorMacro(<< "Could not generate output");
363     return 0;
364   }
365 
366   std::vector<vtkPointSet*> outputs = vtkCompositeDataSet::GetDataSets<vtkPointSet>(outputDO);
367 
368   return this->ExposeOverlappingCellsAmongBlocks(outputs);
369 }
370 
371 //----------------------------------------------------------------------------
ExposeOverlappingCellsAmongBlocks(std::vector<vtkPointSet * > & outputs)372 int vtkOverlappingCellsDetector::ExposeOverlappingCellsAmongBlocks(
373   std::vector<vtkPointSet*>& outputs)
374 {
375   std::vector<std::vector<vtkBoundingBox>> cellBoundingBoxesArray(outputs.size());
376   std::vector<vtkSmartPointer<vtkPointSet>> pointCloudArray;
377 
378   const int size = static_cast<int>(outputs.size());
379 
380   vtkLogStartScope(TRACE, "extract cell bounding spheres");
381   pointCloudArray.reserve(size);
382   for (int localId = 0; localId < size; ++localId)
383   {
384     pointCloudArray.emplace_back(vtkSmartPointer<vtkPointSet>::Take(
385       ConvertCellsToBoundingSpheres(outputs[localId], cellBoundingBoxesArray[localId])));
386   }
387   vtkLogEndScope("extract cell bounding spheres");
388 
389   // Setting up diy communication
390   diy::mpi::communicator comm = vtkDIYUtilities::GetCommunicator(this->Controller);
391 
392   diy::Master master(
393     comm, 1, -1, []() { return static_cast<void*>(new Block()); },
394     [](void* b) -> void { delete static_cast<Block*>(b); });
395   vtkDIYExplicitAssigner assigner(comm, size);
396 
397   vtkLogStartScope(TRACE, "populate master");
398   std::vector<int> gids;
399   assigner.local_gids(comm.rank(), gids);
400 
401   diy::RegularDecomposer<diy::DiscreteBounds> decomposer(
402     /*dim*/ 1, diy::interval(0, assigner.nblocks() - 1), assigner.nblocks());
403   decomposer.decompose(comm.rank(), assigner, master);
404   vtkLogEndScope("populate master");
405 
406   int myrank = comm.rank();
407 
408   // First, we share bounding boxes with other blocks
409   vtkLogStartScope(TRACE, "share bounding boxes");
410   diy::all_to_all(master, assigner, [&master, &outputs](Block* block, const diy::ReduceProxy& srp) {
411     int myBlockId = srp.gid();
412     int localId = master.lid(myBlockId);
413     auto& output = outputs[localId];
414     if (srp.round() == 0)
415     {
416       for (int i = 0; i < srp.out_link().size(); ++i)
417       {
418         if (i != myBlockId)
419         {
420           srp.enqueue(srp.out_link().target(i), output->GetBounds(), 6);
421         }
422       }
423     }
424     else
425     {
426       double boundstmp[6];
427       for (int i = 0; i < static_cast<int>(srp.in_link().size()); ++i)
428       {
429         if (i != myBlockId)
430         {
431           const diy::BlockID& blockId = srp.in_link().target(i);
432           srp.dequeue(blockId, boundstmp, 6);
433           block->BoundingBoxes.emplace(blockId.gid, vtkBoundingBox(boundstmp));
434         }
435       }
436     }
437   });
438   vtkLogEndScope("share bounding boxes");
439 
440   std::vector<std::map<int, vtkBoundingBox>> boundingBoxesArray;
441   std::vector<std::map<int, vtkSmartPointer<vtkUnstructuredGrid>>>
442     overlappingCellCandidatesDataSetsArray;
443 
444   vtkLogStartScope(TRACE, "isolate overlapping cell candidates for neighbor ranks");
445   for (int localId = 0; localId < size; ++localId)
446   {
447     boundingBoxesArray.emplace_back(std::move(master.block<Block>(localId)->BoundingBoxes));
448 
449     // We create unstructured grids for each neighbor block, composed
450     // from cells that are candidates for intersecting cells from the neighbor.
451     overlappingCellCandidatesDataSetsArray.emplace_back(
452       ExtractOverlappingCellCandidateByProcess(pointCloudArray[localId],
453         boundingBoxesArray[localId], outputs[localId], cellBoundingBoxesArray[localId]));
454   }
455   vtkLogEndScope("isolate overlapping cell candidates for neighbor ranks");
456 
457   // We check if each rank found the same links between blocks.
458   // If one block finds that a cell intersects the bounding box of another block, but this other
459   // block does not find so, it means that those blocks should not be linked: there won't be any
460   // overlaps.
461   // After this diy communication, the link map is symmetric among blocks.
462   diy::all_to_all(master, assigner,
463     [&master, &overlappingCellCandidatesDataSetsArray](Block*, const diy::ReduceProxy& rp) {
464       int myBlockId = rp.gid();
465       int localId = master.lid(myBlockId);
466       auto& overlappingCellCandidatesDataSets = overlappingCellCandidatesDataSetsArray[localId];
467       if (rp.round() == 0)
468       {
469         for (int i = 0; i < rp.out_link().size(); ++i)
470         {
471           const diy::BlockID& blockId = rp.out_link().target(i);
472           if (blockId.gid != myBlockId)
473           {
474             int connected = (overlappingCellCandidatesDataSets.count(blockId.gid) != 0);
475             const auto dest = rp.out_link().target(i);
476             rp.enqueue(dest, &connected, 1);
477           }
478         };
479       }
480       else
481       {
482         for (int i = 0; i < rp.in_link().size(); ++i)
483         {
484           const auto src = rp.in_link().target(i);
485           if (src.gid != myBlockId)
486           {
487             int connected;
488             rp.dequeue(src, &connected, 1);
489             if (!connected)
490             {
491               auto it = overlappingCellCandidatesDataSets.find(src.gid);
492               if (it != overlappingCellCandidatesDataSets.end())
493               {
494                 overlappingCellCandidatesDataSets.erase(it);
495               }
496             }
497           }
498         }
499       }
500     });
501 
502   vtkLogStartScope(TRACE, "relink master");
503   vtkDIYUtilities::Link(master, assigner, overlappingCellCandidatesDataSetsArray);
504   vtkLogEndScope("relink master");
505 
506   // We share overlapping candidates with neighbor blocks.
507   vtkLogStartScope(TRACE, "send cell candidates accross ranks");
508   master.foreach ([&master, &overlappingCellCandidatesDataSetsArray](
509                     void*, const diy::Master::ProxyWithLink& cp) {
510     int myBlockId = cp.gid();
511     int localId = master.lid(myBlockId);
512     auto& candidates = overlappingCellCandidatesDataSetsArray[localId];
513     // enqueue
514     for (int i = 0; i < static_cast<int>(cp.link()->size()); ++i)
515     {
516       vtkdiy2::BlockID& targetBlockId = cp.link()->target(i);
517       cp.enqueue<vtkDataSet*>(targetBlockId, candidates.at(targetBlockId.gid));
518     }
519   });
520   master.exchange();
521   master.foreach ([](Block* block, const diy::Master::ProxyWithLink& cp) {
522     // dequeue
523     std::vector<int> incoming;
524     cp.incoming(incoming);
525     for (int gid : incoming)
526     {
527       // we need this extra check because incoming is not empty when using only one block
528       if (!cp.incoming(gid).empty())
529       {
530         vtkDataSet* ds = nullptr;
531         cp.dequeue<vtkDataSet*>(gid, ds);
532         block->DataSets.emplace(gid, vtkSmartPointer<vtkDataSet>::Take(ds));
533       }
534     }
535   });
536   vtkLogEndScope("send cell candidates accross ranks");
537 
538   std::vector<std::map<int, std::unordered_map<vtkIdType, std::set<vtkIdType>>>>
539     collisionListMapListArray(outputs.size());
540   std::vector<std::map<int, vtkSmartPointer<vtkDataSet>>> queryCellDataSetsArray;
541 
542   vtkLogStartScope(TRACE, "locally treat received cells");
543   for (int localId = 0; localId < size; ++localId)
544   {
545     auto& output = outputs[localId];
546     auto& pointCloud = pointCloudArray[localId];
547     vtkBoundingBox bounds(output->GetBounds());
548 
549     // Locator to be used for point search inside the point cloud of bounding spheres.
550     if (bounds.ComputeInnerDimension() == 2)
551     {
552       vtkNew<vtkStaticPointLocator2D> locator;
553       locator->SetDataSet(pointCloud);
554       pointCloud->SetPointLocator(locator);
555     }
556 
557     // Locator to be used for point search inside the point cloud of bounding spheres.
558     if (bounds.ComputeInnerDimension() == 2)
559     {
560       vtkNew<vtkStaticPointLocator2D> locator;
561       locator->SetDataSet(pointCloud);
562       pointCloud->SetPointLocator(locator);
563     }
564     else
565     {
566       vtkNew<vtkKdTreePointLocator> locator;
567       locator->SetDataSet(pointCloud);
568       pointCloud->SetPointLocator(locator);
569     }
570 
571     // dummy variable needed in the main cell collision detection algorithm.
572     std::unordered_map<vtkIdType, std::set<vtkIdType>> localCollisionListMaps;
573     queryCellDataSetsArray.emplace_back(std::move(master.block<Block>(localId)->DataSets));
574     auto& queryCellDataSets = queryCellDataSetsArray[localId];
575 
576     const auto& cellBoundingBoxes = cellBoundingBoxesArray[localId];
577 
578     if (!this->DetectOverlappingCells(output, pointCloud, cellBoundingBoxes, output, pointCloud,
579           cellBoundingBoxes, localCollisionListMaps, true /* updateProgress */))
580     {
581       vtkErrorMacro(<< "Failed to detect self colliding cells");
582       return 0;
583     }
584 
585     auto& collisionListMapList = collisionListMapListArray[localId];
586 
587     // We now detect collision with the cells sent by other blocks.
588     for (auto& pair : queryCellDataSets)
589     {
590       vtkSmartPointer<vtkDataSet>& queryCellDataSet = pair.second;
591       int globalId = pair.first;
592       std::vector<vtkBoundingBox> queryCellBoundingBoxes;
593       vtkSmartPointer<vtkPointSet> queryPointCloud = vtkSmartPointer<vtkPointSet>::Take(
594         ConvertCellsToBoundingSpheres(queryCellDataSet, queryCellBoundingBoxes));
595 
596       if (!this->DetectOverlappingCells(queryCellDataSet, queryPointCloud, queryCellBoundingBoxes,
597             output, pointCloud, cellBoundingBoxes, collisionListMapList[globalId]))
598       {
599         vtkErrorMacro(<< "Failed to detect self colliding cells in process " << myrank << " on "
600                       << globalId << "th local block");
601         return 0;
602       }
603     }
604   }
605   vtkLogEndScope("locally treat received cells");
606 
607   // We need to send back collision information to the original block, so they
608   // can add the collisions they couldn't detect.
609   vtkLogStartScope(TRACE, "send back detected overlaps");
610   master.foreach (
611     [&master, &collisionListMapListArray](void*, const diy::Master::ProxyWithLink& cp) {
612       int myBlockId = cp.gid();
613       int localId = master.lid(myBlockId);
614       auto& collisionListMapList = collisionListMapListArray[localId];
615       // enqueue
616       for (int i = 0; i < static_cast<int>(cp.link()->size()); ++i)
617       {
618         vtkdiy2::BlockID& targetBlockId = cp.link()->target(i);
619         cp.enqueue(targetBlockId, collisionListMapList[targetBlockId.gid]);
620       }
621     });
622   master.exchange();
623   master.foreach ([](Block* block, const diy::Master::ProxyWithLink& cp) {
624     // dequeue
625     std::vector<int> incoming;
626     cp.incoming(incoming);
627     for (int gid : incoming)
628     {
629       if (!cp.incoming(gid).empty())
630       {
631         std::unordered_map<vtkIdType, std::set<vtkIdType>> collisionListMap;
632         cp.dequeue(gid, collisionListMap);
633         block->CollisionListMaps.emplace(gid, std::move(collisionListMap));
634       }
635     }
636   });
637   vtkLogEndScope("send back detected overlaps");
638 
639   vtkLogStartScope(TRACE, "add detected overlaps from other ranks");
640   for (int localId = 0; localId < size; ++localId)
641   {
642     auto& collisionIdList = master.block<Block>(localId)->CollisionListMaps;
643     auto& collisionListMapList = collisionListMapListArray[localId];
644     vtkIdTypeArray* queryNumberOfOverlapsPerCell = vtkIdTypeArray::SafeDownCast(
645       outputs[localId]->GetCellData()->GetAbstractArray(this->NumberOfOverlapsPerCellArrayName));
646     auto& queryCellDataSets = queryCellDataSetsArray[localId];
647 
648     // Last pass. We look at what intersections were found in the other blocks, and check if
649     // we found them or not, and increment collision count accordingly.
650     for (const auto& collisionIdPair : collisionIdList)
651     {
652       int globalId = collisionIdPair.first;
653       auto& collisionIds = collisionIdPair.second;
654       auto& collisionListMap = collisionListMapList[globalId];
655       vtkDataSet* queryCellDataSet = queryCellDataSets[globalId];
656       vtkIdTypeArray* neighborIdMapArray = vtkIdTypeArray::SafeDownCast(
657         queryCellDataSet->GetCellData()->GetArray(ID_MAP_TO_ORIGIN_DATASET_IDS_NAME));
658       for (const auto& pair : collisionListMap)
659       {
660         // pair.first <=> id of neighbor process which has collision with at least one of our cell.
661         // pair.second <=> list of ids from this process colliding with cell of id pair.first
662         for (vtkIdType id : pair.second)
663         {
664           auto it = collisionIds.find(neighborIdMapArray->GetValue(id));
665           // it->first <=> id in this process which has collision with at least one of our
666           // neighbor's cell. it->second <=> list of ids from our neighbor process colliding with
667           // cell of id it->first.
668           if (it == collisionIds.end() || !pair.second.count(id))
669           {
670             queryNumberOfOverlapsPerCell->SetValue(
671               pair.first, queryNumberOfOverlapsPerCell->GetValue(pair.first) + 1);
672           }
673         }
674       }
675     }
676   }
677   vtkLogEndScope("add detected overlaps from other ranks");
678 
679   this->UpdateProgress(1.0);
680 
681   for (auto output : outputs)
682   {
683     output->GetCellData()->SetActiveScalars(this->GetNumberOfOverlapsPerCellArrayName());
684   }
685 
686   return 1;
687 }
688 
689 //----------------------------------------------------------------------------
690 // Main collision detection algorithm.
DetectOverlappingCells(vtkDataSet * queryCellDataSet,vtkPointSet * queryPointCloud,const std::vector<vtkBoundingBox> & queryCellBoundingBoxes,vtkDataSet * cellDataSet,vtkPointSet * pointCloud,const std::vector<vtkBoundingBox> & cellBoundingBoxes,std::unordered_map<vtkIdType,std::set<vtkIdType>> & collisionListMap,bool updateProgress)691 bool vtkOverlappingCellsDetector::DetectOverlappingCells(vtkDataSet* queryCellDataSet,
692   vtkPointSet* queryPointCloud, const std::vector<vtkBoundingBox>& queryCellBoundingBoxes,
693   vtkDataSet* cellDataSet, vtkPointSet* pointCloud,
694   const std::vector<vtkBoundingBox>& cellBoundingBoxes,
695   std::unordered_map<vtkIdType, std::set<vtkIdType>>& collisionListMap, bool updateProgress)
696 {
697   assert(cellDataSet->GetNumberOfCells() == pointCloud->GetNumberOfPoints() &&
698     static_cast<vtkIdType>(cellBoundingBoxes.size()) == pointCloud->GetNumberOfPoints());
699   assert(queryCellDataSet->GetNumberOfCells() == queryPointCloud->GetNumberOfPoints() &&
700     static_cast<vtkIdType>(queryCellBoundingBoxes.size()) == queryPointCloud->GetNumberOfPoints());
701 
702   vtkAbstractPointLocator* locator = pointCloud->GetPointLocator();
703   if (!locator)
704   {
705     pointCloud->BuildPointLocator();
706     locator = pointCloud->GetPointLocator();
707   }
708 
709   vtkDataArray* querySphereRadiusArray =
710     queryPointCloud->GetPointData()->GetArray(SPHERE_RADIUS_ARRAY_NAME);
711 
712   vtkIdType querySize = queryPointCloud->GetNumberOfPoints();
713   vtkIdType twentieth = querySize / 20 + 1;
714   double decimal = 0.0;
715 
716   vtkNew<vtkIdTypeArray> queryNumberOfOverlapsPerCellsArray;
717   queryNumberOfOverlapsPerCellsArray->SetNumberOfComponents(1);
718   queryNumberOfOverlapsPerCellsArray->SetNumberOfTuples(querySize);
719   queryNumberOfOverlapsPerCellsArray->SetName(this->GetNumberOfOverlapsPerCellArrayName());
720   queryNumberOfOverlapsPerCellsArray->Fill(0.0);
721 
722   // Handling case where both input data sets point to the same address.
723   vtkIdTypeArray* numberOfCollisionPerCellsArray = queryCellDataSet != cellDataSet
724     ? vtkArrayDownCast<vtkIdTypeArray>(
725         cellDataSet->GetCellData()->GetArray(this->GetNumberOfOverlapsPerCellArrayName()))
726     : queryNumberOfOverlapsPerCellsArray;
727 
728   vtkNew<vtkIdList> neighborIds;
729 
730   // We want to discard ghost cells, so we have to acknowledge them.
731   vtkUnsignedCharArray* queryCellGhostArray = queryCellDataSet->GetCellGhostArray();
732   vtkUnsignedCharArray* cellGhostArray = cellDataSet->GetCellGhostArray();
733 
734   // local cell bank to avoid calling ::New() too many times.
735   std::map<int, vtkSmartPointer<vtkCell>> cellBank, neighborCellBank;
736   for (vtkIdType id = 0; id < querySize; ++id)
737   {
738     if (updateProgress)
739     {
740       // Update progress
741       if (!(id % twentieth))
742       {
743         decimal += 0.05;
744         this->UpdateProgress(decimal);
745       }
746     }
747     if (queryCellGhostArray && queryCellGhostArray->GetValue(id))
748     {
749       continue;
750     }
751     locator->FindPointsWithinRadius(2.0 * std::sqrt(querySphereRadiusArray->GetTuple1(id)),
752       queryPointCloud->GetPoint(id), neighborIds);
753     int cellType = queryCellDataSet->GetCellType(id);
754     auto cellBankHandle = cellBank.find(cellType);
755     vtkCell* currentCell;
756     if (cellBankHandle == cellBank.end())
757     {
758       currentCell = vtkGenericCell::InstantiateCell(cellType);
759       cellBank[cellType] = vtkSmartPointer<vtkCell>::Take(currentCell);
760     }
761     else
762     {
763       currentCell = cellBankHandle->second;
764     }
765     // We need to deep copy because if the 2 inputs share the same address,
766     // currentCell and the other cell on which we want to detect collision
767     // will step on each other: they inner data would share the same address.
768     currentCell->DeepCopy(queryCellDataSet->GetCell(id));
769 
770     const vtkBoundingBox& bbox = queryCellBoundingBoxes[id];
771 
772     // We need to shrink currentCell to discard false positive from adjacent cells.
773     double currentCellTolerance = std::max(ComputeEpsilon(bbox), 0.5 * this->Tolerance);
774     currentCell->Inflate(-currentCellTolerance);
775 
776     vtkIdType intersectionCount = 0;
777     auto collisionListMapHandle = collisionListMap.find(id);
778     for (vtkIdType i = 0; i < neighborIds->GetNumberOfIds(); ++i)
779     {
780       vtkIdType neighborId = neighborIds->GetId(i);
781       if (cellGhostArray && cellGhostArray->GetValue(neighborId))
782       {
783         continue;
784       }
785 
786       // We do not want to compute the same collision twice, so we use collisionListMap info here
787       if ((queryCellDataSet != cellDataSet ||
788             (id != neighborId &&
789               (collisionListMapHandle == collisionListMap.end() ||
790                 !collisionListMapHandle->second.count(neighborId)))))
791       {
792         // Same procedure as for currentCell.
793         // We have a bank of cells to aleviate dynamic allocating when possible.
794         int neighborCellType = cellDataSet->GetCellType(neighborId);
795         auto neighborCellBankHandle = neighborCellBank.find(neighborCellType);
796         vtkCell* neighborCell;
797         if (neighborCellBankHandle == neighborCellBank.end())
798         {
799           neighborCell = vtkGenericCell::InstantiateCell(neighborCellType);
800           neighborCellBank[neighborCellType] = vtkSmartPointer<vtkCell>::Take(neighborCell);
801         }
802         else
803         {
804           neighborCell = neighborCellBankHandle->second;
805         }
806         neighborCell->DeepCopy(cellDataSet->GetCell(neighborId));
807 
808         // Shrinking this cell as well.
809         double neighborCellTolerance =
810           std::max(ComputeEpsilon(cellBoundingBoxes[neighborId]), 0.5 * this->Tolerance);
811         neighborCell->Inflate(-neighborCellTolerance);
812         if (currentCell->IntersectWithCell(neighborCell, bbox, cellBoundingBoxes[neighborId]))
813         {
814           ++intersectionCount;
815           numberOfCollisionPerCellsArray->SetValue(
816             neighborId, numberOfCollisionPerCellsArray->GetValue(neighborId) + 1);
817           collisionListMap[neighborId].insert(id);
818         }
819       }
820     }
821     if (intersectionCount)
822     {
823       queryNumberOfOverlapsPerCellsArray->SetValue(
824         id, queryNumberOfOverlapsPerCellsArray->GetValue(id) + intersectionCount);
825     }
826   }
827   queryCellDataSet->GetCellData()->AddArray(queryNumberOfOverlapsPerCellsArray);
828 
829   return true;
830 }
831 
832 //----------------------------------------------------------------------------
PrintSelf(ostream & os,vtkIndent indent)833 void vtkOverlappingCellsDetector::PrintSelf(ostream& os, vtkIndent indent)
834 {
835   this->Superclass::PrintSelf(os, indent);
836   os << indent << "Controller: " << this->Controller << endl;
837   os << indent << "NumberOfOverlapsPerCellArrayName: " << this->NumberOfOverlapsPerCellArrayName
838      << std::endl;
839   os << indent << "Tolerance: " << this->Tolerance << std::endl;
840 }
841