TetrahedralMesh.cpp

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

Generated by  doxygen 1.6.2