21 #include "libmesh/metis_partitioner.h" 23 #include "libmesh/libmesh_config.h" 24 #include "libmesh/elem.h" 25 #include "libmesh/enum_partitioner_type.h" 26 #include "libmesh/error_vector.h" 27 #include "libmesh/libmesh_logging.h" 28 #include "libmesh/mesh_base.h" 29 #include "libmesh/mesh_communication.h" 30 #include "libmesh/mesh_tools.h" 31 #include "libmesh/metis_csr_graph.h" 32 #include "libmesh/parallel.h" 33 #include "libmesh/utility.h" 34 #include "libmesh/vectormap.h" 36 #ifdef LIBMESH_HAVE_METIS 43 # include "libmesh/ignore_warnings.h" 45 # include "libmesh/restore_warnings.h" 49 # include "libmesh/sfc_partitioner.h" 55 #include <unordered_map> 72 unsigned int n_pieces)
84 libmesh_assert_greater (n_pieces, 0);
87 if (!
mesh.is_serial())
89 libMesh::out <<
"WARNING: Forced to gather a distributed mesh for METIS" << std::endl;
94 #ifndef LIBMESH_HAVE_METIS 97 libMesh::out <<
"ERROR: The library has been built without" << std::endl
98 <<
"Metis support. Using a space-filling curve" << std::endl
99 <<
"partitioner instead!" << std::endl;);
107 LOG_SCOPE(
"partition_range()",
"MetisPartitioner");
118 global_index_map.reserve (n_range_elem);
121 std::vector<dof_id_type> global_index;
125 beg, end, global_index);
127 libmesh_assert_equal_to (global_index.size(), n_range_elem);
131 #ifdef LIBMESH_ENABLE_UNIQUE_ID 133 for (
const auto & elem :
as_range(beg, end))
134 global_index_map.emplace(elem->id(), global_index[cnt++]);
141 std::unordered_map<dof_id_type, std::vector<dof_id_type>>
142 global_index_inverses;
143 bool found_duplicate_indices =
false;
146 for (
const auto & elem :
as_range(beg, end))
148 auto & vec = global_index_inverses[global_index[cnt++]];
150 found_duplicate_indices =
true;
151 vec.push_back(elem->id());
158 if (found_duplicate_indices)
160 const std::map<dof_id_type, std::vector<dof_id_type>>
161 sorted_inverses(global_index_inverses.begin(),
162 global_index_inverses.end());
163 std::size_t new_global_index=0;
164 for (
const auto & [index, elem_ids] : sorted_inverses)
166 global_index_map.emplace(elem_id, new_global_index++);
171 for (
const auto & elem :
as_range(beg, end))
172 global_index_map.emplace(elem->id(), global_index[cnt++]);
176 libmesh_assert_equal_to (global_index_map.size(), n_range_elem);
179 std::unordered_set<dof_id_type> distinct_global_indices, distinct_ids;
180 for (
const auto & [elem_id, index] : global_index_map)
182 distinct_ids.insert(elem_id);
183 distinct_global_indices.insert(index);
185 libmesh_assert_equal_to (distinct_global_indices.size(), n_range_elem);
186 libmesh_assert_equal_to (distinct_ids.size(), n_range_elem);
194 typedef std::unordered_multimap<const Elem *, const Elem *> map_type;
195 map_type interior_to_boundary_map;
197 for (
const auto & elem :
as_range(beg, end))
201 if ((elem->dim() >= LIBMESH_DIM) ||
202 !elem->interior_parent())
206 std::set<Elem *> neighbor_set;
207 elem->find_interior_neighbors(neighbor_set);
209 for (
auto & neighbor : neighbor_set)
210 interior_to_boundary_map.emplace(neighbor, elem);
214 std::vector<Metis::idx_t> part(n_range_elem);
218 if (
mesh.processor_id() == 0)
222 std::vector<Metis::idx_t> vwgt(n_range_elem);
225 n =
static_cast<Metis::idx_t
>(n_range_elem),
228 nparts = static_cast<Metis::idx_t>(n_pieces),
237 csr_graph.
offsets.resize(n_range_elem + 1, 0);
245 #ifdef LIBMESH_ENABLE_AMR 246 std::vector<const Elem *> neighbors_offspring;
250 std::size_t graph_size=0;
255 for (
const auto & elem :
as_range(beg, end))
258 global_index_map[elem->id()];
260 libmesh_assert_less (elem_global_index, vwgt.size());
272 vwgt[elem_global_index] = 50;
274 vwgt[elem_global_index] = elem->n_nodes();
277 vwgt[elem_global_index] =
static_cast<Metis::idx_t
>((*_weights)[elem->id()]);
279 unsigned int num_neighbors = 0;
283 for (
auto neighbor : elem->neighbor_ptr_range())
285 if (neighbor !=
nullptr)
290 if (neighbor->active() && !global_index_map.count(neighbor->id()))
295 if (neighbor->active())
298 #ifdef LIBMESH_ENABLE_AMR 307 const unsigned int ns =
308 neighbor->which_neighbor_am_i (elem);
309 libmesh_assert_less (ns, neighbor->n_neighbors());
321 neighbor->active_family_tree (neighbors_offspring);
326 for (
const auto & child : neighbors_offspring)
329 if (!global_index_map.count(child->id()))
335 if (child->neighbor_ptr(ns) == elem)
349 if ((elem->dim() < LIBMESH_DIM) && elem->interior_parent())
352 std::set<const Elem *> neighbor_set;
353 elem->find_interior_neighbors(neighbor_set);
355 num_neighbors += cast_int<unsigned int>(neighbor_set.size());
359 typedef map_type::iterator map_it_type;
360 std::pair<map_it_type, map_it_type>
361 bounds = interior_to_boundary_map.equal_range(elem);
362 num_neighbors += cast_int<unsigned int>
374 graph_size += num_neighbors;
381 for (
const auto & elem :
as_range(beg, end))
384 global_index_map[elem->id()];
386 unsigned int connection=0;
390 for (
auto neighbor : elem->neighbor_ptr_range())
392 if (neighbor !=
nullptr)
397 if (neighbor->active() && !global_index_map.count(neighbor->id()))
402 if (neighbor->active())
403 csr_graph(elem_global_index, connection++) = global_index_map[neighbor->id()];
405 #ifdef LIBMESH_ENABLE_AMR 414 const unsigned int ns =
415 neighbor->which_neighbor_am_i (elem);
416 libmesh_assert_less (ns, neighbor->n_neighbors());
420 neighbor->active_family_tree (neighbors_offspring);
425 for (
const auto & child : neighbors_offspring)
428 if (!global_index_map.count(child->id()))
434 if (child->neighbor_ptr(ns) == elem)
438 csr_graph(elem_global_index, connection++) = global_index_map[child->id()];
448 if ((elem->dim() < LIBMESH_DIM) &&
449 elem->interior_parent())
452 std::set<const Elem *> neighbor_set;
453 elem->find_interior_neighbors(neighbor_set);
455 for (
const Elem * neighbor : neighbor_set)
463 const Elem * queried_elem =
mesh.query_elem_ptr(neighbor->id());
467 if (queried_elem && queried_elem == neighbor)
468 csr_graph(elem_global_index, connection++) =
469 libmesh_map_find(global_index_map, neighbor->id());
474 for (
const auto & pr :
as_range(interior_to_boundary_map.equal_range(elem)))
476 const Elem * neighbor = pr.second;
477 csr_graph(elem_global_index, connection++) =
478 global_index_map[neighbor->
id()];
484 libmesh_assert_equal_to (csr_graph.
vals.size(),
485 std::max(graph_size, std::size_t(1)));
488 Metis::idx_t ncon = 1;
494 Metis::METIS_PartGraphRecursive(&n,
497 csr_graph.
vals.data(),
510 Metis::METIS_PartGraphKway(&n,
513 csr_graph.
vals.data(),
527 mesh.comm().broadcast(part);
532 for (
auto & elem :
as_range(beg, end))
537 global_index_map[elem->id()];
539 libmesh_assert_less (elem_global_index, part.size());
543 elem->processor_id() = elem_procid;
551 const unsigned int n_pieces)
554 mesh.active_elements_begin(),
555 mesh.active_elements_end(),
virtual void partition_range(MeshBase &mesh, MeshBase::element_iterator it, MeshBase::element_iterator end, const unsigned int n) override
Called by the SubdomainPartitioner to partition elements in the range (it, end).
The definition of the element_iterator struct.
void find_global_indices(const Parallel::Communicator &communicator, const libMesh::BoundingBox &, const ForwardIterator &, const ForwardIterator &, std::vector< dof_id_type > &) const
This method determines a globally unique, partition-agnostic index for each object in the input range...
bool single_partition_range(MeshBase::element_iterator it, MeshBase::element_iterator end)
Slightly generalized version of single_partition which acts on a range of elements defined by the pai...
This utility class provides a convenient implementation for building the compressed-row-storage graph...
virtual void partition_range(MeshBase &mesh, MeshBase::element_iterator it, MeshBase::element_iterator end, const unsigned int n) override
Called by the SubdomainPartitioner to partition elements in the range (it, end).
This is the base class from which all geometric element types are derived.
The libMesh namespace provides an interface to certain functionality in the library.
Real distance(const Point &p)
uint8_t processor_id_type
This is the MeshBase class.
ErrorVector * _weights
The weights that might be used for partitioning.
std::vector< IndexType > offsets
PartitionerType
Defines an enum for mesh partitioner types.
This is the MeshCommunication class.
SimpleRange< IndexType > as_range(const std::pair< IndexType, IndexType > &p)
Helper function that allows us to treat a homogenous pair as a range.
std::vector< IndexType > vals
virtual void _do_partition(MeshBase &mesh, const unsigned int n) override
Partition the MeshBase into n subdomains.
The SFCPartitioner uses a Hilbert or Morton-ordered space filling curve to partition the elements...
void prepare_for_use()
Replaces the entries in the offsets vector with their partial sums and resizes the val vector accordi...
void prep_n_nonzeros(const libMesh::dof_id_type row, const libMesh::dof_id_type n_nonzeros_in)
Sets the number of non-zero values in the given row.
This vectormap templated class is intended to provide the performance characteristics of a sorted s...
virtual PartitionerType type() const override