$extrastylesheet
metis_partitioner.C
Go to the documentation of this file.
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