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
61
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),
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)),
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);
145 }
146}
147
148/*!
149 * Destructor.
150 */
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 ;
224
225}
226
228 * Set whether a signed or unsigned levelset is used when signdness is not explicitly specified.
229 * This function is only needed for guarantee backwards compatibility with older versions. In
230 * such versions, functions for evaluating levelset information were not taking in input the
231 * signdness.
232 * @param signedLevelSet controls if signed levelset function will be used
233 */
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
252 PiercedVector<Cell, long> *cacheKernel = &(m_kernel->getMesh()->getCells());
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
496 if (m_cellLocationCacheId == CellCacheCollection::NULL_CACHE_ID) {
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();
563 VolumeKernel::CellConstIterator internalCellsBegin = mesh.internalCellConstBegin();
564 VolumeKernel::CellConstIterator internalCellsEnd = mesh.internalCellConstEnd();
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()) {
688 VolumeKernel::CellConstIterator cellItr = mesh.getCellConstIterator(cellId);
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
871
876{
878 m_cellLocationCacheId = CellCacheCollection::NULL_CACHE_ID;
879}
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()) {
1161 VolumeKernel::CellConstIterator neighItr = mesh.getCellConstIterator(neighId);
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
1298
1307
1345
1380{
1381 // Try evaluating intersection information from the cell location
1382 //
1383 // Regardless from the requested mode, a cell can intersect the zero-levelset iso-surface
1384 // only if it is inside the narrow band.
1385 //
1386 // If the requested mode is the same mode used for identify the cell location, we can get the
1387 // intersection information directly from the location.
1388 LevelSetCellLocation cellLocation = getCellLocation(id);
1389 if (cellLocation == LevelSetCellLocation::BULK) {
1391 } else if (mode == CELL_LOCATION_INTERSECTION_MODE) {
1394 } else if (cellLocation != LevelSetCellLocation::NARROW_BAND_UNDEFINED) {
1396 }
1397 }
1398
1399 // Check for intersection with zero-levelset iso-surface
1400 double distance = evalCellValue(id, false);
1401
1402 return _isCellIntersected(id, distance, mode);
1403}
1404
1425 std::array<std::array<double, 3>, 2> *intersection,
1426 std::vector<std::array<double, 3>> *polygon) const
1427{
1428 return _isInterfaceIntersected(id, invert, intersection, polygon);
1429}
1430
1440{
1441 std::array<std::array<double, 3>, 2> intersection;
1442 std::vector<std::array<double, 3>> polygon;
1443 return isInterfaceIntersected(id, false, &intersection, &polygon);
1444}
1445
1482{
1483 double distanceTolerance = m_kernel->getDistanceTolerance();
1484
1485 switch(mode){
1487 {
1488 double tangentSphere = m_kernel->computeCellTangentRadius(id) ;
1489 if(utils::DoubleFloatingLessEqual()(distance, tangentSphere, distanceTolerance, distanceTolerance)){
1491 } else {
1493 }
1494
1495 break;
1496 }
1497
1499 {
1500 double boundingSphere = m_kernel->computeCellBoundingRadius(id) ;
1501 if(utils::DoubleFloatingGreater()(distance, boundingSphere, distanceTolerance, distanceTolerance)){
1503 } else {
1505 }
1506
1507 break;
1508 }
1509
1511 {
1512 double boundingSphere = m_kernel->computeCellBoundingRadius(id) ;
1513 if(utils::DoubleFloatingGreater()(distance, boundingSphere, distanceTolerance, distanceTolerance)){
1515 }
1516
1517 double tangentSphere = m_kernel->computeCellTangentRadius(id) ;
1518 if(utils::DoubleFloatingLessEqual()(distance, tangentSphere, distanceTolerance, distanceTolerance)){
1520 }
1521
1523
1524 break;
1525 }
1526
1528 {
1529 double boundingSphere = m_kernel->computeCellBoundingRadius(id) ;
1530 if(utils::DoubleFloatingGreater()(distance, boundingSphere, distanceTolerance, distanceTolerance)){
1532 }
1533
1534 double tangentSphere = m_kernel->computeCellTangentRadius(id) ;
1535 if(utils::DoubleFloatingLessEqual()(distance, tangentSphere, distanceTolerance, distanceTolerance)){
1537 }
1538
1539 std::array<double,3> root = evalCellProjectionPoint(id);
1540 std::array<double,3> normal = evalCellGradient(id, true);
1541 if( m_kernel->intersectCellPlane(id,root,normal, distanceTolerance) ){
1543 } else {
1545 }
1546
1547 break;
1548 }
1549 }
1550
1551 BITPIT_UNREACHABLE("cannot reach");
1552
1553}
1554
1574 std::array<std::array<double, 3>, 2> *intersection,
1575 std::vector<std::array<double, 3>> *polygon) const
1576{
1577 const Interface &interface = m_kernel->getMesh()->getInterface(id);
1578 std::array<double,3> centroid = m_kernel->getMesh()->evalElementCentroid(interface);
1579
1580 std::array<double,3> root = evalProjectionPoint(centroid);
1581 std::array<double,3> normal = evalGradient(centroid, true);
1582 if (!invert) {
1583 normal *= -1.;
1584 }
1585
1586 if( m_kernel->getMesh()->intersectInterfacePlane(id, root, normal, intersection, polygon) ){
1588 } else {
1590 }
1591}
1592
1598short LevelSetObject::evalCellSign(long id) const {
1599
1600 // Define sign evaluators
1601 auto evaluator = [this] (long id) -> short
1602 {
1603 // Try fetching the sign from sign propagation
1604 CellCacheCollection::ValueCache<char> *propagatedSignCache = getCellCache<char>(m_cellPropagatedSignCacheId);
1605 if (propagatedSignCache) {
1606 CellCacheCollection::ValueCache<char>::Entry propagatedSignCacheEntry = propagatedSignCache->findEntry(id);
1607 if (propagatedSignCacheEntry.isValid()) {
1608 return static_cast<short>(*propagatedSignCacheEntry);
1609 }
1610 }
1611
1612 // Evaluate the sign
1613 return _evalCellSign(id);
1614 };
1615
1616 auto fallback = [this] (long id) -> short
1617 {
1618 // Try fetching the sign from sign propagation
1619 CellCacheCollection::ValueCache<char> *propagatedSignCache = getCellCache<char>(m_cellPropagatedSignCacheId);
1620 if (propagatedSignCache) {
1621
1622 CellCacheCollection::ValueCache<char>::Entry propagatedSignCacheEntry = propagatedSignCache->findEntry(id);
1623 if (propagatedSignCacheEntry.isValid()) {
1624 return static_cast<short>(*propagatedSignCacheEntry);
1625 }
1626 }
1627
1628 // Return a dummy value
1630 };
1631
1633 short sign = evalCellFieldCached<short>(field, id, evaluator, fallback);
1634
1635 return sign;
1636}
1637
1644double LevelSetObject::evalCellValue(long id, bool signedLevelSet) const {
1645
1646 // Evaluate signed value
1647 //
1648 // The value stored in the cache is unsigned.
1649 auto evaluator = [this] (long id) -> double
1650 {
1651 return _evalCellValue(id, false);
1652 };
1653
1654 auto fallback = [] (long id) -> double
1655 {
1656 BITPIT_UNUSED(id);
1657
1659 };
1660
1662 double value = evalCellFieldCached<double>(field, id, evaluator, fallback);
1663
1664 // Evaluate the value with the correct signdness
1665 if (signedLevelSet) {
1666 value *= evalCellSign(id);
1667 }
1668
1669 return value;
1670}
1671
1678std::array<double,3> LevelSetObject::evalCellGradient(long id, bool signedLevelSet) const {
1679 // Evaluate signed gradient
1680 //
1681 // The gradient stored in the cache is unsigned.
1682 auto evaluator = [this] (long id) -> std::array<double, 3>
1683 {
1684 return _evalCellGradient(id, false);
1685 };
1686
1687 auto fallback = [] (long id) -> const std::array<double, 3> &
1688 {
1689 BITPIT_UNUSED(id);
1690
1692 };
1693
1695 std::array<double, 3> gradient = evalCellFieldCached<std::array<double, 3>>(field, id, evaluator, fallback);
1696
1697 // Evaluate the gradient with the correct signdness
1698 if (signedLevelSet) {
1699 short cellSign = evalCellSign(id);
1700 if (cellSign <= 0) {
1701 gradient *= static_cast<double>(cellSign);
1702 }
1703 }
1704
1705 return gradient;
1706}
1707
1714std::array<double,3> LevelSetObject::evalCellProjectionPoint(long id) const {
1715 return m_kernel->computeCellCentroid(id) - evalCellValue(id, true) * evalCellGradient(id, true);
1716}
1717
1723short LevelSetObject::evalSign(const std::array<double,3> &point) const {
1724 return _evalSign(point);
1725}
1726
1733double LevelSetObject::evalValue(const std::array<double,3> &point, bool signedLevelSet) const {
1734 return _evalValue(point, signedLevelSet);
1735}
1736
1743std::array<double,3> LevelSetObject::evalGradient(const std::array<double,3> &point, bool signedLevelSet) const {
1744 return _evalGradient(point, signedLevelSet);
1745}
1746
1753short LevelSetObject::_evalSign(const std::array<double,3> &point) const {
1754 return evalValueSign(this->evalValue(point, true));
1755}
1756
1762std::array<double,3> LevelSetObject::evalProjectionPoint(const std::array<double,3> &point) const{
1763 return point - evalValue(point, true) * evalGradient(point, true);
1764}
1765
1771short LevelSetObject::evalValueSign(double value) const{
1772 return static_cast<short>(sign(value));
1773}
1774
1779void LevelSetObject::dump( std::ostream &stream ){
1780 // Identifier
1781 utils::binary::write(stream, m_id) ;
1782 utils::binary::write(stream, m_nReferences) ;
1783
1784 // Object properties
1785 utils::binary::write(stream, m_defaultSignedLevelSet) ;
1787 utils::binary::write(stream, m_cellBulkEvaluationMode) ;
1788
1789 // Restore cell caches
1792
1793 std::size_t nCaches = m_cellCacheCollection->size();
1794 utils::binary::write(stream, nCaches);
1795 for (std::size_t cacheId = 0; cacheId < nCaches; ++cacheId) {
1796 CellCacheCollection::Item &cacheItem = (*m_cellCacheCollection)[cacheId];
1797
1798 // Skip items without a factory
1799 bool hasFactory = cacheItem.hasFactory();
1800 utils::binary::write(stream, hasFactory);
1801 if (!hasFactory) {
1802 continue;
1803 }
1804
1805 // Field information
1806 LevelSetField field = LevelSetField::UNDEFINED;
1807 for (std::size_t fieldIndex = 0; fieldIndex < static_cast<std::size_t>(LevelSetField::COUNT); ++fieldIndex) {
1808 field = static_cast<LevelSetField>(fieldIndex);
1809 if (cacheId == getFieldCellCacheId(field)) {
1810 break;
1811 } else {
1812 field = LevelSetField::UNDEFINED;
1813 }
1814 }
1815
1816 utils::binary::write(stream, field);
1817 if (field != LevelSetField::UNDEFINED) {
1819 }
1820
1821 // Dump the cache
1822 bool hasCache = cacheItem.hasCache();
1823 utils::binary::write(stream, hasCache);
1824 if (hasCache) {
1825 CellCacheCollection::Cache *cache = cacheItem.getCache();
1826 cache->dump(stream);
1827 }
1828 }
1829
1830 // Output fields
1831 std::size_t nEnabledOutputFields = m_enabledOutputFields.size() ;
1832 utils::binary::write(stream, nEnabledOutputFields) ;
1833 for (const auto &fieldEntry : m_enabledOutputFields) {
1834 utils::binary::write(stream, fieldEntry.first) ;
1835 utils::binary::write(stream, fieldEntry.second) ;
1836 }
1837}
1838
1843void LevelSetObject::restore( std::istream &stream ){
1844 // Disable output
1845 LevelSetFieldset enabledOutputFieldset;
1846 enabledOutputFieldset.reserve(m_enabledOutputFields.size());
1847 for ( const auto &fieldEntry : m_enabledOutputFields ) {
1848 enabledOutputFieldset.push_back(fieldEntry.first);
1849 }
1850
1851 enableVTKOutput(enabledOutputFieldset, false);
1852
1853 // Reset object information
1854 for (std::size_t fieldIndex = 0; fieldIndex < static_cast<std::size_t>(LevelSetField::COUNT); ++fieldIndex) {
1855 m_cellFieldCacheModes[fieldIndex] = LevelSetCacheMode::NONE;
1856 m_cellFieldCacheIds[fieldIndex] = CellCacheCollection::NULL_CACHE_ID;
1857 }
1858
1859 m_cellPropagatedSignCacheId = CellCacheCollection::NULL_CACHE_ID;
1860 m_cellLocationCacheId = CellCacheCollection::NULL_CACHE_ID;
1861
1862 m_cellCacheCollection->clear();
1863
1864 // Identifier
1865 utils::binary::read(stream, m_id) ;
1866 utils::binary::read(stream, m_nReferences) ;
1867
1868 // Object properties
1869 utils::binary::read(stream, m_defaultSignedLevelSet) ;
1871 utils::binary::read(stream, m_cellBulkEvaluationMode) ;
1872
1873 // Cell caches
1874 std::size_t expectedCellLocationCacheId;
1875 utils::binary::read(stream, expectedCellLocationCacheId);
1876
1877 std::size_t expectedCellPropagatedSignCacheId;
1878 utils::binary::read(stream, expectedCellPropagatedSignCacheId);
1879
1880 std::size_t nCaches;
1881 utils::binary::read(stream, nCaches);
1882 for (std::size_t cacheId = 0; cacheId < nCaches; ++cacheId) {
1883 // Skip items without a factory
1884 bool hasFactory;
1885 utils::binary::read(stream, hasFactory);
1886 if (!hasFactory) {
1887 continue;
1888 }
1889
1890 // Field information
1891 LevelSetField field;
1892 utils::binary::read(stream, field);
1893 if (field != LevelSetField::UNDEFINED) {
1894 std::size_t fieldIndex = static_cast<std::size_t>(field);
1895 utils::binary::read(stream, m_cellFieldCacheModes[fieldIndex]);
1896 }
1897
1898 // Restore the cache
1899 bool hasCache;
1900 utils::binary::read(stream, hasCache);
1901 if (hasCache) {
1902 // Create the cache
1903 if (cacheId == expectedCellLocationCacheId) {
1904 createCellLocationCache(cacheId);
1905 } else if (cacheId == expectedCellPropagatedSignCacheId) {
1907 } else if (field != LevelSetField::UNDEFINED) {
1908 createFieldCellCache(field, cacheId);
1909 } else {
1910 throw std::runtime_error("Unable to restore levelset object " + std::to_string(getId()) + "!");
1911 }
1912
1913 // Restore cache contents
1914 CellCacheCollection::Cache *cache = getCellCache(cacheId);
1915 cache->restore(stream);
1916 } else {
1917 }
1918 }
1919
1920 // Enable output
1921 std::size_t nEnabledVTKOutputs ;
1922 utils::binary::read(stream, nEnabledVTKOutputs) ;
1923 for (std::size_t i = 0; i < nEnabledVTKOutputs; ++i) {
1924 LevelSetField field ;
1925 std::string fieldName;
1926 utils::binary::read(stream, field) ;
1927 utils::binary::read(stream, fieldName) ;
1928
1929 enableVTKOutput(field, fieldName);
1930 }
1931}
1932
1938void LevelSetObject::enableVTKOutput( const LevelSetFieldset &fieldset, bool enable) {
1939
1940 std::stringstream objectNameStream;
1941 objectNameStream << getId();
1942
1943 enableVTKOutput(fieldset, objectNameStream.str(), enable);
1944
1945}
1946
1954void LevelSetObject::enableVTKOutput( const LevelSetFieldset &fieldset, const std::string &objectName, bool enable) {
1955
1956 for (LevelSetField field : fieldset) {
1957 enableVTKOutput(field, objectName, enable);
1958 }
1959
1960}
1961
1968
1969 std::stringstream objectNameStream;
1970 objectNameStream << getId();
1971
1972 enableVTKOutput(field, objectNameStream.str(), enable);
1973
1974}
1975
1983void LevelSetObject::enableVTKOutput( LevelSetField field, const std::string &objectName, bool enable) {
1984
1985 // Discard fields that are not supported
1986 LevelSetFieldset supportedFields = getSupportedFields();
1987 if (std::find(supportedFields.begin(), supportedFields.end(), field) == supportedFields.end()) {
1988 return;
1989 }
1990
1991 // Check if the state of the filed is already the requested one
1992 if (enable == hasVTKOutputData(field, objectName)) {
1993 return;
1994 }
1995
1996 // Process the field
1997 if (!enable) {
1998 removeVTKOutputData(field, objectName) ;
1999 m_enabledOutputFields.erase(field) ;
2000 } else {
2001 addVTKOutputData(field, objectName) ;
2002 m_enabledOutputFields.insert({field, getVTKOutputDataName(field, objectName)}) ;
2003 }
2004
2005}
2006
2013
2014 std::stringstream objectNameStream;
2015 objectNameStream << getId();
2016
2017 enableVTKOutput(field, objectNameStream.str(), enable);
2018
2019}
2020
2028void LevelSetObject::enableVTKOutput( LevelSetWriteField writeField, const std::string &objectName, bool enable) {
2029
2030 LevelSetFieldset fieldset;
2031 if( writeField==LevelSetWriteField::ALL){
2032 fieldset = getSupportedFields();
2033
2034 } else if ( writeField==LevelSetWriteField::DEFAULT){
2035 fieldset.push_back(LevelSetField::VALUE);
2036 fieldset.push_back(LevelSetField::GRADIENT);
2037
2038 } else {
2039 LevelSetField field = static_cast<LevelSetField>(writeField);
2040
2041 LevelSetFieldset supportedFields = getSupportedFields();
2042 if (std::find(supportedFields.begin(), supportedFields.end(), field) == supportedFields.end()) {
2043 log::warning() << "The specified field is not supported by the levelset object" << std::endl;
2044 return;
2045 }
2046
2047 fieldset.push_back(field);
2048 }
2049
2050 enableVTKOutput( fieldset, objectName, enable);
2051
2052}
2053
2062bool LevelSetObject::hasVTKOutputData( LevelSetField field, const std::string &objectName) const {
2063
2064 VTK &vtkWriter = m_kernel->getMesh()->getVTK() ;
2065 std::string name = getVTKOutputDataName(field, objectName);
2066
2067 return vtkWriter.hasData(name);
2068
2069}
2070
2077void LevelSetObject::removeVTKOutputData( LevelSetField field, const std::string &objectName) {
2078
2079 VTK &vtkWriter = m_kernel->getMesh()->getVTK() ;
2080 std::string name = getVTKOutputDataName(field, objectName);
2081
2082 vtkWriter.removeData(name);
2083
2084}
2085
2092void LevelSetObject::addVTKOutputData( LevelSetField field, const std::string &objectName) {
2093
2094 VTK &vtkWriter = m_kernel->getMesh()->getVTK() ;
2095 std::string name = getVTKOutputDataName(field, objectName);
2096
2097 switch(field){
2098
2100 vtkWriter.addData<double>( name, VTKFieldType::SCALAR, VTKLocation::CELL, this);
2101 break;
2102
2104 vtkWriter.addData<short>( name, VTKFieldType::SCALAR, VTKLocation::CELL, this);
2105 break;
2106
2108 vtkWriter.addData<double>( name, VTKFieldType::VECTOR, VTKLocation::CELL, this);
2109 break;
2110
2111 default:
2112 throw std::runtime_error("Unsupported value of field in LevelSetObject::addDataToVTK() ");
2113 break;
2114
2115 }
2116
2117}
2118
2126std::string LevelSetObject::getVTKOutputDataName( LevelSetField field, const std::string &objectName) const {
2127
2128 std::stringstream nameStream;
2129 nameStream << "levelset" << getVTKOutputFieldName(field) << "_" << objectName;
2130 std::string name = nameStream.str();
2131
2132 return name;
2133
2134}
2135
2143
2144 switch(field){
2145
2147 return "Value";
2148
2150 return "Sign";
2151
2153 return "Gradient";
2154
2155 default:
2156 throw std::runtime_error("Unsupported value of field in LevelSetObject::addDataToVTK() ");
2157 break;
2158
2159 }
2160
2161}
2162
2173void LevelSetObject::flushData( std::fstream &stream, const std::string &name, VTKFormat format){
2174
2175 for ( const auto &fieldEntry : m_enabledOutputFields ) {
2176 const std::string &fieldName = fieldEntry.second;
2177 if (utils::string::keywordInString(name, fieldName)) {
2178 LevelSetField field = fieldEntry.first;
2179 flushVTKOutputData(stream, format, field);
2180 }
2181 }
2182
2183}
2184
2197void LevelSetObject::flushVTKOutputData(std::fstream &stream, VTKFormat format, LevelSetField field) const {
2198
2199 switch(field) {
2200
2202 {
2203 auto evaluator = [this] (long id) -> double { return evalCellValue(id, true); };
2204 auto fallback = [] (long id) -> double { BITPIT_UNUSED(id); return levelSetDefaults::VALUE; };
2205 flushVTKOutputData<double>(stream, format, field, evaluator, fallback);
2206 break;
2207 }
2208
2210 {
2211 auto evaluator = [this] (long id) -> short { return (short) evalCellSign(id); };
2212 auto fallback = [] (long id) -> short { BITPIT_UNUSED(id); return levelSetDefaults::SIGN; };
2213 flushVTKOutputData<short>(stream, format, field, evaluator, fallback);
2214 break;
2215 }
2216
2218 {
2219 auto evaluator = [this] (long id) -> std::array<double, 3> { return evalCellGradient(id, true); };
2220 auto fallback = [] (long id) -> const std::array<double, 3> & { BITPIT_UNUSED(id); return levelSetDefaults::GRADIENT; };
2221 flushVTKOutputData<std::array<double, 3>>(stream, format, field, evaluator, fallback);
2222 break;
2223 }
2224
2225 default:
2226 {
2227 throw std::runtime_error("Unable to write the field.");
2228 }
2229
2230 }
2231}
2232
2233#if BITPIT_ENABLE_MPI
2241void LevelSetObject::startCellCacheExchange( const std::unordered_map<int, std::vector<long>> &sendCellIds,
2242 std::size_t cacheId, DataCommunicator *dataCommunicator) const
2243{
2244 startCellCachesExchange(sendCellIds, std::vector<std::size_t>(1, cacheId), dataCommunicator);
2245}
2246
2254void LevelSetObject::startCellCachesExchange( const std::unordered_map<int, std::vector<long>> &sendCellIds,
2255 const std::vector<std::size_t> &cacheIds,
2256 DataCommunicator *dataCommunicator) const {
2257
2258 // Fill the exchange buffer with the content of the cache
2259 dataCommunicator->clearAllSends(false);
2260 for (const auto &entry : sendCellIds) {
2261 // Create an empty send
2262 int rank = entry.first;
2263 dataCommunicator->setSend(rank, 0);
2264
2265 // Write data in the buffer
2266 const std::vector<long> &rankSendCellIds = entry.second;
2267 SendBuffer &buffer = dataCommunicator->getSendBuffer(rank);
2268 for (std::size_t cacheId : cacheIds) {
2269 getCellCache(cacheId)->writeBuffer(rankSendCellIds, buffer);
2270 }
2271 buffer.squeeze( ) ;
2272 }
2273
2274 // Discover the receives
2275 dataCommunicator->discoverRecvs();
2276
2277 // Start the sends
2278 dataCommunicator->startAllRecvs();
2279
2280 // Start the sends
2281 dataCommunicator->startAllSends();
2282
2283}
2284
2292void LevelSetObject::completeCellCacheExchange( const std::unordered_map<int, std::vector<long>> &recvCellIds,
2293 std::size_t cacheId, DataCommunicator *dataCommunicator)
2294{
2295 completeCellCachesExchange(recvCellIds, std::vector<std::size_t>(1, cacheId), dataCommunicator);
2296}
2297
2305void LevelSetObject::completeCellCachesExchange( const std::unordered_map<int, std::vector<long>> &recvCellIds,
2306 const std::vector<std::size_t> &cacheIds,
2307 DataCommunicator *dataCommunicator){
2308
2309 // Read cache data from the exchange buffer
2310 int nCompletedRecvs = 0;
2311 while (nCompletedRecvs < dataCommunicator->getRecvCount()) {
2312 int rank = dataCommunicator->waitAnyRecv();
2313
2314 const std::vector<long> &rankRecvCellIds = recvCellIds.at(rank);
2315 RecvBuffer &buffer = dataCommunicator->getRecvBuffer(rank);
2316 for (std::size_t cacheId : cacheIds) {
2317 getCellCache(cacheId)->readBuffer(rankRecvCellIds, buffer);
2318 }
2319
2320 ++nCompletedRecvs;
2321 }
2322
2323 // Wait completion of all sends
2324 dataCommunicator->waitAllSends();
2325
2326}
2327#endif
2328
2336{
2337 std::size_t fieldIndex = static_cast<std::size_t>(field);
2338
2339 return m_cellFieldCacheModes[fieldIndex];
2340}
2341
2354{
2355 // Early return if the cache mode doesn't need to be updated
2356 if (getFieldCellCacheMode(field) == cacheMode) {
2357 return;
2358 }
2359
2360 // Early return if the cache should be disabled
2361 if (cacheMode == LevelSetCacheMode::NONE) {
2362 disableFieldCellCache(field);
2363 return;
2364 }
2365
2366 // Destroy existing cache
2367 destroyFieldCellCache(field);
2368
2369 // Update field properties
2370 std::size_t fieldIndex = static_cast<std::size_t>(field);
2371 m_cellFieldCacheModes[fieldIndex] = cacheMode;
2372
2373 // Update data
2374 if (getKernel()) {
2375 // Create field cell caches
2376 createFieldCellCache(field);
2377
2378 // Fill field cell caches inside the narrow band
2379 fillFieldCellCaches(LevelSetZone::NARROW_BAND, std::vector<LevelSetField>(1, field));
2380
2381 // Fill field cell caches inside the bulk
2382 fillFieldCellCaches(LevelSetZone::BULK, std::vector<LevelSetField>(1, field));
2383 }
2384}
2385
2392{
2393 // Early return if no cache is associated with the field
2395 return;
2396 }
2397
2398 // Destroy the cache
2399 destroyFieldCellCache(field);
2400
2401 // Update field properties
2402 std::size_t fieldIndex = static_cast<std::size_t>(field);
2403 m_cellFieldCacheModes[fieldIndex] = LevelSetCacheMode::NONE;
2404}
2405
2414void LevelSetObject::adaptCellCaches( const std::vector<adaption::Info> &adaptionData )
2415{
2416#if BITPIT_ENABLE_MPI==1
2417 // Get mesh information
2418 const VolumeKernel &mesh = *(m_kernel->getMesh());
2419#endif
2420
2421 // Early return if all the caches are empty
2422 bool allCachesEmpty = true;
2423 for (std::size_t cacheId = 0; cacheId < m_cellCacheCollection->size(); ++cacheId) {
2424 CellCacheCollection::Item &cacheItem = (*m_cellCacheCollection)[cacheId];
2425 if (cacheItem.hasCache()) {
2426 allCachesEmpty = false;
2427 break;
2428 }
2429 }
2430
2431#if BITPIT_ENABLE_MPI==1
2432 if (mesh.isPartitioned()) {
2433 MPI_Allreduce(MPI_IN_PLACE, &allCachesEmpty, 1, MPI_C_BOOL, MPI_LAND, m_kernel->getCommunicator()) ;
2434 }
2435#endif
2436
2437 if (allCachesEmpty) {
2438 return;
2439 }
2440
2441#if BITPIT_ENABLE_MPI
2442 // Identify the ids of the non-volatile caches
2443 std::vector<std::size_t> nonVolatileCellCacheIds;
2444
2445 CellCacheCollection::Cache *locationCache = getCellCache(m_cellLocationCacheId);
2446 if (locationCache && !locationCache->isVolatile()) {
2447 nonVolatileCellCacheIds.push_back(m_cellLocationCacheId);
2448 }
2449
2450 for (std::size_t fieldIndex = 0; fieldIndex < static_cast<std::size_t>(LevelSetField::COUNT); ++fieldIndex) {
2451 LevelSetField field = static_cast<LevelSetField>(fieldIndex);
2452 CellCacheCollection::Cache *fieldCache = getFieldCellCache(field);
2453 if (fieldCache && !fieldCache->isVolatile()) {
2454 std::size_t fieldCacheId = getFieldCellCacheId(field);
2455 nonVolatileCellCacheIds.push_back(fieldCacheId);
2456 }
2457 }
2458
2459 // Initialize data exchange
2460 //
2461 // Data exchange can be performed only for non-volatile caches.
2462 std::unordered_map<int, std::vector<long>> exchangeSendList ;
2463 std::unordered_map<int, std::vector<long>> exchangeRecvList ;
2464 if (!nonVolatileCellCacheIds.empty()) {
2465 if (m_kernel->getMesh()->isPartitioned()) {
2466 for( const adaption::Info &adaptionInfo : adaptionData){
2467 if( adaptionInfo.entity != adaption::Entity::ENTITY_CELL){
2468 continue;
2469 }
2470
2471 switch (adaptionInfo.type) {
2472
2473 case adaption::Type::TYPE_PARTITION_SEND:
2474 exchangeSendList.insert({{adaptionInfo.rank,adaptionInfo.previous}}) ;
2475 break;
2476
2477 case adaption::Type::TYPE_PARTITION_RECV:
2478 exchangeRecvList.insert({{adaptionInfo.rank,adaptionInfo.current}}) ;
2479 break;
2480
2481 default:
2482 break;
2483
2484 }
2485 }
2486 }
2487 }
2488
2489 bool exchangeCacheData = (!exchangeSendList.empty() || !exchangeRecvList.empty());
2490 if (m_kernel->getMesh()->isPartitioned()) {
2491 MPI_Allreduce(MPI_IN_PLACE, &exchangeCacheData, 1, MPI_C_BOOL, MPI_LOR, m_kernel->getCommunicator()) ;
2492 }
2493
2494 // Create data communicator
2495 std::unique_ptr<DataCommunicator> dataCommunicator;
2496 if (exchangeCacheData) {
2497 dataCommunicator = m_kernel->createDataCommunicator() ;
2498 }
2499#endif
2500
2501#if BITPIT_ENABLE_MPI
2502 // Start data exchange
2503 if (exchangeCacheData) {
2504 startCellCachesExchange(exchangeSendList, nonVolatileCellCacheIds, dataCommunicator.get());
2505 }
2506#endif
2507
2508 // Remove stale entries from the caches
2509 std::vector<long> staleCellIds = evalCellCacheStaleIds(adaptionData);
2510
2511 for (std::size_t cacheId = 0; cacheId < m_cellCacheCollection->size(); ++cacheId) {
2512 CellCacheCollection::Item &cacheItem = (*m_cellCacheCollection)[cacheId];
2513 if (!cacheItem.hasCache()) {
2514 continue;
2515 }
2516
2517 pruneCellCache(cacheId, staleCellIds);
2518 }
2519
2520#if BITPIT_ENABLE_MPI
2521 // Complete data exchange
2522 if (exchangeCacheData) {
2523 completeCellCachesExchange(exchangeRecvList, nonVolatileCellCacheIds, dataCommunicator.get());
2524 }
2525#endif
2526}
2527
2535void LevelSetObject::clearCellCache( std::size_t cacheId, bool release )
2536{
2537 CellCacheCollection::Item &cacheItem = (*m_cellCacheCollection)[cacheId];
2538
2539 if (release) {
2540 cacheItem.destroyCache();
2541 } else if (cacheItem.hasCache()) {
2542 CellCacheCollection::Cache *cache = cacheItem.getCache();
2543 cache->clear();
2544 }
2545}
2546
2553void LevelSetObject::pruneCellCache(std::size_t cacheId, const std::vector<long> &cellIds)
2554{
2555 // Get cache
2556 CellCacheCollection::Item &cacheItem = (*m_cellCacheCollection)[cacheId];
2557 if (!cacheItem.hasCache()) {
2558 return;
2559 }
2560
2561 CellCacheCollection::Cache *cache = cacheItem.getCache();
2562
2563 // Remove entries
2564 cache->erase(cellIds);
2565
2566 // Reclaim unused memory
2567 if (m_kernel->getExpectedFillIn() == LevelSetFillIn::SPARSE) {
2568 cache->shrink_to_fit();
2569 }
2570}
2571
2580{
2581 switch (cacheMode) {
2582
2584 return evalCellOnDemandCacheFillIds(zone);
2585
2587 return evalCellNarrowBandCacheFillIds(zone);
2588
2590 return evalCellFullCacheFillIds(zone);
2591
2592 default:
2593 throw std::runtime_error("Unsupported cache mode!");
2594
2595 }
2596}
2597
2609 const std::vector<adaption::Info> &adaptionData) const
2610{
2611 switch (cacheMode) {
2612
2614 return evalCellOnDemandCacheFillIds(zone, adaptionData);
2615
2617 return evalCellNarrowBandCacheFillIds(zone, adaptionData);
2618
2620 return evalCellFullCacheFillIds(zone, adaptionData);
2621
2622 default:
2623 throw std::runtime_error("Unsupported cache mode!");
2624
2625 }
2626}
2627
2637{
2638 BITPIT_UNUSED(zone);
2639
2640 return std::vector<long>();
2641}
2642
2654std::vector<long> LevelSetObject::evalCellOnDemandCacheFillIds(LevelSetZone zone, const std::vector<adaption::Info> &adaptionData) const
2655{
2656 BITPIT_UNUSED(zone);
2657 BITPIT_UNUSED(adaptionData);
2658
2659 return std::vector<long>();
2660}
2661
2671{
2672 // Early return if the object is empty
2673 if (empty()) {
2674 return std::vector<long>();
2675 }
2676
2677 // There are no narrow band cells in the bulk
2678 if (zone == LevelSetZone::BULK) {
2679 return std::vector<long>();
2680 }
2681
2682 // Get mesh information
2683 const VolumeKernel &mesh = *(m_kernel->getMesh()) ;
2684
2685 // Detect the cells to be filled
2686 //
2687 // If no narrow band size was set, all cells should be filled, otherwise only
2688 // the cells inside the narrow band should be filled.
2689 if (m_narrowBandSize == LEVELSET_NARROW_BAND_UNLIMITED) {
2690 if (const VolCartesian *cartesianMesh = dynamic_cast<const VolCartesian *>(&mesh)) {
2691 long nCells = cartesianMesh->getCellCount();
2692 std::vector<long> cellFillIds(nCells);
2693 std::iota(cellFillIds.begin(), cellFillIds.end(), 0);
2694
2695 return cellFillIds;
2696 } else {
2697 return mesh.getCells().getIds(false);
2698 }
2699 } else {
2700 std::vector<long> cellFillIds;
2701 if (const VolCartesian *cartesianMesh = dynamic_cast<const VolCartesian *>(&mesh)) {
2702 long nCells = cartesianMesh->getCellCount();
2703 for (long cellId = 0; cellId < nCells; ++cellId) {
2704 if (isCellInNarrowBand(cellId)) {
2705 cellFillIds.push_back(cellId);
2706 }
2707 }
2708 } else {
2709 for (const Cell &cell : mesh.getCells()) {
2710 long cellId = cell.getId();
2711 if (isCellInNarrowBand(cellId)) {
2712 cellFillIds.push_back(cellId);
2713 }
2714 }
2715 }
2716
2717 return cellFillIds;
2718 }
2719}
2720
2732std::vector<long> LevelSetObject::evalCellNarrowBandCacheFillIds(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 // There are no narrow band cells in the bulk
2740 if (zone == LevelSetZone::BULK) {
2741 return std::vector<long>();
2742 }
2743
2744 // Detect the cells to be filled
2745 //
2746 // If no narrow band size was set, all new cells should be filled, otherwise only
2747 // the cells inside the narrow band should be filled.
2748 std::vector<long> cellFillIds;
2749 for (const adaption::Info &adaptionInfo : adaptionData) {
2750 if (adaptionInfo.entity != adaption::Entity::ENTITY_CELL) {
2751 continue;
2752 }
2753
2754 if (m_narrowBandSize == LEVELSET_NARROW_BAND_UNLIMITED) {
2755 cellFillIds.insert(cellFillIds.end(), adaptionInfo.current.begin(), adaptionInfo.current.end());
2756 } else {
2757 for (long cellId : adaptionInfo.current) {
2758 if (isCellInNarrowBand(cellId)) {
2759 cellFillIds.push_back(cellId);
2760 }
2761 }
2762 }
2763 }
2764
2765 return cellFillIds;
2766}
2767
2777{
2778 // Early return if the object is empty
2779 if (empty()) {
2780 return std::vector<long>();
2781 }
2782
2783 // Detect the cells to be filled
2784 //
2785 // If the requested zone is the narrow band, it is possible to call the same function used
2786 // for the caches in narrow band mode.
2787 //
2788 // If the requested zone is the bulk, only the functions outside the narrow band should be
2789 // filled. This implies that, if the narrow band size was not not set, no cells should be
2790 // filled.
2791 if (zone == LevelSetZone::NARROW_BAND) {
2792 return evalCellNarrowBandCacheFillIds(zone);
2793 } else {
2794 if (m_narrowBandSize == LEVELSET_NARROW_BAND_UNLIMITED) {
2795 return std::vector<long>();
2796 } else {
2797 // Get mesh information
2798 const VolumeKernel &mesh = *(m_kernel->getMesh()) ;
2799
2800 // Identify cells outside the narrow band
2801 std::vector<long> cellFillIds;
2802 if (const VolCartesian *cartesianMesh = dynamic_cast<const VolCartesian *>(&mesh)) {
2803 long nCells = cartesianMesh->getCellCount();
2804 for (long cellId = 0; cellId < nCells; ++cellId) {
2805 if (!isCellInNarrowBand(cellId)) {
2806 cellFillIds.push_back(cellId);
2807 }
2808 }
2809 } else {
2810 for (const Cell &cell : mesh.getCells()) {
2811 long cellId = cell.getId();
2812 if (!isCellInNarrowBand(cellId)) {
2813 cellFillIds.push_back(cellId);
2814 }
2815 }
2816 }
2817
2818 return cellFillIds;
2819 }
2820 }
2821}
2822
2834std::vector<long> LevelSetObject::evalCellFullCacheFillIds(LevelSetZone zone, const std::vector<adaption::Info> &adaptionData) const
2835{
2836 // Early return if the object is empty
2837 if (empty()) {
2838 return std::vector<long>();
2839 }
2840
2841 // Detect the cells to be filled
2842 //
2843 // If the requested zone is the narrow band, it is possible to call the same function used
2844 // for the caches in narrow band mode.
2845 //
2846 // If the requested zone is the bulk, only the functions outside the narrow band should be
2847 // filled. This implies that, if the narrow band size was not not set, no cells should be
2848 // filled.
2849 if (zone == LevelSetZone::NARROW_BAND) {
2850 return evalCellNarrowBandCacheFillIds(zone);
2851 } else {
2852 if (m_narrowBandSize == LEVELSET_NARROW_BAND_UNLIMITED) {
2853 return std::vector<long>();
2854 } else {
2855 // Identify cells outside the narrow band
2856 std::vector<long> cellFillIds;
2857 for (const adaption::Info &adaptionInfo : adaptionData) {
2858 if (adaptionInfo.entity != adaption::Entity::ENTITY_CELL) {
2859 continue;
2860 }
2861
2862 for (long cellId : adaptionInfo.current) {
2863 if (!isCellInNarrowBand(cellId)) {
2864 cellFillIds.push_back(cellId);
2865 }
2866 }
2867 }
2868
2869 return cellFillIds;
2870 }
2871 }
2872}
2873
2880std::vector<long> LevelSetObject::evalCellCacheStaleIds(const std::vector<adaption::Info> &adaptionData) const
2881{
2882 // Identify cells whose entries should be pruned
2883 //
2884 // We need to prune entries for both cells that have been removed and newly added cells.
2885 // When a new cell is added to the cache, synchronized caches will automatically create
2886 // an entry for the new cell, however the information associated to that entry is not
2887 // initialized and may contain random data. We need to explicitly state that the newly
2888 // added cells are not in the cached yet.
2889 std::vector<long> cellPruneIds;
2890 for (const adaption::Info &adaptionInfo : adaptionData) {
2891 if (adaptionInfo.entity != adaption::Entity::ENTITY_CELL) {
2892 continue;
2893 }
2894
2895 cellPruneIds.insert(cellPruneIds.end(), adaptionInfo.previous.begin(), adaptionInfo.previous.end());
2896 cellPruneIds.insert(cellPruneIds.end(), adaptionInfo.current.begin(), adaptionInfo.current.end());
2897 }
2898
2899 return cellPruneIds;
2900}
2901
2910typename LevelSetObject::CellCacheCollection::Cache * LevelSetObject::getFieldCellCache(LevelSetField field) const
2911{
2912 // Early return if no cache is associated with the field
2914 return nullptr;
2915 }
2916
2917 // Get field cache
2918 std::size_t cacheId = getFieldCellCacheId(field);
2919
2920 return getCellCache(cacheId);
2921}
2922
2930{
2931 std::size_t fieldIndex = static_cast<std::size_t>(field);
2932
2933 return m_cellFieldCacheIds[fieldIndex];
2934}
2935
2944std::size_t LevelSetObject::createFieldCellCache(LevelSetField field, std::size_t cacheId)
2945{
2946 switch(field) {
2947
2949 return createFieldCellCache<double>(field, cacheId);
2950
2952 return createFieldCellCache<short>(field, cacheId);
2953
2955 return createFieldCellCache<std::array<double, 3>>(field, cacheId);
2956
2957 default:
2958 throw std::runtime_error("The requested field is not supported!");
2959
2960 }
2961}
2962
2969{
2970 // Update field properties
2971 std::size_t fieldIndex = static_cast<std::size_t>(field);;
2972 m_cellFieldCacheIds[fieldIndex] = CellCacheCollection::NULL_CACHE_ID;
2973
2974 // Destroy cache
2975 std::size_t cacheId = getFieldCellCacheId(field);
2976 destroyCellCache(cacheId);
2977}
2978
2985void LevelSetObject::fillFieldCellCaches(LevelSetZone zone, const std::vector<LevelSetField> &fields)
2986{
2987 // Early return if there are no caches to update
2988 if (fields.empty()) {
2989 return;
2990 }
2991
2992 // It's more efficient to process the fields grouped by their cache mode
2993 for (std::size_t cacheModeIndex = 0; cacheModeIndex < static_cast<std::size_t>(LevelSetCacheMode::COUNT); ++cacheModeIndex) {
2994 LevelSetCacheMode cacheMode = static_cast<LevelSetCacheMode>(cacheModeIndex);
2995
2996 // Get fields that operate in the processed cache mode
2997 std::vector<LevelSetField> cacheModeFields;
2998 for (LevelSetField field : fields) {
2999 if (getFieldCellCacheMode(field) != cacheMode) {
3000 continue;
3001 }
3002
3003 cacheModeFields.push_back(field);
3004 }
3005
3006 if (cacheModeFields.empty()) {
3007 continue;
3008 }
3009
3010 // Identify the cell ids that should be added to the cache
3011 std::vector<long> cellCacheFillIds = evalCellCacheFillIds(zone, cacheMode);
3012
3013 // Fill the caches
3014 for (LevelSetField field : cacheModeFields) {
3015 fillFieldCellCache(field, cellCacheFillIds);
3016 }
3017 }
3018}
3019
3027void LevelSetObject::fillFieldCellCaches(LevelSetZone zone, const std::vector<LevelSetField> &fields,
3028 const std::vector<adaption::Info> &adaptionData)
3029{
3030 // Early return if there are no caches to update
3031 if (fields.empty()) {
3032 return;
3033 }
3034
3035#if BITPIT_ENABLE_MPI==1
3036 // Get mesh information
3037 const VolumeKernel &mesh = *(m_kernel->getMesh());
3038
3039 // Check if there are non-volatile caches to process
3040 bool areNonVolatileCacheProcessed = false;
3041 for (LevelSetField field : fields) {
3042 CellCacheCollection::Cache *fieldCache = getFieldCellCache(field);
3043 if (fieldCache && !fieldCache->isVolatile()) {
3044 areNonVolatileCacheProcessed = true;
3045 break;
3046 }
3047 }
3048
3049 // Identify the cells that have been received.
3050 std::unordered_set<long> recievedCellIds;
3051 if (areNonVolatileCacheProcessed) {
3052 if (m_kernel->getMesh()->isPartitioned()) {
3053 for( const adaption::Info &adaptionInfo : adaptionData){
3054 if( adaptionInfo.entity != adaption::Entity::ENTITY_CELL){
3055 continue;
3056 }
3057
3058 switch (adaptionInfo.type) {
3059
3060 case adaption::Type::TYPE_PARTITION_RECV:
3061 recievedCellIds.insert(adaptionInfo.current.begin(), adaptionInfo.current.end());
3062 break;
3063
3064 default:
3065 break;
3066
3067 }
3068 }
3069 }
3070 }
3071#endif
3072
3073 // It's more efficient to process the fields grouped by their cache mode
3074 for (std::size_t cacheModeIndex = 0; cacheModeIndex < static_cast<std::size_t>(LevelSetCacheMode::COUNT); ++cacheModeIndex) {
3075 LevelSetCacheMode cacheMode = static_cast<LevelSetCacheMode>(cacheModeIndex);
3076
3077 // Get fields with caches that operate in the processed mode
3078 std::vector<LevelSetField> cacheModeFields;
3079 for (LevelSetField field : fields) {
3080 if (getFieldCellCacheMode(field) != cacheMode) {
3081 continue;
3082 }
3083
3084 cacheModeFields.push_back(field);
3085 }
3086
3087 if (cacheModeFields.empty()) {
3088 continue;
3089 }
3090
3091 // Identify the cell ids that should be filled for volatile caches
3092 std::vector<long> cellVolatileCacheFillIds = evalCellCacheFillIds(zone, cacheMode, adaptionData);
3093
3094 bool emptyCellVolatileCacheFillIds = cellVolatileCacheFillIds.empty();
3095#if BITPIT_ENABLE_MPI==1
3096 if (mesh.isPartitioned()) {
3097 MPI_Allreduce(MPI_IN_PLACE, &emptyCellVolatileCacheFillIds, 1, MPI_C_BOOL, MPI_LAND, m_kernel->getCommunicator()) ;
3098 }
3099#endif
3100
3101 if (emptyCellVolatileCacheFillIds) {
3102 continue;
3103 }
3104
3105 // Identify the cell ids that should be filled for non-volatile caches
3106 //
3107 // For non-volatile caches, entries associated with cell received from other processes
3108 // are exchanged when the caches are adapted and there is no need to evaluate them.
3109 std::vector<long> cellNonVolatileCacheFillIds;
3110#if BITPIT_ENABLE_MPI
3111 if (areNonVolatileCacheProcessed && !recievedCellIds.empty()) {
3112 cellNonVolatileCacheFillIds.reserve(cellVolatileCacheFillIds.size());
3113 for (long cellId : cellVolatileCacheFillIds) {
3114 if (recievedCellIds.count(cellId)) {
3115 continue;
3116 }
3117
3118 cellNonVolatileCacheFillIds.push_back(cellId);
3119 }
3120 } else {
3121 cellNonVolatileCacheFillIds = cellVolatileCacheFillIds;
3122 }
3123#else
3124 cellNonVolatileCacheFillIds = cellVolatileCacheFillIds;
3125#endif
3126
3127 // Fill field caches
3128 for (LevelSetField field : cacheModeFields) {
3129 CellCacheCollection::Cache *cache = getFieldCellCache(field);
3130 if (cache->isVolatile()) {
3131 fillFieldCellCache(field, cellVolatileCacheFillIds);
3132 } else {
3133 fillFieldCellCache(field, cellNonVolatileCacheFillIds);
3134 }
3135 }
3136 }
3137}
3138
3145void LevelSetObject::fillFieldCellCache(LevelSetField field, const std::vector<long> &cellIds)
3146{
3147 // Early return if no cache is associated with the field
3149 return;
3150 }
3151
3152#if BITPIT_ENABLE_MPI==1
3153 // Get mesh information
3154 const VolumeKernel &mesh = *(m_kernel->getMesh());
3155#endif
3156
3157 // Early return if there are no cells to fill
3158 //
3159 // Even if there are no cells to fill, the cache will now be considered non-empty, in this
3160 // way it will be processed by the functions that update the caches.
3161 bool emptyCellIds = cellIds.empty();
3162#if BITPIT_ENABLE_MPI==1
3163 if (mesh.isPartitioned()) {
3164 MPI_Allreduce(MPI_IN_PLACE, &emptyCellIds, 1, MPI_C_BOOL, MPI_LAND, m_kernel->getCommunicator()) ;
3165 }
3166#endif
3167
3168 if (emptyCellIds) {
3169 return;
3170 }
3171
3172 // Get list of ghost exchange sources
3173 std::unordered_set<long> ghostExchangeSources;
3174 std::unordered_set<long> ghostExchangeTargets;
3175#if BITPIT_ENABLE_MPI
3176 for (const auto &entry : mesh.getGhostCellExchangeSources()) {
3177 const std::vector<long> &rankSourceList = entry.second;
3178 ghostExchangeSources.insert(rankSourceList.begin(), rankSourceList.end());
3179 }
3180
3181 for (const auto &entry : mesh.getGhostCellExchangeTargets()) {
3182 const std::vector<long> &rankTargetList = entry.second;
3183 ghostExchangeTargets.insert(rankTargetList.begin(), rankTargetList.end());
3184 }
3185#endif
3186
3187#if BITPIT_ENABLE_MPI
3188 // Get field cache
3189 std::size_t cacheId = getFieldCellCacheId(field);
3190
3191 // Create data communicator
3192 std::unique_ptr<DataCommunicator> dataCommunicator;
3193 if (mesh.isPartitioned()) {
3194 dataCommunicator = m_kernel->createDataCommunicator();
3195 }
3196
3197 // Fill cache values of cells that are sources for ghost exchange
3198 for (long cellId : cellIds) {
3199 if (ghostExchangeSources.count(cellId) == 0) {
3200 continue;
3201 }
3202
3203 fillFieldCellCache(field, cellId);
3204 }
3205
3206 // Start ghost exchange
3207 if (dataCommunicator) {
3208 startCellCacheExchange(mesh.getGhostCellExchangeSources(), cacheId, dataCommunicator.get());
3209 }
3210#endif
3211
3212 // Fill cache values of internal cells that are not sources for ghost exchange
3213 //
3214 // Values on ghost cells are not evaluated, because those values will be communicated
3215 // by the ghost data exchange.
3216 for (long cellId : cellIds) {
3217 if (ghostExchangeSources.count(cellId) > 0) {
3218 continue;
3219 } else if (ghostExchangeTargets.count(cellId) > 0) {
3220 continue;
3221 }
3222
3223 fillFieldCellCache(field, cellId);
3224 }
3225
3226#if BITPIT_ENABLE_MPI
3227 // Complete ghost exchange
3228 if (dataCommunicator) {
3229 completeCellCacheExchange(mesh.getGhostCellExchangeTargets(), cacheId, dataCommunicator.get());
3230 }
3231#endif
3232}
3233
3241{
3242 // Early return if no cache is associated with the field
3244 return;
3245 }
3246
3247 // Fill cache
3248 switch (field) {
3249
3251 evalCellSign(id);
3252 break;
3253
3255 evalCellValue(id, true);
3256 break;
3257
3259 evalCellGradient(id, true);
3260 break;
3261
3262 default:
3263 throw std::runtime_error("Unsupported field!");
3264
3265 }
3266}
3267
3277typename LevelSetObject::CellCacheCollection::Cache * LevelSetObject::getCellCache(std::size_t cacheId) const
3278{
3279 if (cacheId == CellCacheCollection::NULL_CACHE_ID) {
3280 return nullptr;
3281 }
3282
3283 return (*m_cellCacheCollection)[cacheId].getCache();
3284}
3285
3291void LevelSetObject::destroyCellCache(std::size_t cacheId)
3292{
3293 m_cellCacheCollection->erase(cacheId);
3294}
3295
3302std::array<double,3> LevelSetObject::computeProjectionPoint(long cellId) const {
3303 return evalCellProjectionPoint(cellId);
3304}
3305
3312std::array<double,3> LevelSetObject::computeVertexProjectionPoint(long vertexId) const {
3313 const std::array<double,3> &vertexCoords = m_kernel->getMesh()->getVertexCoords(vertexId);
3314 return evalProjectionPoint(vertexCoords);
3315}
3316
3322std::array<double,3> LevelSetObject::computeProjectionPoint(const std::array<double,3> &point) const{
3323 return evalProjectionPoint(point);
3324}
3325
3331short LevelSetObject::getSign(long cellId) const {
3332
3333 return evalCellSign(cellId);
3334
3335}
3336
3342double LevelSetObject::getValue(long cellId) const {
3343
3344 return evalCellValue(cellId, m_defaultSignedLevelSet);
3345
3346}
3347
3353std::array<double,3> LevelSetObject::getGradient(long cellId) const {
3354
3355 return evalCellGradient(cellId, m_defaultSignedLevelSet);
3356
3357}
3358
3365
3366 return LevelSetInfo(evalCellValue(cellId, m_defaultSignedLevelSet), evalCellGradient(cellId, m_defaultSignedLevelSet));
3367
3368}
3369
3375double LevelSetObject::getLS(long cellId) const {
3376
3377 return evalCellValue(cellId, m_defaultSignedLevelSet);
3378
3379}
3380
3387 return getNarrowBandSize();
3388}
3389
3398
3399}
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 Interface class defines the interfaces among cells.
Definition interface.hpp:37
std::size_t erase(const Keys &keys)
Mesh specific implementation to calculate the levelset function.
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)
std::size_t createCellCache(LevelSetFillIn expectedFillIn, std::size_t cacheId=CellCacheCollection::NULL_CACHE_ID)
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)
LevelSetIntersectionStatus isCellIntersected(long id, LevelSetIntersectionMode=LevelSetIntersectionMode::FAST_FUZZY) const
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
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 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 LevelSetIntersectionStatus _isInterfaceIntersected(long id, bool positivePart, std::array< std::array< double, 3 >, 2 > *intersection, std::vector< std::array< double, 3 > > *polygon) 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
LevelSetIntersectionStatus intersectSurface(long id, LevelSetIntersectionMode=LevelSetIntersectionMode::FAST_FUZZY) const
void adaptCellCaches(const std::vector< adaption::Info > &adaptionData)
virtual bool isCellInNarrowBand(long id) const
std::vector< long > evalCellOnDemandCacheFillIds(LevelSetZone zone) const
value_t evalCellFieldCached(LevelSetField field, long id, const evaluator_t &evaluator, const fallback_t &fallback) const
void setNarrowBandSize(double size)
LevelSetIntersectionStatus isInterfaceIntersected(long id, bool invert, std::array< std::array< double, 3 >, 2 > *intersection, std::vector< std::array< double, 3 > > *polygon) const
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 LevelSetIntersectionStatus _isCellIntersected(long, double distance, LevelSetIntersectionMode=LevelSetIntersectionMode::FAST_FUZZY) const
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 update(const std::vector< adaption::Info > &adaptionData)
void startCellCacheExchange(const std::unordered_map< int, std::vector< long > > &recvCellIds, std::size_t cacheIds, DataCommunicator *) const
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
const MPI_Comm & getCommunicator() const
void processCellFaceNeighbours(long seedId, int nLayers, Function function) const
virtual long getCellCount() const
CellConstIterator getCellConstIterator(long id) const
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:39
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.
@ 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.
namespace containing default values
const std::array< double, 3 > GRADIENT
Logger & error(log::Visibility defaultVisibility)
Definition logger.cpp:1786
Logger & warning(log::Visibility defaultVisibility)
Definition logger.cpp:1821
A public container which includes all information provided by LevelSet.
The Info struct defines the infomation associated to an adaption.
Definition adaption.hpp:63