Loading...
Searching...
No Matches
voloctree.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 <cassert>
26#include <cmath>
27
28#include "bitpit_common.hpp"
29
30#include "logger.hpp"
31#include "voloctree.hpp"
32
33namespace bitpit {
34
43
51
58
59#if BITPIT_ENABLE_MPI==1
76VolOctree::VolOctree(MPI_Comm communicator, std::size_t haloSize)
77 : VolumeKernel(communicator, haloSize, ADAPTION_AUTOMATIC, PARTITIONING_ENABLED)
78#else
84#endif
85{
86 // Create the tree
87#if BITPIT_ENABLE_MPI==1
88 m_tree = std::unique_ptr<PabloUniform>(new PabloUniform(PabloUniform::DEFAULT_LOG_FILE, communicator));
89#else
90 m_tree = std::unique_ptr<PabloUniform>(new PabloUniform(PabloUniform::DEFAULT_LOG_FILE));
91#endif
92
93 // Initialize the tree
94#if BITPIT_ENABLE_MPI==1
95 initializeTree(nullptr, haloSize);
96#else
97 initializeTree(nullptr);
98#endif
99
100 // Initialize the patch
101 initialize();
102
103 // Reset
104 //
105 // The function that resets the patch is virtual, but since is called
106 // from the constructor of the patch kernel only the base function is
107 // called.
108 __reset(false);
109}
110
111#if BITPIT_ENABLE_MPI==1
132VolOctree::VolOctree(int dimension, const std::array<double, 3> &origin, double length, double dh, MPI_Comm communicator, std::size_t haloSize)
133 : VolOctree(PatchManager::AUTOMATIC_ID, dimension, origin, length, dh, communicator, haloSize)
134#else
143VolOctree::VolOctree(int dimension, const std::array<double, 3> &origin, double length, double dh)
144 : VolOctree(PatchManager::AUTOMATIC_ID, dimension, origin, length, dh)
145#endif
146{
147}
148
149#if BITPIT_ENABLE_MPI==1
171VolOctree::VolOctree(int id, int dimension, const std::array<double, 3> &origin, double length, double dh, MPI_Comm communicator, std::size_t haloSize)
172 : VolumeKernel(id, dimension, communicator, haloSize, ADAPTION_AUTOMATIC, PARTITIONING_ENABLED)
173#else
183VolOctree::VolOctree(int id, int dimension, const std::array<double, 3> &origin, double length, double dh)
184 : VolumeKernel(id, dimension, ADAPTION_AUTOMATIC)
185#endif
186{
187 // Create the tree
188#if BITPIT_ENABLE_MPI==1
189 m_tree = std::unique_ptr<PabloUniform>(
190 new PabloUniform(origin[0], origin[1], origin[2], length, dimension,
191 PabloUniform::DEFAULT_LOG_FILE, communicator));
192#else
193 m_tree = std::unique_ptr<PabloUniform>(
194 new PabloUniform(origin[0], origin[1], origin[2], length, dimension,
196#endif
197
198 // Initialize the tree
199#if BITPIT_ENABLE_MPI==1
200 initializeTree(nullptr, haloSize);
201#else
202 initializeTree(nullptr);
203#endif
204
205 // Initialize the patch
206 initialize();
207
208 // Reset
209 //
210 // The function that resets the patch is virtual, but since is called
211 // from the constructor of the patch kernel only the base function is
212 // called.
213 __reset(false);
214
215 // Set the dimension
216 //
217 // The function that sets the dimension is virtual, but since is called
218 // from the constructor of the patch kernel only the base function is
219 // called.
220 __setDimension(dimension);
221
222 // Set the bounding
223 setBoundingBox();
224
225 // Initialize refinement markers
226 if (m_tree->getNumOctants() > 0) {
227 int initialLevel = static_cast<int>(std::ceil(log2(std::max(1., length / dh))));
228 m_tree->setMarker((uint32_t) 0, initialLevel);
229 }
230}
231
232#if BITPIT_ENABLE_MPI==1
245VolOctree::VolOctree(std::istream &stream, MPI_Comm communicator, std::size_t haloSize)
246 : VolumeKernel(communicator, haloSize, ADAPTION_AUTOMATIC, PARTITIONING_ENABLED)
247#else
253VolOctree::VolOctree(std::istream &stream)
255#endif
256{
257 // Initialize the tree
258#if BITPIT_ENABLE_MPI==1
259 initializeTree(nullptr, haloSize);
260#else
261 initializeTree(nullptr);
262#endif
263
264 // Initialize the patch
265 initialize();
266
267 // Restore the patch
268 restore(stream);
269}
270
274#if BITPIT_ENABLE_MPI==1
279#endif
284VolOctree::VolOctree(std::unique_ptr<PabloUniform> &&tree, std::unique_ptr<PabloUniform> *adopter)
285 : VolOctree(PatchManager::AUTOMATIC_ID, std::move(tree), adopter)
286{
287}
288
295#if BITPIT_ENABLE_MPI==1
300#endif
306VolOctree::VolOctree(int id, std::unique_ptr<PabloUniform> &&tree, std::unique_ptr<PabloUniform> *adopter)
307#if BITPIT_ENABLE_MPI==1
308 : VolumeKernel(id, tree->getDim(), tree->getComm(), tree->getNofGhostLayers(), ADAPTION_AUTOMATIC, PARTITIONING_ENABLED)
309#else
310 : VolumeKernel(id, tree->getDim(), ADAPTION_AUTOMATIC)
311#endif
312{
313 // Associate the tree
314 assert(tree);
315 m_tree.swap(tree);
316
317 // Initialize the tree
318#if BITPIT_ENABLE_MPI==1
319 initializeTree(adopter, m_tree->getNofGhostLayers());
320#else
321 initializeTree(adopter);
322#endif
323
324 // Initialize the patch
325 initialize();
326
327 // Reset
328 //
329 // The function that resets the patch is virtual, but since is called
330 // from the constructor of the patch kernel only the base function is
331 // called.
332 __reset(false);
333
334 // Set the dimension
335 //
336 // The function that sets the dimension is virtual, but since is called
337 // from the constructor of the patch kernel only the base function is
338 // called.
339 __setDimension(m_tree->getDim());
340}
341
348 : VolumeKernel(other)
349{
350 m_octantLocalFacesOnVertex = other.m_octantLocalFacesOnVertex;
351 m_octantLocalFacesOnEdge = other.m_octantLocalFacesOnEdge;
352 m_octantLocalEdgesOnVertex = other.m_octantLocalEdgesOnVertex;
353
354 m_cellTypeInfo = other.m_cellTypeInfo;
355 m_interfaceTypeInfo = other.m_interfaceTypeInfo;
356
357 m_cellToOctant = other.m_cellToOctant;
358 m_cellToGhost = other.m_cellToGhost;
359 m_octantToCell = other.m_octantToCell;
360 m_ghostToCell = other.m_ghostToCell;
361
362 if (other.m_tree) {
363 m_tree = std::unique_ptr<PabloUniform>(new PabloUniform(*(other.m_tree)));
364 }
365
366 m_treeAdopter = other.m_treeAdopter;
367}
368
375{
376 VolOctree temporary(other);
377 std::swap(temporary, *this);
378
379 return *this;
380}
381
386{
387 if (m_treeAdopter) {
388 m_treeAdopter->swap(m_tree);
389 }
390}
391
397std::unique_ptr<PatchKernel> VolOctree::clone() const
398{
399 return std::unique_ptr<VolOctree>(new VolOctree(*this));
400}
401
406{
407 // Reset the patch kernel
409
410 // Reset the current patch
411 __reset(true);
412}
413
417void VolOctree::__reset(bool resetTree)
418{
419 // Reset the tree
420 if (resetTree) {
421 m_tree->reset();
422 m_tree->setOrigin(std::array<double, 3>{{0., 0., 0.}});
423 m_tree->setL(1.);
424 }
425
426 // Reset cell-to-octants maps
427 m_cellToOctant.clear();
428 m_octantToCell.clear();
429
430 m_cellToGhost.clear();
431 m_ghostToCell.clear();
432}
433
439#if BITPIT_ENABLE_MPI==1
444void VolOctree::initializeTree(std::unique_ptr<PabloUniform> *adopter, std::size_t haloSize)
445#else
446void VolOctree::initializeTree(std::unique_ptr<PabloUniform> *adopter)
447#endif
448{
449 // Initialize logger
450#ifdef NDEBUG
451 m_tree->getLog().setVerbosities(log::LEVEL_WARNING);
452#endif
453
454#if BITPIT_ENABLE_MPI==1
455 // Initialize partitioning
456 if (isCommunicatorSet()) {
457 if (!m_tree->getParallel()) {
458 // Initialize halo size
459 if (haloSize != m_tree->getNofGhostLayers()) {
460 initializeTreeHaloSize(haloSize);
461 }
462
463 // Initialize partitioning
464 initializeTreePartitioning();
465 } else {
466 // Check if the current halo size is equal to the requested one
467 if (haloSize != m_tree->getNofGhostLayers()) {
468 throw std::runtime_error ("Unable to set the requested halo size.");
469 }
470 }
471 }
472#endif
473
474 // Set tree adopter
475 setTreeAdopter(adopter);
476}
477
481void VolOctree::initialize()
482{
483 log::cout() << ">> Initializing Octree mesh" << std::endl;
484
485 // Set the adaption as clean
486 //
487 // Setting the adaptation as dirty guarantees that, at the first patch
488 // updated, all the data structures will be properly initialized.
489 setAdaptionStatus(ADAPTION_DIRTY);
490
491 // Reset the cell and interface type info
492 m_cellTypeInfo = nullptr;
493 m_interfaceTypeInfo = nullptr;
494
495 // Initialize the tolerance
496 //
497 // Since the patch re-implements the function to reset the tolerance,
498 // it has to explicitly initialize the tolerance.
499 resetTol();
500
501 // Set the bounding box as frozen
503}
504
510void VolOctree::setDimension(int dimension)
511{
513
514 __setDimension(dimension);
515}
516
522void VolOctree::__setDimension(int dimension)
523{
524 if (m_tree->getDim() > 0 && dimension != m_tree->getDim()) {
525 throw std::runtime_error ("The dimension does not match the dimension of the octree.");
526 }
527
528 // Initialize local edges/vertex/faces association
529 std::vector<std::vector<int>>().swap(m_octantLocalFacesOnVertex);
530 std::vector<std::vector<int>>().swap(m_octantLocalEdgesOnVertex);
531 std::vector<std::vector<int>>().swap(m_octantLocalFacesOnEdge);
532
533 if (dimension == 3) {
534 m_octantLocalFacesOnVertex.reserve(8);
535 m_octantLocalFacesOnVertex.push_back({{0, 2, 4}});
536 m_octantLocalFacesOnVertex.push_back({{1, 2, 4}});
537 m_octantLocalFacesOnVertex.push_back({{0, 3, 4}});
538 m_octantLocalFacesOnVertex.push_back({{1, 3, 4}});
539 m_octantLocalFacesOnVertex.push_back({{0, 2, 5}});
540 m_octantLocalFacesOnVertex.push_back({{1, 2, 5}});
541 m_octantLocalFacesOnVertex.push_back({{0, 3, 5}});
542 m_octantLocalFacesOnVertex.push_back({{1, 3, 5}});
543
544 m_octantLocalEdgesOnVertex.reserve(8);
545 m_octantLocalEdgesOnVertex.push_back({{0, 2, 4}});
546 m_octantLocalEdgesOnVertex.push_back({{1, 2, 5}});
547 m_octantLocalEdgesOnVertex.push_back({{0, 3, 6}});
548 m_octantLocalEdgesOnVertex.push_back({{1, 3, 7}});
549 m_octantLocalEdgesOnVertex.push_back({{4, 8, 10}});
550 m_octantLocalEdgesOnVertex.push_back({{5, 9, 10}});
551 m_octantLocalEdgesOnVertex.push_back({{6, 8, 11}});
552 m_octantLocalEdgesOnVertex.push_back({{7, 9, 11}});
553
554 m_octantLocalFacesOnEdge.reserve(12);
555 m_octantLocalFacesOnEdge.push_back({{0, 4}});
556 m_octantLocalFacesOnEdge.push_back({{1, 4}});
557 m_octantLocalFacesOnEdge.push_back({{2, 4}});
558 m_octantLocalFacesOnEdge.push_back({{3, 4}});
559 m_octantLocalFacesOnEdge.push_back({{0, 2}});
560 m_octantLocalFacesOnEdge.push_back({{1, 2}});
561 m_octantLocalFacesOnEdge.push_back({{0, 3}});
562 m_octantLocalFacesOnEdge.push_back({{1, 3}});
563 m_octantLocalFacesOnEdge.push_back({{0, 5}});
564 m_octantLocalFacesOnEdge.push_back({{1, 5}});
565 m_octantLocalFacesOnEdge.push_back({{2, 5}});
566 m_octantLocalFacesOnEdge.push_back({{3, 5}});
567 } else {
568 m_octantLocalFacesOnVertex.reserve(4);
569 m_octantLocalFacesOnVertex.push_back({{0, 2}});
570 m_octantLocalFacesOnVertex.push_back({{1, 2}});
571 m_octantLocalFacesOnVertex.push_back({{0, 3}});
572 m_octantLocalFacesOnVertex.push_back({{1, 3}});
573 }
574
575 // Info of the cell type
576 ElementType cellType;
577 if (dimension == 3) {
578 cellType = ElementType::VOXEL;
579 } else {
580 cellType = ElementType::PIXEL;
581 }
582
583 m_cellTypeInfo = &ReferenceElementInfo::getInfo(cellType);
584
585 // Info on the interface type
586 ElementType interfaceType;
587 if (dimension == 3) {
588 interfaceType = ElementType::PIXEL;
589 } else {
590 interfaceType = ElementType::LINE;
591 }
592
593 m_interfaceTypeInfo = &ReferenceElementInfo::getInfo(interfaceType);
594}
595
599void VolOctree::setBoundingBox()
600{
601 std::array<double, 3> minPoint;
602 std::array<double, 3> maxPoint;
603
604 // Get the bounding box from the tree
605 m_tree->getBoundingBox(minPoint, maxPoint);
606
607#if BITPIT_ENABLE_MPI==1
608 // The tree is only evaluating the bounding box of the internal octants,
609 // we need to consider also ghosts cells.
610 CellConstIterator endItr = ghostCellConstEnd();
611 for (CellConstIterator ghostCellItr = ghostCellConstBegin(); ghostCellItr != endItr; ++ghostCellItr) {
612 ConstProxyVector<long> ghostVertexIds = ghostCellItr->getVertexIds();
613 int nGhostCellVertices = ghostVertexIds.size();
614 for (int i = 0; i < nGhostCellVertices; ++i) {
615 const std::array<double, 3> coords = m_vertices[ghostVertexIds[i]].getCoords();
616 for (int d = 0; d < 3; ++d) {
617 minPoint[d] = std::min(coords[d], minPoint[d]);
618 maxPoint[d] = std::max(coords[d], maxPoint[d]);
619 }
620 }
621 }
622#endif
623
624 // Set the bounding box
625 setBoundingBox(minPoint, maxPoint);
626}
627
638void VolOctree::simulateCellUpdate(long id, adaption::Marker marker, std::vector<Cell> *virtualCells, PiercedVector<Vertex, long> *virtualVertices) const
639{
640 // Get virtual post-update octants
641 OctantInfo octantInfo = getCellOctant(id);
642 const Octant *octant = getOctantPointer(octantInfo);
643
644 int8_t markerValue;
645 switch (marker) {
646
647 case adaption::MARKER_COARSEN:
648 markerValue = -1;
649 break;
650
651 case adaption::MARKER_REFINE:
652 markerValue = 1;
653 break;
654
655 default:
656 markerValue = 0;
657
658 }
659
660 int nMaxVirtualOctants;
661 if (getDimension() == 3) {
662 nMaxVirtualOctants = 8;
663 } else {
664 nMaxVirtualOctants = 4;
665 }
666
667 std::vector<Octant> virtualOctants;
668 virtualOctants.reserve(nMaxVirtualOctants);
669 m_tree->expectedOctantAdapt(octant, markerValue, &virtualOctants);
670 std::size_t nVirtualOctants = virtualOctants.size();
671
672 // Create virtual post-update cells
673 int nCellVertices = m_cellTypeInfo->nVertices;
674
675 virtualVertices->clear();
676 virtualVertices->reserve(nCellVertices * nVirtualOctants);
677
678 virtualCells->clear();
679 virtualCells->resize(nVirtualOctants);
680
681 for (std::size_t k = 0; k < nVirtualOctants; ++k) {
682 const Octant *virtualOctant = &(virtualOctants[k]);
683
684 std::unique_ptr<long[]> connectStorage = std::unique_ptr<long[]>(new long[nCellVertices]);
685 for (int i = 0; i < nCellVertices; ++i) {
686 long vertexId = k * nCellVertices + i;
687 connectStorage[i] = vertexId;
688 virtualVertices->emplaceBack(vertexId, vertexId, m_tree->getNode(virtualOctant, i));
689 }
690
691 virtualCells->emplace_back(id, m_cellTypeInfo->type, std::move(connectStorage), true, false, false);
692 }
693}
694
701double VolOctree::evalCellVolume(long id) const
702{
703 OctantInfo octantInfo = getCellOctant(id);
704 const Octant *octant = getOctantPointer(octantInfo);
705
706 return m_tree->getVolume(octant);
707}
708
715std::array<double, 3> VolOctree::evalCellCentroid(long id) const
716{
717 OctantInfo octantInfo = getCellOctant(id);
718 const Octant *octant = getOctantPointer(octantInfo);
719
720 return m_tree->getCenter(octant);
721}
722
729double VolOctree::evalCellSize(long id) const
730{
731 OctantInfo octantInfo = getCellOctant(id);
732 const Octant *octant = getOctantPointer(octantInfo);
733
734 return m_tree->getSize(octant);
735}
736
743double VolOctree::evalInterfaceArea(long id) const
744{
745 const Interface &interface = getInterface(id);
746 long owner = interface.getOwner();
747
748 OctantInfo octantInfo = getCellOctant(owner);
749 const Octant *octant = getOctantPointer(octantInfo);
750
751 return m_tree->getArea(octant);
752}
753
760std::array<double, 3> VolOctree::evalInterfaceNormal(long id) const
761{
762 const Interface &interface = getInterface(id);
763 int ownerFace = interface.getOwnerFace();
764 long owner = interface.getOwner();
765
766 OctantInfo octantInfo = getCellOctant(owner);
767 const Octant *octant = getOctantPointer(octantInfo);
768
769 return m_tree->getNormal(octant, (uint8_t) ownerFace);
770}
771
779{
780 // Search the cell among the internal octants
781 auto octantItr = m_cellToOctant.find(id);
782 if (octantItr != m_cellToOctant.end()) {
783 return OctantInfo(octantItr->second, true);
784 }
785
786 // Search the cell among the ghosts
787 return OctantInfo(m_cellToGhost.at(id), false);
788}
789
796{
797 return *m_tree;
798}
799
806{
807 return *m_tree;
808}
809
816void VolOctree::setTreeAdopter(std::unique_ptr<PabloUniform> *adopter)
817{
818 m_treeAdopter = adopter;
819}
820
827long VolOctree::getOctantId(const OctantInfo &octantInfo) const
828{
829 std::unordered_map<uint32_t, long>::const_iterator octantItr;
830 if (octantInfo.internal) {
831 octantItr = m_octantToCell.find(octantInfo.id);
832 if (octantItr == m_octantToCell.end()) {
833 return Element::NULL_ID;
834 }
835 } else {
836 octantItr = m_ghostToCell.find(octantInfo.id);
837 if (octantItr == m_ghostToCell.end()) {
838 return Element::NULL_ID;
839 }
840 }
841
842 return octantItr->second;
843}
844
852{
853 Octant *octant;
854 if (octantInfo.internal) {
855 octant = m_tree->getOctant(octantInfo.id);
856 } else {
857 octant = m_tree->getGhostOctant(octantInfo.id);
858 }
859
860 return octant;
861}
862
869const Octant * VolOctree::getOctantPointer(const OctantInfo &octantInfo) const
870{
871 const Octant *octant;
872 if (octantInfo.internal) {
873 octant = m_tree->getOctant(octantInfo.id);
874 } else {
875 octant = m_tree->getGhostOctant(octantInfo.id);
876 }
877
878 return octant;
879}
880
887VolOctree::OctantHash VolOctree::evaluateOctantHash(const OctantInfo &octantInfo)
888{
889 uint8_t level = m_tree->getLevel(octantInfo.id);
890 uint64_t morton = m_tree->getMorton(octantInfo.id);
891
892 OctantHash octantHash;
893 octantHash |= morton;
894 octantHash <<= 8;
895 octantHash |= level;
896
897 return octantHash;
898}
899
906int VolOctree::getCellLevel(long id) const
907{
908 OctantInfo octantInfo = getCellOctant(id);
909 const Octant* octant = getOctantPointer(octantInfo);
910
911 return octant->getLevel();
912}
913
921{
922 OctantInfo octantInfo = getCellOctant(id);
923 const Octant *octant = getOctantPointer(octantInfo);
924
925 return m_tree->getFamilySplittingNode(octant);
926}
927
939std::vector<adaption::Info> VolOctree::_adaptionPrepare(bool trackAdaption)
940{
941 BITPIT_UNUSED(trackAdaption);
942
943 // Call pre-adapt routine
944 m_tree->preadapt();
945
946 // Track adaption changes
947 adaption::InfoCollection adaptionData;
948 if (trackAdaption) {
949 // Current rank
950 int currentRank = -1;
951#if BITPIT_ENABLE_MPI==1
952 currentRank = getRank();
953#endif
954
955 // Track internal and ghosts octants that will be coarsend/refined
956 std::vector<uint32_t> treeIds;
957 std::vector<int8_t> treeMarkers;
958 std::vector<bool> treeGhosts;
959 m_tree->getPreMapping(treeIds, treeMarkers, treeGhosts);
960 std::size_t nUpdatedOctants = treeIds.size();
961
962 adaption::Info *adaptionInfo = nullptr;
963 uint64_t previousFatherMorton = PABLO::INVALID_MORTON;
964 for (std::size_t n = 0; n < nUpdatedOctants; ++n) {
965 OctantInfo octantInfo(treeIds[n], !treeGhosts[n]);
966 Octant *octant = getOctantPointer(octantInfo);
967
968 adaption::Type adaptionType;
969 if (treeMarkers[n] > 0) {
970 adaptionType = adaption::TYPE_REFINEMENT;
971 } else {
972 adaptionType = adaption::TYPE_COARSENING;
973 }
974
975 bool createAdaption = true;
976 if (adaptionType == adaption::TYPE_COARSENING) {
977 uint64_t fatherMorton = octant->computeFatherMorton();
978 if (fatherMorton == previousFatherMorton) {
979 createAdaption = false;
980 } else {
981 previousFatherMorton = fatherMorton;
982 }
983 }
984
985 if (createAdaption) {
986 std::size_t adaptionInfoId = adaptionData.insert(adaptionType, adaption::ENTITY_CELL, currentRank);
987 adaptionInfo = &(adaptionData[adaptionInfoId]);
988 }
989
990 assert(adaptionInfo);
991 adaptionInfo->previous.emplace_back(getOctantId(octantInfo));
992 }
993
994#if BITPIT_ENABLE_MPI==1
995 // Ghost cells will be removed
996 if (isPartitioned() && getGhostCellCount() > 0) {
997 CellConstIterator beginItr = ghostCellConstBegin();
998 CellConstIterator endItr = ghostCellConstEnd();
999
1000 std::size_t adaptionInfoId = adaptionData.insert(adaption::TYPE_DELETION, adaption::ENTITY_CELL, currentRank);
1001 adaption::Info &adaptionInfo = adaptionData[adaptionInfoId];
1002 adaptionInfo.previous.reserve(getGhostCellCount());
1003 for (CellConstIterator itr = beginItr; itr != endItr; ++itr) {
1004 adaptionInfo.previous.emplace_back();
1005 long &deletedGhostCellId = adaptionInfo.previous.back();
1006 deletedGhostCellId = itr.getId();
1007 }
1008 }
1009#endif
1010 }
1011
1012 return adaptionData.dump();
1013}
1014
1024std::vector<adaption::Info> VolOctree::_adaptionAlter(bool trackAdaption)
1025{
1026 // Updating the tree
1027 log::cout() << ">> Adapting tree...";
1028
1029 bool emptyPatch = empty();
1030 bool buildMapping = !emptyPatch;
1031 bool updated = m_tree->adapt(buildMapping);
1032
1033 if (!updated && !emptyPatch) {
1034 log::cout() << " Already updated" << std::endl;
1035 return std::vector<adaption::Info>();
1036 }
1037 log::cout() << " Done" << std::endl;
1038
1039 // Sync the patch
1040 return sync(trackAdaption);
1041}
1042
1047{
1048 // Nothing to do
1049}
1050
1060{
1061 m_tree->settleMarkers();
1062}
1063
1071std::vector<adaption::Info> VolOctree::sync(bool trackChanges)
1072{
1073 log::cout() << ">> Syncing patch..." << std::endl;
1074
1075 // Detect if we are in import-from-scratch mode
1076 //
1077 // In import-from-scratch mode we start form an empty patch and we need
1078 // to import all the octants of the tree. If we are importing the tree
1079 // from scratch there are no cells to delete/renumber.
1080 bool importFromScratch = empty();
1081
1082 // Last operation on the tree
1083 ParaTree::Operation lastTreeOperation = m_tree->getLastOperation();
1084 if (lastTreeOperation == ParaTree::OP_ADAPT_UNMAPPED && !importFromScratch) {
1085 throw std::runtime_error ("Unable to sync the patch after an unmapped adaption");
1086 }
1087
1088 // Info on the tree
1089 uint32_t nOctants = static_cast<uint32_t>(m_tree->getNumOctants());
1090 uint32_t nPreviousOctants = static_cast<uint32_t>(m_octantToCell.size());
1091
1092 log::cout() << ">> Number of octants : " << nOctants << std::endl;
1093
1094 // Info on the tree
1095 uint32_t nGhostsOctants = static_cast<uint32_t>(m_tree->getNumGhosts());
1096 uint32_t nPreviousGhosts = static_cast<uint32_t>(m_ghostToCell.size());
1097
1098 // Initialize tracking data
1099 adaption::InfoCollection adaptionData;
1100
1101 // Current rank
1102 int currentRank = -1;
1103#if BITPIT_ENABLE_MPI==1
1104 currentRank = getRank();
1105#endif
1106
1107 // Extract information for transforming the patch
1108 //
1109 // If there are no cells in the mesh we need to import all
1110 // octants.
1111 log::cout() << ">> Extract information for transforming the patch...";
1112
1113 std::vector<bool> unmappedOctants(nPreviousOctants, true);
1114 std::vector<OctantInfo> addedOctants;
1115 std::vector<RenumberInfo> renumberedOctants;
1116 std::vector<DeleteInfo> deletedOctants;
1117
1118 addedOctants.reserve(nOctants + nGhostsOctants);
1119 if (!importFromScratch) {
1120 renumberedOctants.reserve(nPreviousOctants + nPreviousGhosts);
1121 deletedOctants.reserve(nPreviousOctants + nPreviousGhosts);
1122 }
1123
1124 uint32_t treeId = 0;
1125 std::vector<uint32_t> mapper_octantMap;
1126 std::vector<bool> mapper_ghostFlag;
1127 std::vector<int> mapper_octantRank;
1128 while (treeId < (uint32_t) nOctants) {
1129 // Octant mapping
1130 mapper_octantMap.clear();
1131 mapper_ghostFlag.clear();
1132 mapper_octantRank.clear();
1133 if (!importFromScratch) {
1134 m_tree->getMapping(treeId, mapper_octantMap, mapper_ghostFlag, mapper_octantRank);
1135 }
1136
1137 // Adaption type
1138 adaption::Type adaptionType = adaption::TYPE_NONE;
1139 if (importFromScratch) {
1140 adaptionType = adaption::TYPE_CREATION;
1141 } else if (lastTreeOperation == ParaTree::OP_ADAPT_MAPPED) {
1142 bool isNewR = m_tree->getIsNewR(treeId);
1143 if (isNewR) {
1144 adaptionType = adaption::TYPE_REFINEMENT;
1145 } else {
1146 bool isNewC = m_tree->getIsNewC(treeId);
1147 if (isNewC) {
1148 adaptionType = adaption::TYPE_COARSENING;
1149 } else if (treeId != mapper_octantMap.front()) {
1150 adaptionType = adaption::TYPE_RENUMBERING;
1151 }
1152 }
1153#if BITPIT_ENABLE_MPI==1
1154 } else if (lastTreeOperation == ParaTree::OP_LOADBALANCE || lastTreeOperation == ParaTree::OP_LOADBALANCE_FIRST) {
1155 if (currentRank != mapper_octantRank.front()) {
1156 adaptionType = adaption::TYPE_PARTITION_RECV;
1157 } else if (treeId != mapper_octantMap.front()) {
1158 adaptionType = adaption::TYPE_RENUMBERING;
1159 }
1160#endif
1161 }
1162
1163 // If the octant cell has not been modified we can skip to the next
1164 // octant.
1165 if (adaptionType == adaption::TYPE_NONE) {
1166 unmappedOctants[treeId] = false;
1167 ++treeId;
1168 continue;
1169 }
1170
1171 // Re-numbered cells just need to be added to the proper list.
1172 //
1173 // Renumbered cells are not tracked, because the re-numbering
1174 // only happens inside VolOctree.
1175 if (adaptionType == adaption::TYPE_RENUMBERING) {
1176 uint32_t previousTreeId = mapper_octantMap.front();
1177 OctantInfo previousOctantInfo(previousTreeId, !mapper_ghostFlag.front());
1178 long cellId = getOctantId(previousOctantInfo);
1179 renumberedOctants.emplace_back(cellId, treeId);
1180 unmappedOctants[previousTreeId] = false;
1181
1182 // No more work needed, skip to the next octant
1183 ++treeId;
1184 continue;
1185 }
1186
1187 // Handle other kind of adaption
1188 //
1189 // New octants need to be imported into the patch,
1190 // whereas cells associated to previous octants
1191 // need to be removed.
1192 //
1193 // If the user want to track adaption, adaption
1194 // data needs to be filled.
1195
1196 // Current tree ids that will be imported
1197 long nCurrentTreeIds;
1198 if (importFromScratch) {
1199 nCurrentTreeIds = nOctants - treeId;
1200 } else if (adaptionType == adaption::TYPE_REFINEMENT) {
1201 nCurrentTreeIds = uipow(2, getDimension());
1202 } else {
1203 nCurrentTreeIds = 1;
1204 }
1205
1206 const long lastCurrentTreeId = treeId + nCurrentTreeIds;
1207 for (int currentTreeId = treeId; currentTreeId < lastCurrentTreeId; ++currentTreeId) {
1208 addedOctants.emplace_back(currentTreeId, true);
1209 }
1210
1211 // Cells that will be removed
1212 //
1213 // Mark the cells associated to previous local octants for deletion.
1214 if (!importFromScratch) {
1215 int nPreviousTreeIds = mapper_octantMap.size();
1216 for (int k = 0; k < nPreviousTreeIds; ++k) {
1217#if BITPIT_ENABLE_MPI==1
1218 // Only local cells can be deleted
1219 if (mapper_octantRank[k] != currentRank) {
1220 continue;
1221 }
1222#endif
1223
1224 // Ghost octants will be processed later
1225 if (mapper_ghostFlag[k]) {
1226 continue;
1227 }
1228
1229 // Mark previous octant for deletion
1230 uint32_t previousTreeId = mapper_octantMap[k];
1231 OctantInfo previousOctantInfo(previousTreeId, !mapper_ghostFlag[k]);
1232 long cellId = getOctantId(previousOctantInfo);
1233 deletedOctants.emplace_back(cellId, adaptionType);
1234
1235 unmappedOctants[previousTreeId] = false;
1236 }
1237 }
1238
1239 // Adaption tracking
1240 //
1241 // The adaption info associated to the octants that has been received
1242 // from external partitions will contain the current octants sorted by
1243 // their tree id (we are looping over the octants in that order), this
1244 // is the same order that will be used on the process that has sent
1245 // the octants. Since the order is the same, the two processes are able
1246 // to exchange cell data without any additional extra communication
1247 // (they already know the list of cells for which data is needed and
1248 // the order in which these data will be sent).
1249 if (trackChanges) {
1250 // Rank assocated to the adaption info
1251 int rank = currentRank;
1252#if BITPIT_ENABLE_MPI==1
1253 if (adaptionType == adaption::TYPE_PARTITION_RECV) {
1254 rank = mapper_octantRank[0];
1255 }
1256#endif
1257
1258 // Get the adaption info
1259 std::size_t infoId = adaptionData.insert(adaptionType, adaption::ENTITY_CELL, rank);
1260 adaption::Info &adaptionInfo = adaptionData[infoId];
1261
1262 // Current status
1263 //
1264 // We don't know the id of the current status, because those
1265 // cells are not yet in the mesh. Store the trre id, and
1266 // make the translation later.
1267 //
1268 // WARNING: tree id are uint32_t wherase adaptionInfo stores
1269 // id as long.
1270 adaptionInfo.current.reserve(nCurrentTreeIds);
1271 auto addedOctantsIter = addedOctants.cend() - nCurrentTreeIds;
1272 while (addedOctantsIter != addedOctants.cend()) {
1273 adaptionInfo.current.emplace_back();
1274 long &adaptionId = adaptionInfo.current.back();
1275 adaptionId = (*addedOctantsIter).id;
1276
1277 addedOctantsIter++;
1278 }
1279
1280 // Previous cells
1281 //
1282 // A coarsening can merge togheter cells of different processes.
1283 // However, since the coarsening is limited to one level, the
1284 // previous cells will always be internal or among the ghost of
1285 // the current process.
1286 int nPreviousCellIds = mapper_octantMap.size();
1287 adaptionInfo.previous.reserve(nPreviousCellIds);
1288 for (int k = 0; k < nPreviousCellIds; ++k) {
1289 long previousCellId;
1290#if BITPIT_ENABLE_MPI==1
1291 if (mapper_octantRank[k] != currentRank) {
1292 previousCellId = Cell::NULL_ID;
1293 } else
1294#endif
1295 {
1296 OctantInfo previousOctantInfo(mapper_octantMap[k], !mapper_ghostFlag[k]);
1297 previousCellId = getOctantId(previousOctantInfo);
1298 }
1299
1300 adaptionInfo.previous.emplace_back();
1301 long &adaptionId = adaptionInfo.previous.back();
1302 adaptionId = previousCellId;
1303 }
1304 }
1305
1306 // Incremente tree id
1307 treeId += nCurrentTreeIds;
1308 }
1309
1310 log::cout() << " Done" << std::endl;
1311
1312#if BITPIT_ENABLE_MPI==1
1313 // New ghost octants need to be added
1314 for (uint32_t treeId = 0; treeId < (uint32_t) nGhostsOctants; ++treeId) {
1315 addedOctants.emplace_back(treeId, false);
1316 }
1317#endif
1318
1319 // Remove octants that are no more in the tree
1320 if (!importFromScratch) {
1321#if BITPIT_ENABLE_MPI==1
1322 // Cells that have been send to other processes need to be removed
1323 PabloUniform::LoadBalanceRanges loadBalanceRanges = m_tree->getLoadBalanceRanges();
1324 for (const auto &rankEntry : loadBalanceRanges.sendRanges) {
1325 int rank = rankEntry.first;
1326 if (rank == currentRank) {
1327 continue;
1328 }
1329
1330 adaption::Type deletionType;
1331 if (loadBalanceRanges.sendAction == PabloUniform::LoadBalanceRanges::ACTION_DELETE) {
1332 deletionType = adaption::TYPE_DELETION;
1333 } else {
1334 deletionType = adaption::TYPE_PARTITION_SEND;
1335 }
1336
1337 uint32_t beginTreeId = rankEntry.second[0];
1338 uint32_t endTreeId = rankEntry.second[1];
1339 for (uint32_t treeId = beginTreeId; treeId < endTreeId; ++treeId) {
1340 OctantInfo octantInfo(treeId, true);
1341 long cellId = getOctantId(octantInfo);
1342 deletedOctants.emplace_back(cellId, deletionType, rank);
1343 unmappedOctants[treeId] = false;
1344 }
1345 }
1346
1347 // Previous ghosts cells need to be removed
1348 if (nPreviousGhosts > 0) {
1349 for (uint32_t ghostTreeId = 0; ghostTreeId < nPreviousGhosts; ++ghostTreeId) {
1350 OctantInfo ghostOctantInfo(ghostTreeId, false);
1351 long ghostCellId = getOctantId(ghostOctantInfo);
1352 deletedOctants.emplace_back(ghostCellId, adaption::TYPE_DELETION);
1353 }
1354 }
1355#endif
1356
1357 // Remove unmapped octants
1358 //
1359 // A coarsening that merges cells from different processes, can leave, on
1360 // the processes which own the ghost octants involved in the coarsening,
1361 // some octants that are not mapped.
1362 for (uint32_t previousTreeId = 0; previousTreeId < nPreviousOctants; ++previousTreeId) {
1363 if (unmappedOctants[previousTreeId]) {
1364 OctantInfo octantInfo = OctantInfo(previousTreeId, true);
1365 long cellId = getOctantId(octantInfo);
1366 deletedOctants.emplace_back(cellId, adaption::TYPE_DELETION);
1367 }
1368 }
1369 }
1370
1371 // Enable manual adaption
1372 AdaptionMode previousAdaptionMode = getAdaptionMode();
1374
1375 // Renumber cells
1376 renumberCells(renumberedOctants);
1377
1378 // Remove deleted cells
1379 StitchInfo stitchInfo;
1380 if (deletedOctants.size() > 0) {
1381 log::cout() << ">> Removing non-existing cells...";
1382
1383 // Track changes
1384 //
1385 // The adaption info associated to the octants that has been sent
1386 // to external partitions will contain the current octants sorted by
1387 // their tree id (they were added to the deleted octants list in that
1388 // order), this is the same order that will be used on the process
1389 // that has received the octants. Since the order is the same, the two
1390 // processes are able to exchange cell data without any additional
1391 // extra communication (they already know the list of cells for which
1392 // data is needed and the order in which these data will be sent).
1393 if (trackChanges) {
1394 // Track deleted cells
1395 std::unordered_set<long> sendAdaptionInfo;
1396 std::unordered_set<long> removedInterfaces;
1397 for (const DeleteInfo &deleteInfo : deletedOctants) {
1398 // Cell id
1399 long cellId = deleteInfo.cellId;
1400
1401 // Adaption tracking
1402 //
1403 // Only cells deleted from a real deletion or a partition send
1404 // needs to be tracked here, the other cells will be tracked
1405 // where the adaption that deleted the cell is tracked.
1406 adaption::Type adaptionType = deleteInfo.trigger;
1407 bool adaptionIsDeletion = (adaptionType == adaption::TYPE_DELETION);
1408 bool adaptionIsSend = (adaptionType == adaption::TYPE_PARTITION_SEND);
1409
1410 if (adaptionIsDeletion || adaptionIsSend) {
1411 int rank = deleteInfo.rank;
1412 std::size_t adaptionInfoId = adaptionData.insert(adaptionType, adaption::ENTITY_CELL, rank);
1413 adaption::Info &adaptionInfo = adaptionData[adaptionInfoId];
1414 adaptionInfo.previous.emplace_back();
1415 long &deletedId = adaptionInfo.previous.back();
1416 deletedId = cellId;
1417
1418 // Keep track of adaption info for the send cells
1419 if (adaptionIsSend) {
1420 sendAdaptionInfo.insert(adaptionInfoId);
1421 }
1422 }
1423
1424 // List of deleted interfaces
1425 const Cell &cell = m_cells.at(cellId);
1426 long nCellInterfaces = cell.getInterfaceCount();
1427 const long *interfaces = cell.getInterfaces();
1428 for (int k = 0; k < nCellInterfaces; ++k) {
1429 long interfaceId = interfaces[k];
1430 if (interfaceId >= 0) {
1431 removedInterfaces.insert(interfaceId);
1432 }
1433 }
1434 }
1435
1436#if BITPIT_ENABLE_MPI==1
1437 // Sort sent cells
1438 //
1439 // We cannot use native functions to evaluate the position of the
1440 // cells because the octants associated to the cells no longer
1441 // exist on the octree. The cells are still there, therefore we
1442 // can evaluate the cell positions using generic patch functions.
1443 for (int adaptionInfoId : sendAdaptionInfo) {
1444 adaption::Info &adaptionInfo = adaptionData[adaptionInfoId];
1445 std::sort(adaptionInfo.previous.begin(), adaptionInfo.previous.end(), CellPositionLess(*this, false));
1446 }
1447#endif
1448
1449 // Adaption info for the deleted interfaces
1450 std::size_t adaptionInfoId = adaptionData.insert(adaption::TYPE_DELETION, adaption::ENTITY_INTERFACE, currentRank);
1451 adaption::Info &adaptionInfo = adaptionData[adaptionInfoId];
1452 adaptionInfo.previous.reserve(removedInterfaces.size());
1453 for (long interfaceId : removedInterfaces) {
1454 adaptionInfo.previous.emplace_back();
1455 long &deletedInterfaceId = adaptionInfo.previous.back();
1456 deletedInterfaceId = interfaceId;
1457 }
1458 }
1459
1460 // Delete cells
1461 stitchInfo = deleteCells(deletedOctants);
1462
1463 log::cout() << " Done" << std::endl;
1464 log::cout() << ">> Cells removed: " << deletedOctants.size() << std::endl;
1465 }
1466
1467 std::vector<DeleteInfo>().swap(deletedOctants);
1468
1469 // Import added cells
1470 std::vector<long> createdCells;
1471 if (addedOctants.size() > 0) {
1472 log::cout() << ">> Importing new octants...";
1473
1474 createdCells = importCells(addedOctants, stitchInfo);
1475
1476 log::cout() << " Done" << std::endl;
1477 log::cout() << ">> Octants imported: " << addedOctants.size() << std::endl;
1478 }
1479
1480 StitchInfo().swap(stitchInfo);
1481
1482 // Restore previous adaption mode
1483 setAdaptionMode(previousAdaptionMode);
1484
1485 // Track mesh adaption
1486 if (trackChanges) {
1487 // Complete mesh adaption info for the cells
1488 for (auto &adaptionInfo : adaptionData.data()) {
1489 if (adaptionInfo.entity != adaption::ENTITY_CELL) {
1490 continue;
1491 }
1492
1493 // Map ids of the added cells
1494 int nCurrentIds = adaptionInfo.current.size();
1495 for (int k = 0; k < nCurrentIds; ++k) {
1496 long cellId = m_octantToCell.at(adaptionInfo.current[k]);
1497 adaptionInfo.current[k] = cellId;
1498 }
1499
1500#if BITPIT_ENABLE_MPI==1
1501 // Sort received cells
1502 //
1503 // To match the sorting done on the procesor that sent the cells,
1504 // we don't use the native functions to evaluate the position of
1505 // the cells.
1506 adaption::Type adaptionType = adaptionInfo.type;
1507 if (adaptionType == adaption::TYPE_PARTITION_RECV) {
1508 std::sort(adaptionInfo.current.begin(), adaptionInfo.current.end(), CellPositionLess(*this, false));
1509 }
1510#endif
1511 }
1512
1513#if BITPIT_ENABLE_MPI==1
1514 // Track created ghosts cells
1515 if (nGhostsOctants > 0) {
1516 std::size_t adaptionInfoId = adaptionData.insert(adaption::TYPE_CREATION, adaption::ENTITY_CELL, currentRank);
1517 adaption::Info &adaptionInfo = adaptionData[adaptionInfoId];
1518
1519 adaptionInfo.current.reserve(nGhostsOctants);
1520 auto cellIterator = m_cellToGhost.cbegin();
1521 while (cellIterator != m_cellToGhost.cend()) {
1522 adaptionInfo.current.emplace_back();
1523 long &adaptionId = adaptionInfo.current.back();
1524 adaptionId = cellIterator->first;
1525
1526 cellIterator++;
1527 }
1528 }
1529#endif
1530
1531 // Track created interfaces
1532 if (createdCells.size() > 0) {
1533 // List of unique interfaces that have been created
1534 std::unordered_set<long> createdInterfaces;
1535 for (const auto &cellId : createdCells) {
1536 const Cell &cell = m_cells.at(cellId);
1537 long nCellInterfaces = cell.getInterfaceCount();
1538 const long *interfaces = cell.getInterfaces();
1539 for (int k = 0; k < nCellInterfaces; ++k) {
1540 long interfaceId = interfaces[k];
1541 if (interfaceId >= 0) {
1542 createdInterfaces.insert(interfaceId);
1543 }
1544 }
1545 }
1546
1547 // Adaption info
1548 std::size_t infoId = adaptionData.insert(adaption::TYPE_CREATION, adaption::ENTITY_INTERFACE, currentRank);
1549 adaption::Info &adaptionInfo = adaptionData[infoId];
1550 adaptionInfo.current.reserve(createdInterfaces.size());
1551 for (long interfaceId : createdInterfaces) {
1552 adaptionInfo.current.emplace_back();
1553 long &createdInterfaceId = adaptionInfo.current.back();
1554 createdInterfaceId = interfaceId;
1555 }
1556 }
1557 }
1558
1559 // Done
1560 return adaptionData.dump();
1561}
1562
1563
1572VolOctree::StitchInfo VolOctree::deleteCells(const std::vector<DeleteInfo> &deletedOctants)
1573{
1574 // Info of the cells
1575 int nCellVertices = m_cellTypeInfo->nVertices;
1576 int nCellFaces = m_cellTypeInfo->nFaces;
1577
1578 // Delete the cells
1579 std::unordered_set<long> deadVertices;
1580
1581 std::vector<long> deadCells;
1582 deadCells.reserve(deletedOctants.size());
1583 for (const DeleteInfo &deleteInfo : deletedOctants) {
1584 long cellId = deleteInfo.cellId;
1585 const Cell &cell = m_cells[cellId];
1586
1587 // List vertices to remove
1588 //
1589 // For now, all cell vertices will be listed. Later, the vertex of
1590 // the dangling faces will be removed from the list.
1591 ConstProxyVector<long> cellVertexIds = cell.getVertexIds();
1592 deadVertices.insert(cellVertexIds.cbegin(), cellVertexIds.cend());
1593
1594 // Remove patch-tree associations
1595 const OctantInfo &octantInfo = getCellOctant(cellId);
1596 uint32_t treeId = octantInfo.id;
1597
1598 if (cell.isInterior()) {
1599 m_cellToOctant.erase(cellId);
1600
1601 auto octantToCellItr = m_octantToCell.find(treeId);
1602 assert(octantToCellItr != m_octantToCell.end());
1603 if (octantToCellItr->second == cellId) {
1604 m_octantToCell.erase(octantToCellItr);
1605 }
1606 } else {
1607 m_cellToGhost.erase(cellId);
1608
1609 auto ghostToCellItr = m_ghostToCell.find(treeId);
1610 assert(ghostToCellItr != m_ghostToCell.end());
1611 if (ghostToCellItr->second == cellId) {
1612 m_ghostToCell.erase(ghostToCellItr);
1613 }
1614 }
1615
1616 // Cell needs to be removed
1617 deadCells.push_back(cellId);
1618 }
1619
1620 PatchKernel::deleteCells(deadCells);
1621
1622 // Prune cell adjacencies and interfaces
1623 //
1624 // At this stage we cannot fully update adjacencies and interfaces, but
1625 // we need to remove stale adjacencies and interfaces.
1627
1629
1630 // All the vertices belonging to the dangling cells has to be kept
1631 //
1632 // We need to keep all the vertices that lie on a dangling face and belong
1633 // to cells that were not deleted. These are the vertices of the dangling
1634 // faces, plus all the vertices that lie on the borders of the dangling
1635 // faces. Latter vertices arise from three-dimensional configurations where
1636 // a small cell has been deleted and, among its non-deleted neighburs, there
1637 // areboth larger cells and cells of the same size. In this case, a vertex
1638 // of the deleted small cell can lie on one of the edges of a bigger cell
1639 // and belong also to a smaller cell. If only the bigger cell is dangling,
1640 // that vertex will lie on a dangling face (it's on the edge of the cell,
1641 // hence on the border of the face), but it's not one of the vertices of
1642 // the dangling cells.
1643 //
1644 // To identify all vertices that need to be kept, we consider the vertices
1645 // of the dangling faces and the vertices of the smaller neighbouring faces
1646 // (i.e., the faces of the smaller neigbours of the dangling cells that lie
1647 // on the faces of the dangling cells).
1648 //
1649 // Morover we need to build a map between the patch numbering and the
1650 // octree numbering of the vertices of the dangling cells. This map will
1651 // be used when imprting the octants to stitch the imported octants to
1652 // the existing cells.
1653 StitchInfo stitchVertices;
1654 for (const auto &entry: m_alteredCells) {
1655 // Consider only dangling faces
1656 if (!testAlterationFlags(entry.second, FLAG_DANGLING)) {
1657 continue;
1658 }
1659
1660 // Vertices of the cell
1661 long cellId = entry.first;
1662 const Cell &cell = m_cells[cellId];
1663 ConstProxyVector<long> cellVertexIds = cell.getVertexIds();
1664
1665 OctantInfo octantInfo = getCellOctant(cellId);
1666 Octant *octant = getOctantPointer(octantInfo);
1667
1668 for (int k = 0; k < nCellVertices; ++k) {
1669 long vertexId = cellVertexIds[k];
1670 uint64_t vertexTreeKey = m_tree->computeNodePersistentKey(octant, k);
1671 stitchVertices.insert({vertexTreeKey, vertexId});
1672 deadVertices.erase(vertexId);
1673 }
1674
1675 // Vertices of neighbours
1676 //
1677 // We need to select the neighbours smaller than the current cell and
1678 // keep the vertices of the faces that are shared with the current cell.
1679 int cellLevel = octant->getLevel();
1680
1681 for (int face = 0; face < nCellFaces; ++face) {
1682 int nFaceAdjacencies = cell.getAdjacencyCount(face);
1683 const long *faceAdjacencies = cell.getAdjacencies(face);
1684 for (int i = 0; i < nFaceAdjacencies; ++i) {
1685 int neighId = faceAdjacencies[i];
1686 OctantInfo neighOctantInfo = getCellOctant(neighId);
1687 Octant *neighOctant = getOctantPointer(neighOctantInfo);
1688 int neighLevel = neighOctant->getLevel();
1689 if (neighLevel <= cellLevel) {
1690 continue;
1691 }
1692
1693 const Cell &neigh = m_cells[neighId];
1694 int neighFace = findAdjoinNeighFace(cell, face, neigh);
1695 const int *localNeighFaceConnect = m_cellTypeInfo->faceConnectStorage[neighFace].data();
1696 ConstProxyVector<long> faceVertexIds = neigh.getFaceVertexIds(neighFace);
1697 std::size_t nFaceVertexIds = faceVertexIds.size();
1698 for (std::size_t k = 0; k < nFaceVertexIds; ++k) {
1699 long vertexId = faceVertexIds[k];
1700 uint64_t vertexTreeKey = m_tree->computeNodePersistentKey(neighOctant, localNeighFaceConnect[k]);
1701 stitchVertices.insert({vertexTreeKey, vertexId});
1702 deadVertices.erase(vertexId);
1703 }
1704 }
1705 }
1706 }
1707
1708 // Delete the vertices
1709 PatchKernel::deleteVertices(deadVertices);
1710
1711 // Done
1712 return stitchVertices;
1713}
1714
1721void VolOctree::renumberCells(const std::vector<RenumberInfo> &renumberedOctants)
1722{
1723 // Remove old patch-to-tree and tree-to-patch associations
1724 for (const RenumberInfo &renumberInfo : renumberedOctants) {
1725 long cellId = renumberInfo.cellId;
1726 if (!m_cells[cellId].isInterior()) {
1727 continue;
1728 }
1729
1730 const OctantInfo &previousOctantInfo = getCellOctant(cellId);
1731 uint32_t previousTreeId = previousOctantInfo.id;
1732
1733 m_octantToCell.erase(previousTreeId);
1734 }
1735
1736 // Create new patch-to-tree and tree-to-patch associations
1737 for (const RenumberInfo &renumberInfo : renumberedOctants) {
1738 long cellId = renumberInfo.cellId;
1739 if (!m_cells[cellId].isInterior()) {
1740 continue;
1741 }
1742
1743 uint32_t treeId = renumberInfo.newTreeId;
1744
1745 m_cellToOctant[cellId] = treeId;
1746 m_octantToCell[treeId] = cellId;
1747 }
1748}
1749
1760std::vector<long> VolOctree::importCells(const std::vector<OctantInfo> &octantInfoList,
1761 StitchInfo &stitchInfo, std::istream *restoreStream)
1762{
1763 // Create the new vertices
1764 int nCellVertices = m_cellTypeInfo->nVertices;
1765 for (const OctantInfo &octantInfo : octantInfoList) {
1766 Octant *octant = getOctantPointer(octantInfo);
1767 for (int k = 0; k < nCellVertices; ++k) {
1768 uint64_t vertexTreeKey = m_tree->computeNodePersistentKey(octant, k);
1769 if (stitchInfo.count(vertexTreeKey) == 0) {
1770 // Vertex coordinates
1771 std::array<double, 3> nodeCoords = m_tree->getNode(octant, k);
1772
1773 // Create the vertex
1774 long vertexId;
1775 if (!restoreStream) {
1776#if BITPIT_ENABLE_MPI==1
1777 VertexIterator vertexIterator = addVertex(std::move(nodeCoords));
1778#else
1779 VertexIterator vertexIterator = addVertex(std::move(nodeCoords));
1780#endif
1781 vertexId = vertexIterator.getId();
1782 } else {
1783 utils::binary::read(*restoreStream, vertexId);
1784
1785#if BITPIT_ENABLE_MPI==1
1786 int rank;
1787 utils::binary::read(*restoreStream, rank);
1788
1789 restoreVertex(nodeCoords, rank, vertexId);
1790#else
1791 int dummyRank;
1792 utils::binary::read(*restoreStream, dummyRank);
1793
1794 restoreVertex(nodeCoords, vertexId);
1795#endif
1796 }
1797
1798 // Add the vertex to the stitching info
1799 stitchInfo[vertexTreeKey] = vertexId;
1800 }
1801 }
1802 }
1803
1804 // Reserve space for the maps
1805 long nOctants = m_tree->getNumOctants();
1806 m_cellToOctant.reserve(nOctants);
1807 m_octantToCell.reserve(nOctants);
1808
1809 long nGhostsOctants = m_tree->getNumGhosts();
1810 m_cellToGhost.reserve(nGhostsOctants);
1811 m_ghostToCell.reserve(nGhostsOctants);
1812
1813 // Add the cells
1814 size_t octantInfoListSize = octantInfoList.size();
1815 m_cells.reserve(m_cells.size() + octantInfoListSize);
1816
1817 std::vector<long> createdCells(octantInfoListSize);
1818 for (size_t i = 0; i < octantInfoListSize; ++i) {
1819 const OctantInfo &octantInfo = octantInfoList[i];
1820
1821 // Octant connectivity
1822 Octant *octant = getOctantPointer(octantInfo);
1823
1824 // Cell connectivity
1825 std::unique_ptr<long[]> cellConnect = std::unique_ptr<long[]>(new long[nCellVertices]);
1826 for (int k = 0; k < nCellVertices; ++k) {
1827 uint64_t vertexTreeKey = m_tree->computeNodePersistentKey(octant, k);
1828 cellConnect[k] = stitchInfo.at(vertexTreeKey);
1829 }
1830
1831#if BITPIT_ENABLE_MPI==1
1832 // Cell owner
1833 int owner;
1834 if (octantInfo.internal) {
1835 owner = getRank();
1836 } else {
1837 uint64_t globalTreeId = m_tree->getGhostGlobalIdx(octantInfo.id);
1838 owner = m_tree->getOwnerRank(globalTreeId);
1839 }
1840
1841 // Cell halo layer
1842 int haloLayer = m_tree->getGhostLayer(octant);
1843#endif
1844
1845 // Add cell
1846 long cellId;
1847 if (!restoreStream) {
1848#if BITPIT_ENABLE_MPI==1
1849 CellIterator cellIterator = addCell(m_cellTypeInfo->type, std::move(cellConnect), owner, haloLayer);
1850#else
1851 CellIterator cellIterator = addCell(m_cellTypeInfo->type, std::move(cellConnect));
1852#endif
1853 cellId = cellIterator.getId();
1854 } else {
1855 utils::binary::read(*restoreStream, cellId);
1856
1857#if BITPIT_ENABLE_MPI==1
1858 restoreCell(m_cellTypeInfo->type, std::move(cellConnect), owner, haloLayer, cellId);
1859#else
1860 restoreCell(m_cellTypeInfo->type, std::move(cellConnect), cellId);
1861#endif
1862 }
1863
1864 // Create patch-tree associations
1865 if (octantInfo.internal) {
1866 m_cellToOctant.insert({{cellId, octantInfo.id}});
1867 m_octantToCell.insert({{octantInfo.id, cellId}});
1868 } else {
1869 m_cellToGhost.insert({{cellId, octantInfo.id}});
1870 m_ghostToCell.insert({{octantInfo.id, cellId}});
1871 }
1872
1873 // Add the cell to the list of created cells
1874 createdCells[i] = cellId;
1875 }
1876
1877 // Update first ghost and last internal information
1878 if (restoreStream) {
1880#if BITPIT_ENABLE_MPI==1
1882#endif
1883 }
1884
1885 // Update cell PIDs
1886 if (restoreStream) {
1887 for (Cell &cell : getCells()) {
1888 int PID;
1889 utils::binary::read(*restoreStream, PID);
1890 cell.setPID(PID);
1891 }
1892 }
1893
1894 // Update adjacencies
1896
1897 // Update interfaces
1898 if (!restoreStream) {
1900 }
1901
1902 // Done
1903 return createdCells;
1904}
1905
1913{
1914 // Tree information
1915 int maxLevel = m_tree->getMaxDepth();
1916
1917 // Face information
1918 int nCellFaces = m_cellTypeInfo->nFaces;
1919 const uint8_t *oppositeFace = m_tree->getOppface();
1920
1921 // Count cells with dirty adjacencies
1922 std::vector<std::size_t> nDirtyAdjacenciesCellsByLevel(maxLevel + 1, 0);
1923 for (const auto &entry : m_alteredCells) {
1924 AlterationFlags cellAlterationFlags = entry.second;
1925 if (!testAlterationFlags(cellAlterationFlags, FLAG_ADJACENCIES_DIRTY)) {
1926 continue;
1927 }
1928
1929 long cellId = entry.first;
1930 int cellLevel = getCellLevel(cellId);
1931
1932 ++nDirtyAdjacenciesCellsByLevel[cellLevel];
1933 }
1934
1935 // Group altered cells by their tree level
1936 std::vector<std::vector<long>> hierarchicalCellIds(maxLevel + 1);
1937 for (int level = 0; level <= maxLevel; ++level) {
1938 hierarchicalCellIds[level].reserve(nDirtyAdjacenciesCellsByLevel[level]);
1939 }
1940
1941 for (const auto &entry : m_alteredCells) {
1942 AlterationFlags cellAlterationFlags = entry.second;
1943 if (!testAlterationFlags(cellAlterationFlags, FLAG_ADJACENCIES_DIRTY)) {
1944 continue;
1945 }
1946
1947 long cellId = entry.first;
1948 int cellLevel = getCellLevel(cellId);
1949 hierarchicalCellIds[cellLevel].push_back(cellId);
1950 }
1951
1952 // Update the adjacencies
1953 std::vector<uint32_t> neighTreeIds;
1954 std::vector<bool> neighGhostFlags;
1955 for (int level = 0; level <= maxLevel; ++level) {
1956 for (long cellId : hierarchicalCellIds[level]) {
1957 Cell &cell = m_cells[cellId];
1958 OctantInfo octantInfo = getCellOctant(cellId);
1959 Octant *octant = getOctantPointer(octantInfo);
1960 for (int face = 0; face < nCellFaces; ++face) {
1961 // Check if the face needs to be processed
1962 //
1963 // If the face has no adjacencies, we need to process it to
1964 // figure out if it is a border or it has some neighbours.
1965 //
1966 // If the face has only one adjacency and the neighbour's level
1967 // is smaller or equal than the one of the current cell, all
1968 // face adjacencies have already been found.
1969 //
1970 // If the face has only one adjacency and the neighbour's level
1971 // is greater than the one of the current cell, there are still
1972 // adjacencies to be found. The adjacency associated with the
1973 // face is an adjacency that was not created during this update
1974 // (cells are processed in level increasing order, only cells
1975 // with a level smaller or equal to the one of the current cell
1976 // may already have been processed during this update). Since
1977 // the cell is bigger that its neighbour it cannot have only
1978 // one adjacency, there are other adjacencies to be found in
1979 // the cell marked as dirty.
1980 //
1981 // If the face has multiple adjacencies, we need to figure out
1982 // if all the adjacencies have already been found. Current
1983 // adjacencies were not created during this update (since there
1984 // are multiple adjacencies, those adjacencies are cells smaller
1985 // than the current one, however since cells are processed in
1986 // level increasing order, only cells with a level smaller or
1987 // equal to the one of the current cell may already have been
1988 // processed during this update). To check if all adjacencies
1989 // have been found, we check if the area coverd by the current
1990 // adjacencies is equal to the face area.
1991 int nFaceAdjacencies = cell.getAdjacencyCount(face);
1992 if (nFaceAdjacencies > 0) {
1993 if (nFaceAdjacencies == 1) {
1994 long cellLevel = octant->getLevel();
1995 long neighId = cell.getAdjacency(face, 0);
1996 long neighLevel = getCellLevel(neighId);
1997 if (neighLevel <= cellLevel) {
1998 continue;
1999 }
2000 } else {
2001 uint64_t neighsArea = 0;
2002 const long *faceAdjacencies = cell.getAdjacencies(face);
2003 for (int k = 0; k < nFaceAdjacencies; ++k) {
2004 long neighId = faceAdjacencies[k];
2005 OctantInfo neighOctantInfo = getCellOctant(neighId);
2006 Octant *neighOctant = getOctantPointer(neighOctantInfo);
2007 neighsArea += neighOctant->getLogicalArea();
2008 }
2009
2010 uint64_t faceArea = octant->getLogicalArea();
2011 if (faceArea == neighsArea) {
2012 continue;
2013 }
2014 }
2015 }
2016
2017 // Find cell neighbours
2018 neighTreeIds.clear();
2019 neighGhostFlags.clear();
2020 m_tree->findNeighbours(octant, face, 1, neighTreeIds, neighGhostFlags);
2021
2022 // Set the adjacencies
2023 //
2024 // Some of the neighbours may already be among the adjacencies
2025 // of the cell, this is not a problem because the function that
2026 // pushs the adjacency will insert only unique adjacencies.
2027 int nNeighs = neighTreeIds.size();
2028 for (int k = 0; k < nNeighs; ++k) {
2029 OctantInfo neighOctantInfo(neighTreeIds[k], !neighGhostFlags[k]);
2030 long neighId = getOctantId(neighOctantInfo);
2031
2032 // Set cell data
2033 cell.pushAdjacency(face, neighId);
2034
2035 // Set neighbour data
2036 int neighFace = oppositeFace[face];
2037 Cell &neigh = m_cells[neighId];
2038 neigh.pushAdjacency(neighFace, cellId);
2039 }
2040 }
2041 }
2042 }
2043}
2044
2051{
2052 return setMarker(id, 1);
2053}
2054
2061{
2062 return setMarker(id, -1);
2063}
2064
2072{
2073 return setMarker(id, 0);
2074}
2075
2082adaption::Marker VolOctree::_getCellAdaptionMarker(long id)
2083{
2084 OctantInfo octantInfo = getCellOctant(id);
2085 if (!octantInfo.internal) {
2086 return adaption::MARKER_UNDEFINED;
2087 }
2088
2089 int8_t treeMarker = m_tree->getMarker(octantInfo.id);
2090 if (treeMarker > 0) {
2091 return adaption::MARKER_REFINE;
2092 } else if (treeMarker < 0) {
2093 return adaption::MARKER_COARSEN;
2094 } else {
2095 return adaption::MARKER_NONE;
2096 }
2097}
2098
2105bool VolOctree::setMarker(long id, int8_t value)
2106{
2107 OctantInfo octantInfo = getCellOctant(id);
2108 if (!octantInfo.internal) {
2109 return false;
2110 }
2111
2112 m_tree->setMarker(octantInfo.id, value);
2113
2114 return true;
2115}
2116
2124bool VolOctree::_enableCellBalancing(long id, bool enabled)
2125{
2126 OctantInfo octantInfo = getCellOctant(id);
2127 if (!octantInfo.internal) {
2128 return false;
2129 }
2130
2131 m_tree->setBalance(octantInfo.id, enabled);
2132
2133 return true;
2134}
2135
2142bool VolOctree::isPointInside(const std::array<double, 3> &point) const
2143{
2144 bool isGhost;
2145
2146 return (m_tree->getPointOwner(point, isGhost) != nullptr);
2147}
2148
2156bool VolOctree::isPointInside(long id, const std::array<double, 3> &point) const
2157{
2158 const Cell &cell = m_cells[id];
2159 ConstProxyVector<long> cellVertexIds = cell.getVertexIds();
2160
2161 int lowerLeftVertex = 0;
2162 int upperRightVertex = uipow(2, getDimension()) - 1;
2163
2164 const std::array<double, 3> &lowerLeft = getVertexCoords(cellVertexIds[lowerLeftVertex]);
2165 const std::array<double, 3> &upperRight = getVertexCoords(cellVertexIds[upperRightVertex]);
2166
2167 const double EPS = getTol();
2168 for (int d = 0; d < 3; ++d){
2169 if (point[d] < lowerLeft[d] - EPS || point[d] > upperRight[d] + EPS) {
2170 return false;
2171 }
2172 }
2173
2174 return true;
2175}
2176
2187long VolOctree::locatePoint(const std::array<double, 3> &point) const
2188{
2189 bool isGhost;
2190 uint32_t treeId = m_tree->getPointOwnerIdx(point, isGhost);
2191 if (treeId == std::numeric_limits<uint32_t>::max()) {
2192 return Element::NULL_ID;
2193 }
2194
2195 OctantInfo octantInfo(treeId, !isGhost);
2196 return getOctantId(octantInfo);
2197}
2198
2205void VolOctree::_setTol(double tolerance)
2206{
2207 m_tree->setTol(tolerance);
2208
2209 VolumeKernel::_setTol(tolerance);
2210}
2211
2216{
2217 m_tree->setTol();
2218
2219 double tolerance = m_tree->getTol();
2220 VolumeKernel::_setTol(tolerance);
2221}
2222
2229{
2230 const int DUMP_VERSION = 4;
2231
2232 return DUMP_VERSION;
2233}
2234
2240void VolOctree::_dump(std::ostream &stream) const
2241{
2242 // List all octants
2243 std::size_t nOctants = m_tree->getNumOctants();
2244 std::size_t nGhostsOctants = m_tree->getNumGhosts();
2245
2246 std::vector<OctantInfo> octantInfoList;
2247 octantInfoList.reserve(nOctants + nGhostsOctants);
2248
2249 for (std::size_t n = 0; n < nOctants; ++n) {
2250 octantInfoList.emplace_back(n, true);
2251 }
2252
2253 for (std::size_t n = 0; n < nGhostsOctants; ++n) {
2254 octantInfoList.emplace_back(n, false);
2255 }
2256
2257 // Dump tree data
2258 m_tree->dump(stream);
2259
2260 // Dump kernel of vertex's containers
2261 //
2262 // We want the kernel state to be kept after the restore.
2263 m_vertices.dumpKernel(stream);
2264
2265 // Dump kernel of cell's containers
2266 //
2267 // We want the kernel state to be kept after the restore.
2268 m_cells.dumpKernel(stream);
2269
2270 // Dump vertex information
2271 //
2272 // This indexes will be read when importing the cells to keep the
2273 // association between numeration of tree vertices and patch vertices.
2274 std::unordered_set<long> dumpedVertices;
2275 dumpedVertices.reserve(getVertexCount());
2276 for (const OctantInfo &octantInfo : octantInfoList) {
2277 const Cell &cell = m_cells.at(getOctantId(octantInfo));
2278 ConstProxyVector<long> cellVertexIds = cell.getVertexIds();
2279 for (int k = 0; k < m_cellTypeInfo->nVertices; ++k) {
2280 long vertexId = cellVertexIds[k];
2281 if (dumpedVertices.count(vertexId) == 0) {
2282 utils::binary::write(stream, vertexId);
2283#if BITPIT_ENABLE_MPI==1
2284 utils::binary::write(stream, getVertexOwner(vertexId));
2285#else
2286 int dummyRank = 0;
2287 utils::binary::write(stream, dummyRank);
2288#endif
2289 dumpedVertices.insert(vertexId);
2290 }
2291 }
2292 }
2293
2294 // Dump cell information
2295 //
2296 // This indexes will be read when importing the cells to keep the
2297 // association between numeration of tree octants and patch cells.
2298 for (const OctantInfo &octantInfo : octantInfoList) {
2299 utils::binary::write(stream, getOctantId(octantInfo));
2300 }
2301
2302 for (const Cell &cell : getCells()) {
2303 utils::binary::write(stream, cell.getPID());
2304 }
2305
2306 // Dump interfaces
2307 dumpInterfaces(stream);
2308}
2309
2315void VolOctree::_restore(std::istream &stream)
2316{
2317 // Restore tree
2318 m_tree->restore(stream);
2319
2320 // Restore kernel of vertex's containers
2321 m_vertices.restoreKernel(stream);
2322
2323 // Restore kernel of cell's containers
2324 m_cells.restoreKernel(stream);
2325
2326 // Enable manual adaption
2327 AdaptionMode previousAdaptionMode = getAdaptionMode();
2329
2330 // Restore cells
2331 size_t nOctants = m_tree->getNumOctants();
2332 size_t nGhostsOctants = m_tree->getNumGhosts();
2333
2334 StitchInfo stitchInfo;
2335
2336 std::vector<OctantInfo> octantInfoList;
2337 octantInfoList.reserve(nOctants + nGhostsOctants);
2338 for (std::size_t n = 0; n < nOctants; ++n) {
2339 octantInfoList.emplace_back(n, true);
2340 }
2341 for (std::size_t n = 0; n < nGhostsOctants; ++n) {
2342 octantInfoList.emplace_back(n, false);
2343 }
2344
2345 importCells(octantInfoList, stitchInfo, &stream);
2346
2347 // Restore interfaces
2348 restoreInterfaces(stream);
2349
2350 // Restore previous adaption mode
2351 setAdaptionMode(previousAdaptionMode);
2352
2353 //
2354 // Restore bounding box
2355 //
2356 // The bounding box is frozen, it is necessary to update it manually.
2357 setBoundingBox();
2358}
2359
2367{
2368 OctantInfo octantInfo = getCellOctant(id);
2369 return octantInfo.id;
2370}
2371
2380std::array<double, 3> VolOctree::getOrigin() const
2381{
2382 return m_tree->getOrigin();
2383}
2384
2392void VolOctree::setOrigin(const std::array<double, 3> &origin)
2393{
2394 std::array<double, 3> translation = origin - getOrigin();
2395 translate(translation);
2396}
2397
2403void VolOctree::translate(const std::array<double, 3> &translation)
2404{
2405 m_tree->setOrigin(m_tree->getOrigin() + translation);
2406
2407 VolumeKernel::translate(translation);
2408
2409 // The bounding box is frozen, it is not updated automatically
2410 setBoundingBox();
2411}
2412
2419{
2420 return m_tree->getL();
2421}
2422
2428void VolOctree::setLength(double length)
2429{
2430 // Set the length
2431 m_tree->setL(length);
2432
2433 // Set the new bounding box
2434 setBoundingBox();
2435
2436 // If needed, update the discretization
2437 if (m_vertices.size() > 0) {
2438 // Create tree connectivity
2439 m_tree->computeConnectivity();
2440
2441 // Update vertex coordinates
2442 std::unordered_set<long> alreadyEvaluated;
2443 for (const Cell &cell : m_cells) {
2444 OctantInfo octantInfo = getCellOctant(cell.getId());
2445 Octant *octant = getOctantPointer(octantInfo);
2446 ConstProxyVector<long> cellVertexIds = cell.getVertexIds();
2447 for (int k = 0; k < m_cellTypeInfo->nVertices; ++k) {
2448 long vertexId = cellVertexIds[k];
2449 if (alreadyEvaluated.count(vertexId) == 0) {
2450 Vertex &vertex = m_vertices.at(vertexId);
2451 vertex.setCoords(m_tree->getNode(octant, k));
2452 alreadyEvaluated.insert(vertexId);
2453 }
2454 }
2455 }
2456
2457 // Destroy tree connectivity
2458 m_tree->clearConnectivity();
2459 }
2460}
2461
2468void VolOctree::scale(const std::array<double, 3> &scaling, const std::array<double, 3> &center)
2469{
2470 bool uniformScaling = true;
2471 uniformScaling &= (std::abs(scaling[0] - scaling[1]) > 1e-14);
2472 uniformScaling &= (std::abs(scaling[0] - scaling[2]) > 1e-14);
2473 assert(uniformScaling);
2474 if (!uniformScaling) {
2475 log::cout() << "VolOctree patch only allows uniform scaling." << std::endl;
2476 return;
2477 }
2478
2479 std::array<double, 3> origin = m_tree->getOrigin();
2480 for (int n = 0; n < 3; ++n) {
2481 origin[n] = center[n] + scaling[n] * (origin[n] - center[n]);
2482 }
2483 m_tree->setOrigin(origin);
2484
2485 m_tree->setL(m_tree->getL() * scaling[0]);
2486
2487 VolumeKernel::scale(scaling, center);
2488
2489 // The bounding box is frozen, it is not updated automatically
2490 setBoundingBox();
2491}
2492
2507void VolOctree::_findCellNeighs(long id, const std::vector<long> *blackList, std::vector<long> *neighs) const
2508{
2509 OctantInfo octantInfo = getCellOctant(id);
2510 const Octant *octant = getOctantPointer(octantInfo);
2511
2512 std::vector<uint32_t> neighTreeIds;
2513 std::vector<bool> neighGhostFlags;
2514 m_tree->findAllCodimensionNeighbours(octant, neighTreeIds, neighGhostFlags);
2515
2516 int nNeighs = neighTreeIds.size();
2517 for (int i = 0; i < nNeighs; ++i) {
2518 OctantInfo neighOctantInfo(neighTreeIds[i], !neighGhostFlags[i]);
2519 long neighId = getOctantId(neighOctantInfo);
2520
2521 if (!blackList || utils::findInOrderedVector<long>(neighId, *blackList) == blackList->end()) {
2522 utils::addToOrderedVector<long>(neighId, *neighs);
2523 }
2524 }
2525}
2526
2542void VolOctree::_findCellEdgeNeighs(long id, int edge, const std::vector<long> *blackList, std::vector<long> *neighs) const
2543{
2544 assert(isThreeDimensional());
2545 if (!isThreeDimensional()) {
2546 return;
2547 }
2548
2549 // Get octant info
2550 const OctantInfo octantInfo = getCellOctant(id);
2551
2552 // Get edge neighbours
2553 int codimension = getDimension() - 1;
2554 findOctantCodimensionNeighs(octantInfo, edge, codimension, blackList, neighs);
2555
2556 // Add face neighbours
2557 //
2558 // Get all face neighbours and select the ones that contains the edge
2559 // for which the neighbours are requested. To correctly consider these
2560 // neighbours, the following logic can be used:
2561 // - if a face/edge neighbour has the same level or a lower level than
2562 // the current cell, then it certainly is also a vertex neighbour;
2563 // - if a face/edge neighbour has a higher level than the current cell,
2564 // it is necessary to check if the neighbour actually contains the
2565 // edge.
2566 //
2567 std::vector<long> faceNeighs;
2568 const Octant *octant = getOctantPointer(octantInfo);
2569 int octantLevel = octant->getLevel();
2570 for (int face : m_octantLocalFacesOnEdge[edge]) {
2571 faceNeighs.clear();
2572 _findCellFaceNeighs(id, face, blackList, &faceNeighs);
2573 for (long neighId : faceNeighs) {
2574 const OctantInfo neighOctantInfo = getCellOctant(neighId);
2575 const Octant *neighOctant = getOctantPointer(neighOctantInfo);
2576 int neighOctantLevel = neighOctant->getLevel();
2577 if (neighOctantLevel <= octantLevel) {
2578 utils::addToOrderedVector<long>(neighId, *neighs);
2579 } else if (m_tree->isEdgeOnOctant(octant, edge, neighOctant)) {
2580 utils::addToOrderedVector<long>(neighId, *neighs);
2581 }
2582 }
2583 }
2584}
2585
2599void VolOctree::_findCellVertexNeighs(long id, int vertex, const std::vector<long> *blackList, std::vector<long> *neighs) const
2600{
2601 // Get octant info
2602 const OctantInfo octantInfo = getCellOctant(id);
2603 const Octant *octant = getOctantPointer(octantInfo);
2604
2605 // Get vertex neighbours
2606 std::vector<uint32_t> neighTreeIds;
2607 std::vector<bool> neighGhostFlags;
2608 m_tree->findAllNodeNeighbours(octant, vertex, neighTreeIds, neighGhostFlags);
2609
2610 int nNeighs = neighTreeIds.size();
2611 for (int i = 0; i < nNeighs; ++i) {
2612 OctantInfo neighOctantInfo(neighTreeIds[i], !neighGhostFlags[i]);
2613 long neighId = getOctantId(neighOctantInfo);
2614
2615 if (!blackList || utils::findInOrderedVector<long>(neighId, *blackList) == blackList->end()) {
2616 utils::addToOrderedVector<long>(neighId, *neighs);
2617 }
2618 }
2619}
2620
2637void VolOctree::findOctantCodimensionNeighs(const OctantInfo &octantInfo, int index, int codimension,
2638 const std::vector<long> *blackList, std::vector<long> *neighs) const
2639{
2640 int dimension = getDimension();
2641 if (codimension > dimension || codimension <= 0) {
2642 return;
2643 }
2644
2645 const Octant *octant = getOctantPointer(octantInfo);
2646
2647 std::vector<uint32_t> neighTreeIds;
2648 std::vector<bool> neighGhostFlags;
2649 m_tree->findNeighbours(octant, index, codimension, neighTreeIds, neighGhostFlags);
2650
2651 int nNeighs = neighTreeIds.size();
2652 for (int i = 0; i < nNeighs; ++i) {
2653 OctantInfo neighOctantInfo(neighTreeIds[i], !neighGhostFlags[i]);
2654 long neighId = getOctantId(neighOctantInfo);
2655
2656 if (!blackList || utils::findInOrderedVector<long>(neighId, *blackList) == blackList->end()) {
2657 utils::addToOrderedVector<long>(neighId, *neighs);
2658 }
2659 }
2660}
2661
2671int VolOctree::findAdjoinNeighFace(const Cell &cell, int cellFace, const Cell &neigh) const
2672{
2673 long cellId = cell.getId();
2674
2675 // Get the neighbour face
2676 int neighFace = m_tree->getOppface()[cellFace];
2677
2678 // Check if the two cells are neighbours
2679 int nNeighFaceAdjacencies = neigh.getAdjacencyCount(neighFace);
2680 const long *neighFaceAdjacencies = neigh.getAdjacencies(neighFace);
2681 for (int k = 0; k < nNeighFaceAdjacencies; ++k) {
2682 if (neighFaceAdjacencies[k] == cellId) {
2683 return neighFace;
2684 }
2685 }
2686
2687 return -1;
2688}
2689
2700bool VolOctree::isSameFace(const Cell &cell_A, int face_A, const Cell &cell_B, int face_B) const
2701{
2702 // Check if the face matches
2703 if (m_tree->getOppface()[face_A] != face_B) {
2704 return false;
2705 }
2706
2707 // Check if the two cells are neighbours
2708 long cellId_A = cell_A.getId();
2709
2710 int nNeighFaceAdjacencies_B = cell_B.getAdjacencyCount(face_B);
2711 const long *neighFaceAdjacencies_B = cell_B.getAdjacencies(face_B);
2712 for (int k = 0; k < nNeighFaceAdjacencies_B; ++k) {
2713 if (neighFaceAdjacencies_B[k] == cellId_A) {
2714 return true;
2715 }
2716 }
2717
2718 return false;
2719}
2720
2721}
The Cell class defines the cells.
Definition cell.hpp:42
bool pushAdjacency(int face, long adjacency)
Definition cell.cpp:771
long getAdjacency(int face, int index=0) const
Definition cell.cpp:831
const long * getAdjacencies() const
Definition cell.cpp:841
int getAdjacencyCount() const
Definition cell.cpp:805
long getId() const
Definition element.cpp:510
static ConstProxyVector< long > getVertexIds(ElementType type, const long *connectivity)
Definition element.cpp:1193
The Interface class defines the interfaces among cells.
Definition interface.hpp:37
long getOwner() const
int getOwnerFace() const
Octant class definition.
Definition Octant.hpp:89
uint64_t getLogicalArea() const
Definition Octant.cpp:440
uint8_t getLevel() const
Definition Octant.cpp:259
uint64_t computeFatherMorton() const
Definition Octant.cpp:609
PABLO Uniform is an example of user class derived from ParaTree to map ParaTree in a uniform (square/...
static BITPIT_PUBLIC_API const std::string DEFAULT_LOG_FILE
Definition ParaTree.hpp:119
void dumpInterfaces(std::ostream &stream) const
int getVertexOwner(long id) const
PiercedVector< Cell > & getCells()
@ ADAPTION_AUTOMATIC
No adaption can be performed.
bool testAlterationFlags(AlterationFlags availableFlags, AlterationFlags requestedFlags) const
VertexIterator restoreVertex(const std::array< double, 3 > &coords, int owner, long id)
AdaptionMode getAdaptionMode() const
CellIterator restoreCell(ElementType type, std::unique_ptr< long[]> &&connectStorage, int owner, int haloLayer, long id)
virtual long getVertexCount() const
virtual void _findCellFaceNeighs(long id, int face, const std::vector< long > *blackList, std::vector< long > *neighs) const
VertexIterator addVertex(const Vertex &source, long id=Vertex::NULL_ID)
bool empty(bool global=true) const
void setBoundingBoxFrozen(bool frozen)
virtual void _setTol(double tolerance)
bool deleteVertices(const IdStorage &ids)
CellIterator addCell(const Cell &source, long id=Element::NULL_ID)
void updateInterfaces(bool forcedUpdated=false)
void scale(const std::array< double, 3 > &scaling)
virtual void setDimension(int dimension)
virtual void translate(const std::array< double, 3 > &translation)
@ PARTITIONING_ENABLED
No partitioning can be performed.
void setAdaptionStatus(AdaptionStatus status)
CellConstIterator ghostCellConstEnd() const
void restore(std::istream &stream, bool reregister=false)
virtual void reset()
void restoreInterfaces(std::istream &stream)
void setAdaptionMode(AdaptionMode mode)
const std::array< double, 3 > & getVertexCoords(long id) const
void updateAdjacencies(bool forcedUpdated=false)
CellConstIterator ghostCellConstBegin() const
double getTol() const
bool deleteCells(const IdStorage &ids)
bool isThreeDimensional() const
The PatchManager oversee the handling of the patches.
Metafunction for generating a pierced vector.
void clear(bool release=true)
void emplaceBack(id_t id, Args &&... args)
void reserve(std::size_t n)
static BITPIT_PUBLIC_API const ReferenceElementInfo & getInfo(ElementType type)
The Vertex class defines the vertexs.
Definition vertex.hpp:42
void setCoords(const std::array< double, 3 > &coords)
Definition vertex.cpp:236
void _updateAdjacencies() override
void setDimension(int dimension) override
int getCellLevel(long id) const
long locatePoint(const std::array< double, 3 > &point) const override
std::vector< adaption::Info > _adaptionPrepare(bool trackAdaption) override
std::array< double, 3 > evalInterfaceNormal(long id) const override
double evalCellSize(long id) const override
void _setTol(double tolerance) override
void _findCellEdgeNeighs(long id, int edge, const std::vector< long > *blackList, std::vector< long > *neighs) const override
void scale(const std::array< double, 3 > &scaling, const std::array< double, 3 > &center) override
long _getCellNativeIndex(long id) const override
void _restore(std::istream &stream) override
std::array< double, 3 > getOrigin() const
void translate(const std::array< double, 3 > &translation) override
std::vector< adaption::Info > _adaptionAlter(bool trackAdaption) override
adaption::Marker _getCellAdaptionMarker(long id) override
double evalCellVolume(long id) const override
bool isPointInside(const std::array< double, 3 > &point) const override
void settleAdaptionMarkers() override
OctantInfo getCellOctant(long id) const
Octant * getOctantPointer(const OctantInfo &octantInfo)
void reset() override
void simulateCellUpdate(long id, adaption::Marker marker, std::vector< Cell > *virtualCells, PiercedVector< Vertex, long > *virtualVertices) const override
double getLength() const
VolOctree(MPI_Comm communicator, std::size_t haloSize=1)
Definition voloctree.cpp:76
double evalInterfaceArea(long id) const override
bool _enableCellBalancing(long id, bool enabled) override
int getCellFamilySplitLocalVertex(long id) const
int _getDumpVersion() const override
void _findCellVertexNeighs(long id, int vertex, const std::vector< long > *blackList, std::vector< long > *neighs) const override
void setTreeAdopter(std::unique_ptr< PabloUniform > *entruster)
void _resetTol() override
bool _resetCellAdaptionMarker(long id) override
PabloUniform & getTree()
Gets a reference to the octree associated with the patch.
VolOctree & operator=(const VolOctree &other)
void setOrigin(const std::array< double, 3 > &origin)
std::array< double, 3 > evalCellCentroid(long id) const override
std::unique_ptr< PatchKernel > clone() const override
bool isSameFace(const Cell &cell_A, int face_A, const Cell &cell_B, int face_B) const override
void _adaptionCleanup() override
void _dump(std::ostream &stream) const override
bool _markCellForRefinement(long id) override
void setLength(double length)
void _findCellNeighs(long id, const std::vector< long > *blackList, std::vector< long > *neighs) const override
long getOctantId(const OctantInfo &octantInfo) const
int findAdjoinNeighFace(const Cell &cell, int cellFace, const Cell &neigh) const override
bool _markCellForCoarsening(long id) override
VolumeKernel(MPI_Comm communicator, std::size_t haloSize, AdaptionMode adaptionMode, PartitioningMode partitioningMode)
The InfoCollection class is a container that holds one or more adaption info items.
Definition adaption.hpp:82
std::vector< Info > dump()
Definition adaption.cpp:398
T uipow(const T &, unsigned int)
Definition Operators.tpp:56
void write(std::ostream &stream, const std::vector< bool > &container)
void read(std::istream &stream, std::vector< bool > &container)
#define BITPIT_UNUSED(variable)
Definition compiler.hpp:63
Logger & cout(log::Level defaultSeverity, log::Visibility defaultVisibility)
Definition logger.cpp:1714
The Info struct defines the infomation associated to an adaption.
Definition adaption.hpp:63