Loading...
Searching...
No Matches
levelSetObject.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 "levelSetObject.hpp"
26#include "levelSetCommon.hpp"
27
28#if BITPIT_ENABLE_MPI
29# include <mpi.h>
30#endif
31
32#include <algorithm>
33#include <numeric>
34#include <vector>
35
36namespace bitpit {
37
68
79
85 : m_kernel(nullptr),
86 m_defaultSignedLevelSet(false),
87 m_narrowBandSize(levelSetDefaults::NARROW_BAND_SIZE),
88 m_cellLocationCacheId(CellCacheCollection::NULL_CACHE_ID),
89 m_cellPropagatedSignCacheId(CellCacheCollection::NULL_CACHE_ID),
90 m_nReferences(0),
91 m_cellBulkEvaluationMode(LevelSetBulkEvaluationMode::SIGN_PROPAGATION),
92 m_cellFieldCacheModes(static_cast<std::size_t>(LevelSetField::COUNT), LevelSetCacheMode::NONE),
93 m_cellFieldCacheIds(static_cast<std::size_t>(LevelSetField::COUNT), CellCacheCollection::NULL_CACHE_ID)
94{
95 setId(id);
96}
97
103 : m_kernel(other.m_kernel),
104 m_defaultSignedLevelSet(other.m_defaultSignedLevelSet),
105 m_enabledOutputFields(other.m_enabledOutputFields),
106 m_narrowBandSize(other.m_narrowBandSize),
107 m_cellLocationCacheId(other.m_cellLocationCacheId),
108 m_cellPropagatedSignCacheId(other.m_cellPropagatedSignCacheId),
109 m_id(other.m_id),
110 m_nReferences(other.m_nReferences),
111 m_cellBulkEvaluationMode(other.m_cellBulkEvaluationMode),
112 m_cellCacheCollection(new CellCacheCollection(*(other.m_cellCacheCollection))),
113 m_cellFieldCacheModes(other.m_cellFieldCacheModes),
114 m_cellFieldCacheIds(other.m_cellFieldCacheIds)
115
116{
117 for ( const auto &fieldEntry : m_enabledOutputFields ) {
118 enableVTKOutput(fieldEntry.first, true);
119 }
120}
121
127 : m_kernel(std::move(other.m_kernel)),
128 m_defaultSignedLevelSet(std::move(other.m_defaultSignedLevelSet)),
129 m_narrowBandSize(std::move(other.m_narrowBandSize)),
130 m_cellLocationCacheId(std::move(other.m_cellLocationCacheId)),
131 m_cellPropagatedSignCacheId(std::move(other.m_cellPropagatedSignCacheId)),
132 m_id(std::move(other.m_id)),
133 m_nReferences(std::move(other.m_nReferences)),
134 m_cellBulkEvaluationMode(std::move(other.m_cellBulkEvaluationMode)),
135 m_cellCacheCollection(std::move(other.m_cellCacheCollection)),
136 m_cellFieldCacheModes(std::move(other.m_cellFieldCacheModes)),
137 m_cellFieldCacheIds(std::move(other.m_cellFieldCacheIds))
138{
139 for ( const auto &fieldEntry : other.m_enabledOutputFields ) {
140 enableVTKOutput(fieldEntry.first, true);
141 }
142
143 for ( const auto &fieldEntry : other.m_enabledOutputFields ) {
144 other.enableVTKOutput(fieldEntry.first, false);
152 // Disable all output for the object
153 if (m_kernel) {
154 try {
155 // Disable output
156 LevelSetFieldset enabledOutputFieldset;
157 enabledOutputFieldset.reserve(m_enabledOutputFields.size());
158 for ( const auto &fieldEntry : m_enabledOutputFields ) {
159 enabledOutputFieldset.push_back(fieldEntry.first);
160 }
161
162 enableVTKOutput(enabledOutputFieldset, false);
163 } catch (const std::exception &exception) {
164 BITPIT_UNUSED(exception);
165
166 // Nothing to do
167 }
168 }
169}
170
175LevelSetFieldset LevelSetObject::getSupportedFields() const {
176
177 LevelSetFieldset supportedFields;
178 supportedFields.push_back(LevelSetField::SIGN);
179 supportedFields.push_back(LevelSetField::VALUE);
180 supportedFields.push_back(LevelSetField::GRADIENT);
181
182 return supportedFields;
183
184}
185
190void LevelSetObject::setId(int id) {
191 m_id = id;
192}
193
197std::size_t LevelSetObject::incrementReferenceCount() {
198
199 ++m_nReferences;
200
201 return m_nReferences;
202
203}
204
208std::size_t LevelSetObject::decrementReferenceCount() {
209
210 assert(m_nReferences > 0);
211 --m_nReferences;
212
213 return m_nReferences;
214
215}
216
222
223 return m_nReferences ;
226
235 m_defaultSignedLevelSet = signedLevelSet;
236}
237
243 // Set kernel
244 m_kernel = kernel;
245
246 // Enable output
247 for ( const auto &fieldEntry : m_enabledOutputFields ) {
248 enableVTKOutput( fieldEntry.first, true ) ;
249 }
250
251 // Create cell cache collection
253 m_cellCacheCollection = std::unique_ptr<CellCacheCollection>(new CellCacheCollection(cacheKernel));
254
255 // Evaluate data
256 evaluate();
257}
258
266
272 return m_kernel;
273}
274
280 return m_id ;
281}
282
289 return true;
290}
291
296{
297 // Get fields with empty caches
298 std::vector<LevelSetField> fillableFields;
299 for (std::size_t fieldIndex = 0; fieldIndex < static_cast<std::size_t>(LevelSetField::COUNT); ++fieldIndex) {
300 LevelSetField field = static_cast<LevelSetField>(fieldIndex);
301
302 LevelSetCacheMode fieldCacheMode = getFieldCellCacheMode(field);
303 if (fieldCacheMode == LevelSetCacheMode::NONE) {
304 continue;
305 }
306
307 std::size_t fieldCacheId = getFieldCellCacheId(field);
308 if (fieldCacheId != CellCacheCollection::NULL_CACHE_ID) {
309 continue;
310 }
311
312 fillableFields.push_back(field);
313 }
314
315 // Create field cell caches
316 for (LevelSetField field : fillableFields) {
318 }
319
320 // Evaluate narrow band data
322
323 // Fill field cell caches inside the narrow band
325
326 // Evaluate bulk data
328
329 // Fill field cell caches inside the bulk
331}
332
337void LevelSetObject::update( const std::vector<adaption::Info> &adaptionData )
338{
339 // Get fields with non-empty caches
340 std::vector<LevelSetField> fillableFields;
341 for (std::size_t fieldIndex = 0; fieldIndex < static_cast<std::size_t>(LevelSetField::COUNT); ++fieldIndex) {
342 LevelSetField field = static_cast<LevelSetField>(fieldIndex);
343
344 LevelSetCacheMode fieldCacheMode = getFieldCellCacheMode(field);
345 if (fieldCacheMode == LevelSetCacheMode::NONE) {
346 continue;
347 }
348
349 std::size_t fieldCacheId = getFieldCellCacheId(field);
350 if (fieldCacheId == CellCacheCollection::NULL_CACHE_ID) {
351 continue;
352 }
353
354 fillableFields.push_back(field);
355 }
356
357 // Update cell caches
358 adaptCellCaches(adaptionData);
359
360 // Evaluate narrow band data
361 updateCellNarrowBandData(adaptionData);
362
363 // Fill field cell caches inside the narrow band
364 fillFieldCellCaches(LevelSetZone::NARROW_BAND, fillableFields, adaptionData);
365
366 // Evaluate bulk data
367 updateCellBulkData(adaptionData);
368
369 // Fill field cell caches inside the bulk
370 fillFieldCellCaches(LevelSetZone::BULK, fillableFields, adaptionData);
371}
372
385{
386 return m_narrowBandSize;
387}
388
401{
402 // Early return if size doesn't need to be updated
403 if (m_narrowBandSize == size) {
404 return;
405 }
406
407 // Destroy current data
408 //
409 // Current information need to be destroyed (not just cleared) because the updated narrow
410 // band size may require different storage types.
411 if (getKernel()) {
414
415 for (std::size_t fieldIndex = 0; fieldIndex < static_cast<std::size_t>(LevelSetField::COUNT); ++fieldIndex) {
416 LevelSetField field = static_cast<LevelSetField>(fieldIndex);
417 LevelSetCacheMode fieldCacheMode = getFieldCellCacheMode(field);
418
419 bool destroyFieldCache = true;
420 if (fieldCacheMode == LevelSetCacheMode::NONE) {
421 destroyFieldCache = false;
423 destroyFieldCache = false;
424 }
425
426 if (destroyFieldCache) {
428 }
429 }
430 }
431
432 // Set the narrow band size
433 m_narrowBandSize = size;
434
435 // Evaluate updated information
436 if (getKernel()) {
437 evaluate();
438 }
439}
440
445{
446 // Identify cells locations
447 if (m_narrowBandSize != LEVELSET_NARROW_BAND_UNLIMITED) {
450 }
451}
452
458void LevelSetObject::updateCellNarrowBandData( const std::vector<adaption::Info> &adaptionData )
459{
460 // Update cells locations
461 if (m_narrowBandSize != LEVELSET_NARROW_BAND_UNLIMITED) {
462 fillCellLocationCache(adaptionData);
463 }
464}
465
470{
471 // Identify cells inside the narrow band
472 if (m_narrowBandSize != LEVELSET_NARROW_BAND_UNLIMITED) {
474 }
475}
476
484{
485 // Early return if the object is empty
486 if (empty()) {
488 }
489
490 // Early return if the narrow band is unlimited
491 if (m_narrowBandSize == LEVELSET_NARROW_BAND_UNLIMITED) {
493 }
494
495 // Early return if the narrow band cells has not been identified yet
498 }
499
500 // Get the location from the cache
501 CellCacheCollection::ValueCache<char> *locationCache = getCellCache<char>(m_cellLocationCacheId);
502 CellCacheCollection::ValueCache<char>::Entry locationCacheEntry = locationCache->findEntry(id);
503
504 if (locationCacheEntry.isValid()) {
505 return static_cast<LevelSetCellLocation>(*locationCacheEntry);
506 } else {
508 }
509}
510
518{
519 LevelSetCellLocation cellLocation = getCellLocation(id);
520 switch (cellLocation) {
521
523 return LevelSetZone::BULK;
524
530
532 throw std::runtime_error("Cell location identification has not been performed!");
533
534 default:
535 throw std::runtime_error("Unable to identify cell zone!");
536
537 }
538}
539
551{
552 // Mesh information
553 const VolumeKernel &mesh = *(m_kernel->getMesh()) ;
554
555 // Get the ids associated with internal cells
556 std::vector<long> internalCellIds;
557 if (const VolCartesian *cartesianMesh = dynamic_cast<const VolCartesian *>(&mesh)) {
558 long nCells = cartesianMesh->getCellCount();
559 internalCellIds.resize(nCells);
560 std::iota(internalCellIds.begin(), internalCellIds.end(), 0);
561 } else {
562 long nCells = mesh.getCellCount();
565
566 internalCellIds.reserve(nCells);
567 for (VolumeKernel::CellConstIterator cellItr = internalCellsBegin; cellItr != internalCellsEnd; ++cellItr) {
568 long cellId = cellItr.getId();
569 internalCellIds.push_back(cellId);
570 }
571 }
572
573 // Get cell location cache
574 CellCacheCollection::ValueCache<char> *locationCache = getCellCache<char>(m_cellLocationCacheId);
575
576 // Clear cell location cache
578
579 // Assign an unknown location to the internal cells.
580 for (long cellId : internalCellIds) {
581 locationCache->insertEntry(cellId, static_cast<char>(LevelSetCellLocation::UNKNOWN));
582 }
583
584 // Identify internal cells that are geometrically inside the narrow band
585 //
586 // A cell is geometrically inside the narrow band if its distance from the surface is smaller
587 // than the narrow band side or if it intersects the surface.
588 //
589 // Cells that intersect the zero-levelset iso-surface need to be identified because they will
590 // be further processed for finding which of their neighbour is inside the narrow band.
591 std::vector<long> intersectedCellIds;
592 if (!empty()) {
593 for (long cellId : internalCellIds) {
594 // Fill location cache for cells geometrically inside the narrow band
596
597 // Track intersected cells
599 intersectedCellIds.push_back(cellId);
600 }
601 }
602 }
603
604 // Identify face neighbours of cells that intersect the surface
605 for (long cellId : intersectedCellIds) {
606 auto neighProcessor = [this, &locationCache](long neighId, int layer) {
607 BITPIT_UNUSED(layer);
608
609 // Skip neighbours whose region have already been identified
611 return false;
612 }
613
614 // The neighbour is inside the narrow band
615 locationCache->insertEntry(neighId, static_cast<char>(LevelSetCellLocation::NARROW_BAND_NEIGHBOUR));
616
617 // Continue processing the other neighbours
618 return false;
619 };
620
621 mesh.processCellFaceNeighbours(cellId, 1, neighProcessor);
622 }
623
624 // Cells whose location is still unknown are in the bulk
625 for (long cellId : internalCellIds) {
626 CellCacheCollection::ValueCache<char>::Entry locationCacheEntry = locationCache->findEntry(cellId);
627 assert(locationCacheEntry.isValid());
628 if (static_cast<LevelSetCellLocation>(*locationCacheEntry) == LevelSetCellLocation::UNKNOWN) {
629 locationCache->insertEntry(cellId, static_cast<char>(LevelSetCellLocation::BULK));
630 }
631 }
632
633#if BITPIT_ENABLE_MPI==1
634 // Exchange ghost data
635 if (mesh.isPartitioned()) {
636 std::unique_ptr<DataCommunicator> dataCommunicator = m_kernel->createDataCommunicator();
639 }
640#endif
641}
642
655void LevelSetObject::fillCellLocationCache(const std::vector<adaption::Info> &adaptionData)
656{
657 // Mesh information
658 const VolumeKernel &mesh = *(m_kernel->getMesh()) ;
659
660 // Get cell location cache
661 CellCacheCollection::ValueCache<char> *locationCache = getCellCache<char>(m_cellLocationCacheId);
662
663 // Initialize cells updated by the mesh adaption
664 std::unordered_set<long> unknownZoneCellIds;
665 for (const adaption::Info &adaptionInfo : adaptionData) {
666 if (adaptionInfo.entity != adaption::Entity::ENTITY_CELL) {
667 continue;
668 }
669
670 // Skip received cells when the cache is non-volatile
671 //
672 // If the cache is non-volatile, data on exchanged cells has been communicated during
673 // cache adaption.
674 if (adaptionInfo.type == adaption::Type::TYPE_PARTITION_RECV && !locationCache->isVolatile()) {
675 continue;
676 }
677
678 // Add current cells to the process list
679 for (long cellId : adaptionInfo.current) {
680
681 // Assign an unknown location to the newly created cells
682 locationCache->insertEntry(cellId, static_cast<char>(LevelSetCellLocation::UNKNOWN));
683
684 // Add internal cells to the process list
685 bool isCellProcessable = true;
686#if BITPIT_ENABLE_MPI==1
687 if (mesh.isPartitioned()) {
689 isCellProcessable = cellItr->isInterior();
690 }
691#endif
692
693 if (isCellProcessable) {
694 unknownZoneCellIds.insert(cellId);
695 }
696 }
697 }
698
699 // Identify internal cells that are geometrically inside the narrow band
700 //
701 // A cell is geometrically inside the narrow band if its distance from the surface is smaller
702 // than the narrow band side or if it intersects the surface.
703 //
704 // Cells that intersect the surface are identified, because they will be further processed for
705 // finding which of their neighbours is inside the narrow band.
706 std::unordered_set<long> intersectedCellIds;
707 if (!empty()) {
708 auto cellIdItr = unknownZoneCellIds.begin();
709 while (cellIdItr != unknownZoneCellIds.end()) {
710 long cellId = *cellIdItr;
711
712 // Fill location cache for cells geometrically inside the narrow band
714
715 // Track intersected cells
717 intersectedCellIds.insert(cellId);
718 }
719
720 // Remove from the list cell whose region has been identified
721 if (cellLocation != LevelSetCellLocation::UNKNOWN) {
722 cellIdItr = unknownZoneCellIds.erase(cellIdItr);
723 } else {
724 ++cellIdItr;
725 }
726 }
727 }
728
729#if BITPIT_ENABLE_MPI==1
730 // Exchange ghost data
731 if (mesh.isPartitioned()) {
732 std::unique_ptr<DataCommunicator> dataCommunicator = m_kernel->createDataCommunicator();
735 }
736#endif
737
738 // Track intersected neighbours of cells whose location was not yet identified
739 //
740 // We need to identified cells that are the narrow band because of a neighbour that was
741 // not affected by the mesh update.
742 if (!empty()) {
743 for (long cellId : unknownZoneCellIds) {
744 // Process cells whose location has not been identified
745 //
746 // For cells whose location has not been identified, the neighbours that intersect
747 // the surface need to be tracked.
748 auto neighProcessor = [this, &intersectedCellIds](long neighId, int layer) {
749 BITPIT_UNUSED(layer);
750
751 // Skip neighbours that are aready identified as intersected
752 if (intersectedCellIds.count(neighId) > 0) {
753 return false;
754 }
755
756 // Skip neighbours that doesn't intersect the surface
757 LevelSetCellLocation neighLocation = getCellLocation(neighId);
759 return false;
760 }
761
762 // Track neighbours that intersect the surface
763 intersectedCellIds.insert(neighId);
764
765 // Continue processing the other neighbours
766 return false;
767 };
768
769 mesh.processCellFaceNeighbours(cellId, 1, neighProcessor);
770 }
771 }
772
773 // Identify neighbours of cells that intersect the surface
774 //
775 // We may need the distance of cells that are not processed,
776 for (long cellId : intersectedCellIds) {
777 // Process face neighbours
778 auto neighProcessor = [this, &locationCache, &unknownZoneCellIds](long neighId, int layer) {
779 BITPIT_UNUSED(layer);
780
781 // Skip neighbours whose region has already been identified
783 return false;
784 }
785
786 // The neighbour is inside the narrow band
787 locationCache->insertEntry(neighId, static_cast<char>(LevelSetCellLocation::NARROW_BAND_NEIGHBOUR));
788 unknownZoneCellIds.erase(neighId);
789
790 // Continue processing the other neighbours
791 return false;
792 };
793
794 mesh.processCellFaceNeighbours(cellId, 1, neighProcessor);
795 }
796
797 // Cells whose location is still unknown are in the bulk
798 for (long cellId : unknownZoneCellIds) {
799 locationCache->insertEntry(cellId, static_cast<char>(LevelSetCellLocation::BULK));
800 }
801
802#if BITPIT_ENABLE_MPI==1
803 // Exchange ghost data
804 if (mesh.isPartitioned()) {
805 std::unique_ptr<DataCommunicator> dataCommunicator = m_kernel->createDataCommunicator();
808 }
809#endif
810}
811
827{
828 // Get cell information
829 double cellCacheValue = evalCellValue(id, CELL_CACHE_IS_SIGNED);
830 double cellUnsignedValue = std::abs(cellCacheValue);
831
832 // Identify cells that are geometrically inside the narrow band
833 //
834 // First we need to check if the cell intersectes the surface, and only if it
835 // deosn't we should check if its distance is lower than the narrow band size.
839 } else if (cellUnsignedValue <= m_narrowBandSize) {
841 }
842
843 if (cellLocation != LevelSetCellLocation::UNKNOWN) {
844 CellCacheCollection::ValueCache<char> *locationCache = getCellCache<char>(m_cellLocationCacheId);
845 locationCache->insertEntry(id, static_cast<char>(cellLocation));
846 }
847
848 // Fill the cache of the evaluated fields
849 //
850 // Now that the cell location has been identified, we can fill the cache of the
851 // evaluated fields.
852 fillFieldCellCache(LevelSetField::VALUE, id, cellCacheValue);
853
854 // Return the location
855 return cellLocation;
856}
857
865std::size_t LevelSetObject::createCellLocationCache(std::size_t cacheId)
866{
867 m_cellLocationCacheId = createCellCache<char>(LevelSetFillIn::DENSE, cacheId);
868
870}
871
880
897{
898 LevelSetZone zone = getCellZone(id);
899
900 return (zone == LevelSetZone::NARROW_BAND);
901}
902
919bool LevelSetObject::isInNarrowBand(const std::array<double,3> &point)const
920{
921 // Early return if the object is empty
922 if (empty()) {
923 return false;
924 }
925
926 // Early return if no narrow band was set
927 if (m_narrowBandSize == LEVELSET_NARROW_BAND_UNLIMITED) {
928 return true;
929 }
930
931 // Explicitly check if the cell is inside the narrow band
932 double value = evalValue(point, false);
933 if (value <= m_narrowBandSize) {
934 return true;
935 }
936
937 return false;
938}
939
946{
947 return m_cellBulkEvaluationMode;
948}
949
956{
957 // Early return if the bulk evaluation mode doesn't need to be updated
958 if (getCellBulkEvaluationMode() == evaluationMode) {
959 return;
960 }
961
962 // Destroy bulk data
963 //
964 // Current data need to be destroyed (not just cleared) because the updated bulk evaluation
965 // mode may require different storage types.
966 if (getKernel()) {
968
969 for (std::size_t fieldIndex = 0; fieldIndex < static_cast<std::size_t>(LevelSetField::COUNT); ++fieldIndex) {
970 LevelSetField field = static_cast<LevelSetField>(fieldIndex);
971 LevelSetCacheMode fieldCacheMode = getFieldCellCacheMode(field);
972
973 bool destroyFieldCache = false;
974 if (fieldCacheMode == LevelSetCacheMode::ON_DEMAND) {
975 destroyFieldCache = true;
976 } else if (fieldCacheMode == LevelSetCacheMode::FULL) {
977 destroyFieldCache = true;
978 }
979
980 if (destroyFieldCache) {
982 }
983 }
984 }
985
986 // Set bulk evaluation mode
987 m_cellBulkEvaluationMode = evaluationMode;
988
989 // Evaluate data
990 if (getKernel()) {
991 evaluate();
992 }
993}
994
1005
1009void LevelSetObject::updateCellBulkData( const std::vector<adaption::Info> &adaptionData )
1010{
1011 BITPIT_UNUSED(adaptionData);
1012
1013 if (m_cellBulkEvaluationMode == LevelSetBulkEvaluationMode::SIGN_PROPAGATION) {
1015 }
1016}
1017
1022{
1023 if (m_cellBulkEvaluationMode == LevelSetBulkEvaluationMode::SIGN_PROPAGATION) {
1025 }
1026}
1027
1032{
1033 const VolumeKernel &mesh = *(m_kernel->getMesh());
1034
1035#if BITPIT_ENABLE_MPI==1
1036 // Check if the communications are needed
1037 bool ghostExchangeNeeded = mesh.isPartitioned();
1038
1039 // Initialize ghost communications
1040 std::unique_ptr<DataCommunicator> ghostCommunicator;
1041 if (ghostExchangeNeeded) {
1042 ghostCommunicator = std::unique_ptr<DataCommunicator>(new DataCommunicator(mesh.getCommunicator()));
1043
1044 for (auto &entry : mesh.getGhostCellExchangeSources()) {
1045 const int rank = entry.first;
1046 const std::vector<long> &sources = entry.second;
1047 ghostCommunicator->setSend(rank, sources.size() * (sizeof(unsigned char) + sizeof(char)));
1048 }
1049
1050 for (auto &entry : mesh.getGhostCellExchangeTargets()) {
1051 const int rank = entry.first;
1052 const std::vector<long> &targets = entry.second;
1053 ghostCommunicator->setRecv(rank, targets.size() * (sizeof(unsigned char) + sizeof(char)));
1054 }
1055 }
1056#endif
1057
1058 // Get the ids associated with internal cells
1059 std::vector<long> internalCellIds;
1060 if (const VolCartesian *cartesianMesh = dynamic_cast<const VolCartesian *>(&mesh)) {
1061 long nCells = cartesianMesh->getCellCount();
1062 internalCellIds.resize(nCells);
1063 std::iota(internalCellIds.begin(), internalCellIds.end(), 0);
1064 } else {
1065 long nCells = mesh.getCellCount();
1066 VolumeKernel::CellConstIterator internalCellsBegin = mesh.internalCellConstBegin();
1067 VolumeKernel::CellConstIterator internalCellsEnd = mesh.internalCellConstEnd();
1068
1069 internalCellIds.reserve(nCells);
1070 for (VolumeKernel::CellConstIterator cellItr = internalCellsBegin; cellItr != internalCellsEnd; ++cellItr) {
1071 long cellId = cellItr.getId();
1072 internalCellIds.push_back(cellId);
1073 }
1074 }
1075
1076 // Get cache for sign propagation
1077 CellCacheCollection::ValueCache<char> *propagatedSignCache = getCellCache<char>(m_cellPropagatedSignCacheId);
1078
1079 // Clear sign propagation cache
1081
1082 // Initialize sign propagation
1083 //
1084 // Sign propagation will start from the internal cells within the narrow band.
1085 // It is not possible to popagate a null sign.
1086 std::unordered_set<long> seedIds;
1087 for (long cellId : internalCellIds) {
1088 if (isCellInNarrowBand(cellId)) {
1089 char cellSign = static_cast<char>(evalCellSign(cellId));
1090 propagatedSignCache->insertEntry(cellId, cellSign);
1091 if (cellSign != 0) {
1092 seedIds.insert(cellId);
1093 }
1094 }
1095 }
1096
1097 // Propagate the sign
1098 std::vector<long> processList;
1099 while (true) {
1100 bool signMismatch = false;
1101 while (!seedIds.empty()) {
1102 // Get seed
1103 long seedId = *(seedIds.begin());
1104 seedIds.erase(seedIds.begin());
1105
1106 // Get seed sign
1107 CellCacheCollection::ValueCache<char>::Entry seedSignEntry = propagatedSignCache->findEntry(seedId);
1108 char seedSign = *(seedSignEntry);
1109
1110 // Propagate seed sign
1111 //
1112 // We need to continue processing until the process list is empty, even
1113 // if we have already processed the internal cells. The process list may
1114 // contain ghost cells that carry information received by other processes.
1115 processList.assign(1, seedId);
1116 while (!processList.empty()) {
1117 // Get cell to process
1118 long cellId = processList.back();
1119 processList.pop_back();
1120
1121 // Set the sign of the cell
1122 //
1123 // If the sign of the cell has already been set, we check if it matches
1124 // the seed sign and then we skip the cell, otherwise we set the sign of
1125 // the cell and we process its neighoburs. Once the sign of a cell is
1126 // set, we can remove it form the seed list (there is no need to start
1127 // again the porpagation from that cell, beause it will not reach any
1128 // new portions of the domain).
1129 if (cellId != seedId) {
1130 CellCacheCollection::ValueCache<char>::Entry cellSignEntry = propagatedSignCache->findEntry(cellId);
1131 if (cellSignEntry.isValid()) {
1132 char cellSign = *(cellSignEntry);
1133 if (cellSign != seedSign) {
1134 signMismatch = true;
1135 log::error() << "Sign mismatch on cell " << cellId << "!" << std::endl;
1136 break;
1137 } else {
1138 continue;
1139 }
1140 } else {
1141 propagatedSignCache->insertEntry(cellId, seedSign);
1142 seedIds.erase(cellId);
1143 }
1144 }
1145
1146 // Add face neighbours to the process list
1147 auto neighProcessor = [&mesh, &propagatedSignCache, &processList](long neighId, int layer) {
1148 BITPIT_UNUSED(layer);
1149#if BITPIT_ENABLE_MPI==0
1150 BITPIT_UNUSED(mesh);
1151#endif
1152
1153 // Skip neighbours whose sign has already been set
1154 if (propagatedSignCache->contains(neighId)) {
1155 return false;
1156 }
1157
1158#if BITPIT_ENABLE_MPI==1
1159 // Skip ghost neighbours
1160 if (mesh.isPartitioned()) {
1162 const Cell &neigh = *neighItr;
1163 if (!neigh.isInterior()) {
1164 return false;
1165 }
1166 }
1167#endif
1168
1169 // Add neighbour to the process list
1170 processList.push_back(neighId);
1171
1172 // Continue processing the other neighbours
1173 return false;
1174 };
1175
1176 mesh.processCellFaceNeighbours(cellId, 1, neighProcessor);
1177 }
1178
1179 // Stop processing if a sign mismatch was detected
1180 if (signMismatch) {
1181 break;
1182 }
1183 }
1184
1185 // Raise an error if a sign mismatch was detected
1186#if BITPIT_ENABLE_MPI==1
1187 if (ghostExchangeNeeded) {
1188 MPI_Allreduce(MPI_IN_PLACE, &signMismatch, 1, MPI_C_BOOL, MPI_LOR, mesh.getCommunicator());
1189 }
1190#endif
1191
1192 if (signMismatch) {
1193 throw std::runtime_error("Sign mismatch in sign propagation!");
1194 }
1195
1196#if BITPIT_ENABLE_MPI==1
1197 // Add ghost to the process list
1198 //
1199 // A ghost should be added to the process list if it has been processed by the owner
1200 // but is hasn't yet processed by the current process.
1201 if (ghostExchangeNeeded) {
1202 // Exchange ghost data
1203 ghostCommunicator->startAllRecvs();
1204
1205 for(auto &entry : mesh.getGhostCellExchangeSources()) {
1206 int rank = entry.first;
1207 SendBuffer &buffer = ghostCommunicator->getSendBuffer(rank);
1208 for (long cellId : entry.second) {
1209 CellCacheCollection::ValueCache<char>::Entry cellSignEntry = propagatedSignCache->findEntry(cellId);
1210
1211 char cellHasSign = cellSignEntry.isValid() ? 1 : 0;
1212 buffer << cellHasSign;
1213
1214 if (cellHasSign) {
1215 char cellSign = *(cellSignEntry);
1216 buffer << cellSign;
1217 }
1218 }
1219 ghostCommunicator->startSend(rank);
1220 }
1221
1222 bool ghostSignMismatch = false;
1223 int nPendingRecvs = ghostCommunicator->getRecvCount();
1224 while (nPendingRecvs != 0) {
1225 int rank = ghostCommunicator->waitAnyRecv();
1226 RecvBuffer buffer = ghostCommunicator->getRecvBuffer(rank);
1227
1228 for (long cellId : mesh.getGhostCellExchangeTargets(rank)) {
1229 char sourceHasSign;
1230 buffer >> sourceHasSign;
1231
1232 if (sourceHasSign == 1) {
1233 char sourceSign;
1234 buffer >> sourceSign;
1235
1236 if (!propagatedSignCache->contains(cellId)) {
1237 seedIds.insert(cellId);
1238 propagatedSignCache->insertEntry(cellId, sourceSign);
1239 } else {
1240 char cellCachedSign = *(propagatedSignCache->findEntry(cellId));
1241 if (cellCachedSign != sourceSign) {
1242 ghostSignMismatch = true;
1243 log::error() << "Sign mismatch on ghost cell " << cellId << "!" << getId() << std::endl;
1244 break;
1245 }
1246 }
1247 }
1248 }
1249
1250 --nPendingRecvs;
1251
1252 // Stop processing if a sign mismatch was detected
1253 if (ghostSignMismatch) {
1254 break;
1255 }
1256 }
1257
1258 ghostCommunicator->waitAllSends();
1259
1260 // Raise an error if there are ghosts with a sign mismatch
1261 if (ghostExchangeNeeded) {
1262 MPI_Allreduce(MPI_IN_PLACE, &ghostSignMismatch, 1, MPI_C_BOOL, MPI_LOR, mesh.getCommunicator());
1263 }
1264
1265 if (ghostSignMismatch) {
1266 throw std::runtime_error("Sign mismatch in sign propagation!");
1267 }
1268 }
1269#endif
1270
1271 // Detect if the propagation is completed
1272 bool completed = seedIds.empty();
1273#if BITPIT_ENABLE_MPI==1
1274 if (ghostExchangeNeeded) {
1275 MPI_Allreduce(MPI_IN_PLACE, &completed, 1, MPI_C_BOOL, MPI_LAND, mesh.getCommunicator());
1276 }
1277#endif
1278
1279 if (completed) {
1280 break;
1281 }
1282 }
1283}
1284
1293{
1294 m_cellPropagatedSignCacheId = createCellCache<char>(LevelSetFillIn::DENSE, cacheId);
1295
1297}
1298
1307
1342{
1343 // Try evaluating intersection information from the cell location
1344 //
1345 // Regardless from the requested mode, a cell can intersect the zero-levelset iso-surface
1346 // only if it is inside the narrow band.
1347 //
1348 // If the requested mode is the same mode used for identify the cell location, we can get the
1349 // intersection information directly from the location.
1350 LevelSetCellLocation cellLocation = getCellLocation(id);
1351 if (cellLocation == LevelSetCellLocation::BULK) {
1353 } else if (mode == CELL_LOCATION_INTERSECTION_MODE) {
1356 } else if (cellLocation != LevelSetCellLocation::NARROW_BAND_UNDEFINED) {
1358 }
1359 }
1360
1361 // Check for intersection with zero-levelset iso-surface
1362 double distance = evalCellValue(id, false);
1363
1364 return _intersectSurface(id, distance, mode);
1365}
1366
1403{
1404 double distanceTolerance = m_kernel->getDistanceTolerance();
1405
1406 switch(mode){
1408 {
1409 double tangentSphere = m_kernel->computeCellTangentRadius(id) ;
1410 if(utils::DoubleFloatingLessEqual()(distance, tangentSphere, distanceTolerance, distanceTolerance)){
1412 } else {
1414 }
1415
1416 break;
1417 }
1418
1420 {
1421 double boundingSphere = m_kernel->computeCellBoundingRadius(id) ;
1422 if(utils::DoubleFloatingGreater()(distance, boundingSphere, distanceTolerance, distanceTolerance)){
1424 } else {
1426 }
1427
1428 break;
1429 }
1430
1432 {
1433 double boundingSphere = m_kernel->computeCellBoundingRadius(id) ;
1434 if(utils::DoubleFloatingGreater()(distance, boundingSphere, distanceTolerance, distanceTolerance)){
1436 }
1437
1438 double tangentSphere = m_kernel->computeCellTangentRadius(id) ;
1439 if(utils::DoubleFloatingLessEqual()(distance, tangentSphere, distanceTolerance, distanceTolerance)){
1441 }
1442
1444
1445 break;
1446 }
1447
1449 {
1450 double boundingSphere = m_kernel->computeCellBoundingRadius(id) ;
1451 if(utils::DoubleFloatingGreater()(distance, boundingSphere, distanceTolerance, distanceTolerance)){
1453 }
1454
1455 double tangentSphere = m_kernel->computeCellTangentRadius(id) ;
1456 if(utils::DoubleFloatingLessEqual()(distance, tangentSphere, distanceTolerance, distanceTolerance)){
1458 }
1459
1460 std::array<double,3> root = evalCellProjectionPoint(id);
1461 std::array<double,3> normal = evalCellGradient(id, true);
1462 if( m_kernel->intersectCellPlane(id,root,normal, distanceTolerance) ){
1464 } else {
1466 }
1467
1468 break;
1469 }
1470 }
1471
1472 BITPIT_UNREACHABLE("cannot reach");
1473
1474}
1475
1481short LevelSetObject::evalCellSign(long id) const {
1482
1483 // Define sign evaluators
1484 auto evaluator = [this] (long id)
1485 {
1486 // Try fetching the sign from sign propagation
1487 CellCacheCollection::ValueCache<char> *propagatedSignCache = getCellCache<char>(m_cellPropagatedSignCacheId);
1488 if (propagatedSignCache) {
1489 CellCacheCollection::ValueCache<char>::Entry propagatedSignCacheEntry = propagatedSignCache->findEntry(id);
1490 if (propagatedSignCacheEntry.isValid()) {
1491 return static_cast<short>(*propagatedSignCacheEntry);
1492 }
1493 }
1494
1495 // Try fetching the sign from the cached value
1497 CellCacheCollection::ValueCache<double> *valueCache = getFieldCellCache<double>(LevelSetField::VALUE);
1498 if (valueCache) {
1501 if (valueCacheMode == LevelSetCacheMode::FULL || signCacheMode == valueCacheMode) {
1502 CellCacheCollection::ValueCache<double>::Entry valueCacheEntry = valueCache->findEntry(id);
1503 if (valueCacheEntry.isValid()) {
1504 return evalValueSign(*valueCacheEntry);
1505 }
1506 }
1507 }
1508 }
1509
1510 // Evaluate the sign
1511 return _evalCellSign(id);
1512 };
1513
1514 auto fallback = [this] (long id)
1515 {
1516 // Try fetching the sign from sign propagation
1517 CellCacheCollection::ValueCache<char> *propagatedSignCache = getCellCache<char>(m_cellPropagatedSignCacheId);
1518 if (propagatedSignCache) {
1519
1520 CellCacheCollection::ValueCache<char>::Entry propagatedSignCacheEntry = propagatedSignCache->findEntry(id);
1521 if (propagatedSignCacheEntry.isValid()) {
1522 return static_cast<short>(*propagatedSignCacheEntry);
1523 }
1524 }
1525
1526 // Return a dummy value
1528 };
1529
1531 short sign = evalCellFieldCached<short>(field, id, evaluator, fallback);
1532
1533 return sign;
1534}
1535
1542double LevelSetObject::evalCellValue(long id, bool signedLevelSet) const {
1543
1544 // Evaluate signed value
1545 //
1546 // The value stored in the cache is unsigned.
1547 auto evaluator = [this] (long id)
1548 {
1549 return _evalCellValue(id, false);
1550 };
1551
1552 auto fallback = [] (long id)
1553 {
1554 BITPIT_UNUSED(id);
1555
1557 };
1558
1560 double value = evalCellFieldCached<double>(field, id, evaluator, fallback);
1561
1562 // Evaluate the value with the correct signdness
1563 if (signedLevelSet) {
1564 value *= evalCellSign(id);
1565 }
1566
1567 return value;
1568}
1569
1576std::array<double,3> LevelSetObject::evalCellGradient(long id, bool signedLevelSet) const {
1577 // Evaluate signed gradient
1578 //
1579 // The gradient stored in the cache is unsigned.
1580 auto evaluator = [this] (long id)
1581 {
1582 return _evalCellGradient(id, false);
1583 };
1584
1585 auto fallback = [] (long id)
1586 {
1587 BITPIT_UNUSED(id);
1588
1590 };
1591
1593 std::array<double, 3> gradient = evalCellFieldCached<std::array<double, 3>>(field, id, evaluator, fallback);
1594
1595 // Evaluate the gradient with the correct signdness
1596 if (signedLevelSet) {
1597 short cellSign = evalCellSign(id);
1598 if (cellSign <= 0) {
1599 gradient *= static_cast<double>(cellSign);
1600 }
1601 }
1602
1603 return gradient;
1604}
1605
1612std::array<double,3> LevelSetObject::evalCellProjectionPoint(long id) const {
1613 return m_kernel->computeCellCentroid(id) - evalCellValue(id, true) * evalCellGradient(id, true);
1614}
1615
1621short LevelSetObject::evalSign(const std::array<double,3> &point) const {
1622 return _evalSign(point);
1623}
1624
1631double LevelSetObject::evalValue(const std::array<double,3> &point, bool signedLevelSet) const {
1632 return _evalValue(point, signedLevelSet);
1633}
1634
1641std::array<double,3> LevelSetObject::evalGradient(const std::array<double,3> &point, bool signedLevelSet) const {
1642 return _evalGradient(point, signedLevelSet);
1643}
1644
1651short LevelSetObject::_evalSign(const std::array<double,3> &point) const {
1652 return evalValueSign(this->evalValue(point, true));
1653}
1654
1660std::array<double,3> LevelSetObject::evalProjectionPoint(const std::array<double,3> &point) const{
1661 return point - evalValue(point, true) * evalGradient(point, true);
1662}
1663
1669short LevelSetObject::evalValueSign(double value) const{
1670 return static_cast<short>(sign(value));
1671}
1672
1677void LevelSetObject::dump( std::ostream &stream ){
1678 // Identifier
1679 utils::binary::write(stream, m_id) ;
1680 utils::binary::write(stream, m_nReferences) ;
1681
1682 // Object properties
1683 utils::binary::write(stream, m_defaultSignedLevelSet) ;
1685 utils::binary::write(stream, m_cellBulkEvaluationMode) ;
1686
1687 // Restore cell caches
1690
1691 std::size_t nCaches = m_cellCacheCollection->size();
1692 utils::binary::write(stream, nCaches);
1693 for (std::size_t cacheId = 0; cacheId < nCaches; ++cacheId) {
1694 CellCacheCollection::Item &cacheItem = (*m_cellCacheCollection)[cacheId];
1695
1696 // Skip items without a factory
1697 bool hasFactory = cacheItem.hasFactory();
1698 utils::binary::write(stream, hasFactory);
1699 if (!hasFactory) {
1700 continue;
1701 }
1702
1703 // Field information
1704 LevelSetField field = LevelSetField::UNDEFINED;
1705 for (std::size_t fieldIndex = 0; fieldIndex < static_cast<std::size_t>(LevelSetField::COUNT); ++fieldIndex) {
1706 field = static_cast<LevelSetField>(fieldIndex);
1707 if (cacheId == getFieldCellCacheId(field)) {
1708 break;
1709 } else {
1710 field = LevelSetField::UNDEFINED;
1711 }
1712 }
1713
1714 utils::binary::write(stream, field);
1715 if (field != LevelSetField::UNDEFINED) {
1717 }
1718
1719 // Dump the cache
1720 bool hasCache = cacheItem.hasCache();
1721 utils::binary::write(stream, hasCache);
1722 if (hasCache) {
1723 CellCacheCollection::Cache *cache = cacheItem.getCache();
1724 cache->dump(stream);
1725 }
1726 }
1727
1728 // Output fields
1729 std::size_t nEnabledOutputFields = m_enabledOutputFields.size() ;
1730 utils::binary::write(stream, nEnabledOutputFields) ;
1731 for (const auto &fieldEntry : m_enabledOutputFields) {
1732 utils::binary::write(stream, fieldEntry.first) ;
1733 utils::binary::write(stream, fieldEntry.second) ;
1734 }
1735}
1736
1741void LevelSetObject::restore( std::istream &stream ){
1742 // Disable output
1743 LevelSetFieldset enabledOutputFieldset;
1744 enabledOutputFieldset.reserve(m_enabledOutputFields.size());
1745 for ( const auto &fieldEntry : m_enabledOutputFields ) {
1746 enabledOutputFieldset.push_back(fieldEntry.first);
1747 }
1748
1749 enableVTKOutput(enabledOutputFieldset, false);
1750
1751 // Reset object information
1752 for (std::size_t fieldIndex = 0; fieldIndex < static_cast<std::size_t>(LevelSetField::COUNT); ++fieldIndex) {
1753 m_cellFieldCacheModes[fieldIndex] = LevelSetCacheMode::NONE;
1754 m_cellFieldCacheIds[fieldIndex] = CellCacheCollection::NULL_CACHE_ID;
1755 }
1756
1759
1760 m_cellCacheCollection->clear();
1761
1762 // Identifier
1763 utils::binary::read(stream, m_id) ;
1764 utils::binary::read(stream, m_nReferences) ;
1765
1766 // Object properties
1767 utils::binary::read(stream, m_defaultSignedLevelSet) ;
1769 utils::binary::read(stream, m_cellBulkEvaluationMode) ;
1770
1771 // Cell caches
1772 std::size_t expectedCellLocationCacheId;
1773 utils::binary::read(stream, expectedCellLocationCacheId);
1774
1775 std::size_t expectedCellPropagatedSignCacheId;
1776 utils::binary::read(stream, expectedCellPropagatedSignCacheId);
1777
1778 std::size_t nCaches;
1779 utils::binary::read(stream, nCaches);
1780 for (std::size_t cacheId = 0; cacheId < nCaches; ++cacheId) {
1781 // Skip items without a factory
1782 bool hasFactory;
1783 utils::binary::read(stream, hasFactory);
1784 if (!hasFactory) {
1785 continue;
1786 }
1787
1788 // Field information
1789 LevelSetField field;
1790 utils::binary::read(stream, field);
1791 if (field != LevelSetField::UNDEFINED) {
1792 std::size_t fieldIndex = static_cast<std::size_t>(field);
1793 utils::binary::read(stream, m_cellFieldCacheModes[fieldIndex]);
1794 }
1795
1796 // Restore the cache
1797 bool hasCache;
1798 utils::binary::read(stream, hasCache);
1799 if (hasCache) {
1800 // Create the cache
1801 if (cacheId == expectedCellLocationCacheId) {
1802 createCellLocationCache(cacheId);
1803 } else if (cacheId == expectedCellPropagatedSignCacheId) {
1805 } else if (field != LevelSetField::UNDEFINED) {
1806 createFieldCellCache(field, cacheId);
1807 } else {
1808 throw std::runtime_error("Unable to restore levelset object " + std::to_string(getId()) + "!");
1809 }
1810
1811 // Restore cache contents
1812 CellCacheCollection::Cache *cache = getCellCache(cacheId);
1813 cache->restore(stream);
1814 } else {
1815 }
1816 }
1817
1818 // Enable output
1819 std::size_t nEnabledVTKOutputs ;
1820 utils::binary::read(stream, nEnabledVTKOutputs) ;
1821 for (std::size_t i = 0; i < nEnabledVTKOutputs; ++i) {
1822 LevelSetField field ;
1823 std::string fieldName;
1824 utils::binary::read(stream, field) ;
1825 utils::binary::read(stream, fieldName) ;
1826
1827 enableVTKOutput(field, fieldName);
1828 }
1829}
1830
1836void LevelSetObject::enableVTKOutput( const LevelSetFieldset &fieldset, bool enable) {
1837
1838 std::stringstream objectNameStream;
1839 objectNameStream << getId();
1840
1841 enableVTKOutput(fieldset, objectNameStream.str(), enable);
1842
1843}
1844
1852void LevelSetObject::enableVTKOutput( const LevelSetFieldset &fieldset, const std::string &objectName, bool enable) {
1853
1854 for (LevelSetField field : fieldset) {
1855 enableVTKOutput(field, objectName, enable);
1856 }
1857
1858}
1859
1866
1867 std::stringstream objectNameStream;
1868 objectNameStream << getId();
1869
1870 enableVTKOutput(field, objectNameStream.str(), enable);
1871
1872}
1873
1881void LevelSetObject::enableVTKOutput( LevelSetField field, const std::string &objectName, bool enable) {
1882
1883 // Discard fields that are not supported
1884 LevelSetFieldset supportedFields = getSupportedFields();
1885 if (std::find(supportedFields.begin(), supportedFields.end(), field) == supportedFields.end()) {
1886 return;
1887 }
1888
1889 // Check if the state of the filed is already the requested one
1890 if (enable == hasVTKOutputData(field, objectName)) {
1891 return;
1892 }
1893
1894 // Process the field
1895 if (!enable) {
1896 removeVTKOutputData(field, objectName) ;
1897 m_enabledOutputFields.erase(field) ;
1898 } else {
1899 addVTKOutputData(field, objectName) ;
1900 m_enabledOutputFields.insert({field, getVTKOutputDataName(field, objectName)}) ;
1901 }
1902
1903}
1904
1911
1912 std::stringstream objectNameStream;
1913 objectNameStream << getId();
1914
1915 enableVTKOutput(field, objectNameStream.str(), enable);
1916
1917}
1918
1926void LevelSetObject::enableVTKOutput( LevelSetWriteField writeField, const std::string &objectName, bool enable) {
1927
1928 LevelSetFieldset fieldset;
1929 if( writeField==LevelSetWriteField::ALL){
1930 fieldset = getSupportedFields();
1931
1932 } else if ( writeField==LevelSetWriteField::DEFAULT){
1933 fieldset.push_back(LevelSetField::VALUE);
1934 fieldset.push_back(LevelSetField::GRADIENT);
1935
1936 } else {
1937 LevelSetField field = static_cast<LevelSetField>(writeField);
1938
1939 LevelSetFieldset supportedFields = getSupportedFields();
1940 if (std::find(supportedFields.begin(), supportedFields.end(), field) == supportedFields.end()) {
1941 log::warning() << "The specified field is not supported by the levelset object" << std::endl;
1942 return;
1943 }
1944
1945 fieldset.push_back(field);
1946 }
1947
1948 enableVTKOutput( fieldset, objectName, enable);
1949
1950}
1951
1960bool LevelSetObject::hasVTKOutputData( LevelSetField field, const std::string &objectName) const {
1961
1962 VTK &vtkWriter = m_kernel->getMesh()->getVTK() ;
1963 std::string name = getVTKOutputDataName(field, objectName);
1964
1965 return vtkWriter.hasData(name);
1966
1967}
1968
1975void LevelSetObject::removeVTKOutputData( LevelSetField field, const std::string &objectName) {
1976
1977 VTK &vtkWriter = m_kernel->getMesh()->getVTK() ;
1978 std::string name = getVTKOutputDataName(field, objectName);
1979
1980 vtkWriter.removeData(name);
1981
1982}
1983
1990void LevelSetObject::addVTKOutputData( LevelSetField field, const std::string &objectName) {
1991
1992 VTK &vtkWriter = m_kernel->getMesh()->getVTK() ;
1993 std::string name = getVTKOutputDataName(field, objectName);
1994
1995 switch(field){
1996
1998 vtkWriter.addData<double>( name, VTKFieldType::SCALAR, VTKLocation::CELL, this);
1999 break;
2000
2002 vtkWriter.addData<short>( name, VTKFieldType::SCALAR, VTKLocation::CELL, this);
2003 break;
2004
2006 vtkWriter.addData<double>( name, VTKFieldType::VECTOR, VTKLocation::CELL, this);
2007 break;
2008
2009 default:
2010 throw std::runtime_error("Unsupported value of field in LevelSetObject::addDataToVTK() ");
2011 break;
2012
2013 }
2014
2015}
2016
2024std::string LevelSetObject::getVTKOutputDataName( LevelSetField field, const std::string &objectName) const {
2025
2026 std::stringstream nameStream;
2027 nameStream << "levelset" << getVTKOutputFieldName(field) << "_" << objectName;
2028 std::string name = nameStream.str();
2029
2030 return name;
2031
2032}
2033
2041
2042 switch(field){
2043
2045 return "Value";
2046
2048 return "Sign";
2049
2051 return "Gradient";
2052
2053 default:
2054 throw std::runtime_error("Unsupported value of field in LevelSetObject::addDataToVTK() ");
2055 break;
2056
2057 }
2058
2059}
2060
2071void LevelSetObject::flushData( std::fstream &stream, const std::string &name, VTKFormat format){
2072
2073 for ( const auto &fieldEntry : m_enabledOutputFields ) {
2074 const std::string &fieldName = fieldEntry.second;
2075 if (utils::string::keywordInString(name, fieldName)) {
2076 LevelSetField field = fieldEntry.first;
2077 flushVTKOutputData(stream, format, field);
2078 }
2079 }
2080
2081}
2082
2095void LevelSetObject::flushVTKOutputData(std::fstream &stream, VTKFormat format, LevelSetField field) const {
2096
2097 switch(field) {
2098
2100 {
2101 auto evaluator = [this] (long id) { return evalCellValue(id, true); };
2102 auto fallback = [] (long id) { BITPIT_UNUSED(id); return levelSetDefaults::VALUE; };
2103 flushVTKOutputData<double>(stream, format, field, evaluator, fallback);
2104 break;
2105 }
2106
2108 {
2109 auto evaluator = [this] (long id) { return (short) evalCellSign(id); };
2110 auto fallback = [] (long id) { BITPIT_UNUSED(id); return levelSetDefaults::SIGN; };
2111 flushVTKOutputData<short>(stream, format, field, evaluator, fallback);
2112 break;
2113 }
2114
2116 {
2117 auto evaluator = [this] (long id) { return evalCellGradient(id, true); };
2118 auto fallback = [] (long id) { BITPIT_UNUSED(id); return levelSetDefaults::GRADIENT; };
2119 flushVTKOutputData<std::array<double, 3>>(stream, format, field, evaluator, fallback);
2120 break;
2121 }
2122
2123 default:
2124 {
2125 throw std::runtime_error("Unable to write the field.");
2126 }
2127
2128 }
2129}
2130
2131#if BITPIT_ENABLE_MPI
2139void LevelSetObject::startCellCacheExchange( const std::unordered_map<int, std::vector<long>> &sendCellIds,
2140 std::size_t cacheId, DataCommunicator *dataCommunicator) const
2141{
2142 startCellCachesExchange(sendCellIds, std::vector<std::size_t>(1, cacheId), dataCommunicator);
2143}
2144
2152void LevelSetObject::startCellCachesExchange( const std::unordered_map<int, std::vector<long>> &sendCellIds,
2153 const std::vector<std::size_t> &cacheIds,
2154 DataCommunicator *dataCommunicator) const {
2155
2156 // Fill the exchange buffer with the content of the cache
2157 dataCommunicator->clearAllSends(false);
2158 for (const auto &entry : sendCellIds) {
2159 // Create an empty send
2160 int rank = entry.first;
2161 dataCommunicator->setSend(rank, 0);
2162
2163 // Write data in the buffer
2164 const std::vector<long> &rankSendCellIds = entry.second;
2165 SendBuffer &buffer = dataCommunicator->getSendBuffer(rank);
2166 for (std::size_t cacheId : cacheIds) {
2167 getCellCache(cacheId)->writeBuffer(rankSendCellIds, buffer);
2168 }
2169 buffer.squeeze( ) ;
2170 }
2171
2172 // Discover the receives
2173 dataCommunicator->discoverRecvs();
2174
2175 // Start the sends
2176 dataCommunicator->startAllRecvs();
2177
2178 // Start the sends
2179 dataCommunicator->startAllSends();
2180
2181}
2182
2190void LevelSetObject::completeCellCacheExchange( const std::unordered_map<int, std::vector<long>> &recvCellIds,
2191 std::size_t cacheId, DataCommunicator *dataCommunicator)
2192{
2193 completeCellCachesExchange(recvCellIds, std::vector<std::size_t>(1, cacheId), dataCommunicator);
2194}
2195
2203void LevelSetObject::completeCellCachesExchange( const std::unordered_map<int, std::vector<long>> &recvCellIds,
2204 const std::vector<std::size_t> &cacheIds,
2205 DataCommunicator *dataCommunicator){
2206
2207 // Read cache data from the exchange buffer
2208 int nCompletedRecvs = 0;
2209 while (nCompletedRecvs < dataCommunicator->getRecvCount()) {
2210 int rank = dataCommunicator->waitAnyRecv();
2211
2212 const std::vector<long> &rankRecvCellIds = recvCellIds.at(rank);
2213 RecvBuffer &buffer = dataCommunicator->getRecvBuffer(rank);
2214 for (std::size_t cacheId : cacheIds) {
2215 getCellCache(cacheId)->readBuffer(rankRecvCellIds, buffer);
2216 }
2217
2218 ++nCompletedRecvs;
2219 }
2220
2221 // Wait completion of all sends
2222 dataCommunicator->waitAllSends();
2223
2224}
2225#endif
2226
2234{
2235 std::size_t fieldIndex = static_cast<std::size_t>(field);
2236
2237 return m_cellFieldCacheModes[fieldIndex];
2238}
2239
2252{
2253 // Early return if the cache mode doesn't need to be updated
2254 if (getFieldCellCacheMode(field) == cacheMode) {
2255 return;
2256 }
2257
2258 // Early return if the cache should be disabled
2259 if (cacheMode == LevelSetCacheMode::NONE) {
2260 disableFieldCellCache(field);
2261 return;
2262 }
2263
2264 // Destroy existing cache
2265 destroyFieldCellCache(field);
2266
2267 // Update field properties
2268 std::size_t fieldIndex = static_cast<std::size_t>(field);
2269 m_cellFieldCacheModes[fieldIndex] = cacheMode;
2270
2271 // Update data
2272 if (getKernel()) {
2273 // Create field cell caches
2274 createFieldCellCache(field);
2275
2276 // Fill field cell caches inside the narrow band
2277 fillFieldCellCaches(LevelSetZone::NARROW_BAND, std::vector<LevelSetField>(1, field));
2278
2279 // Fill field cell caches inside the bulk
2280 fillFieldCellCaches(LevelSetZone::BULK, std::vector<LevelSetField>(1, field));
2281 }
2282}
2283
2290{
2291 // Early return if no cache is associated with the field
2293 return;
2294 }
2295
2296 // Destroy the cache
2297 destroyFieldCellCache(field);
2298
2299 // Update field properties
2300 std::size_t fieldIndex = static_cast<std::size_t>(field);
2301 m_cellFieldCacheModes[fieldIndex] = LevelSetCacheMode::NONE;
2302}
2303
2312void LevelSetObject::adaptCellCaches( const std::vector<adaption::Info> &adaptionData )
2313{
2314#if BITPIT_ENABLE_MPI==1
2315 // Get mesh information
2316 const VolumeKernel &mesh = *(m_kernel->getMesh());
2317#endif
2318
2319 // Early return if all the caches are empty
2320 bool allCachesEmpty = true;
2321 for (std::size_t cacheId = 0; cacheId < m_cellCacheCollection->size(); ++cacheId) {
2322 CellCacheCollection::Item &cacheItem = (*m_cellCacheCollection)[cacheId];
2323 if (cacheItem.hasCache()) {
2324 allCachesEmpty = false;
2325 break;
2326 }
2327 }
2328
2329#if BITPIT_ENABLE_MPI==1
2330 if (mesh.isPartitioned()) {
2331 MPI_Allreduce(MPI_IN_PLACE, &allCachesEmpty, 1, MPI_C_BOOL, MPI_LAND, m_kernel->getCommunicator()) ;
2332 }
2333#endif
2334
2335 if (allCachesEmpty) {
2336 return;
2337 }
2338
2339#if BITPIT_ENABLE_MPI
2340 // Identify the ids of the non-volatile caches
2341 std::vector<std::size_t> nonVolatileCellCacheIds;
2342
2344 if (locationCache && !locationCache->isVolatile()) {
2345 nonVolatileCellCacheIds.push_back(m_cellLocationCacheId);
2346 }
2347
2348 for (std::size_t fieldIndex = 0; fieldIndex < static_cast<std::size_t>(LevelSetField::COUNT); ++fieldIndex) {
2349 LevelSetField field = static_cast<LevelSetField>(fieldIndex);
2350 CellCacheCollection::Cache *fieldCache = getFieldCellCache(field);
2351 if (fieldCache && !fieldCache->isVolatile()) {
2352 std::size_t fieldCacheId = getFieldCellCacheId(field);
2353 nonVolatileCellCacheIds.push_back(fieldCacheId);
2354 }
2355 }
2356
2357 // Initialize data exchange
2358 //
2359 // Data exchange can be performed only for non-volatile caches.
2360 std::unordered_map<int, std::vector<long>> exchangeSendList ;
2361 std::unordered_map<int, std::vector<long>> exchangeRecvList ;
2362 if (!nonVolatileCellCacheIds.empty()) {
2363 if (m_kernel->getMesh()->isPartitioned()) {
2364 for( const adaption::Info &adaptionInfo : adaptionData){
2365 if( adaptionInfo.entity != adaption::Entity::ENTITY_CELL){
2366 continue;
2367 }
2368
2369 switch (adaptionInfo.type) {
2370
2371 case adaption::Type::TYPE_PARTITION_SEND:
2372 exchangeSendList.insert({{adaptionInfo.rank,adaptionInfo.previous}}) ;
2373 break;
2374
2375 case adaption::Type::TYPE_PARTITION_RECV:
2376 exchangeRecvList.insert({{adaptionInfo.rank,adaptionInfo.current}}) ;
2377 break;
2378
2379 default:
2380 break;
2381
2382 }
2383 }
2384 }
2385 }
2386
2387 bool exchangeCacheData = (!exchangeSendList.empty() || !exchangeRecvList.empty());
2388 if (m_kernel->getMesh()->isPartitioned()) {
2389 MPI_Allreduce(MPI_IN_PLACE, &exchangeCacheData, 1, MPI_C_BOOL, MPI_LOR, m_kernel->getCommunicator()) ;
2390 }
2391
2392 // Create data communicator
2393 std::unique_ptr<DataCommunicator> dataCommunicator;
2394 if (exchangeCacheData) {
2395 dataCommunicator = m_kernel->createDataCommunicator() ;
2396 }
2397#endif
2398
2399#if BITPIT_ENABLE_MPI
2400 // Start data exchange
2401 if (exchangeCacheData) {
2402 startCellCachesExchange(exchangeSendList, nonVolatileCellCacheIds, dataCommunicator.get());
2403 }
2404#endif
2405
2406 // Remove stale entries from the caches
2407 std::vector<long> staleCellIds = evalCellCacheStaleIds(adaptionData);
2408
2409 for (std::size_t cacheId = 0; cacheId < m_cellCacheCollection->size(); ++cacheId) {
2410 CellCacheCollection::Item &cacheItem = (*m_cellCacheCollection)[cacheId];
2411 if (!cacheItem.hasCache()) {
2412 continue;
2413 }
2414
2415 pruneCellCache(cacheId, staleCellIds);
2416 }
2417
2418#if BITPIT_ENABLE_MPI
2419 // Complete data exchange
2420 if (exchangeCacheData) {
2421 completeCellCachesExchange(exchangeRecvList, nonVolatileCellCacheIds, dataCommunicator.get());
2422 }
2423#endif
2424}
2425
2433void LevelSetObject::clearCellCache( std::size_t cacheId, bool release )
2434{
2435 CellCacheCollection::Item &cacheItem = (*m_cellCacheCollection)[cacheId];
2436
2437 if (release) {
2438 cacheItem.destroyCache();
2439 } else if (cacheItem.hasCache()) {
2440 CellCacheCollection::Cache *cache = cacheItem.getCache();
2441 cache->clear();
2442 }
2443}
2444
2451void LevelSetObject::pruneCellCache(std::size_t cacheId, const std::vector<long> &cellIds)
2452{
2453 // Get cache
2454 CellCacheCollection::Item &cacheItem = (*m_cellCacheCollection)[cacheId];
2455 if (!cacheItem.hasCache()) {
2456 return;
2457 }
2458
2459 CellCacheCollection::Cache *cache = cacheItem.getCache();
2460
2461 // Remove entries
2462 cache->erase(cellIds);
2463
2464 // Reclaim unused memory
2466 cache->shrink_to_fit();
2467 }
2468}
2469
2478{
2479 switch (cacheMode) {
2480
2482 return evalCellOnDemandCacheFillIds(zone);
2483
2485 return evalCellNarrowBandCacheFillIds(zone);
2486
2488 return evalCellFullCacheFillIds(zone);
2489
2490 default:
2491 throw std::runtime_error("Unsupported cache mode!");
2492
2493 }
2494}
2495
2507 const std::vector<adaption::Info> &adaptionData) const
2508{
2509 switch (cacheMode) {
2510
2512 return evalCellOnDemandCacheFillIds(zone, adaptionData);
2513
2515 return evalCellNarrowBandCacheFillIds(zone, adaptionData);
2516
2518 return evalCellFullCacheFillIds(zone, adaptionData);
2519
2520 default:
2521 throw std::runtime_error("Unsupported cache mode!");
2522
2523 }
2524}
2525
2535{
2536 BITPIT_UNUSED(zone);
2537
2538 return std::vector<long>();
2539}
2540
2552std::vector<long> LevelSetObject::evalCellOnDemandCacheFillIds(LevelSetZone zone, const std::vector<adaption::Info> &adaptionData) const
2553{
2554 BITPIT_UNUSED(zone);
2555 BITPIT_UNUSED(adaptionData);
2556
2557 return std::vector<long>();
2558}
2559
2569{
2570 // Early return if the object is empty
2571 if (empty()) {
2572 return std::vector<long>();
2573 }
2574
2575 // There are no narrow band cells in the bulk
2576 if (zone == LevelSetZone::BULK) {
2577 return std::vector<long>();
2578 }
2579
2580 // Get mesh information
2581 const VolumeKernel &mesh = *(m_kernel->getMesh()) ;
2582
2583 // Detect the cells to be filled
2584 //
2585 // If no narrow band size was set, all cells should be filled, otherwise only
2586 // the cells inside the narrow band should be filled.
2587 if (m_narrowBandSize == LEVELSET_NARROW_BAND_UNLIMITED) {
2588 if (const VolCartesian *cartesianMesh = dynamic_cast<const VolCartesian *>(&mesh)) {
2589 long nCells = cartesianMesh->getCellCount();
2590 std::vector<long> cellFillIds(nCells);
2591 std::iota(cellFillIds.begin(), cellFillIds.end(), 0);
2592
2593 return cellFillIds;
2594 } else {
2595 return mesh.getCells().getIds(false);
2596 }
2597 } else {
2598 std::vector<long> cellFillIds;
2599 if (const VolCartesian *cartesianMesh = dynamic_cast<const VolCartesian *>(&mesh)) {
2600 long nCells = cartesianMesh->getCellCount();
2601 for (long cellId = 0; cellId < nCells; ++cellId) {
2602 if (isCellInNarrowBand(cellId)) {
2603 cellFillIds.push_back(cellId);
2604 }
2605 }
2606 } else {
2607 for (const Cell &cell : mesh.getCells()) {
2608 long cellId = cell.getId();
2609 if (isCellInNarrowBand(cellId)) {
2610 cellFillIds.push_back(cellId);
2611 }
2612 }
2613 }
2614
2615 return cellFillIds;
2616 }
2617}
2618
2630std::vector<long> LevelSetObject::evalCellNarrowBandCacheFillIds(LevelSetZone zone, const std::vector<adaption::Info> &adaptionData) const
2631{
2632 // Early return if the object is empty
2633 if (empty()) {
2634 return std::vector<long>();
2635 }
2636
2637 // There are no narrow band cells in the bulk
2638 if (zone == LevelSetZone::BULK) {
2639 return std::vector<long>();
2640 }
2641
2642 // Detect the cells to be filled
2643 //
2644 // If no narrow band size was set, all new cells should be filled, otherwise only
2645 // the cells inside the narrow band should be filled.
2646 std::vector<long> cellFillIds;
2647 for (const adaption::Info &adaptionInfo : adaptionData) {
2648 if (adaptionInfo.entity != adaption::Entity::ENTITY_CELL) {
2649 continue;
2650 }
2651
2652 if (m_narrowBandSize == LEVELSET_NARROW_BAND_UNLIMITED) {
2653 cellFillIds.insert(cellFillIds.end(), adaptionInfo.current.begin(), adaptionInfo.current.end());
2654 } else {
2655 for (long cellId : adaptionInfo.current) {
2656 if (isCellInNarrowBand(cellId)) {
2657 cellFillIds.push_back(cellId);
2658 }
2659 }
2660 }
2661 }
2662
2663 return cellFillIds;
2664}
2665
2675{
2676 // Early return if the object is empty
2677 if (empty()) {
2678 return std::vector<long>();
2679 }
2680
2681 // Detect the cells to be filled
2682 //
2683 // If the requested zone is the narrow band, it is possible to call the same function used
2684 // for the caches in narrow band mode.
2685 //
2686 // If the requested zone is the bulk, only the functions outside the narrow band should be
2687 // filled. This implies that, if the narrow band size was not not set, no cells should be
2688 // filled.
2689 if (zone == LevelSetZone::NARROW_BAND) {
2690 return evalCellNarrowBandCacheFillIds(zone);
2691 } else {
2692 if (m_narrowBandSize == LEVELSET_NARROW_BAND_UNLIMITED) {
2693 return std::vector<long>();
2694 } else {
2695 // Get mesh information
2696 const VolumeKernel &mesh = *(m_kernel->getMesh()) ;
2697
2698 // Identify cells outside the narrow band
2699 std::vector<long> cellFillIds;
2700 if (const VolCartesian *cartesianMesh = dynamic_cast<const VolCartesian *>(&mesh)) {
2701 long nCells = cartesianMesh->getCellCount();
2702 for (long cellId = 0; cellId < nCells; ++cellId) {
2703 if (!isCellInNarrowBand(cellId)) {
2704 cellFillIds.push_back(cellId);
2705 }
2706 }
2707 } else {
2708 for (const Cell &cell : mesh.getCells()) {
2709 long cellId = cell.getId();
2710 if (!isCellInNarrowBand(cellId)) {
2711 cellFillIds.push_back(cellId);
2712 }
2713 }
2714 }
2715
2716 return cellFillIds;
2717 }
2718 }
2719}
2720
2732std::vector<long> LevelSetObject::evalCellFullCacheFillIds(LevelSetZone zone, const std::vector<adaption::Info> &adaptionData) const
2733{
2734 // Early return if the object is empty
2735 if (empty()) {
2736 return std::vector<long>();
2737 }
2738
2739 // Detect the cells to be filled
2740 //
2741 // If the requested zone is the narrow band, it is possible to call the same function used
2742 // for the caches in narrow band mode.
2743 //
2744 // If the requested zone is the bulk, only the functions outside the narrow band should be
2745 // filled. This implies that, if the narrow band size was not not set, no cells should be
2746 // filled.
2747 if (zone == LevelSetZone::NARROW_BAND) {
2748 return evalCellNarrowBandCacheFillIds(zone);
2749 } else {
2750 if (m_narrowBandSize == LEVELSET_NARROW_BAND_UNLIMITED) {
2751 return std::vector<long>();
2752 } else {
2753 // Identify cells outside the narrow band
2754 std::vector<long> cellFillIds;
2755 for (const adaption::Info &adaptionInfo : adaptionData) {
2756 if (adaptionInfo.entity != adaption::Entity::ENTITY_CELL) {
2757 continue;
2758 }
2759
2760 for (long cellId : adaptionInfo.current) {
2761 if (!isCellInNarrowBand(cellId)) {
2762 cellFillIds.push_back(cellId);
2763 }
2764 }
2765 }
2766
2767 return cellFillIds;
2768 }
2769 }
2770}
2771
2778std::vector<long> LevelSetObject::evalCellCacheStaleIds(const std::vector<adaption::Info> &adaptionData) const
2779{
2780 // Identify cells whose entries should be pruned
2781 //
2782 // We need to prune entries for both cells that have been removed and newly added cells.
2783 // When a new cell is added to the cache, synchronized caches will automatically create
2784 // an entry for the new cell, however the information associated to that entry is not
2785 // initialized and may contain random data. We need to explicitly state that the newly
2786 // added cells are not in the cached yet.
2787 std::vector<long> cellPruneIds;
2788 for (const adaption::Info &adaptionInfo : adaptionData) {
2789 if (adaptionInfo.entity != adaption::Entity::ENTITY_CELL) {
2790 continue;
2791 }
2792
2793 cellPruneIds.insert(cellPruneIds.end(), adaptionInfo.previous.begin(), adaptionInfo.previous.end());
2794 cellPruneIds.insert(cellPruneIds.end(), adaptionInfo.current.begin(), adaptionInfo.current.end());
2795 }
2796
2797 return cellPruneIds;
2798}
2799
2809{
2810 // Early return if no cache is associated with the field
2812 return nullptr;
2813 }
2814
2815 // Get field cache
2816 std::size_t cacheId = getFieldCellCacheId(field);
2817
2818 return getCellCache(cacheId);
2819}
2820
2828{
2829 std::size_t fieldIndex = static_cast<std::size_t>(field);
2830
2831 return m_cellFieldCacheIds[fieldIndex];
2832}
2833
2842std::size_t LevelSetObject::createFieldCellCache(LevelSetField field, std::size_t cacheId)
2843{
2844 switch(field) {
2845
2847 return createFieldCellCache<double>(field, cacheId);
2848
2850 return createFieldCellCache<short>(field, cacheId);
2851
2853 return createFieldCellCache<std::array<double, 3>>(field, cacheId);
2854
2855 default:
2856 throw std::runtime_error("The requested field is not supported!");
2857
2858 }
2859}
2860
2867{
2868 // Update field properties
2869 std::size_t fieldIndex = static_cast<std::size_t>(field);;
2870 m_cellFieldCacheIds[fieldIndex] = CellCacheCollection::NULL_CACHE_ID;
2871
2872 // Destroy cache
2873 std::size_t cacheId = getFieldCellCacheId(field);
2874 destroyCellCache(cacheId);
2875}
2876
2883void LevelSetObject::fillFieldCellCaches(LevelSetZone zone, const std::vector<LevelSetField> &fields)
2884{
2885 // Early return if there are no caches to update
2886 if (fields.empty()) {
2887 return;
2888 }
2889
2890 // It's more efficient to process the fields grouped by their cache mode
2891 for (std::size_t cacheModeIndex = 0; cacheModeIndex < static_cast<std::size_t>(LevelSetCacheMode::COUNT); ++cacheModeIndex) {
2892 LevelSetCacheMode cacheMode = static_cast<LevelSetCacheMode>(cacheModeIndex);
2893
2894 // Get fields that operate in the processed cache mode
2895 std::vector<LevelSetField> cacheModeFields;
2896 for (LevelSetField field : fields) {
2897 if (getFieldCellCacheMode(field) != cacheMode) {
2898 continue;
2899 }
2900
2901 cacheModeFields.push_back(field);
2902 }
2903
2904 if (cacheModeFields.empty()) {
2905 continue;
2906 }
2907
2908 // Identify the cell ids that should be added to the cache
2909 std::vector<long> cellCacheFillIds = evalCellCacheFillIds(zone, cacheMode);
2910
2911 // Fill the caches
2912 for (LevelSetField field : cacheModeFields) {
2913 fillFieldCellCache(field, cellCacheFillIds);
2914 }
2915 }
2916}
2917
2925void LevelSetObject::fillFieldCellCaches(LevelSetZone zone, const std::vector<LevelSetField> &fields,
2926 const std::vector<adaption::Info> &adaptionData)
2927{
2928 // Early return if there are no caches to update
2929 if (fields.empty()) {
2930 return;
2931 }
2932
2933#if BITPIT_ENABLE_MPI==1
2934 // Get mesh information
2935 const VolumeKernel &mesh = *(m_kernel->getMesh());
2936
2937 // Check if there are non-volatile caches to process
2938 bool areNonVolatileCacheProcessed = false;
2939 for (LevelSetField field : fields) {
2940 CellCacheCollection::Cache *fieldCache = getFieldCellCache(field);
2941 if (fieldCache && !fieldCache->isVolatile()) {
2942 areNonVolatileCacheProcessed = true;
2943 break;
2944 }
2945 }
2946
2947 // Identify the cells that have been received.
2948 std::unordered_set<long> recievedCellIds;
2949 if (areNonVolatileCacheProcessed) {
2950 if (m_kernel->getMesh()->isPartitioned()) {
2951 for( const adaption::Info &adaptionInfo : adaptionData){
2952 if( adaptionInfo.entity != adaption::Entity::ENTITY_CELL){
2953 continue;
2954 }
2955
2956 switch (adaptionInfo.type) {
2957
2958 case adaption::Type::TYPE_PARTITION_RECV:
2959 recievedCellIds.insert(adaptionInfo.current.begin(), adaptionInfo.current.end());
2960 break;
2961
2962 default:
2963 break;
2964
2965 }
2966 }
2967 }
2968 }
2969#endif
2970
2971 // It's more efficient to process the fields grouped by their cache mode
2972 for (std::size_t cacheModeIndex = 0; cacheModeIndex < static_cast<std::size_t>(LevelSetCacheMode::COUNT); ++cacheModeIndex) {
2973 LevelSetCacheMode cacheMode = static_cast<LevelSetCacheMode>(cacheModeIndex);
2974
2975 // Get fields with caches that operate in the processed mode
2976 std::vector<LevelSetField> cacheModeFields;
2977 for (LevelSetField field : fields) {
2978 if (getFieldCellCacheMode(field) != cacheMode) {
2979 continue;
2980 }
2981
2982 cacheModeFields.push_back(field);
2983 }
2984
2985 if (cacheModeFields.empty()) {
2986 continue;
2987 }
2988
2989 // Identify the cell ids that should be filled for volatile caches
2990 std::vector<long> cellVolatileCacheFillIds = evalCellCacheFillIds(zone, cacheMode, adaptionData);
2991
2992 bool emptyCellVolatileCacheFillIds = cellVolatileCacheFillIds.empty();
2993#if BITPIT_ENABLE_MPI==1
2994 if (mesh.isPartitioned()) {
2995 MPI_Allreduce(MPI_IN_PLACE, &emptyCellVolatileCacheFillIds, 1, MPI_C_BOOL, MPI_LAND, m_kernel->getCommunicator()) ;
2996 }
2997#endif
2998
2999 if (emptyCellVolatileCacheFillIds) {
3000 continue;
3001 }
3002
3003 // Identify the cell ids that should be filled for non-volatile caches
3004 //
3005 // For non-volatile caches, entries associated with cell received from other processes
3006 // are exchanged when the caches are adapted and there is no need to evaluate them.
3007 std::vector<long> cellNonVolatileCacheFillIds;
3008#if BITPIT_ENABLE_MPI
3009 if (areNonVolatileCacheProcessed && !recievedCellIds.empty()) {
3010 cellNonVolatileCacheFillIds.reserve(cellVolatileCacheFillIds.size());
3011 for (long cellId : cellVolatileCacheFillIds) {
3012 if (recievedCellIds.count(cellId)) {
3013 continue;
3014 }
3015
3016 cellNonVolatileCacheFillIds.push_back(cellId);
3017 }
3018 } else {
3019 cellNonVolatileCacheFillIds = cellVolatileCacheFillIds;
3020 }
3021#else
3022 cellNonVolatileCacheFillIds = cellVolatileCacheFillIds;
3023#endif
3024
3025 // Fill field caches
3026 for (LevelSetField field : cacheModeFields) {
3028 if (cache->isVolatile()) {
3029 fillFieldCellCache(field, cellVolatileCacheFillIds);
3030 } else {
3031 fillFieldCellCache(field, cellNonVolatileCacheFillIds);
3032 }
3033 }
3034 }
3035}
3036
3043void LevelSetObject::fillFieldCellCache(LevelSetField field, const std::vector<long> &cellIds)
3044{
3045 // Early return if no cache is associated with the field
3047 return;
3048 }
3049
3050#if BITPIT_ENABLE_MPI==1
3051 // Get mesh information
3052 const VolumeKernel &mesh = *(m_kernel->getMesh());
3053#endif
3054
3055 // Early return if there are no cells to fill
3056 //
3057 // Even if there are no cells to fill, the cache will now be considered non-empty, in this
3058 // way it will be processed by the functions that update the caches.
3059 bool emptyCellIds = cellIds.empty();
3060#if BITPIT_ENABLE_MPI==1
3061 if (mesh.isPartitioned()) {
3062 MPI_Allreduce(MPI_IN_PLACE, &emptyCellIds, 1, MPI_C_BOOL, MPI_LAND, m_kernel->getCommunicator()) ;
3063 }
3064#endif
3065
3066 if (emptyCellIds) {
3067 return;
3068 }
3069
3070 // Get list of ghost exchange sources
3071 std::unordered_set<long> ghostExchangeSources;
3072 std::unordered_set<long> ghostExchangeTargets;
3073#if BITPIT_ENABLE_MPI
3074 for (const auto &entry : mesh.getGhostCellExchangeSources()) {
3075 const std::vector<long> &rankSourceList = entry.second;
3076 ghostExchangeSources.insert(rankSourceList.begin(), rankSourceList.end());
3077 }
3078
3079 for (const auto &entry : mesh.getGhostCellExchangeTargets()) {
3080 const std::vector<long> &rankTargetList = entry.second;
3081 ghostExchangeTargets.insert(rankTargetList.begin(), rankTargetList.end());
3082 }
3083#endif
3084
3085#if BITPIT_ENABLE_MPI
3086 // Get field cache
3087 std::size_t cacheId = getFieldCellCacheId(field);
3088
3089 // Create data communicator
3090 std::unique_ptr<DataCommunicator> dataCommunicator;
3091 if (mesh.isPartitioned()) {
3092 dataCommunicator = m_kernel->createDataCommunicator();
3093 }
3094
3095 // Fill cache values of cells that are sources for ghost exchange
3096 for (long cellId : cellIds) {
3097 if (ghostExchangeSources.count(cellId) == 0) {
3098 continue;
3099 }
3100
3101 fillFieldCellCache(field, cellId);
3102 }
3103
3104 // Start ghost exchange
3105 if (dataCommunicator) {
3106 startCellCacheExchange(mesh.getGhostCellExchangeSources(), cacheId, dataCommunicator.get());
3107 }
3108#endif
3109
3110 // Fill cache values of internal cells that are not sources for ghost exchange
3111 //
3112 // Values on ghost cells are not evaluated, because those values will be communicated
3113 // by the ghost data exchange.
3114 for (long cellId : cellIds) {
3115 if (ghostExchangeSources.count(cellId) > 0) {
3116 continue;
3117 } else if (ghostExchangeTargets.count(cellId) > 0) {
3118 continue;
3119 }
3120
3121 fillFieldCellCache(field, cellId);
3122 }
3123
3124#if BITPIT_ENABLE_MPI
3125 // Complete ghost exchange
3126 if (dataCommunicator) {
3127 completeCellCacheExchange(mesh.getGhostCellExchangeTargets(), cacheId, dataCommunicator.get());
3128 }
3129#endif
3130}
3131
3139{
3140 // Early return if no cache is associated with the field
3142 return;
3143 }
3144
3145 // Fill cache
3146 switch (field) {
3147
3149 evalCellSign(id);
3150 break;
3151
3153 evalCellValue(id, true);
3154 break;
3155
3157 evalCellGradient(id, true);
3158 break;
3159
3160 default:
3161 throw std::runtime_error("Unsupported field!");
3162
3163 }
3164}
3165
3176{
3177 if (cacheId == CellCacheCollection::NULL_CACHE_ID) {
3178 return nullptr;
3179 }
3180
3181 return (*m_cellCacheCollection)[cacheId].getCache();
3182}
3183
3189void LevelSetObject::destroyCellCache(std::size_t cacheId)
3190{
3191 m_cellCacheCollection->erase(cacheId);
3192}
3193
3200std::array<double,3> LevelSetObject::computeProjectionPoint(long cellId) const {
3201 return evalCellProjectionPoint(cellId);
3202}
3203
3210std::array<double,3> LevelSetObject::computeVertexProjectionPoint(long vertexId) const {
3211 const std::array<double,3> &vertexCoords = m_kernel->getMesh()->getVertexCoords(vertexId);
3212 return evalProjectionPoint(vertexCoords);
3213}
3214
3220std::array<double,3> LevelSetObject::computeProjectionPoint(const std::array<double,3> &point) const{
3221 return evalProjectionPoint(point);
3222}
3223
3229short LevelSetObject::getSign(long cellId) const {
3230
3231 return evalCellSign(cellId);
3232
3233}
3234
3240double LevelSetObject::getValue(long cellId) const {
3241
3242 return evalCellValue(cellId, m_defaultSignedLevelSet);
3243
3244}
3245
3251std::array<double,3> LevelSetObject::getGradient(long cellId) const {
3252
3253 return evalCellGradient(cellId, m_defaultSignedLevelSet);
3254
3255}
3256
3263
3264 return LevelSetInfo(evalCellValue(cellId, m_defaultSignedLevelSet), evalCellGradient(cellId, m_defaultSignedLevelSet));
3265
3266}
3267
3273double LevelSetObject::getLS(long cellId) const {
3274
3275 return evalCellValue(cellId, m_defaultSignedLevelSet);
3276
3277}
3278
3285 return getNarrowBandSize();
3286}
3287
3296
3297}
The Cell class defines the cells.
Definition cell.hpp:42
bool isInterior() const
Definition cell.cpp:396
The DataCommunicator class provides the infrastructure needed to exchange data among processes.
void clearAllSends(bool synchronous=false)
SendBuffer & getSendBuffer(int rank)
int waitAnyRecv(const std::vector< int > &blackList=std::vector< int >())
void setSend(int rank, long length=0)
RecvBuffer & getRecvBuffer(int rank)
The class ElementCacheCollection allows to store a collection of caches for the elements of a mesh.
The class LevelSetCache is the base class for defining caches.
std::size_t erase(const Keys &keys)
Mesh specific implementation to calculate the levelset function.
virtual VolumeKernel * getMesh() const
LevelSetFillIn getExpectedFillIn() const
double getDistanceTolerance() const
virtual bool intersectCellPlane(long, const std::array< double, 3 > &, const std::array< double, 3 > &, double)
MPI_Comm getCommunicator() const
std::unique_ptr< DataCommunicator > createDataCommunicator() const
Interface class for all objects with respect to whom the levelset function may be computed.
double getLS(long cellId) const
std::vector< long > evalCellCacheStaleIds(const std::vector< adaption::Info > &adaptionData) const
void clearCellCache(std::size_t cacheId, bool release)
void flushData(std::fstream &, const std::string &, VTKFormat) override
LevelSetInfo getLevelSetInfo(long cellId) const
virtual void destroyFieldCellCache(LevelSetField field)
CellCacheCollection::ValueCache< value_t > * getCellCache(std::size_t cacheId) const
static const LevelSetIntersectionMode CELL_LOCATION_INTERSECTION_MODE
void destroyCellCache(std::size_t cacheId)
LevelSetBulkEvaluationMode getCellBulkEvaluationMode() const
void pruneCellCache(std::size_t cacheId, const std::vector< long > &cellIds)
void updateCellNarrowBandData(const std::vector< adaption::Info > &adaptionData)
void completeCellCachesExchange(const std::unordered_map< int, std::vector< long > > &sendCellIds, const std::vector< std::size_t > &cacheIds, DataCommunicator *)
void startCellCachesExchange(const std::unordered_map< int, std::vector< long > > &recvCellIds, const std::vector< std::size_t > &cacheIds, DataCommunicator *) const
std::vector< long > evalCellCacheFillIds(LevelSetZone zone, LevelSetCacheMode cacheMode) const
void fillFieldCellCache(LevelSetField field, const std::vector< long > &cellIds)
void setDefaultLevelSetSigndness(bool signedLevelSet)
short evalValueSign(double value) const
virtual double evalValue(const std::array< double, 3 > &point, bool signedLevelSet) const
LevelSetKernel * m_kernel
virtual std::array< double, 3 > evalCellProjectionPoint(long id) const
void removeVTKOutputData(LevelSetField field, const std::string &objectName)
void completeCellCacheExchange(const std::unordered_map< int, std::vector< long > > &sendCellIds, std::size_t cacheIds, DataCommunicator *)
void enableFieldCellCache(LevelSetField field, LevelSetCacheMode cacheMode)
std::array< double BITPIT_COMMA 3 > computeVertexProjectionPoint(long vertexId) const
virtual const LevelSetKernel * getKernel() const
std::size_t getFieldCellCacheId(LevelSetField field) const
CellCacheCollection::ValueCache< value_t > * getFieldCellCache(LevelSetField field) const
virtual std::size_t createCellLocationCache(std::size_t cacheId=CellCacheCollection::NULL_CACHE_ID)
void updateCellBulkData(const std::vector< adaption::Info > &adaptionData)
void setCellBulkEvaluationMode(LevelSetBulkEvaluationMode evaluationMode)
virtual void addVTKOutputData(LevelSetField field, const std::string &objectName)
static const bool CELL_CACHE_IS_SIGNED
double m_narrowBandSize
Size of narrow band.
virtual std::string getVTKOutputFieldName(LevelSetField field) const
LevelSetIntersectionStatus intersectSurface(long, LevelSetIntersectionMode=LevelSetIntersectionMode::FAST_FUZZY) const
virtual bool isPrimary() const
virtual std::size_t createCellPropagatedSignCache(std::size_t cacheId=CellCacheCollection::NULL_CACHE_ID)
virtual void fillCellPropagatedSignCache()
virtual short evalSign(const std::array< double, 3 > &point) const
virtual std::array< double, 3 > evalProjectionPoint(const std::array< double, 3 > &point) const
LevelSetCacheMode getFieldCellCacheMode(LevelSetField field) const
virtual short _evalSign(const std::array< double, 3 > &point) const
std::vector< long > evalCellNarrowBandCacheFillIds(LevelSetZone zone) const
virtual LevelSetIntersectionStatus _intersectSurface(long, double distance, LevelSetIntersectionMode=LevelSetIntersectionMode::FAST_FUZZY) const
virtual std::array< double, 3 > evalCellGradient(long id, bool signedLevelSet) const
void disableFieldCellCache(LevelSetField field)
virtual double evalCellValue(long id, bool signedLevelSet) const
virtual std::array< double, 3 > evalGradient(const std::array< double, 3 > &point, bool signedLevelSet) const
virtual LevelSetFieldset getSupportedFields() const
virtual void dump(std::ostream &)
bool hasVTKOutputData(LevelSetField field, const std::string &objectName) const
virtual void fillCellLocationCache()
virtual short evalCellSign(long id) const
LevelSetCellLocation getCellLocation(long id) const
LevelSetZone getCellZone(long id) const
void adaptCellCaches(const std::vector< adaption::Info > &adaptionData)
virtual bool isCellInNarrowBand(long id) const
std::vector< long > evalCellOnDemandCacheFillIds(LevelSetZone zone) const
void setNarrowBandSize(double size)
std::size_t m_cellLocationCacheId
Id of the cache that will keep track if cell zones.
double getNarrowBandSize() const
double getSizeNarrowBand() const
std::array< double BITPIT_COMMA 3 > computeProjectionPoint(long cellId) const
virtual LevelSetCellLocation fillCellGeometricNarrowBandLocationCache(long id)
void enableVTKOutput(const LevelSetFieldset &fieldset, bool enable=true)
std::size_t m_cellPropagatedSignCacheId
Id of the cache that will keep track if cell propagated sign.
void fillFieldCellCaches(LevelSetZone zone, const std::vector< LevelSetField > &fields)
virtual void flushVTKOutputData(std::fstream &stream, VTKFormat format, LevelSetField field) const
virtual void setKernel(LevelSetKernel *)
virtual void restore(std::istream &)
std::size_t getReferenceCount() const
std::vector< long > evalCellFullCacheFillIds(LevelSetZone zone) const
virtual std::size_t createFieldCellCache(LevelSetField field, std::size_t cacheId=CellCacheCollection::NULL_CACHE_ID)
std::string getVTKOutputDataName(LevelSetField field, const std::string &objectName) const
virtual bool isInNarrowBand(const std::array< double, 3 > &point) const
void startCellCacheExchange(const std::unordered_map< int, std::vector< long > > &recvCellIds, std::size_t cacheIds, DataCommunicator *) const
The class LevelSetCache is the base class for defining caches that store values.
CellConstIterator internalCellConstEnd() const
const std::unordered_map< int, std::vector< long > > & getGhostCellExchangeSources() const
PiercedVector< Cell > & getCells()
CellConstIterator internalCellConstBegin() const
const std::unordered_map< int, std::vector< long > > & getGhostCellExchangeTargets() const
bool empty(bool global=true) const
VTKUnstructuredGrid & getVTK()
const MPI_Comm & getCommunicator() const
void processCellFaceNeighbours(long seedId, int nLayers, Function function) const
virtual long getCellCount() const
const std::array< double, 3 > & getVertexCoords(long id) const
CellConstIterator getCellConstIterator(long id) const
id_t getId(const id_t &fallback=-1) const noexcept
Iterator for the class PiercedStorage.
Metafunction for generating a pierced vector.
Buffer to be used for receive communications.
Buffer to be used for send communications.
A base class for VTK input output.
Definition VTK.hpp:298
VTKField & addData(VTKField &&field)
Definition VTK.cpp:281
void removeData(const std::string &)
Definition VTK.cpp:330
bool hasData(const std::string &) const
Definition VTK.cpp:352
The VolCartesian defines a Cartesian patch.
The VolumeKernel class provides an interface for defining volume patches.
T sign(const T &)
Definition Operators.tpp:38
VTKFormat
Definition VTK.hpp:92
void write(std::ostream &stream, const std::vector< bool > &container)
void read(std::istream &stream, std::vector< bool > &container)
#define BITPIT_UNREACHABLE(str)
Definition compiler.hpp:53
#define BITPIT_UNUSED(variable)
Definition compiler.hpp:63
bool keywordInString(const std::string &line, const std::string &key)
@ NARROW_BAND
Narrow band zone.
@ NARROW_BAND
Data are cached only inside the narrow band.
@ NONE
No caching will be performed.
@ FULL
Data are cached in the whole domain.
@ ON_DEMAND
Data are cached only where explicitly evaluated.
@ UNKNOWN
Unknown location.
@ NARROW_BAND_INTERSECTED
Narrow band zone, the cell intersects the surface.
@ SIGN_PROPAGATION
Sign is propagated from the narrow band, no other data will be evaluated.
@ EXACT
Exact data is evaluated.
const std::array< double, 3 > GRADIENT
Logger & error(log::Visibility defaultVisibility)
Definition logger.cpp:1777
Logger & warning(log::Visibility defaultVisibility)
Definition logger.cpp:1812
A public container which includes all information provided by LevelSet.
The Info struct defines the information associated to an adaption.
Definition adaption.hpp:63
--- layout: doxygen_footer ---