35 #ifdef LIBMESH_HAVE_PARMETIS 40 # include "parmetis.h" 49 #include <unordered_map> 57 #ifdef LIBMESH_HAVE_PARMETIS 64 #ifdef LIBMESH_HAVE_PARMETIS 65 : _pmetis(libmesh_make_unique<ParmetisHelper>())
73 #ifdef LIBMESH_HAVE_PARMETIS
74 , _pmetis(libmesh_make_unique<ParmetisHelper>(*(other._pmetis)))
81 ParmetisPartitioner::~ParmetisPartitioner() =
default;
85 void ParmetisPartitioner::_do_partition (MeshBase &
mesh,
86 const unsigned int n_sbdmns)
88 this->_do_repartition (
mesh, n_sbdmns);
94 const unsigned int n_sbdmns)
97 libmesh_parallel_only(
mesh.comm());
105 this->single_partition(
mesh);
109 libmesh_assert_greater (n_sbdmns, 0);
112 #ifndef LIBMESH_HAVE_PARMETIS 115 libMesh::out <<
"ERROR: The library has been built without" << std::endl
116 <<
"Parmetis support. Using a Metis" << std::endl
117 <<
"partitioner instead!" << std::endl;);
125 mesh.active_elements_end(), n_sbdmns);
131 if (
mesh.n_processors() == 1)
141 mesh.active_elements_end(), n_sbdmns);
145 LOG_SCOPE(
"repartition()",
"ParmetisPartitioner");
148 this->initialize (
mesh, n_sbdmns);
154 bool all_have_enough_elements =
true;
155 for (std::size_t pid=0; pid<_n_active_elem_on_proc.size(); pid++)
157 all_have_enough_elements =
false;
162 if (!all_have_enough_elements)
173 this->build_graph (
mesh);
177 std::vector<Parmetis::idx_t> vsize(_pmetis->vwgt.size(), 1);
178 Parmetis::real_t itr = 1000000.0;
179 MPI_Comm mpi_comm =
mesh.comm().get();
184 Parmetis::ParMETIS_V3_AdaptiveRepart(_pmetis->vtxdist.empty() ? nullptr : _pmetis->vtxdist.data(),
185 _pmetis->xadj.empty() ? nullptr : _pmetis->xadj.data(),
186 _pmetis->adjncy.empty() ? nullptr : _pmetis->adjncy.data(),
187 _pmetis->vwgt.empty() ? nullptr : _pmetis->vwgt.data(),
188 vsize.empty() ? nullptr : vsize.data(),
194 _pmetis->tpwgts.empty() ? nullptr : _pmetis->tpwgts.data(),
195 _pmetis->ubvec.empty() ? nullptr : _pmetis->ubvec.data(),
197 _pmetis->options.data(),
199 _pmetis->part.empty() ? nullptr :
reinterpret_cast<Parmetis::idx_t *
>(_pmetis->part.data()),
203 this->assign_partitioning (
mesh, _pmetis->part);
205 #endif // #ifndef LIBMESH_HAVE_PARMETIS ... else ... 212 #ifdef LIBMESH_HAVE_PARMETIS 215 const unsigned int n_sbdmns)
217 LOG_SCOPE(
"initialize()",
"ParmetisPartitioner");
221 _pmetis->wgtflag = 2;
223 _pmetis->numflag = 0;
224 _pmetis->nparts =
static_cast<Parmetis::idx_t
>(n_sbdmns);
225 _pmetis->edgecut = 0;
229 _pmetis->vtxdist.assign (
mesh.n_processors()+1, 0);
230 _pmetis->tpwgts.assign (_pmetis->nparts, 1./_pmetis->nparts);
231 _pmetis->ubvec.assign (_pmetis->ncon, 1.05);
232 _pmetis->part.assign (n_active_local_elem, 0);
233 _pmetis->options.resize (5);
234 _pmetis->vwgt.resize (n_active_local_elem);
237 _pmetis->options[0] = 1;
238 _pmetis->options[1] = 0;
239 _pmetis->options[2] = 15;
240 _pmetis->options[3] = 2;
254 _find_global_index_by_pid_map(
mesh);
266 libmesh_assert_equal_to (_pmetis->vtxdist.size(),
267 cast_int<std::size_t>(
mesh.n_processors()+1));
268 libmesh_assert_equal_to (_pmetis->vtxdist[0], 0);
272 _pmetis->vtxdist[pid+1] = _pmetis->vtxdist[pid] + _n_active_elem_on_proc[pid];
273 n_active_elem += _n_active_elem_on_proc[pid];
275 libmesh_assert_equal_to (_pmetis->vtxdist.back(),
static_cast<Parmetis::idx_t
>(n_active_elem));
280 std::unordered_map<dof_id_type, dof_id_type> global_index_map;
283 std::vector<dof_id_type> global_index;
298 const Elem * elem = *it;
302 libmesh_assert_less (cnt, global_index.size());
303 libmesh_assert_less (global_index[cnt], n_active_elem);
305 global_index_map.insert(std::make_pair(elem->
id(), global_index[cnt++]));
309 libmesh_assert_less_equal (global_index_map.size(), n_active_elem);
310 libmesh_assert_less_equal (_global_index_by_pid_map.size(), n_active_elem);
316 if (global_index_map.size() != _global_index_by_pid_map.size())
317 libmesh_error_msg(
"ERROR: ParmetisPartitioner cannot handle unpartitioned objects!");
324 std::vector<dof_id_type> subdomain_bounds(
mesh.n_processors());
326 const dof_id_type first_local_elem = _pmetis->vtxdist[
mesh.processor_id()];
333 if (pid < static_cast<unsigned int>(_pmetis->nparts))
335 tgt_subdomain_size = n_active_elem/
std::min 336 (cast_int<Parmetis::idx_t>(
mesh.n_processors()), _pmetis->nparts);
338 if (pid < n_active_elem%_pmetis->nparts)
339 tgt_subdomain_size++;
342 subdomain_bounds[0] = tgt_subdomain_size;
344 subdomain_bounds[pid] = subdomain_bounds[pid-1] + tgt_subdomain_size;
347 libmesh_assert_equal_to (subdomain_bounds.back(), n_active_elem);
349 for (
const auto & elem :
mesh.active_local_element_ptr_range())
351 libmesh_assert (_global_index_by_pid_map.count(elem->id()));
353 _global_index_by_pid_map[elem->id()];
354 libmesh_assert_less (global_index_by_pid, n_active_elem);
357 global_index_by_pid - first_local_elem;
359 libmesh_assert_less (local_index, n_active_local_elem);
360 libmesh_assert_less (local_index, _pmetis->vwgt.size());
363 _pmetis->vwgt[local_index] = elem->n_nodes();
366 libmesh_assert (global_index_map.count(elem->id()));
368 global_index_map[elem->id()];
370 libmesh_assert_less (global_index, subdomain_bounds.back());
372 const unsigned int subdomain_id =
373 cast_int<unsigned int>
374 (std::distance(subdomain_bounds.begin(),
375 std::lower_bound(subdomain_bounds.begin(),
376 subdomain_bounds.end(),
378 libmesh_assert_less (subdomain_id, static_cast<unsigned int>(_pmetis->nparts));
379 libmesh_assert_less (local_index, _pmetis->part.size());
381 _pmetis->part[local_index] = subdomain_id;
390 LOG_SCOPE(
"build_graph()",
"ParmetisPartitioner");
397 Partitioner::build_graph(
mesh);
401 for (
auto & row: _dual_graph)
402 graph_size += cast_int<dof_id_type>(row.size());
405 _pmetis->xadj.clear();
406 _pmetis->xadj.reserve (n_active_local_elem + 1);
407 _pmetis->adjncy.clear();
408 _pmetis->adjncy.reserve (graph_size);
410 for (
auto & graph_row : _dual_graph)
412 _pmetis->xadj.push_back(cast_int<int>(_pmetis->adjncy.size()));
413 _pmetis->adjncy.insert(_pmetis->adjncy.end(),
419 _pmetis->xadj.push_back(cast_int<int>(_pmetis->adjncy.size()));
421 libmesh_assert_equal_to (_pmetis->xadj.size(), n_active_local_elem+1);
422 libmesh_assert_equal_to (_pmetis->adjncy.size(), graph_size);
425 #endif // #ifdef LIBMESH_HAVE_PARMETIS void find_global_indices(const Parallel::Communicator &communicator, const libMesh::BoundingBox &, const ForwardIterator &, const ForwardIterator &, std::vector< dof_id_type > &) const
virtual void partition_range(MeshBase &mesh, MeshBase::element_iterator it, MeshBase::element_iterator end, const unsigned int n) override
The base class for all geometric element types.
uint8_t processor_id_type
Partitioner which interfaces with the METIS library.
const unsigned int MIN_ELEM_PER_PROC
Temporarily serializes a DistributedMesh for output.
virtual void partition(MeshBase &mesh, const unsigned int n)
OStreamProxy out(std::cout)
long double min(long double a, double b)