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