MueLu Version of the Day
Loading...
Searching...
No Matches
MueLu_RepartitionFactory_def.hpp
Go to the documentation of this file.
1// @HEADER
2// *****************************************************************************
3// MueLu: A package for multigrid based preconditioning
4//
5// Copyright 2012 NTESS and the MueLu contributors.
6// SPDX-License-Identifier: BSD-3-Clause
7// *****************************************************************************
8// @HEADER
9
10#ifndef MUELU_REPARTITIONFACTORY_DEF_HPP
11#define MUELU_REPARTITIONFACTORY_DEF_HPP
12
13#include <algorithm>
14#include <iostream>
15#include <sstream>
16
17#include "MueLu_RepartitionFactory_decl.hpp" // TMP JG NOTE: before other includes, otherwise I cannot test the fwd declaration in _def
18
19#ifdef HAVE_MPI
20#include <Teuchos_DefaultMpiComm.hpp>
21#include <Teuchos_CommHelpers.hpp>
22#include <Teuchos_Details_MpiTypeTraits.hpp>
23
24#include <Xpetra_Map.hpp>
25#include <Xpetra_MapFactory.hpp>
26#include <Xpetra_MultiVectorFactory.hpp>
27#include <Xpetra_VectorFactory.hpp>
28#include <Xpetra_Import.hpp>
29#include <Xpetra_ImportFactory.hpp>
30#include <Xpetra_Export.hpp>
31#include <Xpetra_ExportFactory.hpp>
32#include <Xpetra_Matrix.hpp>
33#include <Xpetra_MatrixFactory.hpp>
34
35#include "MueLu_Utilities.hpp"
36
37#include "MueLu_CloneRepartitionInterface.hpp"
38
39#include "MueLu_Level.hpp"
40#include "MueLu_MasterList.hpp"
41#include "MueLu_Monitor.hpp"
42#include "MueLu_PerfUtils.hpp"
43
44#include "MueLu_RepartitionUtilities.hpp"
45#include "MueLu_Behavior.hpp"
46
47namespace MueLu {
48
49template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
51
52template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
54
55template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
57 RCP<ParameterList> validParamList = rcp(new ParameterList());
58
59#define SET_VALID_ENTRY(name) validParamList->setEntry(name, MasterList::getEntry(name))
60 SET_VALID_ENTRY("repartition: print partition distribution");
61 SET_VALID_ENTRY("repartition: remap parts");
62 SET_VALID_ENTRY("repartition: remap num values");
63 SET_VALID_ENTRY("repartition: remap accept partition");
64 SET_VALID_ENTRY("repartition: node repartition level");
65 SET_VALID_ENTRY("repartition: save importer");
66#undef SET_VALID_ENTRY
67
68 validParamList->set<RCP<const FactoryBase> >("A", Teuchos::null, "Factory of the matrix A");
69 validParamList->set<RCP<const FactoryBase> >("number of partitions", Teuchos::null, "Instance of RepartitionHeuristicFactory.");
70 validParamList->set<RCP<const FactoryBase> >("Partition", Teuchos::null, "Factory of the partition");
71
72 return validParamList;
73}
74
75template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
77 Input(currentLevel, "A");
78 Input(currentLevel, "number of partitions");
79 Input(currentLevel, "Partition");
80}
81
82template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
84 FactoryMonitor m(*this, "Build", currentLevel);
85
87
88 const Teuchos::ParameterList& pL = GetParameterList();
89 // Access parameters here to make sure that we set the parameter entry flag to "used" even in case of short-circuit evaluation.
90 // TODO (JG): I don't really know if we want to do this.
91 bool remapPartitions = pL.get<bool>("repartition: remap parts");
92
93 // TODO: We only need a CrsGraph. This class does not have to be templated on Scalar types.
94 RCP<Matrix> A = Get<RCP<Matrix> >(currentLevel, "A");
95 if (A == Teuchos::null) {
96 Set<RCP<const Import> >(currentLevel, "Importer", Teuchos::null);
97 return;
98 }
99 RCP<const Map> rowMap = A->getRowMap();
100 GO indexBase = rowMap->getIndexBase();
101 Xpetra::UnderlyingLib lib = rowMap->lib();
102
103 RCP<const Teuchos::Comm<int> > origComm = rowMap->getComm();
104 RCP<const Teuchos::Comm<int> > comm = origComm;
105
106 int numProcs = comm->getSize();
107
108 RCP<const Teuchos::MpiComm<int> > tmpic = rcp_dynamic_cast<const Teuchos::MpiComm<int> >(comm);
109 TEUCHOS_TEST_FOR_EXCEPTION(tmpic == Teuchos::null, Exceptions::RuntimeError, "Cannot cast base Teuchos::Comm to Teuchos::MpiComm object.");
110 RCP<const Teuchos::OpaqueWrapper<MPI_Comm> > rawMpiComm = tmpic->getRawMpiComm();
111
113 int numPartitions = Get<int>(currentLevel, "number of partitions");
114
115 // ======================================================================================================
116 // Construct decomposition vector
117 // ======================================================================================================
118 RCP<GOVector> decomposition = Get<RCP<GOVector> >(currentLevel, "Partition");
119
120 // check which factory provides "Partition"
121 if (remapPartitions == true && Teuchos::rcp_dynamic_cast<const CloneRepartitionInterface>(GetFactory("Partition")) != Teuchos::null) {
122 // if "Partition" is provided by a CloneRepartitionInterface class we have to switch of remapPartitions
123 // as we can assume the processor Ids in Partition to be the expected ones. If we would do remapping we
124 // would get different processors for the different blocks which screws up matrix-matrix multiplication.
125 remapPartitions = false;
126 }
127
128 // check special cases
129 if (numPartitions == 1) {
130 // Trivial case: decomposition is the trivial one, all zeros. We skip the call to Zoltan_Interface
131 // (this is mostly done to avoid extra output messages, as even if we didn't skip there is a shortcut
132 // in Zoltan[12]Interface).
133 // TODO: We can probably skip more work in this case (like building all extra data structures)
134 GetOStream(Runtime0) << "Only one partition: Skip call to the repartitioner." << std::endl;
135 } else if (numPartitions == -1) {
136 // No repartitioning necessary: decomposition should be Teuchos::null
137 GetOStream(Runtime0) << "No repartitioning necessary: partitions were left unchanged by the repartitioner" << std::endl;
138 Set<RCP<const Import> >(currentLevel, "Importer", Teuchos::null);
139 return;
140 }
141
142 // If we're doing node away, we need to be sure to get the mapping to the NodeComm's rank 0.
143 const int nodeRepartLevel = pL.get<int>("repartition: node repartition level");
144 if (currentLevel.GetLevelID() == nodeRepartLevel) {
145 // NodePartitionInterface returns the *ranks* of the guy who gets the info, not the *partition number*
146 // In a sense, we've already done remap here.
147
148 // FIXME: We need a low-comm import construction
149 remapPartitions = false;
150 }
151
152 // ======================================================================================================
153 // Remap if necessary
154 // ======================================================================================================
155 // From a user perspective, we want user to not care about remapping, thinking of it as only a performance feature.
156 // There are two problems, however.
157 // (1) Next level aggregation depends on the order of GIDs in the vector, if one uses "natural" or "random" orderings.
158 // This also means that remapping affects next level aggregation, despite the fact that the _set_ of GIDs for
159 // each partition is the same.
160 // (2) Even with the fixed order of GIDs, the remapping may influence the aggregation for the next-next level.
161 // Let us consider the following example. Lets assume that when we don't do remapping, processor 0 would have
162 // GIDs {0,1,2}, and processor 1 GIDs {3,4,5}, and if we do remapping processor 0 would contain {3,4,5} and
163 // processor 1 {0,1,2}. Now, when we run repartitioning algorithm on the next level (say Zoltan1 RCB), it may
164 // be dependent on whether whether it is [{0,1,2}, {3,4,5}] or [{3,4,5}, {0,1,2}]. Specifically, the tie-breaking
165 // algorithm can resolve these differently. For instance, running
166 // mpirun -np 5 ./MueLu_ScalingTestParamList.exe --xml=easy_sa.xml --nx=12 --ny=12 --nz=12
167 // with
168 // <ParameterList name="MueLu">
169 // <Parameter name="coarse: max size" type="int" value="1"/>
170 // <Parameter name="repartition: enable" type="bool" value="true"/>
171 // <Parameter name="repartition: min rows per proc" type="int" value="2"/>
172 // <ParameterList name="level 1">
173 // <Parameter name="repartition: remap parts" type="bool" value="false/true"/>
174 // </ParameterList>
175 // </ParameterList>
176 // produces different repartitioning for level 2.
177 // This different repartitioning may then escalate into different aggregation for the next level.
178 //
179 // We fix (1) by fixing the order of GIDs in a vector by sorting the resulting vector.
180 // Fixing (2) is more complicated.
181 // FIXME: Fixing (2) in Zoltan may not be enough, as we may use some arbitration in MueLu,
182 // for instance with CoupledAggregation. What we really need to do is to use the same order of processors containing
183 // the same order of GIDs. To achieve that, the newly created subcommunicator must be conforming with the order. For
184 // instance, if we have [{0,1,2}, {3,4,5}], we create a subcommunicator where processor 0 gets rank 0, and processor 1
185 // gets rank 1. If, on the other hand, we have [{3,4,5}, {0,1,2}], we assign rank 1 to processor 0, and rank 0 to processor 1.
186 // This rank permutation requires help from Epetra/Tpetra, both of which have no such API in place.
187 // One should also be concerned that if we had such API in place, rank 0 in subcommunicator may no longer be rank 0 in
188 // MPI_COMM_WORLD, which may lead to issues for logging.
189 if (remapPartitions) {
190 SubFactoryMonitor m1(*this, "DeterminePartitionPlacement", currentLevel);
191
192 bool acceptPartition = pL.get<bool>("repartition: remap accept partition");
193 bool allSubdomainsAcceptPartitions;
194 int localNumAcceptPartition = acceptPartition;
195 int globalNumAcceptPartition;
196 MueLu_sumAll(comm, localNumAcceptPartition, globalNumAcceptPartition);
197 GetOStream(Statistics2) << "Number of ranks that accept partitions: " << globalNumAcceptPartition << std::endl;
198 if (globalNumAcceptPartition < numPartitions) {
199 GetOStream(Warnings0) << "Not enough ranks are willing to accept a partition, allowing partitions on all ranks." << std::endl;
200 acceptPartition = true;
201 allSubdomainsAcceptPartitions = true;
202 } else if (numPartitions > numProcs) {
203 // We are trying to repartition to a larger communicator.
204 allSubdomainsAcceptPartitions = true;
205 } else {
206 allSubdomainsAcceptPartitions = false;
207 }
208
209 DeterminePartitionPlacement(*A, *decomposition, numPartitions, acceptPartition, allSubdomainsAcceptPartitions);
210 }
211
212 // ======================================================================================================
213 // Construct importer
214 // ======================================================================================================
215 // At this point, the following is true:
216 // * Each processors owns 0 or 1 partitions
217 // * If a processor owns a partition, that partition number is equal to the processor rank
218 // * The decomposition vector contains the partitions ids that the corresponding GID belongs to
219
220 if (Behavior::debug()) {
221 int myRank = comm->getRank();
222 ArrayRCP<const GO> decompEntries;
223 if (decomposition->getLocalLength() > 0)
224 decompEntries = decomposition->getData(0);
225
226 // Test range of partition ids
227 int incorrectRank = -1;
228 for (int i = 0; i < decompEntries.size(); i++)
229 if (decompEntries[i] >= numProcs || decompEntries[i] < 0) {
230 incorrectRank = myRank;
231 break;
232 }
233
234 int incorrectGlobalRank = -1;
235 MueLu_maxAll(comm, incorrectRank, incorrectGlobalRank);
236 TEUCHOS_TEST_FOR_EXCEPTION(incorrectGlobalRank > -1, Exceptions::RuntimeError, "pid " + Teuchos::toString(incorrectGlobalRank) + " encountered a partition number is that out-of-range");
237 }
238
239 // Step 1: Construct GIDs
240 Array<GO> myGIDs;
241 {
242 SubFactoryMonitor m1(*this, "Compute GIDs", currentLevel);
243 GO numLocalKept;
244 std::tie(myGIDs, numLocalKept) = Util::ConstructGIDs(decomposition);
245
246 if (IsPrint(Statistics2)) {
247 GO numGlobalKept, numGlobalRows = A->getGlobalNumRows();
248 MueLu_sumAll(comm, numLocalKept, numGlobalKept);
249 GetOStream(Statistics2) << "Unmoved rows: " << numGlobalKept << " / " << numGlobalRows << " (" << 100 * Teuchos::as<double>(numGlobalKept) / numGlobalRows << "%)" << std::endl;
250 }
251 }
252
253 // Step 2: Construct importer
254 RCP<Map> newRowMap;
255 {
256 SubFactoryMonitor m1(*this, "Map construction", currentLevel);
257 auto mapParams = Teuchos::rcp(new Teuchos::ParameterList);
258 mapParams->set("compute global constants", IsPrint(Statistics1));
259 newRowMap = MapFactory ::Build(lib, rowMap->getGlobalNumElements(), myGIDs(), indexBase, origComm, mapParams);
260 if (!IsPrint(Statistics1) && Xpetra::toTpetra(rowMap)->haveGlobalConstants()) {
261 // Copy global constants
262 auto tpMap = Xpetra::toTpetra(newRowMap->getMap());
263 Teuchos::rcp_const_cast<Tpetra::Map<LocalOrdinal, GlobalOrdinal, Node> >(tpMap)->copyGlobalConstants(*Xpetra::toTpetra(rowMap));
264 }
265 }
266
267 RCP<const Import> rowMapImporter;
268
269 RCP<const BlockedMap> blockedRowMap = Teuchos::rcp_dynamic_cast<const BlockedMap>(rowMap);
270
271 {
272 SubFactoryMonitor m1(*this, "Import construction", currentLevel);
273 // Generate a nonblocked rowmap if we need one
274 if (blockedRowMap.is_null())
275 rowMapImporter = ImportFactory::Build(rowMap, newRowMap);
276 else {
277 rowMapImporter = ImportFactory::Build(blockedRowMap->getMap(), newRowMap);
278 }
279 }
280
281 // If we're running BlockedCrs we should chop up the newRowMap into a newBlockedRowMap here (and do likewise for importers)
282 if (!blockedRowMap.is_null()) {
283 SubFactoryMonitor m1(*this, "Blocking newRowMap and Importer", currentLevel);
284 RCP<const BlockedMap> blockedTargetMap = MueLu::UtilitiesBase<Scalar, LocalOrdinal, GlobalOrdinal, Node>::GeneratedBlockedTargetMap(*blockedRowMap, *rowMapImporter);
285
286 // NOTE: This code qualifies as "correct but not particularly performant" If this needs to be sped up, we can probably read data from the existing importer to
287 // build sub-importers rather than generating new ones ex nihilo
288 size_t numBlocks = blockedRowMap->getNumMaps();
289 std::vector<RCP<const Import> > subImports(numBlocks);
290
291 for (size_t i = 0; i < numBlocks; i++) {
292 RCP<const Map> source = blockedRowMap->getMap(i);
293 RCP<const Map> target = blockedTargetMap->getMap(i);
294 subImports[i] = ImportFactory::Build(source, target);
295 }
296 Set(currentLevel, "SubImporters", subImports);
297 }
298
299 Set(currentLevel, "Importer", rowMapImporter);
300
301 // Importer saving
302 bool save_importer = pL.get<bool>("repartition: save importer");
303 if (save_importer) {
304 currentLevel.Set("Importer", rowMapImporter, NoFactory::get());
305 currentLevel.AddKeepFlag("Importer", NoFactory::get(), MueLu::Final);
306 currentLevel.RemoveKeepFlag("Importer", NoFactory::get(), MueLu::UserData); // FIXME: This is a hack
307 }
308 // ======================================================================================================
309 // Print some data
310 // ======================================================================================================
311 if (!rowMapImporter.is_null() && IsPrint(Statistics2)) {
312 // int oldRank = SetProcRankVerbose(rebalancedAc->getRowMap()->getComm()->getRank());
313 GetOStream(Statistics2) << PerfUtils::PrintImporterInfo(rowMapImporter, "Importer for rebalancing");
314 // SetProcRankVerbose(oldRank);
315 }
316 if (pL.get<bool>("repartition: print partition distribution") && IsPrint(Statistics2)) {
317 // Print the grid of processors
318 GetOStream(Statistics2) << "Partition distribution over cores (ownership is indicated by '+')" << std::endl;
319
320 char amActive = (myGIDs.size() ? 1 : 0);
321 std::vector<char> areActive(numProcs, 0);
322 MPI_Gather(&amActive, 1, MPI_CHAR, &areActive[0], 1, MPI_CHAR, 0, *rawMpiComm);
323
324 int rowWidth = std::min(Teuchos::as<int>(ceil(sqrt(numProcs))), 100);
325 for (int proc = 0; proc < numProcs; proc += rowWidth) {
326 for (int j = 0; j < rowWidth; j++)
327 if (proc + j < numProcs)
328 GetOStream(Statistics2) << (areActive[proc + j] ? "+" : ".");
329 else
330 GetOStream(Statistics2) << " ";
331
332 GetOStream(Statistics2) << " " << proc << ":" << std::min(proc + rowWidth, numProcs) - 1 << std::endl;
333 }
334 }
335
336} // Build
337
338//----------------------------------------------------------------------
339template <typename T, typename W>
340struct Triplet {
341 T i, j;
342 W v;
343};
344template <typename T, typename W>
345static bool compareTriplets(const Triplet<T, W>& a, const Triplet<T, W>& b) {
346 return (a.v > b.v); // descending order
347}
348
349template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
351 DeterminePartitionPlacement(const Matrix& A, GOVector& decomposition, GO numPartitions, bool willAcceptPartition, bool allSubdomainsAcceptPartitions) const {
352 RCP<const Map> rowMap = A.getRowMap();
353
354 RCP<const Teuchos::Comm<int> > comm = rowMap->getComm()->duplicate();
355 int numProcs = comm->getSize();
356
357 RCP<const Teuchos::MpiComm<int> > tmpic = rcp_dynamic_cast<const Teuchos::MpiComm<int> >(comm);
358 TEUCHOS_TEST_FOR_EXCEPTION(tmpic == Teuchos::null, Exceptions::RuntimeError, "Cannot cast base Teuchos::Comm to Teuchos::MpiComm object.");
359 RCP<const Teuchos::OpaqueWrapper<MPI_Comm> > rawMpiComm = tmpic->getRawMpiComm();
360
361 const Teuchos::ParameterList& pL = GetParameterList();
362
363 // maxLocal is a constant which determins the number of largest edges which are being exchanged
364 // The idea is that we do not want to construct the full bipartite graph, but simply a subset of
365 // it, which requires less communication. By selecting largest local edges we hope to achieve
366 // similar results but at a lower cost.
367 const int maxLocal = pL.get<int>("repartition: remap num values");
368 const int dataSize = 2 * maxLocal;
369
370 ArrayRCP<GO> decompEntries;
371 if (decomposition.getLocalLength() > 0)
372 decompEntries = decomposition.getDataNonConst(0);
373
374 // Step 1: Sort local edges by weight
375 // Each edge of a bipartite graph corresponds to a triplet (i, j, v) where
376 // i: processor id that has some piece of part with part_id = j
377 // j: part id
378 // v: weight of the edge
379 // We set edge weights to be the total number of nonzeros in rows on this processor which
380 // correspond to this part_id. The idea is that when we redistribute matrix, this weight
381 // is a good approximation of the amount of data to move.
382 // We use two maps, original which maps a partition id of an edge to the corresponding weight,
383 // and a reverse one, which is necessary to sort by edges.
384 std::map<GO, GO> lEdges;
385 if (willAcceptPartition)
386 for (LO i = 0; i < decompEntries.size(); i++)
387 lEdges[decompEntries[i]] += A.getNumEntriesInLocalRow(i);
388
389 // Reverse map, so that edges are sorted by weight.
390 // This results in multimap, as we may have edges with the same weight
391 std::multimap<GO, GO> revlEdges;
392 for (typename std::map<GO, GO>::const_iterator it = lEdges.begin(); it != lEdges.end(); it++)
393 revlEdges.insert(std::make_pair(it->second, it->first));
394
395 // Both lData and gData are arrays of data which we communicate. The data is stored
396 // in pairs, so that data[2*i+0] is the part index, and data[2*i+1] is the corresponding edge weight.
397 // We do not store processor id in data, as we can compute that by looking on the offset in the gData.
398 Array<GO> lData(dataSize, -1), gData(numProcs * dataSize);
399 int numEdges = 0;
400 for (typename std::multimap<GO, GO>::reverse_iterator rit = revlEdges.rbegin(); rit != revlEdges.rend() && numEdges < maxLocal; rit++) {
401 lData[2 * numEdges + 0] = rit->second; // part id
402 lData[2 * numEdges + 1] = rit->first; // edge weight
403 numEdges++;
404 }
405
406 // Step 2: Gather most edges
407 // Each processors contributes maxLocal edges by providing maxLocal pairs <part id, weight>, which is of size dataSize
408 MPI_Datatype MpiType = Teuchos::Details::MpiTypeTraits<GO>::getType();
409 MPI_Allgather(static_cast<void*>(lData.getRawPtr()), dataSize, MpiType, static_cast<void*>(gData.getRawPtr()), dataSize, MpiType, *rawMpiComm);
410
411 // Step 3: Construct mapping
412
413 // Construct the set of triplets
414 Teuchos::Array<Triplet<int, int> > gEdges(numProcs * maxLocal);
415 Teuchos::Array<bool> procWillAcceptPartition(numProcs, allSubdomainsAcceptPartitions);
416 size_t k = 0;
417 for (LO i = 0; i < gData.size(); i += 2) {
418 int procNo = i / dataSize; // determine the processor by its offset (since every processor sends the same amount)
419 GO part = gData[i + 0];
420 GO weight = gData[i + 1];
421 if (part != -1) { // skip nonexistent edges
422 gEdges[k].i = procNo;
423 gEdges[k].j = part;
424 gEdges[k].v = weight;
425 procWillAcceptPartition[procNo] = true;
426 k++;
427 }
428 }
429 gEdges.resize(k);
430
431 // Sort edges by weight
432 // NOTE: compareTriplets is actually a reverse sort, so the edges weight is in decreasing order
433 std::sort(gEdges.begin(), gEdges.end(), compareTriplets<int, int>);
434
435 // Do matching
436 std::map<int, int> match;
437 Teuchos::Array<char> matchedRanks(numProcs, 0), matchedParts(numPartitions, 0);
438 int numMatched = 0;
439 for (typename Teuchos::Array<Triplet<int, int> >::const_iterator it = gEdges.begin(); it != gEdges.end(); it++) {
440 GO rank = it->i;
441 GO part = it->j;
442 if (matchedRanks[rank] == 0 && matchedParts[part] == 0) {
443 matchedRanks[rank] = 1;
444 matchedParts[part] = 1;
445 match[part] = rank;
446 numMatched++;
447 }
448 }
449 GetOStream(Statistics1) << "Number of unassigned partitions before cleanup stage: " << (numPartitions - numMatched) << " / " << numPartitions << std::endl;
450
451 // Step 4: Assign unassigned partitions if necessary.
452 // We do that through desperate matching for remaining partitions:
453 // We select the lowest rank that can still take a partition.
454 // The reason it is done this way is that we don't need any extra communication, as we don't
455 // need to know which parts are valid.
456 if (numPartitions - numMatched > 0) {
457 Teuchos::Array<char> partitionCounts(numPartitions, 0);
458 for (typename std::map<int, int>::const_iterator it = match.begin(); it != match.end(); it++)
459 partitionCounts[it->first] += 1;
460 for (int part = 0, matcher = 0; part < numPartitions; part++) {
461 if (partitionCounts[part] == 0) {
462 // Find first non-matched rank that accepts partitions
463 while (matchedRanks[matcher] || !procWillAcceptPartition[matcher])
464 matcher++;
465
466 match[part] = matcher++;
467 numMatched++;
468 }
469 }
470 }
471
472 TEUCHOS_TEST_FOR_EXCEPTION(numMatched != numPartitions, Exceptions::RuntimeError, "MueLu::RepartitionFactory::DeterminePartitionPlacement: Only " << numMatched << " partitions out of " << numPartitions << " got assigned to ranks.");
473
474 // Step 5: Permute entries in the decomposition vector
475 for (LO i = 0; i < decompEntries.size(); i++)
476 decompEntries[i] = match[decompEntries[i]];
477}
478
479} // namespace MueLu
480
481#endif // ifdef HAVE_MPI
482
483#endif // MUELU_REPARTITIONFACTORY_DEF_HPP
#define SET_VALID_ENTRY(name)
#define MueLu_maxAll(rcpComm, in, out)
#define MueLu_sumAll(rcpComm, in, out)
static bool debug()
Whether MueLu is in debug mode.
Exception throws to report errors in the internal logical of the program.
Timer to be used in factories. Similar to Monitor but with additional timers.
Class that holds all level-specific information.
void RemoveKeepFlag(const std::string &ename, const FactoryBase *factory, KeepType keep=MueLu::All)
int GetLevelID() const
Return level number.
void AddKeepFlag(const std::string &ename, const FactoryBase *factory=NoFactory::get(), KeepType keep=MueLu::Keep)
void Set(const std::string &ename, const T &entry, const FactoryBase *factory=NoFactory::get())
static const NoFactory * get()
static std::string PrintImporterInfo(RCP< const Import > importer, const std::string &msgTag)
virtual ~RepartitionFactory()
Destructor.
void Build(Level &currentLevel) const
Build an object with this factory.
void DeterminePartitionPlacement(const Matrix &A, GOVector &decomposition, GO numPartitions, bool willAcceptPartition=true, bool allSubdomainsAcceptPartitions=true) const
Determine which process should own each partition.
void DeclareInput(Level &currentLevel) const
Determines the data that RepartitionFactory needs, and the factories that generate that data.
RepartitionFactory()
Constructor.
RCP< const ParameterList > GetValidParameterList() const
Return a const parameter list of valid parameters that setParameterList() will accept.
Timer to be used in factories. Similar to SubMonitor but adds a timer level by level.
static RCP< const Xpetra::BlockedMap< LocalOrdinal, GlobalOrdinal, Node > > GeneratedBlockedTargetMap(const Xpetra::BlockedMap< LocalOrdinal, GlobalOrdinal, Node > &sourceBlockedMap, const Xpetra::Import< LocalOrdinal, GlobalOrdinal, Node > &Importer)
Namespace for MueLu classes and methods.
@ Final
Keep data only for this run. Used to keep data useful for Hierarchy::Iterate(). Data will be deleted ...
@ UserData
User data are always kept. This flag is set automatically when Level::Set("data", data) is used....
static bool compareTriplets(const Triplet< T, W > &a, const Triplet< T, W > &b)
@ Warnings0
Important warning messages (one line)
@ Statistics2
Print even more statistics.
@ Statistics1
Print more statistics.
@ Runtime0
One-liner description of what is happening.