TetrahedralMesh.cpp

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