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
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
83 : VolumeKernel(ADAPTION_AUTOMATIC)
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)
254 : VolumeKernel(ADAPTION_AUTOMATIC)
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.create(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) {
999
1000 std::size_t adaptionInfoId = adaptionData.create(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.create(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.create(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.create(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.create(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.create(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 if (octantToCellItr->second == cellId) {
1603 m_octantToCell.erase(octantToCellItr);
1604 }
1605 } else {
1606 m_cellToGhost.erase(cellId);
1607
1608 auto ghostToCellItr = m_ghostToCell.find(treeId);
1609 if (ghostToCellItr->second == cellId) {
1610 m_ghostToCell.erase(ghostToCellItr);
1611 }
1612 }
1613
1614 // Cell needs to be removed
1615 deadCells.push_back(cellId);
1616 }
1617
1618 PatchKernel::deleteCells(deadCells);
1619
1620 // Prune cell adjacencies and interfaces
1621 //
1622 // At this stage we cannot fully update adjacencies and interfaces, but
1623 // we need to remove stale adjacencies and interfaces.
1625
1627
1628 // All the vertices belonging to the dangling cells has to be kept
1629 //
1630 // We need to keep all the vertices that lie on a dangling face and belong
1631 // to cells that were not deleted. These are the vertices of the dangling
1632 // faces, plus all the vertices that lie on the borders of the dangling
1633 // faces. Latter vertices arise from three-dimensional configurations where
1634 // a small cell has been deleted and, among its non-deleted neighburs, there
1635 // areboth larger cells and cells of the same size. In this case, a vertex
1636 // of the deleted small cell can lie on one of the edges of a bigger cell
1637 // and belong also to a smaller cell. If only the bigger cell is dangling,
1638 // that vertex will lie on a dangling face (it's on the edge of the cell,
1639 // hence on the border of the face), but it's not one of the vertices of
1640 // the dangling cells.
1641 //
1642 // To identify all vertices that need to be kept, we consider the vertices
1643 // of the dangling faces and the vertices of the smaller neighbouring faces
1644 // (i.e., the faces of the smaller neigbours of the dangling cells that lie
1645 // on the faces of the dangling cells).
1646 //
1647 // Morover we need to build a map between the patch numbering and the
1648 // octree numbering of the vertices of the dangling cells. This map will
1649 // be used when imprting the octants to stitch the imported octants to
1650 // the existing cells.
1651 StitchInfo stitchVertices;
1652 for (const auto &entry: m_alteredCells) {
1653 // Consider only dangling faces
1654 if (!testAlterationFlags(entry.second, FLAG_DANGLING)) {
1655 continue;
1656 }
1657
1658 // Vertices of the cell
1659 long cellId = entry.first;
1660 const Cell &cell = m_cells[cellId];
1661 ConstProxyVector<long> cellVertexIds = cell.getVertexIds();
1662
1663 OctantInfo octantInfo = getCellOctant(cellId);
1664 Octant *octant = getOctantPointer(octantInfo);
1665
1666 for (int k = 0; k < nCellVertices; ++k) {
1667 long vertexId = cellVertexIds[k];
1668 uint64_t vertexTreeKey = m_tree->computeNodePersistentKey(octant, k);
1669 stitchVertices.insert({vertexTreeKey, vertexId});
1670 deadVertices.erase(vertexId);
1671 }
1672
1673 // Vertices of neighbours
1674 //
1675 // We need to select the neighbours smaller than the current cell and
1676 // keep the vertices of the faces that are shared with the current cell.
1677 int cellLevel = octant->getLevel();
1678
1679 for (int face = 0; face < nCellFaces; ++face) {
1680 int nFaceAdjacencies = cell.getAdjacencyCount(face);
1681 const long *faceAdjacencies = cell.getAdjacencies(face);
1682 for (int i = 0; i < nFaceAdjacencies; ++i) {
1683 int neighId = faceAdjacencies[i];
1684 OctantInfo neighOctantInfo = getCellOctant(neighId);
1685 Octant *neighOctant = getOctantPointer(neighOctantInfo);
1686 int neighLevel = neighOctant->getLevel();
1687 if (neighLevel <= cellLevel) {
1688 continue;
1689 }
1690
1691 const Cell &neigh = m_cells[neighId];
1692 int neighFace = findAdjoinNeighFace(cell, face, neigh);
1693 const int *localNeighFaceConnect = m_cellTypeInfo->faceConnectStorage[neighFace].data();
1694 ConstProxyVector<long> faceVertexIds = neigh.getFaceVertexIds(neighFace);
1695 std::size_t nFaceVertexIds = faceVertexIds.size();
1696 for (std::size_t k = 0; k < nFaceVertexIds; ++k) {
1697 long vertexId = faceVertexIds[k];
1698 uint64_t vertexTreeKey = m_tree->computeNodePersistentKey(neighOctant, localNeighFaceConnect[k]);
1699 stitchVertices.insert({vertexTreeKey, vertexId});
1700 deadVertices.erase(vertexId);
1701 }
1702 }
1703 }
1704 }
1705
1706 // Delete the vertices
1707 PatchKernel::deleteVertices(deadVertices);
1708
1709 // Done
1710 return stitchVertices;
1711}
1712
1719void VolOctree::renumberCells(const std::vector<RenumberInfo> &renumberedOctants)
1720{
1721 // Remove old patch-to-tree and tree-to-patch associations
1722 for (const RenumberInfo &renumberInfo : renumberedOctants) {
1723 long cellId = renumberInfo.cellId;
1724 if (!m_cells[cellId].isInterior()) {
1725 continue;
1726 }
1727
1728 const OctantInfo &previousOctantInfo = getCellOctant(cellId);
1729 uint32_t previousTreeId = previousOctantInfo.id;
1730
1731 m_octantToCell.erase(previousTreeId);
1732 }
1733
1734 // Create new patch-to-tree and tree-to-patch associations
1735 for (const RenumberInfo &renumberInfo : renumberedOctants) {
1736 long cellId = renumberInfo.cellId;
1737 if (!m_cells[cellId].isInterior()) {
1738 continue;
1739 }
1740
1741 uint32_t treeId = renumberInfo.newTreeId;
1742
1743 m_cellToOctant[cellId] = treeId;
1744 m_octantToCell[treeId] = cellId;
1745 }
1746}
1747
1758std::vector<long> VolOctree::importCells(const std::vector<OctantInfo> &octantInfoList,
1759 StitchInfo &stitchInfo, std::istream *restoreStream)
1760{
1761 // Create the new vertices
1762 int nCellVertices = m_cellTypeInfo->nVertices;
1763 for (const OctantInfo &octantInfo : octantInfoList) {
1764 Octant *octant = getOctantPointer(octantInfo);
1765 for (int k = 0; k < nCellVertices; ++k) {
1766 uint64_t vertexTreeKey = m_tree->computeNodePersistentKey(octant, k);
1767 if (stitchInfo.count(vertexTreeKey) == 0) {
1768 // Vertex coordinates
1769 std::array<double, 3> nodeCoords = m_tree->getNode(octant, k);
1770
1771 // Create the vertex
1772 long vertexId;
1773 if (!restoreStream) {
1774#if BITPIT_ENABLE_MPI==1
1775 VertexIterator vertexIterator = addVertex(std::move(nodeCoords));
1776#else
1777 VertexIterator vertexIterator = addVertex(std::move(nodeCoords));
1778#endif
1779 vertexId = vertexIterator.getId();
1780 } else {
1781 utils::binary::read(*restoreStream, vertexId);
1782
1783#if BITPIT_ENABLE_MPI==1
1784 int rank;
1785 utils::binary::read(*restoreStream, rank);
1786
1787 restoreVertex(nodeCoords, rank, vertexId);
1788#else
1789 int dummyRank;
1790 utils::binary::read(*restoreStream, dummyRank);
1791
1792 restoreVertex(nodeCoords, vertexId);
1793#endif
1794 }
1795
1796 // Add the vertex to the stitching info
1797 stitchInfo[vertexTreeKey] = vertexId;
1798 }
1799 }
1800 }
1801
1802 // Reserve space for the maps
1803 long nOctants = m_tree->getNumOctants();
1804 m_cellToOctant.reserve(nOctants);
1805 m_octantToCell.reserve(nOctants);
1806
1807 long nGhostsOctants = m_tree->getNumGhosts();
1808 m_cellToGhost.reserve(nGhostsOctants);
1809 m_ghostToCell.reserve(nGhostsOctants);
1810
1811 // Add the cells
1812 size_t octantInfoListSize = octantInfoList.size();
1813 m_cells.reserve(m_cells.size() + octantInfoListSize);
1814
1815 std::vector<long> createdCells(octantInfoListSize);
1816 for (size_t i = 0; i < octantInfoListSize; ++i) {
1817 const OctantInfo &octantInfo = octantInfoList[i];
1818
1819 // Octant connectivity
1820 Octant *octant = getOctantPointer(octantInfo);
1821
1822 // Cell connectivity
1823 std::unique_ptr<long[]> cellConnect = std::unique_ptr<long[]>(new long[nCellVertices]);
1824 for (int k = 0; k < nCellVertices; ++k) {
1825 uint64_t vertexTreeKey = m_tree->computeNodePersistentKey(octant, k);
1826 cellConnect[k] = stitchInfo.at(vertexTreeKey);
1827 }
1828
1829#if BITPIT_ENABLE_MPI==1
1830 // Cell owner
1831 int owner;
1832 if (octantInfo.internal) {
1833 owner = getRank();
1834 } else {
1835 uint64_t globalTreeId = m_tree->getGhostGlobalIdx(octantInfo.id);
1836 owner = m_tree->getOwnerRank(globalTreeId);
1837 }
1838
1839 // Cell halo layer
1840 int haloLayer = m_tree->getGhostLayer(octant);
1841#endif
1842
1843 // Add cell
1844 long cellId;
1845 if (!restoreStream) {
1846#if BITPIT_ENABLE_MPI==1
1847 CellIterator cellIterator = addCell(m_cellTypeInfo->type, std::move(cellConnect), owner, haloLayer);
1848#else
1849 CellIterator cellIterator = addCell(m_cellTypeInfo->type, std::move(cellConnect));
1850#endif
1851 cellId = cellIterator.getId();
1852 } else {
1853 utils::binary::read(*restoreStream, cellId);
1854
1855#if BITPIT_ENABLE_MPI==1
1856 restoreCell(m_cellTypeInfo->type, std::move(cellConnect), owner, haloLayer, cellId);
1857#else
1858 restoreCell(m_cellTypeInfo->type, std::move(cellConnect), cellId);
1859#endif
1860 }
1861
1862 // Create patch-tree associations
1863 if (octantInfo.internal) {
1864 m_cellToOctant.insert({{cellId, octantInfo.id}});
1865 m_octantToCell.insert({{octantInfo.id, cellId}});
1866 } else {
1867 m_cellToGhost.insert({{cellId, octantInfo.id}});
1868 m_ghostToCell.insert({{octantInfo.id, cellId}});
1869 }
1870
1871 // Add the cell to the list of created cells
1872 createdCells[i] = cellId;
1873 }
1874
1875 // Update first ghost and last internal information
1876 if (restoreStream) {
1878#if BITPIT_ENABLE_MPI==1
1880#endif
1881 }
1882
1883 // Update cell PIDs
1884 if (restoreStream) {
1885 for (Cell &cell : getCells()) {
1886 int PID;
1887 utils::binary::read(*restoreStream, PID);
1888 cell.setPID(PID);
1889 }
1890 }
1891
1892 // Update adjacencies
1894
1895 // Update interfaces
1896 if (!restoreStream) {
1898 }
1899
1900 // Done
1901 return createdCells;
1902}
1903
1911{
1912 // Tree information
1913 int maxLevel = m_tree->getMaxDepth();
1914
1915 // Face information
1916 int nCellFaces = m_cellTypeInfo->nFaces;
1917 const uint8_t *oppositeFace = m_tree->getOppface();
1918
1919 // Count cells with dirty adjacencies
1920 std::vector<std::size_t> nDirtyAdjacenciesCellsByLevel(maxLevel + 1, 0);
1921 for (const auto &entry : m_alteredCells) {
1922 AlterationFlags cellAlterationFlags = entry.second;
1923 if (!testAlterationFlags(cellAlterationFlags, FLAG_ADJACENCIES_DIRTY)) {
1924 continue;
1925 }
1926
1927 long cellId = entry.first;
1928 int cellLevel = getCellLevel(cellId);
1929
1930 ++nDirtyAdjacenciesCellsByLevel[cellLevel];
1931 }
1932
1933 // Group altered cells by their tree level
1934 std::vector<std::vector<long>> hierarchicalCellIds(maxLevel + 1);
1935 for (int level = 0; level <= maxLevel; ++level) {
1936 hierarchicalCellIds[level].reserve(nDirtyAdjacenciesCellsByLevel[level]);
1937 }
1938
1939 for (const auto &entry : m_alteredCells) {
1940 AlterationFlags cellAlterationFlags = entry.second;
1941 if (!testAlterationFlags(cellAlterationFlags, FLAG_ADJACENCIES_DIRTY)) {
1942 continue;
1943 }
1944
1945 long cellId = entry.first;
1946 int cellLevel = getCellLevel(cellId);
1947 hierarchicalCellIds[cellLevel].push_back(cellId);
1948 }
1949
1950 // Update the adjacencies
1951 std::vector<uint32_t> neighTreeIds;
1952 std::vector<bool> neighGhostFlags;
1953 for (int level = 0; level <= maxLevel; ++level) {
1954 for (long cellId : hierarchicalCellIds[level]) {
1955 Cell &cell = m_cells[cellId];
1956 OctantInfo octantInfo = getCellOctant(cellId);
1957 Octant *octant = getOctantPointer(octantInfo);
1958 for (int face = 0; face < nCellFaces; ++face) {
1959 // Check if the face needs to be processed
1960 //
1961 // If the face has no adjacencies, we need to process it to
1962 // figure out if it is a border or it has some neighbours.
1963 //
1964 // If the face has only one adjacency and the neighbour's level
1965 // is smaller or equal than the one of the current cell, all
1966 // face adjacencies have already been found.
1967 //
1968 // If the face has only one adjacency and the neighbour's level
1969 // is greater than the one of the current cell, there are still
1970 // adjacencies to be found. The adjacency associated with the
1971 // face is an adjacency that was not created during this update
1972 // (cells are processed in level increasing order, only cells
1973 // with a level smaller or equal to the one of the current cell
1974 // may already have been processed during this update). Since
1975 // the cell is bigger that its neighbour it cannot have only
1976 // one adjacency, there are other adjacencies to be found in
1977 // the cell marked as dirty.
1978 //
1979 // If the face has multiple adjacencies, we need to figure out
1980 // if all the adjacencies have already been found. Current
1981 // adjacencies were not created during this update (since there
1982 // are multiple adjacencies, those adjacencies are cells smaller
1983 // than the current one, however since cells are processed in
1984 // level increasing order, only cells with a level smaller or
1985 // equal to the one of the current cell may already have been
1986 // processed during this update). To check if all adjacencies
1987 // have been found, we check if the area coverd by the current
1988 // adjacencies is equal to the face area.
1989 int nFaceAdjacencies = cell.getAdjacencyCount(face);
1990 if (nFaceAdjacencies > 0) {
1991 if (nFaceAdjacencies == 1) {
1992 long cellLevel = octant->getLevel();
1993 long neighId = cell.getAdjacency(face, 0);
1994 long neighLevel = getCellLevel(neighId);
1995 if (neighLevel <= cellLevel) {
1996 continue;
1997 }
1998 } else {
1999 uint64_t neighsArea = 0;
2000 const long *faceAdjacencies = cell.getAdjacencies(face);
2001 for (int k = 0; k < nFaceAdjacencies; ++k) {
2002 long neighId = faceAdjacencies[k];
2003 OctantInfo neighOctantInfo = getCellOctant(neighId);
2004 Octant *neighOctant = getOctantPointer(neighOctantInfo);
2005 neighsArea += neighOctant->getLogicalArea();
2006 }
2007
2008 uint64_t faceArea = octant->getLogicalArea();
2009 if (faceArea == neighsArea) {
2010 continue;
2011 }
2012 }
2013 }
2014
2015 // Find cell neighbours
2016 neighTreeIds.clear();
2017 neighGhostFlags.clear();
2018 m_tree->findNeighbours(octant, face, 1, neighTreeIds, neighGhostFlags);
2019
2020 // Set the adjacencies
2021 //
2022 // Some of the neighbours may already be among the adjacencies
2023 // of the cell, this is not a problem because the function that
2024 // pushs the adjacency will insert only unique adjacencies.
2025 int nNeighs = neighTreeIds.size();
2026 for (int k = 0; k < nNeighs; ++k) {
2027 OctantInfo neighOctantInfo(neighTreeIds[k], !neighGhostFlags[k]);
2028 long neighId = getOctantId(neighOctantInfo);
2029
2030 // Set cell data
2031 cell.pushAdjacency(face, neighId);
2032
2033 // Set neighbour data
2034 int neighFace = oppositeFace[face];
2035 Cell &neigh = m_cells[neighId];
2036 neigh.pushAdjacency(neighFace, cellId);
2037 }
2038 }
2039 }
2040 }
2041}
2042
2049{
2050 return setMarker(id, 1);
2051}
2052
2059{
2060 return setMarker(id, -1);
2061}
2062
2070{
2071 return setMarker(id, 0);
2072}
2073
2080adaption::Marker VolOctree::_getCellAdaptionMarker(long id)
2081{
2082 OctantInfo octantInfo = getCellOctant(id);
2083 if (!octantInfo.internal) {
2084 return adaption::MARKER_UNDEFINED;
2085 }
2086
2087 int8_t treeMarker = m_tree->getMarker(octantInfo.id);
2088 if (treeMarker > 0) {
2089 return adaption::MARKER_REFINE;
2090 } else if (treeMarker < 0) {
2091 return adaption::MARKER_COARSEN;
2092 } else {
2093 return adaption::MARKER_NONE;
2094 }
2095}
2096
2103bool VolOctree::setMarker(long id, int8_t value)
2104{
2105 OctantInfo octantInfo = getCellOctant(id);
2106 if (!octantInfo.internal) {
2107 return false;
2108 }
2109
2110 m_tree->setMarker(octantInfo.id, value);
2111
2112 return true;
2113}
2114
2122bool VolOctree::_enableCellBalancing(long id, bool enabled)
2123{
2124 OctantInfo octantInfo = getCellOctant(id);
2125 if (!octantInfo.internal) {
2126 return false;
2127 }
2128
2129 m_tree->setBalance(octantInfo.id, enabled);
2130
2131 return true;
2132}
2133
2140bool VolOctree::isPointInside(const std::array<double, 3> &point) const
2141{
2142 bool isGhost;
2143
2144 return (m_tree->getPointOwner(point, isGhost) != nullptr);
2145}
2146
2154bool VolOctree::isPointInside(long id, const std::array<double, 3> &point) const
2155{
2156 const Cell &cell = m_cells[id];
2157 ConstProxyVector<long> cellVertexIds = cell.getVertexIds();
2158
2159 int lowerLeftVertex = 0;
2160 int upperRightVertex = uipow(2, getDimension()) - 1;
2161
2162 const std::array<double, 3> &lowerLeft = getVertexCoords(cellVertexIds[lowerLeftVertex]);
2163 const std::array<double, 3> &upperRight = getVertexCoords(cellVertexIds[upperRightVertex]);
2164
2165 const double EPS = getTol();
2166 for (int d = 0; d < 3; ++d){
2167 if (point[d] < lowerLeft[d] - EPS || point[d] > upperRight[d] + EPS) {
2168 return false;
2169 }
2170 }
2171
2172 return true;
2173}
2174
2185long VolOctree::locatePoint(const std::array<double, 3> &point) const
2186{
2187 bool isGhost;
2188 uint32_t treeId = m_tree->getPointOwnerIdx(point, isGhost);
2189 if (treeId == std::numeric_limits<uint32_t>::max()) {
2190 return Element::NULL_ID;
2191 }
2192
2193 OctantInfo octantInfo(treeId, !isGhost);
2194 return getOctantId(octantInfo);
2195}
2196
2203void VolOctree::_setTol(double tolerance)
2204{
2205 m_tree->setTol(tolerance);
2206
2207 VolumeKernel::_setTol(tolerance);
2208}
2209
2214{
2215 m_tree->setTol();
2216
2217 double tolerance = m_tree->getTol();
2218 VolumeKernel::_setTol(tolerance);
2219}
2220
2227{
2228 const int DUMP_VERSION = 4;
2229
2230 return DUMP_VERSION;
2231}
2232
2238void VolOctree::_dump(std::ostream &stream) const
2239{
2240 // List all octants
2241 std::size_t nOctants = m_tree->getNumOctants();
2242 std::size_t nGhostsOctants = m_tree->getNumGhosts();
2243
2244 std::vector<OctantInfo> octantInfoList;
2245 octantInfoList.reserve(nOctants + nGhostsOctants);
2246
2247 for (std::size_t n = 0; n < nOctants; ++n) {
2248 octantInfoList.emplace_back(n, true);
2249 }
2250
2251 for (std::size_t n = 0; n < nGhostsOctants; ++n) {
2252 octantInfoList.emplace_back(n, false);
2253 }
2254
2255 // Dump tree data
2256 m_tree->dump(stream);
2257
2258 // Dump kernel of vertex's containers
2259 //
2260 // We want the kernel state to be kept after the restore.
2261 m_vertices.dumpKernel(stream);
2262
2263 // Dump kernel of cell's containers
2264 //
2265 // We want the kernel state to be kept after the restore.
2266 m_cells.dumpKernel(stream);
2267
2268 // Dump vertex information
2269 //
2270 // This indexes will be read when importing the cells to keep the
2271 // association between numeration of tree vertices and patch vertices.
2272 std::unordered_set<long> dumpedVertices;
2273 dumpedVertices.reserve(getVertexCount());
2274 for (const OctantInfo &octantInfo : octantInfoList) {
2275 const Cell &cell = m_cells.at(getOctantId(octantInfo));
2276 ConstProxyVector<long> cellVertexIds = cell.getVertexIds();
2277 for (int k = 0; k < m_cellTypeInfo->nVertices; ++k) {
2278 long vertexId = cellVertexIds[k];
2279 if (dumpedVertices.count(vertexId) == 0) {
2280 utils::binary::write(stream, vertexId);
2281#if BITPIT_ENABLE_MPI==1
2282 utils::binary::write(stream, getVertexOwner(vertexId));
2283#else
2284 int dummyRank = 0;
2285 utils::binary::write(stream, dummyRank);
2286#endif
2287 dumpedVertices.insert(vertexId);
2288 }
2289 }
2290 }
2291
2292 // Dump cell information
2293 //
2294 // This indexes will be read when importing the cells to keep the
2295 // association between numeration of tree octants and patch cells.
2296 for (const OctantInfo &octantInfo : octantInfoList) {
2297 utils::binary::write(stream, getOctantId(octantInfo));
2298 }
2299
2300 for (const Cell &cell : getCells()) {
2301 utils::binary::write(stream, cell.getPID());
2302 }
2303
2304 // Dump interfaces
2305 dumpInterfaces(stream);
2306}
2307
2313void VolOctree::_restore(std::istream &stream)
2314{
2315 // Restore tree
2316 m_tree->restore(stream);
2317
2318 // Restore kernel of vertex's containers
2319 m_vertices.restoreKernel(stream);
2320
2321 // Restore kernel of cell's containers
2322 m_cells.restoreKernel(stream);
2323
2324 // Enable manual adaption
2325 AdaptionMode previousAdaptionMode = getAdaptionMode();
2327
2328 // Restore cells
2329 size_t nOctants = m_tree->getNumOctants();
2330 size_t nGhostsOctants = m_tree->getNumGhosts();
2331
2332 StitchInfo stitchInfo;
2333
2334 std::vector<OctantInfo> octantInfoList;
2335 octantInfoList.reserve(nOctants + nGhostsOctants);
2336 for (std::size_t n = 0; n < nOctants; ++n) {
2337 octantInfoList.emplace_back(n, true);
2338 }
2339 for (std::size_t n = 0; n < nGhostsOctants; ++n) {
2340 octantInfoList.emplace_back(n, false);
2341 }
2342
2343 importCells(octantInfoList, stitchInfo, &stream);
2344
2345 // Restore interfaces
2346 restoreInterfaces(stream);
2347
2348 // Restore previous adaption mode
2349 setAdaptionMode(previousAdaptionMode);
2350
2351 //
2352 // Restore bounding box
2353 //
2354 // The bounding box is frozen, it is necessary to update it manually.
2355 setBoundingBox();
2356}
2357
2365{
2366 OctantInfo octantInfo = getCellOctant(id);
2367 return octantInfo.id;
2368}
2369
2378std::array<double, 3> VolOctree::getOrigin() const
2379{
2380 return m_tree->getOrigin();
2381}
2382
2390void VolOctree::setOrigin(const std::array<double, 3> &origin)
2391{
2392 std::array<double, 3> translation = origin - getOrigin();
2393 translate(translation);
2394}
2395
2401void VolOctree::translate(const std::array<double, 3> &translation)
2402{
2403 m_tree->setOrigin(m_tree->getOrigin() + translation);
2404
2405 VolumeKernel::translate(translation);
2406
2407 // The bounding box is frozen, it is not updated automatically
2408 setBoundingBox();
2409}
2410
2417{
2418 return m_tree->getL();
2419}
2420
2426void VolOctree::setLength(double length)
2427{
2428 // Set the length
2429 m_tree->setL(length);
2430
2431 // Set the new bounding box
2432 setBoundingBox();
2433
2434 // If needed, update the discretization
2435 if (m_vertices.size() > 0) {
2436 // Create tree connectivity
2437 m_tree->computeConnectivity();
2438
2439 // Update vertex coordinates
2440 std::unordered_set<long> alreadyEvaluated;
2441 for (const Cell &cell : m_cells) {
2442 OctantInfo octantInfo = getCellOctant(cell.getId());
2443 Octant *octant = getOctantPointer(octantInfo);
2444 ConstProxyVector<long> cellVertexIds = cell.getVertexIds();
2445 for (int k = 0; k < m_cellTypeInfo->nVertices; ++k) {
2446 long vertexId = cellVertexIds[k];
2447 if (alreadyEvaluated.count(vertexId) == 0) {
2448 Vertex &vertex = m_vertices.at(vertexId);
2449 vertex.setCoords(m_tree->getNode(octant, k));
2450 alreadyEvaluated.insert(vertexId);
2451 }
2452 }
2453 }
2454
2455 // Destroy tree connectivity
2456 m_tree->clearConnectivity();
2457 }
2458}
2459
2466void VolOctree::scale(const std::array<double, 3> &scaling, const std::array<double, 3> &center)
2467{
2468 bool uniformScaling = true;
2469 uniformScaling &= (std::abs(scaling[0] - scaling[1]) > 1e-14);
2470 uniformScaling &= (std::abs(scaling[0] - scaling[2]) > 1e-14);
2471 assert(uniformScaling);
2472 if (!uniformScaling) {
2473 log::cout() << "VolOctree patch only allows uniform scaling." << std::endl;
2474 return;
2475 }
2476
2477 std::array<double, 3> origin = m_tree->getOrigin();
2478 for (int n = 0; n < 3; ++n) {
2479 origin[n] = center[n] + scaling[n] * (origin[n] - center[n]);
2480 }
2481 m_tree->setOrigin(origin);
2482
2483 m_tree->setL(m_tree->getL() * scaling[0]);
2484
2485 VolumeKernel::scale(scaling, center);
2486
2487 // The bounding box is frozen, it is not updated automatically
2488 setBoundingBox();
2489}
2490
2505void VolOctree::_findCellNeighs(long id, const std::vector<long> *blackList, std::vector<long> *neighs) const
2506{
2507 OctantInfo octantInfo = getCellOctant(id);
2508 const Octant *octant = getOctantPointer(octantInfo);
2509
2510 std::vector<uint32_t> neighTreeIds;
2511 std::vector<bool> neighGhostFlags;
2512 m_tree->findAllCodimensionNeighbours(octant, neighTreeIds, neighGhostFlags);
2513
2514 int nNeighs = neighTreeIds.size();
2515 for (int i = 0; i < nNeighs; ++i) {
2516 OctantInfo neighOctantInfo(neighTreeIds[i], !neighGhostFlags[i]);
2517 long neighId = getOctantId(neighOctantInfo);
2518
2519 if (!blackList || utils::findInOrderedVector<long>(neighId, *blackList) == blackList->end()) {
2520 utils::addToOrderedVector<long>(neighId, *neighs);
2521 }
2522 }
2523}
2524
2540void VolOctree::_findCellEdgeNeighs(long id, int edge, const std::vector<long> *blackList, std::vector<long> *neighs) const
2541{
2542 assert(isThreeDimensional());
2543 if (!isThreeDimensional()) {
2544 return;
2545 }
2546
2547 // Get octant info
2548 const OctantInfo octantInfo = getCellOctant(id);
2549
2550 // Get edge neighbours
2551 int codimension = getDimension() - 1;
2552 findOctantCodimensionNeighs(octantInfo, edge, codimension, blackList, neighs);
2553
2554 // Add face neighbours
2555 //
2556 // Get all face neighbours and select the ones that contains the edge
2557 // for which the neighbours are requested. To correctly consider these
2558 // neighbours, the following logic can be used:
2559 // - if a face/edge neighbour has the same level or a lower level than
2560 // the current cell, then it certainly is also a vertex neighbour;
2561 // - if a face/edge neighbour has a higher level than the current cell,
2562 // it is necessary to check if the neighbour actually contains the
2563 // edge.
2564 //
2565 std::vector<long> faceNeighs;
2566 const Octant *octant = getOctantPointer(octantInfo);
2567 int octantLevel = octant->getLevel();
2568 for (int face : m_octantLocalFacesOnEdge[edge]) {
2569 faceNeighs.clear();
2570 _findCellFaceNeighs(id, face, blackList, &faceNeighs);
2571 for (long neighId : faceNeighs) {
2572 const OctantInfo neighOctantInfo = getCellOctant(neighId);
2573 const Octant *neighOctant = getOctantPointer(neighOctantInfo);
2574 int neighOctantLevel = neighOctant->getLevel();
2575 if (neighOctantLevel <= octantLevel) {
2576 utils::addToOrderedVector<long>(neighId, *neighs);
2577 } else if (m_tree->isEdgeOnOctant(octant, edge, neighOctant)) {
2578 utils::addToOrderedVector<long>(neighId, *neighs);
2579 }
2580 }
2581 }
2582}
2583
2597void VolOctree::_findCellVertexNeighs(long id, int vertex, const std::vector<long> *blackList, std::vector<long> *neighs) const
2598{
2599 // Get octant info
2600 const OctantInfo octantInfo = getCellOctant(id);
2601 const Octant *octant = getOctantPointer(octantInfo);
2602
2603 // Get vertex neighbours
2604 std::vector<uint32_t> neighTreeIds;
2605 std::vector<bool> neighGhostFlags;
2606 m_tree->findAllNodeNeighbours(octant, vertex, neighTreeIds, neighGhostFlags);
2607
2608 int nNeighs = neighTreeIds.size();
2609 for (int i = 0; i < nNeighs; ++i) {
2610 OctantInfo neighOctantInfo(neighTreeIds[i], !neighGhostFlags[i]);
2611 long neighId = getOctantId(neighOctantInfo);
2612
2613 if (!blackList || utils::findInOrderedVector<long>(neighId, *blackList) == blackList->end()) {
2614 utils::addToOrderedVector<long>(neighId, *neighs);
2615 }
2616 }
2617}
2618
2635void VolOctree::findOctantCodimensionNeighs(const OctantInfo &octantInfo, int index, int codimension,
2636 const std::vector<long> *blackList, std::vector<long> *neighs) const
2637{
2638 int dimension = getDimension();
2639 if (codimension > dimension || codimension <= 0) {
2640 return;
2641 }
2642
2643 const Octant *octant = getOctantPointer(octantInfo);
2644
2645 std::vector<uint32_t> neighTreeIds;
2646 std::vector<bool> neighGhostFlags;
2647 m_tree->findNeighbours(octant, index, codimension, neighTreeIds, neighGhostFlags);
2648
2649 int nNeighs = neighTreeIds.size();
2650 for (int i = 0; i < nNeighs; ++i) {
2651 OctantInfo neighOctantInfo(neighTreeIds[i], !neighGhostFlags[i]);
2652 long neighId = getOctantId(neighOctantInfo);
2653
2654 if (!blackList || utils::findInOrderedVector<long>(neighId, *blackList) == blackList->end()) {
2655 utils::addToOrderedVector<long>(neighId, *neighs);
2656 }
2657 }
2658}
2659
2669int VolOctree::findAdjoinNeighFace(const Cell &cell, int cellFace, const Cell &neigh) const
2670{
2671 long cellId = cell.getId();
2672
2673 // Get the neighbour face
2674 int neighFace = m_tree->getOppface()[cellFace];
2675
2676 // Check if the two cells are neighbours
2677 int nNeighFaceAdjacencies = neigh.getAdjacencyCount(neighFace);
2678 const long *neighFaceAdjacencies = neigh.getAdjacencies(neighFace);
2679 for (int k = 0; k < nNeighFaceAdjacencies; ++k) {
2680 if (neighFaceAdjacencies[k] == cellId) {
2681 return neighFace;
2682 }
2683 }
2684
2685 return -1;
2686}
2687
2698bool VolOctree::isSameFace(const Cell &cell_A, int face_A, const Cell &cell_B, int face_B) const
2699{
2700 // Check if the face matches
2701 if (m_tree->getOppface()[face_A] != face_B) {
2702 return false;
2703 }
2704
2705 // Check if the two cells are neighbours
2706 long cellId_A = cell_A.getId();
2707
2708 int nNeighFaceAdjacencies_B = cell_B.getAdjacencyCount(face_B);
2709 const long *neighFaceAdjacencies_B = cell_B.getAdjacencies(face_B);
2710 for (int k = 0; k < nNeighFaceAdjacencies_B; ++k) {
2711 if (neighFaceAdjacencies_B[k] == cellId_A) {
2712 return true;
2713 }
2714 }
2715
2716 return false;
2717}
2718
2719}
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()
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)
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.
Iterator for the class PiercedStorage.
Metafunction for generating a pierced vector.
void clear(bool release=true)
void emplaceBack(id_t id, Args &&... args)
void reserve(std::size_t n)
Metafunction for generating a list of elements that can be either stored in an external vectror or,...
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
The VolOctree defines a Octree patch.
Definition voloctree.hpp:37
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
The VolumeKernel class provides an interface for defining volume patches.
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:217
T uipow(const T &, unsigned int)
Definition Operators.tpp:55
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:1705
The Info struct defines the information associated to an adaption.
Definition adaption.hpp:63
--- layout: doxygen_footer ---