libMesh::MetisPartitioner Class Reference
#include <metis_partitioner.h>

Public Member Functions | |
| MetisPartitioner () | |
| virtual AutoPtr< Partitioner > | clone () const |
| virtual void | attach_weights (ErrorVector *weights) |
| void | partition (MeshBase &mesh, const unsigned int n=libMesh::n_processors()) |
| void | repartition (MeshBase &mesh, const unsigned int n=libMesh::n_processors()) |
Static Public Member Functions | |
| static void | partition_unpartitioned_elements (MeshBase &mesh, const unsigned int n=libMesh::n_processors()) |
| static void | set_parent_processor_ids (MeshBase &mesh) |
| static void | set_node_processor_ids (MeshBase &mesh) |
Protected Member Functions | |
| virtual void | _do_partition (MeshBase &mesh, const unsigned int n) |
| void | single_partition (MeshBase &mesh) |
| virtual void | _do_repartition (MeshBase &mesh, const unsigned int n) |
Protected Attributes | |
| ErrorVector * | _weights |
Static Protected Attributes | |
| static const dof_id_type | communication_blocksize = 1000000 |
Detailed Description
The MetisPartitioner uses the Metis graph partitioner to partition the elements.
Definition at line 38 of file metis_partitioner.h.
Constructor & Destructor Documentation
| libMesh::MetisPartitioner::MetisPartitioner | ( | ) | [inline] |
Member Function Documentation
| void libMesh::MetisPartitioner::_do_partition | ( | MeshBase & | mesh, | |
| const unsigned int | n | |||
| ) | [protected, virtual] |
Partition the MeshBase into n subdomains.
Implements libMesh::Partitioner.
Definition at line 55 of file metis_partitioner.C.
References libMesh::Partitioner::_weights, libMesh::Elem::active(), libMesh::MeshBase::active_elements_begin(), libMesh::MeshBase::active_elements_end(), libMesh::Elem::active_family_tree(), libMesh::MeshTools::bounding_box(), end, libMesh::err, libMesh::DofObject::id(), libMesh::MeshBase::is_serial(), libMesh::MeshBase::n_active_elem(), libMesh::Elem::n_neighbors(), libMesh::Elem::n_nodes(), libMesh::Elem::neighbor(), libMesh::Partitioner::partition(), libMesh::DofObject::processor_id(), libMesh::Partitioner::single_partition(), and libMesh::Elem::which_neighbor_am_i().
00057 { 00058 libmesh_assert_greater (n_pieces, 0); 00059 libmesh_assert (mesh.is_serial()); 00060 00061 // Check for an easy return 00062 if (n_pieces == 1) 00063 { 00064 this->single_partition (mesh); 00065 return; 00066 } 00067 00068 // What to do if the Metis library IS NOT present 00069 #ifndef LIBMESH_HAVE_METIS 00070 00071 libmesh_here(); 00072 libMesh::err << "ERROR: The library has been built without" << std::endl 00073 << "Metis support. Using a space-filling curve" << std::endl 00074 << "partitioner instead!" << std::endl; 00075 00076 SFCPartitioner sfcp; 00077 00078 sfcp.partition (mesh, n_pieces); 00079 00080 // What to do if the Metis library IS present 00081 #else 00082 00083 START_LOG("partition()", "MetisPartitioner"); 00084 00085 const dof_id_type n_active_elem = mesh.n_active_elem(); 00086 00087 // build the graph 00088 // std::vector<int> options(5); 00089 std::vector<int> vwgt(n_active_elem); 00090 std::vector<int> part(n_active_elem); 00091 00092 int 00093 n = static_cast<int>(n_active_elem), // number of "nodes" (elements) 00094 // in the graph 00095 // wgtflag = 2, // weights on vertices only, 00096 // // none on edges 00097 // numflag = 0, // C-style 0-based numbering 00098 nparts = static_cast<int>(n_pieces), // number of subdomains to create 00099 edgecut = 0; // the numbers of edges cut by the 00100 // resulting partition 00101 00102 // Set the options 00103 // options[0] = 0; // use default options 00104 00105 // Metis will only consider the active elements. 00106 // We need to map the active element ids into a 00107 // contiguous range. Further, we want the unique range indexing to be 00108 // independednt of the element ordering, otherwise a circular dependency 00109 // can result in which the partitioning depends on the ordering which 00110 // depends on the partitioning... 00111 std::map<const Elem*, dof_id_type> global_index_map; 00112 { 00113 std::vector<dof_id_type> global_index; 00114 00115 MeshBase::element_iterator it = mesh.active_elements_begin(); 00116 const MeshBase::element_iterator end = mesh.active_elements_end(); 00117 00118 MeshCommunication().find_global_indices (MeshTools::bounding_box(mesh), 00119 it, end, global_index); 00120 00121 libmesh_assert_equal_to (global_index.size(), n_active_elem); 00122 00123 for (std::size_t cnt=0; it != end; ++it) 00124 { 00125 const Elem *elem = *it; 00126 libmesh_assert (!global_index_map.count(elem)); 00127 00128 global_index_map[elem] = global_index[cnt++]; 00129 } 00130 libmesh_assert_equal_to (global_index_map.size(), n_active_elem); 00131 } 00132 00133 00134 // build the graph in CSR format. Note that 00135 // the edges in the graph will correspond to 00136 // face neighbors 00137 std::vector<int> xadj, adjncy; 00138 { 00139 std::vector<const Elem*> neighbors_offspring; 00140 00141 MeshBase::element_iterator elem_it = mesh.active_elements_begin(); 00142 const MeshBase::element_iterator elem_end = mesh.active_elements_end(); 00143 00144 // This will be exact when there is no refinement and all the 00145 // elements are of the same type. 00146 std::size_t graph_size=0; 00147 std::vector<std::vector<dof_id_type> > graph(n_active_elem); 00148 00149 for (; elem_it != elem_end; ++elem_it) 00150 { 00151 const Elem* elem = *elem_it; 00152 00153 libmesh_assert (global_index_map.count(elem)); 00154 00155 const dof_id_type elem_global_index = 00156 global_index_map[elem]; 00157 00158 libmesh_assert_less (elem_global_index, vwgt.size()); 00159 libmesh_assert_less (elem_global_index, graph.size()); 00160 00161 // maybe there is a better weight? 00162 // The weight is used to define what a balanced graph is 00163 if(!_weights) 00164 vwgt[elem_global_index] = elem->n_nodes(); 00165 else 00166 vwgt[elem_global_index] = static_cast<int>((*_weights)[elem->id()]); 00167 00168 // Loop over the element's neighbors. An element 00169 // adjacency corresponds to a face neighbor 00170 for (unsigned int ms=0; ms<elem->n_neighbors(); ms++) 00171 { 00172 const Elem* neighbor = elem->neighbor(ms); 00173 00174 if (neighbor != NULL) 00175 { 00176 // If the neighbor is active treat it 00177 // as a connection 00178 if (neighbor->active()) 00179 { 00180 libmesh_assert (global_index_map.count(neighbor)); 00181 00182 const dof_id_type neighbor_global_index = 00183 global_index_map[neighbor]; 00184 00185 graph[elem_global_index].push_back(neighbor_global_index); 00186 graph_size++; 00187 } 00188 00189 #ifdef LIBMESH_ENABLE_AMR 00190 00191 // Otherwise we need to find all of the 00192 // neighbor's children that are connected to 00193 // us and add them 00194 else 00195 { 00196 // The side of the neighbor to which 00197 // we are connected 00198 const unsigned int ns = 00199 neighbor->which_neighbor_am_i (elem); 00200 libmesh_assert_less (ns, neighbor->n_neighbors()); 00201 00202 // Get all the active children (& grandchildren, etc...) 00203 // of the neighbor. 00204 neighbor->active_family_tree (neighbors_offspring); 00205 00206 // Get all the neighbor's children that 00207 // live on that side and are thus connected 00208 // to us 00209 for (unsigned int nc=0; nc<neighbors_offspring.size(); nc++) 00210 { 00211 const Elem* child = 00212 neighbors_offspring[nc]; 00213 00214 // This does not assume a level-1 mesh. 00215 // Note that since children have sides numbered 00216 // coincident with the parent then this is a sufficient test. 00217 if (child->neighbor(ns) == elem) 00218 { 00219 libmesh_assert (child->active()); 00220 libmesh_assert (global_index_map.count(child)); 00221 00222 const dof_id_type child_global_index = 00223 global_index_map[child]; 00224 00225 graph[elem_global_index].push_back(child_global_index); 00226 graph_size++; 00227 } 00228 } 00229 } 00230 00231 #endif /* ifdef LIBMESH_ENABLE_AMR */ 00232 00233 } 00234 } 00235 } 00236 00237 // Convert the graph into the format Metis wants 00238 xadj.reserve(n_active_elem+1); 00239 adjncy.reserve(graph_size); 00240 00241 for (std::size_t r=0; r<graph.size(); r++) 00242 { 00243 xadj.push_back(adjncy.size()); 00244 std::vector<dof_id_type> graph_row; // build this emtpy 00245 graph_row.swap(graph[r]); // this will deallocate at the end of scope 00246 adjncy.insert(adjncy.end(), 00247 graph_row.begin(), 00248 graph_row.end()); 00249 } 00250 00251 // The end of the adjacency array for the last elem 00252 xadj.push_back(adjncy.size()); 00253 00254 libmesh_assert_equal_to (adjncy.size(), graph_size); 00255 libmesh_assert_equal_to (xadj.size(), n_active_elem+1); 00256 } // done building the graph 00257 00258 00259 if (adjncy.empty()) 00260 adjncy.push_back(0); 00261 00262 int ncon = 1; 00263 00264 // Select which type of partitioning to create 00265 00266 // Use recursive if the number of partitions is less than or equal to 8 00267 if (n_pieces <= 8) 00268 Metis::METIS_PartGraphRecursive(&n, &ncon, &xadj[0], &adjncy[0], &vwgt[0], NULL, 00269 NULL, &nparts, NULL, NULL, NULL, 00270 &edgecut, &part[0]); 00271 00272 // Otherwise use kway 00273 else 00274 Metis::METIS_PartGraphKway(&n, &ncon, &xadj[0], &adjncy[0], &vwgt[0], NULL, 00275 NULL, &nparts, NULL, NULL, NULL, 00276 &edgecut, &part[0]); 00277 00278 00279 // Assign the returned processor ids. The part array contains 00280 // the processor id for each active element, but in terms of 00281 // the contiguous indexing we defined above 00282 { 00283 MeshBase::element_iterator it = mesh.active_elements_begin(); 00284 const MeshBase::element_iterator end = mesh.active_elements_end(); 00285 00286 for (; it!=end; ++it) 00287 { 00288 Elem* elem = *it; 00289 00290 libmesh_assert (global_index_map.count(elem)); 00291 00292 const dof_id_type elem_global_index = 00293 global_index_map[elem]; 00294 00295 libmesh_assert_less (elem_global_index, part.size()); 00296 const processor_id_type elem_procid = 00297 static_cast<processor_id_type>(part[elem_global_index]); 00298 00299 elem->processor_id() = elem_procid; 00300 } 00301 } 00302 00303 STOP_LOG("partition()", "MetisPartitioner"); 00304 #endif 00305 }
| virtual void libMesh::Partitioner::_do_repartition | ( | MeshBase & | mesh, | |
| const unsigned int | n | |||
| ) | [inline, protected, virtual, inherited] |
This is the actual re-partitioning method which can be overloaded in derived classes. Note that the default behavior is to simply call the partition function.
Reimplemented in libMesh::ParmetisPartitioner.
Definition at line 143 of file partitioner.h.
References libMesh::Partitioner::_do_partition().
Referenced by libMesh::Partitioner::repartition().
00144 { this->_do_partition (mesh, n); }
| virtual void libMesh::MetisPartitioner::attach_weights | ( | ErrorVector * | ) | [inline, virtual] |
Attach weights that can be used for partitioning. This ErrorVector should be _exactly_ the same on every processor and should have mesh->max_elem_id() entries.
Reimplemented from libMesh::Partitioner.
Definition at line 57 of file metis_partitioner.h.
References libMesh::Partitioner::_weights.
00057 { _weights = weights; }
| virtual AutoPtr<Partitioner> libMesh::MetisPartitioner::clone | ( | ) | const [inline, virtual] |
Creates a new partitioner of this type and returns it in an AutoPtr.
Implements libMesh::Partitioner.
Definition at line 51 of file metis_partitioner.h.
References MetisPartitioner().
00051 { 00052 AutoPtr<Partitioner> cloned_partitioner 00053 (new MetisPartitioner()); 00054 return cloned_partitioner; 00055 }
| void libMesh::Partitioner::partition | ( | MeshBase & | mesh, | |
| const unsigned int | n = libMesh::n_processors() | |||
| ) | [inherited] |
Partition the MeshBase into n parts. If the user does not specify a number of pieces into which the mesh should be partitioned, then the default behavior of the partitioner is to partition according to the number of processors defined in libMesh::n_processors(). The partitioner currently does not modify the subdomain_id of each element. This number is reserved for things like material properties, etc.
Definition at line 48 of file partitioner.C.
References libMesh::Partitioner::_do_partition(), libMesh::MeshTools::libmesh_assert_valid_procids< Elem >(), libMesh::MeshTools::libmesh_assert_valid_remote_elems(), std::min(), libMesh::MeshBase::n_active_elem(), libMesh::Partitioner::partition_unpartitioned_elements(), libMesh::MeshBase::redistribute(), libMesh::MeshBase::set_n_partitions(), libMesh::Partitioner::set_node_processor_ids(), libMesh::Partitioner::set_parent_processor_ids(), libMesh::Partitioner::single_partition(), and libMesh::MeshBase::update_post_partitioning().
Referenced by libMesh::SFCPartitioner::_do_partition(), _do_partition(), and libMesh::ParmetisPartitioner::_do_repartition().
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 }
| void libMesh::Partitioner::partition_unpartitioned_elements | ( | MeshBase & | mesh, | |
| const unsigned int | n = libMesh::n_processors() | |||
| ) | [static, inherited] |
This function
Definition at line 168 of file partitioner.C.
References libMesh::MeshTools::bounding_box(), end, libMesh::MeshTools::n_elem(), libMesh::n_processors(), libMesh::DofObject::processor_id(), libMesh::MeshBase::unpartitioned_elements_begin(), and libMesh::MeshBase::unpartitioned_elements_end().
Referenced by libMesh::Partitioner::partition(), and libMesh::Partitioner::repartition().
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 }
| void libMesh::Partitioner::repartition | ( | MeshBase & | mesh, | |
| const unsigned int | n = libMesh::n_processors() | |||
| ) | [inherited] |
Repartitions the MeshBase into n parts. This is required since some partitoning algorithms can repartition more efficiently than computing a new partitioning from scratch. The default behavior is to simply call this->partition(n)
Definition at line 110 of file partitioner.C.
References libMesh::Partitioner::_do_repartition(), std::min(), libMesh::MeshBase::n_active_elem(), libMesh::Partitioner::partition_unpartitioned_elements(), libMesh::MeshBase::set_n_partitions(), libMesh::Partitioner::set_node_processor_ids(), libMesh::Partitioner::set_parent_processor_ids(), and libMesh::Partitioner::single_partition().
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 }
| void libMesh::Partitioner::set_node_processor_ids | ( | MeshBase & | mesh | ) | [static, inherited] |
This function is called after partitioning to set the processor IDs for the nodes. By definition, a Node's processor ID is the minimum processor ID for all of the elements which share the node.
Definition at line 419 of file partitioner.C.
References libMesh::MeshBase::active_elements_begin(), libMesh::MeshBase::active_elements_end(), libMesh::CommWorld, libMesh::Elem::get_node(), libMesh::DofObject::id(), libMesh::DofObject::invalid_processor_id, libMesh::DofObject::invalidate_processor_id(), libMesh::MeshTools::libmesh_assert_valid_procids< Node >(), std::min(), libMesh::MeshTools::n_elem(), libMesh::Elem::n_nodes(), libMesh::MeshBase::n_partitions(), libMesh::n_processors(), libMesh::MeshBase::node_ptr(), libMesh::MeshBase::nodes_begin(), libMesh::MeshBase::nodes_end(), libMesh::MeshBase::not_active_elements_begin(), libMesh::MeshBase::not_active_elements_end(), libMesh::processor_id(), libMesh::DofObject::processor_id(), libMesh::Parallel::Communicator::send_receive(), libMesh::MeshBase::subactive_elements_begin(), libMesh::MeshBase::subactive_elements_end(), libMesh::MeshBase::unpartitioned_elements_begin(), and libMesh::MeshBase::unpartitioned_elements_end().
Referenced by libMesh::UnstructuredMesh::all_first_order(), libMesh::Partitioner::partition(), libMesh::XdrIO::read(), libMesh::Partitioner::repartition(), and libMesh::BoundaryInfo::sync().
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 }
| static void libMesh::Partitioner::set_parent_processor_ids | ( | MeshBase & | mesh | ) | [static, inherited] |
This function is called after partitioning to set the processor IDs for the inactive parent elements. A Parent's processor ID is the same as its first child.
Referenced by libMesh::Partitioner::partition(), and libMesh::Partitioner::repartition().
| void libMesh::Partitioner::single_partition | ( | MeshBase & | mesh | ) | [protected, inherited] |
Trivially "partitions" the mesh for one processor. Simply loops through the elements and assigns all of them to processor 0. Is is provided as a separate function so that derived classes may use it without reimplementing it.
Definition at line 145 of file partitioner.C.
References libMesh::MeshBase::elements_begin(), libMesh::MeshBase::elements_end(), libMesh::MeshBase::nodes_begin(), and libMesh::MeshBase::nodes_end().
Referenced by libMesh::SFCPartitioner::_do_partition(), _do_partition(), libMesh::LinearPartitioner::_do_partition(), libMesh::CentroidPartitioner::_do_partition(), libMesh::ParmetisPartitioner::_do_repartition(), libMesh::Partitioner::partition(), and libMesh::Partitioner::repartition().
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 }
Member Data Documentation
ErrorVector* libMesh::Partitioner::_weights [protected, inherited] |
The weights that might be used for partitioning.
Definition at line 155 of file partitioner.h.
Referenced by _do_partition(), and attach_weights().
const dof_id_type libMesh::Partitioner::communication_blocksize = 1000000 [static, protected, inherited] |
The blocksize to use when doing blocked parallel communication. This limits the maximum vector size which can be used in a single communication step.
Definition at line 150 of file partitioner.h.
The documentation for this class was generated from the following files:
Site Created By: libMesh Developers
Last modified: February 05 2013 19:55:30 UTC
Hosted By: