Chaste Release::3.1
QuadraticMeshHelper.cpp
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>;