1 // Copyright (c) 2017-2021, Lawrence Livermore National Security, LLC and 2 // other Axom Project Developers. See the top-level LICENSE file for details. 3 // 4 // SPDX-License-Identifier: (BSD-3-Clause) 5 6 /** 7 * \file InOutOctreeValidator.hpp 8 * 9 * \brief Defines helper class to validate an InOutOctree instance 10 */ 11 12 #ifndef AXOM_QUEST_INOUT_OCTREE_VALIDATOR__HPP_ 13 #define AXOM_QUEST_INOUT_OCTREE_VALIDATOR__HPP_ 14 15 #include "axom/core.hpp" 16 #include "axom/slic.hpp" 17 #include "axom/slam.hpp" 18 #include "axom/primal.hpp" 19 20 #include "BlockData.hpp" 21 #include "MeshWrapper.hpp" 22 23 #include "axom/fmt.hpp" 24 25 namespace axom 26 { 27 namespace quest 28 { 29 // Predeclare InOutOctree class 30 template <int DIM> 31 class InOutOctree; 32 33 namespace detail 34 { 35 template <int DIM> 36 class InOutOctreeValidator 37 { 38 public: 39 using InOutOctreeType = InOutOctree<DIM>; 40 using CellIndexSet = typename InOutOctreeType::CellIndexSet; 41 42 using OctreeBaseType = typename InOutOctreeType::OctreeBaseType; 43 using OctreeLevels = typename OctreeBaseType::OctreeLevels; 44 using BlockIndex = typename OctreeBaseType::BlockIndex; 45 46 using SpacePt = typename InOutOctreeType::SpacePt; 47 using VertexIndex = typename InOutOctreeType::VertexIndex; 48 using CellIndex = typename InOutOctreeType::CellIndex; 49 using CellVertIndices = typename MeshWrapper<DIM>::CellVertIndices; 50 using GeometricBoundingBox = typename InOutOctreeType::GeometricBoundingBox; 51 52 public: InOutOctreeValidator(const InOutOctreeType & octree)53 InOutOctreeValidator(const InOutOctreeType& octree) 54 : m_octree(octree) 55 , m_generationState(m_octree.m_generationState) 56 { } 57 58 /// This function checks that all leaf blocks in the tree were assigned a color checkAllLeavesColored() const59 void checkAllLeavesColored() const 60 { 61 SLIC_DEBUG( 62 "--Checking that all leaves have a color -- black, white and gray"); 63 64 for(int lev = 0; lev < m_octree.m_levels.size(); ++lev) 65 { 66 checkAllLeavesColoredAtLevel(lev); 67 } 68 } 69 70 /// This function checks that each leaf block at level \a level has a color checkAllLeavesColoredAtLevel(int level) const71 void checkAllLeavesColoredAtLevel(int level) const 72 { 73 const auto& levelLeafMap = m_octree.getOctreeLevel(level); 74 auto itEnd = levelLeafMap.end(); 75 for(auto it = levelLeafMap.begin(); it != itEnd; ++it) 76 { 77 if(!it->isLeaf()) continue; 78 79 SLIC_ASSERT_MSG( 80 it->isColored(), 81 fmt::format( 82 "Error after coloring level {} leaf block {} was not colored.", 83 level, 84 BlockIndex(it.pt(), level))); 85 } 86 } 87 88 /// This function checks that each vertex in the mesh is indexed by a leaf block of the octree checkEachVertexIsIndexed() const89 void checkEachVertexIsIndexed() const 90 { 91 SLIC_DEBUG("--Checking that each vertex is in a leaf block of the tree."); 92 93 const VertexIndex numVertices = m_octree.m_meshWrapper.numMeshVertices(); 94 for(VertexIndex i = 0; i < numVertices; ++i) 95 { 96 const SpacePt& pos = m_octree.m_meshWrapper.vertexPosition(i); 97 BlockIndex vertBlock = m_octree.findLeafBlock(pos); 98 const InOutBlockData& leafData = m_octree[vertBlock]; 99 100 VertexIndex vertInBlock = m_octree.leafVertex(vertBlock, leafData); 101 AXOM_UNUSED_VAR(vertInBlock); 102 103 // Check that we can find the leaf block indexing each vertex from its position 104 SLIC_ASSERT_MSG( 105 leafData.hasData() && vertInBlock == i, 106 fmt::format( 107 " Vertex {} at position {} was not indexed in block {} with bounding " 108 "box {} (point {} contained in block)." 109 " \n\n *** \n Leaf data: {} \n ***", 110 i, 111 pos, 112 vertBlock, 113 m_octree.blockBoundingBox(vertBlock), 114 (m_octree.blockBoundingBox(vertBlock).contains(pos) ? "is" : "is NOT"), 115 leafData)); 116 117 // Check that our cached value of the vertex's block is accurate 118 SLIC_ASSERT_MSG( 119 vertBlock == m_octree.m_vertexToBlockMap[i], 120 fmt::format( 121 "Cached block for vertex {} differs from its found block. " 122 "\n\t -- cached block {} -- is leaf? {}" 123 "\n\t -- actual block {} -- is leaf? {}" 124 "\n\t -- vertex set size: {}", 125 i, 126 m_octree.m_vertexToBlockMap[i], 127 (m_octree[m_octree.m_vertexToBlockMap[i]].isLeaf() ? "yes" : "no"), 128 vertBlock, 129 (m_octree[vertBlock].isLeaf() ? "yes" : "no"), 130 numVertices)); 131 } 132 } 133 134 // Check that for each a cell's vertices, that cell is indexed by the block containing that vertex checkCellsReferencedInBoundaryVertexBlocks() const135 void checkCellsReferencedInBoundaryVertexBlocks() const 136 { 137 SLIC_DEBUG( 138 "--Checking that each cell is referenced by the leaf blocks " 139 "containing its vertices."); 140 141 const axom::IndexType numCells = m_octree.m_meshWrapper.numMeshCells(); 142 for(axom::IndexType cIdx = 0; cIdx < numCells; ++cIdx) 143 { 144 CellVertIndices cvRel = m_octree.m_meshWrapper.cellVertexIndices(cIdx); 145 for(int j = 0; j < cvRel.size(); ++j) 146 { 147 VertexIndex vIdx = cvRel[j]; 148 BlockIndex vertBlock = m_octree.m_vertexToBlockMap[vIdx]; 149 const InOutBlockData& leafData = m_octree[vertBlock]; 150 151 // Check that this cell is referenced in this block 152 bool foundCell = false; 153 CellIndexSet leafCells = m_octree.leafCells(vertBlock, leafData); 154 for(int k = 0; !foundCell && k < leafCells.size(); ++k) 155 { 156 if(leafCells[k] == cIdx) foundCell = true; 157 } 158 159 SLIC_ASSERT_MSG( 160 foundCell, 161 fmt::format("Did not find cell with index {} and vertices [{}] " 162 "in block {} containing vertex {}" 163 " \n\n *** " 164 "\n Leaf data: {} " 165 "\n\t Cells in block? [{}] " 166 "\n ***", 167 cIdx, 168 fmt::join(cvRel, ", "), 169 vertBlock, 170 vIdx, 171 leafData, 172 fmt::join(leafCells.begin(), leafCells.end(), ", "))); 173 } 174 } 175 } 176 177 /// Checks some indexing contstaints on internal and leaf blocks checkBlockIndexingConsistency() const178 void checkBlockIndexingConsistency() const 179 { 180 SLIC_DEBUG( 181 "--Checking that internal blocks have no data, and that leaves satisfy " 182 "all PM conditions"); 183 184 const double bb_scale_factor = m_octree.m_boundingBoxScaleFactor; 185 186 for(int lev = 0; lev < m_octree.m_levels.size(); ++lev) 187 { 188 const auto& levelLeafMap = m_octree.getOctreeLevel(lev); 189 auto itEnd = levelLeafMap.end(); 190 for(auto it = levelLeafMap.begin(); it != itEnd; ++it) 191 { 192 const BlockIndex block(it.pt(), lev); 193 const InOutBlockData& data = *it; 194 195 if(!data.isLeaf()) 196 { 197 SLIC_ASSERT(!data.hasData()); // internal blocks should not have data 198 } 199 else // leaf block 200 { 201 if(data.hasData()) 202 { 203 VertexIndex vIdx = m_octree.leafVertex(block, data); 204 CellIndexSet blockCells = m_octree.leafCells(block, data); 205 for(int i = 0; i < blockCells.size(); ++i) 206 { 207 CellIndex cIdx = blockCells[i]; 208 209 // Check that vIdx is one of this cell's vertices 210 CellVertIndices cvRel = 211 m_octree.m_meshWrapper.cellVertexIndices(cIdx); 212 213 SLIC_ASSERT_MSG( 214 m_octree.m_meshWrapper.incidentInVertex(cvRel, vIdx), 215 fmt::format("All cells in a gray block must be incident " 216 " in a common vertex, but cell {} with " 217 "vertices [{}] in block {}" 218 " is not incident in vertex {}", 219 cIdx, 220 fmt::join(cvRel, ", "), 221 block, 222 vIdx)); 223 224 // Check that this cell intersects the bounding box of the block 225 GeometricBoundingBox blockBB = m_octree.blockBoundingBox(block); 226 blockBB.expand(bb_scale_factor); 227 SLIC_ASSERT_MSG( 228 m_octree.blockIndexesElementVertex(cIdx, block) || 229 intersect(m_octree.m_meshWrapper.cellPositions(cIdx), blockBB), 230 fmt::format("Cell {} was indexed in block {}" 231 " but it does not intersect the block." 232 "\n\tBlock bounding box: {}" 233 "\n\tCell positions: {}" 234 "\n\tCell vertex indices: [{}]" 235 "\n\tLeaf vertex is: {}" 236 "\n\tLeaf cells: {} ({})", 237 cIdx, 238 block, 239 blockBB, 240 m_octree.m_meshWrapper.cellPositions(cIdx), 241 fmt::join(cvRel, ", "), 242 vIdx, 243 fmt::join(blockCells, ", "), 244 blockCells.size())); 245 } 246 } 247 } 248 } 249 } 250 } 251 252 /// Checks conditions about block colors checkNeighboringBlockColors() const253 void checkNeighboringBlockColors() const 254 { 255 SLIC_DEBUG("--Checking that inside blocks do not neighbor outside blocks"); 256 for(int lev = m_octree.maxLeafLevel() - 1; lev >= 0; --lev) 257 { 258 const auto& levelLeafMap = m_octree.getOctreeLevel(lev); 259 auto itEnd = levelLeafMap.end(); 260 for(auto it = levelLeafMap.begin(); it != itEnd; ++it) 261 { 262 const BlockIndex block(it.pt(), lev); 263 const InOutBlockData& data = *it; 264 265 if(data.isLeaf() && data.color() != InOutBlockData::Gray) 266 { 267 // When leaf is inside or outside -- face neighbors need same label 268 for(int i = 0; i < block.numFaceNeighbors(); ++i) 269 { 270 BlockIndex neighborBlk = 271 m_octree.coveringLeafBlock(block.faceNeighbor(i)); 272 if(neighborBlk != BlockIndex::invalid_index()) 273 { 274 const InOutBlockData& neighborData = m_octree[neighborBlk]; 275 switch(neighborData.color()) 276 { 277 case InOutBlockData::Black: // intentional fallthrough 278 case InOutBlockData::White: 279 SLIC_CHECK_MSG(data.color() == neighborData.color(), 280 fmt::format("Problem at block {} with data {} " 281 "--- neighbor {} with data {}." 282 " Neighboring leaves that are not " 283 "gray must have the same color.", 284 block, 285 data, 286 neighborBlk, 287 neighborData)); 288 break; 289 case InOutBlockData::Gray: // intentional fallthrough 290 case InOutBlockData::Undetermined: 291 break; 292 } 293 } 294 } 295 } 296 } 297 } 298 } 299 checkValid() const300 void checkValid() const 301 { 302 // We are assumed to be valid before we insert the vertices 303 if(m_generationState < InOutOctreeType::INOUTOCTREE_VERTICES_INSERTED) 304 return; 305 306 // Iterate through the tree 307 // Internal blocks should not have associated vertex data 308 // Leaf block consistency depends on 'color' 309 // Black or White blocks should not have vertex data 310 // Gray blocks should have a vertex reference; not necessarily located within the block 311 // Gray blocks should have one or more cells. 312 // All cells should be incident in a common vertex -- the indexed vertex -- if it exists. 313 // The total sum of vertices located within a block should equal the number of mesh vertices. 314 315 if(m_generationState > InOutOctreeType::INOUTOCTREE_VERTICES_INSERTED) 316 { 317 checkEachVertexIsIndexed(); 318 } 319 320 if(m_generationState >= InOutOctreeType::INOUTOCTREE_ELEMENTS_INSERTED) 321 { 322 checkCellsReferencedInBoundaryVertexBlocks(); 323 checkBlockIndexingConsistency(); 324 } 325 326 if(m_generationState >= InOutOctreeType::INOUTOCTREE_LEAVES_COLORED) 327 { 328 checkAllLeavesColored(); 329 checkNeighboringBlockColors(); 330 } 331 } 332 333 private: 334 const InOutOctreeType& m_octree; 335 typename InOutOctreeType::GenerationState m_generationState; 336 }; 337 338 } // namespace detail 339 } // namespace quest 340 } // namespace axom 341 342 #endif // AXOM_QUEST_INOUT_OCTREE_VALIDATOR__HPP_ 343