MueLu Version of the Day
Loading...
Searching...
No Matches
MueLu_StructuredRAPFactory_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_STRUCTUREDRAPFACTORY_DEF_HPP
11#define MUELU_STRUCTUREDRAPFACTORY_DEF_HPP
12
13#include <sstream>
14#include <vector>
15
16#include <Kokkos_Core.hpp>
17
18#include <Xpetra_Matrix.hpp>
19#include <Xpetra_MatrixUtils.hpp>
20#include <Xpetra_MatrixFactory.hpp>
21#include <Xpetra_TripleMatrixMultiply.hpp>
22#include <Xpetra_CrsGraphFactory.hpp>
23#include <Xpetra_CrsGraph.hpp>
24#include <Xpetra_MapFactory.hpp>
25
27
28#include "MueLu_MasterList.hpp"
29#include "MueLu_Monitor.hpp"
30#include "MueLu_PerfUtils.hpp"
31#include "MueLu_Behavior.hpp"
33#include "Teuchos_TestForException.hpp"
34#include "Teuchos_CommHelpers.hpp"
35
36namespace MueLu {
37
38namespace StructuredRAPFactoryDetails {
39
40template <class GO, class LocalNodes>
41KOKKOS_INLINE_FUNCTION void getLocalNodeIndices(const GO localNode,
42 const LocalNodes& localNodes,
43 GO& x, GO& y, GO& z) {
44 x = localNode % localNodes[0];
45 y = (localNode / localNodes[0]) % localNodes[1];
46 z = localNode / (localNodes[0] * localNodes[1]);
47}
48
49template <class GO, class Offset, class LocalNodes, class ProcGrid, class RankData>
50KOKKOS_INLINE_FUNCTION bool resolveNeighbor(const GO x, const GO y, const GO z,
51 const Offset& offset,
52 const LocalNodes& localNodes,
53 const ProcGrid& procGrid,
54 const RankData& rankData,
55 const int myProcX,
56 const int myProcY,
57 const int myProcZ,
58 int& neighborRank,
59 GO& neighborNode) {
60 int neighborProcX = myProcX;
61 int neighborProcY = myProcY;
62 int neighborProcZ = myProcZ;
63 GO neighborX = x + static_cast<GO>(offset.x);
64 GO neighborY = y + static_cast<GO>(offset.y);
65 GO neighborZ = z + static_cast<GO>(offset.z);
66
67 if (neighborX < 0)
68 --neighborProcX;
69 else if (neighborX >= localNodes[0])
70 ++neighborProcX;
71 if (neighborY < 0)
72 --neighborProcY;
73 else if (neighborY >= localNodes[1])
74 ++neighborProcY;
75 if (neighborZ < 0)
76 --neighborProcZ;
77 else if (neighborZ >= localNodes[2])
78 ++neighborProcZ;
79
80 if (neighborProcX < 0 || neighborProcX >= procGrid[0] ||
81 neighborProcY < 0 || neighborProcY >= procGrid[1] ||
82 neighborProcZ < 0 || neighborProcZ >= procGrid[2])
83 return false;
84
85 neighborRank = neighborProcZ * procGrid[0] * procGrid[1] + neighborProcY * procGrid[0] + neighborProcX;
86 const GO neighborLocalNx = rankData(4 * neighborRank + 1);
87 const GO neighborLocalNy = rankData(4 * neighborRank + 2);
88 const GO neighborLocalNz = rankData(4 * neighborRank + 3);
89 if (neighborX < 0)
90 neighborX = neighborLocalNx - 1;
91 else if (neighborX >= localNodes[0])
92 neighborX = 0;
93 if (neighborY < 0)
94 neighborY = neighborLocalNy - 1;
95 else if (neighborY >= localNodes[1])
96 neighborY = 0;
97 if (neighborZ < 0)
98 neighborZ = neighborLocalNz - 1;
99 else if (neighborZ >= localNodes[2])
100 neighborZ = 0;
101
102 neighborNode = rankData(4 * neighborRank) +
103 neighborZ * neighborLocalNx * neighborLocalNy +
104 neighborY * neighborLocalNx + neighborX;
105 return true;
106}
107
108} // namespace StructuredRAPFactoryDetails
109
110template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
114
115template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
117 RCP<ParameterList> validParamList = rcp(new ParameterList());
118 validParamList->set<std::string>(
119 "rap: matrix type", "", "Galeri matrix type used to infer the structured RAP graph.");
120
121#define SET_VALID_ENTRY(name) validParamList->setEntry(name, MasterList::getEntry(name))
122 SET_VALID_ENTRY("rap: triple product"); // in the long term this has to be the only option for multiplication
123 SET_VALID_ENTRY("rap: prebuild coarse graph"); // if true, the coarse graph is prebuilt and passed to the triple matrix product kernel. This can be used to optimize the symbolic phase of the triple matrix product.
124 SET_VALID_ENTRY("rap: fix zero diagonals");
125 SET_VALID_ENTRY("rap: fix zero diagonals threshold");
126 SET_VALID_ENTRY("rap: fix zero diagonals replacement");
127 SET_VALID_ENTRY("rap: relative diagonal floor");
128#undef SET_VALID_ENTRY
129 validParamList->set<bool>(
130 "transpose: use implicit", true,
131 "Use P^T as the restriction operator. StructuredRAPFactory requires this option to be true.");
132 validParamList->set<RCP<const FactoryBase>>("A", null, "Generating factory of the matrix A used during the prolongator smoothing process");
133 validParamList->set<RCP<const FactoryBase>>("P", null, "Prolongator factory");
134 validParamList->set<RCP<const FactoryBase>>("lCoarseNodesPerDim", null, "Number of nodes per spatial dimension on the coarse grid.");
135 validParamList->set<RCP<const FactoryBase>>("structuredInterpolationOrder", null, "Interpolation order used to construct the structured prolongator.");
136 validParamList->set<RCP<const FactoryBase>>(
137 "matrixType", null, "Matrix type used to infer the structured RAP graph.");
138
139 validParamList->set<bool>("CheckMainDiagonal", false, "Check main diagonal for zeros");
140 validParamList->set<bool>("RepairMainDiagonal", false, "Repair zeros on main diagonal");
141
142 // Make sure we don't recursively validate options for the matrixmatrix kernels
143 ParameterList norecurse;
144 norecurse.disableRecursiveValidation();
145 validParamList->set<ParameterList>("matrixmatrix: kernel params", norecurse, "MatrixMatrix kernel parameters");
146
147 return validParamList;
148}
149
150// Configure RAPFactory to delegate to if coarse graph prebuilding is disabled
151template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
153 const Teuchos::ParameterList& pL = GetParameterList();
154
155 ParameterList rapParams;
156 RCP<const ParameterList> validRAPParams = rapFactoryDelegate_->GetValidParameterList();
157 for (ParameterList::ConstIterator it = validRAPParams->begin(); it != validRAPParams->end(); ++it) {
158 const std::string& paramName = validRAPParams->name(it);
159 if (pL.isParameter(paramName))
160 rapParams.setEntry(paramName, pL.getEntry(paramName));
161 else if (pL.isSublist(paramName))
162 rapParams.sublist(paramName) = pL.sublist(paramName);
163 }
164
165 rapFactoryDelegate_->SetParameterList(rapParams);
166 rapFactoryDelegate_->SetFactory("A", GetFactory("A"));
167 rapFactoryDelegate_->SetFactory("P", GetFactory("P"));
168}
169
170template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
172 const Teuchos::ParameterList& pL = GetParameterList();
173 TEUCHOS_TEST_FOR_EXCEPTION(
174 !pL.get<bool>("transpose: use implicit"), Exceptions::RuntimeError,
175 "StructuredRAPFactory requires \"transpose: use implicit\" = true because "
176 "the prebuilt coarse graph assumes the Galerkin product P^T A P.");
177
178 const bool prebuildCoarseGraph = pL.get<bool>("rap: prebuild coarse graph");
179 const bool useRAPDelegate = !prebuildCoarseGraph;
180
181 if (useRAPDelegate) {
182 ConfigureRAPFactoryDelegate();
183 coarseLevel.DeclareInput("A", rapFactoryDelegate_.get(), this);
184 coarseLevel.DeclareInput("RAP reuse data", rapFactoryDelegate_.get(), this);
185 hasDeclaredInput_ = true;
186 return;
187 }
188
189 Input(fineLevel, "A");
190 Input(coarseLevel, "P");
191
192 if (prebuildCoarseGraph) {
193 Input(fineLevel, "lCoarseNodesPerDim");
194 Input(fineLevel, "structuredInterpolationOrder");
195
196 if (pL.get<std::string>("rap: matrix type").empty())
197 Input(fineLevel, "matrixType");
198 }
199
200 // call DeclareInput of all user-given transfer factories
201 for (std::vector<RCP<const FactoryBase>>::const_iterator it = transferFacts_.begin(); it != transferFacts_.end(); ++it)
202 (*it)->CallDeclareInput(coarseLevel);
203
204 hasDeclaredInput_ = true;
205}
206
207// Describe the expected coarse-matrix sparsity pattern based on the matrix type and interpolation order
208template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
211 const std::string& matrixType, const int interpolationOrder) const {
212 StructuredGraphSpec graphSpec;
213 graphSpec.description = matrixType;
214
215 bool useFullStencil = false;
216 if (matrixType == "Laplace1D" || matrixType == "Elasticity1D") {
217 graphSpec.numDimensions = 1;
218 graphSpec.dofsPerNode = Teuchos::as<LO>(1);
219 } else if (matrixType == "Laplace2D") {
220 TEUCHOS_TEST_FOR_EXCEPTION(interpolationOrder < 0 || interpolationOrder > 1, Exceptions::RuntimeError,
221 "StructuredRAPFactory::GetStructuredGraphSpec: interpolation order "
222 << interpolationOrder << " is not supported for " << matrixType << ".");
223 graphSpec.numDimensions = 2;
224 graphSpec.dofsPerNode = Teuchos::as<LO>(1);
225 useFullStencil = interpolationOrder == 1;
226 } else if (matrixType == "Elasticity2D") {
227 graphSpec.numDimensions = 2;
228 graphSpec.dofsPerNode = Teuchos::as<LO>(2);
229 useFullStencil = true;
230 } else if (matrixType == "Laplace3D") {
231 TEUCHOS_TEST_FOR_EXCEPTION(interpolationOrder < 0 || interpolationOrder > 1, Exceptions::RuntimeError,
232 "StructuredRAPFactory::GetStructuredGraphSpec: interpolation order "
233 << interpolationOrder << " is not supported for " << matrixType << ".");
234 graphSpec.numDimensions = 3;
235 graphSpec.dofsPerNode = Teuchos::as<LO>(1);
236 useFullStencil = interpolationOrder == 1;
237 } else if (matrixType == "Elasticity3D") {
238 graphSpec.numDimensions = 3;
239 graphSpec.dofsPerNode = Teuchos::as<LO>(3);
240 useFullStencil = true;
241 } else {
242 TEUCHOS_TEST_FOR_EXCEPTION(true, Exceptions::RuntimeError,
243 "StructuredRAPFactory: matrixType \"" << matrixType
244 << "\" is not supported for prebuilt Ac graph.");
245 }
246
247 const int minY = graphSpec.numDimensions > 1 ? -1 : 0;
248 const int maxY = graphSpec.numDimensions > 1 ? 1 : 0;
249 const int minZ = graphSpec.numDimensions > 2 ? -1 : 0;
250 const int maxZ = graphSpec.numDimensions > 2 ? 1 : 0;
251 for (int dz = minZ; dz <= maxZ; ++dz) {
252 for (int dy = minY; dy <= maxY; ++dy) {
253 for (int dx = -1; dx <= 1; ++dx) {
254 const int numChangedDimensions = (dx != 0 ? 1 : 0) + (dy != 0 ? 1 : 0) + (dz != 0 ? 1 : 0);
255 if (useFullStencil || numChangedDimensions <= 1)
256 graphSpec.stencilOffsets.push_back(StencilOffset{dx, dy, dz});
257 }
258 }
259 }
260
261 return graphSpec;
262}
263
264// Prebuild sparsity structure of coarse matrix
265template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
267 RCP<Matrix>& Ac, RCP<Matrix> P,
268 const Teuchos::Array<LocalOrdinal>& lCoarseNodesPerDim,
269 const StructuredGraphSpec& graphSpec) const {
270 using local_graph_type = typename CrsGraph::local_graph_type;
271 using row_map_type = typename local_graph_type::row_map_type::non_const_type;
272 using entries_type = typename local_graph_type::entries_type::non_const_type;
273 using device_type = typename Node::device_type;
274 using execution_space = typename device_type::execution_space;
275 using range_policy = Kokkos::RangePolicy<execution_space, Kokkos::IndexType<size_t>>;
276
277 TEUCHOS_TEST_FOR_EXCEPTION(graphSpec.numDimensions < 1 || graphSpec.numDimensions > 3, Exceptions::RuntimeError,
278 "StructuredRAPFactory::GetStructuredGraph(" << graphSpec.description
279 << "): number of dimensions must be between one and three.");
280 TEUCHOS_TEST_FOR_EXCEPTION(lCoarseNodesPerDim.size() < graphSpec.numDimensions, Exceptions::RuntimeError,
281 "StructuredRAPFactory::GetStructuredGraph(" << graphSpec.description
282 << "): insufficient local coarse-grid dimensions.");
283 TEUCHOS_TEST_FOR_EXCEPTION(graphSpec.dofsPerNode <= 0, Exceptions::RuntimeError,
284 "StructuredRAPFactory::GetStructuredGraph(" << graphSpec.description
285 << "): dofsPerNode must be positive.");
286 TEUCHOS_TEST_FOR_EXCEPTION(graphSpec.stencilOffsets.empty(), Exceptions::RuntimeError,
287 "StructuredRAPFactory::GetStructuredGraph(" << graphSpec.description
288 << "): coarse-grid stencil is empty; it must define at least one relative node position (dx, dy, dz).");
289 constexpr size_t maxSupportedStencilSize = 27;
290 constexpr size_t maxSupportedDofsPerNode = 3;
291 TEUCHOS_TEST_FOR_EXCEPTION(graphSpec.stencilOffsets.size() > maxSupportedStencilSize, Exceptions::RuntimeError,
292 "StructuredRAPFactory::GetStructuredGraph(" << graphSpec.description
293 << "): a radius-one stencil may contain at most 27 entries.");
294 TEUCHOS_TEST_FOR_EXCEPTION(Teuchos::as<size_t>(graphSpec.dofsPerNode) > maxSupportedDofsPerNode, Exceptions::RuntimeError,
295 "StructuredRAPFactory::GetStructuredGraph(" << graphSpec.description
296 << "): at most three DOFs per node are supported.");
297
298 Kokkos::Array<GO, 3> localNodes{};
299 for (int dim = 0; dim < 3; ++dim)
300 localNodes[dim] = Teuchos::as<GO>(1);
301 for (int dim = 0; dim < graphSpec.numDimensions; ++dim) {
302 localNodes[dim] = Teuchos::as<GO>(lCoarseNodesPerDim[dim]);
303 TEUCHOS_TEST_FOR_EXCEPTION(localNodes[dim] <= 0, Exceptions::RuntimeError,
304 "StructuredRAPFactory::GetStructuredGraph(" << graphSpec.description
305 << "): local coarse dimensions must be positive.");
306 }
307
308 Kokkos::Array<StencilOffset, maxSupportedStencilSize> stencilOffsets{};
309 Kokkos::Array<GO, maxSupportedStencilSize> localStencilNodeOffsets{};
310 for (size_t stencil = 0; stencil < graphSpec.stencilOffsets.size(); ++stencil) {
311 const StencilOffset& offset = graphSpec.stencilOffsets[stencil];
312 TEUCHOS_TEST_FOR_EXCEPTION(offset.x < -1 || offset.x > 1 ||
313 offset.y < -1 || offset.y > 1 ||
314 offset.z < -1 || offset.z > 1,
316 "StructuredRAPFactory::GetStructuredGraph(" << graphSpec.description
317 << "): only radius-one stencil offsets are currently supported.");
318 TEUCHOS_TEST_FOR_EXCEPTION((graphSpec.numDimensions < 2 && offset.y != 0) ||
319 (graphSpec.numDimensions < 3 && offset.z != 0),
321 "StructuredRAPFactory::GetStructuredGraph(" << graphSpec.description
322 << "): stencil contains an offset in an inactive dimension.");
323 stencilOffsets[stencil] = offset;
324 localStencilNodeOffsets[stencil] =
325 static_cast<GO>(offset.x) + static_cast<GO>(offset.y) * localNodes[0] +
326 static_cast<GO>(offset.z) * localNodes[0] * localNodes[1];
327 }
328
329 RCP<ParameterList> paramList = rcp(new ParameterList);
330 paramList->set("No Nonlocal Changes", true);
331 paramList->set("Optimize Storage", true);
332 paramList->set("compute global constants", true);
333
334 // Columns of P represent coarse-grid nodes
335 auto rowMap = P->getDomainMap();
336 const size_t localNumRows = rowMap->getLocalNumElements();
337 const LO dofsPerNode = graphSpec.dofsPerNode;
338 const GO dofsPerNodeGO = Teuchos::as<GO>(dofsPerNode);
339 const size_t rowsPerNode = Teuchos::as<size_t>(dofsPerNode);
340 const GO localNumNodesGO = localNodes[0] * localNodes[1] * localNodes[2];
341 const size_t localNumNodes = Teuchos::as<size_t>(localNumNodesGO);
342 const GO expectedLocalRows = localNumNodesGO * dofsPerNodeGO;
343 TEUCHOS_TEST_FOR_EXCEPTION(Teuchos::as<GO>(localNumRows) != expectedLocalRows, Exceptions::RuntimeError,
344 "StructuredRAPFactory::GetStructuredGraph(" << graphSpec.description
345 << "): local coarse dimensions with " << graphSpec.dofsPerNode
346 << " dofs per node do not match local coarse row count "
347 << localNumRows << ".");
348 TEUCHOS_TEST_FOR_EXCEPTION(rowMap->lib() != Xpetra::UseTpetra, Exceptions::RuntimeError,
349 "StructuredRAPFactory::GetStructuredGraph requires the Tpetra backend.");
350
351 const GO numGlobalRows = Teuchos::as<GO>(rowMap->getGlobalNumElements());
352 TEUCHOS_TEST_FOR_EXCEPTION(numGlobalRows % dofsPerNodeGO != 0, Exceptions::RuntimeError,
353 "StructuredRAPFactory::GetStructuredGraph(" << graphSpec.description
354 << "): global coarse row count is not divisible by dofsPerNode.");
355 const GO numGlobalNodes = numGlobalRows / dofsPerNodeGO;
356 const GO globalMinGid = rowMap->getMinAllGlobalIndex();
357 const GO globalMaxGid = rowMap->getMaxAllGlobalIndex();
358 TEUCHOS_TEST_FOR_EXCEPTION(globalMaxGid - globalMinGid + 1 != numGlobalRows, Exceptions::RuntimeError,
359 "StructuredRAPFactory::GetStructuredGraph(" << graphSpec.description
360 << "): coarse row-map GIDs must form a contiguous range.");
361
362 RCP<const Teuchos::Comm<int>> comm = rowMap->getComm();
363 const int myRank = comm->getRank();
364 const int numRanks = comm->getSize();
365 const GO localMinGid = rowMap->getMinGlobalIndex();
366 TEUCHOS_TEST_FOR_EXCEPTION((localMinGid - globalMinGid) % dofsPerNodeGO != 0, Exceptions::RuntimeError,
367 "StructuredRAPFactory::GetStructuredGraph(" << graphSpec.description
368 << "): local row range does not begin at a nodal boundary.");
369 const GO firstLocalNode = (localMinGid - globalMinGid) / dofsPerNodeGO;
370
371 Teuchos::Array<GO> localRankData(4);
372 localRankData[0] = firstLocalNode;
373 localRankData[1] = localNodes[0];
374 localRankData[2] = localNodes[1];
375 localRankData[3] = localNodes[2];
376 Teuchos::Array<GO> rankData(4 * numRanks);
377 {
378 // For RankData on every rank
379 Teuchos::gatherAll(*comm, 4, localRankData.getRawPtr(), 4 * numRanks, rankData.getRawPtr());
380 }
381
382 // Get mx, my and mz (number of ranks per dimension) like they are defined in Galeri (compare Galeri_XpetraMaps_def.hpp)
383 Kokkos::Array<int, 3> procGrid{};
384 for (int dim = 0; dim < 3; ++dim)
385 procGrid[dim] = 1;
386 if (graphSpec.numDimensions == 1) {
387 procGrid[0] = numRanks;
388 } else if (graphSpec.numDimensions == 2) {
389 while ((procGrid[0] + 1) * (procGrid[0] + 1) <= numRanks)
390 ++procGrid[0];
391 procGrid[1] = numRanks / procGrid[0];
392 while (procGrid[0] * procGrid[1] != numRanks)
393 procGrid[1] = numRanks / (--procGrid[0]);
394 } else {
395 int cubeRootNumRanks = 1;
396 while ((cubeRootNumRanks + 1) * (cubeRootNumRanks + 1) * (cubeRootNumRanks + 1) <= numRanks)
397 ++cubeRootNumRanks;
398 procGrid[0] = cubeRootNumRanks;
399 procGrid[1] = cubeRootNumRanks;
400 procGrid[2] = cubeRootNumRanks;
401
402 if (procGrid[0] * procGrid[1] * procGrid[2] != numRanks) {
403 procGrid[0] = 1;
404 procGrid[1] = 1;
405 procGrid[2] = 1;
406
407 int procTemp = numRanks;
408 // Keep this factorization limit synchronized with Galeri_XpetraMaps_def.hpp.
409 const int galeriFactorSearchLimit = 50;
410 int factors[galeriFactorSearchLimit] = {};
411 for (int factor = 2; factor < galeriFactorSearchLimit; ++factor) {
412 while (procTemp % factor == 0) {
413 ++factors[factor];
414 procTemp /= factor;
415 }
416 }
417 procGrid[0] = procTemp;
418 for (int factor = galeriFactorSearchLimit - 1; factor > 0; --factor) {
419 while (factors[factor] != 0) {
420 if (procGrid[0] <= procGrid[1] && procGrid[0] <= procGrid[2])
421 procGrid[0] *= factor;
422 else if (procGrid[1] <= procGrid[0] && procGrid[1] <= procGrid[2])
423 procGrid[1] *= factor;
424 else
425 procGrid[2] *= factor;
426 --factors[factor];
427 }
428 }
429 }
430 }
431
432 const int procXY = procGrid[0] * procGrid[1];
433 TEUCHOS_TEST_FOR_EXCEPTION(procGrid[0] <= 0 || procGrid[1] <= 0 || procGrid[2] <= 0 ||
434 procXY * procGrid[2] != numRanks,
436 "StructuredRAPFactory::GetStructuredGraph(" << graphSpec.description
437 << "): invalid inferred processor grid "
438 << procGrid[0] << "x" << procGrid[1] << "x" << procGrid[2]
439 << " for " << numRanks << " ranks.");
440
441 const int myProcX = myRank % procGrid[0];
442 const int myProcY = (myRank % procXY) / procGrid[0];
443 const int myProcZ = myRank / procXY;
444
445 Kokkos::Array<GO, 3> globalNodes{};
446 for (int dim = 0; dim < 3; ++dim)
447 globalNodes[dim] = Teuchos::as<GO>(0);
448 for (int px = 0; px < procGrid[0]; ++px)
449 globalNodes[0] += rankData[4 * px + 1];
450 for (int py = 0; py < procGrid[1]; ++py)
451 globalNodes[1] += rankData[4 * (py * procGrid[0]) + 2];
452 for (int pz = 0; pz < procGrid[2]; ++pz)
453 globalNodes[2] += rankData[4 * (pz * procXY) + 3];
454 TEUCHOS_TEST_FOR_EXCEPTION(globalNodes[0] * globalNodes[1] * globalNodes[2] != numGlobalNodes,
456 "StructuredRAPFactory::GetStructuredGraph(" << graphSpec.description
457 << "): processor-grid coarse dimensions do not match coarse node count.");
458
459 execution_space executionSpace;
460 Kokkos::View<GO*, device_type> rankDataDevice(
461 Kokkos::ViewAllocateWithoutInitializing("StructuredRAP: rank metadata"), rankData.size());
462 auto rankDataHost = Kokkos::create_mirror_view(rankDataDevice);
463 for (int entry = 0; entry < rankData.size(); ++entry)
464 rankDataHost(entry) = rankData[entry];
465 Kokkos::deep_copy(executionSpace, rankDataDevice, rankDataHost);
466
467 const size_t stencilSize = graphSpec.stencilOffsets.size();
468 const int numDimensions = graphSpec.numDimensions;
469 const auto rowLocalMap = rowMap->getLocalMap();
470
471 const bool debug = Behavior::debug();
472 if (debug) {
473 size_t invalidLocalRows = 0;
474 Kokkos::parallel_reduce(
475 "StructuredRAP: validate local rows", range_policy(executionSpace, 0, localNumRows),
476 KOKKOS_LAMBDA(const size_t rowLid, size_t& invalid) {
477 const GO rowGid = rowLocalMap.getGlobalElement(static_cast<LO>(rowLid));
478 const GO expectedRowGid = localMinGid + static_cast<GO>(rowLid);
479 if (rowGid != expectedRowGid)
480 ++invalid;
481 },
482 invalidLocalRows);
483 TEUCHOS_TEST_FOR_EXCEPTION(invalidLocalRows != 0, Exceptions::RuntimeError,
484 "StructuredRAPFactory::GetStructuredGraph(" << graphSpec.description
485 << "): coarse row-map GIDs are not locally contiguous and ordered.");
486 }
487
488 // A radius-one stencil reaches disjoint faces, edges, and corners of at most
489 // 26 neighboring ranks, so generate each remote column exactly once.
490 constexpr size_t maxNeighborRegions = 26;
491 Kokkos::Array<int, maxNeighborRegions> remoteRegionRanks{};
492 Kokkos::Array<StencilOffset, maxNeighborRegions> remoteRegionDirections{};
493 Kokkos::Array<GO, maxNeighborRegions> remoteRegionFirstNodes{};
494 Kokkos::Array<GO, maxNeighborRegions> remoteRegionNx{};
495 Kokkos::Array<GO, maxNeighborRegions> remoteRegionNy{};
496 Kokkos::Array<GO, maxNeighborRegions> remoteRegionNz{};
497 Kokkos::Array<size_t, maxNeighborRegions + 1> remoteRegionOffsets{};
498 size_t numRemoteRegions = 0;
499
500 const int minProcY = graphSpec.numDimensions > 1 ? -1 : 0;
501 const int maxProcY = graphSpec.numDimensions > 1 ? 1 : 0;
502 const int minProcZ = graphSpec.numDimensions > 2 ? -1 : 0;
503 const int maxProcZ = graphSpec.numDimensions > 2 ? 1 : 0;
504 for (int procDz = minProcZ; procDz <= maxProcZ; ++procDz) {
505 for (int procDy = minProcY; procDy <= maxProcY; ++procDy) {
506 for (int procDx = -1; procDx <= 1; ++procDx) {
507 if (procDx == 0 && procDy == 0 && procDz == 0)
508 continue;
509
510 bool stencilReachesRegion = false;
511 for (size_t stencil = 0; stencil < stencilSize; ++stencil) {
512 const StencilOffset& offset = stencilOffsets[stencil];
513 const bool reachesX = procDx == 0 || offset.x == procDx;
514 const bool reachesY = procDy == 0 || offset.y == procDy;
515 const bool reachesZ = procDz == 0 || offset.z == procDz;
516 if (reachesX && reachesY && reachesZ) {
517 stencilReachesRegion = true;
518 break;
519 }
520 }
521 if (!stencilReachesRegion)
522 continue;
523
524 const int neighborProcX = myProcX + procDx;
525 const int neighborProcY = myProcY + procDy;
526 const int neighborProcZ = myProcZ + procDz;
527 if (neighborProcX < 0 || neighborProcX >= procGrid[0] ||
528 neighborProcY < 0 || neighborProcY >= procGrid[1] ||
529 neighborProcZ < 0 || neighborProcZ >= procGrid[2])
530 continue;
531
532 const int neighborRank = neighborProcZ * procXY + neighborProcY * procGrid[0] + neighborProcX;
533 const GO neighborNx = rankData[4 * neighborRank + 1];
534 const GO neighborNy = rankData[4 * neighborRank + 2];
535 const GO neighborNz = rankData[4 * neighborRank + 3];
536 const size_t regionNodes = Teuchos::as<size_t>((procDx == 0 ? neighborNx : 1) *
537 (procDy == 0 ? neighborNy : 1) *
538 (procDz == 0 ? neighborNz : 1));
539
540 remoteRegionRanks[numRemoteRegions] = neighborRank;
541 remoteRegionDirections[numRemoteRegions] = StencilOffset{procDx, procDy, procDz};
542 remoteRegionFirstNodes[numRemoteRegions] = rankData[4 * neighborRank];
543 remoteRegionNx[numRemoteRegions] = neighborNx;
544 remoteRegionNy[numRemoteRegions] = neighborNy;
545 remoteRegionNz[numRemoteRegions] = neighborNz;
546 remoteRegionOffsets[numRemoteRegions + 1] =
547 remoteRegionOffsets[numRemoteRegions] + regionNodes * rowsPerNode;
548 ++numRemoteRegions;
549 }
550 }
551 }
552 const size_t numRemoteColumns = remoteRegionOffsets[numRemoteRegions];
553
554 Kokkos::View<GO*, device_type> colMapGids(
555 Kokkos::ViewAllocateWithoutInitializing("StructuredRAP: column map GIDs"),
556 localNumRows + numRemoteColumns);
557 const auto localRowGidsDevice = rowMap->getMyGlobalIndicesDevice();
558 Kokkos::parallel_for(
559 "StructuredRAP: fill column map", range_policy(executionSpace, 0, localNumRows + numRemoteColumns),
560 KOKKOS_LAMBDA(const size_t entry) {
561 if (entry < localNumRows) {
562 colMapGids(entry) = localRowGidsDevice(entry);
563 return;
564 }
565
566 const size_t remoteEntry = entry - localNumRows;
567 size_t region = 0;
568 while (remoteEntry >= remoteRegionOffsets[region + 1])
569 ++region;
570
571 const StencilOffset direction = remoteRegionDirections[region];
572 const GO neighborNx = remoteRegionNx[region];
573 const GO neighborNy = remoteRegionNy[region];
574 const GO neighborNz = remoteRegionNz[region];
575 size_t regionEntry = remoteEntry - remoteRegionOffsets[region];
576 const GO dof = static_cast<GO>(regionEntry % rowsPerNode);
577 size_t regionNode = regionEntry / rowsPerNode;
578
579 GO neighborX = direction.x < 0 ? neighborNx - 1 : 0;
580 GO neighborY = direction.y < 0 ? neighborNy - 1 : 0;
581 GO neighborZ = direction.z < 0 ? neighborNz - 1 : 0;
582 if (direction.x == 0) {
583 neighborX = static_cast<GO>(regionNode % static_cast<size_t>(neighborNx));
584 regionNode /= static_cast<size_t>(neighborNx);
585 }
586 if (direction.y == 0) {
587 neighborY = static_cast<GO>(regionNode % static_cast<size_t>(neighborNy));
588 regionNode /= static_cast<size_t>(neighborNy);
589 }
590 if (direction.z == 0)
591 neighborZ = static_cast<GO>(regionNode);
592
593 const GO neighborNode = remoteRegionFirstNodes[region] +
594 neighborZ * neighborNx * neighborNy + neighborY * neighborNx + neighborX;
595 colMapGids(entry) = globalMinGid + neighborNode * dofsPerNodeGO + dof;
596 });
597 executionSpace.fence("StructuredRAP: column map GIDs ready");
598
599 typename Map::global_indices_array_device_type constColMapGids = colMapGids;
600 // colMap is filled here
601 RCP<const Map> colMap;
602 {
603 colMap = MapFactory::Build(
604 rowMap->lib(), Teuchos::OrdinalTraits<Xpetra::global_size_t>::invalid(),
605 constColMapGids, rowMap->getIndexBase(), comm);
606 }
607
608 // Loop to fill rowptr
609 row_map_type rowptr;
610 size_t localNnz = 0;
611 {
612 rowptr = row_map_type(
613 Kokkos::ViewAllocateWithoutInitializing("StructuredRAP: graph row pointers"), localNumRows + 1);
614 Kokkos::parallel_scan(
615 "StructuredRAP: compute graph row pointers", range_policy(executionSpace, 0, localNumNodes + 1),
616 KOKKOS_LAMBDA(const size_t localNodeIndex, size_t& update, const bool final) {
617 if (localNodeIndex == localNumNodes) {
618 if (final)
619 rowptr(localNumRows) = update;
620 return;
621 }
622
623 const GO localNode = static_cast<GO>(localNodeIndex);
624 GO x = 0, y = 0, z = 0;
625 StructuredRAPFactoryDetails::getLocalNodeIndices(localNode, localNodes, x, y, z);
626 const bool isInterior =
627 x > 0 && x + 1 < localNodes[0] &&
628 (numDimensions < 2 || (y > 0 && y + 1 < localNodes[1])) &&
629 (numDimensions < 3 || (z > 0 && z + 1 < localNodes[2]));
630 size_t rowLength = isInterior ? stencilSize * rowsPerNode : 0;
631 if (!isInterior) {
632 for (size_t stencil = 0; stencil < stencilSize; ++stencil) {
633 int neighborRank = myRank;
634 GO neighborNode = 0;
636 x, y, z, stencilOffsets[stencil], localNodes, procGrid, rankDataDevice,
637 myProcX, myProcY, myProcZ, neighborRank, neighborNode))
638 rowLength += rowsPerNode;
639 }
640 }
641
642 if (final) {
643 const size_t firstRow = localNodeIndex * rowsPerNode;
644 for (size_t rowDof = 0; rowDof < rowsPerNode; ++rowDof)
645 rowptr(firstRow + rowDof) = update + rowDof * rowLength;
646 }
647 update += rowLength * rowsPerNode;
648 },
649 localNnz);
650 }
651
652 entries_type colind;
653 {
654 colind = entries_type(
655 Kokkos::ViewAllocateWithoutInitializing("StructuredRAP: graph column indices"), localNnz);
656 }
657
658 const LO invalidLocalOrdinal = Teuchos::OrdinalTraits<LO>::invalid();
659 Kokkos::View<int, device_type> invalidColumn;
660 if (debug) {
661 invalidColumn = Kokkos::View<int, device_type>("StructuredRAP: invalid column");
662 Kokkos::deep_copy(executionSpace, invalidColumn, 0);
663 }
664
665 // Loop to fill colind
666 {
667 Kokkos::parallel_for(
668 "StructuredRAP: fill graph column indices", range_policy(executionSpace, 0, localNumNodes),
669 KOKKOS_LAMBDA(const size_t localNodeIndex) {
670 const GO localNode = static_cast<GO>(localNodeIndex);
671 GO x = 0, y = 0, z = 0;
672 StructuredRAPFactoryDetails::getLocalNodeIndices(localNode, localNodes, x, y, z);
673 size_t columnOffset = 0;
674 const bool isInterior =
675 x > 0 && x + 1 < localNodes[0] &&
676 (numDimensions < 2 || (y > 0 && y + 1 < localNodes[1])) &&
677 (numDimensions < 3 || (z > 0 && z + 1 < localNodes[2]));
678
679 if (isInterior) {
680 const size_t firstRow = localNodeIndex * rowsPerNode;
681 for (size_t stencil = 0; stencil < stencilSize; ++stencil) {
682 const GO neighborLocalNode = localNode + localStencilNodeOffsets[stencil];
683 for (size_t colDof = 0; colDof < rowsPerNode; ++colDof) {
684 const LO colLid = static_cast<LO>(
685 neighborLocalNode * dofsPerNodeGO + static_cast<GO>(colDof));
686 for (size_t rowDof = 0; rowDof < rowsPerNode; ++rowDof)
687 colind(rowptr(firstRow + rowDof) + columnOffset) = colLid;
688 ++columnOffset;
689 }
690 }
691 return;
692 }
693
694 Kokkos::Array<LO, maxSupportedStencilSize * maxSupportedDofsPerNode> nodeColumns;
695 for (size_t stencil = 0; stencil < stencilSize; ++stencil) {
696 int neighborRank = myRank;
697 GO neighborNode = 0;
699 x, y, z, stencilOffsets[stencil], localNodes, procGrid, rankDataDevice,
700 myProcX, myProcY, myProcZ, neighborRank, neighborNode))
701 continue;
702
703 for (size_t colDof = 0; colDof < rowsPerNode; ++colDof) {
704 const GO colGid = globalMinGid + neighborNode * dofsPerNodeGO + static_cast<GO>(colDof);
705 LO colLid = invalidLocalOrdinal;
706 if (neighborRank == myRank) {
707 colLid = static_cast<LO>(colGid - localMinGid);
708 } else {
709 size_t region = 0;
710 while (region < numRemoteRegions && remoteRegionRanks[region] != neighborRank)
711 ++region;
712 if (region < numRemoteRegions) {
713 const GO neighborNx = remoteRegionNx[region];
714 const GO neighborNy = remoteRegionNy[region];
715 const GO localNeighborNode = neighborNode - remoteRegionFirstNodes[region];
716 const GO neighborX = localNeighborNode % neighborNx;
717 const GO neighborY = (localNeighborNode / neighborNx) % neighborNy;
718 const GO neighborZ = localNeighborNode / (neighborNx * neighborNy);
719 const StencilOffset direction = remoteRegionDirections[region];
720 size_t regionNode = 0;
721 size_t stride = 1;
722 if (direction.x == 0) {
723 regionNode += static_cast<size_t>(neighborX) * stride;
724 stride *= static_cast<size_t>(neighborNx);
725 }
726 if (direction.y == 0) {
727 regionNode += static_cast<size_t>(neighborY) * stride;
728 stride *= static_cast<size_t>(neighborNy);
729 }
730 if (direction.z == 0)
731 regionNode += static_cast<size_t>(neighborZ) * stride;
732 colLid = static_cast<LO>(localNumRows + remoteRegionOffsets[region] +
733 regionNode * rowsPerNode + colDof);
734 }
735 }
736
737 if (debug && colLid == invalidLocalOrdinal)
738 Kokkos::atomic_exchange(&invalidColumn(), 1);
739
740 nodeColumns[columnOffset++] = colLid;
741 }
742 }
743
744 for (size_t entry = 1; entry < columnOffset; ++entry) {
745 const LO value = nodeColumns[entry];
746 size_t insertion = entry;
747 while (insertion > 0 && value < nodeColumns[insertion - 1]) {
748 nodeColumns[insertion] = nodeColumns[insertion - 1];
749 --insertion;
750 }
751 nodeColumns[insertion] = value;
752 }
753
754 const size_t firstRow = localNodeIndex * rowsPerNode;
755 for (size_t rowDof = 0; rowDof < rowsPerNode; ++rowDof) {
756 const size_t rowStart = rowptr(firstRow + rowDof);
757 for (size_t entry = 0; entry < columnOffset; ++entry)
758 colind(rowStart + entry) = nodeColumns[entry];
759 }
760 });
761 executionSpace.fence("StructuredRAP: graph column indices ready");
762 }
763
764 if (debug) {
765 int invalidColumnHost = 0;
766 Kokkos::deep_copy(executionSpace, invalidColumnHost, invalidColumn);
767 TEUCHOS_TEST_FOR_EXCEPTION(invalidColumnHost != 0, Exceptions::RuntimeError,
768 "StructuredRAPFactory::GetStructuredGraph(" << graphSpec.description
769 << "): a generated column GID was not found in the coarse column map.");
770
771 size_t unsortedRows = 0;
772 Kokkos::parallel_reduce(
773 "StructuredRAP: validate sorted graph rows", range_policy(executionSpace, 0, localNumRows),
774 KOKKOS_LAMBDA(const size_t row, size_t& invalid) {
775 for (size_t entry = rowptr(row) + 1; entry < rowptr(row + 1); ++entry) {
776 if (colind(entry) < colind(entry - 1)) {
777 ++invalid;
778 break;
779 }
780 }
781 },
782 unsortedRows);
783 TEUCHOS_TEST_FOR_EXCEPTION(unsortedRows != 0, Exceptions::RuntimeError,
784 "StructuredRAPFactory::GetStructuredGraph(" << graphSpec.description
785 << "): generated graph rows are not sorted.");
786 }
787
788 local_graph_type localGraph(colind, rowptr);
789 RCP<CrsGraph> graph;
790 {
791 graph = CrsGraphFactory::Build(
792 localGraph, rowMap, colMap, rowMap, rowMap, paramList);
793 }
794 {
795 using values_type = typename Matrix::local_matrix_type::values_type;
796 values_type values(
797 Kokkos::ViewAllocateWithoutInitializing("StructuredRAP: matrix values"), localNnz);
798 Ac = MatrixFactory::Build(graph, values, paramList);
799 }
800}
801
802template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
804 const bool doTranspose = true;
805 const bool doFillComplete = true;
806 const bool doOptimizeStorage = true;
807 const Teuchos::ParameterList& pL = GetParameterList();
808 const bool prebuildCoarseGraph = pL.get<bool>("rap: prebuild coarse graph");
809 const bool useRAPDelegate = !prebuildCoarseGraph;
810
811 TEUCHOS_TEST_FOR_EXCEPTION(
812 !pL.get<bool>("transpose: use implicit"), Exceptions::RuntimeError,
813 "StructuredRAPFactory requires \"transpose: use implicit\" = true because "
814 "the prebuilt coarse graph assumes the Galerkin product P^T A P.");
815
816 RCP<Matrix> Ac;
817
818 TEUCHOS_TEST_FOR_EXCEPTION(hasDeclaredInput_ == false, Exceptions::RuntimeError,
819 "MueLu::RAPFactory::Build(): CallDeclareInput has not been called before Build!");
820
821 if (useRAPDelegate) {
822 if (coarseLevel.IsAvailable("RAP reuse data", this)) {
823 RCP<ParameterList> RAPparams = coarseLevel.Get<RCP<ParameterList>>("RAP reuse data", this);
824 coarseLevel.Set("RAP reuse data", RAPparams, rapFactoryDelegate_.get());
825 }
826
827 // RAPFactory does not accept a prebuilt Ac graph, so delegate only when
828 // coarse-graph prebuilding is disabled.
829 rapFactoryDelegate_->Build(fineLevel, coarseLevel);
830
831 Ac = coarseLevel.Get<RCP<Matrix>>("A", rapFactoryDelegate_.get());
832 Set(coarseLevel, "A", Ac);
833
834 if (coarseLevel.IsAvailable("RAP reuse data", rapFactoryDelegate_.get())) {
835 RCP<ParameterList> RAPparams = coarseLevel.Get<RCP<ParameterList>>("RAP reuse data", rapFactoryDelegate_.get());
836 Set(coarseLevel, "RAP reuse data", RAPparams);
837 }
838
839 return;
840 }
841
842 {
843 FactoryMonitor m(*this, "Computing Ac", coarseLevel);
844
845 std::ostringstream levelstr;
846 levelstr << coarseLevel.GetLevelID();
847 std::string labelstr = FormattingHelper::getColonLabel(coarseLevel.getObjectLabel());
848
849 TEUCHOS_TEST_FOR_EXCEPTION(
850 pL.get<bool>("rap: triple product") == false, Exceptions::RuntimeError,
851 "StructuredRAPFactory requires \"rap: triple product\" = true.");
852
853 RCP<Matrix> A = Get<RCP<Matrix>>(fineLevel, "A");
854 RCP<Matrix> P = Get<RCP<Matrix>>(coarseLevel, "P");
855 // We don't have a valid P (e.g., # global aggregates = 0) so we bail.
856 // This level will ultimately be removed in MueLu_Hierarchy_defs.h via a resize()
857 if (P.is_null()) {
858 Ac = Teuchos::null;
859 Set(coarseLevel, "A", Ac);
860 return;
861 }
862
863 {
864 RCP<ParameterList> RAPparams = rcp(new ParameterList);
865 if (pL.isSublist("matrixmatrix: kernel params"))
866 RAPparams->sublist("matrixmatrix: kernel params") = pL.sublist("matrixmatrix: kernel params");
867
868 if (coarseLevel.IsAvailable("RAP reuse data", this)) {
869 GetOStream(static_cast<MsgType>(Runtime0 | Test)) << "Reusing previous RAP data" << std::endl;
870
871 RAPparams = coarseLevel.Get<RCP<ParameterList>>("RAP reuse data", this);
872
873 TEUCHOS_TEST_FOR_EXCEPTION(!RAPparams->isParameter("graph"), Exceptions::RuntimeError,
874 "StructuredRAPFactory::Build(): \"RAP reuse data\" does not contain the expected graph.");
875 Ac = RAPparams->get<RCP<Matrix>>("graph");
876 TEUCHOS_TEST_FOR_EXCEPTION(Ac.is_null(), Exceptions::RuntimeError,
877 "StructuredRAPFactory::Build(): \"RAP reuse data\" graph is null.");
878
879 // Some eigenvalue may have been cached with the matrix in the previous run.
880 // As the matrix values will be updated, we need to reset the eigenvalue.
881 Ac->SetMaxEigenvalueEstimate(-Teuchos::ScalarTraits<SC>::one());
882
883 // If we want to prebuild the coarse graph, do that here. Otherwise, we will get it in the symbolic phase of the triple matrix product,
884 // but that will be more expensive
885 } else if (prebuildCoarseGraph) {
886 // if reuse data not available, try to get sparse fill graph via the knowledge of the matrix structure
887 std::string matrixType = pL.get<std::string>("rap: matrix type");
888 if (matrixType.empty())
889 matrixType = Get<std::string>(fineLevel, "matrixType");
890 Teuchos::Array<LocalOrdinal> lCoarseNodesPerDim =
891 Get<Teuchos::Array<LocalOrdinal>>(fineLevel, "lCoarseNodesPerDim");
892 const int interpolationOrder = Get<int>(fineLevel, "structuredInterpolationOrder");
893 const StructuredGraphSpec graphSpec = GetStructuredGraphSpec(matrixType, interpolationOrder);
894 GetOStream(Statistics1) << "StructuredRAP: Using " << graphSpec.description
895 << " stencil with " << graphSpec.stencilOffsets.size()
896 << " nodal entries." << std::endl;
897 GetStructuredGraph(Ac, P, lCoarseNodesPerDim, graphSpec);
898 }
899
900 // We *always* need global constants for the RAP, but not for the temps
901 RAPparams->set("compute global constants: temporaries", RAPparams->get("compute global constants: temporaries", false));
902 RAPparams->set("compute global constants", true);
903
904 if (Ac.is_null())
905 Ac = MatrixFactory::Build(P->getDomainMap(), Teuchos::as<LocalOrdinal>(0));
906
907 SubFactoryMonitor m2(*this, "MxMxM: P^T x A x P (implicit)", coarseLevel);
908
909 Xpetra::TripleMatrixMultiply<SC, LO, GO, NO>::
910 MultiplyRAP(*P, doTranspose, *A, !doTranspose, *P, !doTranspose, *Ac, doFillComplete,
911 doOptimizeStorage, labelstr + std::string("MueLu::P^T*A*P-implicit-") + levelstr.str(),
912 RAPparams);
913
914 GetOStream(Statistics1) << "StructuredRAP: Ac nnz (prebuild coarse graph = "
915 << (prebuildCoarseGraph ? "true" : "false")
916 << "): local = " << Ac->getLocalNumEntries()
917 << ", global = " << Ac->getGlobalNumEntries() << std::endl;
918
919 Teuchos::ArrayView<const double> relativeFloor = pL.get<Teuchos::Array<double>>("rap: relative diagonal floor")();
920 if (relativeFloor.size() > 0) {
921 Xpetra::MatrixUtils<SC, LO, GO, NO>::RelativeDiagonalBoost(Ac, relativeFloor, GetOStream(Statistics2));
922 }
923
924 bool repairZeroDiagonals = pL.get<bool>("RepairMainDiagonal") || pL.get<bool>("rap: fix zero diagonals");
925 bool checkAc = pL.get<bool>("CheckMainDiagonal") || pL.get<bool>("rap: fix zero diagonals");
926 if (checkAc || repairZeroDiagonals) {
927 using magnitudeType = typename Teuchos::ScalarTraits<Scalar>::magnitudeType;
928 magnitudeType threshold;
929 if (pL.isType<magnitudeType>("rap: fix zero diagonals threshold"))
930 threshold = pL.get<magnitudeType>("rap: fix zero diagonals threshold");
931 else
932 threshold = Teuchos::as<magnitudeType>(pL.get<double>("rap: fix zero diagonals threshold"));
933 Scalar replacement = Teuchos::as<Scalar>(pL.get<double>("rap: fix zero diagonals replacement"));
934 Xpetra::MatrixUtils<SC, LO, GO, NO>::CheckRepairMainDiagonal(Ac, repairZeroDiagonals, GetOStream(Warnings1), threshold, replacement);
935 }
936
937 if (IsPrint(Statistics2)) {
938 RCP<ParameterList> params = rcp(new ParameterList());
939 params->set("printLoadBalancingInfo", true);
940 params->set("printCommInfo", true);
941
942 GetOStream(Statistics2) << PerfUtils::PrintMatrixInfo(*Ac, "Ac", params);
943 }
944
945 if (!Ac.is_null()) {
946 std::ostringstream oss;
947 oss << "A_" << coarseLevel.GetLevelID();
948 Ac->setObjectLabel(oss.str());
949 }
950 Set(coarseLevel, "A", Ac);
951
952 RAPparams->set("graph", Ac);
953 Set(coarseLevel, "RAP reuse data", RAPparams);
954 }
955 }
956
957 if (Behavior::debug())
958 MatrixUtils::checkLocalRowMapMatchesColMap(*Ac);
959
960 if (transferFacts_.begin() != transferFacts_.end()) {
961 SubFactoryMonitor m(*this, "Projections", coarseLevel);
962
963 // call Build of all user-given transfer factories
964 for (std::vector<RCP<const FactoryBase>>::const_iterator it = transferFacts_.begin(); it != transferFacts_.end(); ++it) {
965 RCP<const FactoryBase> fac = *it;
966 GetOStream(Runtime0) << "RAPFactory: call transfer factory: " << fac->description() << std::endl;
967 fac->CallBuild(coarseLevel);
968 // Coordinates transfer is marginally different from all other operations
969 // because it is *optional*, and not required. For instance, we may need
970 // coordinates only on level 4 if we start repartitioning from that level,
971 // but we don't need them on level 1,2,3. As our current Hierarchy setup
972 // assumes propagation of dependencies only through three levels, this
973 // means that we need to rely on other methods to propagate optional data.
974 //
975 // The method currently used is through RAP transfer factories, which are
976 // simply factories which are called at the end of RAP with a single goal:
977 // transfer some fine data to coarser level. Because these factories are
978 // kind of outside of the mainline factories, they behave different. In
979 // particular, we call their Build method explicitly, rather than through
980 // Get calls. This difference is significant, as the Get call is smart
981 // enough to know when to release all factory dependencies, and Build is
982 // dumb. This led to the following CoordinatesTransferFactory sequence:
983 // 1. Request level 0
984 // 2. Request level 1
985 // 3. Request level 0
986 // 4. Release level 0
987 // 5. Release level 1
988 //
989 // The problem is missing "6. Release level 0". Because it was missing,
990 // we had outstanding request on "Coordinates", "Aggregates" and
991 // "CoarseMap" on level 0.
992 //
993 // This was fixed by explicitly calling Release on transfer factories in
994 // RAPFactory. I am still unsure how exactly it works, but now we have
995 // clear data requests for all levels.
996 coarseLevel.Release(*fac);
997 }
998 }
999}
1000
1001template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1003 // check if it's a TwoLevelFactoryBase based transfer factory
1004 TEUCHOS_TEST_FOR_EXCEPTION(Teuchos::rcp_dynamic_cast<const TwoLevelFactoryBase>(factory) == Teuchos::null, Exceptions::BadCast,
1005 "MueLu::StructuredRAPFactory::AddTransferFactory: Transfer factory is not derived from TwoLevelFactoryBase. "
1006 "This is very strange. (Note: you can remove this exception if there's a good reason for)");
1007 TEUCHOS_TEST_FOR_EXCEPTION(hasDeclaredInput_, Exceptions::RuntimeError, "MueLu::StructuredRAPFactory::AddTransferFactory: Factory is being added after we have already declared input");
1008 transferFacts_.push_back(factory);
1009 rapFactoryDelegate_->AddTransferFactory(factory);
1010}
1011
1012} // namespace MueLu
1013
1014#define MUELU_STRUCTUREDRAPFACTORY_SHORT
1015#endif // MUELU_STRUCTUREDRAPFACTORY_DEF_HPP
#define SET_VALID_ENTRY(name)
MueLu::DefaultLocalOrdinal LocalOrdinal
MueLu::DefaultScalar Scalar
MueLu::DefaultGlobalOrdinal GlobalOrdinal
MueLu::DefaultNode Node
static bool debug()
Whether MueLu is in debug mode.
Exception indicating invalid cast attempted.
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.
bool IsAvailable(const std::string &ename, const FactoryBase *factory=NoFactory::get()) const
Test whether a need's value has been saved.
void DeclareInput(const std::string &ename, const FactoryBase *factory, const FactoryBase *requestedBy=NoFactory::get())
Callback from FactoryBase::CallDeclareInput() and FactoryBase::DeclareInput()
void Release(const FactoryBase &factory)
Decrement the storage counter for all the inputs of a factory.
int GetLevelID() const
Return level number.
T & Get(const std::string &ename, const FactoryBase *factory=NoFactory::get())
Get data without decrementing associated storage counter (i.e., read-only access)....
void Set(const std::string &ename, const T &entry, const FactoryBase *factory=NoFactory::get())
static std::string PrintMatrixInfo(const Matrix &A, const std::string &msgTag, RCP< const Teuchos::ParameterList > params=Teuchos::null)
Factory for building coarse matrices.
void AddTransferFactory(const RCP< const FactoryBase > &factory)
Add transfer factory in the end of list of transfer factories in RepartitionAcFactory.
void GetStructuredGraph(RCP< Matrix > &Ac, const RCP< Matrix > P, const Teuchos::Array< LocalOrdinal > &lCoarseNodesPerDim, const StructuredGraphSpec &graphSpec) const
void DeclareInput(Level &fineLevel, Level &coarseLevel) const
Input.
void Build(Level &fineLevel, Level &coarseLevel) const
Build an object with this factory.
StructuredGraphSpec GetStructuredGraphSpec(const std::string &matrixType, int interpolationOrder) const
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.
KOKKOS_INLINE_FUNCTION void getLocalNodeIndices(const GO localNode, const LocalNodes &localNodes, GO &x, GO &y, GO &z)
KOKKOS_INLINE_FUNCTION bool resolveNeighbor(const GO x, const GO y, const GO z, const Offset &offset, const LocalNodes &localNodes, const ProcGrid &procGrid, const RankData &rankData, const int myProcX, const int myProcY, const int myProcZ, int &neighborRank, GO &neighborNode)
Namespace for MueLu classes and methods.
@ Statistics2
Print even more statistics.
@ Statistics1
Print more statistics.
@ Runtime0
One-liner description of what is happening.
@ Warnings1
Additional warnings.
static std::string getColonLabel(const std::string &label)
Helper function for object label.