QuadraticMesh.cpp

00001 /*
00002 
00003 Copyright (C) University of Oxford, 2005-2009
00004 
00005 University of Oxford means the Chancellor, Masters and Scholars of the
00006 University of Oxford, having an administrative office at Wellington
00007 Square, Oxford OX1 2JD, UK.
00008 
00009 This file is part of Chaste.
00010 
00011 Chaste is free software: you can redistribute it and/or modify it
00012 under the terms of the GNU Lesser General Public License as published
00013 by the Free Software Foundation, either version 2.1 of the License, or
00014 (at your option) any later version.
00015 
00016 Chaste is distributed in the hope that it will be useful, but WITHOUT
00017 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00018 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
00019 License for more details. The offer of Chaste under the terms of the
00020 License is subject to the License being interpreted in accordance with
00021 English Law and subject to any action against the University of Oxford
00022 being under the jurisdiction of the English Courts.
00023 
00024 You should have received a copy of the GNU Lesser General Public License
00025 along with Chaste. If not, see <http://www.gnu.org/licenses/>.
00026 
00027 */
00028 
00029 #include "QuadraticMesh.hpp"
00030 
00031 template<unsigned DIM>
00032 QuadraticMesh<DIM>::QuadraticMesh(const std::string& fileName)
00033 {
00034     LoadFromFile(fileName);
00035 }
00036 
00037 template<unsigned DIM>
00038 QuadraticMesh<DIM>::QuadraticMesh(double xEnd, double yEnd, unsigned numElemX, unsigned numElemY)
00039 {
00040     assert(DIM==2);
00041     
00042     assert(xEnd>0);
00043     assert(yEnd>0);
00044     assert(numElemX>0);
00045     assert(numElemY>0);
00046 
00047     std::string tempfile_name_stem = "temp_quadmesh";
00048 
00050     // write the node file (vertices only)
00052     OutputFileHandler handler("");
00053     out_stream p_file = handler.OpenOutputFile(tempfile_name_stem+".node");
00054     
00055     *p_file << (numElemX+1)*(numElemY+1) << " 2 0 1\n";
00056     unsigned node_index = 0;
00057     for(unsigned j=0; j<=numElemY; j++)
00058     {
00059         for(unsigned i=0; i<=numElemX; i++)
00060         {
00061             double x = xEnd*i/numElemX;
00062             double y = yEnd*j/numElemY;
00063             
00064             bool on_boundary = ( (i==0) || (i==numElemX) || (j==0) || (j==numElemX) );
00065             *p_file << node_index++ << " " << x << " " << y << " " << (on_boundary?1:0) << "\n";
00066         }
00067     }
00068     p_file->close();
00069     
00071     // create the quadratic mesh files using triangle and load
00073      
00074     RunMesherAndReadMesh("triangle", handler.GetOutputDirectoryFullPath(), tempfile_name_stem);
00075 }
00076 
00077 
00078 template<unsigned DIM>
00079 QuadraticMesh<DIM>::QuadraticMesh(double xEnd, double yEnd, double zEnd, 
00080                                   unsigned numElemX, unsigned numElemY, unsigned numElemZ)
00081 {
00082     assert(DIM==3);
00083     
00084     assert(xEnd>0);
00085     assert(yEnd>0);
00086     assert(zEnd>0);
00087     assert(numElemX>0);
00088     assert(numElemY>0);
00089     assert(numElemZ>0);
00090 
00091     std::string tempfile_name_stem = "temp_quadmesh3d";
00092 
00094     // write the node file (vertices only)
00096     OutputFileHandler handler("");
00097     out_stream p_file = handler.OpenOutputFile(tempfile_name_stem+".node");
00098     
00099     *p_file << (numElemX+1)*(numElemY+1)*(numElemZ+1) << " 3 0 0\n";
00100     unsigned node_index = 0;
00101     for(unsigned k=0; k<=numElemZ; k++)
00102     {
00103         for(unsigned j=0; j<=numElemY; j++)
00104         {
00105             for(unsigned i=0; i<=numElemX; i++)
00106             {
00107                 double x = xEnd*i/numElemX;
00108                 double y = yEnd*j/numElemY;
00109                 double z = zEnd*k/numElemZ; //Not yEnd!
00110                 
00111                 //bool on_boundary = ( (i==0) || (i==numElemX) || (j==0) || (j==numElemY) || (k==0) || (k==numElemZ) );
00112                 *p_file << node_index++ << " " << x << " " << y << " " << z << "\n"; // << (on_boundary?1:0) << "\n";
00113             }
00114         }
00115     }
00116     p_file->close();
00117     
00119     // create the quadratic mesh files using triangle and load
00121     
00122 
00123     RunMesherAndReadMesh("tetgen", handler.GetOutputDirectoryFullPath(), tempfile_name_stem);
00124 }
00125 
00126 
00127 
00128 template<unsigned DIM>
00129 void QuadraticMesh<DIM>::RunMesherAndReadMesh(std::string binary, 
00130                                               std::string outputDir, 
00131                                               std::string fileStem)
00132 {
00133     // Q = quiet, e = make edge data, o2 = order of elements is 2, ie quadratics
00134     std::string args = "-Qeo2";
00135     
00136     // In 2D we need an edge file. In 3D we need a face file (which is written automatically in Tetgen)
00137     if (DIM == 3)
00138     {
00139         args = "-Qo2";   
00140     }
00141         
00142     std::string command =  binary + " " + args + " " + outputDir
00143                            + "/" + fileStem + ".node";
00144     
00145     if (DIM == 3)
00146     {
00147         // Tetgen's quiet mode isn't as quiet as Triangle's
00148         command += " > /dev/null";
00149     }
00150                  
00151     int return_value = system(command.c_str());
00152  
00153     if(return_value != 0)
00154     {
00155         #define COVERAGE_IGNORE
00156         EXCEPTION("Remeshing (by calling " + binary + ") failed.  Do you have it in your path?\n"+
00157         "The quadratic mesh relies on functionality from triangle (http://www.cs.cmu.edu/~quake/triangle.html) and tetgen (http://tetgen.berlios.de/).");
00158         #undef COVERAGE_IGNORE 
00159     }
00160     
00161     // move the output files to the chaste directory
00162     command =   "mv " + outputDir + "/" 
00163               + fileStem + ".1.* .";
00164     
00165     // NOTE: we don't check whether the return value here is zero, because if CHASTE_TESTOUTPUT
00166     // is "." (ie if it hasn't been exported), then the mv will fail (source and destination files
00167     // are the same), but this isn't a problem.
00168     return_value = system(command.c_str());
00169     
00170     // load
00171     LoadFromFile( fileStem + ".1");
00172     
00173     // delete the temporary files
00174     command = "rm -f " + outputDir + "/" + fileStem + ".node";
00175     EXPECT0(system, command);
00176     EXPECT0(system, "rm -f " + fileStem + ".1.node");
00177     EXPECT0(system, "rm -f " + fileStem + ".1.ele");
00178     
00179     if (DIM==2) 
00180     {
00181         EXPECT0(system, "rm -f " + fileStem + ".1.edge");
00182     }
00183     if (DIM==3) 
00184     {
00185         EXPECT0(system, "rm -f " + fileStem + ".1.face");
00186     }
00187 }
00188 
00189 
00190 template<unsigned DIM>
00191 void QuadraticMesh<DIM>::LoadFromFile(const std::string& fileName)
00192 {
00193     TrianglesMeshReader<DIM,DIM> mesh_reader(fileName, 2); // 2=quadratic mesh
00194 
00195     ConstructFromMeshReader(mesh_reader);
00196 
00197     // set up the information on whether a node is an internal node or not (if not,
00198     // it'll be a vertex)
00199     mIsInternalNode.resize(this->GetNumNodes(), true);
00200     for(unsigned elem_index=0; elem_index<this->GetNumElements(); elem_index++)
00201     {
00202         for(unsigned i=0; i<DIM+1 /*num vertices*/; i++)
00203         {
00204             unsigned node_index = this->GetElement(elem_index)->GetNodeGlobalIndex(i);
00205             mIsInternalNode[ node_index ] = false;
00206         }
00207     }
00208     
00209     // count the number of vertices, and also check all vertices come before the 
00210     // rest of the nodes (as this is assumed in other parts of the code)
00211     mNumVertices = 0;
00212     bool vertices_mode = true;
00213     for(unsigned i=0; i<this->GetNumNodes(); i++)
00214     {
00215         if(mIsInternalNode[i]==false)
00216         {
00217             mNumVertices++;
00218         }
00219         if((vertices_mode == false)  && (mIsInternalNode[i]==false ) )
00220         {
00221             EXCEPTION("The quadratic mesh doesn't appear to have all vertices before the rest of the nodes");
00222         }
00223         if( (vertices_mode == true)  && (mIsInternalNode[i]==true) )
00224         {
00225             vertices_mode = false;
00226         }
00227     }
00228         
00229     
00230     mesh_reader.Reset();
00231 
00232     // add the extra nodes (1 extra node in 1D, 3 in 2D, 6 in 3D) to the element
00233     // data.
00234     for(unsigned i=0; i<this->GetNumElements(); i++)
00235     {
00236         std::vector<unsigned> nodes = mesh_reader.GetNextElementData().NodeIndices;
00237         assert(nodes.size()==(DIM+1)*(DIM+2)/2);
00238         for(unsigned j=DIM+1; j<(DIM+1)*(DIM+2)/2; j++)
00239         {
00240             this->GetElement(i)->AddNode( this->GetNode(nodes[j]) );
00241             this->GetNode(nodes[j])->AddElement(this->GetElement(i)->GetIndex());
00242         }
00243     }
00244     
00245     // Loop over all boundary elements, find the equivalent face from all
00246     // the elements, and add the extra nodes to the boundary element
00247     if(DIM>1)
00248     {
00249         for(typename TetrahedralMesh<DIM,DIM>::BoundaryElementIterator iter
00250               = this->GetBoundaryElementIteratorBegin();
00251             iter != this->GetBoundaryElementIteratorEnd();
00252             ++iter)
00253         {
00254             // collect the nodes of this boundary element in a set        
00255             std::set<unsigned> boundary_element_node_indices;
00256             for(unsigned i=0; i<DIM; i++)
00257             {
00258                 boundary_element_node_indices.insert( (*iter)->GetNodeGlobalIndex(i) );
00259             }
00260     
00261             bool found_this_boundary_element = false;
00262 
00263             // loop over elements
00264             for(unsigned i=0; i<this->GetNumElements(); i++)
00265             {
00266                 Element<DIM,DIM>* p_element = this->GetElement(i);
00267                 
00268                 // for each element, loop over faces (the opposites to a node)
00269                 for(unsigned face=0; face<DIM+1; face++)
00270                 {
00271                     // collect the node indices for this face
00272                     std::set<unsigned> node_indices;
00273                     for(unsigned local_node_index=0; local_node_index<DIM+1; local_node_index++)
00274                     {  
00275                         if(local_node_index!=face)
00276                         {
00277                             node_indices.insert( p_element->GetNodeGlobalIndex(local_node_index) );
00278                         }
00279                     }
00280     
00281                     assert(node_indices.size()==DIM);
00282 
00283                     // see if this face matches the boundary element,
00284                     // and call AddExtraBoundaryNodes() if so
00285                     if(node_indices==boundary_element_node_indices)
00286                     {
00287                         AddExtraBoundaryNodes(*iter, p_element, face);
00288                         
00289                         found_this_boundary_element = true;
00290                         break;
00291                     }
00292                 }
00293     
00294                 if(found_this_boundary_element)
00295                 {
00296                     break;
00297                 }
00298             }
00299             
00300             if(!found_this_boundary_element)
00301             {
00302                 #define COVERAGE_IGNORE
00303                 EXCEPTION("Unable to find a face of an element which matches one of the boundary elements");
00304                 #undef COVERAGE_IGNORE
00305             }
00306         }
00307     }
00308 }
00309 
00310 
00311 template<unsigned DIM>
00312 void QuadraticMesh<DIM>::AddNodeToBoundaryElement(BoundaryElement<DIM-1,DIM>* pBoundaryElement,
00313                                                   Element<DIM,DIM>* pElement,
00314                                                   unsigned internalNode)
00315 {
00316     assert(DIM>1);
00317     assert(internalNode >= DIM+1);
00318     assert(internalNode < (DIM+1)*(DIM+2)/2);
00319     Node<DIM>* p_internal_node = pElement->GetNode(internalNode);
00320 
00321     // add node to the boundary node list   
00322     if(!p_internal_node->IsBoundaryNode())
00323     {
00324         p_internal_node->SetAsBoundaryNode();
00325         this->mBoundaryNodes.push_back(p_internal_node);
00326     }
00327 
00328     pBoundaryElement->AddNode( p_internal_node );        
00329 }
00330 
00331 
00332 template<unsigned DIM>
00333 void QuadraticMesh<DIM>::AddExtraBoundaryNodes(BoundaryElement<DIM-1,DIM>* pBoundaryElement,
00334                                                Element<DIM,DIM>* pElement,
00335                                                unsigned nodeIndexOppositeToFace)
00336 {
00337     assert(DIM!=1);
00338     if(DIM==2)
00339     {
00340         assert(nodeIndexOppositeToFace<3);
00341         // the single internal node of the elements face will be numbered 'face+3'
00342         AddNodeToBoundaryElement(pBoundaryElement, pElement, nodeIndexOppositeToFace+3);
00343     }        
00344     else
00345     {
00346         assert(DIM==3);        
00347 
00348         unsigned b_elem_n0 = pBoundaryElement->GetNodeGlobalIndex(0);
00349         unsigned b_elem_n1 = pBoundaryElement->GetNodeGlobalIndex(1);
00350 
00351         unsigned offset;
00352         bool reverse;
00353 
00354         if(nodeIndexOppositeToFace==0)
00355         {
00356             // face opposite to node 0 = {1,2,3}, with corresponding internals {9,8,5}
00357             HelperMethod1(b_elem_n0, b_elem_n1, pElement, 1, 2, 3, offset, reverse);
00358             HelperMethod2(pBoundaryElement, pElement, 9, 8, 5, offset, reverse);
00359         }
00360         else if(nodeIndexOppositeToFace==1)
00361         {
00362             // face opposite to node 1 = {2,0,3}, with corresponding internals {7,9,6}
00363             HelperMethod1(b_elem_n0, b_elem_n1, pElement, 2, 0, 3, offset, reverse);
00364             HelperMethod2(pBoundaryElement, pElement, 7, 9, 6, offset, reverse);
00365         }
00366         else if(nodeIndexOppositeToFace==2)
00367         {
00368             // face opposite to node 2 = {0,1,3}, with corresponding internals {8,7,4}
00369             HelperMethod1(b_elem_n0, b_elem_n1, pElement, 0, 1, 3, offset, reverse);
00370             HelperMethod2(pBoundaryElement, pElement, 8, 7, 4, offset, reverse);
00371         }
00372         else
00373         {
00374             assert(nodeIndexOppositeToFace==3);
00375             // face opposite to node 3 = {0,1,2}, with corresponding internals {5,6,4}
00376             HelperMethod1(b_elem_n0, b_elem_n1, pElement, 0, 1, 2, offset, reverse);
00377             HelperMethod2(pBoundaryElement, pElement, 5, 6, 4, offset, reverse);
00378         }
00379     }
00380 }
00381 
00382 
00384 // two unpleasant helper methods for AddExtraBoundaryNodes()
00386 
00396 #define COVERAGE_IGNORE 
00397 template<unsigned DIM>
00398 void QuadraticMesh<DIM>::HelperMethod1(unsigned boundaryElemNode0, unsigned boundaryElemNode1,
00399                                        Element<DIM,DIM>* pElement,
00400                                        unsigned node0, unsigned node1, unsigned node2,
00401                                        unsigned& rOffset,
00402                                        bool& rReverse)
00403 {
00404     if(pElement->GetNodeGlobalIndex(node0)==boundaryElemNode0)
00405     {
00406         rOffset = 0;
00407         if(pElement->GetNodeGlobalIndex(node1)==boundaryElemNode1)
00408         {
00409             rReverse = false;
00410         }
00411         else
00412         {
00413             assert(pElement->GetNodeGlobalIndex(node2)==boundaryElemNode1);
00414             rReverse = true;
00415         }
00416     }
00417     else if(pElement->GetNodeGlobalIndex(node1)==boundaryElemNode0)
00418     {
00419         rOffset = 1;
00420         if(pElement->GetNodeGlobalIndex(node2)==boundaryElemNode1)
00421         {
00422             rReverse = false;
00423         }
00424         else 
00425         {
00426             assert(pElement->GetNodeGlobalIndex(node0)==boundaryElemNode1);
00427             rReverse = true;
00428         }
00429     }
00430     else
00431     {
00432         assert(pElement->GetNodeGlobalIndex(node2)==boundaryElemNode0);
00433         rOffset = 2;
00434         if(pElement->GetNodeGlobalIndex(node0)==boundaryElemNode1)
00435         {
00436             rReverse = false;
00437         }
00438         else
00439         {
00440             assert(pElement->GetNodeGlobalIndex(node1)==boundaryElemNode1);
00441             rReverse = true;
00442         }
00443     }
00444 }
00445 #undef COVERAGE_IGNORE 
00446 
00447 
00448 
00455 #define COVERAGE_IGNORE 
00456 template<unsigned DIM>
00457 void QuadraticMesh<DIM>::HelperMethod2(BoundaryElement<DIM-1,DIM>* pBoundaryElement,
00458                                        Element<DIM,DIM>* pElement,
00459                                        unsigned internalNode0, unsigned internalNode1, unsigned internalNode2,
00460                                        unsigned offset,
00461                                        bool reverse)
00462 {
00463     if(offset==1)
00464     {
00465         unsigned temp = internalNode0;
00466         internalNode0 = internalNode1;
00467         internalNode1 = internalNode2;
00468         internalNode2 = temp;
00469     }
00470     else if(offset == 2)
00471     {
00472         unsigned temp = internalNode0;
00473         internalNode0 = internalNode2;
00474         internalNode2 = internalNode1;
00475         internalNode1 = temp;
00476     }
00477     
00478     if(reverse)
00479     {
00480         unsigned temp = internalNode1;
00481         internalNode1 = internalNode2;
00482         internalNode2 = temp;
00483     }
00484     
00485     AddNodeToBoundaryElement(pBoundaryElement, pElement, internalNode0);
00486     AddNodeToBoundaryElement(pBoundaryElement, pElement, internalNode1);
00487     AddNodeToBoundaryElement(pBoundaryElement, pElement, internalNode2);
00488 }
00489 #undef COVERAGE_IGNORE 
00490 
00491 template class QuadraticMesh<1>;
00492 template class QuadraticMesh<2>;
00493 template class QuadraticMesh<3>;

Generated on Wed Mar 18 12:51:52 2009 for Chaste by  doxygen 1.5.5