TetrahedralMesh.cpp

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

Generated by  doxygen 1.6.2