$extrastylesheet
00001 // The libMesh Finite Element Library. 00002 // Copyright (C) 2002-2014 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/libmesh_config.h" 00024 #include "libmesh/mesh_base.h" 00025 #include "libmesh/metis_partitioner.h" 00026 #include "libmesh/libmesh_logging.h" 00027 #include "libmesh/elem.h" 00028 #include "libmesh/mesh_communication.h" 00029 #include "libmesh/error_vector.h" 00030 #include "libmesh/vectormap.h" 00031 #include "libmesh/metis_csr_graph.h" 00032 00033 #ifdef LIBMESH_HAVE_METIS 00034 // MIPSPro 7.4.2 gets confused about these nested namespaces 00035 # ifdef __sgi 00036 # include <cstdarg> 00037 # endif 00038 namespace Metis { 00039 extern "C" { 00040 # include "libmesh/ignore_warnings.h" 00041 # include "metis.h" 00042 # include "libmesh/restore_warnings.h" 00043 } 00044 } 00045 #else 00046 # include "libmesh/sfc_partitioner.h" 00047 #endif 00048 00049 00050 00051 00052 namespace libMesh 00053 { 00054 00055 00056 // ------------------------------------------------------------ 00057 // MetisPartitioner implementation 00058 void MetisPartitioner::_do_partition (MeshBase& mesh, 00059 const unsigned int n_pieces) 00060 { 00061 libmesh_assert_greater (n_pieces, 0); 00062 libmesh_assert (mesh.is_serial()); 00063 00064 // Check for an easy return 00065 if (n_pieces == 1) 00066 { 00067 this->single_partition (mesh); 00068 return; 00069 } 00070 00071 // What to do if the Metis library IS NOT present 00072 #ifndef LIBMESH_HAVE_METIS 00073 00074 libmesh_here(); 00075 libMesh::err << "ERROR: The library has been built without" << std::endl 00076 << "Metis support. Using a space-filling curve" << std::endl 00077 << "partitioner instead!" << std::endl; 00078 00079 SFCPartitioner sfcp; 00080 00081 sfcp.partition (mesh, n_pieces); 00082 00083 // What to do if the Metis library IS present 00084 #else 00085 00086 START_LOG("partition()", "MetisPartitioner"); 00087 00088 const dof_id_type n_active_elem = mesh.n_active_elem(); 00089 00090 // build the graph 00091 // std::vector<int> options(5); 00092 std::vector<int> vwgt(n_active_elem); 00093 std::vector<int> part(n_active_elem); 00094 00095 int 00096 n = static_cast<int>(n_active_elem), // number of "nodes" (elements) 00097 // in the graph 00098 // wgtflag = 2, // weights on vertices only, 00099 // // none on edges 00100 // numflag = 0, // C-style 0-based numbering 00101 nparts = static_cast<int>(n_pieces), // number of subdomains to create 00102 edgecut = 0; // the numbers of edges cut by the 00103 // resulting partition 00104 00105 // Set the options 00106 // options[0] = 0; // use default options 00107 00108 // Metis will only consider the active elements. 00109 // We need to map the active element ids into a 00110 // contiguous range. Further, we want the unique range indexing to be 00111 // independednt of the element ordering, otherwise a circular dependency 00112 // can result in which the partitioning depends on the ordering which 00113 // depends on the partitioning... 00114 vectormap<dof_id_type, dof_id_type> global_index_map; 00115 global_index_map.reserve (n_active_elem); 00116 00117 { 00118 std::vector<dof_id_type> global_index; 00119 00120 MeshBase::element_iterator it = mesh.active_elements_begin(); 00121 const MeshBase::element_iterator end = mesh.active_elements_end(); 00122 00123 MeshCommunication().find_global_indices (mesh.comm(), 00124 MeshTools::bounding_box(mesh), 00125 it, end, global_index); 00126 00127 libmesh_assert_equal_to (global_index.size(), n_active_elem); 00128 00129 for (std::size_t cnt=0; it != end; ++it) 00130 { 00131 const Elem *elem = *it; 00132 00133 global_index_map.insert (std::make_pair(elem->id(), global_index[cnt++])); 00134 } 00135 libmesh_assert_equal_to (global_index_map.size(), n_active_elem); 00136 } 00137 00138 00139 // Invoke METIS, but only on processor 0. 00140 // Then broadcast the resulting decomposition 00141 if (mesh.processor_id() == 0) 00142 { 00143 METIS_CSR_Graph csr_graph; 00144 00145 csr_graph.offsets.resize(n_active_elem+1, 0); 00146 00147 // Local scope for these 00148 { 00149 // build the graph in CSR format. Note that 00150 // the edges in the graph will correspond to 00151 // face neighbors 00152 00153 #ifdef LIBMESH_ENABLE_AMR 00154 std::vector<const Elem*> neighbors_offspring; 00155 #endif 00156 00157 MeshBase::element_iterator elem_it = mesh.active_elements_begin(); 00158 const MeshBase::element_iterator elem_end = mesh.active_elements_end(); 00159 00160 #ifndef NDEBUG 00161 std::size_t graph_size=0; 00162 #endif 00163 00164 // (1) first pass - get the row sizes for each element by counting the number 00165 // of face neighbors. Also populate the vwght array if necessary 00166 for (; elem_it != elem_end; ++elem_it) 00167 { 00168 const Elem* elem = *elem_it; 00169 00170 const dof_id_type elem_global_index = 00171 global_index_map[elem->id()]; 00172 00173 libmesh_assert_less (elem_global_index, vwgt.size()); 00174 00175 // maybe there is a better weight? 00176 // The weight is used to define what a balanced graph is 00177 if(!_weights) 00178 vwgt[elem_global_index] = elem->n_nodes(); 00179 else 00180 vwgt[elem_global_index] = static_cast<int>((*_weights)[elem->id()]); 00181 00182 unsigned int num_neighbors = 0; 00183 00184 // Loop over the element's neighbors. An element 00185 // adjacency corresponds to a face neighbor 00186 for (unsigned int ms=0; ms<elem->n_neighbors(); ms++) 00187 { 00188 const Elem* neighbor = elem->neighbor(ms); 00189 00190 if (neighbor != NULL) 00191 { 00192 // If the neighbor is active treat it 00193 // as a connection 00194 if (neighbor->active()) 00195 num_neighbors++; 00196 00197 #ifdef LIBMESH_ENABLE_AMR 00198 00199 // Otherwise we need to find all of the 00200 // neighbor's children that are connected to 00201 // us and add them 00202 else 00203 { 00204 // The side of the neighbor to which 00205 // we are connected 00206 const unsigned int ns = 00207 neighbor->which_neighbor_am_i (elem); 00208 libmesh_assert_less (ns, neighbor->n_neighbors()); 00209 00210 // Get all the active children (& grandchildren, etc...) 00211 // of the neighbor. 00212 neighbor->active_family_tree (neighbors_offspring); 00213 00214 // Get all the neighbor's children that 00215 // live on that side and are thus connected 00216 // to us 00217 for (unsigned int nc=0; nc<neighbors_offspring.size(); nc++) 00218 { 00219 const Elem* child = 00220 neighbors_offspring[nc]; 00221 00222 // This does not assume a level-1 mesh. 00223 // Note that since children have sides numbered 00224 // coincident with the parent then this is a sufficient test. 00225 if (child->neighbor(ns) == elem) 00226 { 00227 libmesh_assert (child->active()); 00228 num_neighbors++; 00229 } 00230 } 00231 } 00232 00233 #endif /* ifdef LIBMESH_ENABLE_AMR */ 00234 00235 } 00236 } 00237 00238 csr_graph.prep_n_nonzeros(elem_global_index, num_neighbors); 00239 #ifndef NDEBUG 00240 graph_size += num_neighbors; 00241 #endif 00242 } 00243 00244 csr_graph.prepare_for_use(); 00245 00246 // (2) second pass - fill the compressed adjacency array 00247 elem_it = mesh.active_elements_begin(); 00248 00249 for (; elem_it != elem_end; ++elem_it) 00250 { 00251 const Elem* elem = *elem_it; 00252 00253 const dof_id_type elem_global_index = 00254 global_index_map[elem->id()]; 00255 00256 unsigned int connection=0; 00257 00258 // Loop over the element's neighbors. An element 00259 // adjacency corresponds to a face neighbor 00260 for (unsigned int ms=0; ms<elem->n_neighbors(); ms++) 00261 { 00262 const Elem* neighbor = elem->neighbor(ms); 00263 00264 if (neighbor != NULL) 00265 { 00266 // If the neighbor is active treat it 00267 // as a connection 00268 if (neighbor->active()) 00269 csr_graph(elem_global_index, connection++) = global_index_map[neighbor->id()]; 00270 00271 #ifdef LIBMESH_ENABLE_AMR 00272 00273 // Otherwise we need to find all of the 00274 // neighbor's children that are connected to 00275 // us and add them 00276 else 00277 { 00278 // The side of the neighbor to which 00279 // we are connected 00280 const unsigned int ns = 00281 neighbor->which_neighbor_am_i (elem); 00282 libmesh_assert_less (ns, neighbor->n_neighbors()); 00283 00284 // Get all the active children (& grandchildren, etc...) 00285 // of the neighbor. 00286 neighbor->active_family_tree (neighbors_offspring); 00287 00288 // Get all the neighbor's children that 00289 // live on that side and are thus connected 00290 // to us 00291 for (unsigned int nc=0; nc<neighbors_offspring.size(); nc++) 00292 { 00293 const Elem* child = 00294 neighbors_offspring[nc]; 00295 00296 // This does not assume a level-1 mesh. 00297 // Note that since children have sides numbered 00298 // coincident with the parent then this is a sufficient test. 00299 if (child->neighbor(ns) == elem) 00300 { 00301 libmesh_assert (child->active()); 00302 00303 csr_graph(elem_global_index, connection++) = global_index_map[child->id()]; 00304 } 00305 } 00306 } 00307 00308 #endif /* ifdef LIBMESH_ENABLE_AMR */ 00309 00310 } 00311 } 00312 } 00313 00314 // We create a non-empty vals for a disconnected graph, to 00315 // work around a segfault from METIS. 00316 libmesh_assert_equal_to (csr_graph.vals.size(), 00317 std::max(graph_size,std::size_t(1))); 00318 } // done building the graph 00319 00320 int ncon = 1; 00321 00322 // Select which type of partitioning to create 00323 00324 // Use recursive if the number of partitions is less than or equal to 8 00325 if (n_pieces <= 8) 00326 Metis::METIS_PartGraphRecursive(&n, &ncon, &csr_graph.offsets[0], &csr_graph.vals[0], &vwgt[0], NULL, 00327 NULL, &nparts, NULL, NULL, NULL, 00328 &edgecut, &part[0]); 00329 00330 // Otherwise use kway 00331 else 00332 Metis::METIS_PartGraphKway(&n, &ncon, &csr_graph.offsets[0], &csr_graph.vals[0], &vwgt[0], NULL, 00333 NULL, &nparts, NULL, NULL, NULL, 00334 &edgecut, &part[0]); 00335 00336 } // end processor 0 part 00337 00338 // Broadcase the resutling partition 00339 mesh.comm().broadcast(part); 00340 00341 // Assign the returned processor ids. The part array contains 00342 // the processor id for each active element, but in terms of 00343 // the contiguous indexing we defined above 00344 { 00345 MeshBase::element_iterator it = mesh.active_elements_begin(); 00346 const MeshBase::element_iterator end = mesh.active_elements_end(); 00347 00348 for (; it!=end; ++it) 00349 { 00350 Elem* elem = *it; 00351 00352 libmesh_assert (global_index_map.count(elem->id())); 00353 00354 const dof_id_type elem_global_index = 00355 global_index_map[elem->id()]; 00356 00357 libmesh_assert_less (elem_global_index, part.size()); 00358 const processor_id_type elem_procid = 00359 static_cast<processor_id_type>(part[elem_global_index]); 00360 00361 elem->processor_id() = elem_procid; 00362 } 00363 } 00364 00365 STOP_LOG("partition()", "MetisPartitioner"); 00366 #endif 00367 } 00368 00369 } // namespace libMesh