Chaste Release::3.1
|
00001 /* 00002 00003 Copyright (c) 2005-2012, University of Oxford. 00004 All rights reserved. 00005 00006 University of Oxford means the Chancellor, Masters and Scholars of the 00007 University of Oxford, having an administrative office at Wellington 00008 Square, Oxford OX1 2JD, UK. 00009 00010 This file is part of Chaste. 00011 00012 Redistribution and use in source and binary forms, with or without 00013 modification, are permitted provided that the following conditions are met: 00014 * Redistributions of source code must retain the above copyright notice, 00015 this list of conditions and the following disclaimer. 00016 * Redistributions in binary form must reproduce the above copyright notice, 00017 this list of conditions and the following disclaimer in the documentation 00018 and/or other materials provided with the distribution. 00019 * Neither the name of the University of Oxford nor the names of its 00020 contributors may be used to endorse or promote products derived from this 00021 software without specific prior written permission. 00022 00023 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 00024 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 00025 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 00026 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 00027 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 00028 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 00029 GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 00030 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 00031 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 00032 OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 00033 00034 */ 00035 00036 #include "QuadraticMeshHelper.hpp" 00037 00038 #define SEEK_TO_CONTENT(methNameDirect, methNameIncrement, index) \ 00039 if (index > 0u) { \ 00040 if (rMeshReader.IsFileFormatBinary()) { \ 00041 rMeshReader.methNameDirect(index - 1u); \ 00042 } else { \ 00043 for (unsigned i=0; i<index-1u; ++i) { \ 00044 rMeshReader.methNameIncrement(); \ 00045 } } } 00046 00047 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM> 00048 void SeekToBoundaryElement(AbstractMeshReader<ELEMENT_DIM, SPACE_DIM>& rMeshReader, 00049 unsigned boundaryElementIndex) 00050 { 00051 SEEK_TO_CONTENT(GetFaceData, GetNextFaceData, boundaryElementIndex); 00052 } 00053 00054 template<unsigned DIM> 00055 void QuadraticMeshHelper<DIM>::AddInternalNodesToElements(AbstractTetrahedralMesh<DIM, DIM>* pMesh, 00056 TrianglesMeshReader<DIM,DIM>* pMeshReader) 00057 { 00058 assert(pMesh); 00059 assert(pMeshReader); 00060 00061 if (pMesh->GetNumLocalElements() > 0u) 00062 { 00063 pMeshReader->Reset(); 00064 00065 // Create a set of element indices we own 00066 std::set<unsigned> owned_element_indices; 00067 for (typename AbstractTetrahedralMesh<DIM,DIM>::ElementIterator iter = pMesh->GetElementIteratorBegin(); 00068 iter != pMesh->GetElementIteratorEnd(); 00069 ++iter) 00070 { 00071 owned_element_indices.insert(iter->GetIndex()); 00072 } 00073 00074 const std::vector<unsigned>& r_node_perm = pMesh->rGetNodePermutation(); 00075 00076 // Add the extra nodes (1 extra node in 1D, 3 in 2D, 6 in 3D) to the element data 00077 for (typename AbstractMeshReader<DIM,DIM>::ElementIterator iter = pMeshReader->GetElementIteratorBegin(owned_element_indices); 00078 iter != pMeshReader->GetElementIteratorEnd(); 00079 ++iter) 00080 { 00081 std::vector<unsigned> nodes = iter->NodeIndices; 00082 assert(nodes.size()==(DIM+1)*(DIM+2)/2); 00083 Element<DIM,DIM>* p_element = pMesh->GetElement(iter.GetIndex()); 00084 assert(p_element->GetNumNodes()==DIM+1); // Element is initially linear 00085 00086 // Add extra nodes to make it a quad element 00087 for (unsigned j=DIM+1; j<(DIM+1)*(DIM+2)/2; j++) 00088 { 00089 unsigned node_index = nodes[j]; 00090 if (!r_node_perm.empty()) 00091 { 00092 node_index = r_node_perm[node_index]; 00093 } 00094 Node<DIM>* p_node = pMesh->GetNodeOrHaloNode(node_index); 00095 p_element->AddNode(p_node); 00096 p_node->AddElement(p_element->GetIndex()); 00097 p_node->MarkAsInternal(); 00098 } 00099 } 00100 } 00101 } 00102 00103 template<unsigned DIM> 00104 void QuadraticMeshHelper<DIM>::AddInternalNodesToBoundaryElements(AbstractTetrahedralMesh<DIM, DIM>* pMesh, 00105 TrianglesMeshReader<DIM,DIM>* pMeshReader) 00106 { 00107 assert(pMesh); 00108 assert(pMeshReader); 00109 // We only have boundary elements in 2d or 3d 00110 if (DIM > 1 && pMesh->GetNumLocalBoundaryElements() > 0u) 00111 { 00112 // If the data is on disk our job is easy 00113 if (pMeshReader->GetOrderOfBoundaryElements() == 2u) 00114 { 00115 // The work should have been done in the linear constructor, but let's check 00116 // that the first face has more than DIM nodes. 00117 assert((*pMesh->GetBoundaryElementIteratorBegin())->GetNumNodes()==DIM*(DIM+1)/2); 00118 return; 00119 } 00120 else 00121 { 00122 AddNodesToBoundaryElements(pMesh, pMeshReader); 00123 } 00124 } 00125 } 00126 00127 template<unsigned DIM> 00128 void QuadraticMeshHelper<DIM>::AddNodesToBoundaryElements(AbstractTetrahedralMesh<DIM, DIM>* pMesh, 00129 TrianglesMeshReader<DIM,DIM>* pMeshReader) 00130 { 00131 // Loop over all boundary elements, find the equivalent face from all 00132 // the elements, and add the extra nodes to the boundary element 00133 bool boundary_element_file_has_containing_element_info = false; 00134 00135 if (pMeshReader) 00136 { 00137 boundary_element_file_has_containing_element_info = pMeshReader->GetReadContainingElementOfBoundaryElement(); 00138 } 00139 00140 if (DIM > 1) 00141 { 00142 if (boundary_element_file_has_containing_element_info) 00143 { 00144 pMeshReader->Reset(); 00145 } 00146 00148 // we may need to skip through the boundary element file searching for containing elements hints. 00149 // This counter keeps track of our position in the file. 00150 unsigned next_face_on_file = 0u; 00151 00152 for (typename AbstractTetrahedralMesh<DIM,DIM>::BoundaryElementIterator iter 00153 = pMesh->GetBoundaryElementIteratorBegin(); 00154 iter != pMesh->GetBoundaryElementIteratorEnd(); 00155 ++iter) 00156 { 00157 00158 // collect the nodes of this boundary element in a set 00159 std::set<unsigned> boundary_element_node_indices; 00160 for (unsigned i=0; i<DIM; i++) 00161 { 00162 boundary_element_node_indices.insert( (*iter)->GetNodeGlobalIndex(i) ); 00163 } 00164 00165 bool found_this_boundary_element = false; 00166 00167 // Loop over elements surrounding this face, then loop over each face of that element, and see if it matches 00168 // this boundary element. 00169 // Note, if we know what elem it should be in (boundary_element_file_has_containing_element_info==true) 00170 // we will reset elem_index immediately (below) 00171 Node<DIM>* p_representative_node = (*iter)->GetNode(0); 00172 for (typename Node<DIM>::ContainingElementIterator element_iter = p_representative_node->ContainingElementsBegin(); 00173 element_iter != p_representative_node->ContainingElementsEnd(); 00174 ++element_iter) 00175 { 00176 unsigned elem_index = *element_iter; 00177 00178 // We know what elem it should be in (but we'll still check the node indices match in case) 00179 if (boundary_element_file_has_containing_element_info) 00180 { 00181 unsigned face_index = (*iter)->GetIndex(); 00183 do 00184 { 00185 elem_index = pMeshReader->GetNextFaceData().ContainingElement; 00186 next_face_on_file++; 00187 } 00188 while (face_index >= next_face_on_file); 00189 } 00190 00191 Element<DIM,DIM>* p_element = pMesh->GetElement(elem_index); 00192 00193 // For each element, loop over faces (the opposites to a node) 00194 for (unsigned face=0; face<DIM+1; face++) 00195 { 00196 // Collect the node indices for this face 00197 std::set<unsigned> node_indices; 00198 for (unsigned local_node_index=0; local_node_index<DIM+1; local_node_index++) 00199 { 00200 if (local_node_index != face) 00201 { 00202 node_indices.insert( p_element->GetNodeGlobalIndex(local_node_index) ); 00203 } 00204 } 00205 00206 assert(node_indices.size()==DIM); 00207 00208 // See if this face matches the boundary element, and add internal nodes if so 00209 if (node_indices == boundary_element_node_indices) 00210 { 00211 QuadraticMeshHelper<DIM>::AddExtraBoundaryNodes(pMesh, *iter, p_element, face); 00212 00213 found_this_boundary_element = true; 00214 break; 00215 } 00216 } 00217 00218 // If the containing element info was given, we must have found the face first time 00219 if (boundary_element_file_has_containing_element_info && !found_this_boundary_element) 00220 { 00221 #define COVERAGE_IGNORE 00222 //std::cout << (*iter)->GetIndex() << " " << pMeshReader->GetNextFaceData().ContainingElement << "\n"; 00223 EXCEPTION("Boundary element " << (*iter)->GetIndex() 00224 << "wasn't found in the containing element given for it " 00225 << elem_index); 00226 #undef COVERAGE_IGNORE 00227 } 00228 00229 if (found_this_boundary_element) 00230 { 00231 break; 00232 } 00233 } 00234 00235 if (!found_this_boundary_element) 00236 { 00237 #define COVERAGE_IGNORE 00238 EXCEPTION("Unable to find a face of an element which matches one of the boundary elements"); 00239 #undef COVERAGE_IGNORE 00240 } 00241 } 00242 } 00243 } 00244 00245 template<unsigned DIM> 00246 void QuadraticMeshHelper<DIM>::CheckBoundaryElements(AbstractTetrahedralMesh<DIM, DIM>* pMesh) 00247 { 00248 #ifndef NDEBUG 00249 unsigned expected_num_nodes = DIM*(DIM+1)/2; 00250 for (typename AbstractTetrahedralMesh<DIM,DIM>::BoundaryElementIterator iter 00251 = pMesh->GetBoundaryElementIteratorBegin(); 00252 iter != pMesh->GetBoundaryElementIteratorEnd(); 00253 ++iter) 00254 { 00255 assert((*iter)->GetNumNodes() == expected_num_nodes); 00256 } 00257 #endif 00258 } 00259 00260 template<unsigned DIM> 00261 void QuadraticMeshHelper<DIM>::AddNodeToBoundaryElement(AbstractTetrahedralMesh<DIM, DIM>* pMesh, 00262 BoundaryElement<DIM-1,DIM>* pBoundaryElement, 00263 Node<DIM>* pNode) 00264 { 00265 assert(DIM > 1); 00266 00267 // Add node to the boundary node list 00268 if (!pNode->IsBoundaryNode()) 00269 { 00270 pNode->SetAsBoundaryNode(); 00271 pMesh->mBoundaryNodes.push_back(pNode); 00272 } 00273 // Add it to the boundary element 00274 pBoundaryElement->AddNode(pNode); 00275 } 00276 00277 template<unsigned DIM> 00278 void QuadraticMeshHelper<DIM>::AddNodeToBoundaryElement(AbstractTetrahedralMesh<DIM, DIM>* pMesh, 00279 BoundaryElement<DIM-1,DIM>* pBoundaryElement, 00280 Element<DIM,DIM>* pElement, 00281 unsigned internalNode) 00282 { 00283 assert(DIM > 1); 00284 assert(internalNode >= DIM+1); 00285 assert(internalNode < (DIM+1)*(DIM+2)/2); 00286 Node<DIM>* p_internal_node = pElement->GetNode(internalNode); 00287 AddNodeToBoundaryElement(pMesh, pBoundaryElement, p_internal_node); 00288 } 00289 00290 template<unsigned DIM> 00291 void QuadraticMeshHelper<DIM>::AddExtraBoundaryNodes(AbstractTetrahedralMesh<DIM, DIM>* pMesh, 00292 BoundaryElement<DIM-1,DIM>* pBoundaryElement, 00293 Element<DIM,DIM>* pElement, 00294 unsigned nodeIndexOppositeToFace) 00295 { 00296 assert(DIM!=1); 00297 if (DIM==2) 00298 { 00299 assert(nodeIndexOppositeToFace<3); 00300 // the single internal node of the element's face will be numbered 'face+3' 00301 AddNodeToBoundaryElement(pMesh, pBoundaryElement, pElement, nodeIndexOppositeToFace+3); 00302 } 00303 else 00304 { 00305 assert(DIM==3); 00306 00307 unsigned b_elem_n0 = pBoundaryElement->GetNodeGlobalIndex(0); 00308 unsigned b_elem_n1 = pBoundaryElement->GetNodeGlobalIndex(1); 00309 00310 unsigned offset; 00311 bool reverse; 00312 00313 if (nodeIndexOppositeToFace==0) 00314 { 00315 // face opposite to node 0 = {1,2,3}, with corresponding internals {9,8,5} 00316 HelperMethod1(b_elem_n0, b_elem_n1, pElement, 1, 2, 3, offset, reverse); 00317 HelperMethod2(pMesh, pBoundaryElement, pElement, 9, 8, 5, offset, reverse); 00318 } 00319 else if (nodeIndexOppositeToFace==1) 00320 { 00321 // face opposite to node 1 = {2,0,3}, with corresponding internals {7,9,6} 00322 HelperMethod1(b_elem_n0, b_elem_n1, pElement, 2, 0, 3, offset, reverse); 00323 HelperMethod2(pMesh, pBoundaryElement, pElement, 7, 9, 6, offset, reverse); 00324 } 00325 else if (nodeIndexOppositeToFace==2) 00326 { 00327 // face opposite to node 2 = {0,1,3}, with corresponding internals {8,7,4} 00328 HelperMethod1(b_elem_n0, b_elem_n1, pElement, 0, 1, 3, offset, reverse); 00329 HelperMethod2(pMesh, pBoundaryElement, pElement, 8, 7, 4, offset, reverse); 00330 } 00331 else 00332 { 00333 assert(nodeIndexOppositeToFace==3); 00334 // face opposite to node 3 = {0,1,2}, with corresponding internals {5,6,4} 00335 HelperMethod1(b_elem_n0, b_elem_n1, pElement, 0, 1, 2, offset, reverse); 00336 HelperMethod2(pMesh, pBoundaryElement, pElement, 5, 6, 4, offset, reverse); 00337 } 00338 } 00339 } 00340 00342 // two unpleasant helper methods for AddExtraBoundaryNodes() 00344 00345 #define COVERAGE_IGNORE /// \todo These helper methods aren't properly covered 00346 template<unsigned DIM> 00347 void QuadraticMeshHelper<DIM>::HelperMethod1(unsigned boundaryElemNode0, unsigned boundaryElemNode1, 00348 Element<DIM,DIM>* pElement, 00349 unsigned node0, unsigned node1, unsigned node2, 00350 unsigned& rOffset, 00351 bool& rReverse) 00352 { 00353 if (pElement->GetNodeGlobalIndex(node0)==boundaryElemNode0) 00354 { 00355 rOffset = 0; 00356 if (pElement->GetNodeGlobalIndex(node1)==boundaryElemNode1) 00357 { 00358 rReverse = false; 00359 } 00360 else 00361 { 00362 assert(pElement->GetNodeGlobalIndex(node2)==boundaryElemNode1); 00363 rReverse = true; 00364 } 00365 } 00366 else if (pElement->GetNodeGlobalIndex(node1)==boundaryElemNode0) 00367 { 00368 rOffset = 1; 00369 if (pElement->GetNodeGlobalIndex(node2)==boundaryElemNode1) 00370 { 00371 rReverse = false; 00372 } 00373 else 00374 { 00375 assert(pElement->GetNodeGlobalIndex(node0)==boundaryElemNode1); 00376 rReverse = true; 00377 } 00378 } 00379 else 00380 { 00381 assert(pElement->GetNodeGlobalIndex(node2)==boundaryElemNode0); 00382 rOffset = 2; 00383 if (pElement->GetNodeGlobalIndex(node0)==boundaryElemNode1) 00384 { 00385 rReverse = false; 00386 } 00387 else 00388 { 00389 assert(pElement->GetNodeGlobalIndex(node1)==boundaryElemNode1); 00390 rReverse = true; 00391 } 00392 } 00393 } 00394 #undef COVERAGE_IGNORE /// \todo These helper methods aren't properly covered 00395 00396 00397 #define COVERAGE_IGNORE /// \todo These helper methods aren't properly covered 00398 template<unsigned DIM> 00399 void QuadraticMeshHelper<DIM>::HelperMethod2(AbstractTetrahedralMesh<DIM, DIM>* pMesh, 00400 BoundaryElement<DIM-1,DIM>* pBoundaryElement, 00401 Element<DIM,DIM>* pElement, 00402 unsigned internalNode0, unsigned internalNode1, unsigned internalNode2, 00403 unsigned offset, 00404 bool reverse) 00405 { 00406 if (offset==1) 00407 { 00408 unsigned temp = internalNode0; 00409 internalNode0 = internalNode1; 00410 internalNode1 = internalNode2; 00411 internalNode2 = temp; 00412 } 00413 else if (offset == 2) 00414 { 00415 unsigned temp = internalNode0; 00416 internalNode0 = internalNode2; 00417 internalNode2 = internalNode1; 00418 internalNode1 = temp; 00419 } 00420 00421 if (reverse) 00422 { 00423 unsigned temp = internalNode1; 00424 internalNode1 = internalNode2; 00425 internalNode2 = temp; 00426 } 00427 00428 AddNodeToBoundaryElement(pMesh, pBoundaryElement, pElement, internalNode0); 00429 AddNodeToBoundaryElement(pMesh, pBoundaryElement, pElement, internalNode1); 00430 AddNodeToBoundaryElement(pMesh, pBoundaryElement, pElement, internalNode2); 00431 } 00432 #undef COVERAGE_IGNORE /// \todo These helper methods aren't properly covered 00433 00435 // Explicit instantiation 00437 00438 template class QuadraticMeshHelper<1>; 00439 template class QuadraticMeshHelper<2>; 00440 template class QuadraticMeshHelper<3>;