preCICE v3.2.0
Loading...
Searching...
No Matches
ReceivedPartition.cpp
Go to the documentation of this file.
2#include <algorithm>
3#include <map>
4#include <memory>
5#include <ostream>
6#include <utility>
7#include <vector>
8
10#include "com/Extra.hpp"
11#include "com/SharedPointer.hpp"
12#include "logging/LogMacros.hpp"
13#include "m2n/M2N.hpp"
14#include "mapping/Mapping.hpp"
16#include "mesh/BoundingBox.hpp"
17#include "mesh/Filter.hpp"
18#include "mesh/Mesh.hpp"
19#include "mesh/Utils.hpp"
20#include "mesh/Vertex.hpp"
23#include "profiling/Event.hpp"
24#include "utils/IntraComm.hpp"
25#include "utils/algorithm.hpp"
26#include "utils/assertion.hpp"
27#include "utils/fmt.hpp"
28
30
31namespace precice::partition {
32
34 const mesh::PtrMesh &mesh, GeometricFilter geometricFilter, double safetyFactor, bool allowDirectAccess)
35 : Partition(mesh),
36 _geometricFilter(geometricFilter),
37 _bb(mesh->getDimensions()),
38 _dimensions(mesh->getDimensions()),
39 _safetyFactor(safetyFactor),
40 _allowDirectAccess(allowDirectAccess)
41{
42}
43
45{
47 PRECICE_ASSERT(_mesh->empty());
48
49 // for two-level initialization, receive mesh partitions
50 if (m2n().usesTwoLevelInitialization()) {
51 PRECICE_INFO("Receive mesh partitions for mesh {}", _mesh->getName());
52 Event e("partition.receiveMeshPartitions." + _mesh->getName(), profiling::Synchronize);
53
55 // Primary rank receives remote mesh's global number of vertices
56 int globalNumberOfVertices = -1;
57 m2n().getPrimaryRankCommunication()->receive(globalNumberOfVertices, 0);
58 _mesh->setGlobalNumberOfVertices(globalNumberOfVertices);
59 }
60
61 // each rank receives max/min global vertex indices from connected remote ranks
64 // each rank receives mesh partition from connected remote ranks
66
67 } else {
68 // for one-level initialization receive complete mesh on primary rank
69 PRECICE_INFO("Receive global mesh {}", _mesh->getName());
70 Event e("partition.receiveGlobalMesh." + _mesh->getName(), profiling::Synchronize);
71
73 // a ReceivedPartition can only have one communication, @todo nicer design
74 com::receiveMesh(*(m2n().getPrimaryRankCommunication()), 0, *_mesh);
75 _mesh->setGlobalNumberOfVertices(_mesh->nVertices());
76 }
77 }
78
79 // for both initialization concepts broadcast and set the global number of vertices
81 utils::IntraComm::getCommunication()->broadcast(_mesh->getGlobalNumberOfVertices());
82 }
84 int globalNumberOfVertices = -1;
85 utils::IntraComm::getCommunication()->broadcast(globalNumberOfVertices, 0);
86 PRECICE_ASSERT(globalNumberOfVertices >= 0);
87 _mesh->setGlobalNumberOfVertices(globalNumberOfVertices);
88 }
89
90 PRECICE_ASSERT(_mesh->getGlobalNumberOfVertices() >= 0);
91}
92
94{
96
97 // handle coupling mode first (i.e. serial participant)
99 PRECICE_DEBUG("Handle partition data structures for serial participant");
100
101 if (_allowDirectAccess) {
102 // Prepare the bounding boxes
104
105 // To discuss: maybe check this somewhere in the ParticipantImpl, as we have now a similar check for the parallel case
106 PRECICE_CHECK(!_bb.empty(), "You are running this participant in serial mode and the bounding box on mesh \"{}\", is empty. Did you call setMeshAccessRegion with valid data?", _mesh->getName());
107
108 // In serial mode, we keep the vertices, but filter them in the API functions
109 const auto nVerticesInBox = mesh::countVerticesInBoundingBox(_mesh, _bb);
110 unsigned int nFilteredVertices = nVerticesInBox - _mesh->nVertices();
111
112 PRECICE_WARN_IF(nFilteredVertices > 0,
113 "{} vertices on mesh \"{}\" have been filtered out due to the defined bounding box in \"setMeshAccessRegion\" "
114 "in serial mode. For direct-mesh access via \"getMeshVertexCoordinatesAndIDs()\", these vertices are not accessible. "
115 "Their data values will internally be filled with zero values in order to provide valid data for other participants when reading data.",
116 nFilteredVertices, _mesh->getName());
117 }
118
119 mesh::Mesh::VertexDistribution vertexDistribution;
120 int vertexCounter = 0;
121 for (mesh::Vertex &v : _mesh->vertices()) {
122 v.setOwner(true);
123 vertexDistribution[0].push_back(vertexCounter);
124 vertexCounter++;
125 }
126 PRECICE_ASSERT(_mesh->getVertexDistribution().empty());
127 _mesh->setVertexDistribution(std::move(vertexDistribution));
128 PRECICE_ASSERT(_mesh->getVertexOffsets().empty());
129 _mesh->setVertexOffsets({vertexCounter});
130 return;
131 }
132
133 // check to prevent false configuration
136 "The received mesh {} needs a mapping (either from it, to it, or both) or API access enabled (api-access=\"true\"). Maybe you don't want to receive this mesh at all?",
137 _mesh->getName());
138 }
139
140 // To better understand steps (2) to (5), it is recommended to look at BU's thesis, especially Figure 69 on page 89
141 // for RBF-based filtering. https://mediatum.ub.tum.de/doc/1320661/document.pdf
142
143 // (1) Bounding-Box-Filter
145
146 // (2) Tag vertices 1st round (i.e. who could be owned by this rank)
147 PRECICE_DEBUG("Tag vertices for filtering: 1st round.");
148 // go to both meshes, vertex is tagged if already one mesh tags him
150
151 // (3) Define which vertices are owned by this rank
152 PRECICE_DEBUG("Create owner information.");
154
155 // (4) Tag vertices 2nd round (what should be filtered out)
156 PRECICE_DEBUG("Tag vertices for filtering: 2nd round.");
158
159 // (5) Filter mesh according to tag
160 PRECICE_INFO("Filter mesh {} by mappings", _mesh->getName());
161 Event e5("partition.filterMeshMappings" + _mesh->getName(), profiling::Synchronize);
162 mesh::Mesh filteredMesh("FilteredMesh", _dimensions, mesh::Mesh::MESH_ID_UNDEFINED);
163 mesh::filterMesh(filteredMesh, *_mesh, [&](const mesh::Vertex &v) { return v.isTagged(); });
164 PRECICE_DEBUG("Mapping filter, filtered from {} to {} vertices, {} to {} edges, and {} to {} triangles.",
165 _mesh->nVertices(), filteredMesh.nVertices(),
166 _mesh->edges().size(), filteredMesh.edges().size(),
167 _mesh->triangles().size(), filteredMesh.triangles().size());
168
169 _mesh->clear();
170 _mesh->addMesh(filteredMesh);
171 e5.stop();
172
173 // (6) Compute vertex distribution or local communication map
174 if (m2n().usesTwoLevelInitialization()) {
175
176 PRECICE_INFO("Compute communication map for mesh {}", _mesh->getName());
177 Event e6("partition.computeCommunicationMap." + _mesh->getName(), profiling::Synchronize);
178
179 // Fill two data structures: remoteCommunicationMap and this rank's communication map (_mesh->getCommunicationMap()).
180 // remoteCommunicationMap: connectedRank -> {remote local vertex index}
181 // _mesh->getCommunicationMap(): connectedRank -> {this rank's local vertex index}
182 // A vertex belongs to a specific connected rank if its global vertex ID lies within the ranks min and max.
183 mesh::Mesh::CommunicationMap remoteCommunicationMap;
184
185 for (size_t vertexIndex = 0; vertexIndex < _mesh->nVertices(); ++vertexIndex) {
186 for (size_t rankIndex = 0; rankIndex < _mesh->getConnectedRanks().size(); ++rankIndex) {
187 int globalVertexIndex = _mesh->vertex(vertexIndex).getGlobalIndex();
188 if (globalVertexIndex <= _remoteMaxGlobalVertexIDs[rankIndex] && globalVertexIndex >= _remoteMinGlobalVertexIDs[rankIndex]) {
189 int remoteRank = _mesh->getConnectedRanks()[rankIndex];
190 remoteCommunicationMap[remoteRank].push_back(globalVertexIndex - _remoteMinGlobalVertexIDs[rankIndex]); // remote local vertex index
191 _mesh->getCommunicationMap()[remoteRank].push_back(vertexIndex); // this rank's local vertex index
192 }
193 }
194 }
195
196 // communicate remote communication map to all remote connected ranks
197 m2n().scatterAllCommunicationMap(remoteCommunicationMap, *_mesh);
198
199 } else {
200
201 PRECICE_INFO("Feedback distribution for mesh {}", _mesh->getName());
202 Event e6("partition.feedbackMesh." + _mesh->getName(), profiling::Synchronize);
204 int numberOfVertices = _mesh->nVertices();
205 std::vector<VertexID> vertexIDs(numberOfVertices, -1);
206 for (int i = 0; i < numberOfVertices; i++) {
207 vertexIDs[i] = _mesh->vertex(i).getGlobalIndex();
208 }
209 PRECICE_DEBUG("Send partition feedback to primary rank");
210 utils::IntraComm::getCommunication()->sendRange(vertexIDs, 0);
211 } else { // Primary
212 mesh::Mesh::VertexDistribution vertexDistribution;
213 int numberOfVertices = _mesh->nVertices();
214 std::vector<VertexID> vertexIDs(numberOfVertices, -1);
215 for (int i = 0; i < numberOfVertices; i++) {
216 vertexIDs[i] = _mesh->vertex(i).getGlobalIndex();
217 }
218 vertexDistribution[0] = std::move(vertexIDs);
219
220 for (int secondaryRank : utils::IntraComm::allSecondaryRanks()) {
221 PRECICE_DEBUG("Receive partition feedback from the secondary rank {}", secondaryRank);
222 vertexDistribution[secondaryRank] = utils::IntraComm::getCommunication()->receiveRange(secondaryRank, com::asVector<VertexID>);
223 }
224 PRECICE_ASSERT(_mesh->getVertexDistribution().empty());
225 _mesh->setVertexDistribution(std::move(vertexDistribution));
226 }
227 }
228
229 // (7) Compute vertex offsets
230 PRECICE_DEBUG("Compute vertex offsets");
232
233 // send number of vertices
234 PRECICE_DEBUG("Send number of vertices: {}", _mesh->nVertices());
235 int numberOfVertices = _mesh->nVertices();
236 utils::IntraComm::getCommunication()->send(numberOfVertices, 0);
237
238 // receive vertex offsets
239 mesh::Mesh::VertexOffsets vertexOffsets;
240 utils::IntraComm::getCommunication()->broadcast(vertexOffsets, 0);
241 PRECICE_DEBUG("My vertex offsets: {}", vertexOffsets);
242 PRECICE_ASSERT(_mesh->getVertexOffsets().empty());
243 _mesh->setVertexOffsets(std::move(vertexOffsets));
244
245 } else if (utils::IntraComm::isPrimary()) {
246
248 vertexOffsets[0] = _mesh->nVertices();
249
250 // receive number of secondary vertices and fill vertex offsets
251 for (int secondaryRank : utils::IntraComm::allSecondaryRanks()) {
252 int numberOfSecondaryRankVertices = -1;
253 utils::IntraComm::getCommunication()->receive(numberOfSecondaryRankVertices, secondaryRank);
254 PRECICE_ASSERT(numberOfSecondaryRankVertices >= 0);
255 vertexOffsets[secondaryRank] = numberOfSecondaryRankVertices + vertexOffsets[secondaryRank - 1];
256 }
257
258 // broadcast vertex offsets
259 PRECICE_DEBUG("My vertex offsets: {}", vertexOffsets);
260 utils::IntraComm::getCommunication()->broadcast(vertexOffsets);
261 PRECICE_ASSERT(_mesh->getVertexOffsets().empty());
262 _mesh->setVertexOffsets(std::move(vertexOffsets));
263 }
264
265 PRECICE_ASSERT(!_mesh->getVertexOffsets().empty());
266 if (!m2n().usesTwoLevelInitialization() && utils::IntraComm::isPrimary()) {
267 PRECICE_ASSERT(!_mesh->getVertexDistribution().empty());
268 }
269}
270
271namespace {
272auto errorMeshFilteredOut(const std::string &meshName, const int rank)
273{
274 return fmt::format("The re-partitioning completely filtered out the mesh \"{0}\" received on rank {1} "
275 "at the coupling interface, although the provided mesh partition on this rank is "
276 "non-empty. Most probably, the coupling interfaces of your coupled participants do "
277 "not match geometry-wise. Please check your geometry setup again. Small overlaps or "
278 "gaps are no problem. If your geometry setup is correct and if you have very different "
279 "mesh resolutions on both sides, you may want to increase the safety-factor: "
280 "\"<receive-mesh mesh=\"{0} \" ... safety-factor=\"N\"/> (default value is 0.5) of the "
281 "decomposition strategy or disable the filtering completely: "
282 "\"<receive-mesh mesh=\"{0}\" ... geometric-filter=\"no-filter\" />",
283 meshName, rank);
284}
285} // namespace
286
288{
289 PRECICE_TRACE(static_cast<int>(_geometricFilter));
290
291 if (m2n().usesTwoLevelInitialization()) {
292 std::string msg = "The received mesh " + _mesh->getName() +
293 " cannot solely be filtered on the primary rank "
294 "(option \"filter-on-primary-rank\") if it is communicated by an m2n communication that uses "
295 "two-level initialization. Use \"filter-on-secondary-rank\" or \"no-filter\" instead.";
297 }
298
300
301 if (_geometricFilter == ON_PRIMARY_RANK) { // filter on primary rank and communicate reduced mesh then
302
303 PRECICE_ASSERT(not m2n().usesTwoLevelInitialization());
304 PRECICE_INFO("Pre-filter mesh {} by bounding box on primary rank", _mesh->getName());
305 Event e("partition.preFilterMesh." + _mesh->getName(), profiling::Synchronize);
306
308 PRECICE_DEBUG("Send bounding box to primary rank");
310 PRECICE_DEBUG("Receive filtered mesh");
312
314 PRECICE_CHECK(not _mesh->empty(), errorMeshFilteredOut(_mesh->getName(), utils::IntraComm::getRank()));
315 }
316
317 } else { // Primary
320
321 for (int secondaryRank : utils::IntraComm::allSecondaryRanks()) {
322 mesh::BoundingBox secondaryBB(_bb.getDimension());
323 com::receiveBoundingBox(*utils::IntraComm::getCommunication(), secondaryRank, secondaryBB);
324
325 PRECICE_DEBUG("From secondary rank {}, bounding mesh: {}", secondaryRank, secondaryBB);
326 mesh::Mesh secondaryMesh("SecondaryMesh", _dimensions, mesh::Mesh::MESH_ID_UNDEFINED);
327 mesh::filterMesh(secondaryMesh, *_mesh, [&secondaryBB](const mesh::Vertex &v) { return secondaryBB.contains(v); });
328 PRECICE_DEBUG("Send filtered mesh to secondary rank: {}", secondaryRank);
329 com::sendMesh(*utils::IntraComm::getCommunication(), secondaryRank, secondaryMesh);
330 }
331
332 // Now also filter the remaining primary mesh
333 mesh::Mesh filteredMesh("FilteredMesh", _dimensions, mesh::Mesh::MESH_ID_UNDEFINED);
334 mesh::filterMesh(filteredMesh, *_mesh, [&](const mesh::Vertex &v) { return _bb.contains(v); });
335 PRECICE_DEBUG("Primary rank mesh, filtered from {} to {} vertices, {} to {} edges, and {} to {} triangles.",
336 _mesh->nVertices(), filteredMesh.nVertices(),
337 _mesh->edges().size(), filteredMesh.edges().size(),
338 _mesh->triangles().size(), filteredMesh.triangles().size());
339 _mesh->clear();
340 _mesh->addMesh(filteredMesh);
341
343 PRECICE_CHECK(not _mesh->empty(), errorMeshFilteredOut(_mesh->getName(), utils::IntraComm::getRank()));
344 }
345 }
346 } else {
347 if (not m2n().usesTwoLevelInitialization()) {
348 PRECICE_INFO("Broadcast mesh {}", _mesh->getName());
349 Event e("partition.broadcastMesh." + _mesh->getName(), profiling::Synchronize);
350
353 } else { // Primary
356 }
357 }
359
360 PRECICE_INFO("Filter mesh {} by bounding box on secondary ranks", _mesh->getName());
361 Event e("partition.filterMeshBB." + _mesh->getName(), profiling::Synchronize);
362
363 mesh::Mesh filteredMesh("FilteredMesh", _dimensions, mesh::Mesh::MESH_ID_UNDEFINED);
364 mesh::filterMesh(filteredMesh, *_mesh, [&](const mesh::Vertex &v) { return _bb.contains(v); });
365
366 PRECICE_DEBUG("Bounding box filter, filtered from {} to {} vertices, {} to {} edges, and {} to {} triangles.",
367 _mesh->nVertices(), filteredMesh.nVertices(),
368 _mesh->edges().size(), filteredMesh.edges().size(),
369 _mesh->triangles().size(), filteredMesh.triangles().size());
370
371 _mesh->clear();
372 _mesh->addMesh(filteredMesh);
374 PRECICE_CHECK(not _mesh->empty(), errorMeshFilteredOut(_mesh->getName(), utils::IntraComm::getRank()));
375 }
376 } else {
378 }
379 }
380}
381
383{
385
386 _mesh->clear();
387 _mesh->clearPartitioning();
388 _boundingBoxPrepared = false;
391
392 // @todo handle coupling mode (i.e. serial participant)
393 // @todo treatment of multiple m2ns
394 PRECICE_ASSERT(_m2ns.size() == 1);
395 if (not m2n().usesTwoLevelInitialization())
396 return;
397
398 // receive and broadcast number of remote ranks
399 int numberOfRemoteRanks = -1;
401 m2n().getPrimaryRankCommunication()->receive(numberOfRemoteRanks, 0);
402 utils::IntraComm::getCommunication()->broadcast(numberOfRemoteRanks);
403 } else {
405 utils::IntraComm::getCommunication()->broadcast(numberOfRemoteRanks, 0);
406 }
407
408 // define and initialize remote bounding box map
409 mesh::Mesh::BoundingBoxMap remoteBBMap;
410 mesh::BoundingBox initialBB(_mesh->getDimensions());
411
412 for (int remoteRank = 0; remoteRank < numberOfRemoteRanks; remoteRank++) {
413 remoteBBMap.emplace(remoteRank, initialBB);
414 }
415
416 // receive and broadcast remote bounding box map
418 com::receiveBoundingBoxMap(*m2n().getPrimaryRankCommunication(), 0, remoteBBMap);
420 } else {
423 }
424
425 // prepare local bounding box
427
428 if (utils::IntraComm::isPrimary()) { // Primary
429 mesh::Mesh::CommunicationMap connectionMap; // local ranks -> {remote ranks}
430 std::vector<Rank> connectedRanksList; // local ranks with any connection
431
432 // connected ranks for primary rank
433 std::vector<Rank> connectedRanks;
434 for (auto &remoteBB : remoteBBMap) {
435 if (_bb.overlapping(remoteBB.second)) {
436 connectedRanks.push_back(remoteBB.first); // connected remote ranks for this rank
437 }
438 }
439 PRECICE_ASSERT(_mesh->getConnectedRanks().empty());
440 _mesh->setConnectedRanks(connectedRanks);
441 if (not connectedRanks.empty()) {
442 connectionMap.emplace(0, std::move(connectedRanks));
443 connectedRanksList.push_back(0);
444 }
445
446 // receive connected ranks from secondary ranks and add them to the connection map
447 for (int rank : utils::IntraComm::allSecondaryRanks()) {
448 std::vector<Rank> secondaryConnectedRanks = utils::IntraComm::getCommunication()->receiveRange(rank, com::asVector<Rank>);
449 if (!secondaryConnectedRanks.empty()) {
450 connectedRanksList.push_back(rank);
451 connectionMap.emplace(rank, std::move(secondaryConnectedRanks));
452 }
453 }
454
455 // send connectionMap to other primary rank
456 m2n().getPrimaryRankCommunication()->sendRange(connectedRanksList, 0);
457 PRECICE_CHECK(not connectionMap.empty(),
458 "The mesh \"{}\" of this participant seems to have no partitions at the coupling interface. "
459 "Check that both mapped meshes are describing the same geometry. "
460 "If you deal with very different mesh resolutions, consider increasing the safety-factor in the <receive-mesh /> tag.",
461 _mesh->getName());
462 com::sendConnectionMap(*m2n().getPrimaryRankCommunication(), 0, connectionMap);
463 } else {
465
466 std::vector<Rank> connectedRanks;
467 for (const auto &remoteBB : remoteBBMap) {
468 if (_bb.overlapping(remoteBB.second)) {
469 connectedRanks.push_back(remoteBB.first);
470 }
471 }
472 PRECICE_ASSERT(_mesh->getConnectedRanks().empty());
473 _mesh->setConnectedRanks(connectedRanks);
474
475 // send connected ranks to primary rank
476 utils::IntraComm::getCommunication()->sendRange(connectedRanks, 0);
477 }
478}
479
481{
483
485 return;
486
487 PRECICE_DEBUG("Merge bounding boxes and increase by safety factor");
488
489 // Reset the BoundingBox
491
492 // Create BB around all "other" meshes, where others are local meshes in parallel runs
493 // For just-in-time mapping, we enter the loops here for the (just-in-time) mapping we hold,
494 // however, any bounding box operation will be NOP because bounding boxes around local
495 // meshes are empty
496 for (mapping::PtrMapping &fromMapping : _fromMappings) {
497 auto other_bb = fromMapping->getOutputMesh()->getBoundingBox();
498 _bb.expandBy(other_bb);
499 _bb.scaleBy(_safetyFactor);
501 }
502 for (mapping::PtrMapping &toMapping : _toMappings) {
503 auto other_bb = toMapping->getInputMesh()->getBoundingBox();
504 _bb.expandBy(other_bb);
505 _bb.scaleBy(_safetyFactor);
507 }
508
509 // Expand by user-defined bounding box in case a direct access is desired
510 if (_allowDirectAccess) {
511 auto &other_bb = _mesh->getBoundingBox();
512 _bb.expandBy(other_bb);
513
514 // In case we have an just-in-time mapping associated to this direct access
515 // we need to extend the bounding box for accuracy reasons
516 // the behavior is then comparable to a conventional mapping
517 if (std::any_of(_fromMappings.begin(), _fromMappings.end(), [](auto m) { return m->isJustInTimeMapping(); }) ||
518 std::any_of(_toMappings.begin(), _toMappings.end(), [](auto m) { return m->isJustInTimeMapping(); })) {
519 // The (preliminary) repartitioning is based on the _bb
520 // we extend the _bb here and later on enable the (just-in-time) mappings
521 // to apply any kind of tagging to account for the halo layer added here
522 _bb.scaleBy(_safetyFactor);
523 }
524 // The safety factor is for mapping based partitionings applied, as usual.
525 // For the direct access, however, we don't apply any safety factor scaling.
526 // If the user defines a safety factor and the partitioning is entirely based
527 // on the defined access region (setMeshAccessRegion), we raise a warning
528 // to inform the user
529 // hasAnyMapping is true for just-in-time mappinges
530 const float defaultSafetyFactor = 0.5;
532 utils::IntraComm::isPrimary() && !hasAnyMapping() && (_safetyFactor != defaultSafetyFactor),
533 "The received mesh \"{}\" was entirely partitioned based on the defined access region "
534 "(setMeshAccessRegion) and a safety-factor was defined. However, the safety factor "
535 "will be ignored in this case. You may want to modify the access region by modifying "
536 "the specified region in the function itself.",
537 _mesh->getName());
539 }
540}
541
543{
545 Event e("partition.createOwnerInformation." + _mesh->getName(), profiling::Synchronize);
546
547 /*
548 We follow different approaches for two-level and one-level methods. For 1LI, a centeralized
549 approach is followed, while the 2LI employs a local/parallel scheme for assigning vertices
550 to corresponding ranks.
551 */
552
553 if (m2n().usesTwoLevelInitialization()) {
554 /*
555 This function ensures that each vertex is owned by only a single rank and
556 is not shared among ranks. Initially, the vertices are checked against the
557 bounding box of each rank. If a vertex fits into only a single bounding box,
558 the vertex is assigned to that rank. If it fits to various bbs, the rank with the
559 lowest number of vertices gets ownership to keep the load as balanced as
560 possible.
561
562 Following steps are taken:
563
564 1- receive local bb map from primary rank
565 2- filter bb map to keep the connected ranks
566 3- own the vertices that only fit into this rank's bb
567 4- send number of owned vertices and the list of shared vertices to neighbors
568 5- for the remaining vertices: check if we have less vertices -> own it!
569 */
570
571 // #1: receive local bb map from primary rank
572 // Define and initialize localBBMap to save local bbs
573
575 for (Rank rank = 0; rank < utils::IntraComm::getSize(); rank++) {
576 localBBMap.emplace(rank, mesh::BoundingBox(_dimensions));
577 }
578
579 // Define a bb map to save the local connected ranks and respective boundingboxes
580 mesh::Mesh::BoundingBoxMap localConnectedBBMap;
581
582 // store global IDs and list of possible shared vertices
583
584 // Global IDs to be saved in:
585 std::vector<VertexID> sharedVerticesGlobalIDs;
586 // Local IDs to be saved in:
587 std::vector<VertexID> sharedVerticesLocalIDs;
588
589 // store possible shared vertices in a map to communicate with neighbors, map: rank -> vertex_global_id
590 mesh::Mesh::CommunicationMap sharedVerticesSendMap;
591
592 // receive list of possible shared vertices from neighboring ranks
593 mesh::Mesh::CommunicationMap sharedVerticesReceiveMap;
594
596
597 // Insert bounding box of primary ranks
598 localBBMap.at(0) = _bb;
599
600 // primary rank receives local bb from each secondary rank
601 for (int secondaryRank = 1; secondaryRank < utils::IntraComm::getSize(); secondaryRank++) {
602 com::receiveBoundingBox(*utils::IntraComm::getCommunication(), secondaryRank, localBBMap.at(secondaryRank));
603 }
604
605 // primary rank broadcast localBBMap to all secondary ranks
607 } else if (utils::IntraComm::isSecondary()) {
608 // secondary ranks send local bb to primary rank
610 // secondary ranks receive localBBMap from primary rank
612 }
613
614 // #2: filter bb map to keep the connected ranks
615 // remove the own bb from the map since we compare the own bb only with other ranks bb.
616 localBBMap.erase(utils::IntraComm::getRank());
617 // find and store local connected ranks
618 for (const auto &localBB : localBBMap) {
619 if (_bb.overlapping(localBB.second)) {
620 localConnectedBBMap.emplace(localBB.first, localBB.second);
621 }
622 }
623
624 // First, insert all comm partnerts, otherwise we end up in a deadlock further down
625 // when exchanging the vector sizes as vertices might be shared from the other
626 // connected rank(s), although the actual size we request here is zero
627 // i.e., we never tell the other ranks that we don't want any vertices
628 // See also test Integration/Parallel/TestBoundingBoxInitializationEmpty
629 for (auto &neighborRank : localConnectedBBMap)
630 sharedVerticesSendMap[neighborRank.first] = std::vector<VertexID>();
631
632 // #3: check vertices and keep only those that fit into the current rank's bb
633 const int numberOfVertices = _mesh->nVertices();
634 PRECICE_DEBUG("Tag vertices, number of vertices {}", numberOfVertices);
635 std::vector<int> tags(numberOfVertices, 1);
636 std::vector<VertexID> globalIDs(numberOfVertices, -1);
637 int ownedVerticesCount = 0; // number of vertices owned by this rank
638 for (int i = 0; i < numberOfVertices; i++) {
639 globalIDs[i] = _mesh->vertex(i).getGlobalIndex();
640 if (_mesh->vertex(i).isTagged()) {
641 bool vertexIsShared = false;
642 for (const auto &neighborRank : localConnectedBBMap) {
643 if (neighborRank.second.contains(_mesh->vertex(i))) {
644 vertexIsShared = true;
645 sharedVerticesSendMap[neighborRank.first].push_back(globalIDs[i]);
646 }
647 }
648
649 if (not vertexIsShared) {
650 tags[i] = 1;
651 ownedVerticesCount++;
652 } else {
653 sharedVerticesGlobalIDs.push_back(globalIDs[i]);
654 sharedVerticesLocalIDs.push_back(i);
655 }
656 } else {
657 tags[i] = 0;
658 }
659 }
660
661 // #4: Exchange number of already owned vertices with the neighbors
662
663 // to store receive requests.
664 std::vector<com::PtrRequest> vertexNumberRequests;
665
666 // Define and initialize for load balancing
667 std::map<int, int> neighborRanksVertexCount;
668 for (auto &neighborRank : localConnectedBBMap) {
669 neighborRanksVertexCount.emplace(neighborRank.first, 0);
670 }
671
672 // Asynchronous receive number of owned vertices from neighbor ranks
673 for (auto &neighborRank : localConnectedBBMap) {
674 auto request = utils::IntraComm::getCommunication()->aReceive(neighborRanksVertexCount.at(neighborRank.first), neighborRank.first);
675 vertexNumberRequests.push_back(request);
676 }
677
678 // Synchronous send number of owned vertices to neighbor ranks
679 for (auto &neighborRank : localConnectedBBMap) {
680 utils::IntraComm::getCommunication()->send(ownedVerticesCount, neighborRank.first);
681 }
682
683 // wait until all aReceives are complete.
684 for (auto &rqst : vertexNumberRequests) {
685 rqst->wait();
686 }
687
688 // Exchange list of shared vertices with the neighbor
689 // to store send requests.
690 std::vector<com::PtrRequest> vertexListRequests;
691
692 for (auto &receivingRank : sharedVerticesSendMap) {
693 int sendSize = receivingRank.second.size();
694 auto request = utils::IntraComm::getCommunication()->aSend(sendSize, receivingRank.first);
695 vertexListRequests.push_back(request);
696 if (sendSize != 0) {
697 auto request = utils::IntraComm::getCommunication()->aSend(span<const int>{receivingRank.second}, receivingRank.first);
698 vertexListRequests.push_back(request);
699 }
700 }
701
702 for (auto &neighborRank : sharedVerticesSendMap) {
703 int receiveSize = 0;
704 utils::IntraComm::getCommunication()->receive(receiveSize, neighborRank.first);
705 if (receiveSize != 0) {
706 std::vector<int> receivedSharedVertices(receiveSize, -1);
707 utils::IntraComm::getCommunication()->receive(span<int>{receivedSharedVertices}, neighborRank.first);
708 sharedVerticesReceiveMap.insert(std::make_pair(neighborRank.first, receivedSharedVertices));
709 }
710 }
711
712 // wait until all aSends are complete.
713 for (auto &rqst : vertexListRequests) {
714 rqst->wait();
715 }
716
717 // #5: Second round assignment according to the number of owned vertices
718 // In case that a vertex can be shared between two ranks, the rank with lower
719 // vertex count will own the vertex.
720 // If both ranks have same vertex count, the lower rank will own the vertex.
721
722 // To do so, we look at all vertices shared with all neighbors
723 for (auto &sharingRank : sharedVerticesReceiveMap) {
724 // First, check if we would change the ownership at all (by default initialization
725 // above, we would be considered as owner of the shared vertices)
726 // If this is fulfilled, we need to set the ownership to false
727 if ((ownedVerticesCount > neighborRanksVertexCount[sharingRank.first]) ||
728 (ownedVerticesCount == neighborRanksVertexCount[sharingRank.first] && utils::IntraComm::getRank() > sharingRank.first)) {
729 // In such a case, we need to find out the tags we need to switch to 'false', as we don't want
730 // to own them any longer. We compute the intersection of all globalIDs this rank shares with
731 // others and the globalIDs of the neighbor rank we are just considering.
732 // The set_intersection_indices gives us the indices in the sharedVerticesGlobalIDs, which we
733 // can use in the sharedVerticesLocalIDs to get the actual index in the 'tags' vector.
735 precice::utils::set_intersection_indices(sharedVerticesGlobalIDs.begin(), sharedVerticesGlobalIDs.begin(), sharedVerticesGlobalIDs.end(),
736 sharingRank.second.begin(), sharingRank.second.end(), std::back_inserter(res));
737 for (auto r : res)
738 tags[sharedVerticesLocalIDs[r]] = static_cast<int>(false);
739 }
740 }
741
743 PRECICE_DEBUG("{} of {} vertices of mesh {} have been filtered out on rank {} since they have no influence on the mapping.",
744 std::count(tags.begin(), tags.end(), 0), tags.size(), _mesh->getName(), utils::IntraComm::getRank());
745 // end of two-level initialization section
746 } else {
748 int numberOfVertices = _mesh->nVertices();
749 utils::IntraComm::getCommunication()->send(numberOfVertices, 0);
750
751 if (numberOfVertices != 0) {
752 PRECICE_DEBUG("Tag vertices, number of vertices {}", numberOfVertices);
753 std::vector<int> tags(numberOfVertices, -1);
754 std::vector<VertexID> globalIDs(numberOfVertices, -1);
755 bool atInterface = false;
756 for (int i = 0; i < numberOfVertices; i++) {
757 globalIDs[i] = _mesh->vertex(i).getGlobalIndex();
758 if (_mesh->vertex(i).isTagged()) {
759 tags[i] = 1;
760 atInterface = true;
761 } else {
762 tags[i] = 0;
763 }
764 }
765 PRECICE_DEBUG("My tags: {}", tags);
766 PRECICE_DEBUG("My global IDs: {}", globalIDs);
767 PRECICE_DEBUG("Send tags and global IDs");
768 utils::IntraComm::getCommunication()->sendRange(tags, 0);
769 utils::IntraComm::getCommunication()->sendRange(globalIDs, 0);
770 utils::IntraComm::getCommunication()->send(atInterface, 0);
771
772 PRECICE_DEBUG("Receive owner information");
774 PRECICE_DEBUG("My owner information: {}", ownerVec);
775 PRECICE_ASSERT(ownerVec.size() == static_cast<std::size_t>(numberOfVertices));
776 setOwnerInformation(ownerVec);
777 }
778 }
779
780 else if (utils::IntraComm::isPrimary()) {
781 // To temporary store which vertices already have an owner
782 std::vector<VertexID> globalOwnerVec(_mesh->getGlobalNumberOfVertices(), 0);
783 // The same per rank
785 // Global IDs per rank
787 // Tag information per rank
789
790 // Fill primary data
791 PRECICE_DEBUG("Tag vertices of primary rank");
792 bool primaryRankAtInterface = false;
793 secondaryOwnerVecs[0].resize(_mesh->nVertices());
794 secondaryGlobalIDs[0].resize(_mesh->nVertices());
795 secondaryTags[0].resize(_mesh->nVertices());
796 for (size_t i = 0; i < _mesh->nVertices(); i++) {
797 secondaryGlobalIDs[0][i] = _mesh->vertex(i).getGlobalIndex();
798 if (_mesh->vertex(i).isTagged()) {
799 primaryRankAtInterface = true;
800 secondaryTags[0][i] = 1;
801 } else {
802 secondaryTags[0][i] = 0;
803 }
804 }
805 PRECICE_DEBUG("My tags: {}", secondaryTags[0]);
806
807 // receive secondary data
808 Rank ranksAtInterface = 0;
809 if (primaryRankAtInterface)
810 ranksAtInterface++;
811
813 int localNumberOfVertices = -1;
814 utils::IntraComm::getCommunication()->receive(localNumberOfVertices, rank);
815 PRECICE_DEBUG("Rank {} has {} vertices.", rank, localNumberOfVertices);
816 secondaryOwnerVecs[rank].resize(localNumberOfVertices, 0);
817
818 if (localNumberOfVertices != 0) {
819 PRECICE_DEBUG("Receive tags from secondary rank {}", rank);
820 secondaryTags[rank] = utils::IntraComm::getCommunication()->receiveRange(rank, com::asVector<int>);
821 secondaryGlobalIDs[rank] = utils::IntraComm::getCommunication()->receiveRange(rank, com::asVector<VertexID>);
822 PRECICE_DEBUG("Rank {} has tags {}", rank, secondaryTags[rank]);
823 PRECICE_DEBUG("Rank {} has global IDs {}", rank, secondaryGlobalIDs[rank]);
824 bool atInterface = false;
825 utils::IntraComm::getCommunication()->receive(atInterface, rank);
826 if (atInterface)
827 ranksAtInterface++;
828 }
829 }
830
831 // Decide upon owners,
832 PRECICE_DEBUG("Decide owners, first round by rough load balancing");
833 // Provide a more descriptive error message if direct access was enabled
834 PRECICE_CHECK(!(ranksAtInterface == 0 && _allowDirectAccess),
835 "After repartitioning of mesh \"{}\" all ranks are empty. "
836 "Please check the dimensions of the provided bounding box "
837 "(in \"setMeshAccessRegion\") and verify that it covers vertices "
838 "in the mesh or check the definition of the provided meshes.",
839 _mesh->getName());
840 PRECICE_ASSERT(ranksAtInterface != 0);
841 int localGuess = _mesh->getGlobalNumberOfVertices() / ranksAtInterface; // Guess for a decent load balancing
842 // First round: every secondary rank gets localGuess vertices
843 for (Rank rank : utils::IntraComm::allRanks()) {
844 int counter = 0;
845 for (size_t i = 0; i < secondaryOwnerVecs[rank].size(); i++) {
846 // Vertex has no owner yet and rank could be owner
847 if (globalOwnerVec[secondaryGlobalIDs[rank][i]] == 0 && secondaryTags[rank][i] == 1) {
848 secondaryOwnerVecs[rank][i] = 1; // Now rank is owner
849 globalOwnerVec[secondaryGlobalIDs[rank][i]] = 1; // Vertex now has owner
850 counter++;
851 if (counter == localGuess)
852 break;
853 }
854 }
855 }
856
857 // Second round: distribute all other vertices in a greedy way
858 PRECICE_DEBUG("Decide owners, second round in greedy way");
859 for (Rank rank : utils::IntraComm::allRanks()) {
860 for (size_t i = 0; i < secondaryOwnerVecs[rank].size(); i++) {
861 if (globalOwnerVec[secondaryGlobalIDs[rank][i]] == 0 && secondaryTags[rank][i] == 1) {
862 secondaryOwnerVecs[rank][i] = 1;
863 globalOwnerVec[secondaryGlobalIDs[rank][i]] = rank + 1;
864 }
865 }
866 }
867
868 // Send information back to secondary ranks
870 if (not secondaryTags[rank].empty()) {
871 PRECICE_DEBUG("Send owner information to secondary rank {}", rank);
872 utils::IntraComm::getCommunication()->sendRange(secondaryOwnerVecs[rank], rank);
873 }
874 }
875 // Primary rank data
876 PRECICE_DEBUG("My owner information: {}", secondaryOwnerVecs[0]);
877 setOwnerInformation(secondaryOwnerVecs[0]);
878
879#ifndef NDEBUG
880 for (size_t i = 0; i < globalOwnerVec.size(); i++) {
881 PRECICE_DEBUG_IF(globalOwnerVec[i] == 0,
882 "The Vertex with global index {} of mesh: {} was completely filtered out, since it has no influence on any mapping.",
883 i, _mesh->getName());
884 }
885#endif
886 auto filteredVertices = std::count(globalOwnerVec.begin(), globalOwnerVec.end(), 0);
887 PRECICE_WARN_IF(filteredVertices,
888 "{} of {} vertices of mesh {} have been filtered out since they have no influence on the mapping.{}",
889 filteredVertices, _mesh->getGlobalNumberOfVertices(), _mesh->getName(),
890 _allowDirectAccess ? " Associated data values of the filtered vertices will be filled with zero values in order to "
891 "provide valid data for other participants when reading data."
892 : "");
893 }
894 }
895}
896
898{
899 for (const auto &fromMapping : _fromMappings) {
900 if (not fromMapping->getOutputMesh()->empty()) {
901 return true;
902 }
903 }
904 for (const auto &toMapping : _toMappings) {
905 if (not toMapping->getInputMesh()->empty()) {
906 return true;
907 }
908 }
909 return false;
910}
911
913{
914 return not(_fromMappings.empty() && _toMappings.empty());
915}
916
918{
919 // We want to have every vertex within user-definded bounding box if we access the mesh directly
920 if (_allowDirectAccess) {
921 // _mesh->getBoundingBox is based on the bounding box of the mesh, which is
922 // - set via the API function (setMeshAccessRegion)
923 // - potentially enlarged with another bounding box if there is another mapping defined
924 // i.e. we have a local mesh involved with a bounding box used to enlarge the original one
925 // concluding: it might be that the boundingBox is not (purely) the one asked for by the user
926 // but using mesh-tagAll() would tag the safety margin. Of course, this only applied for combinations of direct access plus mapping, for pure
927 // direct accesses, there is no safety factor
928 auto userDefinedBB = _mesh->getBoundingBox();
929 for (auto &vertex : _mesh->vertices()) {
930 if (userDefinedBB.contains(vertex)) {
931 vertex.tag();
932 }
933 }
934 }
935
936 for (const mapping::PtrMapping &fromMapping : _fromMappings) {
937 fromMapping->tagMeshFirstRound();
938 }
939 for (const mapping::PtrMapping &toMapping : _toMappings) {
940 toMapping->tagMeshFirstRound();
941 }
942}
943
945{
946 for (const mapping::PtrMapping &fromMapping : _fromMappings) {
947 fromMapping->tagMeshSecondRound();
948 }
949 for (const mapping::PtrMapping &toMapping : _toMappings) {
950 toMapping->tagMeshSecondRound();
951 }
952}
953
955{
956 size_t i = 0;
957 for (mesh::Vertex &vertex : _mesh->vertices()) {
958 PRECICE_ASSERT(i < ownerVec.size());
959 PRECICE_ASSERT(ownerVec[i] != -1);
960 vertex.setOwner(ownerVec[i] == 1);
961 i++;
962 }
963}
964
966{
967 PRECICE_ASSERT(_m2ns.size() == 1);
968 return *_m2ns[0];
969}
970
971} // namespace precice::partition
#define PRECICE_WARN_IF(condition,...)
Definition LogMacros.hpp:18
#define PRECICE_DEBUG(...)
Definition LogMacros.hpp:61
#define PRECICE_DEBUG_IF(condition,...)
Definition LogMacros.hpp:63
#define PRECICE_TRACE(...)
Definition LogMacros.hpp:92
#define PRECICE_INFO(...)
Definition LogMacros.hpp:14
#define PRECICE_CHECK(check,...)
Definition LogMacros.hpp:32
T any_of(T... args)
#define PRECICE_ASSERT(...)
Definition assertion.hpp:85
T at(T... args)
T back_inserter(T... args)
T begin(T... args)
M2N communication class. This layer is necessary since communication between two participants can be ...
Definition M2N.hpp:31
void scatterAllCommunicationMap(std::map< int, std::vector< int > > &localCommunicationMap, mesh::Mesh &mesh)
Scatters a communication map over connected ranks on remote participant (concerning the given mesh)
Definition M2N.cpp:333
void broadcastReceiveAll(std::vector< int > &itemToReceive, mesh::Mesh &mesh)
Receives an int per connected rank on remote participant (concerning the given mesh) @para[out] itemT...
Definition M2N.cpp:424
com::PtrCommunication getPrimaryRankCommunication()
Get the basic communication between the 2 primary ranks.
Definition M2N.cpp:250
void broadcastReceiveAllMesh(mesh::Mesh &mesh)
Receive mesh partitions per connected rank on remote participant (concerning the given mesh)
Definition M2N.cpp:433
An axis-aligned bounding box around a (partition of a) mesh.
bool contains(const Vertex &vertex) const
Checks if vertex in contained in _bb.
Container and creator for meshes.
Definition Mesh.hpp:38
std::map< Rank, std::vector< VertexID > > CommunicationMap
A mapping from remote local ranks to the IDs that must be communicated.
Definition Mesh.hpp:51
static constexpr MeshID MESH_ID_UNDEFINED
Use if the id of the mesh is not necessary.
Definition Mesh.hpp:57
std::map< Rank, std::vector< VertexID > > VertexDistribution
A mapping from rank to used (not necessarily owned) vertex IDs.
Definition Mesh.hpp:48
std::size_t nVertices() const
Returns the number of vertices.
Definition Mesh.cpp:65
std::map< int, BoundingBox > BoundingBoxMap
Definition Mesh.hpp:45
std::vector< int > VertexOffsets
Definition Mesh.hpp:54
TriangleContainer & triangles()
Returns modifiable container holding all triangles.
Definition Mesh.cpp:80
EdgeContainer & edges()
Returns modifiable container holding all edges.
Definition Mesh.cpp:70
Vertex of a mesh.
Definition Vertex.hpp:16
bool isTagged() const
Definition Vertex.cpp:32
std::vector< mapping::PtrMapping > _toMappings
Definition Partition.hpp:66
Partition(mesh::PtrMesh mesh)
Constructor.
Definition Partition.cpp:7
std::vector< mapping::PtrMapping > _fromMappings
Definition Partition.hpp:64
std::vector< m2n::PtrM2N > _m2ns
m2n connection to each connected participant
Definition Partition.hpp:69
void prepareBoundingBox()
Sets _bb to the union with the mesh from fromMapping resp. toMapping, also enlage by _safetyFactor.
ReceivedPartition(const mesh::PtrMesh &mesh, GeometricFilter geometricFilter, double safetyFactor, bool allowDirectAccess=false)
Constructor.
void compute() override
The partition is computed, i.e. the mesh re-partitioned if required and all data structures are set u...
std::vector< int > _remoteMaxGlobalVertexIDs
Max global vertex IDs of remote connected ranks.
void tagMeshFirstRound()
Tag mesh in first round according to all mappings.
std::vector< int > _remoteMinGlobalVertexIDs
Min global vertex IDs of remote connected ranks.
bool hasAnyMapping() const
Returns whether any mapping is defined.
void tagMeshSecondRound()
Tag mesh in second round according to all mappings.
m2n::M2N & m2n()
return the one m2n, a ReceivedPartition can only have one m2n
void compareBoundingBoxes() override
Intersections between bounding boxes around each rank are computed.
bool _boundingBoxPrepared
Is the local other (i.e. provided) bounding box already prepared (i.e. has prepareBoundingBox() been ...
GeometricFilter
Defines the type of geometric filter used.
@ ON_PRIMARY_RANK
Filter at primary rank and communicate only filtered mesh.
@ ON_SECONDARY_RANKS
Filter after communication on all secondary ranks.
@ NO_FILTER
No geometric filter used (e.g. for RBF mappings)
void setOwnerInformation(const std::vector< int > &ownerVec)
Helper function for 'createOwnerFunction' to set local owner information.
void communicate() override
The mesh is communicated between both primary ranks (if required)
void stop()
Stops a running event.
Definition Event.cpp:51
A C++ 11 implementation of the non-owning C++20 std::span type.
Definition span.hpp:284
PRECICE_SPAN_CONSTEXPR11 span< element_type, Count > first() const
Definition span.hpp:416
static int getSize()
Number of ranks. This includes ranks from both participants, e.g. minimal size is 2.
Definition IntraComm.cpp:47
static Rank getRank()
Current rank.
Definition IntraComm.cpp:42
static bool isPrimary()
True if this process is running the primary rank.
Definition IntraComm.cpp:52
static auto allSecondaryRanks()
Returns an iterable range over salve ranks [1, _size)
Definition IntraComm.hpp:37
static auto allRanks()
Returns an iterable range over all ranks [0, _size)
Definition IntraComm.hpp:43
static bool isParallel()
True if this process is running in parallel.
Definition IntraComm.cpp:62
static bool isSecondary()
True if this process is running a secondary rank.
Definition IntraComm.cpp:57
static com::PtrCommunication & getCommunication()
Intra-participant communication.
Definition IntraComm.hpp:31
T count(T... args)
T emplace(T... args)
T empty(T... args)
T end(T... args)
T erase(T... args)
T insert(T... args)
T make_pair(T... args)
void receiveBoundingBoxMap(Communication &communication, int rankSender, mesh::Mesh::BoundingBoxMap &bbm)
Definition Extra.cpp:63
void broadcastReceiveBoundingBoxMap(Communication &communication, mesh::Mesh::BoundingBoxMap &bbm)
Definition Extra.cpp:73
void sendMesh(Communication &communication, int rankReceiver, const mesh::Mesh &mesh)
Definition Extra.cpp:8
void broadcastSendMesh(Communication &communication, const mesh::Mesh &mesh)
Definition Extra.cpp:18
void sendBoundingBox(Communication &communication, int rankReceiver, const mesh::BoundingBox &bb)
Definition Extra.cpp:48
void sendConnectionMap(Communication &communication, int rankReceiver, const mesh::Mesh::ConnectionMap &cm)
Definition Extra.cpp:28
constexpr auto asVector
Allows to use Communication::AsVectorTag in a less verbose way.
void broadcastReceiveMesh(Communication &communication, mesh::Mesh &mesh)
Definition Extra.cpp:23
void receiveMesh(Communication &communication, int rankSender, mesh::Mesh &mesh)
Definition Extra.cpp:13
void broadcastSendBoundingBoxMap(Communication &communication, const mesh::Mesh::BoundingBoxMap &bbm)
Definition Extra.cpp:68
void receiveBoundingBox(Communication &communication, int rankSender, mesh::BoundingBox &bb)
Definition Extra.cpp:53
std::shared_ptr< Mapping > PtrMapping
provides Mesh, Data and primitives.
void filterMesh(Mesh &destination, const Mesh &source, UnaryPredicate p)
Definition Filter.hpp:16
std::shared_ptr< Mesh > PtrMesh
std::size_t countVerticesInBoundingBox(mesh::PtrMesh mesh, const mesh::BoundingBox &bb)
Given a Mesh and a bounding box, counts all vertices within the bounding box.
Definition Utils.cpp:72
contains the partitioning of distributed meshes.
Definition Partition.cpp:5
static constexpr SynchronizeTag Synchronize
Convenience instance of the SynchronizeTag.
Definition Event.hpp:21
void set_intersection_indices(InputIt1 ref1, InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2, OutputIt d_first)
This function is by and large the same as std::set_intersection(). The only difference is that we don...
Definition algorithm.hpp:33
int Rank
Definition Types.hpp:37
T push_back(T... args)
T resize(T... args)
T size(T... args)