Loading...
Searching...
No Matches
patch_skd_tree.cpp
1/*---------------------------------------------------------------------------*\
2 *
3 * bitpit
4 *
5 * Copyright (C) 2015-2021 OPTIMAD engineering Srl
6 *
7 * -------------------------------------------------------------------------
8 * License
9 * This file is part of bitpit.
10 *
11 * bitpit is free software: you can redistribute it and/or modify it
12 * under the terms of the GNU Lesser General Public License v3 (LGPL)
13 * as published by the Free Software Foundation.
14 *
15 * bitpit is distributed in the hope that it will be useful, but WITHOUT
16 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
17 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
18 * License for more details.
19 *
20 * You should have received a copy of the GNU Lesser General Public License
21 * along with bitpit. If not, see <http://www.gnu.org/licenses/>.
22 *
23\*---------------------------------------------------------------------------*/
24
25#include <cmath>
26#include <queue>
27
28#include "bitpit_CG.hpp"
29
30#include "patch_skd_tree.hpp"
31
32namespace bitpit {
33
47SkdPatchInfo::SkdPatchInfo(const PatchKernel *patch, const std::vector<std::size_t> *cellRawIds)
48 : m_patch(patch), m_cellRawIds(cellRawIds)
49{
50 if (!m_patch) {
51 throw std::runtime_error("Unable to initialize the patch info. Provided patch is not valid.");
52 }
53}
54
59{
61
62 buildCache(cellRange);
63}
64
71{
72 m_cellBoxes = std::unique_ptr<BoxCache>(new BoxCache(1, &(m_patch->getCells())));
73 for (auto itr = cellRange.cbegin(); itr != cellRange.cend(); ++itr) {
74 std::size_t rawCellId = itr.getRawIndex();
75
76 // Bounding box
77 std::array<std::array<double, 3>, 2> *cellBox = m_cellBoxes->rawData(rawCellId);
78 m_patch->evalElementBoundingBox(*itr, cellBox->data(), cellBox->data() + 1);
79 }
80}
81
86{
87 m_cellBoxes.reset();
88}
89
96{
97 return *m_patch;
98}
99
105const std::vector<std::size_t> & SkdPatchInfo::getCellRawIds() const
106{
107 return *m_cellRawIds;
108}
109
116std::size_t SkdPatchInfo::getCellRawId(std::size_t n) const
117{
118 return (*m_cellRawIds)[n];
119}
120
130const std::array<std::array<double, 3>, 2> & SkdPatchInfo::getCachedBox(std::size_t rawId) const
131{
132 return m_cellBoxes->rawAt(rawId);
133}
134
141std::array<double, 3> SkdPatchInfo::evalCachedBoxMean(std::size_t rawId) const
142{
143 const std::array<std::array<double, 3>, 2> &cellBox = m_cellBoxes->rawAt(rawId);
144
145 return 0.5 * (cellBox[0] + cellBox[1]);
146}
147
155double SkdPatchInfo::evalCachedBoxMean(std::size_t rawId, int direction) const
156{
157 const std::array<std::array<double, 3>, 2> &cellBox = m_cellBoxes->rawAt(rawId);
158
159 return 0.5 * (cellBox[0][direction] + cellBox[1][direction]);
160}
161
172 : m_boxMin{ std::numeric_limits<double>::max(), std::numeric_limits<double>::max(), std::numeric_limits<double>::max()},
173 m_boxMax{- std::numeric_limits<double>::max(), - std::numeric_limits<double>::max(), - std::numeric_limits<double>::max()}
174{
175}
176
183SkdBox::SkdBox(const std::array<double,3> &boxMin, const std::array<double,3> &boxMax)
184 : m_boxMin(boxMin),
185 m_boxMax(boxMax)
186{
187}
188
197bool SkdBox::isEmpty() const
198{
199 for (int d = 0; d < 3; ++d) {
200 if (m_boxMin[d] > m_boxMax[d]) {
201 return true;
202 }
203 }
204
205 return false;
206}
207
213const std::array<double, 3> & SkdBox::getBoxMin() const
214{
215 return m_boxMin;
216}
217
223const std::array<double, 3> & SkdBox::getBoxMax() const
224{
225 return m_boxMax;
226}
227
238double SkdBox::evalPointMinDistance(const std::array<double, 3> &point, double emptyDistance) const
239{
240 if (isEmpty()) {
241 return emptyDistance;
242 }
243
244 return evalPointMinDistance(point);
245}
246
255double SkdBox::evalPointMinDistance(const std::array<double, 3> &point) const
256{
257 return std::sqrt(evalPointMinSquareDistance(point));
258}
259
272double SkdBox::evalPointMinSquareDistance(const std::array<double, 3> &point, double emptySquareDistance) const
273{
274 if (isEmpty()) {
275 return emptySquareDistance;
276 }
277
278 return evalPointMinSquareDistance(point);
279}
280
291double SkdBox::evalPointMinSquareDistance(const std::array<double, 3> &point) const
292{
293 double squareDistance = 0.;
294 for (int d = 0; d < 3; ++d) {
295 squareDistance += std::pow(std::max({0., m_boxMin[d] - point[d], point[d] - m_boxMax[d]}), 2);
296 }
297
298 return squareDistance;
299}
300
311double SkdBox::evalPointMaxDistance(const std::array<double, 3> &point, double emptyDistance) const
312{
313 if (isEmpty()) {
314 return emptyDistance;
315 }
316
317 return evalPointMaxDistance(point);
318}
319
328double SkdBox::evalPointMaxDistance(const std::array<double, 3> &point) const
329{
330 return std::sqrt(evalPointMaxSquareDistance(point));
331}
332
345double SkdBox::evalPointMaxSquareDistance(const std::array<double, 3> &point, double emptySquareDistance) const
346{
347 if (isEmpty()) {
348 return emptySquareDistance;
349 }
350
351 return evalPointMaxSquareDistance(point);
352}
353
364double SkdBox::evalPointMaxSquareDistance(const std::array<double, 3> &point) const
365{
366 double squareDistance = 0.;
367 for (int d = 0; d < 3; ++d) {
368 squareDistance += std::pow(std::max(point[d] - m_boxMin[d], m_boxMax[d] - point[d]), 2);
369 }
370
371 return squareDistance;
372}
373
383bool SkdBox::boxContainsPoint(const std::array<double, 3> &point, double offset) const
384{
385 if (isEmpty()) {
386 return false;
387 }
388
389 for (int d = 0; d < 3; d++) {
390 if (point[d] < (m_boxMin[d] - offset)) {
391 return false;
392 }
393
394 if (point[d] > (m_boxMax[d] + offset)) {
395 return false;
396 }
397 }
398
399 return true;
400}
401
410bool SkdBox::boxIntersectsSphere(const std::array<double, 3> &center, double radius) const
411{
412 // If the box contains the center it will also intersect the sphere
413 if (boxContainsPoint(center, 0.)) {
414 return true;
415 }
416
417 // Check if the distance between the center and its projection on
418 // the bounding box is smaller than the radius of the sphere.
419 std::array<double, 3> delta;
420 for (int d = 0; d < 3; d++) {
421 delta[d] = std::min(std::max(center[d], m_boxMin[d]), m_boxMax[d]) - center[d];
422 }
423 double distance = dotProduct(delta, delta);
424
425 return (distance < radius * radius);
426}
427
437const std::size_t SkdNode::NULL_ID = std::numeric_limits<std::size_t>::max();
438
443 : m_patchInfo(nullptr),
444 m_cellRangeBegin(0), m_cellRangeEnd(0),
445 m_children({{NULL_ID, NULL_ID}})
446{
447}
448
458SkdNode::SkdNode(const SkdPatchInfo *patchInfo, std::size_t cellRangeBegin, std::size_t cellRangeEnd)
459 : m_patchInfo(patchInfo),
460 m_cellRangeBegin(cellRangeBegin), m_cellRangeEnd(cellRangeEnd),
461 m_children({{NULL_ID, NULL_ID}})
462{
463 initializeBoundingBox();
464}
465
469void SkdNode::initializeBoundingBox()
470{
471 // Early return if there are no cells
472 if (m_cellRangeBegin == m_cellRangeEnd){
473 m_boxMin.fill( std::numeric_limits<double>::max());
474 m_boxMax.fill(- std::numeric_limits<double>::max());
475
476 return;
477 }
478
479 // Evaluate the bounding box
480 const std::vector<std::size_t> &cellRawIds = m_patchInfo->getCellRawIds();
481
482 const std::array<std::array<double, 3>, 2> &firstCellBox = m_patchInfo->getCachedBox(cellRawIds[m_cellRangeBegin]);
483 m_boxMin = firstCellBox[0];
484 m_boxMax = firstCellBox[1];
485 for (std::size_t n = m_cellRangeBegin + 1; n < m_cellRangeEnd; n++) {
486 const std::array<std::array<double, 3>, 2> &cellBox = m_patchInfo->getCachedBox(cellRawIds[n]);
487 for (int d = 0; d < 3; ++d) {
488 m_boxMin[d] = std::min(cellBox[0][d], m_boxMin[d]);
489 m_boxMax[d] = std::max(cellBox[1][d], m_boxMax[d]);
490 }
491 }
492
493 // Inlfate the bounding box by a small epsilon
494 //
495 // The small inflation allows us to test intersection of a query
496 // against a safety box and be assured that non-intersection of
497 // with the box implies non-intersection with all the contents
498 // in the box, even when computations are susceptible to round-off
499 // error.
500 double tolerance = m_patchInfo->getPatch().getTol();
501
502 m_boxMin -= tolerance;
503 m_boxMax += tolerance;
504}
505
512std::size_t SkdNode::getCellCount() const
513{
514 return (m_cellRangeEnd - m_cellRangeBegin);
515}
516
523long SkdNode::getCell(std::size_t n) const
524{
525 const PatchKernel &patch = m_patchInfo->getPatch();
526 const PiercedKernel<long> &cellKernel = patch.getCells().getKernel();
527
528 const std::size_t rawCellId = m_patchInfo->getCellRawId(m_cellRangeBegin + n);
529 long cellId = cellKernel.rawFind(rawCellId).getId();
530
531 return cellId;
532}
533
540std::vector<long> SkdNode::getCells() const
541{
542 const PatchKernel &patch = m_patchInfo->getPatch();
543 const std::vector<std::size_t> &cellRawIds = m_patchInfo->getCellRawIds();
544 const PiercedKernel<long> &cellKernel = patch.getCells().getKernel();
545
546 std::size_t nCells = getCellCount();
547 std::vector<long> cellList(nCells);
548 for (std::size_t n = m_cellRangeBegin; n < m_cellRangeEnd; ++n) {
549 const std::size_t rawCellId = cellRawIds[n];
550 long cellId = cellKernel.rawFind(rawCellId).getId();
551
552 cellList[n - m_cellRangeBegin] = cellId;
553 }
554
555 return cellList;
556}
557
562{
563 return *this;
564}
565
573std::array<double, 3> SkdNode::evalBoxWeightedMean() const
574{
575 const std::vector<std::size_t> &cellRawIds = m_patchInfo->getCellRawIds();
576
577 std::array<double, 3> boxWeightedMean = m_patchInfo->evalCachedBoxMean(cellRawIds[m_cellRangeBegin]);
578 for (std::size_t n = m_cellRangeBegin + 1; n < m_cellRangeEnd; ++n) {
579 const std::size_t rawCellId = cellRawIds[n];
580 boxWeightedMean += m_patchInfo->evalCachedBoxMean(rawCellId);
581 }
582 boxWeightedMean /= (double) getCellCount();
583
584 return boxWeightedMean;
585}
586
595double SkdNode::evalBoxWeightedMean(int direction) const
596{
597 const std::vector<std::size_t> &cellRawIds = m_patchInfo->getCellRawIds();
598
599 double boxWeightedMean = m_patchInfo->evalCachedBoxMean(cellRawIds[m_cellRangeBegin], direction);
600 for (std::size_t n = m_cellRangeBegin + 1; n < m_cellRangeEnd; ++n) {
601 const std::size_t rawCellId = cellRawIds[n];
602 boxWeightedMean += m_patchInfo->evalCachedBoxMean(rawCellId, direction);
603 }
604 boxWeightedMean /= getCellCount();
605
606 return boxWeightedMean;
607}
608
614bool SkdNode::isLeaf() const
615{
616 for (int i = CHILD_BEGIN; i != CHILD_END; ++i) {
617 SkdNode::ChildLocation childLocation = static_cast<ChildLocation>(i);
618 if (hasChild(childLocation)) {
619 return false;
620 }
621 }
622
623 return true;
624}
625
632bool SkdNode::hasChild(ChildLocation child) const
633{
634 return (m_children[static_cast<std::size_t>(child)] != NULL_ID);
635}
636
643std::size_t SkdNode::getChildId(ChildLocation child) const
644{
645 return m_children[static_cast<std::size_t>(child)];
646}
647
659double SkdNode::evalPointDistance(const std::array<double, 3> &point, bool interiorCellsOnly) const
660{
661 long id;
662 double distance;
663
664 findPointClosestCell(point, interiorCellsOnly, &id, &distance);
665
666 return distance;
667}
668
682void SkdNode::findPointClosestCell(const std::array<double, 3> &point, bool interiorCellsOnly,
683 long *id, double *closestDistance) const
684{
685 *id = Cell::NULL_ID;
686 *closestDistance = std::numeric_limits<double>::max();
687
688 updatePointClosestCell(point, interiorCellsOnly, id, closestDistance);
689}
690
709void SkdNode::updatePointClosestCell(const std::array<double, 3> &point, bool interiorCellsOnly,
710 long *closestId, double *closestDistance) const
711{
712 if (getCellCount() == 0) {
713 return;
714 }
715
716 const PatchKernel &patch = m_patchInfo->getPatch();
717 const PiercedVector<Cell> &cells = patch.getCells();
718 const std::vector<std::size_t> &cellRawIds = m_patchInfo->getCellRawIds();
719
720 for (std::size_t n = m_cellRangeBegin; n < m_cellRangeEnd; n++) {
721 std::size_t cellRawId = cellRawIds[n];
722 const Cell &cell = cells.rawAt(cellRawId);
723 if (interiorCellsOnly && !cell.isInterior()) {
724 continue;
725 }
726
727 // Evaluate point projection
728 int nCellVertices = cell.getVertexCount();
729 BITPIT_CREATE_WORKSPACE(cellVertexCoordinates, std::array<double BITPIT_COMMA 3>, nCellVertices, ReferenceElementInfo::MAX_ELEM_VERTICES);
730 patch.getElementVertexCoordinates(cell, cellVertexCoordinates);
731
732 double cellDistance;
733 std::array<double, 3> cellProjection;
734 cell.evalPointProjection(point, cellVertexCoordinates, &cellProjection, &cellDistance);
735
736 // Check if the cell is closest that the current closest cell
737 bool isCellClosest = false;
738 if (utils::DoubleFloatingEqual()(cellDistance, *closestDistance, patch.getTol(), patch.getTol())) {
739 if (*closestId == Cell::NULL_ID) {
740 isCellClosest = true;
741 } else {
742 // Project point ont the closest cell
743 const Cell &closest = patch.getCell(*closestId);
744
745 int nClosestCellVertices = closest.getVertexCount();
746 BITPIT_CREATE_WORKSPACE(closestCellVertexCoordinates, std::array<double BITPIT_COMMA 3>, nClosestCellVertices, ReferenceElementInfo::MAX_ELEM_VERTICES);
747 patch.getElementVertexCoordinates(closest, closestCellVertexCoordinates);
748
749 double closestCellDistance;
750 std::array<double, 3> closestCellProjection;
751 closest.evalPointProjection(point, closestCellVertexCoordinates, &closestCellProjection, &closestCellDistance);
752
753 // Find normal of the cells at the centroids
754 //
755 // To be more precise, the normals should beevaluated on projection
756 // points.
757 std::array<double, 3> cellNormal = cell.evalNormal(cellVertexCoordinates);
758 std::array<double, 3> closestNormal = closest.evalNormal(closestCellVertexCoordinates);
759
760 // Find cell more aligned with respect to the point
761 //
762 // We want to chhose the cell whose normal is most aligned towards
763 // the point.
764 double cellAligement = dotProduct(cellNormal, point - cellProjection);
765 double closestAligement = dotProduct(closestNormal, point - closestCellProjection);
766
767 // The cell will be closest if it is more aligned than the current closest cell
768 if (cellAligement > closestAligement) {
769 isCellClosest = true;
770 }
771 }
772 } else if (cellDistance < *closestDistance) {
773 isCellClosest = true;
774 }
775
776 // Update the closest cell
777 if (isCellClosest) {
778 *closestId = cell.getId();
779 *closestDistance = cellDistance;
780 }
781 }
782}
783
802void SkdNode::updatePointClosestCells(const std::array<double, 3> &point, bool interiorCellsOnly,
803 std::vector<long> *closestIds, double *closestDistance) const
804{
805 if (getCellCount() == 0) {
806 return;
807 }
808
809 const PatchKernel &patch = m_patchInfo->getPatch();
810 const PiercedVector<Cell> &cells = patch.getCells();
811 const std::vector<std::size_t> &cellRawIds = m_patchInfo->getCellRawIds();
812
813 for (std::size_t n = m_cellRangeBegin; n < m_cellRangeEnd; n++) {
814 std::size_t cellRawId = cellRawIds[n];
815 const Cell &cell = cells.rawAt(cellRawId);
816 if (interiorCellsOnly && !cell.isInterior()) {
817 continue;
818 }
819
820 // Evaluate point projection
821 int nCellVertices = cell.getVertexCount();
822 BITPIT_CREATE_WORKSPACE(cellVertexCoordinates, std::array<double BITPIT_COMMA 3>, nCellVertices, ReferenceElementInfo::MAX_ELEM_VERTICES);
823 patch.getElementVertexCoordinates(cell, cellVertexCoordinates);
824
825 double cellDistance;
826 std::array<double, 3> cellProjection;
827 cell.evalPointProjection(point, cellVertexCoordinates, &cellProjection, &cellDistance);
828
829 // Update the closest cell
830 //
831 // If the cell is closest that the current closest cells, the list should be cleard and
832 // the current cell should be added to it. If the cell has the same distance of the
833 // closest cells, it should be simply added to the list. If the cell i farther than the
834 // closest one, there is nothing to do.
835 if (utils::DoubleFloatingEqual()(cellDistance, *closestDistance, patch.getTol(), patch.getTol())) {
836 closestIds->push_back(cell.getId());
837 } else if (cellDistance < *closestDistance) {
838 closestIds->assign(1, cell.getId());
839 *closestDistance = cellDistance;
840 }
841 }
842}
843
844#if BITPIT_ENABLE_MPI
852bool SkdGlobalCellDistance::m_MPIDatatypeInitialized = false;
853MPI_Datatype SkdGlobalCellDistance::m_MPIDatatype;
854
855bool SkdGlobalCellDistance::m_MPIMinOperationInitialized = false;
856MPI_Op SkdGlobalCellDistance::m_MPIMinOperation;
857
864{
865 if (!m_MPIDatatypeInitialized) {
866 int blocklengths[3] = {1, 1, 1};
867 MPI_Aint displacements[3] = {offsetof(SkdGlobalCellDistance, m_distance), offsetof(SkdGlobalCellDistance, m_id), offsetof(SkdGlobalCellDistance, m_rank)};
868 MPI_Datatype types[3] = {MPI_DOUBLE, MPI_LONG, MPI_INT};
869 MPI_Type_create_struct(3, blocklengths, displacements, types, &m_MPIDatatype);
870 MPI_Type_commit(&m_MPIDatatype);
871 m_MPIDatatypeInitialized = true;
872 }
873
874 return m_MPIDatatype;
875}
876
883{
884 if (!m_MPIMinOperationInitialized) {
885 MPI_Op_create((MPI_User_function *) executeMPIMinOperation, false, &m_MPIMinOperation);
886 m_MPIMinOperationInitialized = true;
887 }
888
889 return m_MPIMinOperation;
890}
891
901{
902 BITPIT_UNUSED(datatype);
903
904 int nDistances = *len;
905 for (int i = 0; i < nDistances; ++i) {
906 bool updateDistance = false;
907 if (in[i].m_id != Cell::NULL_ID) {
908 if (inout[i].m_id == Cell::NULL_ID) {
909 updateDistance = true;
910 } else if (std::abs(in[i].m_distance) < std::abs(inout[i].m_distance)) {
911 updateDistance = true;
912 }
913 }
914
915 if (updateDistance) {
916 inout[i] = in[i];
917 }
918 }
919}
920
927{
928 return m_rank;
929}
930
937{
938 return m_id;
939}
940
947{
948 return m_distance;
949}
950
958void SkdGlobalCellDistance::exportData(int *rank, long *id, double *distance) const
959{
960 *rank = m_rank;
961 *id = m_id;
962 *distance = m_distance;
963}
964#endif
965
1023PatchSkdTree::PatchSkdTree(const PatchKernel *patch, bool interiorCellsOnly)
1024 : m_patchInfo(patch, &m_cellRawIds),
1025 m_cellRawIds(interiorCellsOnly ? patch->getInternalCellCount() : patch->getCellCount()),
1026 m_nLeafs(0), m_nMinLeafCells(0), m_nMaxLeafCells(0),
1027 m_interiorCellsOnly(interiorCellsOnly),
1028 m_threadSafeLookups(false)
1029#if BITPIT_ENABLE_MPI
1030 , m_rank(0), m_nProcessors(1), m_communicator(MPI_COMM_NULL)
1031#endif
1032{
1033
1034}
1035
1042{
1043 return m_nMinLeafCells;
1044}
1045
1052{
1053 return m_nMaxLeafCells;
1054}
1055
1079void PatchSkdTree::build(std::size_t leafThreshold, bool squeezeStorage)
1080{
1081 const PatchKernel &patch = m_patchInfo.getPatch();
1082
1083 // Clear existing tree
1084 clear();
1085
1086 // Initialize list of cell raw ids
1087 std::size_t nCells;
1089 if (m_interiorCellsOnly) {
1090 nCells = patch.getInternalCellCount();
1091 cellRange.initialize(patch.internalCellConstBegin(), patch.internalCellConstEnd());
1092 } else {
1093 nCells = patch.getCellCount();
1094 cellRange.initialize(patch.cellConstBegin(), patch.cellConstEnd());
1095 }
1096
1097 if (m_cellRawIds.size() != nCells) {
1098 m_cellRawIds.resize(nCells);
1099 }
1100
1101 std::size_t k = 0;
1102 for (auto itr = cellRange.begin(); itr != cellRange.end(); ++itr) {
1103 m_cellRawIds[k++] = itr.getRawIndex();
1104 }
1105
1106 // Build patch cache
1107 m_patchInfo.buildCache(cellRange);
1108
1109 // Initialize node list
1110 std::size_t nodesCount = std::max(1, int(std::ceil(2. * nCells / leafThreshold - 1.)));
1111 m_nodes.reserve(nodesCount);
1112
1113 // Create the root
1114 m_nodes.emplace_back(&m_patchInfo, 0, nCells);
1115
1116 // Create the tree
1117 std::queue<std::size_t> nodeStack;
1118 nodeStack.push(0);
1119 while (!nodeStack.empty()) {
1120 std::size_t nodeId = nodeStack.front();
1121 nodeStack.pop();
1122
1123 // Create the children of the node
1124 //
1125 // This function may add new nodes to the tree, this may invalidate
1126 // any reference or pointer to the nodes. To avoid potential problems
1127 // we pass to the function a node id.
1128 createChildren(nodeId, leafThreshold);
1129
1130 // Add the newly created childrend to the stack
1131 const SkdNode &node = getNode(nodeId);
1132 for (int i = SkdNode::CHILD_BEGIN; i != SkdNode::CHILD_END; ++i) {
1133 std::size_t childId = node.getChildId(static_cast<SkdNode::ChildLocation>(i));
1134 if (childId != SkdNode::NULL_ID) {
1135 nodeStack.push(childId);
1136 }
1137 }
1138 }
1139
1140 // Squeeze storage
1141 if (squeezeStorage) {
1142 m_nodes.shrink_to_fit();
1143 }
1144
1145 // Patch cache is no longer needed
1146 m_patchInfo.destroyCache();
1147
1148#if BITPIT_ENABLE_MPI
1149 // Set partition information
1150 if (patch.isPartitioned()){
1151 // Set communicator
1153
1154 // Build partition info with partition boxes if the patch is partitioned
1155 buildPartitionBoxes();
1156 } else {
1157 m_rank = 0;
1158 m_nProcessors = 1;
1159 }
1160#endif
1161}
1162
1169void PatchSkdTree::clear(bool release)
1170{
1171 m_nLeafs = 0;
1172 m_nMinLeafCells = 0;
1173 m_nMaxLeafCells = 0;
1174
1175 if (release) {
1176 std::vector<SkdNode>().swap(m_nodes);
1177 std::vector<std::size_t>().swap(m_cellRawIds);
1178 } else {
1179 m_nodes.clear();
1180 m_cellRawIds.clear();
1181 }
1182
1183#if BITPIT_ENABLE_MPI
1185 if (release) {
1186 std::vector<SkdBox>().swap(m_partitionBoxes);
1187 } else {
1188 m_partitionBoxes.clear();
1189 }
1190#endif
1191}
1192
1199{
1200 return m_patchInfo.getPatch();
1201}
1202
1209{
1210 return m_nodes.size();
1211}
1212
1219{
1220 return m_nLeafs;
1221}
1222
1229const SkdNode & PatchSkdTree::getNode(std::size_t nodeId) const
1230{
1231 return m_nodes[nodeId];
1232}
1233
1240SkdNode & PatchSkdTree::_getNode(std::size_t nodeId)
1241{
1242 return m_nodes[nodeId];
1243}
1244
1251std::size_t PatchSkdTree::evalMaxDepth(std::size_t rootId) const
1252{
1253 if (rootId == SkdNode::NULL_ID) {
1254 return 0;
1255 }
1256
1257 const SkdNode &node = getNode(rootId);
1258
1259 std::size_t depth = 0;
1260 for (int i = SkdNode::CHILD_BEGIN; i != SkdNode::CHILD_END; ++i) {
1261 SkdNode::ChildLocation childLocation = static_cast<SkdNode::ChildLocation>(i);
1262 depth = std::max(evalMaxDepth(node.getChildId(childLocation)), depth);
1263 }
1264 ++depth;
1265
1266 return depth;
1267}
1268
1276void PatchSkdTree::createChildren(std::size_t parentId, std::size_t leafThreshold)
1277{
1278 const SkdNode &parent = getNode(parentId);
1279
1280 // Check if the parent is a leaf
1281 std::size_t parentCellCount = parent.getCellCount();
1282 if (parentCellCount <= leafThreshold) {
1283 createLeaf(parentId);
1284 return;
1285 }
1286
1287 // Evaluate the preferred direction along which elements will be split.
1288 //
1289 // The elements will be split along a plane normal to the direction
1290 // for which the bounding box has the maximum length.
1291 const std::array<double, 3> &parentBoxMin = parent.getBoxMin();
1292 const std::array<double, 3> &parentBoxMax = parent.getBoxMax();
1293
1294 int largerDirection = 0;
1295 double boxMaximumLength = parentBoxMax[largerDirection] - parentBoxMin[largerDirection];
1296 for (int d = 1; d < 3; ++d) {
1297 double length = parentBoxMax[d] - parentBoxMin[d];
1298 if (length > boxMaximumLength) {
1299 largerDirection = d;
1300 boxMaximumLength = length;
1301 }
1302 }
1303
1304 // Split the elements.
1305 //
1306 // Preferred direction is tried first, if the split along this direction
1307 // will produce and empty child (e.g., all the cell's box centroids have
1308 // the same coordinate along the split direction and therefore all the
1309 // cells would be assigned to the left chiled) the split will be performed
1310 // along one of the other directions.
1311 for (int d = 0; d < 3; ++d) {
1312 // Update the split direction
1313 int splitDirection = (largerDirection + d) % 3;
1314
1315 // Get the threshold for the split
1316 double splitThreshold = parent.evalBoxWeightedMean(splitDirection);
1317
1318 // Order the elements
1319 //
1320 // All the elements with a centroid coordinate less or equal than the
1321 // threshold will be assigned to the left child, the others will be
1322 // assigned to the right child.
1323 std::size_t leftBegin = parent.m_cellRangeBegin;
1324 std::size_t leftEnd = parent.m_cellRangeEnd;
1325 std::size_t rightBegin = parent.m_cellRangeBegin;
1326 std::size_t rightEnd = parent.m_cellRangeEnd;
1327 while (true) {
1328 // Update the right begin
1329 while (rightBegin != leftEnd && m_patchInfo.evalCachedBoxMean(m_cellRawIds[rightBegin], splitDirection) <= splitThreshold) {
1330 rightBegin++;
1331 }
1332
1333 // Update the left end
1334 while (rightBegin != leftEnd && m_patchInfo.evalCachedBoxMean(m_cellRawIds[leftEnd - 1], splitDirection) > splitThreshold) {
1335 leftEnd--;
1336 }
1337
1338 // If all the elements are in the right position we can exit
1339 if (rightBegin == leftEnd) {
1340 break;
1341 }
1342
1343 // If left end and and right begin are not equal, that the two ids
1344 // point to misplaced elements. Swap the elements, advance the ids
1345 // and continue iterating.
1346 std::iter_swap(m_cellRawIds.begin() + (leftEnd - 1), m_cellRawIds.begin() + rightBegin);
1347 }
1348
1349 if (leftEnd <= leftBegin || rightEnd <= rightBegin) {
1350 continue;
1351 }
1352
1353 // Create the left child
1354 //
1355 // Adding new nodes may invalidate any pointer and reference to the
1356 // nodes, we cannot store a reference to the parent node.
1357 long leftId = m_nodes.size();
1358 m_nodes.emplace_back(&m_patchInfo, leftBegin, leftEnd);
1359 _getNode(parentId).m_children[static_cast<std::size_t>(SkdNode::CHILD_LEFT)] = leftId;
1360
1361 // Create the right child
1362 //
1363 // Adding new nodes may invalidate any pointer and reference to the
1364 // nodes, we cannot store a reference to the parent node.
1365 long rightId = m_nodes.size();
1366 m_nodes.emplace_back(&m_patchInfo, rightBegin, rightEnd);
1367 _getNode(parentId).m_children[static_cast<std::size_t>(SkdNode::CHILD_RIGHT)] = rightId;
1368
1369 // Done
1370 return;
1371 }
1372
1373 // It was not possible to split the elements. They have all the same
1374 // characteristic position and therefore they will be clustered together
1375 // in a leaf node.
1376 createLeaf(parentId);
1377}
1378
1384void PatchSkdTree::createLeaf(std::size_t nodeId)
1385{
1386 const SkdNode &node = getNode(nodeId);
1387 std::size_t nodeCellCount = node.getCellCount();
1388
1389 ++m_nLeafs;
1390 m_nMinLeafCells = std::min(nodeCellCount, m_nMinLeafCells);
1391 m_nMaxLeafCells = std::max(nodeCellCount, m_nMaxLeafCells);
1392}
1393
1400{
1401 m_threadSafeLookups = enable;
1402}
1403
1410{
1411 return m_threadSafeLookups;
1412}
1413
1414#if BITPIT_ENABLE_MPI
1421void PatchSkdTree::setCommunicator(MPI_Comm communicator)
1422{
1423 // Communication can be set just once
1424 if (isCommunicatorSet()) {
1425 throw std::runtime_error("Skd-tree communicator can be set just once.");
1426 }
1427
1428 // The communicator has to be valid
1429 if (communicator == MPI_COMM_NULL) {
1430 throw std::runtime_error("Skd-tree communicator is not valid.");
1431 }
1432
1433 // Creat a copy of the user-specified communicator
1434 //
1435 // No library routine should use MPI_COMM_WORLD as the communicator;
1436 // instead, a duplicate of a user-specified communicator should always
1437 // be used.
1438 MPI_Comm_dup(communicator, &m_communicator);
1439
1440 // Get MPI information
1441 MPI_Comm_size(m_communicator, &m_nProcessors);
1442 MPI_Comm_rank(m_communicator, &m_rank);
1443}
1444
1449{
1450 if (!isCommunicatorSet()) {
1451 return;
1452 }
1453
1454 int finalizedCalled;
1455 MPI_Finalized(&finalizedCalled);
1456 if (finalizedCalled) {
1457 return;
1458 }
1459
1460 MPI_Comm_free(&m_communicator);
1461}
1462
1470{
1471 return (getCommunicator() != MPI_COMM_NULL);
1472}
1473
1479const MPI_Comm & PatchSkdTree::getCommunicator() const
1480{
1481 return m_communicator;
1482}
1483
1490void PatchSkdTree::buildPartitionBoxes()
1491{
1492 // Recover local bounding box of the root node
1493 const SkdBox &box = getNode(0).getBoundingBox();
1494
1495 // Collect minimum and maximum coordinates of bounding boxes
1496 std::vector<int> count(m_nProcessors, 3);
1497
1498 std::vector<int> displacements(m_nProcessors);
1499 for (int i = 0; i < m_nProcessors; ++i) {
1500 displacements[i] = i*3;
1501 }
1502
1503 std::vector<std::array<double, 3>> minBoxes(m_nProcessors);
1504 minBoxes[m_rank] = box.getBoxMin();
1505 MPI_Allgatherv(MPI_IN_PLACE, 3, MPI_DOUBLE, minBoxes.data(),
1506 count.data(), displacements.data(), MPI_DOUBLE, m_communicator);
1507
1508 std::vector<std::array<double, 3>> maxBoxes(m_nProcessors);
1509 maxBoxes[m_rank] = box.getBoxMax();
1510 MPI_Allgatherv(MPI_IN_PLACE, 3, MPI_DOUBLE, maxBoxes.data(),
1511 count.data(), displacements.data(), MPI_DOUBLE, m_communicator);
1512
1513 // Fill partition boxes
1514 m_partitionBoxes.resize(getPatch().getProcessorCount());
1515 for (int i = 0; i < m_nProcessors; ++i) {
1516 m_partitionBoxes[i] = SkdBox(minBoxes[i], maxBoxes[i]);
1517 }
1518}
1519
1520
1528{
1529 return m_partitionBoxes[rank];
1530}
1531#endif
1532
1533}
The Cell class defines the cells.
Definition cell.hpp:42
bool isInterior() const
Definition cell.cpp:396
long getId() const
Definition element.cpp:510
void evalPointProjection(const std::array< double, 3 > &point, const std::array< double, 3 > *coordinates, std::array< double, 3 > *projection, double *distance) const
Definition element.cpp:1846
std::array< double, 3 > evalNormal(const std::array< double, 3 > *coordinates, const std::array< double, 3 > &orientation={{0., 0., 1.}}, const std::array< double, 3 > &point={{0.5, 0.5, 0.5}}) const
Definition element.cpp:1759
int getVertexCount() const
Definition element.cpp:1152
The PatchKernel class provides an interface for defining patches.
CellConstIterator cellConstBegin() const
CellConstIterator internalCellConstEnd() const
PiercedVector< Cell > & getCells()
CellConstIterator internalCellConstBegin() const
Cell & getCell(long id)
void evalElementBoundingBox(const Element &element, std::array< double, 3 > *minPoint, std::array< double, 3 > *maxPoint) const
ConstProxyVector< std::array< double BITPIT_COMMA 3 > > getElementVertexCoordinates(const Element &element) const
CellConstIterator cellConstEnd() const
const MPI_Comm & getCommunicator() const
virtual long getCellCount() const
long getInternalCellCount() const
double getTol() const
std::size_t evalMaxDepth(std::size_t rootId=0) const
const SkdBox & getPartitionBox(int rank) const
std::size_t getLeafMinCellCount() const
std::size_t getLeafMaxCellCount() const
bool areLookupsThreadSafe() const
void setCommunicator(MPI_Comm communicator)
std::size_t getLeafCount() const
SkdNode & _getNode(std::size_t nodeId)
void enableThreadSafeLookups(bool enable)
const SkdNode & getNode(std::size_t nodeId) const
void build(std::size_t leaftThreshold=1, bool squeezeStorage=false)
std::size_t getNodeCount() const
void clear(bool release=false)
PatchSkdTree(const PatchKernel *patch, bool interiorCellsOnly=false)
const MPI_Comm & getCommunicator() const
const PatchKernel & getPatch() const
id_t getId(const id_t &fallback=-1) const noexcept
Metafunction for generating a pierced kernel.
const_iterator rawFind(std::size_t pos) const noexcept
The PiercedStorageRange allow to iterate using range-based loops over a PiercedStorage.
const_iterator cbegin() const noexcept
const_iterator cend() const noexcept
void initialize(const storage_t *storage)
Metafunction for generating a pierced storage.
__PVS_REFERENCE__ rawAt(std::size_t pos)
Metafunction for generating a pierced vector.
The SkdBox class defines a box.
bool boxIntersectsSphere(const std::array< double, 3 > &center, double radius) const
double evalPointMaxSquareDistance(const std::array< double, 3 > &point) const
const std::array< double, 3 > & getBoxMin() const
double evalPointMaxDistance(const std::array< double, 3 > &point) const
double evalPointMinDistance(const std::array< double, 3 > &point) const
bool isEmpty() const
double evalPointMinSquareDistance(const std::array< double, 3 > &point) const
const std::array< double, 3 > & getBoxMax() const
bool boxContainsPoint(const std::array< double, 3 > &point, double offset) const
The SkdPatchInfo class defines a node of the skd-tree.
double evalPointDistance(const std::array< double, 3 > &point, bool interiorCellsOnly) const
std::vector< long > getCells() const
bool isLeaf() const
long getCell(std::size_t n) const
void updatePointClosestCell(const std::array< double, 3 > &point, bool interiorCellsOnly, long *closestId, double *closestDistance) const
void findPointClosestCell(const std::array< double, 3 > &point, bool interiorCellsOnly, long *closestId, double *closestDistance) const
std::array< double, 3 > evalBoxWeightedMean() const
const SkdBox & getBoundingBox() const
void updatePointClosestCells(const std::array< double, 3 > &point, bool interiorCellsOnly, std::vector< long > *closestIds, double *closestDistance) const
static BITPIT_PUBLIC_API const std::size_t NULL_ID
std::size_t getChildId(ChildLocation child) const
bool hasChild(ChildLocation child) const
std::size_t getCellCount() const
T dotProduct(const std::array< T, d > &x, const std::array< T, d > &y)
std::array< T, d > max(const std::array< T, d > &x, const std::array< T, d > &y)
#define BITPIT_UNUSED(variable)
Definition compiler.hpp:63
#define BITPIT_CREATE_WORKSPACE(workspace, item_type, size, stack_size)
The SkdGlobalCellDistance class allows to define a distance between a point and a cell.
static MPI_Datatype getMPIDatatype()
static void executeMPIMinOperation(SkdGlobalCellDistance *in, SkdGlobalCellDistance *inout, int *len, MPI_Datatype *datatype)
void exportData(int *rank, long *id, double *distance) const
The SkdPatchInfo class allows to store patch information needed for the construction and the utilizat...
std::array< double, 3 > evalCachedBoxMean(std::size_t rawId) const
SkdPatchInfo(const PatchKernel *patch, const std::vector< std::size_t > *cellRawIds)
const PatchKernel & getPatch() const
const std::vector< std::size_t > & getCellRawIds() const
const std::array< std::array< double, 3 >, 2 > & getCachedBox(std::size_t rawId) const
std::size_t getCellRawId(std::size_t n) const
--- layout: doxygen_footer ---