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_NoFactory.hpp"
30#include "MueLu_Monitor.hpp"
31#include "MueLu_PerfUtils.hpp"
32#include "MueLu_Behavior.hpp"
34#include "Teuchos_TestForException.hpp"
35#include "Teuchos_CommHelpers.hpp"
36
37namespace MueLu {
38
40
41template <class GO, class LocalNodes>
42KOKKOS_INLINE_FUNCTION void getLocalNodeIndices(const GO localNode,
43 const LocalNodes& localNodes,
44 GO& x, GO& y, GO& z) {
45 x = localNode % localNodes[0];
46 y = (localNode / localNodes[0]) % localNodes[1];
47 z = localNode / (localNodes[0] * localNodes[1]);
48}
49
50template <class GO, class Offset, class LocalNodes, class ProcGrid, class RankData>
51KOKKOS_INLINE_FUNCTION bool resolveNeighbor(const GO x, const GO y, const GO z,
52 const Offset& offset,
53 const LocalNodes& localNodes,
54 const ProcGrid& procGrid,
55 const RankData& rankData,
56 const int myProcX,
57 const int myProcY,
58 const int myProcZ,
59 int& neighborRank,
60 GO& neighborNode) {
61 int neighborProcX = myProcX;
62 int neighborProcY = myProcY;
63 int neighborProcZ = myProcZ;
64 GO neighborX = x + static_cast<GO>(offset.x);
65 GO neighborY = y + static_cast<GO>(offset.y);
66 GO neighborZ = z + static_cast<GO>(offset.z);
67
68 if (neighborX < 0)
69 --neighborProcX;
70 else if (neighborX >= localNodes[0])
71 ++neighborProcX;
72 if (neighborY < 0)
73 --neighborProcY;
74 else if (neighborY >= localNodes[1])
75 ++neighborProcY;
76 if (neighborZ < 0)
77 --neighborProcZ;
78 else if (neighborZ >= localNodes[2])
79 ++neighborProcZ;
80
81 if (neighborProcX < 0 || neighborProcX >= procGrid[0] ||
82 neighborProcY < 0 || neighborProcY >= procGrid[1] ||
83 neighborProcZ < 0 || neighborProcZ >= procGrid[2])
84 return false;
85
86 neighborRank = neighborProcZ * procGrid[0] * procGrid[1] + neighborProcY * procGrid[0] + neighborProcX;
87 const GO neighborLocalNx = rankData(4 * neighborRank + 1);
88 const GO neighborLocalNy = rankData(4 * neighborRank + 2);
89 const GO neighborLocalNz = rankData(4 * neighborRank + 3);
90 if (neighborX < 0)
91 neighborX = neighborLocalNx - 1;
92 else if (neighborX >= localNodes[0])
93 neighborX = 0;
94 if (neighborY < 0)
95 neighborY = neighborLocalNy - 1;
96 else if (neighborY >= localNodes[1])
97 neighborY = 0;
98 if (neighborZ < 0)
99 neighborZ = neighborLocalNz - 1;
100 else if (neighborZ >= localNodes[2])
101 neighborZ = 0;
102
103 neighborNode = rankData(4 * neighborRank) +
104 neighborZ * neighborLocalNx * neighborLocalNy +
105 neighborY * neighborLocalNx + neighborX;
106 return true;
107}
108
109} // namespace StructuredRAPFactoryDetails
110
111template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
115
116template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
118 RCP<ParameterList> validParamList = rcp(new ParameterList());
119#define SET_VALID_ENTRY(name) validParamList->setEntry(name, MasterList::getEntry(name))
120 SET_VALID_ENTRY("rap: triple product"); // in the long term this has to be the only option for multiplication
121 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.
122 SET_VALID_ENTRY("rap: fix zero diagonals");
123 SET_VALID_ENTRY("rap: fix zero diagonals threshold");
124 SET_VALID_ENTRY("rap: fix zero diagonals replacement");
125 SET_VALID_ENTRY("rap: relative diagonal floor");
126#undef SET_VALID_ENTRY
127 validParamList->set<bool>(
128 "transpose: use implicit", true,
129 "Use P^T as the restriction operator. StructuredRAPFactory requires this option to be true.");
130 validParamList->set<RCP<const FactoryBase>>("A", null, "Generating factory of the matrix A used during the prolongator smoothing process");
131 validParamList->set<RCP<const FactoryBase>>("P", null, "Prolongator factory");
132 validParamList->set<RCP<const FactoryBase>>("numDimensions", null, "Number of spatial dimensions.");
133 validParamList->set<RCP<const FactoryBase>>("lNodesPerDim", null, "Local number of fine-grid nodes per spatial dimension.");
134 validParamList->set<RCP<const FactoryBase>>("lCoarseNodesPerDim", null, "Local number of coarse-grid nodes per spatial dimension.");
135 validParamList->set<RCP<const FactoryBase>>("structuredInterpolationOrder", null, "Interpolation order used to construct the structured prolongator.");
136
137 validParamList->set<bool>("CheckMainDiagonal", false, "Check main diagonal for zeros");
138 validParamList->set<bool>("RepairMainDiagonal", false, "Repair zeros on main diagonal");
139
140 // Make sure we don't recursively validate options for the matrixmatrix kernels
141 ParameterList norecurse;
142 norecurse.disableRecursiveValidation();
143 validParamList->set<ParameterList>("matrixmatrix: kernel params", norecurse, "MatrixMatrix kernel parameters");
144
145 return validParamList;
146}
147
148// Configure RAPFactory to delegate to if coarse graph prebuilding is disabled
149template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
151 const Teuchos::ParameterList& pL = GetParameterList();
152
153 ParameterList rapParams;
154 RCP<const ParameterList> validRAPParams = rapFactoryDelegate_->GetValidParameterList();
155 for (ParameterList::ConstIterator it = validRAPParams->begin(); it != validRAPParams->end(); ++it) {
156 const std::string& paramName = validRAPParams->name(it);
157 if (pL.isParameter(paramName))
158 rapParams.setEntry(paramName, pL.getEntry(paramName));
159 else if (pL.isSublist(paramName))
160 rapParams.sublist(paramName) = pL.sublist(paramName);
161 }
162
163 rapFactoryDelegate_->SetParameterList(rapParams);
164 rapFactoryDelegate_->SetFactory("A", GetFactory("A"));
165 rapFactoryDelegate_->SetFactory("P", GetFactory("P"));
166}
167
168template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
169RCP<const FactoryBase>
171 // Used to get lCoarseNodesPerDim and structuredInterpolationOrder from the prolongator factory
172 const std::string& varName) const {
173 const RCP<const Factory> pFactory =
174 rcp_dynamic_cast<const Factory>(GetFactory("P"));
175 if (!pFactory.is_null()) {
176 const RCP<const FactoryBase> metadataFactory = pFactory->GetFactory(varName);
177 if (!metadataFactory.is_null())
178 return metadataFactory;
179 }
180
181 return GetFactory(varName);
182}
183
184template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
186 const Teuchos::ParameterList& pL = GetParameterList();
187 TEUCHOS_TEST_FOR_EXCEPTION(
188 !pL.get<bool>("transpose: use implicit"), Exceptions::RuntimeError,
189 "StructuredRAPFactory requires \"transpose: use implicit\" = true because "
190 "the prebuilt coarse graph assumes the Galerkin product P^T A P.");
191
192 const bool prebuildCoarseGraph = pL.get<bool>("rap: prebuild coarse graph");
193 const bool useRAPDelegate = !prebuildCoarseGraph;
194
195 if (useRAPDelegate) {
197 coarseLevel.DeclareInput("A", rapFactoryDelegate_.get(), this);
198 coarseLevel.DeclareInput("RAP reuse data", rapFactoryDelegate_.get(), this);
199 hasDeclaredInput_ = true;
200 return;
201 }
202
203 Input(fineLevel, "A");
204 Input(coarseLevel, "P");
205
206 if (prebuildCoarseGraph) {
207 const RCP<const FactoryBase> coarseDimensionsFactory =
208 GetStructuredMetadataFactory("lCoarseNodesPerDim");
209 const RCP<const FactoryBase> interpolationOrderFactory =
210 GetStructuredMetadataFactory("structuredInterpolationOrder");
211 fineLevel.DeclareInput("lCoarseNodesPerDim", coarseDimensionsFactory.get(), this);
212 fineLevel.DeclareInput("structuredInterpolationOrder", interpolationOrderFactory.get(), this);
213 if (fineLevel.GetLevelID() == 0) {
214 TEUCHOS_TEST_FOR_EXCEPTION(!fineLevel.IsAvailable("numDimensions", NoFactory::get()),
216 "StructuredRAPFactory: numDimensions was not provided on level 0.");
217 TEUCHOS_TEST_FOR_EXCEPTION(!fineLevel.IsAvailable("lNodesPerDim", NoFactory::get()),
219 "StructuredRAPFactory: lNodesPerDim was not provided on level 0.");
220 fineLevel.DeclareInput("numDimensions", NoFactory::get(), this);
221 fineLevel.DeclareInput("lNodesPerDim", NoFactory::get(), this);
222 } else {
223 Input(fineLevel, "numDimensions");
224 Input(fineLevel, "lNodesPerDim");
225 }
226 }
227
228 // call DeclareInput of all user-given transfer factories
229 for (std::vector<RCP<const FactoryBase>>::const_iterator it = transferFacts_.begin(); it != transferFacts_.end(); ++it)
230 (*it)->CallDeclareInput(coarseLevel);
231
232 hasDeclaredInput_ = true;
233}
234
235// Infer the fine-grid stencil of A from one representative interior node
236template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
239 const Matrix& A, const int numDimensions,
240 const Teuchos::Array<LocalOrdinal>& lFineNodesPerDim) const {
241 const std::string prefix = "StructuredRAPFactory::DetectFineStencil: ";
242
243 TEUCHOS_TEST_FOR_EXCEPTION(numDimensions < 1 || numDimensions > 3, Exceptions::RuntimeError,
244 prefix << "the number of dimensions must be between one and three.");
245 TEUCHOS_TEST_FOR_EXCEPTION(lFineNodesPerDim.size() < numDimensions, Exceptions::RuntimeError,
246 prefix << "insufficient fine-grid dimensions were supplied.");
247
248 const auto rowMap = A.getRowMap();
249 const auto colMap = A.getColMap();
250 const auto comm = rowMap->getComm();
251 const auto graph = A.getCrsGraph();
252 TEUCHOS_TEST_FOR_EXCEPTION(graph.is_null(), Exceptions::RuntimeError,
253 prefix << "A does not expose a CRS graph.");
254
255 const LO dofsPerNode = Teuchos::as<LO>(A.GetFixedBlockSize());
256 TEUCHOS_TEST_FOR_EXCEPTION(dofsPerNode <= 0, Exceptions::RuntimeError,
257 prefix << "A must have a positive fixed block size.");
258
259 Kokkos::Array<LO, 3> fineNodes{{LO(1), LO(1), LO(1)}};
260 Kokkos::Array<LO, 3> interior{{LO(0), LO(0), LO(0)}};
261 size_t numFineNodes = 1;
262 bool hasLocalInterior = true;
263 // Select interior node in the local subdomain as a representative node for detecting the fine-grid stencil
264 for (int dim = 0; dim < numDimensions; ++dim) {
265 fineNodes[dim] = lFineNodesPerDim[dim];
266 hasLocalInterior = hasLocalInterior && fineNodes[dim] >= LO(3);
267 if (fineNodes[dim] > LO(0)) {
268 interior[dim] = fineNodes[dim] / LO(2);
269 numFineNodes *= Teuchos::as<size_t>(fineNodes[dim]);
270 } else {
271 numFineNodes = 0;
272 }
273 }
274
275 const bool localDimensionsMatch =
276 rowMap->getLocalNumElements() ==
277 numFineNodes * Teuchos::as<size_t>(dofsPerNode);
278 int invalidLocalDimensions = localDimensionsMatch ? 0 : 1;
279 int invalidDimensions = 0;
280 Teuchos::reduceAll(*comm, Teuchos::REDUCE_MAX, 1,
281 &invalidLocalDimensions, &invalidDimensions);
282 TEUCHOS_TEST_FOR_EXCEPTION(
283 invalidDimensions != 0, Exceptions::RuntimeError,
284 prefix << "the structured local dimensions and fixed block size do not match A on every rank.");
285
286 const LO interiorNode = interior[2] * fineNodes[0] * fineNodes[1] +
287 interior[1] * fineNodes[0] + interior[0];
288
289 bool hasCompleteLocalStencil = hasLocalInterior;
290 // Every rank checks its candidate interior node which can be used to detect the fine-grid stencil
291 if (hasCompleteLocalStencil) {
292 for (LO rowDof = 0; rowDof < dofsPerNode && hasCompleteLocalStencil; ++rowDof) {
293 const LO row = interiorNode * dofsPerNode + rowDof;
294 Teuchos::ArrayView<const LO> columns;
295 graph->getLocalRowView(row, columns);
296 if (columns.size() == 0) {
297 hasCompleteLocalStencil = false;
298 break;
299 }
300 for (int entry = 0; entry < columns.size(); ++entry) {
301 const GO globalColumn = colMap->getGlobalElement(columns[entry]);
302 if (!rowMap->isNodeGlobalElement(globalColumn)) {
303 hasCompleteLocalStencil = false;
304 break;
305 }
306 }
307 }
308 }
309
310 const int myRank = comm->getRank();
311 const int numRanks = comm->getSize();
312 const int candidateRank = hasCompleteLocalStencil ? myRank : numRanks;
313 int detectorRank = numRanks;
314 Teuchos::reduceAll(*comm, Teuchos::REDUCE_MIN, 1, &candidateRank, &detectorRank);
315 TEUCHOS_TEST_FOR_EXCEPTION(
316 detectorRank == numRanks, Exceptions::RuntimeError,
317 prefix << "no MPI rank owns a local interior node whose complete stencil is locally owned; "
318 << "increase the local structured subdomain size.");
319
320 FineStencilSpec stencil;
321 stencil.numDimensions = numDimensions;
322 stencil.dofsPerNode = dofsPerNode;
323
324 // Only the elected rank performs detection
325 if (myRank == detectorRank) {
326 for (LO rowDof = 0; rowDof < dofsPerNode; ++rowDof) {
327 const LO row = interiorNode * dofsPerNode + rowDof;
328 Teuchos::ArrayView<const LO> columns;
329 graph->getLocalRowView(row, columns);
330
331 for (int entry = 0; entry < columns.size(); ++entry) {
332 const GO globalColumn = colMap->getGlobalElement(columns[entry]);
333 const LO ownedColumn = rowMap->getLocalElement(globalColumn);
334 TEUCHOS_TEST_FOR_EXCEPTION(
335 ownedColumn == Teuchos::OrdinalTraits<LO>::invalid(), Exceptions::RuntimeError,
336 prefix << "the selected detector row contains a nonowned column.");
337
338 const LO columnNode = ownedColumn / dofsPerNode;
339 const LO columnDof = ownedColumn % dofsPerNode;
340 LO columnX, columnY, columnZ;
342 columnNode, fineNodes, columnX, columnY, columnZ);
343 const StencilOffset offset{
344 Teuchos::as<int>(columnX - interior[0]),
345 Teuchos::as<int>(columnY - interior[1]),
346 Teuchos::as<int>(columnZ - interior[2])};
347
348 bool knownOffset = false;
349 for (const StencilOffset& known : stencil.stencilOffsets) {
350 if (known.x == offset.x && known.y == offset.y && known.z == offset.z) {
351 knownOffset = true;
352 break;
353 }
354 }
355 if (!knownOffset)
356 stencil.stencilOffsets.push_back(offset);
357
358 stencil.entries.push_back(FineStencilEntry{
359 offset, rowDof, columnDof, Teuchos::as<LO>(entry)});
360 }
361 }
362 }
363
364 int counts[2] = {Teuchos::as<int>(stencil.stencilOffsets.size()),
365 Teuchos::as<int>(stencil.entries.size())};
366 Teuchos::broadcast<int, int>(*comm, detectorRank, 2, counts);
367
368 Teuchos::Array<int> offsetData(3 * counts[0]);
369 Teuchos::Array<int> entryOffsetData(3 * counts[1]);
370 Teuchos::Array<LO> entryData(3 * counts[1]);
371 if (myRank == detectorRank) {
372 for (int offset = 0; offset < counts[0]; ++offset) {
373 offsetData[3 * offset] = stencil.stencilOffsets[offset].x;
374 offsetData[3 * offset + 1] = stencil.stencilOffsets[offset].y;
375 offsetData[3 * offset + 2] = stencil.stencilOffsets[offset].z;
376 }
377 for (int entry = 0; entry < counts[1]; ++entry) {
378 entryOffsetData[3 * entry] = stencil.entries[entry].offset.x;
379 entryOffsetData[3 * entry + 1] = stencil.entries[entry].offset.y;
380 entryOffsetData[3 * entry + 2] = stencil.entries[entry].offset.z;
381 entryData[3 * entry] = stencil.entries[entry].rowDof;
382 entryData[3 * entry + 1] = stencil.entries[entry].columnDof;
383 entryData[3 * entry + 2] = stencil.entries[entry].entryOrdinal;
384 }
385 }
386
387 if (counts[0] > 0)
388 Teuchos::broadcast<int, int>(*comm, detectorRank, 3 * counts[0],
389 offsetData.getRawPtr());
390 if (counts[1] > 0) {
391 Teuchos::broadcast<int, int>(*comm, detectorRank, 3 * counts[1],
392 entryOffsetData.getRawPtr());
393 Teuchos::broadcast<int, LO>(*comm, detectorRank, 3 * counts[1],
394 entryData.getRawPtr());
395 }
396
397 if (myRank != detectorRank) {
398 stencil.stencilOffsets.reserve(counts[0]);
399 for (int offset = 0; offset < counts[0]; ++offset)
400 stencil.stencilOffsets.push_back(StencilOffset{
401 offsetData[3 * offset], offsetData[3 * offset + 1],
402 offsetData[3 * offset + 2]});
403
404 stencil.entries.reserve(counts[1]);
405 for (int entry = 0; entry < counts[1]; ++entry)
406 stencil.entries.push_back(FineStencilEntry{
407 StencilOffset{entryOffsetData[3 * entry],
408 entryOffsetData[3 * entry + 1],
409 entryOffsetData[3 * entry + 2]},
410 entryData[3 * entry], entryData[3 * entry + 1],
411 entryData[3 * entry + 2]});
412 }
413
414 TEUCHOS_TEST_FOR_EXCEPTION(stencil.stencilOffsets.empty(), Exceptions::RuntimeError,
415 prefix << "the representative interior rows are empty.");
416 return stencil;
417}
418
419template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
420typename StructuredRAPFactory<Scalar, LocalOrdinal, GlobalOrdinal, Node>::StructuredGraphSpec
422 const FineStencilSpec& fineStencil, const int interpolationOrder) const {
423 const std::string prefix = "StructuredRAPFactory::DeriveCoarseRAPStencil: ";
424
425 TEUCHOS_TEST_FOR_EXCEPTION(fineStencil.numDimensions < 1 || fineStencil.numDimensions > 3,
427 prefix << "the number of dimensions must be between one and three.");
428 TEUCHOS_TEST_FOR_EXCEPTION(fineStencil.dofsPerNode <= 0, Exceptions::RuntimeError,
429 prefix << "dofsPerNode must be positive.");
430 TEUCHOS_TEST_FOR_EXCEPTION(fineStencil.stencilOffsets.empty(), Exceptions::RuntimeError,
431 prefix << "the fine-grid stencil is empty.");
432 TEUCHOS_TEST_FOR_EXCEPTION(interpolationOrder < 0 || interpolationOrder > 1,
434 prefix << "interpolation order " << interpolationOrder
435 << " is not supported; expected zero or one.");
436 for (const StencilOffset& offset : fineStencil.stencilOffsets) {
437 TEUCHOS_TEST_FOR_EXCEPTION(
438 offset.x < -1 || offset.x > 1 ||
439 offset.y < -1 || offset.y > 1 ||
440 offset.z < -1 || offset.z > 1,
442 prefix << "fine-grid stencil offset (" << offset.x << ", "
443 << offset.y << ", " << offset.z
444 << ") exceeds the supported radius one; disable "
445 "\"rap: prebuild coarse graph\" for wider stencils.");
446 }
447
448 StructuredGraphSpec coarseStencil;
449 coarseStencil.numDimensions = fineStencil.numDimensions;
450 coarseStencil.dofsPerNode = fineStencil.dofsPerNode;
451
452 if (interpolationOrder == 0) {
453 coarseStencil.stencilOffsets = fineStencil.stencilOffsets;
454 bool hasCenter = false;
455 for (const StencilOffset& offset : coarseStencil.stencilOffsets)
456 hasCenter = hasCenter || (offset.x == 0 && offset.y == 0 && offset.z == 0);
457 if (!hasCenter)
458 coarseStencil.stencilOffsets.push_back(StencilOffset{0, 0, 0});
459 } else {
460 // The supports of neighboring piecewise-linear basis functions overlap.
461 // For a radius-one fine stencil this produces the full radius-one coarse
462 // stencil in every active dimension.
463 const int minY = fineStencil.numDimensions > 1 ? -1 : 0;
464 const int maxY = fineStencil.numDimensions > 1 ? 1 : 0;
465 const int minZ = fineStencil.numDimensions > 2 ? -1 : 0;
466 const int maxZ = fineStencil.numDimensions > 2 ? 1 : 0;
467 for (int z = minZ; z <= maxZ; ++z)
468 for (int y = minY; y <= maxY; ++y)
469 for (int x = -1; x <= 1; ++x)
470 coarseStencil.stencilOffsets.push_back(StencilOffset{x, y, z});
471 }
472
473 int minimum[3] = {coarseStencil.stencilOffsets[0].x,
474 coarseStencil.stencilOffsets[0].y,
475 coarseStencil.stencilOffsets[0].z};
476 int maximum[3] = {minimum[0], minimum[1], minimum[2]};
477 for (const StencilOffset& offset : coarseStencil.stencilOffsets) {
478 const int components[3] = {offset.x, offset.y, offset.z};
479 for (int dim = 0; dim < fineStencil.numDimensions; ++dim) {
480 if (components[dim] < minimum[dim])
481 minimum[dim] = components[dim];
482 if (components[dim] > maximum[dim])
483 maximum[dim] = components[dim];
484 }
485 }
486
487 std::ostringstream description;
488 for (int dim = 0; dim < fineStencil.numDimensions; ++dim) {
489 if (dim != 0)
490 description << "x";
491 description << maximum[dim] - minimum[dim] + 1;
492 }
493 coarseStencil.description = description.str();
494 return coarseStencil;
495}
496
497// Detect the fine-grid stencil and derive the corresponding coarse RAP stencil.
498template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
501 const Matrix& A, const Matrix& P, const int numDimensions,
502 const Teuchos::Array<LocalOrdinal>& lFineNodesPerDim,
503 const int interpolationOrder) const {
504 const FineStencilSpec fineStencil =
505 DetectFineStencil(A, numDimensions, lFineNodesPerDim);
506
507 Teuchos::FancyOStream& out = GetOStream(Runtime0);
508 out << "StructuredRAP: Detected "
509 << fineStencil.numDimensions << "D fine stencil with "
510 << fineStencil.dofsPerNode << " DOF(s) per node and "
511 << fineStencil.stencilOffsets.size() << " nodal offset(s)";
512 out << std::endl;
513
514 const StructuredGraphSpec coarseStencil =
515 DeriveCoarseRAPStencil(fineStencil, interpolationOrder);
516 out << "StructuredRAP: Deduced " << coarseStencil.description
517 << " coarse stencil with " << coarseStencil.stencilOffsets.size()
518 << " nodal offset(s)";
519 out << std::endl;
520
521 return coarseStencil;
522}
523
524// Prebuild sparsity structure of coarse matrix
525template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
527 RCP<Matrix>& Ac, RCP<Matrix> P,
528 const Teuchos::Array<LocalOrdinal>& lCoarseNodesPerDim,
529 const StructuredGraphSpec& graphSpec) const {
530 using local_graph_type = typename CrsGraph::local_graph_type;
531 using row_map_type = typename local_graph_type::row_map_type::non_const_type;
532 using entries_type = typename local_graph_type::entries_type::non_const_type;
533 using device_type = typename Node::device_type;
534 using execution_space = typename device_type::execution_space;
535 using range_policy = Kokkos::RangePolicy<execution_space, Kokkos::IndexType<size_t>>;
536
537 TEUCHOS_TEST_FOR_EXCEPTION(graphSpec.numDimensions < 1 || graphSpec.numDimensions > 3, Exceptions::RuntimeError,
538 "StructuredRAPFactory::GetStructuredGraph(" << graphSpec.description
539 << "): number of dimensions must be between one and three.");
540 TEUCHOS_TEST_FOR_EXCEPTION(lCoarseNodesPerDim.size() < graphSpec.numDimensions, Exceptions::RuntimeError,
541 "StructuredRAPFactory::GetStructuredGraph(" << graphSpec.description
542 << "): insufficient local coarse-grid dimensions.");
543 TEUCHOS_TEST_FOR_EXCEPTION(graphSpec.dofsPerNode <= 0, Exceptions::RuntimeError,
544 "StructuredRAPFactory::GetStructuredGraph(" << graphSpec.description
545 << "): dofsPerNode must be positive.");
546 TEUCHOS_TEST_FOR_EXCEPTION(graphSpec.stencilOffsets.empty(), Exceptions::RuntimeError,
547 "StructuredRAPFactory::GetStructuredGraph(" << graphSpec.description
548 << "): coarse-grid stencil is empty; it must define at least one relative node position (dx, dy, dz).");
549 constexpr size_t maxSupportedStencilSize = 27;
550 constexpr size_t maxSupportedDofsPerNode = 3;
551 TEUCHOS_TEST_FOR_EXCEPTION(graphSpec.stencilOffsets.size() > maxSupportedStencilSize, Exceptions::RuntimeError,
552 "StructuredRAPFactory::GetStructuredGraph(" << graphSpec.description
553 << "): a radius-one stencil may contain at most 27 entries.");
554 TEUCHOS_TEST_FOR_EXCEPTION(Teuchos::as<size_t>(graphSpec.dofsPerNode) > maxSupportedDofsPerNode, Exceptions::RuntimeError,
555 "StructuredRAPFactory::GetStructuredGraph(" << graphSpec.description
556 << "): at most three DOFs per node are supported.");
557
558 Kokkos::Array<GO, 3> localNodes{};
559 for (int dim = 0; dim < 3; ++dim)
560 localNodes[dim] = Teuchos::as<GO>(1);
561 for (int dim = 0; dim < graphSpec.numDimensions; ++dim) {
562 localNodes[dim] = Teuchos::as<GO>(lCoarseNodesPerDim[dim]);
563 TEUCHOS_TEST_FOR_EXCEPTION(localNodes[dim] <= 0, Exceptions::RuntimeError,
564 "StructuredRAPFactory::GetStructuredGraph(" << graphSpec.description
565 << "): local coarse dimensions must be positive.");
566 }
567
568 Kokkos::Array<StencilOffset, maxSupportedStencilSize> stencilOffsets{};
569 Kokkos::Array<GO, maxSupportedStencilSize> localStencilNodeOffsets{};
570 for (size_t stencil = 0; stencil < graphSpec.stencilOffsets.size(); ++stencil) {
571 const StencilOffset& offset = graphSpec.stencilOffsets[stencil];
572 TEUCHOS_TEST_FOR_EXCEPTION(offset.x < -1 || offset.x > 1 ||
573 offset.y < -1 || offset.y > 1 ||
574 offset.z < -1 || offset.z > 1,
576 "StructuredRAPFactory::GetStructuredGraph(" << graphSpec.description
577 << "): only radius-one stencil offsets are currently supported.");
578 TEUCHOS_TEST_FOR_EXCEPTION((graphSpec.numDimensions < 2 && offset.y != 0) ||
579 (graphSpec.numDimensions < 3 && offset.z != 0),
581 "StructuredRAPFactory::GetStructuredGraph(" << graphSpec.description
582 << "): stencil contains an offset in an inactive dimension.");
583 stencilOffsets[stencil] = offset;
584 localStencilNodeOffsets[stencil] =
585 static_cast<GO>(offset.x) + static_cast<GO>(offset.y) * localNodes[0] +
586 static_cast<GO>(offset.z) * localNodes[0] * localNodes[1];
587 }
588
589 RCP<ParameterList> paramList = rcp(new ParameterList);
590 paramList->set("No Nonlocal Changes", true);
591 paramList->set("Optimize Storage", true);
592 paramList->set("compute global constants", true);
593
594 // Columns of P represent coarse-grid nodes
595 auto rowMap = P->getDomainMap();
596 const size_t localNumRows = rowMap->getLocalNumElements();
597 const LO dofsPerNode = graphSpec.dofsPerNode;
598 const GO dofsPerNodeGO = Teuchos::as<GO>(dofsPerNode);
599 const size_t rowsPerNode = Teuchos::as<size_t>(dofsPerNode);
600 const GO localNumNodesGO = localNodes[0] * localNodes[1] * localNodes[2];
601 const size_t localNumNodes = Teuchos::as<size_t>(localNumNodesGO);
602 const GO expectedLocalRows = localNumNodesGO * dofsPerNodeGO;
603 TEUCHOS_TEST_FOR_EXCEPTION(Teuchos::as<GO>(localNumRows) != expectedLocalRows, Exceptions::RuntimeError,
604 "StructuredRAPFactory::GetStructuredGraph(" << graphSpec.description
605 << "): local coarse dimensions with " << graphSpec.dofsPerNode
606 << " dofs per node do not match local coarse row count "
607 << localNumRows << ".");
608 TEUCHOS_TEST_FOR_EXCEPTION(rowMap->lib() != Xpetra::UseTpetra, Exceptions::RuntimeError,
609 "StructuredRAPFactory::GetStructuredGraph requires the Tpetra backend.");
610
611 const GO numGlobalRows = Teuchos::as<GO>(rowMap->getGlobalNumElements());
612 TEUCHOS_TEST_FOR_EXCEPTION(numGlobalRows % dofsPerNodeGO != 0, Exceptions::RuntimeError,
613 "StructuredRAPFactory::GetStructuredGraph(" << graphSpec.description
614 << "): global coarse row count is not divisible by dofsPerNode.");
615 const GO numGlobalNodes = numGlobalRows / dofsPerNodeGO;
616 const GO globalMinGid = rowMap->getMinAllGlobalIndex();
617 const GO globalMaxGid = rowMap->getMaxAllGlobalIndex();
618 TEUCHOS_TEST_FOR_EXCEPTION(globalMaxGid - globalMinGid + 1 != numGlobalRows, Exceptions::RuntimeError,
619 "StructuredRAPFactory::GetStructuredGraph(" << graphSpec.description
620 << "): coarse row-map GIDs must form a contiguous range.");
621
622 RCP<const Teuchos::Comm<int>> comm = rowMap->getComm();
623 const int myRank = comm->getRank();
624 const int numRanks = comm->getSize();
625 const GO localMinGid = rowMap->getMinGlobalIndex();
626 TEUCHOS_TEST_FOR_EXCEPTION((localMinGid - globalMinGid) % dofsPerNodeGO != 0, Exceptions::RuntimeError,
627 "StructuredRAPFactory::GetStructuredGraph(" << graphSpec.description
628 << "): local row range does not begin at a nodal boundary.");
629 const GO firstLocalNode = (localMinGid - globalMinGid) / dofsPerNodeGO;
630
631 Teuchos::Array<GO> localRankData(4);
632 localRankData[0] = firstLocalNode;
633 localRankData[1] = localNodes[0];
634 localRankData[2] = localNodes[1];
635 localRankData[3] = localNodes[2];
636 Teuchos::Array<GO> rankData(4 * numRanks);
637 {
638 // For RankData on every rank
639 Teuchos::gatherAll(*comm, 4, localRankData.getRawPtr(), 4 * numRanks, rankData.getRawPtr());
640 }
641
642 // Get mx, my and mz (number of ranks per dimension) like they are defined in Galeri (compare Galeri_XpetraMaps_def.hpp)
643 Kokkos::Array<int, 3> procGrid{};
644 for (int dim = 0; dim < 3; ++dim)
645 procGrid[dim] = 1;
646 if (graphSpec.numDimensions == 1) {
647 procGrid[0] = numRanks;
648 } else if (graphSpec.numDimensions == 2) {
649 while ((procGrid[0] + 1) * (procGrid[0] + 1) <= numRanks)
650 ++procGrid[0];
651 procGrid[1] = numRanks / procGrid[0];
652 while (procGrid[0] * procGrid[1] != numRanks)
653 procGrid[1] = numRanks / (--procGrid[0]);
654 } else {
655 int cubeRootNumRanks = 1;
656 while ((cubeRootNumRanks + 1) * (cubeRootNumRanks + 1) * (cubeRootNumRanks + 1) <= numRanks)
657 ++cubeRootNumRanks;
658 procGrid[0] = cubeRootNumRanks;
659 procGrid[1] = cubeRootNumRanks;
660 procGrid[2] = cubeRootNumRanks;
661
662 if (procGrid[0] * procGrid[1] * procGrid[2] != numRanks) {
663 procGrid[0] = 1;
664 procGrid[1] = 1;
665 procGrid[2] = 1;
666
667 int procTemp = numRanks;
668 // Keep this factorization limit synchronized with Galeri_XpetraMaps_def.hpp.
669 const int galeriFactorSearchLimit = 50;
670 int factors[galeriFactorSearchLimit] = {};
671 for (int factor = 2; factor < galeriFactorSearchLimit; ++factor) {
672 while (procTemp % factor == 0) {
673 ++factors[factor];
674 procTemp /= factor;
675 }
676 }
677 procGrid[0] = procTemp;
678 for (int factor = galeriFactorSearchLimit - 1; factor > 0; --factor) {
679 while (factors[factor] != 0) {
680 if (procGrid[0] <= procGrid[1] && procGrid[0] <= procGrid[2])
681 procGrid[0] *= factor;
682 else if (procGrid[1] <= procGrid[0] && procGrid[1] <= procGrid[2])
683 procGrid[1] *= factor;
684 else
685 procGrid[2] *= factor;
686 --factors[factor];
687 }
688 }
689 }
690 }
691
692 const int procXY = procGrid[0] * procGrid[1];
693 TEUCHOS_TEST_FOR_EXCEPTION(procGrid[0] <= 0 || procGrid[1] <= 0 || procGrid[2] <= 0 ||
694 procXY * procGrid[2] != numRanks,
696 "StructuredRAPFactory::GetStructuredGraph(" << graphSpec.description
697 << "): invalid inferred processor grid "
698 << procGrid[0] << "x" << procGrid[1] << "x" << procGrid[2]
699 << " for " << numRanks << " ranks.");
700
701 const int myProcX = myRank % procGrid[0];
702 const int myProcY = (myRank % procXY) / procGrid[0];
703 const int myProcZ = myRank / procXY;
704
705 Kokkos::Array<GO, 3> globalNodes{};
706 for (int dim = 0; dim < 3; ++dim)
707 globalNodes[dim] = Teuchos::as<GO>(0);
708 for (int px = 0; px < procGrid[0]; ++px)
709 globalNodes[0] += rankData[4 * px + 1];
710 for (int py = 0; py < procGrid[1]; ++py)
711 globalNodes[1] += rankData[4 * (py * procGrid[0]) + 2];
712 for (int pz = 0; pz < procGrid[2]; ++pz)
713 globalNodes[2] += rankData[4 * (pz * procXY) + 3];
714 TEUCHOS_TEST_FOR_EXCEPTION(globalNodes[0] * globalNodes[1] * globalNodes[2] != numGlobalNodes,
716 "StructuredRAPFactory::GetStructuredGraph(" << graphSpec.description
717 << "): processor-grid coarse dimensions do not match coarse node count.");
718
719 execution_space executionSpace;
720 Kokkos::View<GO*, device_type> rankDataDevice(
721 Kokkos::ViewAllocateWithoutInitializing("StructuredRAP: rank metadata"), rankData.size());
722 auto rankDataHost = Kokkos::create_mirror_view(rankDataDevice);
723 for (int entry = 0; entry < rankData.size(); ++entry)
724 rankDataHost(entry) = rankData[entry];
725 Kokkos::deep_copy(executionSpace, rankDataDevice, rankDataHost);
726
727 const size_t stencilSize = graphSpec.stencilOffsets.size();
728 const int numDimensions = graphSpec.numDimensions;
729 const auto rowLocalMap = rowMap->getLocalMap();
730
731 const bool debug = Behavior::debug();
732 if (debug) {
733 size_t invalidLocalRows = 0;
734 Kokkos::parallel_reduce(
735 "StructuredRAP: validate local rows", range_policy(executionSpace, 0, localNumRows),
736 KOKKOS_LAMBDA(const size_t rowLid, size_t& invalid) {
737 const GO rowGid = rowLocalMap.getGlobalElement(static_cast<LO>(rowLid));
738 const GO expectedRowGid = localMinGid + static_cast<GO>(rowLid);
739 if (rowGid != expectedRowGid)
740 ++invalid;
741 },
742 invalidLocalRows);
743 TEUCHOS_TEST_FOR_EXCEPTION(invalidLocalRows != 0, Exceptions::RuntimeError,
744 "StructuredRAPFactory::GetStructuredGraph(" << graphSpec.description
745 << "): coarse row-map GIDs are not locally contiguous and ordered.");
746 }
747
748 // A radius-one stencil reaches disjoint faces, edges, and corners of at most
749 // 26 neighboring ranks, so generate each remote column exactly once.
750 constexpr size_t maxNeighborRegions = 26;
751 Kokkos::Array<int, maxNeighborRegions> remoteRegionRanks{};
752 Kokkos::Array<StencilOffset, maxNeighborRegions> remoteRegionDirections{};
753 Kokkos::Array<GO, maxNeighborRegions> remoteRegionFirstNodes{};
754 Kokkos::Array<GO, maxNeighborRegions> remoteRegionNx{};
755 Kokkos::Array<GO, maxNeighborRegions> remoteRegionNy{};
756 Kokkos::Array<GO, maxNeighborRegions> remoteRegionNz{};
757 Kokkos::Array<size_t, maxNeighborRegions + 1> remoteRegionOffsets{};
758 size_t numRemoteRegions = 0;
759
760 const int minProcY = graphSpec.numDimensions > 1 ? -1 : 0;
761 const int maxProcY = graphSpec.numDimensions > 1 ? 1 : 0;
762 const int minProcZ = graphSpec.numDimensions > 2 ? -1 : 0;
763 const int maxProcZ = graphSpec.numDimensions > 2 ? 1 : 0;
764 for (int procDz = minProcZ; procDz <= maxProcZ; ++procDz) {
765 for (int procDy = minProcY; procDy <= maxProcY; ++procDy) {
766 for (int procDx = -1; procDx <= 1; ++procDx) {
767 if (procDx == 0 && procDy == 0 && procDz == 0)
768 continue;
769
770 bool stencilReachesRegion = false;
771 for (size_t stencil = 0; stencil < stencilSize; ++stencil) {
772 const StencilOffset& offset = stencilOffsets[stencil];
773 const bool reachesX = procDx == 0 || offset.x == procDx;
774 const bool reachesY = procDy == 0 || offset.y == procDy;
775 const bool reachesZ = procDz == 0 || offset.z == procDz;
776 if (reachesX && reachesY && reachesZ) {
777 stencilReachesRegion = true;
778 break;
779 }
780 }
781 if (!stencilReachesRegion)
782 continue;
783
784 const int neighborProcX = myProcX + procDx;
785 const int neighborProcY = myProcY + procDy;
786 const int neighborProcZ = myProcZ + procDz;
787 if (neighborProcX < 0 || neighborProcX >= procGrid[0] ||
788 neighborProcY < 0 || neighborProcY >= procGrid[1] ||
789 neighborProcZ < 0 || neighborProcZ >= procGrid[2])
790 continue;
791
792 const int neighborRank = neighborProcZ * procXY + neighborProcY * procGrid[0] + neighborProcX;
793 const GO neighborNx = rankData[4 * neighborRank + 1];
794 const GO neighborNy = rankData[4 * neighborRank + 2];
795 const GO neighborNz = rankData[4 * neighborRank + 3];
796 const size_t regionNodes = Teuchos::as<size_t>((procDx == 0 ? neighborNx : 1) *
797 (procDy == 0 ? neighborNy : 1) *
798 (procDz == 0 ? neighborNz : 1));
799
800 remoteRegionRanks[numRemoteRegions] = neighborRank;
801 remoteRegionDirections[numRemoteRegions] = StencilOffset{procDx, procDy, procDz};
802 remoteRegionFirstNodes[numRemoteRegions] = rankData[4 * neighborRank];
803 remoteRegionNx[numRemoteRegions] = neighborNx;
804 remoteRegionNy[numRemoteRegions] = neighborNy;
805 remoteRegionNz[numRemoteRegions] = neighborNz;
806 remoteRegionOffsets[numRemoteRegions + 1] =
807 remoteRegionOffsets[numRemoteRegions] + regionNodes * rowsPerNode;
808 ++numRemoteRegions;
809 }
810 }
811 }
812 const size_t numRemoteColumns = remoteRegionOffsets[numRemoteRegions];
813
814 Kokkos::View<GO*, device_type> colMapGids(
815 Kokkos::ViewAllocateWithoutInitializing("StructuredRAP: column map GIDs"),
816 localNumRows + numRemoteColumns);
817 const auto localRowGidsDevice = rowMap->getMyGlobalIndicesDevice();
818 Kokkos::parallel_for(
819 "StructuredRAP: fill column map", range_policy(executionSpace, 0, localNumRows + numRemoteColumns),
820 KOKKOS_LAMBDA(const size_t entry) {
821 if (entry < localNumRows) {
822 colMapGids(entry) = localRowGidsDevice(entry);
823 return;
824 }
825
826 const size_t remoteEntry = entry - localNumRows;
827 size_t region = 0;
828 while (remoteEntry >= remoteRegionOffsets[region + 1])
829 ++region;
830
831 const StencilOffset direction = remoteRegionDirections[region];
832 const GO neighborNx = remoteRegionNx[region];
833 const GO neighborNy = remoteRegionNy[region];
834 const GO neighborNz = remoteRegionNz[region];
835 size_t regionEntry = remoteEntry - remoteRegionOffsets[region];
836 const GO dof = static_cast<GO>(regionEntry % rowsPerNode);
837 size_t regionNode = regionEntry / rowsPerNode;
838
839 GO neighborX = direction.x < 0 ? neighborNx - 1 : 0;
840 GO neighborY = direction.y < 0 ? neighborNy - 1 : 0;
841 GO neighborZ = direction.z < 0 ? neighborNz - 1 : 0;
842 if (direction.x == 0) {
843 neighborX = static_cast<GO>(regionNode % static_cast<size_t>(neighborNx));
844 regionNode /= static_cast<size_t>(neighborNx);
845 }
846 if (direction.y == 0) {
847 neighborY = static_cast<GO>(regionNode % static_cast<size_t>(neighborNy));
848 regionNode /= static_cast<size_t>(neighborNy);
849 }
850 if (direction.z == 0)
851 neighborZ = static_cast<GO>(regionNode);
852
853 const GO neighborNode = remoteRegionFirstNodes[region] +
854 neighborZ * neighborNx * neighborNy + neighborY * neighborNx + neighborX;
855 colMapGids(entry) = globalMinGid + neighborNode * dofsPerNodeGO + dof;
856 });
857 executionSpace.fence("StructuredRAP: column map GIDs ready");
858
859 typename Map::global_indices_array_device_type constColMapGids = colMapGids;
860 // colMap is filled here
861 RCP<const Map> colMap;
862 {
863 colMap = MapFactory::Build(
864 rowMap->lib(), Teuchos::OrdinalTraits<Xpetra::global_size_t>::invalid(),
865 constColMapGids, rowMap->getIndexBase(), comm);
866 }
867
868 // Loop to fill rowptr
869 row_map_type rowptr;
870 size_t localNnz = 0;
871 {
872 rowptr = row_map_type(
873 Kokkos::ViewAllocateWithoutInitializing("StructuredRAP: graph row pointers"), localNumRows + 1);
874 Kokkos::parallel_scan(
875 "StructuredRAP: compute graph row pointers", range_policy(executionSpace, 0, localNumNodes + 1),
876 KOKKOS_LAMBDA(const size_t localNodeIndex, size_t& update, const bool final) {
877 if (localNodeIndex == localNumNodes) {
878 if (final)
879 rowptr(localNumRows) = update;
880 return;
881 }
882
883 const GO localNode = static_cast<GO>(localNodeIndex);
884 GO x = 0, y = 0, z = 0;
885 StructuredRAPFactoryDetails::getLocalNodeIndices(localNode, localNodes, x, y, z);
886 const bool isInterior =
887 x > 0 && x + 1 < localNodes[0] &&
888 (numDimensions < 2 || (y > 0 && y + 1 < localNodes[1])) &&
889 (numDimensions < 3 || (z > 0 && z + 1 < localNodes[2]));
890 size_t rowLength = isInterior ? stencilSize * rowsPerNode : 0;
891 if (!isInterior) {
892 for (size_t stencil = 0; stencil < stencilSize; ++stencil) {
893 int neighborRank = myRank;
894 GO neighborNode = 0;
896 x, y, z, stencilOffsets[stencil], localNodes, procGrid, rankDataDevice,
897 myProcX, myProcY, myProcZ, neighborRank, neighborNode))
898 rowLength += rowsPerNode;
899 }
900 }
901
902 if (final) {
903 const size_t firstRow = localNodeIndex * rowsPerNode;
904 for (size_t rowDof = 0; rowDof < rowsPerNode; ++rowDof)
905 rowptr(firstRow + rowDof) = update + rowDof * rowLength;
906 }
907 update += rowLength * rowsPerNode;
908 },
909 localNnz);
910 }
911
912 entries_type colind;
913 {
914 colind = entries_type(
915 Kokkos::ViewAllocateWithoutInitializing("StructuredRAP: graph column indices"), localNnz);
916 }
917
918 const LO invalidLocalOrdinal = Teuchos::OrdinalTraits<LO>::invalid();
919 Kokkos::View<int, device_type> invalidColumn;
920 if (debug) {
921 invalidColumn = Kokkos::View<int, device_type>("StructuredRAP: invalid column");
922 Kokkos::deep_copy(executionSpace, invalidColumn, 0);
923 }
924
925 // Loop to fill colind
926 {
927 Kokkos::parallel_for(
928 "StructuredRAP: fill graph column indices", range_policy(executionSpace, 0, localNumNodes),
929 KOKKOS_LAMBDA(const size_t localNodeIndex) {
930 const GO localNode = static_cast<GO>(localNodeIndex);
931 GO x = 0, y = 0, z = 0;
932 StructuredRAPFactoryDetails::getLocalNodeIndices(localNode, localNodes, x, y, z);
933 size_t columnOffset = 0;
934 const bool isInterior =
935 x > 0 && x + 1 < localNodes[0] &&
936 (numDimensions < 2 || (y > 0 && y + 1 < localNodes[1])) &&
937 (numDimensions < 3 || (z > 0 && z + 1 < localNodes[2]));
938
939 if (isInterior) {
940 const size_t firstRow = localNodeIndex * rowsPerNode;
941 for (size_t stencil = 0; stencil < stencilSize; ++stencil) {
942 const GO neighborLocalNode = localNode + localStencilNodeOffsets[stencil];
943 for (size_t colDof = 0; colDof < rowsPerNode; ++colDof) {
944 const LO colLid = static_cast<LO>(
945 neighborLocalNode * dofsPerNodeGO + static_cast<GO>(colDof));
946 for (size_t rowDof = 0; rowDof < rowsPerNode; ++rowDof)
947 colind(rowptr(firstRow + rowDof) + columnOffset) = colLid;
948 ++columnOffset;
949 }
950 }
951 return;
952 }
953
954 Kokkos::Array<LO, maxSupportedStencilSize * maxSupportedDofsPerNode> nodeColumns;
955 for (size_t stencil = 0; stencil < stencilSize; ++stencil) {
956 int neighborRank = myRank;
957 GO neighborNode = 0;
959 x, y, z, stencilOffsets[stencil], localNodes, procGrid, rankDataDevice,
960 myProcX, myProcY, myProcZ, neighborRank, neighborNode))
961 continue;
962
963 for (size_t colDof = 0; colDof < rowsPerNode; ++colDof) {
964 const GO colGid = globalMinGid + neighborNode * dofsPerNodeGO + static_cast<GO>(colDof);
965 LO colLid = invalidLocalOrdinal;
966 if (neighborRank == myRank) {
967 colLid = static_cast<LO>(colGid - localMinGid);
968 } else {
969 size_t region = 0;
970 while (region < numRemoteRegions && remoteRegionRanks[region] != neighborRank)
971 ++region;
972 if (region < numRemoteRegions) {
973 const GO neighborNx = remoteRegionNx[region];
974 const GO neighborNy = remoteRegionNy[region];
975 const GO localNeighborNode = neighborNode - remoteRegionFirstNodes[region];
976 const GO neighborX = localNeighborNode % neighborNx;
977 const GO neighborY = (localNeighborNode / neighborNx) % neighborNy;
978 const GO neighborZ = localNeighborNode / (neighborNx * neighborNy);
979 const StencilOffset direction = remoteRegionDirections[region];
980 size_t regionNode = 0;
981 size_t stride = 1;
982 if (direction.x == 0) {
983 regionNode += static_cast<size_t>(neighborX) * stride;
984 stride *= static_cast<size_t>(neighborNx);
985 }
986 if (direction.y == 0) {
987 regionNode += static_cast<size_t>(neighborY) * stride;
988 stride *= static_cast<size_t>(neighborNy);
989 }
990 if (direction.z == 0)
991 regionNode += static_cast<size_t>(neighborZ) * stride;
992 colLid = static_cast<LO>(localNumRows + remoteRegionOffsets[region] +
993 regionNode * rowsPerNode + colDof);
994 }
995 }
996
997 if (debug && colLid == invalidLocalOrdinal)
998 Kokkos::atomic_exchange(&invalidColumn(), 1);
999
1000 nodeColumns[columnOffset++] = colLid;
1001 }
1002 }
1003
1004 for (size_t entry = 1; entry < columnOffset; ++entry) {
1005 const LO value = nodeColumns[entry];
1006 size_t insertion = entry;
1007 while (insertion > 0 && value < nodeColumns[insertion - 1]) {
1008 nodeColumns[insertion] = nodeColumns[insertion - 1];
1009 --insertion;
1010 }
1011 nodeColumns[insertion] = value;
1012 }
1013
1014 const size_t firstRow = localNodeIndex * rowsPerNode;
1015 for (size_t rowDof = 0; rowDof < rowsPerNode; ++rowDof) {
1016 const size_t rowStart = rowptr(firstRow + rowDof);
1017 for (size_t entry = 0; entry < columnOffset; ++entry)
1018 colind(rowStart + entry) = nodeColumns[entry];
1019 }
1020 });
1021 executionSpace.fence("StructuredRAP: graph column indices ready");
1022 }
1023
1024 if (debug) {
1025 int invalidColumnHost = 0;
1026 Kokkos::deep_copy(executionSpace, invalidColumnHost, invalidColumn);
1027 TEUCHOS_TEST_FOR_EXCEPTION(invalidColumnHost != 0, Exceptions::RuntimeError,
1028 "StructuredRAPFactory::GetStructuredGraph(" << graphSpec.description
1029 << "): a generated column GID was not found in the coarse column map.");
1030
1031 size_t unsortedRows = 0;
1032 Kokkos::parallel_reduce(
1033 "StructuredRAP: validate sorted graph rows", range_policy(executionSpace, 0, localNumRows),
1034 KOKKOS_LAMBDA(const size_t row, size_t& invalid) {
1035 for (size_t entry = rowptr(row) + 1; entry < rowptr(row + 1); ++entry) {
1036 if (colind(entry) < colind(entry - 1)) {
1037 ++invalid;
1038 break;
1039 }
1040 }
1041 },
1042 unsortedRows);
1043 TEUCHOS_TEST_FOR_EXCEPTION(unsortedRows != 0, Exceptions::RuntimeError,
1044 "StructuredRAPFactory::GetStructuredGraph(" << graphSpec.description
1045 << "): generated graph rows are not sorted.");
1046 }
1047
1048 local_graph_type localGraph(colind, rowptr);
1049 RCP<CrsGraph> graph;
1050 {
1051 graph = CrsGraphFactory::Build(
1052 localGraph, rowMap, colMap, rowMap, rowMap, paramList);
1053 }
1054 {
1055 using values_type = typename Matrix::local_matrix_type::values_type;
1056 values_type values(
1057 Kokkos::ViewAllocateWithoutInitializing("StructuredRAP: matrix values"), localNnz);
1058 Ac = MatrixFactory::Build(graph, values, paramList);
1059 }
1060}
1061
1062template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1064 const bool doTranspose = true;
1065 const bool doFillComplete = true;
1066 const bool doOptimizeStorage = true;
1067 const Teuchos::ParameterList& pL = GetParameterList();
1068 const bool prebuildCoarseGraph = pL.get<bool>("rap: prebuild coarse graph");
1069 const bool useRAPDelegate = !prebuildCoarseGraph;
1070
1071 TEUCHOS_TEST_FOR_EXCEPTION(
1072 !pL.get<bool>("transpose: use implicit"), Exceptions::RuntimeError,
1073 "StructuredRAPFactory requires \"transpose: use implicit\" = true because "
1074 "the prebuilt coarse graph assumes the Galerkin product P^T A P.");
1075
1076 RCP<Matrix> Ac;
1077
1078 TEUCHOS_TEST_FOR_EXCEPTION(hasDeclaredInput_ == false, Exceptions::RuntimeError,
1079 "MueLu::RAPFactory::Build(): CallDeclareInput has not been called before Build!");
1080
1081 if (useRAPDelegate) {
1082 if (coarseLevel.IsAvailable("RAP reuse data", this)) {
1083 RCP<ParameterList> RAPparams = coarseLevel.Get<RCP<ParameterList>>("RAP reuse data", this);
1084 coarseLevel.Set("RAP reuse data", RAPparams, rapFactoryDelegate_.get());
1085 }
1086
1087 // RAPFactory does not accept a prebuilt Ac graph, so delegate only when
1088 // coarse-graph prebuilding is disabled.
1089 rapFactoryDelegate_->Build(fineLevel, coarseLevel);
1090
1091 Ac = coarseLevel.Get<RCP<Matrix>>("A", rapFactoryDelegate_.get());
1092 Set(coarseLevel, "A", Ac);
1093
1094 if (coarseLevel.IsAvailable("RAP reuse data", rapFactoryDelegate_.get())) {
1095 RCP<ParameterList> RAPparams = coarseLevel.Get<RCP<ParameterList>>("RAP reuse data", rapFactoryDelegate_.get());
1096 Set(coarseLevel, "RAP reuse data", RAPparams);
1097 }
1098
1099 return;
1100 }
1101
1102 {
1103 FactoryMonitor m(*this, "Computing Ac", coarseLevel);
1104
1105 std::ostringstream levelstr;
1106 levelstr << coarseLevel.GetLevelID();
1107 std::string labelstr = FormattingHelper::getColonLabel(coarseLevel.getObjectLabel());
1108
1109 TEUCHOS_TEST_FOR_EXCEPTION(
1110 pL.get<bool>("rap: triple product") == false, Exceptions::RuntimeError,
1111 "StructuredRAPFactory requires \"rap: triple product\" = true.");
1112
1113 RCP<Matrix> A;
1114 RCP<Matrix> P;
1115 {
1116 RCP<ParameterList> RAPparams;
1117 {
1118 SubFactoryMonitor mGraph(*this, "Prebuilding coarse Ac graph", coarseLevel);
1119
1120 A = Get<RCP<Matrix>>(fineLevel, "A");
1121 P = Get<RCP<Matrix>>(coarseLevel, "P");
1122 // We do not have a valid P (e.g., # global aggregates = 0), so bail.
1123 // This level will ultimately be removed in MueLu_Hierarchy_defs.h via a resize().
1124 if (P.is_null()) {
1125 Ac = Teuchos::null;
1126 Set(coarseLevel, "A", Ac);
1127 return;
1128 }
1129
1130 RAPparams = rcp(new ParameterList);
1131 if (pL.isSublist("matrixmatrix: kernel params"))
1132 RAPparams->sublist("matrixmatrix: kernel params") = pL.sublist("matrixmatrix: kernel params");
1133
1134 if (coarseLevel.IsAvailable("RAP reuse data", this)) {
1135 GetOStream(static_cast<MsgType>(Runtime0 | Test)) << "Reusing previous RAP data" << std::endl;
1136
1137 RAPparams = coarseLevel.Get<RCP<ParameterList>>("RAP reuse data", this);
1138
1139 TEUCHOS_TEST_FOR_EXCEPTION(!RAPparams->isParameter("graph"), Exceptions::RuntimeError,
1140 "StructuredRAPFactory::Build(): \"RAP reuse data\" does not contain the expected graph.");
1141 Ac = RAPparams->get<RCP<Matrix>>("graph");
1142 TEUCHOS_TEST_FOR_EXCEPTION(Ac.is_null(), Exceptions::RuntimeError,
1143 "StructuredRAPFactory::Build(): \"RAP reuse data\" graph is null.");
1144
1145 // Some eigenvalue may have been cached with the matrix in the previous run.
1146 // As the matrix values will be updated, we need to reset the eigenvalue.
1147 Ac->SetMaxEigenvalueEstimate(-Teuchos::ScalarTraits<SC>::one());
1148 } else if (prebuildCoarseGraph) {
1149 const int numDimensions = fineLevel.GetLevelID() == 0
1150 ? fineLevel.Get<int>("numDimensions", NoFactory::get())
1151 : Get<int>(fineLevel, "numDimensions");
1152 const Teuchos::Array<LocalOrdinal> lFineNodesPerDim =
1153 fineLevel.GetLevelID() == 0
1154 ? fineLevel.Get<Teuchos::Array<LocalOrdinal>>("lNodesPerDim", NoFactory::get())
1155 : Get<Teuchos::Array<LocalOrdinal>>(fineLevel, "lNodesPerDim");
1156 const RCP<const FactoryBase> coarseDimensionsFactory =
1157 GetStructuredMetadataFactory("lCoarseNodesPerDim");
1158 const RCP<const FactoryBase> interpolationOrderFactory =
1159 GetStructuredMetadataFactory("structuredInterpolationOrder");
1160 const Teuchos::Array<LocalOrdinal> lCoarseNodesPerDim =
1161 fineLevel.Get<Teuchos::Array<LocalOrdinal>>(
1162 "lCoarseNodesPerDim", coarseDimensionsFactory.get());
1163 const int interpolationOrder = fineLevel.Get<int>(
1164 "structuredInterpolationOrder", interpolationOrderFactory.get());
1165 const StructuredGraphSpec graphSpec =
1166 GetStructuredGraphSpec(*A, *P, numDimensions, lFineNodesPerDim,
1167 interpolationOrder);
1168 GetOStream(Statistics1) << "StructuredRAP: Using " << graphSpec.description
1169 << " stencil with " << graphSpec.stencilOffsets.size()
1170 << " nodal entries." << std::endl;
1171 GetStructuredGraph(Ac, P, lCoarseNodesPerDim, graphSpec);
1172 }
1173
1174 // We always need global constants for the RAP, but not for the temporaries.
1175 RAPparams->set("compute global constants: temporaries", RAPparams->get("compute global constants: temporaries", false));
1176 RAPparams->set("compute global constants", true);
1177
1178 if (Ac.is_null())
1179 Ac = MatrixFactory::Build(P->getDomainMap(), Teuchos::as<LocalOrdinal>(0));
1180 }
1181
1182 SubFactoryMonitor m2(*this, "MxMxM: P^T x A x P (implicit)", coarseLevel);
1183
1184 Xpetra::TripleMatrixMultiply<SC, LO, GO, NO>::
1185 MultiplyRAP(*P, doTranspose, *A, !doTranspose, *P, !doTranspose, *Ac, doFillComplete,
1186 doOptimizeStorage, labelstr + std::string("MueLu::P^T*A*P-implicit-") + levelstr.str(),
1187 RAPparams);
1188
1189 {
1190 GetOStream(Statistics1) << "StructuredRAP: Ac nnz (prebuild coarse graph = "
1191 << (prebuildCoarseGraph ? "true" : "false")
1192 << "): local = " << Ac->getLocalNumEntries()
1193 << ", global = " << Ac->getGlobalNumEntries() << std::endl;
1194
1195 Teuchos::ArrayView<const double> relativeFloor = pL.get<Teuchos::Array<double>>("rap: relative diagonal floor")();
1196 if (relativeFloor.size() > 0) {
1197 Xpetra::MatrixUtils<SC, LO, GO, NO>::RelativeDiagonalBoost(Ac, relativeFloor, GetOStream(Statistics2));
1198 }
1199
1200 bool repairZeroDiagonals = pL.get<bool>("RepairMainDiagonal") || pL.get<bool>("rap: fix zero diagonals");
1201 bool checkAc = pL.get<bool>("CheckMainDiagonal") || pL.get<bool>("rap: fix zero diagonals");
1202 if (checkAc || repairZeroDiagonals) {
1203 using magnitudeType = typename Teuchos::ScalarTraits<Scalar>::magnitudeType;
1204 magnitudeType threshold;
1205 if (pL.isType<magnitudeType>("rap: fix zero diagonals threshold"))
1206 threshold = pL.get<magnitudeType>("rap: fix zero diagonals threshold");
1207 else
1208 threshold = Teuchos::as<magnitudeType>(pL.get<double>("rap: fix zero diagonals threshold"));
1209 Scalar replacement = Teuchos::as<Scalar>(pL.get<double>("rap: fix zero diagonals replacement"));
1210 Xpetra::MatrixUtils<SC, LO, GO, NO>::CheckRepairMainDiagonal(Ac, repairZeroDiagonals, GetOStream(Warnings1), threshold, replacement);
1211 }
1212
1213 if (IsPrint(Statistics2)) {
1214 RCP<ParameterList> params = rcp(new ParameterList());
1215 params->set("printLoadBalancingInfo", true);
1216 params->set("printCommInfo", true);
1217
1218 GetOStream(Statistics2) << PerfUtils::PrintMatrixInfo(*Ac, "Ac", params);
1219 }
1220
1221 if (!Ac.is_null()) {
1222 std::ostringstream oss;
1223 oss << "A_" << coarseLevel.GetLevelID();
1224 Ac->setObjectLabel(oss.str());
1225 }
1226 Set(coarseLevel, "A", Ac);
1227
1228 RAPparams->set("graph", Ac);
1229 Set(coarseLevel, "RAP reuse data", RAPparams);
1230 }
1231 }
1232 }
1233
1234 if (Behavior::debug())
1235 MatrixUtils::checkLocalRowMapMatchesColMap(*Ac);
1236
1237 if (transferFacts_.begin() != transferFacts_.end()) {
1238 SubFactoryMonitor m(*this, "Projections", coarseLevel);
1239
1240 // call Build of all user-given transfer factories
1241 for (std::vector<RCP<const FactoryBase>>::const_iterator it = transferFacts_.begin(); it != transferFacts_.end(); ++it) {
1242 RCP<const FactoryBase> fac = *it;
1243 GetOStream(Runtime0) << "RAPFactory: call transfer factory: " << fac->description() << std::endl;
1244 fac->CallBuild(coarseLevel);
1245 // Coordinates transfer is marginally different from all other operations
1246 // because it is *optional*, and not required. For instance, we may need
1247 // coordinates only on level 4 if we start repartitioning from that level,
1248 // but we don't need them on level 1,2,3. As our current Hierarchy setup
1249 // assumes propagation of dependencies only through three levels, this
1250 // means that we need to rely on other methods to propagate optional data.
1251 //
1252 // The method currently used is through RAP transfer factories, which are
1253 // simply factories which are called at the end of RAP with a single goal:
1254 // transfer some fine data to coarser level. Because these factories are
1255 // kind of outside of the mainline factories, they behave different. In
1256 // particular, we call their Build method explicitly, rather than through
1257 // Get calls. This difference is significant, as the Get call is smart
1258 // enough to know when to release all factory dependencies, and Build is
1259 // dumb. This led to the following CoordinatesTransferFactory sequence:
1260 // 1. Request level 0
1261 // 2. Request level 1
1262 // 3. Request level 0
1263 // 4. Release level 0
1264 // 5. Release level 1
1265 //
1266 // The problem is missing "6. Release level 0". Because it was missing,
1267 // we had outstanding request on "Coordinates", "Aggregates" and
1268 // "CoarseMap" on level 0.
1269 //
1270 // This was fixed by explicitly calling Release on transfer factories in
1271 // RAPFactory. I am still unsure how exactly it works, but now we have
1272 // clear data requests for all levels.
1273 coarseLevel.Release(*fac);
1274 }
1275 }
1276}
1277
1278template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1280 // check if it's a TwoLevelFactoryBase based transfer factory
1281 TEUCHOS_TEST_FOR_EXCEPTION(Teuchos::rcp_dynamic_cast<const TwoLevelFactoryBase>(factory) == Teuchos::null, Exceptions::BadCast,
1282 "MueLu::StructuredRAPFactory::AddTransferFactory: Transfer factory is not derived from TwoLevelFactoryBase. "
1283 "This is very strange. (Note: you can remove this exception if there's a good reason for)");
1284 TEUCHOS_TEST_FOR_EXCEPTION(hasDeclaredInput_, Exceptions::RuntimeError, "MueLu::StructuredRAPFactory::AddTransferFactory: Factory is being added after we have already declared input");
1285 transferFacts_.push_back(factory);
1286 rapFactoryDelegate_->AddTransferFactory(factory);
1287}
1288
1289} // namespace MueLu
1290
1291#define MUELU_STRUCTUREDRAPFACTORY_SHORT
1292#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.
virtual std::string description() const
Return a simple one-line description of this object.
Exception indicating invalid cast attempted.
Exception throws to report errors in the internal logical of the program.
void Input(Level &level, const std::string &varName) const
T Get(Level &level, const std::string &varName) const
void Set(Level &level, const std::string &varName, const T &data) const
const RCP< const FactoryBase > GetFactory(const std::string &varName) const
Default implementation of FactoryAcceptor::GetFactory().
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 const NoFactory * get()
virtual const Teuchos::ParameterList & GetParameterList() const
static std::string PrintMatrixInfo(const Matrix &A, const std::string &msgTag, RCP< const Teuchos::ParameterList > params=Teuchos::null)
Factory for building coarse matrices.
RCP< const FactoryBase > GetStructuredMetadataFactory(const std::string &varName) const
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.
RCP< MueLu::RAPFactory< Scalar, LocalOrdinal, GlobalOrdinal, Node > > rapFactoryDelegate_
RCP< const ParameterList > GetValidParameterList() const
Return a const parameter list of valid parameters that setParameterList() will accept.
StructuredGraphSpec GetStructuredGraphSpec(const Matrix &A, const Matrix &P, int numDimensions, const Teuchos::Array< LocalOrdinal > &lFineNodesPerDim, int interpolationOrder) const
std::vector< RCP< const FactoryBase > > transferFacts_
list of user-defined transfer Factories
StructuredGraphSpec DeriveCoarseRAPStencil(const FineStencilSpec &fineStencil, int interpolationOrder) const
FineStencilSpec DetectFineStencil(const Matrix &A, int numDimensions, const Teuchos::Array< LocalOrdinal > &lFineNodesPerDim) const
Timer to be used in factories. Similar to SubMonitor but adds a timer level by level.
Teuchos::FancyOStream & GetOStream(MsgType type, int thisProcRankOnly=0) const
Get an output stream for outputting the input message type.
bool IsPrint(MsgType type, int thisProcRankOnly=-1) const
Find out whether we need to print out information for a specific message type.
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.