libMesh
Loading...
Searching...
No Matches
Public Member Functions | Static Public Member Functions | Protected Member Functions | Protected Attributes | Static Protected Attributes | List of all members
GhostDofConstraintPartitioner Class Reference
Inheritance diagram for GhostDofConstraintPartitioner:
[legend]

Public Member Functions

std::unique_ptr< Partitioner > clone () const override
 
virtual bool partitions_from_scratch () const override
 
virtual PartitionerType type () const
 
virtual void partition (MeshBase &mesh, const unsigned int n)
 Partitions the MeshBase into n parts by setting processor_id() on Nodes and Elems.
 
virtual void partition (MeshBase &mesh)
 Partitions the MeshBase into mesh.n_processors() by setting processor_id() on Nodes and Elems.
 
virtual void partition_range (MeshBase &, MeshBase::element_iterator, MeshBase::element_iterator, const unsigned int)
 Partitions elements in the range (it, end) into n parts.
 
void repartition (MeshBase &mesh, const unsigned int n)
 Repartitions the MeshBase into n parts.
 
void repartition (MeshBase &mesh)
 Repartitions the MeshBase into mesh.n_processors() parts.
 
virtual void attach_weights (ErrorVector *)
 Attach weights that can be used for partitioning.
 

Static Public Member Functions

static std::unique_ptr< Partitioner > build (const PartitionerType solver_package)
 Builds a Partitioner of the type specified by partitioner_type.
 
static void partition_unpartitioned_elements (MeshBase &mesh)
 These functions assign processor IDs to newly-created elements (in parallel) which are currently assigned to processor 0.
 
static void partition_unpartitioned_elements (MeshBase &mesh, const unsigned int n)
 
static void partition_unpartitioned_elements (MeshBase &mesh, const unsigned int n, const bool spatial_indexing)
 As above, but spatial_indexing selects how the elements are mapped onto the partitions.
 
static void set_parent_processor_ids (MeshBase &mesh)
 This function is called after partitioning to set the processor IDs for the inactive parent elements.
 
static void set_node_processor_ids (MeshBase &mesh)
 This function is called after partitioning to set the processor IDs for the nodes.
 
static void processor_pairs_to_interface_nodes (MeshBase &mesh, std::map< std::pair< processor_id_type, processor_id_type >, std::set< dof_id_type > > &processor_pair_to_nodes)
 On the partitioning interface, a surface is shared by two and only two processors.
 
static void set_interface_node_processor_ids_linear (MeshBase &mesh)
 Nodes on the partitioning interface is linearly assigned to each pair of processors.
 
static void set_interface_node_processor_ids_BFS (MeshBase &mesh)
 Nodes on the partitioning interface is clustered into two groups BFS (Breadth First Search)scheme for per pair of processors.
 
static void set_interface_node_processor_ids_petscpartitioner (MeshBase &mesh)
 Nodes on the partitioning interface is partitioned into two groups using a PETSc partitioner for each pair of processors.
 

Protected Member Functions

void _do_partition (MeshBase &mesh, const unsigned int n) override
 This is the actual partitioning method which must be overridden in derived classes.
 
bool single_partition (MeshBase &mesh)
 Trivially "partitions" the mesh for one processor.
 
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 pair of iterators (it, end).
 
virtual void _do_repartition (MeshBase &mesh, const unsigned int n)
 This is the actual re-partitioning method which can be overridden in derived classes.
 
virtual void _find_global_index_by_pid_map (const MeshBase &mesh)
 Construct contiguous global indices for the current partitioning.
 
virtual void build_graph (const MeshBase &mesh)
 Build a dual graph for partitioner.
 
void assign_partitioning (MeshBase &mesh, const std::vector< dof_id_type > &parts)
 Assign the computed partitioning to the mesh.
 

Protected Attributes

ErrorVector * _weights
 The weights that might be used for partitioning.
 
std::unordered_map< dof_id_type, dof_id_type > _global_index_by_pid_map
 Maps active element ids into a contiguous range, as needed by parallel partitioner.
 
std::vector< dof_id_type > _n_active_elem_on_proc
 The number of active elements on each processor.
 
std::vector< std::vector< dof_id_type > > _dual_graph
 A dual graph corresponds to the mesh, and it is typically used in paritioner.
 
std::vector< Elem * > _local_id_to_elem
 

Static Protected Attributes

static const dof_id_type communication_blocksize
 The blocksize to use when doing blocked parallel communication.
 

Detailed Description

Definition at line 79 of file dof_map_test.C.

Member Function Documentation

◆ _do_partition()

void GhostDofConstraintPartitioner::_do_partition ( MeshBase &  mesh,
const unsigned int  n 
)
inlineoverrideprotectedvirtual

This is the actual partitioning method which must be overridden in derived classes.

It is called via the public partition() method above by the user.

Implements libMesh::Partitioner.

Definition at line 92 of file dof_map_test.C.

93 {
94 // Keep the first three elements on processor 0 and move the final
95 // element to another processor, creating a processor interface at node 3.
96 for (auto & elem : mesh.active_element_ptr_range())
97 elem->processor_id() = 0;
98
99 if (n > 1)
100 mesh.elem_ref(3).processor_id() = n - 1;
101 }
processor_id_type processor_id() const
Definition dof_object.h:895
virtual const Elem & elem_ref(const dof_id_type i) const
Definition mesh_base.h:785
MeshBase & mesh

References libMesh::MeshBase::elem_ref(), mesh, and libMesh::DofObject::processor_id().

◆ _do_repartition()

virtual void libMesh::Partitioner::_do_repartition ( MeshBase &  mesh,
const unsigned int  n 
)
inlineprotectedvirtualinherited

This is the actual re-partitioning method which can be overridden in derived classes.

Note
The default behavior is to simply call the partition function.

Reimplemented in libMesh::ParmetisPartitioner.

Definition at line 281 of file partitioner.h.

282 { this->_do_partition (mesh, n); }
virtual void _do_partition(MeshBase &mesh, const unsigned int n)=0
This is the actual partitioning method which must be overridden in derived classes.

References libMesh::Partitioner::_do_partition().

Referenced by libMesh::Partitioner::repartition().

◆ _find_global_index_by_pid_map()

void libMesh::Partitioner::_find_global_index_by_pid_map ( const MeshBase &  mesh)
protectedvirtualinherited

Construct contiguous global indices for the current partitioning.

The global indices are ordered part-by-part

Definition at line 1092 of file partitioner.C.

1093{
1094 const dof_id_type n_active_local_elem = mesh.n_active_local_elem();
1095
1096 // Find the number of active elements on each processor. We cannot use
1097 // mesh.n_active_elem_on_proc(pid) since that only returns the number of
1098 // elements assigned to pid which are currently stored on the calling
1099 // processor. This will not in general be correct for parallel meshes
1100 // when (pid!=mesh.processor_id()).
1101 auto n_proc = mesh.n_processors();
1102 _n_active_elem_on_proc.resize(n_proc);
1103 mesh.comm().allgather(n_active_local_elem, _n_active_elem_on_proc);
1104
1105 std::vector<dof_id_type> n_active_elem_before_proc(mesh.n_processors());
1106
1107 for (auto i : make_range(n_proc-1))
1108 n_active_elem_before_proc[i+1] =
1109 n_active_elem_before_proc[i] + _n_active_elem_on_proc[i];
1110
1113
1115
1116 // create the mapping which is contiguous by processor
1118 mesh.active_local_elements_begin(),
1119 mesh.active_local_elements_end(),
1121
1123
1125 (mesh.comm(), mesh.active_elements_begin(), mesh.active_elements_end(), sync);
1126
1127 for (const auto & elem : mesh.active_element_ptr_range())
1128 {
1129 const processor_id_type pid = elem->processor_id();
1130 libmesh_assert_less (_global_index_by_pid_map[elem->id()], _n_active_elem_on_proc[pid]);
1131
1132 _global_index_by_pid_map[elem->id()] += n_active_elem_before_proc[pid];
1133 }
1134}
void allgather(const T &send_data, std::vector< T, A > &recv_data) const
Defines a Cartesian bounding box by the two corner extremum.
dof_id_type n_active_local_elem() const
Definition mesh_base.h:711
This is the MeshCommunication class.
void find_local_indices(const libMesh::BoundingBox &, const ForwardIterator &, const ForwardIterator &, std::unordered_map< dof_id_type, dof_id_type > &) const
This method determines a locally unique, contiguous index for each object in the input range.
const Parallel::Communicator & comm() const
processor_id_type n_processors() const
std::vector< dof_id_type > _n_active_elem_on_proc
The number of active elements on each processor.
std::unordered_map< dof_id_type, dof_id_type > _global_index_by_pid_map
Maps active element ids into a contiguous range, as needed by parallel partitioner.
libMesh::BoundingBox create_bounding_box(const MeshBase &mesh)
Definition mesh_tools.C:566
void sync_dofobject_data_by_id(const Communicator &comm, const Iterator &range_begin, const Iterator &range_end, SyncFunctor &sync)
Request data about a range of ghost dofobjects uniquely identified by their id.
uint8_t dof_id_type
Definition id_types.h:67
uint8_t processor_id_type
Definition id_types.h:104
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...
Definition int_range.h:176

References libMesh::Partitioner::_global_index_by_pid_map, libMesh::Partitioner::_n_active_elem_on_proc, libMesh::Parallel::Communicator::allgather(), libMesh::ParallelObject::comm(), libMesh::MeshTools::create_bounding_box(), libMesh::MeshCommunication::find_local_indices(), libMesh::make_range(), mesh, libMesh::MeshBase::n_active_local_elem(), libMesh::ParallelObject::n_processors(), and libMesh::Parallel::sync_dofobject_data_by_id().

Referenced by libMesh::Partitioner::build_graph().

◆ assign_partitioning()

void libMesh::Partitioner::assign_partitioning ( MeshBase &  mesh,
const std::vector< dof_id_type > &  parts 
)
protectedinherited

Assign the computed partitioning to the mesh.

Definition at line 1491 of file partitioner.C.

1492{
1493 LOG_SCOPE("assign_partitioning()", "Partitioner");
1494
1495 // This function must be run on all processors at once
1496 libmesh_parallel_only(mesh.comm());
1497
1498 dof_id_type first_local_elem = 0;
1499 for (auto pid : make_range(mesh.processor_id()))
1500 first_local_elem += _n_active_elem_on_proc[pid];
1501
1502#ifndef NDEBUG
1503 const dof_id_type n_active_local_elem = mesh.n_active_local_elem();
1504#endif
1505
1506 std::map<processor_id_type, std::vector<dof_id_type>>
1507 requested_ids;
1508
1509 // Results to gather from each processor - kept in a map so we
1510 // do only one loop over elements after all receives are done.
1511 std::map<processor_id_type, std::vector<processor_id_type>>
1512 filled_request;
1513
1514 for (auto & elem : mesh.active_element_ptr_range())
1515 {
1516 // we need to get the index from the owning processor
1517 // (note we cannot assign it now -- we are iterating
1518 // over elements again and this will be bad!)
1519 requested_ids[elem->processor_id()].push_back(elem->id());
1520 }
1521
1522 auto gather_functor =
1523 [this,
1524 & parts,
1525#ifndef NDEBUG
1526 & mesh,
1527 n_active_local_elem,
1528#endif
1529 first_local_elem]
1530 (processor_id_type, const std::vector<dof_id_type> & ids,
1531 std::vector<processor_id_type> & data)
1532 {
1533 const std::size_t ids_size = ids.size();
1534 data.resize(ids.size());
1535
1536 for (std::size_t i=0; i != ids_size; i++)
1537 {
1538 const dof_id_type requested_elem_index = ids[i];
1539
1540 libmesh_assert(_global_index_by_pid_map.count(requested_elem_index));
1541
1542 const dof_id_type global_index_by_pid =
1543 _global_index_by_pid_map[requested_elem_index];
1544
1545 const dof_id_type local_index =
1546 global_index_by_pid - first_local_elem;
1547
1548 libmesh_assert_less (local_index, parts.size());
1549 libmesh_assert_less (local_index, n_active_local_elem);
1550
1551 const processor_id_type elem_procid =
1552 cast_int<processor_id_type>(parts[local_index]);
1553
1554 libmesh_assert_less (elem_procid, mesh.n_partitions());
1555
1556 data[i] = elem_procid;
1557 }
1558 };
1559
1560 auto action_functor =
1561 [&filled_request]
1562 (processor_id_type pid,
1563 const std::vector<dof_id_type> &,
1564 const std::vector<processor_id_type> & new_procids)
1565 {
1566 filled_request[pid] = new_procids;
1567 };
1568
1569 // Trade requests with other processors
1570 const processor_id_type * ex = nullptr;
1572 (mesh.comm(), requested_ids, gather_functor, action_functor, ex);
1573
1574 // and finally assign the partitioning.
1575 // note we are iterating in exactly the same order
1576 // used to build up the request, so we can expect the
1577 // required entries to be in the proper sequence.
1578 std::vector<unsigned int> counters(mesh.n_processors(), 0);
1579 for (auto & elem : mesh.active_element_ptr_range())
1580 {
1581 const processor_id_type current_pid = elem->processor_id();
1582
1583 libmesh_assert_less (counters[current_pid], requested_ids[current_pid].size());
1584
1585 const processor_id_type elem_procid =
1586 filled_request[current_pid][counters[current_pid]++];
1587
1588 libmesh_assert_less (elem_procid, mesh.n_partitions());
1589 elem->processor_id() = elem_procid;
1590 }
1591}
unsigned int n_partitions() const
Definition mesh_base.h:1530
void pull_parallel_vector_data(const Communicator &comm, const MapToVectors &queries, GatherFunctor &gather_data, const ActionFunctor &act_on_data, const datum *example)
libmesh_assert(ctx)

References libMesh::Partitioner::_global_index_by_pid_map, libMesh::Partitioner::_n_active_elem_on_proc, libMesh::ParallelObject::comm(), libMesh::libmesh_assert(), libMesh::make_range(), mesh, libMesh::MeshBase::n_active_local_elem(), libMesh::MeshBase::n_partitions(), libMesh::ParallelObject::n_processors(), libMesh::ParallelObject::processor_id(), and TIMPI::pull_parallel_vector_data().

◆ attach_weights()

virtual void libMesh::Partitioner::attach_weights ( ErrorVector *  )
inlinevirtualinherited

Attach weights that can be used for partitioning.

This ErrorVector should be exactly the same on every processor and should have mesh->max_elem_id() entries.

Reimplemented in libMesh::MetisPartitioner.

Definition at line 224 of file partitioner.h.

224{ libmesh_not_implemented(); }

◆ build()

std::unique_ptr< Partitioner > libMesh::Partitioner::build ( const PartitionerType  solver_package)
staticinherited

Builds a Partitioner of the type specified by partitioner_type.

Definition at line 159 of file partitioner.C.

160{
161 switch (partitioner_type)
162 {
164 return std::make_unique<CentroidPartitioner>();
166 return std::make_unique<LinearPartitioner>();
168 return std::make_unique<MappedSubdomainPartitioner>();
170 return std::make_unique<MetisPartitioner>();
172 return std::make_unique<ParmetisPartitioner>();
174 return std::make_unique<HilbertSFCPartitioner>();
176 return std::make_unique<MortonSFCPartitioner>();
177 case SFC_PARTITIONER:
178 return std::make_unique<SFCPartitioner>();
180 return std::make_unique<SubdomainPartitioner>();
181 default:
182 libmesh_error_msg("Invalid partitioner type: " <<
183 Utility::enum_to_string(partitioner_type));
184 }
185}
std::string enum_to_string(const T e)

References libMesh::CENTROID_PARTITIONER, libMesh::Utility::enum_to_string(), libMesh::HILBERT_SFC_PARTITIONER, libMesh::LINEAR_PARTITIONER, libMesh::MAPPED_SUBDOMAIN_PARTITIONER, libMesh::METIS_PARTITIONER, libMesh::MORTON_SFC_PARTITIONER, libMesh::PARMETIS_PARTITIONER, libMesh::SFC_PARTITIONER, and libMesh::SUBDOMAIN_PARTITIONER.

Referenced by libMesh::DistributedMesh::DistributedMesh(), libMesh::ReplicatedMesh::ReplicatedMesh(), PartitionerTest< PartitionerSubclass, MeshClass >::testBuild(), and MeshDeletionsTest::testDeleteElem().

◆ build_graph()

void libMesh::Partitioner::build_graph ( const MeshBase &  mesh)
protectedvirtualinherited

Build a dual graph for partitioner.

Reimplemented in libMesh::ParmetisPartitioner.

Definition at line 1136 of file partitioner.C.

1137{
1138 LOG_SCOPE("build_graph()", "Partitioner");
1139
1140 const dof_id_type n_active_local_elem = mesh.n_active_local_elem();
1141
1142 // If we have boundary elements in this mesh, we want to account for
1143 // the connectivity between them and interior elements. We can find
1144 // interior elements from boundary elements, but we need to build up
1145 // a lookup map to do the reverse.
1146 typedef std::unordered_multimap<const Elem *, const Elem *> map_type;
1147 map_type interior_to_boundary_map;
1148
1149 // If we have spline nodes in this mesh, we want to account for the
1150 // connectivity between them and integration elements. We can find
1151 // spline nodes from integration elements, but need a reverse map
1152 // {integration_elements} = elems_constrained_by[spline_nodeelem]
1153 map_type elems_constrained_by;
1154
1155 const auto & mesh_constrained_nodes = mesh.get_constraint_rows();
1156
1157 for (const Elem * elem : mesh.active_element_ptr_range())
1158 {
1159 if (!mesh_constrained_nodes.empty()) // quick test for non-IGA cases
1160 {
1161 const auto end_it = mesh_constrained_nodes.end();
1162
1163 // Use a set to avoid duplicates. Use a well-defined method
1164 // of ordering that set to make debugging easier.
1165 std::set<const Elem *, CompareElemIdsByLevel> constraining_elems;
1166 for (const Node & node : elem->node_ref_range())
1167 {
1168 if (const auto row_it = mesh_constrained_nodes.find(&node);
1169 row_it != end_it)
1170 for (const auto & [pr, coef] : row_it->second)
1171 {
1172 libmesh_ignore(coef); // avoid gcc 7 warning
1173 constraining_elems.insert(pr.first);
1174 }
1175 }
1176 for (const Elem * constraining_elem : constraining_elems)
1177 elems_constrained_by.emplace(constraining_elem, elem);
1178 }
1179
1180 // If we don't have an interior_parent and we don't have any
1181 // constrained nodes then there's nothing else to look up.
1182 if (elem->interior_parent())
1183 {
1184 // get all relevant interior elements
1185 std::set<const Elem *> neighbor_set;
1186 elem->find_interior_neighbors(neighbor_set);
1187
1188 for (const auto & neighbor : neighbor_set)
1189 interior_to_boundary_map.emplace(neighbor, elem);
1190 }
1191 }
1192
1193#ifdef LIBMESH_ENABLE_AMR
1194 std::vector<const Elem *> neighbors_offspring;
1195#endif
1196
1197 // This is costly, and we only need to do it if the mesh has
1198 // changed since we last partitioned... but the mesh probably has
1199 // changed since we last partitioned, and if it hasn't we don't
1200 // have a reliable way to be sure of that.
1202
1203 dof_id_type first_local_elem = 0;
1204 for (auto pid : make_range(mesh.processor_id()))
1205 first_local_elem += _n_active_elem_on_proc[pid];
1206
1207 _dual_graph.clear();
1208 _dual_graph.resize(n_active_local_elem);
1209 _local_id_to_elem.resize(n_active_local_elem);
1210
1211 // We may need to communicate constraint-row-based connections
1212 // between processors
1213 std::unordered_map<processor_id_type,
1214 std::vector<std::pair<dof_id_type, dof_id_type>>> connections_to_push;
1215
1216 for (const auto & elem : mesh.active_local_element_ptr_range())
1217 {
1218 libmesh_assert (_global_index_by_pid_map.count(elem->id()));
1219 const dof_id_type global_index_by_pid =
1220 _global_index_by_pid_map[elem->id()];
1221
1222 const dof_id_type local_index =
1223 global_index_by_pid - first_local_elem;
1224 libmesh_assert_less (local_index, n_active_local_elem);
1225
1226 std::vector<dof_id_type> & graph_row = _dual_graph[local_index];
1227
1228 // Save this off to make it easy to index later
1229 _local_id_to_elem[local_index] = const_cast<Elem*>(elem);
1230
1231 // Loop over the element's neighbors. An element
1232 // adjacency corresponds to a face neighbor
1233 for (auto neighbor : elem->neighbor_ptr_range())
1234 {
1235 if (neighbor != nullptr)
1236 {
1237 // If the neighbor is active treat it
1238 // as a connection
1239 if (neighbor->active())
1240 {
1241 libmesh_assert(_global_index_by_pid_map.count(neighbor->id()));
1242 const dof_id_type neighbor_global_index_by_pid =
1243 _global_index_by_pid_map[neighbor->id()];
1244
1245 graph_row.push_back(neighbor_global_index_by_pid);
1246 }
1247
1248#ifdef LIBMESH_ENABLE_AMR
1249
1250 // Otherwise we need to find all of the
1251 // neighbor's children that are connected to
1252 // us and add them
1253 else
1254 {
1255 // The side of the neighbor to which
1256 // we are connected
1257 const unsigned int ns =
1258 neighbor->which_neighbor_am_i (elem);
1259 libmesh_assert_less (ns, neighbor->n_neighbors());
1260
1261 // Get all the active children (& grandchildren, etc...)
1262 // of the neighbor
1263
1264 // FIXME - this is the wrong thing, since we
1265 // should be getting the active family tree on
1266 // our side only. But adding too many graph
1267 // links may cause hanging nodes to tend to be
1268 // on partition interiors, which would reduce
1269 // communication overhead for constraint
1270 // equations, so we'll leave it.
1271
1272 neighbor->active_family_tree (neighbors_offspring);
1273
1274 // Get all the neighbor's children that
1275 // live on that side and are thus connected
1276 // to us
1277 for (const auto & child : neighbors_offspring)
1278 {
1279 // This does not assume a level-1 mesh.
1280 // Note that since children have sides numbered
1281 // coincident with the parent then this is a sufficient test.
1282 if (child->neighbor_ptr(ns) == elem)
1283 {
1284 libmesh_assert (child->active());
1285 libmesh_assert (_global_index_by_pid_map.count(child->id()));
1286 const dof_id_type child_global_index_by_pid =
1287 _global_index_by_pid_map[child->id()];
1288
1289 graph_row.push_back(child_global_index_by_pid);
1290 }
1291 }
1292 }
1293
1294#endif /* ifdef LIBMESH_ENABLE_AMR */
1295
1296
1297 }
1298 }
1299
1300 if ((elem->dim() < LIBMESH_DIM) &&
1301 elem->interior_parent())
1302 {
1303 // get all relevant interior elements
1304 std::set<const Elem *> neighbor_set;
1305 elem->find_interior_neighbors(neighbor_set);
1306
1307 for (const auto & neighbor : neighbor_set)
1308 {
1309 const dof_id_type neighbor_global_index_by_pid =
1310 _global_index_by_pid_map[neighbor->id()];
1311
1312 graph_row.push_back(neighbor_global_index_by_pid);
1313 }
1314 }
1315
1316 // Check for any boundary neighbors
1317 for (const auto & pr : as_range(interior_to_boundary_map.equal_range(elem)))
1318 {
1319 const Elem * neighbor = pr.second;
1320
1321 const dof_id_type neighbor_global_index_by_pid =
1322 _global_index_by_pid_map[neighbor->id()];
1323
1324 graph_row.push_back(neighbor_global_index_by_pid);
1325 }
1326
1327 // Check for any constraining elements
1328 if (!mesh_constrained_nodes.empty()) // quick test for non-IGA cases
1329 {
1330 const auto end_it = mesh_constrained_nodes.end();
1331
1332 // Use a set to avoid duplicates. Use a well-defined method
1333 // of ordering that set to make debugging easier.
1334 std::set<const Elem *, CompareElemIdsByLevel> constraining_elems;
1335 for (const Node & node : elem->node_ref_range())
1336 {
1337 if (const auto row_it = mesh_constrained_nodes.find(&node);
1338 row_it != end_it)
1339 for (const auto & [pr, coef] : row_it->second)
1340 {
1341 libmesh_ignore(coef); // avoid gcc 7 warning
1342 constraining_elems.insert(pr.first);
1343 }
1344 }
1345 for (const Elem * constraining_elem : constraining_elems)
1346 {
1347 const dof_id_type constraining_global_index_by_pid =
1348 _global_index_by_pid_map[constraining_elem->id()];
1349
1350 graph_row.push_back(constraining_global_index_by_pid);
1351
1352 // We can't be sure if the constraining element's owner sees
1353 // the assembly element, so to get a symmetric connectivity
1354 // graph we'll need to tell them about us to be safe.
1355 if (constraining_elem->processor_id() != mesh.processor_id())
1356 connections_to_push[constraining_elem->processor_id()].emplace_back
1357 (global_index_by_pid, constraining_global_index_by_pid);
1358 }
1359 }
1360
1361 // Check for any constrained elements
1362 for (const auto & pr : as_range(elems_constrained_by.equal_range(elem)))
1363 {
1364 const Elem * constrained = pr.second;
1365 const dof_id_type constrained_global_index_by_pid =
1366 _global_index_by_pid_map[constrained->id()];
1367
1368 graph_row.push_back(constrained_global_index_by_pid);
1369
1370 // We can't be sure if the constrained element's owner sees
1371 // the assembly element, so to get a symmetric connectivity
1372 // graph we'll need to tell them about us to be safe.
1373 if (constrained->processor_id() != mesh.processor_id())
1374 connections_to_push[constrained->processor_id()].emplace_back
1375 (global_index_by_pid, constrained_global_index_by_pid);
1376 }
1377 }
1378
1379 // Partitioners like Parmetis require a symmetric adjacency matrix,
1380 // but if we have an assembly element constrained by a spline
1381 // NodeElem owned by another processor, it's possible that that
1382 // processor doesn't see our assembly element. Let's push those
1383 // entries, to ensure they're counted on both sides.
1384 auto symmetrize_entries =
1385 [this, first_local_elem]
1386 (processor_id_type /*src_pid*/,
1387 const std::vector<std::pair<dof_id_type, dof_id_type>> & incoming_entries)
1388 {
1389 for (auto [i, j] : incoming_entries)
1390 {
1391 libmesh_assert_greater_equal(j, first_local_elem);
1392 const std::size_t jl = j - first_local_elem;
1393 libmesh_assert_less(jl, _dual_graph.size());
1394 std::vector<dof_id_type> & graph_row = _dual_graph[jl];
1395 if (std::find(graph_row.begin(), graph_row.end(), i) == graph_row.end())
1396 {
1397// std::cerr << "Pushing back (" << j << ", " << i << ") from " << src_pid << std::endl;
1398 graph_row.push_back(i);
1399 }
1400 }
1401 };
1402
1404 connections_to_push,
1405 symmetrize_entries);
1406
1407 // *Now* we should have a symmetric adjacency matrix. Let's check
1408 // that, so any failures get caught before they e.g. confuse
1409 // Parmetis in hard-to-debug ways. That's a global communication so
1410 // we'll only do it in debug mode.
1411#ifdef DEBUG
1412 auto n_proc = mesh.n_processors();
1413 std::vector<dof_id_type> first_local_index_on_proc(n_proc, 0);
1414 for (auto pid : make_range(1u,n_proc))
1415 first_local_index_on_proc[pid] = first_local_index_on_proc[pid-1] + _n_active_elem_on_proc[pid-1];
1416
1417 libmesh_assert_equal_to(first_local_index_on_proc[mesh.processor_id()],
1418 first_local_elem);
1419
1420 std::unordered_map<processor_id_type, std::vector<std::pair<dof_id_type, dof_id_type>>> entries_to_send;
1421 for (auto il : index_range(_dual_graph))
1422 {
1423 const std::vector<dof_id_type> & graph_row = _dual_graph[il];
1424
1425 const auto i = il + first_local_elem;
1426
1427 for (auto j : graph_row)
1428 {
1429 // Stupid graph rows aren't sorted yet...
1430 processor_id_type target_pid = 0;
1431 while (target_pid+1 < n_proc &&
1432 j >= first_local_index_on_proc[target_pid+1])
1433 ++target_pid;
1434
1435 entries_to_send[target_pid].emplace_back(i,j);
1436 }
1437 }
1438
1439 std::vector<std::tuple<processor_id_type, dof_id_type, dof_id_type>> bad_entries;
1440
1441 auto check_incoming_entries =
1442 [this, first_local_elem, &bad_entries]
1443 (processor_id_type src_pid,
1444 const std::vector<std::pair<dof_id_type, dof_id_type>> & incoming_entries)
1445 {
1446 for (auto [i, j] : incoming_entries)
1447 {
1448 if (j < first_local_elem)
1449 {
1450 bad_entries.emplace_back(src_pid,i,j);
1451 continue;
1452 }
1453 const std::size_t jl = j - first_local_elem;
1454 if (jl >= _dual_graph.size())
1455 {
1456 bad_entries.emplace_back(src_pid,i,j);
1457 continue;
1458 }
1459 const std::vector<dof_id_type> & graph_row = _dual_graph[jl];
1460 if (std::find(graph_row.begin(), graph_row.end(), i) ==
1461 graph_row.end())
1462 {
1463 bad_entries.emplace_back(src_pid,i,j);
1464 continue;
1465 }
1466 }
1467 };
1468
1469 // Keep any failures in sync in parallel, for easier debugging in
1470 // unit tests where the failure exception will be caught.
1472 (mesh.comm(), entries_to_send, check_incoming_entries);
1473 bool bad_entries_exist = !bad_entries.empty();
1474 mesh.comm().max(bad_entries_exist);
1475 if (bad_entries_exist)
1476 {
1477#if 0 // Optional verbosity for if this breaks again...
1478 if (!bad_entries.empty())
1479 {
1480 std::cerr << "Bad entries on processor " << mesh.processor_id() << ": ";
1481 for (auto [p, i, j] : bad_entries)
1482 std::cerr << '(' << p << ", " << i << ", " << j << "), ";
1483 std::cerr << std::endl;
1484 }
1485#endif
1486 libmesh_error_msg("Asymmetric partitioner graph detected");
1487 }
1488#endif
1489}
void max(const T &r, T &o, Request &req) const
dof_id_type id() const
Definition dof_object.h:833
This is the base class from which all geometric element types are derived.
Definition elem.h:96
constraint_rows_type & get_constraint_rows()
Constraint rows accessors.
Definition mesh_base.h:1940
A Node is like a Point, but with more information.
Definition node.h:55
processor_id_type processor_id() const
std::vector< std::vector< dof_id_type > > _dual_graph
A dual graph corresponds to the mesh, and it is typically used in paritioner.
virtual void _find_global_index_by_pid_map(const MeshBase &mesh)
Construct contiguous global indices for the current partitioning.
std::vector< Elem * > _local_id_to_elem
void push_parallel_vector_data(const Communicator &comm, MapToVectors &&data, const ActionFunctor &act_on_data)
SimpleRange< IndexType > as_range(const std::pair< IndexType, IndexType > &p)
Helper function that allows us to treat a homogenous pair as a range.
auto index_range(const T &sizable)
Helper function that returns an IntRange<std::size_t> representing all the indices of the passed-in v...
Definition int_range.h:153
void libmesh_ignore(const Args &...)

References libMesh::Partitioner::_dual_graph, libMesh::Partitioner::_find_global_index_by_pid_map(), libMesh::Partitioner::_global_index_by_pid_map, libMesh::Partitioner::_local_id_to_elem, libMesh::Partitioner::_n_active_elem_on_proc, libMesh::as_range(), libMesh::ParallelObject::comm(), libMesh::MeshBase::get_constraint_rows(), libMesh::DofObject::id(), libMesh::index_range(), libMesh::libmesh_assert(), libMesh::libmesh_ignore(), libMesh::make_range(), libMesh::Parallel::Communicator::max(), mesh, libMesh::MeshBase::n_active_local_elem(), libMesh::ParallelObject::n_processors(), libMesh::DofObject::processor_id(), libMesh::ParallelObject::processor_id(), and TIMPI::push_parallel_vector_data().

◆ clone()

std::unique_ptr< Partitioner > GhostDofConstraintPartitioner::clone ( ) const
inlineoverridevirtual
Returns
A copy of this partitioner wrapped in a smart pointer.

This is used when copying meshes, and must be overridden in the derived classes.

Implements libMesh::Partitioner.

Definition at line 83 of file dof_map_test.C.

84 {
85 return std::make_unique<GhostDofConstraintPartitioner>(*this);
86 }

◆ partition() [1/2]

void libMesh::Partitioner::partition ( MeshBase &  mesh)
virtualinherited

Partitions the MeshBase into mesh.n_processors() by setting processor_id() on Nodes and Elems.

Note
If you are implementing a new type of Partitioner, you most likely do not want to override the partition() function, see instead the protected virtual _do_partition() method below. The partition() function is responsible for doing a lot of libmesh-internals-specific setup and finalization before and after the _do_partition() function is called. The only responsibility of the _do_partition() function, on the other hand, is to set the processor IDs of the elements according to a specific partitioning algorithm. See, e.g. MetisPartitioner for an example.

Definition at line 189 of file partitioner.C.

190{
191 this->partition(mesh,mesh.n_processors());
192}
virtual void partition(MeshBase &mesh, const unsigned int n)
Partitions the MeshBase into n parts by setting processor_id() on Nodes and Elems.

References mesh, libMesh::ParallelObject::n_processors(), and libMesh::Partitioner::partition().

◆ partition() [2/2]

void libMesh::Partitioner::partition ( MeshBase &  mesh,
const unsigned int  n 
)
virtualinherited

Partitions the MeshBase into n parts by setting processor_id() on Nodes and Elems.

Note
If you are implementing a new type of Partitioner, you most likely do not want to override the partition() function, see instead the protected virtual _do_partition() method below. The partition() function is responsible for doing a lot of libmesh-internals-specific setup and finalization before and after the _do_partition() function is called. The only responsibility of the _do_partition() function, on the other hand, is to set the processor IDs of the elements according to a specific partitioning algorithm. See, e.g. MetisPartitioner for an example.

Definition at line 196 of file partitioner.C.

198{
199 libmesh_parallel_only(mesh.comm());
200
201 // BSK - temporary fix while redistribution is integrated 6/26/2008
202 // Uncomment this to not repartition in parallel
203 // if (!mesh.is_serial())
204 // return;
205
206 // we cannot partition into more pieces than we have
207 // active elements!
208 const unsigned int n_parts =
209 static_cast<unsigned int>
210 (std::min(mesh.n_active_elem(), static_cast<dof_id_type>(n)));
211
212 // Set the number of partitions in the mesh
213 mesh.set_n_partitions()=n_parts;
214
215 if (n_parts == 1)
216 {
217 this->single_partition (mesh);
218 return;
219 }
220
221 // First assign a temporary partitioning to any unpartitioned elements. A spatially
222 // coherent assignment is only worth its parallel sort for a partitioner that refines what
223 // it is handed; the other partitioners just need one that is consistent across processors,
224 // which the cheap traversal-order assignment already is on a replicated mesh.
226 (mesh, n_parts,
227 /*spatial_indexing=*/!(mesh.is_replicated() && this->partitions_from_scratch()));
228
229 // Call the partitioning function
230 this->_do_partition(mesh,n_parts);
231
232 // Set the parent's processor ids
234
235 // Redistribute elements if necessary, before setting node processor
236 // ids, to make sure those will be set consistently
238
239#ifdef DEBUG
241
242 // Messed up elem processor_id()s can leave us without the child
243 // elements we need to restrict vectors on a distributed mesh
244 MeshTools::libmesh_assert_valid_procids<Elem>(mesh);
245#endif
246
247 // Set the node's processor ids
249
250#ifdef DEBUG
251 MeshTools::libmesh_assert_valid_procids<Elem>(mesh);
252#endif
253
254 // Give derived Mesh classes a chance to update any cached data to
255 // reflect the new partitioning
257}
virtual bool is_replicated() const
Definition mesh_base.h:379
virtual void redistribute()
Redistribute elements between processors.
Definition mesh_base.C:1169
virtual void update_post_partitioning()
Recalculate any cached data (or invalidate any caches that are computed on the fly) after elements an...
Definition mesh_base.C:1181
virtual dof_id_type n_active_elem() const =0
unsigned int & set_n_partitions()
Definition mesh_base.h:2176
bool single_partition(MeshBase &mesh)
Trivially "partitions" the mesh for one processor.
static void set_parent_processor_ids(MeshBase &mesh)
This function is called after partitioning to set the processor IDs for the inactive parent elements.
static void partition_unpartitioned_elements(MeshBase &mesh)
These functions assign processor IDs to newly-created elements (in parallel) which are currently assi...
static void set_node_processor_ids(MeshBase &mesh)
This function is called after partitioning to set the processor IDs for the nodes.
void libmesh_assert_valid_remote_elems(const MeshBase &mesh)
A function for verifying that active local elements' neighbors are never remote elements.

References libMesh::Partitioner::_do_partition(), libMesh::ParallelObject::comm(), libMesh::MeshBase::is_replicated(), libMesh::MeshTools::libmesh_assert_valid_remote_elems(), mesh, libMesh::MeshBase::n_active_elem(), libMesh::Partitioner::partition_unpartitioned_elements(), libMesh::MeshBase::redistribute(), libMesh::MeshBase::set_n_partitions(), libMesh::Partitioner::set_node_processor_ids(), libMesh::Partitioner::set_parent_processor_ids(), libMesh::Partitioner::single_partition(), and libMesh::MeshBase::update_post_partitioning().

Referenced by libMesh::ParmetisPartitioner::_do_repartition(), and libMesh::Partitioner::partition().

◆ partition_range()

virtual void libMesh::Partitioner::partition_range ( MeshBase &  ,
MeshBase::element_iterator  ,
MeshBase::element_iterator  ,
const unsigned int   
)
inlinevirtualinherited

Partitions elements in the range (it, end) into n parts.

The mesh from which the iterators are created must also be passed in, since it is a parallel object and has other useful information in it.

Although partition_range() is part of the public Partitioner interface, it should not generally be called by applications. Its main purpose is to support the SubdomainPartitioner, which uses it internally to individually partition ranges of elements before combining them into the final partitioning. Most of the time, the protected _do_partition() function is implemented in terms of partition_range() by passing a range which includes all the elements of the Mesh.

Reimplemented in libMesh::CentroidPartitioner, libMesh::LinearPartitioner, libMesh::MappedSubdomainPartitioner, libMesh::MetisPartitioner, and libMesh::SFCPartitioner.

Definition at line 137 of file partitioner.h.

141 { libmesh_not_implemented(); }

Referenced by libMesh::SubdomainPartitioner::_do_partition().

◆ partition_unpartitioned_elements() [1/3]

void libMesh::Partitioner::partition_unpartitioned_elements ( MeshBase &  mesh)
staticinherited

◆ partition_unpartitioned_elements() [2/3]

void libMesh::Partitioner::partition_unpartitioned_elements ( MeshBase &  mesh,
const unsigned int  n 
)
staticinherited

Definition at line 360 of file partitioner.C.

362{
364 /*spatial_indexing=*/true);
365}

References mesh, and libMesh::Partitioner::partition_unpartitioned_elements().

◆ partition_unpartitioned_elements() [3/3]

void libMesh::Partitioner::partition_unpartitioned_elements ( MeshBase &  mesh,
const unsigned int  n,
const bool  spatial_indexing 
)
staticinherited

As above, but spatial_indexing selects how the elements are mapped onto the partitions.

With true they are ordered along a space-filling curve, so that each partition gets a spatially compact set; this costs a parallel sort. With false their position in the traversal of the range is used, which is free but is only consistent across processors for a replicated mesh.

Definition at line 369 of file partitioner.C.

372{
373 MeshBase::element_iterator it = mesh.unpartitioned_elements_begin();
374 const MeshBase::element_iterator end = mesh.unpartitioned_elements_end();
375
376 const dof_id_type n_unpartitioned_elements = MeshTools::n_elem (it, end);
377
378 // the unpartitioned elements must exist on all processors. If the range is empty on one
379 // it is empty on all, and we can quit right here.
380 if (!n_unpartitioned_elements)
381 return;
382
383 // find the target subdomain sizes
384 std::vector<dof_id_type> subdomain_bounds(mesh.n_processors());
385
386 for (auto pid : make_range(mesh.n_processors()))
387 {
388 dof_id_type tgt_subdomain_size = 0;
389
390 // watch out for the case that n_subdomains < n_processors
391 if (pid < n_subdomains)
392 {
393 tgt_subdomain_size = n_unpartitioned_elements/n_subdomains;
394
395 if (pid < n_unpartitioned_elements%n_subdomains)
396 tgt_subdomain_size++;
397
398 }
399
400 //libMesh::out << "pid, #= " << pid << ", " << tgt_subdomain_size << std::endl;
401 if (pid == 0)
402 subdomain_bounds[0] = tgt_subdomain_size;
403 else
404 subdomain_bounds[pid] = subdomain_bounds[pid-1] + tgt_subdomain_size;
405 }
406
407 libmesh_assert_equal_to (subdomain_bounds.back(), n_unpartitioned_elements);
408
409 // create the unique mapping for all unpartitioned elements independent of partitioning
410 // determine the global indexing for all the unpartitioned elements
411 std::vector<dof_id_type> global_indices;
412
413 // Calling this on all processors a unique range in [0,n_unpartitioned_elements) is constructed.
414 // Only the indices for the elements we pass in are returned in the array.
415 // Without the spatial indexing an element's position in the traversal of the range is used
416 // instead, which a replicated mesh reaches in the same order on every processor and which
417 // therefore indexes [0, n_unpartitioned_elements) just as uniquely, at no cost.
418 if (spatial_indexing)
421 global_indices);
422 else
424
425 dof_id_type cnt=0;
426 for (auto & elem : as_range(it, end))
427 {
428 libmesh_assert (!spatial_indexing || cnt < global_indices.size());
429 const dof_id_type global_index =
430 spatial_indexing ? global_indices[cnt++] : cnt++;
431
432 libmesh_assert_less (global_index, subdomain_bounds.back());
433 libmesh_assert_less (global_index, n_unpartitioned_elements);
434
435 const processor_id_type subdomain_id =
436 cast_int<processor_id_type>
437 (std::distance(subdomain_bounds.begin(),
438 std::upper_bound(subdomain_bounds.begin(),
439 subdomain_bounds.end(),
440 global_index)));
441 libmesh_assert_less (subdomain_id, n_subdomains);
442
443 elem->processor_id() = subdomain_id;
444 //libMesh::out << "assigning " << global_index << " to " << subdomain_id << std::endl;
445 }
446}
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...
dof_id_type n_elem(const MeshBase::const_element_iterator &begin, const MeshBase::const_element_iterator &end)
Count up the number of elements of a specific type (as defined by an iterator range).
The definition of the element_iterator struct.
Definition mesh_base.h:2557

References libMesh::as_range(), libMesh::ParallelObject::comm(), libMesh::MeshTools::create_bounding_box(), libMesh::MeshCommunication::find_global_indices(), libMesh::MeshBase::is_replicated(), libMesh::libmesh_assert(), libMesh::make_range(), mesh, libMesh::MeshTools::n_elem(), and libMesh::ParallelObject::n_processors().

◆ partitions_from_scratch()

virtual bool GhostDofConstraintPartitioner::partitions_from_scratch ( ) const
inlineoverridevirtual
Returns
Whether this partitioner computes its partitioning without reference to the partitioning the mesh already carries.

A partitioner that instead refines what it is handed is sensitive to the temporary assignment partition_unpartitioned_elements() gives to unpartitioned elements, and so should have that assignment be spatially coherent rather than merely consistent across processors.

The base-class default is conservative, so that a sub-class partitioner which has not considered whether to override this virtual keeps a spatially coherent temporary partitioning assignment. Overriding it to return true buys a cheaper temporary partitioning on a replicated mesh.

Todo:
Make this pure virtual, so that every partitioner has to state which kind it is, once the partitioners outside the library have been given an override.

Reimplemented from libMesh::Partitioner.

Definition at line 88 of file dof_map_test.C.

88{ return true; }

◆ processor_pairs_to_interface_nodes()

void libMesh::Partitioner::processor_pairs_to_interface_nodes ( MeshBase &  mesh,
std::map< std::pair< processor_id_type, processor_id_type >, std::set< dof_id_type > > &  processor_pair_to_nodes 
)
staticinherited

On the partitioning interface, a surface is shared by two and only two processors.

Try to find which pair of processors corresponds to which surfaces, and store their nodes.

Definition at line 603 of file partitioner.C.

605{
606 // This function must be run on all processors at once
607 libmesh_parallel_only(mesh.comm());
608
609 processor_pair_to_nodes.clear();
610
611 std::set<dof_id_type> mynodes;
612 std::set<dof_id_type> neighbor_nodes;
613 std::vector<dof_id_type> common_nodes;
614
615 // Loop over all the active elements
616 for (auto & elem : mesh.active_element_ptr_range())
617 {
618 libmesh_assert(elem);
619
620 libmesh_assert_not_equal_to (elem->processor_id(), DofObject::invalid_processor_id);
621
622 auto n_nodes = elem->n_nodes();
623
624 // prepare data for this element
625 mynodes.clear();
626 neighbor_nodes.clear();
627 common_nodes.clear();
628
629 for (unsigned int inode = 0; inode < n_nodes; inode++)
630 mynodes.insert(elem->node_id(inode));
631
632 for (auto i : elem->side_index_range())
633 {
634 auto neigh = elem->neighbor_ptr(i);
635 if (neigh && !neigh->is_remote() && neigh->processor_id() != elem->processor_id())
636 {
637 neighbor_nodes.clear();
638 common_nodes.clear();
639 auto neigh_n_nodes = neigh->n_nodes();
640 for (unsigned int inode = 0; inode < neigh_n_nodes; inode++)
641 neighbor_nodes.insert(neigh->node_id(inode));
642
643 std::set_intersection(mynodes.begin(), mynodes.end(),
644 neighbor_nodes.begin(), neighbor_nodes.end(),
645 std::back_inserter(common_nodes));
646
647 auto & map_set = processor_pair_to_nodes[std::make_pair(std::min(elem->processor_id(), neigh->processor_id()),
648 std::max(elem->processor_id(), neigh->processor_id()))];
649 for (auto global_node_id : common_nodes)
650 map_set.insert(global_node_id);
651 }
652 }
653 }
654}
static constexpr processor_id_type invalid_processor_id
An invalid processor_id to distinguish DoFs that have not been assigned to a processor.
Definition dof_object.h:484
const dof_id_type n_nodes
Definition tecplot_io.C:67

References libMesh::ParallelObject::comm(), libMesh::DofObject::invalid_processor_id, libMesh::libmesh_assert(), mesh, and n_nodes.

Referenced by libMesh::Partitioner::set_interface_node_processor_ids_BFS(), libMesh::Partitioner::set_interface_node_processor_ids_linear(), and libMesh::Partitioner::set_interface_node_processor_ids_petscpartitioner().

◆ repartition() [1/2]

void libMesh::Partitioner::repartition ( MeshBase &  mesh)
inherited

Repartitions the MeshBase into mesh.n_processors() parts.

This is required since some partitioning algorithms can repartition more efficiently than computing a new partitioning from scratch.

Definition at line 261 of file partitioner.C.

262{
263 this->repartition(mesh,mesh.n_processors());
264}
void repartition(MeshBase &mesh, const unsigned int n)
Repartitions the MeshBase into n parts.

References mesh, libMesh::ParallelObject::n_processors(), and libMesh::Partitioner::repartition().

◆ repartition() [2/2]

void libMesh::Partitioner::repartition ( MeshBase &  mesh,
const unsigned int  n 
)
inherited

Repartitions the MeshBase into n parts.

(Some partitioning algorithms can repartition more efficiently than computing a new partitioning from scratch.) The default behavior is to simply call this->partition(mesh,n).

Definition at line 268 of file partitioner.C.

270{
271 // we cannot partition into more pieces than we have
272 // active elements!
273 const unsigned int n_parts =
274 static_cast<unsigned int>
275 (std::min(mesh.n_active_elem(), static_cast<dof_id_type>(n)));
276
277 // Set the number of partitions in the mesh
278 mesh.set_n_partitions()=n_parts;
279
280 if (n_parts == 1)
281 {
282 this->single_partition (mesh);
283 return;
284 }
285
286 // First assign a temporary partitioning to any unpartitioned elements; see partition()
287 // for why the indexing is chosen this way.
289 (mesh, n_parts,
290 /*spatial_indexing=*/!(mesh.is_replicated() && this->partitions_from_scratch()));
291
292 // Call the partitioning function
293 this->_do_repartition(mesh,n_parts);
294
295 // Set the parent's processor ids
297
298 // Set the node's processor ids
300}
virtual void _do_repartition(MeshBase &mesh, const unsigned int n)
This is the actual re-partitioning method which can be overridden in derived classes.

References libMesh::Partitioner::_do_repartition(), libMesh::MeshBase::is_replicated(), mesh, libMesh::MeshBase::n_active_elem(), libMesh::Partitioner::partition_unpartitioned_elements(), libMesh::MeshBase::set_n_partitions(), libMesh::Partitioner::set_node_processor_ids(), libMesh::Partitioner::set_parent_processor_ids(), and libMesh::Partitioner::single_partition().

Referenced by libMesh::Partitioner::repartition().

◆ set_interface_node_processor_ids_BFS()

void libMesh::Partitioner::set_interface_node_processor_ids_BFS ( MeshBase &  mesh)
staticinherited

Nodes on the partitioning interface is clustered into two groups BFS (Breadth First Search)scheme for per pair of processors.

Definition at line 681 of file partitioner.C.

682{
683 // This function must be run on all processors at once
684 libmesh_parallel_only(mesh.comm());
685
686 // I see occasional consistency failures when using this on a
687 // distributed mesh
688 libmesh_experimental();
689
690 std::map<std::pair<processor_id_type, processor_id_type>, std::set<dof_id_type>> processor_pair_to_nodes;
691
692 processor_pairs_to_interface_nodes(mesh, processor_pair_to_nodes);
693
694 std::unordered_map<dof_id_type, std::vector<const Elem *>> nodes_to_elem_map;
695
696 MeshTools::build_nodes_to_elem_map(mesh, nodes_to_elem_map);
697
698 std::vector<const Node *> neighbors;
699 std::set<dof_id_type> neighbors_order;
700 std::vector<dof_id_type> common_nodes;
701 std::queue<dof_id_type> nodes_queue;
702 std::set<dof_id_type> visted_nodes;
703
704 for (auto & pmap : processor_pair_to_nodes)
705 {
706 std::size_t n_own_nodes = pmap.second.size()/2;
707
708 // Initialize node assignment
709 for (dof_id_type id : pmap.second)
710 mesh.node_ref(id).processor_id() = pmap.first.second;
711
712 visted_nodes.clear();
713 for (dof_id_type id : pmap.second)
714 {
715 mesh.node_ref(id).processor_id() = pmap.first.second;
716
717 if (visted_nodes.count(id))
718 continue;
719 else
720 {
721 nodes_queue.push(id);
722 visted_nodes.insert(id);
723 if (visted_nodes.size() >= n_own_nodes)
724 break;
725 }
726
727 while (!nodes_queue.empty())
728 {
729 auto & node = mesh.node_ref(nodes_queue.front());
730 nodes_queue.pop();
731
732 neighbors.clear();
733 MeshTools::find_nodal_neighbors(mesh, node, nodes_to_elem_map, neighbors);
734 neighbors_order.clear();
735 for (auto & neighbor : neighbors)
736 neighbors_order.insert(neighbor->id());
737
738 common_nodes.clear();
739 std::set_intersection(pmap.second.begin(), pmap.second.end(),
740 neighbors_order.begin(), neighbors_order.end(),
741 std::back_inserter(common_nodes));
742
743 for (auto c_node : common_nodes)
744 if (!visted_nodes.count(c_node))
745 {
746 nodes_queue.push(c_node);
747 visted_nodes.insert(c_node);
748 if (visted_nodes.size() >= n_own_nodes)
749 goto queue_done;
750 }
751
752 if (visted_nodes.size() >= n_own_nodes)
753 goto queue_done;
754 }
755 }
756 queue_done:
757 for (auto node : visted_nodes)
758 mesh.node_ref(node).processor_id() = pmap.first.first;
759 }
760}
virtual const Node & node_ref(const dof_id_type i) const
Definition mesh_base.h:742
virtual void clear()
Deletes all the element and node data that is currently stored.
Definition mesh_base.C:1036
static void processor_pairs_to_interface_nodes(MeshBase &mesh, std::map< std::pair< processor_id_type, processor_id_type >, std::set< dof_id_type > > &processor_pair_to_nodes)
On the partitioning interface, a surface is shared by two and only two processors.
void build_nodes_to_elem_map(const MeshBase &mesh, std::vector< std::vector< dof_id_type > > &nodes_to_elem_map)
After calling this function the input vector nodes_to_elem_map will contain the node to element conne...
Definition mesh_tools.C:456
void find_nodal_neighbors(const MeshBase &mesh, const Node &n, const std::vector< std::vector< const Elem * > > &nodes_to_elem_map, std::vector< const Node * > &neighbors)
Given a mesh and a node in the mesh, the vector will be filled with every node directly attached to t...

References libMesh::MeshTools::build_nodes_to_elem_map(), libMesh::ParallelObject::comm(), libMesh::MeshTools::find_nodal_neighbors(), mesh, libMesh::MeshBase::node_ref(), libMesh::DofObject::processor_id(), and libMesh::Partitioner::processor_pairs_to_interface_nodes().

Referenced by libMesh::Partitioner::set_node_processor_ids().

◆ set_interface_node_processor_ids_linear()

void libMesh::Partitioner::set_interface_node_processor_ids_linear ( MeshBase &  mesh)
staticinherited

Nodes on the partitioning interface is linearly assigned to each pair of processors.

Definition at line 656 of file partitioner.C.

657{
658 // This function must be run on all processors at once
659 libmesh_parallel_only(mesh.comm());
660
661 std::map<std::pair<processor_id_type, processor_id_type>, std::set<dof_id_type>> processor_pair_to_nodes;
662
663 processor_pairs_to_interface_nodes(mesh, processor_pair_to_nodes);
664
665 for (auto & pmap : processor_pair_to_nodes)
666 {
667 std::size_t n_own_nodes = pmap.second.size()/2, i = 0;
668
669 for (dof_id_type id : pmap.second)
670 {
671 auto & node = mesh.node_ref(id);
672 if (i <= n_own_nodes)
673 node.processor_id() = pmap.first.first;
674 else
675 node.processor_id() = pmap.first.second;
676 i++;
677 }
678 }
679}

References libMesh::ParallelObject::comm(), mesh, libMesh::MeshBase::node_ref(), libMesh::DofObject::processor_id(), and libMesh::Partitioner::processor_pairs_to_interface_nodes().

Referenced by libMesh::Partitioner::set_node_processor_ids().

◆ set_interface_node_processor_ids_petscpartitioner()

void libMesh::Partitioner::set_interface_node_processor_ids_petscpartitioner ( MeshBase &  mesh)
staticinherited

Nodes on the partitioning interface is partitioned into two groups using a PETSc partitioner for each pair of processors.

Definition at line 762 of file partitioner.C.

763{
764 libmesh_ignore(mesh); // Only used if LIBMESH_HAVE_PETSC
765
766 // This function must be run on all processors at once
767 libmesh_parallel_only(mesh.comm());
768
769#ifdef LIBMESH_HAVE_PETSC
770 std::map<std::pair<processor_id_type, processor_id_type>, std::set<dof_id_type>> processor_pair_to_nodes;
771
772 processor_pairs_to_interface_nodes(mesh, processor_pair_to_nodes);
773
774 std::vector<std::vector<const Elem *>> nodes_to_elem_map;
775
776 MeshTools::build_nodes_to_elem_map(mesh, nodes_to_elem_map);
777
778 std::vector<const Node *> neighbors;
779 std::set<dof_id_type> neighbors_order;
780 std::vector<dof_id_type> common_nodes;
781
782 std::vector<dof_id_type> rows;
783 std::vector<dof_id_type> cols;
784
785 std::map<dof_id_type, dof_id_type> global_to_local;
786
787 for (auto & pmap : processor_pair_to_nodes)
788 {
789 unsigned int i = 0;
790
791 rows.clear();
792 rows.resize(pmap.second.size()+1);
793 cols.clear();
794 for (dof_id_type id : pmap.second)
795 global_to_local[id] = i++;
796
797 i = 0;
798 for (auto id : pmap.second)
799 {
800 auto & node = mesh.node_ref(id);
801 neighbors.clear();
802 MeshTools::find_nodal_neighbors(mesh, node, nodes_to_elem_map, neighbors);
803 neighbors_order.clear();
804 for (auto & neighbor : neighbors)
805 neighbors_order.insert(neighbor->id());
806
807 common_nodes.clear();
808 std::set_intersection(pmap.second.begin(), pmap.second.end(),
809 neighbors_order.begin(), neighbors_order.end(),
810 std::back_inserter(common_nodes));
811
812 rows[i+1] = rows[i] + cast_int<dof_id_type>(common_nodes.size());
813
814 for (auto c_node : common_nodes)
815 cols.push_back(global_to_local[c_node]);
816
817 i++;
818 }
819
820 // Next we construct an IS from a MatPartitioning
822 {
823 PetscInt *adj_i, *adj_j;
824 LibmeshPetscCall2(mesh.comm(), PetscCalloc1(rows.size(), &adj_i));
825 LibmeshPetscCall2(mesh.comm(), PetscCalloc1(cols.size(), &adj_j));
826 PetscInt rows_size = cast_int<PetscInt>(rows.size());
827 for (PetscInt ii=0; ii<rows_size; ii++)
828 adj_i[ii] = rows[ii];
829
830 PetscInt cols_size = cast_int<PetscInt>(cols.size());
831 for (PetscInt ii=0; ii<cols_size; ii++)
832 adj_j[ii] = cols[ii];
833
834 const PetscInt sz = cast_int<PetscInt>(pmap.second.size());
835
836 // Create sparse matrix representing an adjacency list
838 LibmeshPetscCall2(mesh.comm(), MatCreateMPIAdj(PETSC_COMM_SELF, sz, sz, adj_i, adj_j, nullptr, adj.get()));
839
840 // Create MatPartitioning object
842 LibmeshPetscCall2(mesh.comm(), MatPartitioningCreate(PETSC_COMM_SELF, part.get()));
843
844 // Apply MatPartitioning, storing results in "is"
845 LibmeshPetscCall2(mesh.comm(), MatPartitioningSetAdjacency(part, adj));
846 LibmeshPetscCall2(mesh.comm(), MatPartitioningSetNParts(part, 2));
847 LibmeshPetscCall2(mesh.comm(), PetscObjectSetOptionsPrefix((PetscObject)(*part), "balance_"));
848 LibmeshPetscCall2(mesh.comm(), MatPartitioningSetFromOptions(part));
849 LibmeshPetscCall2(mesh.comm(), MatPartitioningApply(part, is.get()));
850 }
851
852 PetscInt local_size;
853 const PetscInt *indices;
854 LibmeshPetscCall2(mesh.comm(), ISGetLocalSize(is, &local_size));
855 LibmeshPetscCall2(mesh.comm(), ISGetIndices(is, &indices));
856
857 i = 0;
858 for (auto id : pmap.second)
859 {
860 auto & node = mesh.node_ref(id);
861 if (indices[i])
862 node.processor_id() = pmap.first.second;
863 else
864 node.processor_id() = pmap.first.first;
865
866 i++;
867 }
868 LibmeshPetscCall2(mesh.comm(), ISRestoreIndices(is, &indices));
869 }
870#else
871 libmesh_error_msg("PETSc is required");
872#endif
873}
PetscErrorCode PetscInt const PetscInt IS * is

References libMesh::MeshTools::build_nodes_to_elem_map(), libMesh::ParallelObject::comm(), libMesh::MeshTools::find_nodal_neighbors(), libMesh::WrappedPetsc< T >::get(), libMesh::is, libMesh::libmesh_ignore(), mesh, libMesh::MeshBase::node_ref(), libMesh::DofObject::processor_id(), and libMesh::Partitioner::processor_pairs_to_interface_nodes().

Referenced by libMesh::Partitioner::set_node_processor_ids().

◆ set_node_processor_ids()

void libMesh::Partitioner::set_node_processor_ids ( MeshBase &  mesh)
staticinherited

This function is called after partitioning to set the processor IDs for the nodes.

By definition, a Node's processor ID is the minimum processor ID for all of the elements which share the node.

Definition at line 876 of file partitioner.C.

877{
878 LOG_SCOPE("set_node_processor_ids()","Partitioner");
879
880 // This function must be run on all processors at once
881 libmesh_parallel_only(mesh.comm());
882
883 // If we have any unpartitioned elements at this
884 // stage there is a problem
885 libmesh_assert (MeshTools::n_elem(mesh.unpartitioned_elements_begin(),
886 mesh.unpartitioned_elements_end()) == 0);
887
888 // Start from scratch here: nodes we used to own may not be
889 // eligible for us to own any more.
890 for (auto & node : mesh.node_ptr_range())
891 {
892 node->processor_id() = DofObject::invalid_processor_id;
893 }
894
895 // Loop over all the active elements
896 for (auto & elem : mesh.active_element_ptr_range())
897 {
898 libmesh_assert(elem);
899
900 libmesh_assert_not_equal_to (elem->processor_id(), DofObject::invalid_processor_id);
901
902 // Consider updating the processor id on this element's nodes
903 for (Node & node : elem->node_ref_range())
904 {
905 processor_id_type & pid = node.processor_id();
906 pid = node.choose_processor_id(pid, elem->processor_id());
907 }
908 }
909
910 // How we finish off the node partitioning depends on our command
911 // line options.
912
913 const bool load_balanced_nodes_linear =
914 libMesh::on_command_line ("--load-balanced-nodes-linear");
915
916 const bool load_balanced_nodes_bfs =
917 libMesh::on_command_line ("--load-balanced-nodes-bfs");
918
919 const bool load_balanced_nodes_petscpartition =
920 libMesh::on_command_line ("--load-balanced-nodes-petscpartitioner");
921
922 unsigned int n_load_balance_options = load_balanced_nodes_linear;
923 n_load_balance_options += load_balanced_nodes_bfs;
924 n_load_balance_options += load_balanced_nodes_petscpartition;
925 libmesh_error_msg_if(n_load_balance_options > 1,
926 "Cannot perform more than one load balancing type at a time");
927
928 if (load_balanced_nodes_linear)
930 else if (load_balanced_nodes_bfs)
932 else if (load_balanced_nodes_petscpartition)
934
935 // Node balancing algorithm will response to assign owned nodes.
936 // We still need to sync PIDs
937 {
938 // For inactive elements, we will have already gotten most of
939 // these nodes, *except* for the case of a parent with a subset
940 // of active descendants which are remote elements. In that
941 // case some of the parent nodes will not have been properly
942 // handled yet on our processor.
943 //
944 // We don't want to inadvertently give one of them an incorrect
945 // processor id, but if we're not in serial then we have to
946 // assign them temporary pids to make querying work, so we'll
947 // save our *valid* pids before assigning temporaries.
948 //
949 // Even in serial we'll want to check and make sure we're not
950 // overwriting valid active node pids with pids from subactive
951 // elements.
952 std::unordered_set<dof_id_type> bad_pids;
953
954 for (auto & node : mesh.node_ptr_range())
955 if (node->processor_id() == DofObject::invalid_processor_id)
956 bad_pids.insert(node->id());
957
958 // If we assign our temporary ids by looping from finer elements
959 // to coarser elements, we'll always get an id from the finest
960 // ghost element we can see, which will usually be "closer" to
961 // the true processor we want to query and so will reduce query
962 // cycles that don't reach that processor.
963
964 // But we can still end up with a query cycle that dead-ends, so
965 // we need to prepare a "push" communication step here.
966
967 const bool is_serial = mesh.is_serial();
968 std::unordered_map
970 std::unordered_map<dof_id_type, processor_id_type>>
971 potential_pids;
972
973 const unsigned int n_levels = MeshTools::n_levels(mesh);
974 for (unsigned int level = n_levels; level > 0; --level)
975 {
976 for (auto & elem : as_range(mesh.level_elements_begin(level-1),
977 mesh.level_elements_end(level-1)))
978 {
979 libmesh_assert_not_equal_to (elem->processor_id(),
981
982 const processor_id_type elem_pid = elem->processor_id();
983
984 // Consider updating the processor id on this element's nodes
985 for (Node & node : elem->node_ref_range())
986 {
987 processor_id_type & pid = node.processor_id();
988 if (bad_pids.count(node.id()))
989 pid = node.choose_processor_id(pid, elem_pid);
990 else if (!is_serial)
991 potential_pids[elem_pid][node.id()] = pid;
992 }
993 }
994 }
995
996 if (!is_serial)
997 {
998 std::unordered_map
1000 std::vector<std::pair<dof_id_type, processor_id_type>>>
1001 potential_pids_vecs;
1002
1003 for (auto & pair : potential_pids)
1004 potential_pids_vecs[pair.first].assign(pair.second.begin(), pair.second.end());
1005
1006 auto pids_action_functor =
1007 [& mesh, & bad_pids]
1008 (processor_id_type /* src_pid */,
1009 const std::vector<std::pair<dof_id_type, processor_id_type>> & data)
1010 {
1011 for (auto pair : data)
1012 {
1013 Node & node = mesh.node_ref(pair.first);
1014 processor_id_type & pid = node.processor_id();
1015 if (const auto it = bad_pids.find(pair.first);
1016 it != bad_pids.end())
1017 {
1018 pid = pair.second;
1019 bad_pids.erase(it);
1020 }
1021 else
1022 pid = node.choose_processor_id(pid, pair.second);
1023 }
1024 };
1025
1027 (mesh.comm(), potential_pids_vecs, pids_action_functor);
1028
1029 // Using default libMesh options, we'll just need to sync
1030 // between processors now. The catch here is that we can't
1031 // initially trust Node::choose_processor_id() because some
1032 // of those node processor ids are the temporary ones.
1033 CorrectProcIds correct_pids(mesh, bad_pids);
1035 (mesh, mesh.elements_begin(), mesh.elements_end(),
1037 correct_pids);
1038
1039 // But once we've got all the non-temporary pids synced, we
1040 // may need to sync again to get any pids on nodes only
1041 // connected to subactive elements, for which *only*
1042 // "temporary" pids are possible.
1043 bad_pids.clear();
1045 (mesh,
1046 mesh.elements_begin(), mesh.elements_end(),
1048 correct_pids);
1049 }
1050 }
1051
1052 // We can't assert that all nodes are connected to elements, because
1053 // a DistributedMesh with NodeConstraints might have pulled in some
1054 // remote nodes solely for evaluating those constraints.
1055 // MeshTools::libmesh_assert_connected_nodes(mesh);
1056
1057#ifdef DEBUG
1058 MeshTools::libmesh_assert_valid_procids<Node>(mesh);
1059 //MeshTools::libmesh_assert_canonical_node_procids(mesh);
1060#endif
1061}
The DofObject defines an abstract base class for objects that have degrees of freedom associated with...
Definition dof_object.h:55
virtual bool is_serial() const
Definition mesh_base.h:357
processor_id_type choose_processor_id(processor_id_type pid1, processor_id_type pid2) const
Return which of pid1 and pid2 would be preferred by the current load-balancing heuristic applied to t...
Definition node.C:78
static void set_interface_node_processor_ids_petscpartitioner(MeshBase &mesh)
Nodes on the partitioning interface is partitioned into two groups using a PETSc partitioner for each...
static void set_interface_node_processor_ids_BFS(MeshBase &mesh)
Nodes on the partitioning interface is clustered into two groups BFS (Breadth First Search)scheme for...
static void set_interface_node_processor_ids_linear(MeshBase &mesh)
Nodes on the partitioning interface is linearly assigned to each pair of processors.
unsigned int n_levels(const MeshBase &mesh)
Definition mesh_tools.C:826
void sync_node_data_by_element_id(MeshBase &mesh, const MeshBase::const_element_iterator &range_begin, const MeshBase::const_element_iterator &range_end, const ElemCheckFunctor &elem_check, const NodeCheckFunctor &node_check, SyncFunctor &sync)
Synchronize data about a range of ghost nodes uniquely identified by an element id and local node id,...
bool on_command_line(std::string arg)
Definition libmesh.C:934

References libMesh::as_range(), libMesh::Node::choose_processor_id(), libMesh::ParallelObject::comm(), libMesh::DofObject::id(), libMesh::DofObject::invalid_processor_id, libMesh::MeshBase::is_serial(), libMesh::libmesh_assert(), mesh, libMesh::MeshTools::n_elem(), libMesh::MeshTools::n_levels(), libMesh::MeshBase::node_ref(), libMesh::on_command_line(), libMesh::DofObject::processor_id(), TIMPI::push_parallel_vector_data(), libMesh::Partitioner::set_interface_node_processor_ids_BFS(), libMesh::Partitioner::set_interface_node_processor_ids_linear(), libMesh::Partitioner::set_interface_node_processor_ids_petscpartitioner(), and libMesh::Parallel::sync_node_data_by_element_id().

Referenced by libMesh::UnstructuredMesh::all_first_order(), libMesh::Partitioner::partition(), libMesh::XdrIO::read(), libMesh::Partitioner::repartition(), and libMesh::BoundaryInfo::sync().

◆ set_parent_processor_ids()

void libMesh::Partitioner::set_parent_processor_ids ( MeshBase &  mesh)
staticinherited

This function is called after partitioning to set the processor IDs for the inactive parent elements.

A parent's processor ID is the same as its first child.

Definition at line 450 of file partitioner.C.

451{
452 // Ignore the parameter when !LIBMESH_ENABLE_AMR
454
455 LOG_SCOPE("set_parent_processor_ids()", "Partitioner");
456
457#ifdef LIBMESH_ENABLE_AMR
458
459 // If the mesh is serial we have access to all the elements,
460 // in particular all the active ones. We can therefore set
461 // the parent processor ids indirectly through their children, and
462 // set the subactive processor ids while examining their active
463 // ancestors.
464 // By convention a parent is assigned to the minimum processor
465 // of all its children, and a subactive is assigned to the processor
466 // of its active ancestor.
467 if (mesh.is_serial())
468 {
469 for (auto & elem : mesh.active_element_ptr_range())
470 {
471 // First set descendents
472 std::vector<Elem *> subactive_family;
473 elem->total_family_tree(subactive_family);
474 for (const auto & f : subactive_family)
475 f->processor_id() = elem->processor_id();
476
477 // Then set ancestors
478 Elem * parent = elem->parent();
479
480 while (parent)
481 {
482 // invalidate the parent id, otherwise the min below
483 // will not work if the current parent id is less
484 // than all the children!
485 parent->invalidate_processor_id();
486
487 for (auto & child : parent->child_ref_range())
488 {
489 libmesh_assert(!child.is_remote());
490 libmesh_assert_not_equal_to (child.processor_id(), DofObject::invalid_processor_id);
491 parent->processor_id() = std::min(parent->processor_id(),
492 child.processor_id());
493 }
494 parent = parent->parent();
495 }
496 }
497 }
498
499 // When the mesh is parallel we cannot guarantee that parents have access to
500 // all their children.
501 else
502 {
503 // Setting subactive processor ids is easy: we can guarantee
504 // that children have access to all their parents.
505
506 // Loop over all the active elements in the mesh
507 for (auto & child : mesh.active_element_ptr_range())
508 {
509 std::vector<Elem *> subactive_family;
510 child->total_family_tree(subactive_family);
511 for (const auto & f : subactive_family)
512 f->processor_id() = child->processor_id();
513 }
514
515 // When the mesh is parallel we cannot guarantee that parents have access to
516 // all their children.
517
518 // We will use a brute-force approach here. Each processor finds its parent
519 // elements and sets the parent pid to the minimum of its
520 // semilocal descendants.
521 // A global reduction is then performed to make sure the true minimum is found.
522 // As noted, this is required because we cannot guarantee that a parent has
523 // access to all its children on any single processor.
524 libmesh_parallel_only(mesh.comm());
525 libmesh_assert(MeshTools::n_elem(mesh.unpartitioned_elements_begin(),
526 mesh.unpartitioned_elements_end()) == 0);
527
528 const dof_id_type max_elem_id = mesh.max_elem_id();
529
530 std::vector<processor_id_type>
531 parent_processor_ids (std::min(communication_blocksize,
532 max_elem_id));
533
534 for (dof_id_type blk=0, last_elem_id=0; last_elem_id<max_elem_id; blk++)
535 {
536 last_elem_id =
537 std::min(static_cast<dof_id_type>((blk+1)*communication_blocksize),
538 max_elem_id);
539 const dof_id_type first_elem_id = blk*communication_blocksize;
540
541 std::fill (parent_processor_ids.begin(),
542 parent_processor_ids.end(),
544
545 // first build up local contributions to parent_processor_ids
546 bool have_parent_in_block = false;
547
548 for (auto & parent : as_range(mesh.ancestor_elements_begin(),
549 mesh.ancestor_elements_end()))
550 {
551 const dof_id_type parent_idx = parent->id();
552 libmesh_assert_less (parent_idx, max_elem_id);
553
554 if ((parent_idx >= first_elem_id) &&
555 (parent_idx < last_elem_id))
556 {
557 have_parent_in_block = true;
559
560 std::vector<const Elem *> active_family;
561 parent->active_family_tree(active_family);
562 for (const auto & f : active_family)
563 parent_pid = std::min (parent_pid, f->processor_id());
564
565 const dof_id_type packed_idx = parent_idx - first_elem_id;
566 libmesh_assert_less (packed_idx, parent_processor_ids.size());
567
568 parent_processor_ids[packed_idx] = parent_pid;
569 }
570 }
571
572 // then find the global minimum
573 mesh.comm().min (parent_processor_ids);
574
575 // and assign the ids, if we have a parent in this block.
576 if (have_parent_in_block)
577 for (auto & parent : as_range(mesh.ancestor_elements_begin(),
578 mesh.ancestor_elements_end()))
579 {
580 const dof_id_type parent_idx = parent->id();
581
582 if ((parent_idx >= first_elem_id) &&
583 (parent_idx < last_elem_id))
584 {
585 const dof_id_type packed_idx = parent_idx - first_elem_id;
586 libmesh_assert_less (packed_idx, parent_processor_ids.size());
587
588 const processor_id_type parent_pid =
589 parent_processor_ids[packed_idx];
590
591 libmesh_assert_not_equal_to (parent_pid, DofObject::invalid_processor_id);
592
593 parent->processor_id() = parent_pid;
594 }
595 }
596 }
597 }
598
599#endif // LIBMESH_ENABLE_AMR
600}
void min(const T &r, T &o, Request &req) const
void invalidate_processor_id()
Sets the processor id to invalid_processor_id.
Definition dof_object.h:785
const Elem * parent() const
Definition elem.h:3168
void total_family_tree(std::vector< const Elem * > &family, bool reset=true) const
Same as the family_tree() member, but also adds any subactive descendants.
Definition elem.C:2228
void active_family_tree(std::vector< const Elem * > &active_family, bool reset=true) const
Same as the family_tree() member, but only adds the active children.
Definition elem.C:2244
virtual dof_id_type max_elem_id() const =0
static const dof_id_type communication_blocksize
The blocksize to use when doing blocked parallel communication.

References libMesh::as_range(), libMesh::Elem::child_ref_range(), libMesh::ParallelObject::comm(), libMesh::Partitioner::communication_blocksize, libMesh::DofObject::invalid_processor_id, libMesh::DofObject::invalidate_processor_id(), libMesh::MeshBase::is_serial(), libMesh::libmesh_assert(), libMesh::libmesh_ignore(), libMesh::MeshBase::max_elem_id(), mesh, libMesh::Parallel::Communicator::min(), libMesh::MeshTools::n_elem(), libMesh::Elem::parent(), libMesh::DofObject::processor_id(), and libMesh::Elem::total_family_tree().

Referenced by libMesh::Partitioner::partition(), and libMesh::Partitioner::repartition().

◆ single_partition()

bool libMesh::Partitioner::single_partition ( MeshBase &  mesh)
protectedinherited

Trivially "partitions" the mesh for one processor.

Simply loops through the elements and assigns all of them to processor 0. Is is provided as a separate function so that derived classes may use it without reimplementing it.

Returns true iff any processor id was changed.

Definition at line 306 of file partitioner.C.

307{
308 bool changed_pid =
309 this->single_partition_range(mesh.elements_begin(),
310 mesh.elements_end());
311
312 // If we have a distributed mesh with an empty rank (or where rank
313 // 0 has only its own component of a disconnected mesh, I guess),
314 // that rank might need to be informed of a change.
315 mesh.comm().max(changed_pid);
316
317 // We may need to redistribute, in case someone (like our unit
318 // tests) is doing something silly (like moving a whole
319 // already-distributed mesh back onto rank 0).
320 if (changed_pid)
322
323 return changed_pid;
324}
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...

References libMesh::ParallelObject::comm(), libMesh::Parallel::Communicator::max(), mesh, libMesh::MeshBase::redistribute(), and libMesh::Partitioner::single_partition_range().

Referenced by libMesh::SubdomainPartitioner::_do_partition(), libMesh::Partitioner::partition(), and libMesh::Partitioner::repartition().

◆ single_partition_range()

bool libMesh::Partitioner::single_partition_range ( MeshBase::element_iterator  it,
MeshBase::element_iterator  end 
)
protectedinherited

Slightly generalized version of single_partition which acts on a range of elements defined by the pair of iterators (it, end).

Returns true iff any processor id was changed.

Definition at line 328 of file partitioner.C.

330{
331 LOG_SCOPE("single_partition_range()", "Partitioner");
332
333 bool changed_pid = false;
334
335 for (auto & elem : as_range(it, end))
336 {
337 if (elem->processor_id())
338 changed_pid = true;
339 elem->processor_id() = 0;
340
341 // Assign all this element's nodes to processor 0 as well.
342 for (Node & node : elem->node_ref_range())
343 {
344 if (node.processor_id())
345 changed_pid = true;
346 node.processor_id() = 0;
347 }
348 }
349
350 return changed_pid;
351}

References libMesh::as_range(), and libMesh::DofObject::processor_id().

Referenced by NonManifoldTestPartitioner::_do_partition(), OverlappingTestPartitioner::_do_partition(), libMesh::CentroidPartitioner::partition_range(), libMesh::LinearPartitioner::partition_range(), libMesh::MappedSubdomainPartitioner::partition_range(), libMesh::MetisPartitioner::partition_range(), libMesh::SFCPartitioner::partition_range(), and libMesh::Partitioner::single_partition().

◆ type()

PartitionerType libMesh::Partitioner::type ( ) const
virtualinherited

Member Data Documentation

◆ _dual_graph

std::vector<std::vector<dof_id_type> > libMesh::Partitioner::_dual_graph
protectedinherited

A dual graph corresponds to the mesh, and it is typically used in paritioner.

A vertex represents an element, and its neighbors are the element neighbors.

Definition at line 332 of file partitioner.h.

Referenced by libMesh::Partitioner::build_graph().

◆ _global_index_by_pid_map

std::unordered_map<dof_id_type, dof_id_type> libMesh::Partitioner::_global_index_by_pid_map
protectedinherited

Maps active element ids into a contiguous range, as needed by parallel partitioner.

Definition at line 316 of file partitioner.h.

Referenced by libMesh::Partitioner::_find_global_index_by_pid_map(), libMesh::Partitioner::assign_partitioning(), and libMesh::Partitioner::build_graph().

◆ _local_id_to_elem

std::vector<Elem *> libMesh::Partitioner::_local_id_to_elem
protectedinherited

Definition at line 335 of file partitioner.h.

Referenced by libMesh::Partitioner::build_graph().

◆ _n_active_elem_on_proc

std::vector<dof_id_type> libMesh::Partitioner::_n_active_elem_on_proc
protectedinherited

The number of active elements on each processor.

Note
ParMETIS requires that each processor have some active elements; it will abort if any processor passes a nullptr _part array.

Definition at line 325 of file partitioner.h.

Referenced by libMesh::Partitioner::_find_global_index_by_pid_map(), libMesh::Partitioner::assign_partitioning(), and libMesh::Partitioner::build_graph().

◆ _weights

ErrorVector* libMesh::Partitioner::_weights
protectedinherited

The weights that might be used for partitioning.

Definition at line 311 of file partitioner.h.

Referenced by libMesh::MetisPartitioner::attach_weights(), and libMesh::MetisPartitioner::partition_range().

◆ communication_blocksize

const dof_id_type libMesh::Partitioner::communication_blocksize
staticprotectedinherited
Initial value:
=
dof_id_type(1000000)

The blocksize to use when doing blocked parallel communication.

This limits the maximum vector size which can be used in a single communication step.

Definition at line 288 of file partitioner.h.

Referenced by libMesh::Partitioner::set_parent_processor_ids().


The documentation for this class was generated from the following file: