Chaste Release::3.1
DistributedTetrahedralMesh.cpp
00001 /*
00002 
00003 Copyright (c) 2005-2012, 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 "DistributedTetrahedralMesh.hpp"
00037 
00038 #include <cassert>
00039 #include <sstream>
00040 #include <string>
00041 
00042 #include "Exception.hpp"
00043 #include "Element.hpp"
00044 #include "BoundaryElement.hpp"
00045 
00046 #include "PetscTools.hpp"
00047 #include "DistributedVectorFactory.hpp"
00048 #include "OutputFileHandler.hpp"
00049 #include "NodePartitioner.hpp"
00050 
00051 #include "RandomNumberGenerator.hpp"
00052 
00053 #include "Timer.hpp"
00054 
00055 #include "petscao.h"
00056 
00057 #include "Warnings.hpp"
00058 
00060 //   IMPLEMENTATION
00062 
00063 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00064 DistributedTetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::DistributedTetrahedralMesh(DistributedTetrahedralMeshPartitionType::type partitioningMethod)
00065     :
00066       mTotalNumElements(0u),
00067       mTotalNumBoundaryElements(0u),
00068       mTotalNumNodes(0u),
00069       mMetisPartitioning(partitioningMethod)
00070 {
00071     if (ELEMENT_DIM == 1)
00072     {
00073         //No METIS partition is possible - revert to DUMB
00074         mMetisPartitioning = DistributedTetrahedralMeshPartitionType::DUMB;
00075     }
00076 }
00077 
00078 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00079 DistributedTetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::~DistributedTetrahedralMesh()
00080 {
00081     for (unsigned i=0; i<this->mHaloNodes.size(); i++)
00082     {
00083         delete this->mHaloNodes[i];
00084     }
00085 }
00086 
00087 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00088 void DistributedTetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::SetDistributedVectorFactory(DistributedVectorFactory* pFactory)
00089 {
00090     AbstractMesh<ELEMENT_DIM,SPACE_DIM>::SetDistributedVectorFactory(pFactory);
00091     mMetisPartitioning = DistributedTetrahedralMeshPartitionType::DUMB;
00092 }
00093 
00094 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00095 void DistributedTetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::ComputeMeshPartitioning(
00096     AbstractMeshReader<ELEMENT_DIM, SPACE_DIM>& rMeshReader,
00097     std::set<unsigned>& rNodesOwned,
00098     std::set<unsigned>& rHaloNodesOwned,
00099     std::set<unsigned>& rElementsOwned,
00100     std::vector<unsigned>& rProcessorsOffset)
00101 {
00103 
00104     if (mMetisPartitioning==DistributedTetrahedralMeshPartitionType::PARMETIS_LIBRARY && PetscTools::IsParallel())
00105     {
00106         /*
00107          *  With ParMetisLibraryNodeAndElementPartitioning we compute the element partition first
00108          *  and then we work out the node ownership.
00109          */
00110         ParMetisLibraryNodeAndElementPartitioning(rMeshReader, rElementsOwned, rNodesOwned, rHaloNodesOwned, rProcessorsOffset);
00111     }
00112     else
00113     {
00114         /*
00115          *  Otherwise we compute the node partition and then we work out element distribution
00116          */
00117         if (mMetisPartitioning==DistributedTetrahedralMeshPartitionType::METIS_LIBRARY && PetscTools::IsParallel())
00118         {
00119             NodePartitioner<ELEMENT_DIM, SPACE_DIM>::MetisLibraryPartitioning(rMeshReader, this->mNodesPermutation, rNodesOwned, rProcessorsOffset);
00120         }
00121         else if (mMetisPartitioning==DistributedTetrahedralMeshPartitionType::PETSC_MAT_PARTITION && PetscTools::IsParallel())
00122         {
00123             NodePartitioner<ELEMENT_DIM, SPACE_DIM>::PetscMatrixPartitioning(rMeshReader, this->mNodesPermutation, rNodesOwned, rProcessorsOffset);
00124         }
00125         else
00126         {
00127             NodePartitioner<ELEMENT_DIM, SPACE_DIM>::DumbPartitioning(*this, rNodesOwned);
00128         }
00129 
00130         if ( rMeshReader.HasNclFile() )
00131         {
00132             // Form a set of all the element indices we are going to own
00133             // (union of the sets from the lines in the NCL file)
00134             for ( std::set<unsigned>::iterator iter=rNodesOwned.begin();
00135                   iter!=rNodesOwned.end();
00136                   ++iter )
00137             {
00138                 std::vector<unsigned> containing_elements = rMeshReader.GetContainingElementIndices( *iter );
00139                 rElementsOwned.insert( containing_elements.begin(), containing_elements.end() );
00140             }
00141 
00142             // Iterate through that set rather than mTotalNumElements (knowing that we own a least one node in each line)
00143             // Then read all the data into a node_index set
00144             std::set<unsigned> node_index_set;
00145 
00146             for ( std::set<unsigned>::iterator iter=rElementsOwned.begin();
00147                   iter!=rElementsOwned.end();
00148                   ++iter )
00149             {
00150                 ElementData element_data = rMeshReader.GetElementData( *iter );
00151                 node_index_set.insert( element_data.NodeIndices.begin(), element_data.NodeIndices.end() );
00152             }
00153 
00154             // Subtract off the rNodesOwned set to produce rHaloNodesOwned.
00155             // Note that rNodesOwned is a subset of node_index_set.
00156             // std::set_difference can't be used to fill a set...
00157             std::set<unsigned>::iterator iter_all = node_index_set.begin();
00158             std::set<unsigned>::iterator iter_owned = rNodesOwned.begin();
00159             while (iter_all != node_index_set.end() && iter_owned != rNodesOwned.end())
00160             {
00161                 if (*iter_all < *iter_owned) // Elements in sets are ordered
00162                 {
00163                     rHaloNodesOwned.insert(*iter_all++); // This node doesn't appear in rNodesOwned
00164                 }
00165                 else
00166                 {
00167                     iter_all++;
00168                     iter_owned++;
00169                 }
00170             }
00171             rHaloNodesOwned.insert(iter_all, node_index_set.end()); // Anything left over is halo
00172         }
00173         else
00174         {
00175             for (unsigned element_number = 0; element_number < mTotalNumElements; element_number++)
00176             {
00177                 ElementData element_data = rMeshReader.GetNextElementData();
00178 
00179                 bool element_owned = false;
00180                 std::set<unsigned> temp_halo_nodes;
00181 
00182                 for (std::vector<unsigned>::const_iterator it = element_data.NodeIndices.begin();
00183                      it != element_data.NodeIndices.end();
00184                      ++it)
00185                 {
00186                     if (rNodesOwned.find(*it) != rNodesOwned.end())
00187                     {
00188                         element_owned = true;
00189                         rElementsOwned.insert(element_number);
00190                     }
00191                     else
00192                     {
00193                         temp_halo_nodes.insert(*it);
00194                     }
00195                 }
00196 
00197                 if (element_owned)
00198                 {
00199                     rHaloNodesOwned.insert(temp_halo_nodes.begin(), temp_halo_nodes.end());
00200                 }
00201             }
00202         }
00203 
00204         if (mMetisPartitioning==DistributedTetrahedralMeshPartitionType::PETSC_MAT_PARTITION && PetscTools::IsParallel())
00205         {
00206             PetscTools::Barrier();
00207             if (PetscTools::AmMaster())
00208             {
00209                 Timer::PrintAndReset("Element and halo node assignation");
00210             }
00211         }
00212     }
00213     rMeshReader.Reset();
00214 }
00215 
00216 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00217 void DistributedTetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::ConstructFromMeshReader(
00218     AbstractMeshReader<ELEMENT_DIM, SPACE_DIM>& rMeshReader)
00219 {
00220     std::set<unsigned> nodes_owned;
00221     std::set<unsigned> halo_nodes_owned;
00222     std::set<unsigned> elements_owned;
00223     std::vector<unsigned> proc_offsets;//(PetscTools::GetNumProcs());
00224 
00225     this->mMeshFileBaseName = rMeshReader.GetMeshFileBaseName();
00226     mTotalNumElements = rMeshReader.GetNumElements();
00227     mTotalNumBoundaryElements = rMeshReader.GetNumFaces();
00228     mTotalNumNodes = rMeshReader.GetNumNodes();
00229 
00230 
00231     PetscTools::Barrier();
00232     Timer::Reset();
00233     ComputeMeshPartitioning(rMeshReader, nodes_owned, halo_nodes_owned, elements_owned, proc_offsets);
00234     PetscTools::Barrier();
00235     //Timer::Print("partitioning");
00236 
00237     // Reserve memory
00238     this->mElements.reserve(elements_owned.size());
00239     this->mNodes.reserve(nodes_owned.size());
00240 
00241     if ( rMeshReader.IsFileFormatBinary() )
00242     {
00245         std::vector<double> coords;
00246         // Binary : load only the nodes which are needed
00247         for (typename AbstractMeshReader<ELEMENT_DIM, SPACE_DIM>::NodeIterator node_it = rMeshReader.GetNodeIteratorBegin(nodes_owned);
00248                       node_it != rMeshReader.GetNodeIteratorEnd();
00249                       ++node_it)
00250         {
00251             //Loop over wholly-owned nodes
00252             unsigned global_node_index = node_it.GetIndex();
00253             coords = *node_it;
00254             RegisterNode(global_node_index);
00255             Node<SPACE_DIM>* p_node = new Node<SPACE_DIM>(global_node_index, coords, false);
00256 
00257 //Node attributes in binary format are not yet supported, see #1730
00258 //            for (unsigned i = 0; i < rMeshReader.GetNodeAttributes().size(); i++)
00259 //            {
00260 //                double attribute = rMeshReader.GetNodeAttributes()[i];
00261 //                p_node->AddNodeAttribute(attribute);
00262 //            }
00263 
00264             this->mNodes.push_back(p_node);
00265         }
00266         for (typename AbstractMeshReader<ELEMENT_DIM, SPACE_DIM>::NodeIterator node_it = rMeshReader.GetNodeIteratorBegin(halo_nodes_owned);
00267                       node_it != rMeshReader.GetNodeIteratorEnd();
00268                       ++node_it)
00269         {
00270             //Loop over halo-owned nodes
00271             unsigned global_node_index = node_it.GetIndex();
00272             coords = *node_it;
00273             RegisterHaloNode(global_node_index);
00274             mHaloNodes.push_back(new Node<SPACE_DIM>(global_node_index, coords, false));
00275         }
00276     }
00277     else
00278     {
00279         // Ascii : Sequentially load all the nodes and store those owned (or halo-owned) by the process
00281         for (unsigned node_index=0; node_index < mTotalNumNodes; node_index++)
00282         {
00283             std::vector<double> coords;
00285             coords = rMeshReader.GetNextNode();
00286 
00287             // The node is owned by the processor
00288             if (nodes_owned.find(node_index) != nodes_owned.end())
00289             {
00290                 RegisterNode(node_index);
00291                 Node<SPACE_DIM>* p_node =  new Node<SPACE_DIM>(node_index, coords, false);
00292 
00293                 for (unsigned i = 0; i < rMeshReader.GetNodeAttributes().size(); i++)
00294                 {
00295                     double attribute = rMeshReader.GetNodeAttributes()[i];
00296                     p_node->AddNodeAttribute(attribute);
00297                 }
00298 
00299                 this->mNodes.push_back(p_node);
00300             }
00301 
00302             // The node is a halo node in this processor
00303             if (halo_nodes_owned.find(node_index) != halo_nodes_owned.end())
00304             {
00305                 RegisterHaloNode(node_index);
00306                 mHaloNodes.push_back(new Node<SPACE_DIM>(node_index, coords, false));
00307             }
00308         }
00309     }
00310 
00311     for (typename AbstractMeshReader<ELEMENT_DIM, SPACE_DIM>::ElementIterator elem_it
00312              = rMeshReader.GetElementIteratorBegin(elements_owned);
00313          elem_it != rMeshReader.GetElementIteratorEnd();
00314          ++elem_it)
00315     {
00316         ElementData element_data = *elem_it;
00317         unsigned global_element_index = elem_it.GetIndex();
00318 
00319         std::vector<Node<SPACE_DIM>*> nodes;
00320         for (unsigned j=0; j<ELEMENT_DIM+1; j++)
00321         {
00322             // Because we have populated mNodes and mHaloNodes above, we can now use this method, which should never throw
00323             nodes.push_back(this->GetNodeOrHaloNode(element_data.NodeIndices[j]));
00324         }
00325 
00326         RegisterElement(global_element_index);
00327         Element<ELEMENT_DIM,SPACE_DIM>* p_element = new Element<ELEMENT_DIM,SPACE_DIM>(global_element_index, nodes);
00328         this->mElements.push_back(p_element);
00329 
00330         if (rMeshReader.GetNumElementAttributes() > 0)
00331         {
00332             assert(rMeshReader.GetNumElementAttributes() == 1);
00333             double attribute_value = element_data.AttributeValue;
00334             p_element->SetAttribute(attribute_value);
00335         }
00336     }
00337 
00338     // Boundary nodes and elements
00339     try
00340     {
00341         for (unsigned face_index=0; face_index<mTotalNumBoundaryElements; face_index++)
00342         {
00343             ElementData face_data = rMeshReader.GetNextFaceData();
00344             std::vector<unsigned> node_indices = face_data.NodeIndices;
00345 
00346             bool own = false;
00347 
00348             for (unsigned node_index=0; node_index<node_indices.size(); node_index++)
00349             {
00350                 // if I own this node
00351                 if (mNodesMapping.find(node_indices[node_index]) != mNodesMapping.end())
00352                 {
00353                     own = true;
00354                     break;
00355                 }
00356             }
00357 
00358             if (!own)
00359             {
00360                 continue;
00361             }
00362 
00363             // Determine if this is a boundary face
00364             //std::set<unsigned> containing_element_indices; // Elements that contain this face
00365             std::vector<Node<SPACE_DIM>*> nodes;
00366 
00367             for (unsigned node_index=0; node_index<node_indices.size(); node_index++)
00368             {
00369                 //because we have populated mNodes and mHaloNodes above, we can now use this method,
00370                 //which SHOULD never throw (but it does).
00371                 try
00372                 {
00373                     nodes.push_back(this->GetNodeOrHaloNode(node_indices[node_index]));
00374                 }
00375                 catch (Exception &e)
00376                 {
00377                     EXCEPTION("Face does not appear in element file (Face " << face_index << " in "<<this->mMeshFileBaseName<< ")");
00378                 }
00379             }
00380 
00381             // This is a boundary face
00382             // Ensure all its nodes are marked as boundary nodes
00383             for (unsigned j=0; j<nodes.size(); j++)
00384             {
00385                 if (!nodes[j]->IsBoundaryNode())
00386                 {
00387                     nodes[j]->SetAsBoundaryNode();
00388                     this->mBoundaryNodes.push_back(nodes[j]);
00389                 }
00390                 // Register the index that this boundary element will have with the node
00391                 nodes[j]->AddBoundaryElement(face_index);
00392             }
00393 
00394             RegisterBoundaryElement(face_index);
00395             BoundaryElement<ELEMENT_DIM-1,SPACE_DIM>* p_boundary_element = new BoundaryElement<ELEMENT_DIM-1,SPACE_DIM>(face_index, nodes);
00396             this->mBoundaryElements.push_back(p_boundary_element);
00397 
00398             if (rMeshReader.GetNumFaceAttributes() > 0)
00399             {
00400                 assert(rMeshReader.GetNumFaceAttributes() == 1);
00401                 p_boundary_element->SetAttribute(face_data.AttributeValue);
00402             }
00403         }
00404     }
00405     catch (Exception &e)
00406     {
00407         PetscTools::ReplicateException(true); //Bad face exception
00408         throw e;
00409     }
00410     PetscTools::ReplicateException(false);
00411 
00412     if (mMetisPartitioning != DistributedTetrahedralMeshPartitionType::DUMB && PetscTools::IsParallel())
00413     {
00414         assert(this->mNodesPermutation.size() != 0);
00415         // We reorder so that each process owns a contiguous set of the indices and we can then build a distributed vector factory.
00416         ReorderNodes();
00417 
00418         unsigned num_owned;
00419         unsigned rank = PetscTools::GetMyRank();
00420         if ( !PetscTools::AmTopMost() )
00421         {
00422             num_owned =  proc_offsets[rank+1]-proc_offsets[rank];
00423         }
00424         else
00425         {
00426             num_owned = mTotalNumNodes - proc_offsets[rank];
00427         }
00428 
00429         assert(!this->mpDistributedVectorFactory);
00430         this->mpDistributedVectorFactory = new DistributedVectorFactory(this->GetNumNodes(), num_owned);
00431     }
00432     else
00433     {
00434         // Dumb or sequential partition
00435         assert(this->mpDistributedVectorFactory);
00436     }
00437     rMeshReader.Reset();
00438 }
00439 
00440 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00441 unsigned DistributedTetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::GetNumLocalNodes() const
00442 {
00443     return this->mNodes.size();
00444 }
00445 
00446 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00447 unsigned DistributedTetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::GetNumHaloNodes() const
00448 {
00449     return this->mHaloNodes.size();
00450 }
00451 
00452 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00453 unsigned DistributedTetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::GetNumLocalElements() const
00454 {
00455     return this->mElements.size();
00456 }
00457 
00458 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00459 unsigned DistributedTetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::GetNumLocalBoundaryElements() const
00460 {
00461     return this->mBoundaryElements.size();
00462 }
00463 
00464 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00465 unsigned DistributedTetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::GetNumNodes() const
00466 {
00467     return mTotalNumNodes;
00468 }
00469 
00470 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00471 unsigned DistributedTetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::GetNumAllNodes() const
00472 {
00473     return mTotalNumNodes;
00474 }
00475 
00476 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00477 unsigned DistributedTetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::GetNumElements() const
00478 {
00479     return mTotalNumElements;
00480 }
00481 
00482 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00483 DistributedTetrahedralMeshPartitionType::type DistributedTetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::GetPartitionType() const
00484 {
00485     return mMetisPartitioning;
00486 }
00487 
00488 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00489 unsigned DistributedTetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::GetNumBoundaryElements() const
00490 {
00491     return mTotalNumBoundaryElements;
00492 }
00493 
00494 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00495 void DistributedTetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::GetHaloNodeIndices(std::vector<unsigned>& rHaloIndices) const
00496 {
00497     //Make sure the output vector is empty
00498     rHaloIndices.clear();
00499     for (unsigned i=0; i<mHaloNodes.size(); i++)
00500     {
00501         rHaloIndices.push_back(mHaloNodes[i]->GetIndex());
00502     }
00503 }
00504 
00505 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00506 void DistributedTetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::SetElementOwnerships()
00507 {
00508     // All the local elements are owned by the processor (obviously...)
00509     //Does nothing - unlike the non-distributed version
00510 }
00511 
00512 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00513 bool DistributedTetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::CalculateDesignatedOwnershipOfElement( unsigned elementIndex )
00514 {
00515     try
00516     {
00517         return(AbstractTetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::CalculateDesignatedOwnershipOfElement(elementIndex));
00518     }
00519     catch(Exception& e)      // we don't own the element
00520     {
00521         return false;
00522     }
00523 }
00524 
00525 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00526 bool DistributedTetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::CalculateDesignatedOwnershipOfBoundaryElement( unsigned faceIndex )
00527 {
00528     try
00529     {
00530         return(AbstractTetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::CalculateDesignatedOwnershipOfBoundaryElement(faceIndex));
00531     }
00532     catch(Exception& e)      //  we don't own the face
00533     {
00534         return false;
00535     }
00536 }
00537 
00538 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00539 void DistributedTetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::RegisterNode(unsigned index)
00540 {
00541     mNodesMapping[index] = this->mNodes.size();
00542 }
00543 
00544 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00545 void DistributedTetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::RegisterHaloNode(unsigned index)
00546 {
00547     mHaloNodesMapping[index] = mHaloNodes.size();
00548 }
00549 
00550 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00551 void DistributedTetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::RegisterElement(unsigned index)
00552 {
00553     mElementsMapping[index] = this->mElements.size();
00554 }
00555 
00556 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00557 void DistributedTetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::RegisterBoundaryElement(unsigned index)
00558 {
00559     mBoundaryElementsMapping[index] = this->mBoundaryElements.size();
00560 }
00561 
00562 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00563 unsigned DistributedTetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::SolveNodeMapping(unsigned index) const
00564 {
00565     std::map<unsigned, unsigned>::const_iterator node_position = mNodesMapping.find(index);
00566 
00567     if (node_position == mNodesMapping.end())
00568     {
00569         EXCEPTION("Requested node " << index << " does not belong to processor " << PetscTools::GetMyRank());
00570     }
00571     return node_position->second;
00572 }
00573 
00574 //template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00575 //unsigned DistributedTetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::SolveHaloNodeMapping(unsigned index)
00576 //{
00577 //    assert(mHaloNodesMapping.find(index) != mHaloNodesMapping.end());
00578 //    return mHaloNodesMapping[index];
00579 //}
00580 
00581 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00582 unsigned DistributedTetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::SolveElementMapping(unsigned index) const
00583 {
00584     std::map<unsigned, unsigned>::const_iterator element_position = mElementsMapping.find(index);
00585 
00586     if (element_position == mElementsMapping.end())
00587     {
00588         EXCEPTION("Requested element " << index << " does not belong to processor " << PetscTools::GetMyRank());
00589     }
00590 
00591     return element_position->second;
00592 }
00593 
00594 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00595 unsigned DistributedTetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::SolveBoundaryElementMapping(unsigned index) const
00596 {
00597     std::map<unsigned, unsigned>::const_iterator boundary_element_position = mBoundaryElementsMapping.find(index);
00598 
00599     if (boundary_element_position == mBoundaryElementsMapping.end())
00600     {
00601         EXCEPTION("Requested boundary element " << index << " does not belong to processor " << PetscTools::GetMyRank());
00602     }
00603 
00604     return boundary_element_position->second;
00605 }
00606 
00607 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00608 Node<SPACE_DIM> * DistributedTetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::GetNodeOrHaloNode(unsigned index) const
00609 {
00610     std::map<unsigned, unsigned>::const_iterator node_position;
00611     // First search the halo (expected to be a smaller map so quicker)
00612     if ((node_position=mHaloNodesMapping.find(index)) != mHaloNodesMapping.end())
00613     {
00614         return mHaloNodes[node_position->second];
00615     }
00616     // Next search the owned node
00617     if ((node_position=mNodesMapping.find(index)) != mNodesMapping.end())
00618     {
00619         //Found an owned node
00620         return this->mNodes[node_position->second];
00621     }
00622     // Not here
00623     EXCEPTION("Requested node/halo " << index << " does not belong to processor " << PetscTools::GetMyRank());
00624 }
00625 
00626 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00627 void DistributedTetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::ReorderNodes()
00628 {
00629     assert(PetscTools::IsParallel());
00630 
00631     // Need to rebuild global-local maps
00632     mNodesMapping.clear();
00633     mHaloNodesMapping.clear();
00634 
00635     // Update indices
00636     for (unsigned index=0; index<this->mNodes.size(); index++)
00637     {
00638         unsigned old_index = this->mNodes[index]->GetIndex();
00639         unsigned new_index = this->mNodesPermutation[old_index];
00640 
00641         this->mNodes[index]->SetIndex(new_index);
00642         mNodesMapping[new_index] = index;
00643     }
00644 
00645     for (unsigned index=0; index<mHaloNodes.size(); index++)
00646     {
00647         unsigned old_index = mHaloNodes[index]->GetIndex();
00648         unsigned new_index = this->mNodesPermutation[old_index];
00649 
00650         mHaloNodes[index]->SetIndex(new_index);
00651         mHaloNodesMapping[new_index] = index;
00652     }
00653 }
00654 
00655 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00656 void DistributedTetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::ConstructLinearMesh(unsigned width)
00657 {
00658     assert(ELEMENT_DIM == 1);
00659 
00660      //Check that there are enough nodes to make the parallelisation worthwhile
00661     if (width==0)
00662     {
00663         EXCEPTION("There aren't enough nodes to make parallelisation worthwhile");
00664     }
00665     //Use dumb partition so that archiving doesn't permute anything
00666     mMetisPartitioning=DistributedTetrahedralMeshPartitionType::DUMB;
00667     mTotalNumNodes=width+1;
00668     mTotalNumBoundaryElements=2u;
00669     mTotalNumElements=width;
00670 
00671     //Use DistributedVectorFactory to make a dumb partition of the nodes
00672     assert(!this->mpDistributedVectorFactory);
00673     this->mpDistributedVectorFactory = new DistributedVectorFactory(mTotalNumNodes);
00674     if (this->mpDistributedVectorFactory->GetLocalOwnership() == 0)
00675     {
00676         //It's a short mesh and this process owns no nodes
00677         return;
00678     }
00679 
00680     /* am_top_most is like PetscTools::AmTopMost() but accounts for the fact that a
00681      * higher numbered process may have dropped out of this construction altogether
00682      * (because is has no local ownership)
00683      */
00684     bool am_top_most = (this->mpDistributedVectorFactory->GetHigh() == mTotalNumNodes);
00685 
00686     unsigned lo_node=this->mpDistributedVectorFactory->GetLow();
00687     unsigned hi_node=this->mpDistributedVectorFactory->GetHigh();
00688     if (!PetscTools::AmMaster())
00689     {
00690         //Allow for a halo node
00691         lo_node--;
00692     }
00693     if (!am_top_most)
00694     {
00695         //Allow for a halo node
00696         hi_node++;
00697     }
00698     Node<SPACE_DIM>* p_old_node=NULL;
00699     for (unsigned node_index=lo_node; node_index<hi_node; node_index++)
00700     {
00701         // create node or halo-node
00702         Node<SPACE_DIM>* p_node = new Node<SPACE_DIM>(node_index, node_index==0 || node_index==width, node_index);
00703         if (node_index<this->mpDistributedVectorFactory->GetLow() ||
00704             node_index==this->mpDistributedVectorFactory->GetHigh() )
00705         {
00706             //Beyond left or right it's a halo node
00707             RegisterHaloNode(node_index);
00708             mHaloNodes.push_back(p_node);
00709         }
00710         else
00711         {
00712             RegisterNode(node_index);
00713             this->mNodes.push_back(p_node); // create node
00714 
00715             //A boundary face has to be wholely owned by the process
00716             //Since, when ELEMENT_DIM>1 we have *at least* boundary node as a non-halo
00717             if (node_index==0) // create left boundary node and boundary element
00718             {
00719                 this->mBoundaryNodes.push_back(p_node);
00720                 RegisterBoundaryElement(0);
00721                 this->mBoundaryElements.push_back(new BoundaryElement<ELEMENT_DIM-1,SPACE_DIM>(0, p_node) );
00722             }
00723             if (node_index==width) // create right boundary node and boundary element
00724             {
00725                 this->mBoundaryNodes.push_back(p_node);
00726                 RegisterBoundaryElement(1);
00727                 this->mBoundaryElements.push_back(new BoundaryElement<ELEMENT_DIM-1,SPACE_DIM>(1, p_node) );
00728             }
00729             }
00730         if (node_index>lo_node) // create element
00731         {
00732             std::vector<Node<SPACE_DIM>*> nodes;
00733             nodes.push_back(p_old_node);
00734             nodes.push_back(p_node);
00735             RegisterElement(node_index-1);
00736             this->mElements.push_back(new Element<ELEMENT_DIM,SPACE_DIM>(node_index-1, nodes) );
00737         }
00738         //Keep track of the node which we've just created
00739         p_old_node=p_node;
00740     }
00741 }
00742 
00743 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00744 void DistributedTetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::ConstructRectangularMesh(unsigned width, unsigned height, bool stagger)
00745 {
00746     assert(SPACE_DIM == 2);
00747     assert(ELEMENT_DIM == 2);
00748     //Check that there are enough nodes to make the parallelisation worthwhile
00749     if (height==0)
00750     {
00751         EXCEPTION("There aren't enough nodes to make parallelisation worthwhile");
00752     }
00753     //Use dumb partition so that archiving doesn't permute anything
00754     mMetisPartitioning=DistributedTetrahedralMeshPartitionType::DUMB;
00755 
00756     mTotalNumNodes=(width+1)*(height+1);
00757     mTotalNumBoundaryElements=(width+height)*2;
00758     mTotalNumElements=width*height*2;
00759 
00760     //Use DistributedVectorFactory to make a dumb partition of space
00761     DistributedVectorFactory y_partition(height+1);
00762     unsigned lo_y = y_partition.GetLow();
00763     unsigned hi_y = y_partition.GetHigh();
00764     //Dumb partition of nodes has to be such that each process gets complete slices
00765     assert(!this->mpDistributedVectorFactory);
00766     this->mpDistributedVectorFactory = new DistributedVectorFactory(mTotalNumNodes, (width+1)*y_partition.GetLocalOwnership());
00767     if (this->mpDistributedVectorFactory->GetLocalOwnership() == 0)
00768     {
00769         //It's a short mesh and this process owns no nodes
00770         return;
00771     }
00772     /* am_top_most is like PetscTools::AmTopMost() but accounts for the fact that a
00773      * higher numbered process may have dropped out of this construction altogether
00774      * (because is has no local ownership)
00775      */
00776     bool am_top_most = (this->mpDistributedVectorFactory->GetHigh() == mTotalNumNodes);
00777 
00778 
00779     if (!PetscTools::AmMaster())
00780     {
00781         //Allow for a halo node
00782         lo_y--;
00783     }
00784     if (!am_top_most)
00785     {
00786         //Allow for a halo node
00787         hi_y++;
00788     }
00789 
00790     //Construct the nodes
00791     for (unsigned j=lo_y; j<hi_y; j++)
00792     {
00793         for (unsigned i=0; i<width+1; i++)
00794         {
00795             bool is_boundary=false;
00796             if (i==0 || j==0 || i==width || j==height)
00797             {
00798                 is_boundary=true;
00799             }
00800             unsigned global_node_index=((width+1)*(j) + i); //Verified from sequential
00801             Node<SPACE_DIM>* p_node = new Node<SPACE_DIM>(global_node_index, is_boundary, i, j);
00802             if (j<y_partition.GetLow() || j==y_partition.GetHigh() )
00803             {
00804                 //Beyond left or right it's a halo node
00805                 RegisterHaloNode(global_node_index);
00806                 mHaloNodes.push_back(p_node);
00807             }
00808             else
00809             {
00810                 RegisterNode(global_node_index);
00811                 this->mNodes.push_back(p_node);
00812             }
00813             if (is_boundary)
00814             {
00815                 this->mBoundaryNodes.push_back(p_node);
00816             }
00817         }
00818     }
00819 
00820     //Construct the boundary elements
00821     unsigned belem_index;
00822     //Top
00823     if (am_top_most)
00824     {
00825        for (unsigned i=0; i<width; i++)
00826        {
00827             std::vector<Node<SPACE_DIM>*> nodes;
00828             nodes.push_back(GetNodeOrHaloNode( height*(width+1)+i+1 ));
00829             nodes.push_back(GetNodeOrHaloNode( height*(width+1)+i ));
00830             belem_index=i;
00831             RegisterBoundaryElement(belem_index);
00832             this->mBoundaryElements.push_back(new BoundaryElement<ELEMENT_DIM-1,SPACE_DIM>(belem_index,nodes));
00833         }
00834     }
00835 
00836     //Right
00837     for (unsigned j=lo_y+1; j<hi_y; j++)
00838     {
00839         std::vector<Node<SPACE_DIM>*> nodes;
00840         nodes.push_back(GetNodeOrHaloNode( (width+1)*j-1 ));
00841         nodes.push_back(GetNodeOrHaloNode( (width+1)*(j+1)-1 ));
00842         belem_index=width+j-1;
00843         RegisterBoundaryElement(belem_index);
00844         this->mBoundaryElements.push_back(new BoundaryElement<ELEMENT_DIM-1,SPACE_DIM>(belem_index,nodes));
00845     }
00846 
00847     //Bottom
00848     if (PetscTools::AmMaster())
00849     {
00850         for (unsigned i=0; i<width; i++)
00851         {
00852             std::vector<Node<SPACE_DIM>*> nodes;
00853             nodes.push_back(GetNodeOrHaloNode( i ));
00854             nodes.push_back(GetNodeOrHaloNode( i+1 ));
00855             belem_index=width+height+i;
00856             RegisterBoundaryElement(belem_index);
00857             this->mBoundaryElements.push_back(new BoundaryElement<ELEMENT_DIM-1,SPACE_DIM>(belem_index,nodes));
00858         }
00859     }
00860 
00861     //Left
00862     for (unsigned j=lo_y; j<hi_y-1; j++)
00863     {
00864         std::vector<Node<SPACE_DIM>*> nodes;
00865         nodes.push_back(GetNodeOrHaloNode( (width+1)*(j+1) ));
00866         nodes.push_back(GetNodeOrHaloNode( (width+1)*(j) ));
00867         belem_index=2*width+height+j;
00868         RegisterBoundaryElement(belem_index);
00869         this->mBoundaryElements.push_back(new BoundaryElement<ELEMENT_DIM-1,SPACE_DIM>(belem_index,nodes));
00870     }
00871 
00872 
00873     //Construct the elements
00874     unsigned elem_index;
00875     for (unsigned j=lo_y; j<hi_y-1; j++)
00876     {
00877         for (unsigned i=0; i<width; i++)
00878         {
00879             unsigned parity=(i+(height-j))%2;//Note that parity is measured from the top-left (not bottom left) for historical reasons
00880             unsigned nw=(j+1)*(width+1)+i; //ne=nw+1
00881             unsigned sw=(j)*(width+1)+i;   //se=sw+1
00882             std::vector<Node<SPACE_DIM>*> upper_nodes;
00883             upper_nodes.push_back(GetNodeOrHaloNode( nw ));
00884             upper_nodes.push_back(GetNodeOrHaloNode( nw+1 ));
00885             if (stagger==false  || parity == 1)
00886             {
00887                 upper_nodes.push_back(GetNodeOrHaloNode( sw+1 ));
00888             }
00889             else
00890             {
00891                 upper_nodes.push_back(GetNodeOrHaloNode( sw ));
00892             }
00893             elem_index=2*(j*width+i);
00894             RegisterElement(elem_index);
00895             this->mElements.push_back(new Element<ELEMENT_DIM,SPACE_DIM>(elem_index,upper_nodes));
00896             std::vector<Node<SPACE_DIM>*> lower_nodes;
00897             lower_nodes.push_back(GetNodeOrHaloNode( sw+1 ));
00898             lower_nodes.push_back(GetNodeOrHaloNode( sw ));
00899             if (stagger==false  ||parity == 1)
00900             {
00901                 lower_nodes.push_back(GetNodeOrHaloNode( nw ));
00902             }
00903             else
00904             {
00905                 lower_nodes.push_back(GetNodeOrHaloNode( nw+1 ));
00906             }
00907             elem_index++;
00908             RegisterElement(elem_index);
00909             this->mElements.push_back(new Element<ELEMENT_DIM,SPACE_DIM>(elem_index,lower_nodes));
00910         }
00911     }
00912 
00913 }
00914 
00915 
00916 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00917 void DistributedTetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::ConstructCuboid(unsigned width,
00918         unsigned height,
00919         unsigned depth)
00920 {
00921     assert(SPACE_DIM == 3);
00922     assert(ELEMENT_DIM == 3);
00923     //Check that there are enough nodes to make the parallelisation worthwhile
00924     if (depth==0)
00925     {
00926         EXCEPTION("There aren't enough nodes to make parallelisation worthwhile");
00927     }
00928 
00929     //Use dumb partition so that archiving doesn't permute anything
00930     mMetisPartitioning=DistributedTetrahedralMeshPartitionType::DUMB;
00931 
00932     mTotalNumNodes=(width+1)*(height+1)*(depth+1);
00933     mTotalNumBoundaryElements=((width*height)+(width*depth)+(height*depth))*4;//*2 for top-bottom, *2 for tessellating each unit square
00934     mTotalNumElements=width*height*depth*6;
00935 
00936     //Use DistributedVectorFactory to make a dumb partition of space
00937     DistributedVectorFactory z_partition(depth+1);
00938     unsigned lo_z = z_partition.GetLow();
00939     unsigned hi_z = z_partition.GetHigh();
00940 
00941     //Dumb partition of nodes has to be such that each process gets complete slices
00942     assert(!this->mpDistributedVectorFactory);
00943     this->mpDistributedVectorFactory = new DistributedVectorFactory(mTotalNumNodes, (width+1)*(height+1)*z_partition.GetLocalOwnership());
00944     if (this->mpDistributedVectorFactory->GetLocalOwnership() == 0)
00945     {
00946 #define COVERAGE_IGNORE
00947         // It's a short mesh and this process owns no nodes.  This problem can only occur on 4 or more processes,
00948         // so we can't cover it - coverage only runs with 1 and 2 processes.
00949         WARNING("No nodes were assigned to processor " << PetscTools::GetMyRank() << " in DistributedTetrahedralMesh::ConstructCuboid()");
00950         return;
00951 #undef COVERAGE_IGNORE
00952     }
00953     /* am_top_most is like PetscTools::AmTopMost() but accounts for the fact that a
00954      * higher numbered process may have dropped out of this construction altogether
00955      * (because is has no local ownership)
00956      */
00957     bool am_top_most = (this->mpDistributedVectorFactory->GetHigh() == mTotalNumNodes);
00958 
00959 
00960 
00961     if (!PetscTools::AmMaster())
00962     {
00963         //Allow for a halo node
00964         lo_z--;
00965     }
00966     if (!am_top_most)
00967     {
00968         //Allow for a halo node
00969         hi_z++;
00970     }
00971 
00972     //Construct the nodes
00973     unsigned global_node_index;
00974     for (unsigned k=lo_z; k<hi_z; k++)
00975     {
00976         for (unsigned j=0; j<height+1; j++)
00977         {
00978             for (unsigned i=0; i<width+1; i++)
00979             {
00980                 bool is_boundary = false;
00981                 if (i==0 || j==0 || k==0 || i==width || j==height || k==depth)
00982                 {
00983                     is_boundary = true;
00984                 }
00985                 global_node_index = (k*(height+1)+j)*(width+1)+i;
00986 
00987                 Node<SPACE_DIM>* p_node = new Node<SPACE_DIM>(global_node_index, is_boundary, i, j, k);
00988 
00989                 if (k<z_partition.GetLow() || k==z_partition.GetHigh() )
00990                 {
00991                     //Beyond left or right it's a halo node
00992                     RegisterHaloNode(global_node_index);
00993                     mHaloNodes.push_back(p_node);
00994                 }
00995                 else
00996                 {
00997                     RegisterNode(global_node_index);
00998                     this->mNodes.push_back(p_node);
00999                 }
01000 
01001                 if (is_boundary)
01002                 {
01003                     this->mBoundaryNodes.push_back(p_node);
01004                 }
01005             }
01006         }
01007     }
01008 
01009     // Construct the elements
01010 
01011     unsigned element_nodes[6][4] = {{0, 1, 5, 7}, {0, 1, 3, 7},
01012                                         {0, 2, 3, 7}, {0, 2, 6, 7},
01013                                         {0, 4, 6, 7}, {0, 4, 5, 7}};
01014     std::vector<Node<SPACE_DIM>*> tetrahedra_nodes;
01015 
01016     for (unsigned k=lo_z; k<hi_z-1; k++)
01017     {
01018         unsigned belem_index = 0;
01019         if (k != 0)
01020         {
01021             // height*width squares on upper face, k layers of 2*height+2*width square aroun
01022             belem_index =   2*(height*width+k*2*(height+width));
01023         }
01024 
01025         for (unsigned j=0; j<height; j++)
01026         {
01027             for (unsigned i=0; i<width; i++)
01028             {
01029                 // Compute the nodes' index
01030                 unsigned global_node_indices[8];
01031                 unsigned local_node_index = 0;
01032 
01033                 for (unsigned z = 0; z < 2; z++)
01034                 {
01035                     for (unsigned y = 0; y < 2; y++)
01036                     {
01037                         for (unsigned x = 0; x < 2; x++)
01038                         {
01039                             global_node_indices[local_node_index] = i+x+(width+1)*(j+y+(height+1)*(k+z));
01040 
01041                             local_node_index++;
01042                         }
01043                     }
01044                 }
01045 
01046                 for (unsigned m = 0; m < 6; m++)
01047                 {
01048                     // Tetrahedra #m
01049 
01050                     tetrahedra_nodes.clear();
01051 
01052                     for (unsigned n = 0; n < 4; n++)
01053                     {
01054                         tetrahedra_nodes.push_back(GetNodeOrHaloNode( global_node_indices[element_nodes[m][n]] ));
01055                     }
01056                     unsigned elem_index = 6 * ((k*height+j)*width+i)+m;
01057                     RegisterElement(elem_index);
01058                     this->mElements.push_back(new Element<ELEMENT_DIM,SPACE_DIM>(elem_index, tetrahedra_nodes));
01059                 }
01060 
01061                 //Are we at a boundary?
01062                 std::vector<Node<SPACE_DIM>*> triangle_nodes;
01063 
01064                 if (i == 0) //low face at x==0
01065                 {
01066                     triangle_nodes.clear();
01067                     triangle_nodes.push_back(GetNodeOrHaloNode( global_node_indices[0] ));
01068                     triangle_nodes.push_back(GetNodeOrHaloNode( global_node_indices[2] ));
01069                     triangle_nodes.push_back(GetNodeOrHaloNode( global_node_indices[6] ));
01070                     RegisterBoundaryElement(belem_index);
01071                     this->mBoundaryElements.push_back(new BoundaryElement<ELEMENT_DIM-1,SPACE_DIM>(belem_index++,triangle_nodes));
01072                     triangle_nodes.clear();
01073                     triangle_nodes.push_back(GetNodeOrHaloNode( global_node_indices[0] ));
01074                     triangle_nodes.push_back(GetNodeOrHaloNode( global_node_indices[6] ));
01075                     triangle_nodes.push_back(GetNodeOrHaloNode( global_node_indices[4] ));
01076                     RegisterBoundaryElement(belem_index);
01077                     this->mBoundaryElements.push_back(new BoundaryElement<ELEMENT_DIM-1,SPACE_DIM>(belem_index++,triangle_nodes));
01078                 }
01079                 if (i == width-1) //high face at x=width
01080                 {
01081                     triangle_nodes.clear();
01082                     triangle_nodes.push_back(GetNodeOrHaloNode( global_node_indices[1] ));
01083                     triangle_nodes.push_back(GetNodeOrHaloNode( global_node_indices[5] ));
01084                     triangle_nodes.push_back(GetNodeOrHaloNode( global_node_indices[7] ));
01085                     RegisterBoundaryElement(belem_index);
01086                     this->mBoundaryElements.push_back(new BoundaryElement<ELEMENT_DIM-1,SPACE_DIM>(belem_index++,triangle_nodes));
01087                     triangle_nodes.clear();
01088                     triangle_nodes.push_back(GetNodeOrHaloNode( global_node_indices[1] ));
01089                     triangle_nodes.push_back(GetNodeOrHaloNode( global_node_indices[7] ));
01090                     triangle_nodes.push_back(GetNodeOrHaloNode( global_node_indices[3] ));
01091                     RegisterBoundaryElement(belem_index);
01092                     this->mBoundaryElements.push_back(new BoundaryElement<ELEMENT_DIM-1,SPACE_DIM>(belem_index++,triangle_nodes));
01093                 }
01094                 if (j == 0) //low face at y==0
01095                 {
01096                     triangle_nodes.clear();
01097                     triangle_nodes.push_back(GetNodeOrHaloNode( global_node_indices[0] ));
01098                     triangle_nodes.push_back(GetNodeOrHaloNode( global_node_indices[5] ));
01099                     triangle_nodes.push_back(GetNodeOrHaloNode( global_node_indices[1] ));
01100                     RegisterBoundaryElement(belem_index);
01101                     this->mBoundaryElements.push_back(new BoundaryElement<ELEMENT_DIM-1,SPACE_DIM>(belem_index++,triangle_nodes));
01102                     triangle_nodes.clear();
01103                     triangle_nodes.push_back(GetNodeOrHaloNode( global_node_indices[0] ));
01104                     triangle_nodes.push_back(GetNodeOrHaloNode( global_node_indices[4] ));
01105                     triangle_nodes.push_back(GetNodeOrHaloNode( global_node_indices[5] ));
01106                     RegisterBoundaryElement(belem_index);
01107                     this->mBoundaryElements.push_back(new BoundaryElement<ELEMENT_DIM-1,SPACE_DIM>(belem_index++,triangle_nodes));
01108                 }
01109                 if (j == height-1) //high face at y=height
01110                 {
01111                     triangle_nodes.clear();
01112                     triangle_nodes.push_back(GetNodeOrHaloNode( global_node_indices[2] ));
01113                     triangle_nodes.push_back(GetNodeOrHaloNode( global_node_indices[3] ));
01114                     triangle_nodes.push_back(GetNodeOrHaloNode( global_node_indices[7] ));
01115                     RegisterBoundaryElement(belem_index);
01116                     this->mBoundaryElements.push_back(new BoundaryElement<ELEMENT_DIM-1,SPACE_DIM>(belem_index++,triangle_nodes));
01117                     triangle_nodes.clear();
01118                     triangle_nodes.push_back(GetNodeOrHaloNode( global_node_indices[2] ));
01119                     triangle_nodes.push_back(GetNodeOrHaloNode( global_node_indices[7] ));
01120                     triangle_nodes.push_back(GetNodeOrHaloNode( global_node_indices[6] ));
01121                     RegisterBoundaryElement(belem_index);
01122                     this->mBoundaryElements.push_back(new BoundaryElement<ELEMENT_DIM-1,SPACE_DIM>(belem_index++,triangle_nodes));
01123                 }
01124                 if (k == 0) //low face at z==0
01125                 {
01126                     triangle_nodes.clear();
01127                     triangle_nodes.push_back(GetNodeOrHaloNode( global_node_indices[0] ));
01128                     triangle_nodes.push_back(GetNodeOrHaloNode( global_node_indices[3] ));
01129                     triangle_nodes.push_back(GetNodeOrHaloNode( global_node_indices[2] ));
01130                     RegisterBoundaryElement(belem_index);
01131                     this->mBoundaryElements.push_back(new BoundaryElement<ELEMENT_DIM-1,SPACE_DIM>(belem_index++,triangle_nodes));
01132                     triangle_nodes.clear();
01133                     triangle_nodes.push_back(GetNodeOrHaloNode( global_node_indices[0] ));
01134                     triangle_nodes.push_back(GetNodeOrHaloNode( global_node_indices[1] ));
01135                     triangle_nodes.push_back(GetNodeOrHaloNode( global_node_indices[3] ));
01136                     RegisterBoundaryElement(belem_index);
01137                     this->mBoundaryElements.push_back(new BoundaryElement<ELEMENT_DIM-1,SPACE_DIM>(belem_index++,triangle_nodes));
01138                 }
01139                 if (k == depth-1) //high face at z=depth
01140                 {
01141                     triangle_nodes.clear();
01142                     triangle_nodes.push_back(GetNodeOrHaloNode( global_node_indices[4] ));
01143                     triangle_nodes.push_back(GetNodeOrHaloNode( global_node_indices[7] ));
01144                     triangle_nodes.push_back(GetNodeOrHaloNode( global_node_indices[5] ));
01145                     RegisterBoundaryElement(belem_index);
01146                     this->mBoundaryElements.push_back(new BoundaryElement<ELEMENT_DIM-1,SPACE_DIM>(belem_index++,triangle_nodes));
01147                     triangle_nodes.clear();
01148                     triangle_nodes.push_back(GetNodeOrHaloNode( global_node_indices[4] ));
01149                     triangle_nodes.push_back(GetNodeOrHaloNode( global_node_indices[6] ));
01150                     triangle_nodes.push_back(GetNodeOrHaloNode( global_node_indices[7] ));
01151                     RegisterBoundaryElement(belem_index);
01152                     this->mBoundaryElements.push_back(new BoundaryElement<ELEMENT_DIM-1,SPACE_DIM>(belem_index++,triangle_nodes));
01153                 }
01154             }//i
01155         }//j
01156     }//k
01157 }
01158 
01159 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
01160 void DistributedTetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::Scale(const double xFactor, const double yFactor, const double zFactor)
01161 {
01162     //Base class scale (scales node positions)
01163     AbstractTetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::Scale(xFactor, yFactor, zFactor);
01164     //Scales halos
01165     for (unsigned i=0; i<mHaloNodes.size(); i++)
01166     {
01167         c_vector<double, SPACE_DIM>& r_location = mHaloNodes[i]->rGetModifiableLocation();
01168         if (SPACE_DIM>=3)
01169         {
01170             r_location[2] *= zFactor;
01171         }
01172         if (SPACE_DIM>=2)
01173         {
01174             r_location[1] *= yFactor;
01175         }
01176         r_location[0] *= xFactor;
01177     }
01178 
01179 }
01180 
01181 
01182 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
01183 void DistributedTetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::ParMetisLibraryNodeAndElementPartitioning(
01184         AbstractMeshReader<ELEMENT_DIM, SPACE_DIM>& rMeshReader,
01185         std::set<unsigned>& rElementsOwned,
01186         std::set<unsigned>& rNodesOwned,
01187         std::set<unsigned>& rHaloNodesOwned,
01188         std::vector<unsigned>& rProcessorsOffset)
01189 {
01190     assert(PetscTools::IsParallel());
01191     assert(ELEMENT_DIM==2 || ELEMENT_DIM==3); // Metis works with triangles and tetras
01192 
01193     const unsigned num_elements = rMeshReader.GetNumElements();
01194     const unsigned num_procs = PetscTools::GetNumProcs();
01195     const unsigned local_proc_index = PetscTools::GetMyRank();
01196 
01197     /*
01198      *  Work out initial element distribution
01199      */
01200     idxtype element_distribution[num_procs+1];
01201     idxtype element_count[num_procs];
01202 
01203     element_distribution[0]=0;
01204 
01205     for (unsigned proc_index=1; proc_index<num_procs; proc_index++)
01206     {
01207         element_distribution[proc_index] = element_distribution[proc_index-1] + num_elements/num_procs;
01208         element_count[proc_index-1] = element_distribution[proc_index] - element_distribution[proc_index-1];
01209     }
01210 
01211     element_distribution[num_procs] = num_elements;
01212     element_count[num_procs-1] = element_distribution[num_procs] - element_distribution[num_procs-1];
01213 
01214     /*
01215      *  Create distributed mesh data structure
01216      */
01217     unsigned first_local_element = element_distribution[local_proc_index];
01218     unsigned last_plus_one_element = element_distribution[local_proc_index+1];
01219     unsigned num_local_elements = last_plus_one_element - first_local_element;
01220 
01221     idxtype* eind = new idxtype[num_local_elements*(ELEMENT_DIM+1)];
01222     idxtype* eptr = new idxtype[num_local_elements+1];
01223 
01224     if ( ! rMeshReader.IsFileFormatBinary() )
01225     {
01226         // Advance the file pointer to the first element I own.
01227         for (unsigned element_index = 0; element_index < first_local_element; element_index++)
01228         {
01229             ElementData element_data = rMeshReader.GetNextElementData();
01230         }
01231     }
01232 
01233     unsigned counter=0;
01234     for (unsigned element_index = 0; element_index < num_local_elements; element_index++)
01235     {
01236         ElementData element_data;
01237 
01238         if ( rMeshReader.IsFileFormatBinary() )
01239         {
01240             element_data = rMeshReader.GetElementData(first_local_element + element_index);
01241         }
01242         else
01243         {
01244             element_data = rMeshReader.GetNextElementData();
01245         }
01246 
01247         eptr[element_index] = counter;
01248         for (unsigned i=0; i<ELEMENT_DIM+1; i++)
01249         {
01250             eind[counter++] = element_data.NodeIndices[i];
01251         }
01252     }
01253     eptr[num_local_elements] = counter;
01254 
01255     rMeshReader.Reset();
01256 
01257     int numflag = 0; // METIS speak for C-style numbering
01258     /* Connectivity degree.
01259      * Specifically, an GRAPH EDGE is placed between any two elements if and only if they share
01260      * at least this many nodes.
01261      *
01262      * Manual recommends "for meshes containing only triangular, tetrahedral,
01263      * hexahedral, or rectangular elements, this parameter can be set to two, three, four, or two, respectively.
01264      */
01265     int ncommonnodes = 3; //Linear tetrahedra
01266     if (ELEMENT_DIM == 2)
01267     {
01268         ncommonnodes = 2;
01269     }
01270 
01271     MPI_Comm communicator = PETSC_COMM_WORLD;
01272 
01273     idxtype* xadj;
01274     idxtype* adjncy;
01275 
01276     Timer::Reset();
01277     ParMETIS_V3_Mesh2Dual(element_distribution, eptr, eind,
01278                           &numflag, &ncommonnodes, &xadj, &adjncy, &communicator);
01279     //Timer::Print("ParMETIS Mesh2Dual");
01280 
01281     delete[] eind;
01282     delete[] eptr;
01283 
01284     int weight_flag = 0; // unweighted graph
01285     int n_constrains = 0; // number of weights that each vertex has (number of balance constrains)
01286     int n_subdomains = PetscTools::GetNumProcs();
01287     int options[3]; // extra options
01288     options[0] = 0; // ignore extra options
01289     int edgecut;
01290 
01291     idxtype* local_partition = new idxtype[num_local_elements];
01292 
01293 /*
01294  *  In order to use ParMETIS_V3_PartGeomKway, we need to sort out how to compute the coordinates of the
01295  *  centers of each element efficiently.
01296  *
01297  *  In the meantime use ParMETIS_V3_PartKway.
01298  */
01299 //    int n_dimensions = ELEMENT_DIM;
01300 //    float node_coordinates[num_local_elements*SPACE_DIM];
01301 //
01302 //    ParMETIS_V3_PartGeomKway(element_distribution, xadj, adjncy, NULL, NULL, &weight_flag, &numflag,
01303 //                             &n_dimensions, node_coordinates, &n_constrains, &n_subdomains, NULL, NULL,
01304 //                             options, &edgecut, local_partition, &communicator);
01305 
01306     Timer::Reset();
01307     ParMETIS_V3_PartKway(element_distribution, xadj, adjncy, NULL, NULL, &weight_flag, &numflag,
01308                          &n_constrains, &n_subdomains, NULL, NULL,
01309                          options, &edgecut, local_partition, &communicator);
01310     //Timer::Print("ParMETIS PartKway");
01311 
01312 
01313     idxtype* global_element_partition = new idxtype[num_elements];
01314 
01315     MPI_Allgatherv(local_partition, num_local_elements, MPI_INT,
01316                    global_element_partition, element_count, element_distribution, MPI_INT, PETSC_COMM_WORLD);
01317 
01318     delete[] local_partition;
01319 
01320     for (unsigned elem_index=0; elem_index<num_elements; elem_index++)
01321     {
01322         if ((unsigned) global_element_partition[elem_index] == local_proc_index)
01323         {
01324             rElementsOwned.insert(elem_index);
01325         }
01326     }
01327 
01328     rMeshReader.Reset();
01329     free(xadj);
01330     free(adjncy);
01331 
01332     unsigned num_nodes = rMeshReader.GetNumNodes();
01333 
01334     // Initialise with no nodes known
01335     std::vector<unsigned> global_node_partition(num_nodes, UNASSIGNED_NODE);
01336 
01337     assert(rProcessorsOffset.size() == 0); // Making sure the vector is empty. After calling resize() only newly created memory will be initialised to 0.
01338     rProcessorsOffset.resize(PetscTools::GetNumProcs(), 0);
01339 
01340     /*
01341      *  Work out node distribution based on initial element distribution returned by ParMETIS
01342      *
01343      *  In this loop we handle 4 different data structures:
01344      *      global_node_partition and rProcessorsOffset are global,
01345      *      rNodesOwned and rHaloNodesOwned are local.
01346      */
01347 
01348     std::vector<unsigned> element_access_order;
01349 
01350     if ( rMeshReader.IsFileFormatBinary() )
01351     {
01352         RandomNumberGenerator* p_gen = RandomNumberGenerator::Instance();
01353         p_gen->Reseed(0);
01354         p_gen->Shuffle(mTotalNumElements, element_access_order);
01355     }
01356     else
01357     {
01358         element_access_order.reserve(mTotalNumElements);
01359         for (unsigned element_number = 0; element_number < mTotalNumElements; element_number++)
01360         {
01361             element_access_order.push_back(element_number);
01362         }
01363     }
01364 
01365 
01366     for (unsigned element_count = 0; element_count < mTotalNumElements; element_count++)
01367     {
01368         unsigned element_number = element_access_order[element_count];
01369         unsigned element_owner = global_element_partition[element_number];
01370 
01371         ElementData element_data;
01372 
01373         if ( rMeshReader.IsFileFormatBinary() )
01374         {
01375             element_data = rMeshReader.GetElementData(element_number);
01376         }
01377         else
01378         {
01379             element_data = rMeshReader.GetNextElementData();
01380         }
01381 
01382         for (std::vector<unsigned>::const_iterator node_it = element_data.NodeIndices.begin();
01383              node_it != element_data.NodeIndices.end();
01384              ++node_it)
01385         {
01386             /*
01387              * For each node in this element, check whether it hasn't been assigned to another processor yet.
01388              * If so, assign it to the owner of the element. Otherwise, consider it halo.
01389              */
01390             if ( global_node_partition[*node_it] == UNASSIGNED_NODE )
01391             {
01392                 if (element_owner == local_proc_index)
01393                 {
01394                     rNodesOwned.insert(*node_it);
01395                 }
01396 
01397                 global_node_partition[*node_it] = element_owner;
01398 
01399                 // Offset is defined as the first node owned by a processor. We compute it incrementally.
01400                 // i.e. if node_index belongs to proc 3 (of 6) we have to shift the processors 4, 5, and 6
01401                 // offset a position.
01402                 for (unsigned proc=element_owner+1; proc<PetscTools::GetNumProcs(); proc++)
01403                 {
01404                     rProcessorsOffset[proc]++;
01405                 }
01406             }
01407             else
01408             {
01409                 if (element_owner == local_proc_index)
01410                 {
01411                     //if (rNodesOwned.find(*node_it) == rNodesOwned.end())
01412                     if (global_node_partition[*node_it] != local_proc_index)
01413                     {
01414                         rHaloNodesOwned.insert(*node_it);
01415                     }
01416                 }
01417             }
01418         }
01419     }
01420 
01421     delete[] global_element_partition;
01422 
01423     /*
01424      * Refine element distribution. Add extra elements that parMETIS didn't consider initially but
01425      * include any node owned by the processor. This ensures that all the system matrix rows are
01426      * assembled locally.
01427      */
01428     rMeshReader.Reset();
01429 
01430     for (unsigned element_number = 0; element_number < mTotalNumElements; element_number++)
01431     {
01432         ElementData element_data = rMeshReader.GetNextElementData();
01433 
01434         bool element_owned = false;
01435         std::set<unsigned> temp_halo_nodes;
01436 
01437         for (std::vector<unsigned>::const_iterator node_it = element_data.NodeIndices.begin();
01438              node_it != element_data.NodeIndices.end();
01439              ++node_it)
01440         {
01441             if (rNodesOwned.find(*node_it) != rNodesOwned.end())
01442             {
01443                 element_owned = true;
01444                 rElementsOwned.insert(element_number);
01445             }
01446             else
01447             {
01448                 temp_halo_nodes.insert(*node_it);
01449             }
01450         }
01451 
01452         if (element_owned)
01453         {
01454             rHaloNodesOwned.insert(temp_halo_nodes.begin(), temp_halo_nodes.end());
01455         }
01456     }
01457 
01458     rMeshReader.Reset();
01459 
01460     /*
01461      *  Once we know the offsets we can compute the permutation vector
01462      */
01463     std::vector<unsigned> local_index(PetscTools::GetNumProcs(), 0);
01464 
01465     this->mNodesPermutation.resize(this->GetNumNodes());
01466 
01467     for (unsigned node_index=0; node_index<this->GetNumNodes(); node_index++)
01468     {
01469         unsigned partition = global_node_partition[node_index];
01470         assert(partition != UNASSIGNED_NODE);
01471 
01472         this->mNodesPermutation[node_index] = rProcessorsOffset[partition] + local_index[partition];
01473 
01474         local_index[partition]++;
01475     }
01476 
01477 }
01478 
01479 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
01480 ChasteCuboid<SPACE_DIM> DistributedTetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::CalculateBoundingBox() const
01481 {
01482     ChastePoint<SPACE_DIM> my_minimum_point;
01483     ChastePoint<SPACE_DIM> my_maximum_point;
01484 
01485     try
01486     {
01487         ChasteCuboid<SPACE_DIM> my_box=AbstractMesh<ELEMENT_DIM, SPACE_DIM>::CalculateBoundingBox();
01488         my_minimum_point=my_box.rGetLowerCorner();
01489         my_maximum_point=my_box.rGetUpperCorner();
01490     }
01491     catch (Exception& e)
01492     {
01493 #define COVERAGE_IGNORE
01494         PetscTools::ReplicateException(true);
01495         throw e;
01496 #undef COVERAGE_IGNORE
01497     }
01498 
01499     PetscTools::ReplicateException(false);
01500 
01501     c_vector<double, SPACE_DIM> global_minimum_point;
01502     c_vector<double, SPACE_DIM> global_maximum_point;
01503     MPI_Allreduce(&my_minimum_point.rGetLocation()[0], &global_minimum_point[0], SPACE_DIM, MPI_DOUBLE, MPI_MIN, PETSC_COMM_WORLD);
01504     MPI_Allreduce(&my_maximum_point.rGetLocation()[0], &global_maximum_point[0], SPACE_DIM, MPI_DOUBLE, MPI_MAX, PETSC_COMM_WORLD);
01505 
01506     ChastePoint<SPACE_DIM> min(global_minimum_point);
01507     ChastePoint<SPACE_DIM> max(global_maximum_point);
01508 
01509     return ChasteCuboid<SPACE_DIM>(min, max);
01510 }
01511 
01512 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
01513 c_vector<double, 2> DistributedTetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::CalculateMinMaxEdgeLengths()
01514 {
01515     c_vector<double, 2> local_min_max =  AbstractTetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::CalculateMinMaxEdgeLengths();
01516     c_vector<double, 2> global_min_max;
01517 
01518     MPI_Allreduce(&local_min_max[0], &global_min_max[0], 1, MPI_DOUBLE, MPI_MIN, PETSC_COMM_WORLD);
01519     MPI_Allreduce(&local_min_max[1], &global_min_max[1], 1, MPI_DOUBLE, MPI_MAX, PETSC_COMM_WORLD);
01520 
01521     return global_min_max;
01522 }
01523 
01524 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
01525 typename DistributedTetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::HaloNodeIterator DistributedTetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::GetHaloNodeIteratorBegin() const
01526 {
01527     return mHaloNodes.begin();
01528 }
01529 
01530 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
01531 typename DistributedTetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::HaloNodeIterator DistributedTetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::GetHaloNodeIteratorEnd() const
01532 {
01533     return mHaloNodes.end();
01534 }
01535 
01537 // Explicit instantiation
01539 
01540 template class DistributedTetrahedralMesh<1,1>;
01541 template class DistributedTetrahedralMesh<1,2>;
01542 template class DistributedTetrahedralMesh<1,3>;
01543 template class DistributedTetrahedralMesh<2,2>;
01544 template class DistributedTetrahedralMesh<2,3>;
01545 template class DistributedTetrahedralMesh<3,3>;
01546 
01547 
01548 // Serialization for Boost >= 1.36
01549 #include "SerializationExportWrapperForCpp.hpp"
01550 EXPORT_TEMPLATE_CLASS_ALL_DIMS(DistributedTetrahedralMesh)