TetrahedralMesh.cpp

00001 /*
00002 
00003 Copyright (C) University of Oxford, 2005-2010
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 "TetrahedralMesh.hpp"
00030 
00031 #include <iostream>
00032 #include <cassert>
00033 #include <sstream>
00034 #include <map>
00035 
00036 #include "BoundaryElement.hpp"
00037 #include "Element.hpp"
00038 #include "Exception.hpp"
00039 #include "Node.hpp"
00040 #include "OutputFileHandler.hpp"
00041 #include "PetscTools.hpp"
00042 #include "RandomNumberGenerator.hpp"
00043 
00044 //Jonathan Shewchuk's triangle and Hang Si's tetgen
00045 #define REAL double
00046 #define VOID void
00047 #include "triangle.h"
00048 #include "tetgen.h"
00049 #undef REAL
00050 #undef VOID
00051 
00052 #include "Debug.hpp"
00054 //   IMPLEMENTATION
00056 
00057 
00058 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00059 TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::TetrahedralMesh()
00060 {
00061     Clear();
00062 }
00063 
00064 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00065 void TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::ConstructFromMeshReader(
00066     AbstractMeshReader<ELEMENT_DIM, SPACE_DIM>& rMeshReader)
00067 {
00068     this->mMeshFileBaseName = rMeshReader.GetMeshFileBaseName();
00069 
00070     // Record number of corner nodes
00071     unsigned num_nodes = rMeshReader.GetNumNodes();
00072 
00073     // Reserve memory for nodes, so we don't have problems with pointers stored in
00074     // elements becoming invalid.
00075     this->mNodes.reserve(num_nodes);
00076 
00077     rMeshReader.Reset();
00078 
00079     //typename std::map<std::pair<unsigned,unsigned>,unsigned>::const_iterator iterator;
00080     //std::map<std::pair<unsigned,unsigned>,unsigned> internal_nodes_map;
00081 
00082     // Add corner nodes
00083     std::vector<double> coords;
00084     for (unsigned i=0; i < num_nodes; i++)
00085     {
00086         coords = rMeshReader.GetNextNode();
00087         this->mNodes.push_back(new Node<SPACE_DIM>(i, coords, false));
00088     }
00089 
00090     //unsigned new_node_index = mNumCornerNodes;
00091 
00092     rMeshReader.Reset();
00093     // Add elements
00094     //new_node_index = mNumCornerNodes;
00095     this->mElements.reserve(rMeshReader.GetNumElements());
00096 
00097     for (unsigned element_index=0; element_index < (unsigned) rMeshReader.GetNumElements(); element_index++)
00098     {
00099         ElementData element_data = rMeshReader.GetNextElementData();
00100         std::vector<Node<SPACE_DIM>*> nodes;
00101 
00102         // NOTE: currently just reading element vertices from mesh reader - even if it
00103         // does contain information about internal nodes (ie for quadratics) this is
00104         // ignored here and used elsewhere: ie don't do this:
00105         //   unsigned nodes_size = node_indices.size();
00106 
00107         for (unsigned j=0; j<ELEMENT_DIM+1; j++) // num vertices=ELEMENT_DIM+1, may not be equal to nodes_size.
00108         {
00109             assert(element_data.NodeIndices[j] <  this->mNodes.size());
00110             nodes.push_back(this->mNodes[element_data.NodeIndices[j]]);
00111         }
00112 
00113         Element<ELEMENT_DIM,SPACE_DIM>* p_element = new Element<ELEMENT_DIM,SPACE_DIM>(element_index, nodes);
00114         this->mElements.push_back(p_element);
00115 
00116         if (rMeshReader.GetNumElementAttributes() > 0)
00117         {
00118             assert(rMeshReader.GetNumElementAttributes() == 1);
00119             unsigned attribute_value = element_data.AttributeValue;
00120             p_element->SetRegion(attribute_value);
00121         }
00122     }
00123 
00124     // Add boundary elements and nodes
00125     for (unsigned face_index=0; face_index<(unsigned)rMeshReader.GetNumFaces(); face_index++)
00126     {
00127         ElementData face_data = rMeshReader.GetNextFaceData();
00128         std::vector<unsigned> node_indices = face_data.NodeIndices;
00129 
00130         // NOTE: as above just read boundary element *vertices* from mesh reader - even if
00131         // it is a quadratic mesh with internal elements, the extra nodes are ignored here
00132         // and used elsewhere: ie, we don't do this:
00133         //   unsigned nodes_size = node_indices.size();
00134 
00135         std::vector<Node<SPACE_DIM>*> nodes;
00136         for (unsigned node_index=0; node_index<ELEMENT_DIM; node_index++) // node_index from 0 to DIM-1, not 0 to node.size()-1
00137         {
00138             assert(node_indices[node_index] < this->mNodes.size());
00139             // Add Node pointer to list for creating an element
00140             nodes.push_back(this->mNodes[node_indices[node_index]]);
00141         }
00142 
00143         // This is a boundary face
00144         // Ensure all its nodes are marked as boundary nodes
00145 
00146         assert(nodes.size()==ELEMENT_DIM); // just taken vertices of boundary node from
00147         for (unsigned j=0; j<nodes.size(); j++)
00148         {
00149             if (!nodes[j]->IsBoundaryNode())
00150             {
00151                 nodes[j]->SetAsBoundaryNode();
00152                 this->mBoundaryNodes.push_back(nodes[j]);
00153             }
00154             //Register the index that this bounday element will have
00155             //with the node
00156             nodes[j]->AddBoundaryElement(face_index);
00157         }
00158 
00159         // The added elements will be deleted in our destructor
00160         BoundaryElement<ELEMENT_DIM-1,SPACE_DIM>* p_boundary_element = new BoundaryElement<ELEMENT_DIM-1,SPACE_DIM>(face_index, nodes);
00161         this->mBoundaryElements.push_back(p_boundary_element);
00162 
00163         if (rMeshReader.GetNumFaceAttributes() > 0)
00164         {
00165             assert(rMeshReader.GetNumFaceAttributes() == 1);
00166             unsigned attribute_value = face_data.AttributeValue;
00167             p_boundary_element->SetRegion(attribute_value);
00168         }
00169     }
00170 
00171     RefreshJacobianCachedData();
00172     rMeshReader.Reset();
00173 }
00174 
00175 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00176 void TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::ReadNodesPerProcessorFile(const std::string& rNodesPerProcessorFile)
00177 {
00178     std::vector<unsigned> nodes_per_processor_vec;
00179 
00180     std::ifstream file_stream(rNodesPerProcessorFile.c_str());
00181     if (file_stream.is_open())
00182     {
00183         while (file_stream)
00184         {
00185             unsigned nodes_per_processor;
00186             file_stream >> nodes_per_processor;
00187 
00188             if (file_stream)
00189             {
00190                 nodes_per_processor_vec.push_back(nodes_per_processor);
00191             }
00192         }
00193     }
00194     else
00195     {
00196         EXCEPTION("Unable to read nodes per processor file " + rNodesPerProcessorFile);
00197     }
00198 
00199     unsigned sum = 0;
00200     for (unsigned i=0; i<nodes_per_processor_vec.size(); i++)
00201     {
00202         sum += nodes_per_processor_vec[i];
00203     }
00204 
00205     if (sum != this->GetNumNodes())
00206     {
00207         std::stringstream string_stream;
00208         string_stream << "Sum of nodes per processor, " << sum
00209                      << ", not equal to number of nodes in mesh, " << this->GetNumNodes();
00210         EXCEPTION(string_stream.str());
00211     }
00212 
00213     unsigned num_owned=nodes_per_processor_vec[PetscTools::GetMyRank()];
00214 
00215     if (nodes_per_processor_vec.size() != PetscTools::GetNumProcs())
00216     {
00217         EXCEPTION("Number of processes doesn't match the size of the nodes-per-processor file");
00218     }
00219     delete this->mpDistributedVectorFactory;
00220     this->mpDistributedVectorFactory=new DistributedVectorFactory(this->GetNumNodes(), num_owned);
00221 }
00222 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00223 bool TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::CheckIsConforming()
00224 {
00225     /* Each face of each element is a set of node indices
00226      * We form a set of these in order to get their parity:
00227      *   all faces which appear once are inserted into the set
00228      *   all faces which appear twice are inserted and then removed from the set
00229      *   we're assuming that faces never appear more than twice
00230      */
00231     std::set< std::set<unsigned> > odd_parity_faces;
00232 
00233     for (typename AbstractTetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::ElementIterator iter = this->GetElementIteratorBegin();
00234          iter != this->GetElementIteratorEnd();
00235          ++iter)
00236     {
00237         for (unsigned face_index=0; face_index<=ELEMENT_DIM; face_index++)
00238         {
00239             std::set<unsigned> face_info;
00240             for (unsigned node_index=0; node_index<=ELEMENT_DIM; node_index++)
00241             {
00242                 //Leave one index out each time
00243                 if (node_index != face_index)
00244                 {
00245                     face_info.insert(iter->GetNodeGlobalIndex(node_index));
00246                 }
00247             }
00248             //Face is now formed - attempt to find it
00249             std::set< std::set<unsigned> >::iterator find_face=odd_parity_faces.find(face_info);
00250             if( find_face != odd_parity_faces.end())
00251             {
00252                 //Face was in set, so it now has even parity.
00253                 //Remove it via the iterator
00254                 odd_parity_faces.erase(find_face);
00255             }
00256             else
00257             {
00258                 //Face is not in set so it now has odd parity.  Insert it
00259                 odd_parity_faces.insert(face_info);
00260             }
00261 
00262         }
00263     }
00264     /* At this point the odd parity faces should be the same as the
00265      * boundary elements.  We could check this explicitly or we
00266      * could just count them.
00267      */
00268     return( odd_parity_faces.size() == this->GetNumBoundaryElements());
00269 }
00270 
00271 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00272 double TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::GetVolume()
00273 {
00274     double mesh_volume = 0.0;
00275 
00276     for (typename AbstractTetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::ElementIterator iter = this->GetElementIteratorBegin();
00277          iter != this->GetElementIteratorEnd();
00278          ++iter)
00279     {
00280         mesh_volume += iter->GetVolume(mElementJacobianDeterminants[iter->GetIndex()]);
00281     }
00282 
00283     return mesh_volume;
00284 }
00285 
00286 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00287 double TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::GetSurfaceArea()
00288 {
00289     //ELEMENT_DIM-1 is the dimension of the boundary element
00290     assert(ELEMENT_DIM >= 1);
00291     const unsigned bound_element_dim = ELEMENT_DIM-1;
00292     assert(bound_element_dim < 3);
00293     if ( bound_element_dim == 0)
00294     {
00295         return 0.0;
00296     }
00297 
00298     double mesh_surface = 0.0;
00299     typename TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::BoundaryElementIterator it = this->GetBoundaryElementIteratorBegin();
00300 
00301     while (it != this->GetBoundaryElementIteratorEnd())
00302     {
00303         mesh_surface += mBoundaryElementJacobianDeterminants[(*it)->GetIndex()];
00304         it++;
00305     }
00306 
00307     if ( bound_element_dim == 2)
00308     {
00309         mesh_surface /= 2.0;
00310     }
00311 
00312     return mesh_surface;
00313 }
00314 
00315 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00316 void TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::PermuteNodes()
00317 {
00318     //Make a permutation vector of the identity
00319     RandomNumberGenerator* p_rng = RandomNumberGenerator::Instance();
00320     std::vector<unsigned> perm;
00321     p_rng->Shuffle(this->mNodes.size(), perm);
00322     //Call the non-random version
00323     PermuteNodes(perm);
00324 }
00325 
00326 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00327 void TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::PermuteNodes(const std::vector<unsigned>& perm)
00328 {
00329     // Let's not do this if there are any deleted nodes
00330     assert( this->GetNumAllNodes() == this->GetNumNodes());
00331 
00332     assert(perm.size() == this->mNodes.size());
00333 
00334     // Copy the node pointers
00335     std::vector< Node<SPACE_DIM>* > copy_m_nodes;
00336     copy_m_nodes.assign(this->mNodes.begin(), this->mNodes.end());
00337 
00338     for (unsigned original_index=0; original_index<this->mNodes.size(); original_index++)
00339     {
00340         assert(perm[original_index] < this->mNodes.size());
00341         //perm[original_index] holds the new assigned index of that node
00342         this->mNodes[ perm[original_index] ] = copy_m_nodes[original_index];
00343     }
00344 
00345     // Update indices
00346     for (unsigned index=0; index<this->mNodes.size(); index++)
00347     {
00348         this->mNodes[index]->SetIndex(index);
00349     }
00350     //Copy the permutation vector into the mesh
00351     this->mNodesPermutation = perm;
00352 }
00353 
00354 
00355 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00356 unsigned TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::GetContainingElementIndex(const ChastePoint<SPACE_DIM>& rTestPoint,
00357                                                                             bool strict,
00358                                                                             std::set<unsigned> testElements,
00359                                                                             bool onlyTryWithTestElements)
00360 {
00361     for (std::set<unsigned>::iterator iter=testElements.begin(); iter!=testElements.end(); iter++)
00362     {
00363         assert(*iter<this->GetNumElements());
00364         if (this->mElements[*iter]->IncludesPoint(rTestPoint, strict))
00365         {
00366             assert(!this->mElements[*iter]->IsDeleted());
00367             return *iter;
00368         }
00369     }
00370 
00371     if(!onlyTryWithTestElements)
00372     {
00373         for (unsigned i=0; i<this->mElements.size(); i++)
00374         {
00375             if (this->mElements[i]->IncludesPoint(rTestPoint, strict))
00376             {
00377                 assert(!this->mElements[i]->IsDeleted());
00378                 return i;
00379             }
00380         }
00381     }
00382 
00383     // If it's in none of the elements, then throw
00384     std::stringstream ss;
00385     ss << "Point [";
00386     for(unsigned j=0; (int)j<(int)SPACE_DIM-1; j++)
00387     {
00388         ss << rTestPoint[j] << ",";
00389     }
00390     ss << rTestPoint[SPACE_DIM-1] << "] is not in ";
00391     if(!onlyTryWithTestElements)
00392     {
00393         ss << "mesh - all elements tested";
00394     }
00395     else
00396     {
00397         ss << "set of elements given";
00398     }
00399     EXCEPTION(ss.str());
00400 }
00401 
00402 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00403 unsigned TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::GetContainingElementIndexWithInitialGuess(const ChastePoint<SPACE_DIM>& rTestPoint, unsigned startingElementGuess, bool strict)
00404 {
00405     assert(startingElementGuess<this->GetNumElements());
00406 
00407     // let m=startingElementGuess, N=num_elem-1
00408     // We search from in this order: m, m+1, m+2, .. , N, 0, 1, .., m-1.
00409 
00410     unsigned i = startingElementGuess;
00411     bool reached_end = false;
00412 
00413     while(!reached_end)
00414     {
00415         if (this->mElements[i]->IncludesPoint(rTestPoint, strict))
00416         {
00417             assert(!this->mElements[i]->IsDeleted());
00418             return i;
00419         }
00420 
00421         // increment
00422         i++;
00423         if(i==this->GetNumElements())
00424         {
00425             i=0;
00426         }
00427 
00428         // back to the beginning yet?
00429         if(i==startingElementGuess)
00430         {
00431             reached_end = true;
00432         }
00433     }
00434 
00435     // If it's in none of the elements, then throw
00436     std::stringstream ss;
00437     ss << "Point [";
00438     for(unsigned j=0; (int)j<(int)SPACE_DIM-1; j++)
00439     {
00440         ss << rTestPoint[j] << ",";
00441     }
00442     ss << rTestPoint[SPACE_DIM-1] << "] is not in mesh - all elements tested";
00443     EXCEPTION(ss.str());
00444 }
00445 
00446 
00447 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00448 unsigned TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::GetNearestElementIndex(const ChastePoint<SPACE_DIM>& rTestPoint)
00449 {
00450     double max_min_weight = -INFINITY;
00451     unsigned closest_index = 0;
00452     for (unsigned i=0; i<this->mElements.size(); i++)
00453     {
00454         c_vector<double, ELEMENT_DIM+1> weight=this->mElements[i]->CalculateInterpolationWeights(rTestPoint);
00455         double neg_weight_sum=0.0;
00456         for (unsigned j=0; j<=ELEMENT_DIM; j++)
00457         {
00458             if (weight[j] < 0.0)
00459             {
00460                 neg_weight_sum += weight[j];
00461             }
00462         }
00463         if (neg_weight_sum > max_min_weight)
00464         {
00465             max_min_weight = neg_weight_sum;
00466             closest_index = i;
00467         }
00468 
00469     }
00470     assert(!this->mElements[closest_index]->IsDeleted());
00471     return closest_index;
00472 }
00473 
00474 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00475 unsigned TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::GetNearestElementIndexFromTestElements(const ChastePoint<SPACE_DIM>& rTestPoint,
00476                                                                                          std::set<unsigned> testElements)
00477 {
00478     assert(testElements.size()>0);
00479 
00480     double max_min_weight = -INFINITY;
00481     unsigned closest_index = 0;
00482     for(std::set<unsigned>::iterator iter = testElements.begin();
00483         iter != testElements.end();
00484         iter++)
00485     {
00486         c_vector<double, ELEMENT_DIM+1> weight=this->mElements[*iter]->CalculateInterpolationWeights(rTestPoint);
00487         double neg_weight_sum=0.0;
00488         for (unsigned j=0; j<=ELEMENT_DIM; j++)
00489         {
00490             if (weight[j] < 0.0)
00491             {
00492                 neg_weight_sum += weight[j];
00493             }
00494         }
00495         if (neg_weight_sum > max_min_weight)
00496         {
00497             max_min_weight = neg_weight_sum;
00498             closest_index = *iter;
00499         }
00500 
00501     }
00502     assert(!this->mElements[closest_index]->IsDeleted());
00503     return closest_index;
00504 }
00505 
00506 
00507 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00508 std::vector<unsigned> TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::GetContainingElementIndices(const ChastePoint<SPACE_DIM> &rTestPoint)
00509 {
00510     std::vector<unsigned> element_indices;
00511     for (unsigned i=0; i<this->mElements.size(); i++)
00512     {
00513         if (this->mElements[i]->IncludesPoint(rTestPoint))
00514         {
00515             assert(!this->mElements[i]->IsDeleted());
00516             element_indices.push_back(i);
00517         }
00518     }
00519     return element_indices;
00520 }
00521 
00522 
00523 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00524 void TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::Clear()
00525 {
00526     // three loops, just like the destructor. note we don't delete boundary nodes.
00527     for (unsigned i=0; i<this->mBoundaryElements.size(); i++)
00528     {
00529         delete this->mBoundaryElements[i];
00530     }
00531     for (unsigned i=0; i<this->mElements.size(); i++)
00532     {
00533         delete this->mElements[i];
00534     }
00535     for (unsigned i=0; i<this->mNodes.size(); i++)
00536     {
00537         delete this->mNodes[i];
00538     }
00539 
00540     this->mNodes.clear();
00541     this->mElements.clear();
00542     this->mBoundaryElements.clear();
00543     this->mBoundaryNodes.clear();
00544 }
00545 
00546 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00547 std::set<unsigned> TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::CalculateBoundaryOfFlaggedRegion()
00548 {
00549     // A set of nodes which lie on the face, size 3 in 2D, size 4 in 3D
00550     typedef std::set<unsigned> FaceNodes;
00551 
00552     // Face maps to true the first time it is encountered, and false subsequent
00553     // times. Thus, faces mapping to true at the end are boundary faces
00554     std::map<FaceNodes,bool> face_on_boundary;
00555 
00556     // Loop over all elements
00557     for (typename AbstractTetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::ElementIterator iter = this->GetElementIteratorBegin();
00558          iter != this->GetElementIteratorEnd();
00559          ++iter)
00560     {
00561         if (iter->IsFlagged())
00562         {
00563             // To get faces, initially start with all nodes
00564             std::set<unsigned> all_nodes;
00565             for (unsigned i=0; i<ELEMENT_DIM+1; i++)
00566             {
00567                 all_nodes.insert(iter->GetNodeGlobalIndex(i));
00568             }
00569 
00570             // Remove one node in turn to obtain each face
00571             for (unsigned i=0; i<ELEMENT_DIM+1; i++)
00572             {
00573                 FaceNodes face_nodes = all_nodes;
00574                 face_nodes.erase(iter->GetNodeGlobalIndex(i));
00575 
00576                 // Search the map of faces to see if it contains this face
00577                 std::map<FaceNodes,bool>::iterator it = face_on_boundary.find(face_nodes);
00578 
00579                 if (it == face_on_boundary.end())
00580                 {
00581                     // Face not found, add and assume on boundary
00582                     face_on_boundary[face_nodes]=true;
00583                 }
00584                 else
00585                 {
00586                     // Face found in map, so not on boundary
00587                     it->second = false;
00588                 }
00589             }
00590         }
00591     }
00592 
00593     // Boundary nodes to be returned
00594     std::set<unsigned> boundary_of_flagged_region;
00595 
00596     // Get all faces in the map
00597     std::map<FaceNodes,bool>::iterator it=face_on_boundary.begin();
00598     while (it!=face_on_boundary.end())
00599     {
00600         // If the face maps to true it is on the boundary
00601         if (it->second==true)
00602         {
00603             // Get all nodes in the face and put in set to be returned
00604             boundary_of_flagged_region.insert(it->first.begin(),it->first.end());
00605         }
00606         it++;
00607     }
00608 
00609     return boundary_of_flagged_region;
00610 }
00611 
00612 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00613 double TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::GetAngleBetweenNodes(unsigned indexA, unsigned indexB)
00614 {
00615     assert(SPACE_DIM == 2);
00616     assert(SPACE_DIM == ELEMENT_DIM);
00617 
00618     double x_diff = this->mNodes[indexB]->rGetLocation()[0] - this->mNodes[indexA]->rGetLocation()[0];
00619     double y_diff = this->mNodes[indexB]->rGetLocation()[1] - this->mNodes[indexA]->rGetLocation()[1];
00620 
00621     if (x_diff==0)
00622     {
00623         if (y_diff>0)
00624         {
00625             return M_PI/2.0;
00626         }
00627         else if (y_diff<0)
00628         {
00629             return -M_PI/2.0;
00630         }
00631         else
00632         {
00633             EXCEPTION("Tried to compute polar angle of (0,0)");
00634         }
00635     }
00636 
00637     double angle = atan2(y_diff,x_diff);
00638     return angle;
00639 }
00640 
00641 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00642 void TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::UnflagAllElements()
00643 {
00644     for (typename AbstractTetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::ElementIterator iter = this->GetElementIteratorBegin();
00645          iter != this->GetElementIteratorEnd();
00646          ++iter)
00647     {
00648         iter->Unflag();
00649     }
00650 }
00651 
00652 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00653 void TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::FlagElementsNotContainingNodes(std::set<unsigned> nodesList)
00654 {
00655     for (typename AbstractTetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::ElementIterator iter = this->GetElementIteratorBegin();
00656          iter != this->GetElementIteratorEnd();
00657          ++iter)
00658     {
00659         bool found_node = false;
00660 
00661         for (unsigned i=0; i<iter->GetNumNodes(); i++)
00662         {
00663             unsigned node_index = iter->GetNodeGlobalIndex(i);
00664 
00665             std::set<unsigned>::iterator set_iter = nodesList.find(node_index);
00666             if (set_iter != nodesList.end())
00667             {
00668                 found_node = true;
00669             }
00670         }
00671 
00672         if (!found_node)
00673         {
00674             iter->Flag();
00675         }
00676     }
00677 }
00678 
00679 
00681 //                          edge iterator class                             //
00683 
00684 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00685 Node<SPACE_DIM>* TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::EdgeIterator::GetNodeA()
00686 {
00687     assert((*this) != mrMesh.EdgesEnd());
00688     Element<ELEMENT_DIM,SPACE_DIM>* p_element = mrMesh.GetElement(mElemIndex);
00689     return p_element->GetNode(mNodeALocalIndex);
00690 }
00691 
00692 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00693 Node<SPACE_DIM>* TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::EdgeIterator::GetNodeB()
00694 {
00695     assert((*this) != mrMesh.EdgesEnd());
00696     Element<ELEMENT_DIM,SPACE_DIM>* p_element = mrMesh.GetElement(mElemIndex);
00697     return p_element->GetNode(mNodeBLocalIndex);
00698 }
00699 
00700 
00701 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00702 bool TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::EdgeIterator::operator!=(const TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::EdgeIterator& rOther)
00703 {
00704     return (mElemIndex != rOther.mElemIndex ||
00705             mNodeALocalIndex != rOther.mNodeALocalIndex ||
00706             mNodeBLocalIndex != rOther.mNodeBLocalIndex);
00707 }
00708 
00709 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00710 typename TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::EdgeIterator& TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::EdgeIterator::operator++()
00711 {
00712     bool already_seen_this_edge;
00713 
00714     unsigned num_elements = mrMesh.GetNumAllElements();
00715     std::pair<unsigned, unsigned> current_node_pair;
00716     do
00717     {
00718         // Advance to the next edge in the mesh.
00719         // Node indices are incremented modulo #nodes_per_elem
00720         mNodeBLocalIndex = (mNodeBLocalIndex + 1) % (ELEMENT_DIM+1);
00721         if (mNodeBLocalIndex == mNodeALocalIndex)
00722         {
00723             mNodeALocalIndex = (mNodeALocalIndex + 1) % (ELEMENT_DIM+1);
00724             mNodeBLocalIndex = (mNodeALocalIndex + 1) % (ELEMENT_DIM+1);
00725         }
00726 
00727         if (mNodeALocalIndex == 0 && mNodeBLocalIndex == 1) // advance to next element...
00728         {
00729             
00730             // ...skipping deleted ones
00731             do
00732             {
00733                 mElemIndex++;
00734             }
00735             while (mElemIndex!=num_elements && mrMesh.GetElement(mElemIndex)->IsDeleted());
00736         }
00737          
00738         if (mElemIndex != num_elements)
00739         {
00740             Element<ELEMENT_DIM, SPACE_DIM>* p_element = mrMesh.GetElement(mElemIndex);
00741             unsigned node_a_global_index = p_element->GetNodeGlobalIndex(mNodeALocalIndex);
00742             unsigned node_b_global_index = p_element->GetNodeGlobalIndex(mNodeBLocalIndex);
00743             if (node_b_global_index < node_a_global_index)
00744             {
00745                 //Swap them over
00746                 unsigned temp = node_a_global_index;
00747                 node_a_global_index = node_b_global_index;
00748                 node_b_global_index = temp;
00749             }
00750 
00751             // Check we haven't seen it before
00752             current_node_pair = std::pair<unsigned, unsigned>(node_a_global_index, node_b_global_index);
00753             already_seen_this_edge = (mEdgesVisited.count(current_node_pair) != 0);
00754         }
00755         else
00756         {
00757             already_seen_this_edge = false;
00758         }
00759     }
00760     while (already_seen_this_edge);
00761     mEdgesVisited.insert(current_node_pair);
00762 
00763     return (*this);
00764 }
00765 
00766 
00767 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00768 TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::EdgeIterator::EdgeIterator(TetrahedralMesh& rMesh, unsigned elemIndex)
00769     : mrMesh(rMesh),
00770       mElemIndex(elemIndex),
00771       mNodeALocalIndex(0),
00772       mNodeBLocalIndex(1)
00773 {
00774     if (elemIndex==mrMesh.GetNumAllElements())
00775     {
00776         return;
00777     }
00778 
00779     mEdgesVisited.clear();
00780 
00781     // add the current node pair to the store
00782 
00783     unsigned node_a_global_index = mrMesh.GetElement(mElemIndex)->GetNodeGlobalIndex(mNodeALocalIndex);
00784     unsigned node_b_global_index = mrMesh.GetElement(mElemIndex)->GetNodeGlobalIndex(mNodeBLocalIndex);
00785     if (node_b_global_index < node_a_global_index)
00786     {
00787         //Swap them over
00788         unsigned temp = node_a_global_index;
00789         node_a_global_index = node_b_global_index;
00790         node_b_global_index = temp;
00791     }
00792 
00793     // Check we haven't seen it before
00794     std::pair<unsigned, unsigned> current_node_pair=std::pair<unsigned, unsigned>(node_a_global_index, node_b_global_index);
00795 
00796     mEdgesVisited.insert(current_node_pair);
00797 }
00798 
00799 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00800 typename TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::EdgeIterator TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::EdgesBegin()
00801 {
00802     unsigned first_element_index=0;
00803     while (first_element_index!=this->GetNumAllElements() && this->GetElement(first_element_index)->IsDeleted())
00804     {
00805         first_element_index++;
00806     }
00807     return EdgeIterator(*this, first_element_index);
00808 }
00809 
00810 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00811 typename TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::EdgeIterator TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::EdgesEnd()
00812 {
00813     return EdgeIterator(*this, this->GetNumAllElements());
00814 }
00815 
00816 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00817 void TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::RefreshMesh()
00818 {
00819     RefreshJacobianCachedData();
00820 }
00821 
00822 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00823 unsigned TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::SolveNodeMapping(unsigned index) const
00824 {
00825     assert(index < this->mNodes.size() );
00826     return index;
00827 }
00828 
00829 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00830 unsigned TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::SolveElementMapping(unsigned index) const
00831 {
00832     assert(index < this->mElements.size() );
00833     return index;
00834 }
00835 
00836 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00837 unsigned TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::SolveBoundaryElementMapping(unsigned index) const
00838 {
00839     assert(index < this->mBoundaryElements.size() );
00840     return index;
00841 }
00842 
00843 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00844 void TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::RefreshJacobianCachedData()
00845 {
00846     unsigned num_elements = this->GetNumAllElements();
00847     unsigned num_boundary_elements = this->GetNumAllBoundaryElements();
00848 
00849     // Make sure we have enough space
00850     this->mElementJacobians.resize(num_elements);
00851     this->mElementInverseJacobians.resize(num_elements);
00852 
00853     if (ELEMENT_DIM < SPACE_DIM)
00854     {
00855         this->mElementWeightedDirections.resize(num_elements);
00856     }
00857 
00858     this->mBoundaryElementWeightedDirections.resize(num_boundary_elements);
00859 
00860     this->mElementJacobianDeterminants.resize(num_elements);
00861     this->mBoundaryElementJacobianDeterminants.resize(num_boundary_elements);
00862 
00863     // Update caches
00864     for (typename AbstractTetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::ElementIterator iter = this->GetElementIteratorBegin();
00865          iter != this->GetElementIteratorEnd();
00866          ++iter)
00867     {
00868         unsigned index = iter->GetIndex();
00869         iter->CalculateInverseJacobian(this->mElementJacobians[index], this->mElementJacobianDeterminants[index], this->mElementInverseJacobians[index]);
00870     }
00871 
00872     if (ELEMENT_DIM < SPACE_DIM)
00873     {
00874         for (typename AbstractTetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::ElementIterator iter = this->GetElementIteratorBegin();
00875              iter != this->GetElementIteratorEnd();
00876              ++iter)
00877         {
00878              unsigned index = iter->GetIndex();
00879              iter->CalculateWeightedDirection(this->mElementWeightedDirections[index], this->mElementJacobianDeterminants[index]);
00880         }
00881     }
00882 
00883     for ( typename TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::BoundaryElementIterator itb = this->GetBoundaryElementIteratorBegin();
00884           itb != this->GetBoundaryElementIteratorEnd();
00885           itb++)
00886     {
00887         unsigned index = (*itb)->GetIndex();
00888         (*itb)->CalculateWeightedDirection(this->mBoundaryElementWeightedDirections[index], this->mBoundaryElementJacobianDeterminants[index]);
00889     }
00890 }
00891 
00892 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00893 void TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::GetJacobianForElement(unsigned elementIndex, c_matrix<double, SPACE_DIM, SPACE_DIM>& rJacobian, double& rJacobianDeterminant) const
00894 {
00895     assert(ELEMENT_DIM <= SPACE_DIM);
00896     assert(elementIndex < this->mElementJacobians.size());
00897     rJacobian = this->mElementJacobians[elementIndex];
00898     rJacobianDeterminant = this->mElementJacobianDeterminants[elementIndex];
00899 }
00900 
00901 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00902 void TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::GetInverseJacobianForElement(unsigned elementIndex, c_matrix<double, SPACE_DIM, ELEMENT_DIM>& rJacobian, double& rJacobianDeterminant, c_matrix<double, ELEMENT_DIM, SPACE_DIM>& rInverseJacobian) const
00903 {
00904     assert(ELEMENT_DIM <= SPACE_DIM);
00905     assert(elementIndex < this->mElementInverseJacobians.size());
00906     rInverseJacobian = this->mElementInverseJacobians[elementIndex];
00907     rJacobian = this->mElementJacobians[elementIndex];
00908     rJacobianDeterminant = this->mElementJacobianDeterminants[elementIndex];
00909 }
00910 
00911 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00912 void TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::GetWeightedDirectionForElement(unsigned elementIndex, c_vector<double, SPACE_DIM>& rWeightedDirection, double& rJacobianDeterminant) const
00913 {
00914     assert(ELEMENT_DIM < SPACE_DIM);
00915     assert(elementIndex < this->mElementWeightedDirections.size());
00916     rWeightedDirection = this->mElementWeightedDirections[elementIndex];
00917     rJacobianDeterminant = this->mElementJacobianDeterminants[elementIndex];
00918 }
00919 
00920 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00921 void TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::GetWeightedDirectionForBoundaryElement(unsigned elementIndex, c_vector<double, SPACE_DIM>& rWeightedDirection, double& rJacobianDeterminant) const
00922 {
00923     assert(elementIndex < this->mBoundaryElementWeightedDirections.size());
00924     rWeightedDirection = this->mBoundaryElementWeightedDirections[elementIndex];
00925     rJacobianDeterminant = this->mBoundaryElementJacobianDeterminants[elementIndex];
00926 }
00927 
00928 
00929 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00930 void TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::InitialiseTriangulateIo(triangulateio& mesherIo)
00931 {
00932         mesherIo.numberofpoints = 0;
00933         mesherIo.pointlist = NULL;
00934         mesherIo.numberofpointattributes = 0;
00935         mesherIo.pointattributelist = (double *) NULL;
00936         mesherIo.pointmarkerlist = (int *) NULL;
00937         mesherIo.numberofsegments = 0;
00938         mesherIo.numberofholes = 0;
00939         mesherIo.numberofregions = 0;
00940         mesherIo.trianglelist = (int *) NULL;
00941         mesherIo.triangleattributelist = (double *) NULL;
00942         mesherIo.numberoftriangleattributes = 0;
00943         mesherIo.edgelist = (int *) NULL;
00944         mesherIo.edgemarkerlist = (int *) NULL;
00945 }
00946 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00947 void TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::FreeTriangulateIo(triangulateio& mesherIo)
00948 {
00949     if (mesherIo.numberofpoints != 0)
00950     {
00951         mesherIo.numberofpoints=0;
00952         free(mesherIo.pointlist);
00953     }
00954             
00955     //These (and the above) should actually be safe since we explicity set to NULL above
00956     free(mesherIo.pointattributelist);
00957     free(mesherIo.pointmarkerlist);
00958     free(mesherIo.trianglelist);
00959     free(mesherIo.triangleattributelist);
00960     free(mesherIo.edgelist);
00961     free(mesherIo.edgemarkerlist);
00962 }    
00963 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00964 template <class MESHER_IO>
00965 void TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::ExportToMesher(NodeMap& map, MESHER_IO& mesherInput, int *elementList)
00966 {
00967     if (SPACE_DIM == 2)
00968     {
00969         mesherInput.pointlist = (double *) malloc(this->GetNumNodes() * SPACE_DIM * sizeof(double));
00970     }
00971     else
00972     {
00973         mesherInput.pointlist =  new double[this->GetNumNodes() * SPACE_DIM];
00974     }
00975 
00976     mesherInput.numberofpoints = this->GetNumNodes();
00977     unsigned new_index = 0;
00978     for (unsigned i=0; i<this->GetNumAllNodes(); i++)
00979     {
00980         if (this->mNodes[i]->IsDeleted())
00981         {
00982             map.SetDeleted(i);
00983         }
00984         else
00985         {
00986             map.SetNewIndex(i, new_index);
00987             for (unsigned j=0; j<SPACE_DIM; j++)
00988             {
00989                 mesherInput.pointlist[SPACE_DIM*new_index + j] = this->mNodes[i]->rGetLocation()[j];
00990             }
00991             new_index++;
00992         }
00993     }
00994     if(elementList != NULL)
00995     {
00996         unsigned element_index=0u;
00997         //Assume there is enough space for this
00998         mesherInput.numberofcorners=ELEMENT_DIM+1;
00999         for (typename TetrahedralMesh<ELEMENT_DIM,SPACE_DIM>::ElementIterator elem_iter = this->GetElementIteratorBegin();
01000              elem_iter != this->GetElementIteratorEnd();
01001              ++elem_iter)
01002         {         
01003             
01004             for (unsigned j=0; j<=ELEMENT_DIM; j++)
01005             {
01006                 elementList[element_index*(ELEMENT_DIM+1) + j] = (*elem_iter).GetNodeGlobalIndex(j);
01007                 //PRINT_3_VARIABLES((*elem_iter).GetNodeGlobalIndex(j), element_index*(ELEMENT_DIM+1) + j, elementList[element_index*(ELEMENT_DIM+1) + j]);
01008             }
01009             element_index++;
01010         }
01011     }        
01012 }
01013 
01014 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
01015 template <class MESHER_IO>
01016 void TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::ImportFromMesher(MESHER_IO& mesherOutput, unsigned numberOfElements, int *elementList, unsigned numberOfFaces, int *faceList, int *edgeMarkerList)
01017 {
01018     unsigned nodes_per_element = mesherOutput.numberofcorners;
01019     
01020     assert( nodes_per_element == ELEMENT_DIM+1 || nodes_per_element == (ELEMENT_DIM+1)*(ELEMENT_DIM+2)/2 );
01021 
01022     Clear();
01023     // Construct the nodes
01024     for (unsigned node_index=0; node_index<(unsigned)mesherOutput.numberofpoints; node_index++)
01025     {
01026         this->mNodes.push_back(new Node<SPACE_DIM>(node_index, &mesherOutput.pointlist[node_index * SPACE_DIM], false));
01027     }
01028 
01029     // Construct the elements
01030     this->mElements.reserve(numberOfElements);
01031 
01032     unsigned real_element_index=0;
01033     for (unsigned element_index=0; element_index<numberOfElements; element_index++)
01034     {
01035         std::vector<Node<SPACE_DIM>*> nodes;
01036         for (unsigned j=0; j<ELEMENT_DIM+1; j++)
01037         {
01038             unsigned global_node_index = elementList[element_index*(nodes_per_element) + j];
01039             assert(global_node_index < this->mNodes.size());
01040             nodes.push_back(this->mNodes[global_node_index]);
01041             
01042         }
01043         //For some reason, tetgen in library mode makes its initial Delauney with
01044         //very thin slivers.  Hence we expect to ignore some of the elements!
01045         Element<ELEMENT_DIM, SPACE_DIM>* p_element;
01046         try
01047         {
01048             p_element = new Element<ELEMENT_DIM, SPACE_DIM>(real_element_index, nodes);
01049             //Shouldn't throw after this point
01050             this->mElements.push_back(p_element);
01051             
01052             //Add the internals to quadratics
01053             for (unsigned j=ELEMENT_DIM+1; j<nodes_per_element; j++)
01054             {
01055                 unsigned global_node_index = elementList[element_index*nodes_per_element + j];
01056                 assert(global_node_index < this->mNodes.size());
01057                 this->mElements[real_element_index]->AddNode( this->mNodes[global_node_index] );
01058                 this->mNodes[global_node_index]->AddElement(real_element_index);
01059                 this->mNodes[global_node_index]->MarkAsInternal();
01060             }
01061             real_element_index++;
01062         }
01063         catch (Exception &e)
01064         {
01065             //Tetgen is feeding us lies
01066             assert(SPACE_DIM == 3);
01067         }
01068     }
01069 
01070     // Construct the BoundaryElements (and mark boundary nodes)
01071     unsigned next_boundary_element_index = 0;
01072     for (unsigned boundary_element_index=0; boundary_element_index<numberOfFaces; boundary_element_index++)
01073     {
01074         //Tetgen produces only boundary faces (set edgeMarkerList to NULL)
01075         //Triangle marks which edges are on the boundary
01076         if (edgeMarkerList == NULL || edgeMarkerList[boundary_element_index] == 1)
01077         {
01078             std::vector<Node<SPACE_DIM>*> nodes;
01079             for (unsigned j=0; j<ELEMENT_DIM; j++)
01080             {
01081                 unsigned global_node_index = faceList[boundary_element_index*ELEMENT_DIM + j];
01082                 assert(global_node_index < this->mNodes.size());
01083                 nodes.push_back(this->mNodes[global_node_index]);
01084                 if (!nodes[j]->IsBoundaryNode())
01085                 {
01086                     nodes[j]->SetAsBoundaryNode();
01087                     this->mBoundaryNodes.push_back(nodes[j]);
01088                 }
01089             }
01090             //For some reason, tetgen in library mode makes its initial Delauney with
01091             //very thin slivers.  Hence we expect to ignore some of the faces!
01092             BoundaryElement<ELEMENT_DIM-1, SPACE_DIM>* p_b_element;
01093             try
01094             {
01095                 p_b_element = new BoundaryElement<ELEMENT_DIM-1, SPACE_DIM>(next_boundary_element_index, nodes);
01096                 this->mBoundaryElements.push_back(p_b_element);
01097                 next_boundary_element_index++;
01098             }
01099             catch (Exception &e)
01100             {
01101                 //Tetgen is feeding us lies  //Watch this space for coverage
01102                 assert(SPACE_DIM == 3);
01103             }
01104         }
01105     }
01106     
01107     this->RefreshJacobianCachedData();    
01108 }
01109 
01110 
01111 
01113 // Explicit instantiation
01115 
01116 template class TetrahedralMesh<1,1>;
01117 template class TetrahedralMesh<1,2>;
01118 template class TetrahedralMesh<1,3>;
01119 template class TetrahedralMesh<2,2>;
01120 template class TetrahedralMesh<2,3>;
01121 template class TetrahedralMesh<3,3>;
01122 
01127 template void TetrahedralMesh<2,2>::ExportToMesher<triangulateio>(NodeMap&, triangulateio&, int*);
01128 template void TetrahedralMesh<2,2>::ImportFromMesher<triangulateio>(triangulateio&, unsigned, int *, unsigned, int *, int *);
01129 
01130 template void TetrahedralMesh<3,3>::ExportToMesher<tetgen::tetgenio>(NodeMap&, tetgen::tetgenio&, int*);
01131 template void TetrahedralMesh<3,3>::ImportFromMesher<tetgen::tetgenio>(tetgen::tetgenio&, unsigned, int *, unsigned, int *, int *);
01132 
01133 //The following don't ever need to be instantiated, but are needed to keep some compilers happy
01134 template void TetrahedralMesh<1,2>::ExportToMesher<triangulateio>(NodeMap&, triangulateio&, int*);
01135 template void TetrahedralMesh<1,2>::ImportFromMesher<triangulateio>(triangulateio&, unsigned, int *, unsigned, int *, int *);
01136 
01137 template void TetrahedralMesh<1,3>::ExportToMesher<tetgen::tetgenio>(NodeMap&, tetgen::tetgenio&, int*);
01138 template void TetrahedralMesh<1,3>::ImportFromMesher<tetgen::tetgenio>(tetgen::tetgenio&, unsigned, int *, unsigned, int *, int *);
01139 template void TetrahedralMesh<2,3>::ExportToMesher<tetgen::tetgenio>(NodeMap&, tetgen::tetgenio&, int*);
01140 template void TetrahedralMesh<2,3>::ImportFromMesher<tetgen::tetgenio>(tetgen::tetgenio&, unsigned, int *, unsigned, int *, int *);
01141 
01142 
01143 //Intel compilation with IPO thinks that it's missing some bizarre instantiations
01144 template void TetrahedralMesh<3u, 3u>::ImportFromMesher<triangulateio>(triangulateio&, unsigned int, int*, unsigned int, int*, int*);
01145 template void TetrahedralMesh<1u, 1u>::ImportFromMesher<triangulateio>(triangulateio&, unsigned int, int*, unsigned int, int*, int*);
01146 template void TetrahedralMesh<1u, 1u>::ImportFromMesher<tetgen::tetgenio>(tetgen::tetgenio&, unsigned int, int*, unsigned int, int*, int*);
01147 template void TetrahedralMesh<2u, 2u>::ImportFromMesher<tetgen::tetgenio>(tetgen::tetgenio&, unsigned int, int*, unsigned int, int*, int*);
01148 template void TetrahedralMesh<1u, 1u>::ExportToMesher<tetgen::tetgenio>(NodeMap&, tetgen::tetgenio&, int*);
01154 // Serialization for Boost >= 1.36
01155 #define CHASTE_SERIALIZATION_CPP
01156 #include "SerializationExportWrapper.hpp"
01157 EXPORT_TEMPLATE_CLASS_ALL_DIMS(TetrahedralMesh)

Generated on Mon Nov 1 12:35:23 2010 for Chaste by  doxygen 1.5.5