partitioner.C
Go to the documentation of this file.00001 // The libMesh Finite Element Library. 00002 // Copyright (C) 2002-2012 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner 00003 00004 // This library is free software; you can redistribute it and/or 00005 // modify it under the terms of the GNU Lesser General Public 00006 // License as published by the Free Software Foundation; either 00007 // version 2.1 of the License, or (at your option) any later version. 00008 00009 // This library is distributed in the hope that it will be useful, 00010 // but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00012 // Lesser General Public License for more details. 00013 00014 // You should have received a copy of the GNU Lesser General Public 00015 // License along with this library; if not, write to the Free Software 00016 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 00017 00018 00019 00020 // C++ Includes ----------------------------------- 00021 00022 // Local Includes ----------------------------------- 00023 #include "libmesh/elem.h" 00024 #include "libmesh/mesh_base.h" 00025 #include "libmesh/parallel.h" 00026 #include "libmesh/partitioner.h" 00027 #include "libmesh/mesh_tools.h" 00028 #include "libmesh/mesh_communication.h" 00029 #include "libmesh/libmesh_logging.h" 00030 00031 //FIXME 00032 #include "libmesh/parallel_mesh.h" 00033 #include "libmesh/mesh_tools.h" 00034 00035 namespace libMesh 00036 { 00037 00038 00039 00040 // ------------------------------------------------------------ 00041 // Partitioner static data 00042 const dof_id_type Partitioner::communication_blocksize = 1000000; 00043 00044 00045 00046 // ------------------------------------------------------------ 00047 // Partitioner implementation 00048 void Partitioner::partition (MeshBase& mesh, 00049 const unsigned int n) 00050 { 00051 parallel_only(); 00052 00053 // BSK - temporary fix while redistribution is integrated 6/26/2008 00054 // Uncomment this to not repartition in parallel 00055 // if (!mesh.is_serial()) 00056 // return; 00057 00058 // we cannot partition into more pieces than we have 00059 // active elements! 00060 const unsigned int n_parts = 00061 static_cast<unsigned int> 00062 (std::min(mesh.n_active_elem(), static_cast<dof_id_type>(n))); 00063 00064 // Set the number of partitions in the mesh 00065 mesh.set_n_partitions()=n_parts; 00066 00067 if (n_parts == 1) 00068 { 00069 this->single_partition (mesh); 00070 return; 00071 } 00072 00073 // First assign a temporary partitioning to any unpartitioned elements 00074 Partitioner::partition_unpartitioned_elements(mesh, n_parts); 00075 00076 // Call the partitioning function 00077 this->_do_partition(mesh,n_parts); 00078 00079 // Set the parent's processor ids 00080 Partitioner::set_parent_processor_ids(mesh); 00081 00082 // Redistribute elements if necessary, before setting node processor 00083 // ids, to make sure those will be set consistently 00084 mesh.redistribute(); 00085 00086 #ifdef DEBUG 00087 MeshTools::libmesh_assert_valid_remote_elems(mesh); 00088 00089 // Messed up elem processor_id()s can leave us without the child 00090 // elements we need to restrict vectors on a distributed mesh 00091 MeshTools::libmesh_assert_valid_procids<Elem>(mesh); 00092 #endif 00093 00094 // Set the node's processor ids 00095 Partitioner::set_node_processor_ids(mesh); 00096 00097 #ifdef DEBUG 00098 MeshTools::libmesh_assert_valid_procids<Elem>(mesh); 00099 #endif 00100 00101 // Give derived Mesh classes a chance to update any cached data to 00102 // reflect the new partitioning 00103 mesh.update_post_partitioning(); 00104 } 00105 00106 00107 00108 00109 00110 void Partitioner::repartition (MeshBase& mesh, 00111 const unsigned int n) 00112 { 00113 // we cannot partition into more pieces than we have 00114 // active elements! 00115 const unsigned int n_parts = 00116 static_cast<unsigned int> 00117 (std::min(mesh.n_active_elem(), static_cast<dof_id_type>(n))); 00118 00119 // Set the number of partitions in the mesh 00120 mesh.set_n_partitions()=n_parts; 00121 00122 if (n_parts == 1) 00123 { 00124 this->single_partition (mesh); 00125 return; 00126 } 00127 00128 // First assign a temporary partitioning to any unpartitioned elements 00129 Partitioner::partition_unpartitioned_elements(mesh, n_parts); 00130 00131 // Call the partitioning function 00132 this->_do_repartition(mesh,n_parts); 00133 00134 // Set the parent's processor ids 00135 Partitioner::set_parent_processor_ids(mesh); 00136 00137 // Set the node's processor ids 00138 Partitioner::set_node_processor_ids(mesh); 00139 } 00140 00141 00142 00143 00144 00145 void Partitioner::single_partition (MeshBase& mesh) 00146 { 00147 START_LOG("single_partition()","Partitioner"); 00148 00149 // Loop over all the elements and assign them to processor 0. 00150 MeshBase::element_iterator elem_it = mesh.elements_begin(); 00151 const MeshBase::element_iterator elem_end = mesh.elements_end(); 00152 00153 for ( ; elem_it != elem_end; ++elem_it) 00154 (*elem_it)->processor_id() = 0; 00155 00156 // For a single partition, all the nodes are on processor 0 00157 MeshBase::node_iterator node_it = mesh.nodes_begin(); 00158 const MeshBase::node_iterator node_end = mesh.nodes_end(); 00159 00160 for ( ; node_it != node_end; ++node_it) 00161 (*node_it)->processor_id() = 0; 00162 00163 STOP_LOG("single_partition()","Partitioner"); 00164 } 00165 00166 00167 00168 void Partitioner::partition_unpartitioned_elements (MeshBase &mesh, 00169 const unsigned int n_subdomains) 00170 { 00171 MeshBase::element_iterator it = mesh.unpartitioned_elements_begin(); 00172 const MeshBase::element_iterator end = mesh.unpartitioned_elements_end(); 00173 00174 const dof_id_type n_unpartitioned_elements = MeshTools::n_elem (it, end); 00175 00176 // the unpartitioned elements must exist on all processors. If the range is empty on one 00177 // it is empty on all, and we can quit right here. 00178 if (!n_unpartitioned_elements) return; 00179 00180 // find the target subdomain sizes 00181 std::vector<dof_id_type> subdomain_bounds(libMesh::n_processors()); 00182 00183 for (processor_id_type pid=0; pid<libMesh::n_processors(); pid++) 00184 { 00185 dof_id_type tgt_subdomain_size = 0; 00186 00187 // watch out for the case that n_subdomains < n_processors 00188 if (pid < n_subdomains) 00189 { 00190 tgt_subdomain_size = n_unpartitioned_elements/n_subdomains; 00191 00192 if (pid < n_unpartitioned_elements%n_subdomains) 00193 tgt_subdomain_size++; 00194 00195 } 00196 00197 //libMesh::out << "pid, #= " << pid << ", " << tgt_subdomain_size << std::endl; 00198 if (pid == 0) 00199 subdomain_bounds[0] = tgt_subdomain_size; 00200 else 00201 subdomain_bounds[pid] = subdomain_bounds[pid-1] + tgt_subdomain_size; 00202 } 00203 00204 libmesh_assert_equal_to (subdomain_bounds.back(), n_unpartitioned_elements); 00205 00206 // create the unique mapping for all unpartitioned elements independent of partitioning 00207 // determine the global indexing for all the unpartitoned elements 00208 std::vector<dof_id_type> global_indices; 00209 00210 // Calling this on all processors a unique range in [0,n_unpartitioned_elements) is constructed. 00211 // Only the indices for the elements we pass in are returned in the array. 00212 MeshCommunication().find_global_indices (MeshTools::bounding_box(mesh), it, end, 00213 global_indices); 00214 00215 for (dof_id_type cnt=0; it != end; ++it) 00216 { 00217 Elem *elem = *it; 00218 00219 libmesh_assert_less (cnt, global_indices.size()); 00220 const dof_id_type global_index = 00221 global_indices[cnt++]; 00222 00223 libmesh_assert_less (global_index, subdomain_bounds.back()); 00224 libmesh_assert_less (global_index, n_unpartitioned_elements); 00225 00226 const processor_id_type subdomain_id = 00227 libmesh_cast_int<processor_id_type> 00228 (std::distance(subdomain_bounds.begin(), 00229 std::upper_bound(subdomain_bounds.begin(), 00230 subdomain_bounds.end(), 00231 global_index))); 00232 libmesh_assert_less (subdomain_id, n_subdomains); 00233 00234 elem->processor_id() = subdomain_id; 00235 //libMesh::out << "assigning " << global_index << " to " << subdomain_id << std::endl; 00236 } 00237 } 00238 00239 00240 00241 void Partitioner::set_parent_processor_ids(MeshBase& 00242 #ifdef LIBMESH_ENABLE_AMR 00243 mesh 00244 #endif 00245 ) 00246 { 00247 START_LOG("set_parent_processor_ids()","Partitioner"); 00248 00249 #ifdef LIBMESH_ENABLE_AMR 00250 00251 // If the mesh is serial we have access to all the elements, 00252 // in particular all the active ones. We can therefore set 00253 // the parent processor ids indirecly through their children, and 00254 // set the subactive processor ids while examining their active 00255 // ancestors. 00256 // By convention a parent is assigned to the minimum processor 00257 // of all its children, and a subactive is assigned to the processor 00258 // of its active ancestor. 00259 if (mesh.is_serial()) 00260 { 00261 // Loop over all the active elements in the mesh 00262 MeshBase::element_iterator it = mesh.active_elements_begin(); 00263 const MeshBase::element_iterator end = mesh.active_elements_end(); 00264 00265 for ( ; it!=end; ++it) 00266 { 00267 Elem *child = *it; 00268 00269 // First set descendents 00270 00271 std::vector<const Elem*> subactive_family; 00272 child->total_family_tree(subactive_family); 00273 for (unsigned int i = 0; i != subactive_family.size(); ++i) 00274 const_cast<Elem*>(subactive_family[i])->processor_id() = child->processor_id(); 00275 00276 // Then set ancestors 00277 00278 Elem *parent = child->parent(); 00279 00280 while (parent) 00281 { 00282 // invalidate the parent id, otherwise the min below 00283 // will not work if the current parent id is less 00284 // than all the children! 00285 parent->invalidate_processor_id(); 00286 00287 for(unsigned int c=0; c<parent->n_children(); c++) 00288 { 00289 child = parent->child(c); 00290 libmesh_assert(child); 00291 libmesh_assert(!child->is_remote()); 00292 libmesh_assert_not_equal_to (child->processor_id(), DofObject::invalid_processor_id); 00293 parent->processor_id() = std::min(parent->processor_id(), 00294 child->processor_id()); 00295 } 00296 parent = parent->parent(); 00297 } 00298 } 00299 } 00300 00301 // When the mesh is parallel we cannot guarantee that parents have access to 00302 // all their children. 00303 else 00304 { 00305 // Setting subactive processor ids is easy: we can guarantee 00306 // that children have access to all their parents. 00307 00308 // Loop over all the active elements in the mesh 00309 MeshBase::element_iterator it = mesh.active_elements_begin(); 00310 const MeshBase::element_iterator end = mesh.active_elements_end(); 00311 00312 for ( ; it!=end; ++it) 00313 { 00314 Elem *child = *it; 00315 00316 std::vector<const Elem*> subactive_family; 00317 child->total_family_tree(subactive_family); 00318 for (unsigned int i = 0; i != subactive_family.size(); ++i) 00319 const_cast<Elem*>(subactive_family[i])->processor_id() = child->processor_id(); 00320 } 00321 00322 // When the mesh is parallel we cannot guarantee that parents have access to 00323 // all their children. 00324 00325 // We will use a brute-force approach here. Each processor finds its parent 00326 // elements and sets the parent pid to the minimum of its 00327 // semilocal descendants. 00328 // A global reduction is then performed to make sure the true minimum is found. 00329 // As noted, this is required because we cannot guarantee that a parent has 00330 // access to all its children on any single processor. 00331 parallel_only(); 00332 libmesh_assert(MeshTools::n_elem(mesh.unpartitioned_elements_begin(), 00333 mesh.unpartitioned_elements_end()) == 0); 00334 00335 const dof_id_type max_elem_id = mesh.max_elem_id(); 00336 00337 std::vector<processor_id_type> 00338 parent_processor_ids (std::min(communication_blocksize, 00339 max_elem_id)); 00340 00341 for (dof_id_type blk=0, last_elem_id=0; last_elem_id<max_elem_id; blk++) 00342 { 00343 last_elem_id = 00344 std::min(static_cast<dof_id_type>((blk+1)*communication_blocksize), 00345 max_elem_id); 00346 const dof_id_type first_elem_id = blk*communication_blocksize; 00347 00348 std::fill (parent_processor_ids.begin(), 00349 parent_processor_ids.end(), 00350 DofObject::invalid_processor_id); 00351 00352 // first build up local contributions to parent_processor_ids 00353 MeshBase::element_iterator not_it = mesh.ancestor_elements_begin(); 00354 const MeshBase::element_iterator not_end = mesh.ancestor_elements_end(); 00355 00356 bool have_parent_in_block = false; 00357 00358 for ( ; not_it != not_end; ++not_it) 00359 { 00360 Elem *parent = *not_it; 00361 00362 const dof_id_type parent_idx = parent->id(); 00363 libmesh_assert_less (parent_idx, max_elem_id); 00364 00365 if ((parent_idx >= first_elem_id) && 00366 (parent_idx < last_elem_id)) 00367 { 00368 have_parent_in_block = true; 00369 processor_id_type parent_pid = DofObject::invalid_processor_id; 00370 00371 std::vector<const Elem*> active_family; 00372 parent->active_family_tree(active_family); 00373 for (unsigned int i = 0; i != active_family.size(); ++i) 00374 parent_pid = std::min (parent_pid, active_family[i]->processor_id()); 00375 00376 const dof_id_type packed_idx = parent_idx - first_elem_id; 00377 libmesh_assert_less (packed_idx, parent_processor_ids.size()); 00378 00379 parent_processor_ids[packed_idx] = parent_pid; 00380 } 00381 } 00382 00383 // then find the global minimum 00384 CommWorld.min (parent_processor_ids); 00385 00386 // and assign the ids, if we have a parent in this block. 00387 if (have_parent_in_block) 00388 for (not_it = mesh.ancestor_elements_begin(); 00389 not_it != not_end; ++not_it) 00390 { 00391 Elem *parent = *not_it; 00392 00393 const dof_id_type parent_idx = parent->id(); 00394 00395 if ((parent_idx >= first_elem_id) && 00396 (parent_idx < last_elem_id)) 00397 { 00398 const dof_id_type packed_idx = parent_idx - first_elem_id; 00399 libmesh_assert_less (packed_idx, parent_processor_ids.size()); 00400 00401 const processor_id_type parent_pid = 00402 parent_processor_ids[packed_idx]; 00403 00404 libmesh_assert_not_equal_to (parent_pid, DofObject::invalid_processor_id); 00405 00406 parent->processor_id() = parent_pid; 00407 } 00408 } 00409 } 00410 } 00411 00412 #endif // LIBMESH_ENABLE_AMR 00413 00414 STOP_LOG("set_parent_processor_ids()","Partitioner"); 00415 } 00416 00417 00418 00419 void Partitioner::set_node_processor_ids(MeshBase& mesh) 00420 { 00421 START_LOG("set_node_processor_ids()","Partitioner"); 00422 00423 // This function must be run on all processors at once 00424 parallel_only(); 00425 00426 // If we have any unpartitioned elements at this 00427 // stage there is a problem 00428 libmesh_assert (MeshTools::n_elem(mesh.unpartitioned_elements_begin(), 00429 mesh.unpartitioned_elements_end()) == 0); 00430 00431 00432 // const dof_id_type orig_n_local_nodes = mesh.n_local_nodes(); 00433 00434 // libMesh::err << "[" << libMesh::processor_id() << "]: orig_n_local_nodes=" 00435 // << orig_n_local_nodes << std::endl; 00436 00437 // Build up request sets. Each node is currently owned by a processor because 00438 // it is connected to an element owned by that processor. However, during the 00439 // repartitioning phase that element may have been assigned a new processor id, but 00440 // it is still resident on the original processor. We need to know where to look 00441 // for new ids before assigning new ids, otherwise we may be asking the wrong processors 00442 // for the wrong information. 00443 // 00444 // The only remaining issue is what to do with unpartitioned nodes. Since they are required 00445 // to live on all processors we can simply rely on ourselves to number them properly. 00446 std::vector<std::vector<dof_id_type> > 00447 requested_node_ids(libMesh::n_processors()); 00448 00449 // Loop over all the nodes, count the ones on each processor. We can skip ourself 00450 std::vector<dof_id_type> ghost_nodes_from_proc(libMesh::n_processors(), 0); 00451 00452 MeshBase::node_iterator node_it = mesh.nodes_begin(); 00453 const MeshBase::node_iterator node_end = mesh.nodes_end(); 00454 00455 for (; node_it != node_end; ++node_it) 00456 { 00457 Node *node = *node_it; 00458 libmesh_assert(node); 00459 const processor_id_type current_pid = node->processor_id(); 00460 if (current_pid != libMesh::processor_id() && 00461 current_pid != DofObject::invalid_processor_id) 00462 { 00463 libmesh_assert_less (current_pid, ghost_nodes_from_proc.size()); 00464 ghost_nodes_from_proc[current_pid]++; 00465 } 00466 } 00467 00468 // We know how many objects live on each processor, so reserve() 00469 // space for each. 00470 for (processor_id_type pid=0; pid != libMesh::n_processors(); ++pid) 00471 requested_node_ids[pid].reserve(ghost_nodes_from_proc[pid]); 00472 00473 // We need to get the new pid for each node from the processor 00474 // which *currently* owns the node. We can safely skip ourself 00475 for (node_it = mesh.nodes_begin(); node_it != node_end; ++node_it) 00476 { 00477 Node *node = *node_it; 00478 libmesh_assert(node); 00479 const processor_id_type current_pid = node->processor_id(); 00480 if (current_pid != libMesh::processor_id() && 00481 current_pid != DofObject::invalid_processor_id) 00482 { 00483 libmesh_assert_less (current_pid, requested_node_ids.size()); 00484 libmesh_assert_less (requested_node_ids[current_pid].size(), 00485 ghost_nodes_from_proc[current_pid]); 00486 requested_node_ids[current_pid].push_back(node->id()); 00487 } 00488 00489 // Unset any previously-set node processor ids 00490 node->invalidate_processor_id(); 00491 } 00492 00493 // Loop over all the active elements 00494 MeshBase::element_iterator elem_it = mesh.active_elements_begin(); 00495 const MeshBase::element_iterator elem_end = mesh.active_elements_end(); 00496 00497 for ( ; elem_it != elem_end; ++elem_it) 00498 { 00499 Elem* elem = *elem_it; 00500 libmesh_assert(elem); 00501 00502 libmesh_assert_not_equal_to (elem->processor_id(), DofObject::invalid_processor_id); 00503 00504 // For each node, set the processor ID to the min of 00505 // its current value and this Element's processor id. 00506 // 00507 // TODO: we would probably get better parallel partitioning if 00508 // we did something like "min for even numbered nodes, max for 00509 // odd numbered". We'd need to be careful about how that would 00510 // affect solution ordering for I/O, though. 00511 for (unsigned int n=0; n<elem->n_nodes(); ++n) 00512 elem->get_node(n)->processor_id() = std::min(elem->get_node(n)->processor_id(), 00513 elem->processor_id()); 00514 } 00515 00516 // And loop over the subactive elements, but don't reassign 00517 // nodes that are already active on another processor. 00518 MeshBase::element_iterator sub_it = mesh.subactive_elements_begin(); 00519 const MeshBase::element_iterator sub_end = mesh.subactive_elements_end(); 00520 00521 for ( ; sub_it != sub_end; ++sub_it) 00522 { 00523 Elem* elem = *sub_it; 00524 libmesh_assert(elem); 00525 00526 libmesh_assert_not_equal_to (elem->processor_id(), DofObject::invalid_processor_id); 00527 00528 for (unsigned int n=0; n<elem->n_nodes(); ++n) 00529 if (elem->get_node(n)->processor_id() == DofObject::invalid_processor_id) 00530 elem->get_node(n)->processor_id() = elem->processor_id(); 00531 } 00532 00533 // Same for the inactive elements -- we will have already gotten most of these 00534 // nodes, *except* for the case of a parent with a subset of children which are 00535 // ghost elements. In that case some of the parent nodes will not have been 00536 // properly handled yet 00537 MeshBase::element_iterator not_it = mesh.not_active_elements_begin(); 00538 const MeshBase::element_iterator not_end = mesh.not_active_elements_end(); 00539 00540 for ( ; not_it != not_end; ++not_it) 00541 { 00542 Elem* elem = *not_it; 00543 libmesh_assert(elem); 00544 00545 libmesh_assert_not_equal_to (elem->processor_id(), DofObject::invalid_processor_id); 00546 00547 for (unsigned int n=0; n<elem->n_nodes(); ++n) 00548 if (elem->get_node(n)->processor_id() == DofObject::invalid_processor_id) 00549 elem->get_node(n)->processor_id() = elem->processor_id(); 00550 } 00551 00552 // We can't assert that all nodes are connected to elements, because 00553 // a ParallelMesh with NodeConstraints might have pulled in some 00554 // remote nodes solely for evaluating those constraints. 00555 // MeshTools::libmesh_assert_connected_nodes(mesh); 00556 00557 // For such nodes, we'll do a sanity check later when making sure 00558 // that we successfully reset their processor ids to something 00559 // valid. 00560 00561 // Next set node ids from other processors, excluding self 00562 for (processor_id_type p=1; p != libMesh::n_processors(); ++p) 00563 { 00564 // Trade my requests with processor procup and procdown 00565 processor_id_type procup = (libMesh::processor_id() + p) % 00566 libMesh::n_processors(); 00567 processor_id_type procdown = (libMesh::n_processors() + 00568 libMesh::processor_id() - p) % 00569 libMesh::n_processors(); 00570 std::vector<dof_id_type> request_to_fill; 00571 CommWorld.send_receive(procup, requested_node_ids[procup], 00572 procdown, request_to_fill); 00573 00574 // Fill those requests in-place 00575 for (std::size_t i=0; i != request_to_fill.size(); ++i) 00576 { 00577 Node *node = mesh.node_ptr(request_to_fill[i]); 00578 libmesh_assert(node); 00579 const processor_id_type new_pid = node->processor_id(); 00580 libmesh_assert_not_equal_to (new_pid, DofObject::invalid_processor_id); 00581 libmesh_assert_less (new_pid, mesh.n_partitions()); // this is the correct test -- 00582 request_to_fill[i] = new_pid; // the number of partitions may 00583 } // not equal the number of processors 00584 00585 // Trade back the results 00586 std::vector<dof_id_type> filled_request; 00587 CommWorld.send_receive(procdown, request_to_fill, 00588 procup, filled_request); 00589 libmesh_assert_equal_to (filled_request.size(), requested_node_ids[procup].size()); 00590 00591 // And copy the id changes we've now been informed of 00592 for (std::size_t i=0; i != filled_request.size(); ++i) 00593 { 00594 Node *node = mesh.node_ptr(requested_node_ids[procup][i]); 00595 libmesh_assert(node); 00596 libmesh_assert_less (filled_request[i], mesh.n_partitions()); // this is the correct test -- 00597 node->processor_id(filled_request[i]); // the number of partitions may 00598 } // not equal the number of processors 00599 } 00600 00601 #ifdef DEBUG 00602 MeshTools::libmesh_assert_valid_procids<Node>(mesh); 00603 #endif 00604 00605 STOP_LOG("set_node_processor_ids()","Partitioner"); 00606 } 00607 00608 } // namespace libMesh
Site Created By: libMesh Developers
Last modified: February 05 2013 19:54:48 UTC
Hosted By: