metis_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 #include <map> 00022 00023 // Local Includes ----------------------------------- 00024 #include "libmesh/libmesh_config.h" 00025 #include "libmesh/mesh_base.h" 00026 #include "libmesh/metis_partitioner.h" 00027 #include "libmesh/libmesh_logging.h" 00028 #include "libmesh/elem.h" 00029 #include "libmesh/mesh_communication.h" 00030 #include "libmesh/error_vector.h" 00031 00032 #ifdef LIBMESH_HAVE_METIS 00033 // MIPSPro 7.4.2 gets confused about these nested namespaces 00034 # ifdef __sgi 00035 # include <cstdarg> 00036 # endif 00037 namespace Metis { 00038 extern "C" { 00039 # include "libmesh/ignore_warnings.h" 00040 # include "metis.h" 00041 # include "libmesh/restore_warnings.h" 00042 } 00043 } 00044 #else 00045 # include "libmesh/sfc_partitioner.h" 00046 #endif 00047 00048 00049 namespace libMesh 00050 { 00051 00052 00053 // ------------------------------------------------------------ 00054 // MetisPartitioner implementation 00055 void MetisPartitioner::_do_partition (MeshBase& mesh, 00056 const unsigned int n_pieces) 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 } 00306 00307 } // namespace libMesh 00308 00309
Site Created By: libMesh Developers
Last modified: February 05 2013 19:54:47 UTC
Hosted By: