Chaste Release::3.1
|
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)