21 #include "libmesh/parmetis_partitioner.h" 24 #include "libmesh/libmesh_config.h" 25 #include "libmesh/elem.h" 26 #include "libmesh/elem_range.h" 27 #include "libmesh/enum_partitioner_type.h" 28 #include "libmesh/libmesh_logging.h" 29 #include "libmesh/mesh_base.h" 30 #include "libmesh/mesh_serializer.h" 31 #include "libmesh/mesh_tools.h" 32 #include "libmesh/mesh_communication.h" 33 #include "libmesh/metis_partitioner.h" 34 #include "libmesh/parallel_only.h" 35 #include "libmesh/parmetis_helper.h" 36 #include "libmesh/utility.h" 43 #ifdef LIBMESH_HAVE_PARMETIS 51 # include "libmesh/ignore_warnings.h" 52 # include "parmetis.h" 53 # include "libmesh/restore_warnings.h" 61 #include <unordered_map> 69 #ifdef LIBMESH_HAVE_PARMETIS 76 #ifdef LIBMESH_HAVE_PARMETIS 77 : _pmetis(std::make_unique<ParmetisHelper>())
85 #ifdef LIBMESH_HAVE_PARMETIS
86 , _pmetis(
std::make_unique<ParmetisHelper>(*(other._pmetis)))
93 ParmetisPartitioner::~ParmetisPartitioner() =
default;
105 const unsigned int n_sbdmns)
107 this->_do_repartition (
mesh, n_sbdmns);
113 const unsigned int n_sbdmns)
116 libmesh_parallel_only(
mesh.comm());
124 this->single_partition(
mesh);
128 libmesh_assert_greater (n_sbdmns, 0);
131 #ifndef LIBMESH_HAVE_PARMETIS 134 libMesh::out <<
"ERROR: The library has been built without" << std::endl
135 <<
"Parmetis support. Using a Metis" << std::endl
136 <<
"partitioner instead!" << std::endl;);
147 mp.partition_range (
mesh,
mesh.active_elements_begin(),
148 mesh.active_elements_end(), n_sbdmns);
154 if (
mesh.n_processors() == 1)
164 mesh.active_elements_end(), n_sbdmns);
168 LOG_SCOPE(
"repartition()",
"ParmetisPartitioner");
174 this->build_graph (
mesh);
181 bool ready_for_parmetis =
true;
182 for (
const auto & nelem : _n_active_elem_on_proc)
184 ready_for_parmetis =
false;
186 std::size_t my_adjacency = _pmetis->adjncy.size();
187 mesh.comm().min(my_adjacency);
189 ready_for_parmetis =
false;
194 if (!ready_for_parmetis)
206 std::vector<Parmetis::idx_t> vsize(_pmetis->vwgt.size(), 1);
207 Parmetis::real_t itr = 1000000.0;
208 MPI_Comm mpi_comm =
mesh.comm().get();
213 Parmetis::ParMETIS_V3_AdaptiveRepart(_pmetis->vtxdist.empty() ? nullptr : _pmetis->vtxdist.data(),
214 _pmetis->xadj.empty() ? nullptr : _pmetis->xadj.data(),
215 _pmetis->adjncy.empty() ? nullptr : _pmetis->adjncy.data(),
216 _pmetis->vwgt.empty() ? nullptr : _pmetis->vwgt.data(),
217 vsize.empty() ? nullptr : vsize.data(),
223 _pmetis->tpwgts.empty() ? nullptr : _pmetis->tpwgts.data(),
224 _pmetis->ubvec.empty() ? nullptr : _pmetis->ubvec.data(),
226 _pmetis->options.data(),
228 _pmetis->part.empty() ? nullptr :
reinterpret_cast<Parmetis::idx_t *
>(_pmetis->part.data()),
232 this->assign_partitioning (
mesh, _pmetis->part);
234 #endif // #ifndef LIBMESH_HAVE_PARMETIS ... else ... 241 #ifdef LIBMESH_HAVE_PARMETIS 244 const unsigned int n_sbdmns)
246 LOG_SCOPE(
"initialize()",
"ParmetisPartitioner");
250 _pmetis->wgtflag = 2;
252 _pmetis->numflag = 0;
253 _pmetis->nparts =
static_cast<Parmetis::idx_t
>(n_sbdmns);
254 _pmetis->edgecut = 0;
258 _pmetis->vtxdist.assign (
mesh.n_processors()+1, 0);
259 _pmetis->tpwgts.assign (_pmetis->nparts, 1./_pmetis->nparts);
260 _pmetis->ubvec.assign (_pmetis->ncon, 1.05);
261 _pmetis->part.assign (n_active_local_elem, 0);
262 _pmetis->options.resize (5);
263 _pmetis->vwgt.resize (n_active_local_elem);
266 _pmetis->options[0] = 1;
267 _pmetis->options[1] = 0;
268 _pmetis->options[2] = 15;
269 _pmetis->options[3] = 2;
283 _find_global_index_by_pid_map(
mesh);
295 libmesh_assert_equal_to (_pmetis->vtxdist.size(),
296 cast_int<std::size_t>(
mesh.n_processors()+1));
297 libmesh_assert_equal_to (_pmetis->vtxdist[0], 0);
301 _pmetis->vtxdist[pid+1] = _pmetis->vtxdist[pid] + _n_active_elem_on_proc[pid];
302 n_active_elem += _n_active_elem_on_proc[pid];
304 libmesh_assert_equal_to (_pmetis->vtxdist.back(),
static_cast<Parmetis::idx_t
>(n_active_elem));
309 std::unordered_map<dof_id_type, dof_id_type> global_index_map;
312 std::vector<dof_id_type> global_index;
327 const Elem * elem = *it;
331 libmesh_assert_less (cnt, global_index.size());
332 libmesh_assert_less (global_index[cnt], n_active_elem);
334 global_index_map.emplace(elem->
id(), global_index[cnt++]);
338 libmesh_assert_less_equal (global_index_map.size(), n_active_elem);
339 libmesh_assert_less_equal (_global_index_by_pid_map.size(), n_active_elem);
345 libmesh_error_msg_if(global_index_map.size() != _global_index_by_pid_map.size(),
346 "ERROR: ParmetisPartitioner cannot handle unpartitioned objects!");
353 std::vector<dof_id_type> subdomain_bounds(
mesh.n_processors());
355 const dof_id_type first_local_elem = _pmetis->vtxdist[
mesh.processor_id()];
362 if (pid < static_cast<unsigned int>(_pmetis->nparts))
364 tgt_subdomain_size = n_active_elem/std::min
365 (cast_int<Parmetis::idx_t>(
mesh.n_processors()), _pmetis->nparts);
367 if (pid < n_active_elem%_pmetis->nparts)
368 tgt_subdomain_size++;
371 subdomain_bounds[0] = tgt_subdomain_size;
373 subdomain_bounds[pid] = subdomain_bounds[pid-1] + tgt_subdomain_size;
376 libmesh_assert_equal_to (subdomain_bounds.back(), n_active_elem);
379 (
mesh.active_local_element_stored_range(),
380 [first_local_elem, n_active_elem, n_active_local_elem,
this,
381 &global_index_map, &subdomain_bounds](
const ConstElemRange & range)
383 for (
const Elem * elem : range)
386 libmesh_map_find(_global_index_by_pid_map, elem->id());
387 libmesh_assert_less (global_index_by_pid, n_active_elem);
390 global_index_by_pid - first_local_elem;
392 libmesh_assert_less (local_index, n_active_local_elem);
393 libmesh_assert_less (local_index, _pmetis->vwgt.size());
405 _pmetis->vwgt[local_index] = 50;
407 _pmetis->vwgt[local_index] = elem->n_nodes();
411 libmesh_map_find(global_index_map, elem->id());
413 libmesh_assert_less (global_index, subdomain_bounds.back());
415 const unsigned int subdomain_id =
416 cast_int<unsigned int>
418 std::lower_bound(subdomain_bounds.begin(),
419 subdomain_bounds.end(),
421 libmesh_assert_less (subdomain_id, _pmetis->nparts);
422 libmesh_assert_less (local_index, _pmetis->part.size());
424 _pmetis->part[local_index] = subdomain_id;
434 LOG_SCOPE(
"build_graph()",
"ParmetisPartitioner");
441 Partitioner::build_graph(
mesh);
445 for (
auto & row: _dual_graph)
446 graph_size += cast_int<dof_id_type>(row.size());
449 _pmetis->xadj.clear();
450 _pmetis->xadj.reserve (n_active_local_elem + 1);
451 _pmetis->adjncy.clear();
452 _pmetis->adjncy.reserve (graph_size);
454 for (
auto & graph_row : _dual_graph)
456 _pmetis->xadj.push_back(cast_int<int>(_pmetis->adjncy.size()));
457 _pmetis->adjncy.insert(_pmetis->adjncy.end(),
463 _pmetis->xadj.push_back(cast_int<int>(_pmetis->adjncy.size()));
465 libmesh_assert_equal_to (_pmetis->xadj.size(), n_active_local_elem+1);
466 libmesh_assert_equal_to (_pmetis->adjncy.size(), graph_size);
469 #endif // #ifdef LIBMESH_HAVE_PARMETIS void parallel_for(const Range &range, const Body &body, unsigned int n_threads=libMesh::n_threads())
Execute the provided function object in parallel on the specified range.
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...
The definition of the const_element_iterator struct.
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.
ParmetisPartitioner()
Default and copy ctors.
The StoredRange class defines a contiguous, divisible set of objects.
The MetisPartitioner uses the Metis graph partitioner to partition the elements.
The libMesh namespace provides an interface to certain functionality in the library.
void initialize(EquationSystems &es, const std::string &system_name)
Real distance(const Point &p)
This is the MeshBase class.
PartitionerType
Defines an enum for mesh partitioner types.
void libmesh_ignore(const Args &...)
This is the MeshCommunication class.
Defines a Cartesian bounding box by the two corner extremum.
const unsigned int MIN_ELEM_PER_PROC
Temporarily serialize a DistributedMesh for non-distributed-mesh capable code paths.
virtual void partition(MeshBase &mesh, const unsigned int n)
Partitions the MeshBase into n parts by setting processor_id() on Nodes and Elems.
IntRange< T > make_range(T beg, T end)
The 2-parameter make_range() helper function returns an IntRange<T> when both input parameters are of...