TetrahedralMesh.cpp

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

Generated by  doxygen 1.6.2