239 const Matrix& A,
const int numDimensions,
240 const Teuchos::Array<LocalOrdinal>& lFineNodesPerDim)
const {
241 const std::string prefix =
"StructuredRAPFactory::DetectFineStencil: ";
244 prefix <<
"the number of dimensions must be between one and three.");
246 prefix <<
"insufficient fine-grid dimensions were supplied.");
248 const auto rowMap = A.getRowMap();
249 const auto colMap = A.getColMap();
250 const auto comm = rowMap->getComm();
251 const auto graph = A.getCrsGraph();
253 prefix <<
"A does not expose a CRS graph.");
255 const LO dofsPerNode = Teuchos::as<LO>(A.GetFixedBlockSize());
257 prefix <<
"A must have a positive fixed block size.");
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;
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]);
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(
284 prefix <<
"the structured local dimensions and fixed block size do not match A on every rank.");
286 const LO interiorNode = interior[2] * fineNodes[0] * fineNodes[1] +
287 interior[1] * fineNodes[0] + interior[0];
289 bool hasCompleteLocalStencil = hasLocalInterior;
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;
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;
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(
317 prefix <<
"no MPI rank owns a local interior node whose complete stencil is locally owned; "
318 <<
"increase the local structured subdomain size.");
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);
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(
336 prefix <<
"the selected detector row contains a nonowned column.");
338 const LO columnNode = ownedColumn / dofsPerNode;
339 const LO columnDof = ownedColumn % dofsPerNode;
340 LO columnX, columnY, columnZ;
342 columnNode, fineNodes, columnX, columnY, columnZ);
344 Teuchos::as<int>(columnX - interior[0]),
345 Teuchos::as<int>(columnY - interior[1]),
346 Teuchos::as<int>(columnZ - interior[2])};
348 bool knownOffset =
false;
350 if (known.
x == offset.
x && known.
y == offset.
y && known.
z == offset.
z) {
359 offset, rowDof, columnDof, Teuchos::as<LO>(entry)});
365 Teuchos::as<int>(stencil.
entries.size())};
366 Teuchos::broadcast<int, int>(*comm, detectorRank, 2, counts);
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) {
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;
388 Teuchos::broadcast<int, int>(*comm, detectorRank, 3 * counts[0],
389 offsetData.getRawPtr());
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());
397 if (myRank != detectorRank) {
399 for (
int offset = 0; offset < counts[0]; ++offset)
401 offsetData[3 * offset], offsetData[3 * offset + 1],
402 offsetData[3 * offset + 2]});
404 stencil.
entries.reserve(counts[1]);
405 for (
int entry = 0; entry < counts[1]; ++entry)
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]});
415 prefix <<
"the representative interior rows are empty.");
527 RCP<Matrix>& Ac, RCP<Matrix> P,
528 const Teuchos::Array<LocalOrdinal>& lCoarseNodesPerDim,
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>>;
538 "StructuredRAPFactory::GetStructuredGraph(" << graphSpec.
description
539 <<
"): number of dimensions must be between one and three.");
541 "StructuredRAPFactory::GetStructuredGraph(" << graphSpec.
description
542 <<
"): insufficient local coarse-grid dimensions.");
544 "StructuredRAPFactory::GetStructuredGraph(" << graphSpec.
description
545 <<
"): dofsPerNode must be positive.");
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;
552 "StructuredRAPFactory::GetStructuredGraph(" << graphSpec.
description
553 <<
"): a radius-one stencil may contain at most 27 entries.");
555 "StructuredRAPFactory::GetStructuredGraph(" << graphSpec.
description
556 <<
"): at most three DOFs per node are supported.");
558 Kokkos::Array<GO, 3> localNodes{};
559 for (
int dim = 0; dim < 3; ++dim)
560 localNodes[dim] = Teuchos::as<GO>(1);
562 localNodes[dim] = Teuchos::as<GO>(lCoarseNodesPerDim[dim]);
564 "StructuredRAPFactory::GetStructuredGraph(" << graphSpec.
description
565 <<
"): local coarse dimensions must be positive.");
568 Kokkos::Array<StencilOffset, maxSupportedStencilSize> stencilOffsets{};
569 Kokkos::Array<GO, maxSupportedStencilSize> localStencilNodeOffsets{};
570 for (
size_t stencil = 0; stencil < graphSpec.
stencilOffsets.size(); ++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) ||
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];
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);
595 auto rowMap = P->getDomainMap();
596 const size_t localNumRows = rowMap->getLocalNumElements();
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;
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 <<
".");
609 "StructuredRAPFactory::GetStructuredGraph requires the Tpetra backend.");
611 const GO numGlobalRows = Teuchos::as<GO>(rowMap->getGlobalNumElements());
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();
619 "StructuredRAPFactory::GetStructuredGraph(" << graphSpec.
description
620 <<
"): coarse row-map GIDs must form a contiguous range.");
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();
627 "StructuredRAPFactory::GetStructuredGraph(" << graphSpec.
description
628 <<
"): local row range does not begin at a nodal boundary.");
629 const GO firstLocalNode = (localMinGid - globalMinGid) / dofsPerNodeGO;
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);
639 Teuchos::gatherAll(*comm, 4, localRankData.getRawPtr(), 4 * numRanks, rankData.getRawPtr());
643 Kokkos::Array<int, 3> procGrid{};
644 for (
int dim = 0; dim < 3; ++dim)
647 procGrid[0] = numRanks;
649 while ((procGrid[0] + 1) * (procGrid[0] + 1) <= numRanks)
651 procGrid[1] = numRanks / procGrid[0];
652 while (procGrid[0] * procGrid[1] != numRanks)
653 procGrid[1] = numRanks / (--procGrid[0]);
655 int cubeRootNumRanks = 1;
656 while ((cubeRootNumRanks + 1) * (cubeRootNumRanks + 1) * (cubeRootNumRanks + 1) <= numRanks)
658 procGrid[0] = cubeRootNumRanks;
659 procGrid[1] = cubeRootNumRanks;
660 procGrid[2] = cubeRootNumRanks;
662 if (procGrid[0] * procGrid[1] * procGrid[2] != numRanks) {
667 int procTemp = numRanks;
669 const int galeriFactorSearchLimit = 50;
670 int factors[galeriFactorSearchLimit] = {};
671 for (
int factor = 2; factor < galeriFactorSearchLimit; ++factor) {
672 while (procTemp % factor == 0) {
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;
685 procGrid[2] *= factor;
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.");
701 const int myProcX = myRank % procGrid[0];
702 const int myProcY = (myRank % procXY) / procGrid[0];
703 const int myProcZ = myRank / procXY;
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.");
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);
729 const auto rowLocalMap = rowMap->getLocalMap();
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)
744 "StructuredRAPFactory::GetStructuredGraph(" << graphSpec.
description
745 <<
"): coarse row-map GIDs are not locally contiguous and ordered.");
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;
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)
770 bool stencilReachesRegion =
false;
771 for (
size_t stencil = 0; stencil < stencilSize; ++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;
781 if (!stencilReachesRegion)
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])
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));
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;
812 const size_t numRemoteColumns = remoteRegionOffsets[numRemoteRegions];
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);
826 const size_t remoteEntry = entry - localNumRows;
828 while (remoteEntry >= remoteRegionOffsets[region + 1])
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;
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);
846 if (direction.
y == 0) {
847 neighborY =
static_cast<GO
>(regionNode %
static_cast<size_t>(neighborNy));
848 regionNode /=
static_cast<size_t>(neighborNy);
850 if (direction.
z == 0)
851 neighborZ =
static_cast<GO
>(regionNode);
853 const GO neighborNode = remoteRegionFirstNodes[region] +
854 neighborZ * neighborNx * neighborNy + neighborY * neighborNx + neighborX;
855 colMapGids(entry) = globalMinGid + neighborNode * dofsPerNodeGO + dof;
857 executionSpace.fence(
"StructuredRAP: column map GIDs ready");
859 typename Map::global_indices_array_device_type constColMapGids = colMapGids;
861 RCP<const Map> colMap;
863 colMap = MapFactory::Build(
864 rowMap->lib(), Teuchos::OrdinalTraits<Xpetra::global_size_t>::invalid(),
865 constColMapGids, rowMap->getIndexBase(), comm);
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) {
879 rowptr(localNumRows) = update;
883 const GO localNode =
static_cast<GO
>(localNodeIndex);
884 GO x = 0, y = 0, z = 0;
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;
892 for (
size_t stencil = 0; stencil < stencilSize; ++stencil) {
893 int neighborRank = myRank;
896 x, y, z, stencilOffsets[stencil], localNodes, procGrid, rankDataDevice,
897 myProcX, myProcY, myProcZ, neighborRank, neighborNode))
898 rowLength += rowsPerNode;
903 const size_t firstRow = localNodeIndex * rowsPerNode;
904 for (
size_t rowDof = 0; rowDof < rowsPerNode; ++rowDof)
905 rowptr(firstRow + rowDof) = update + rowDof * rowLength;
907 update += rowLength * rowsPerNode;
914 colind = entries_type(
915 Kokkos::ViewAllocateWithoutInitializing(
"StructuredRAP: graph column indices"), localNnz);
918 const LO invalidLocalOrdinal = Teuchos::OrdinalTraits<LO>::invalid();
919 Kokkos::View<int, device_type> invalidColumn;
921 invalidColumn = Kokkos::View<int, device_type>(
"StructuredRAP: invalid column");
922 Kokkos::deep_copy(executionSpace, invalidColumn, 0);
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;
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]));
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;
954 Kokkos::Array<LO, maxSupportedStencilSize * maxSupportedDofsPerNode> nodeColumns;
955 for (
size_t stencil = 0; stencil < stencilSize; ++stencil) {
956 int neighborRank = myRank;
959 x, y, z, stencilOffsets[stencil], localNodes, procGrid, rankDataDevice,
960 myProcX, myProcY, myProcZ, neighborRank, neighborNode))
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);
970 while (region < numRemoteRegions && remoteRegionRanks[region] != neighborRank)
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;
982 if (direction.
x == 0) {
983 regionNode +=
static_cast<size_t>(neighborX) * stride;
984 stride *=
static_cast<size_t>(neighborNx);
986 if (direction.
y == 0) {
987 regionNode +=
static_cast<size_t>(neighborY) * stride;
988 stride *=
static_cast<size_t>(neighborNy);
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);
997 if (debug && colLid == invalidLocalOrdinal)
998 Kokkos::atomic_exchange(&invalidColumn(), 1);
1000 nodeColumns[columnOffset++] = colLid;
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];
1011 nodeColumns[insertion] = value;
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];
1021 executionSpace.fence(
"StructuredRAP: graph column indices ready");
1025 int invalidColumnHost = 0;
1026 Kokkos::deep_copy(executionSpace, invalidColumnHost, invalidColumn);
1028 "StructuredRAPFactory::GetStructuredGraph(" << graphSpec.
description
1029 <<
"): a generated column GID was not found in the coarse column map.");
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)) {
1044 "StructuredRAPFactory::GetStructuredGraph(" << graphSpec.
description
1045 <<
"): generated graph rows are not sorted.");
1048 local_graph_type localGraph(colind, rowptr);
1049 RCP<CrsGraph> graph;
1051 graph = CrsGraphFactory::Build(
1052 localGraph, rowMap, colMap, rowMap, rowMap, paramList);
1055 using values_type =
typename Matrix::local_matrix_type::values_type;
1057 Kokkos::ViewAllocateWithoutInitializing(
"StructuredRAP: matrix values"), localNnz);
1058 Ac = MatrixFactory::Build(graph, values, paramList);
1064 const bool doTranspose =
true;
1065 const bool doFillComplete =
true;
1066 const bool doOptimizeStorage =
true;
1068 const bool prebuildCoarseGraph = pL.get<
bool>(
"rap: prebuild coarse graph");
1069 const bool useRAPDelegate = !prebuildCoarseGraph;
1071 TEUCHOS_TEST_FOR_EXCEPTION(
1073 "StructuredRAPFactory requires \"transpose: use implicit\" = true because "
1074 "the prebuilt coarse graph assumes the Galerkin product P^T A P.");
1079 "MueLu::RAPFactory::Build(): CallDeclareInput has not been called before Build!");
1081 if (useRAPDelegate) {
1082 if (coarseLevel.
IsAvailable(
"RAP reuse data",
this)) {
1083 RCP<ParameterList> RAPparams = coarseLevel.
Get<RCP<ParameterList>>(
"RAP reuse data",
this);
1092 Set(coarseLevel,
"A", Ac);
1095 RCP<ParameterList> RAPparams = coarseLevel.
Get<RCP<ParameterList>>(
"RAP reuse data",
rapFactoryDelegate_.get());
1096 Set(coarseLevel,
"RAP reuse data", RAPparams);
1105 std::ostringstream levelstr;
1109 TEUCHOS_TEST_FOR_EXCEPTION(
1111 "StructuredRAPFactory requires \"rap: triple product\" = true.");
1116 RCP<ParameterList> RAPparams;
1126 Set(coarseLevel,
"A", Ac);
1130 RAPparams = rcp(
new ParameterList);
1131 if (pL.isSublist(
"matrixmatrix: kernel params"))
1132 RAPparams->sublist(
"matrixmatrix: kernel params") = pL.sublist(
"matrixmatrix: kernel params");
1134 if (coarseLevel.IsAvailable(
"RAP reuse data",
this)) {
1137 RAPparams = coarseLevel.Get<RCP<ParameterList>>(
"RAP reuse data",
this);
1140 "StructuredRAPFactory::Build(): \"RAP reuse data\" does not contain the expected graph.");
1141 Ac = RAPparams->get<RCP<Matrix>>(
"graph");
1143 "StructuredRAPFactory::Build(): \"RAP reuse data\" graph is null.");
1147 Ac->SetMaxEigenvalueEstimate(-Teuchos::ScalarTraits<SC>::one());
1148 }
else if (prebuildCoarseGraph) {
1149 const int numDimensions = fineLevel.GetLevelID() == 0
1151 :
Get<int>(fineLevel,
"numDimensions");
1152 const Teuchos::Array<LocalOrdinal> lFineNodesPerDim =
1153 fineLevel.GetLevelID() == 0
1154 ? fineLevel.Get<Teuchos::Array<LocalOrdinal>>(
"lNodesPerDim",
NoFactory::get())
1156 const RCP<const FactoryBase> coarseDimensionsFactory =
1158 const RCP<const FactoryBase> interpolationOrderFactory =
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());
1167 interpolationOrder);
1170 <<
" nodal entries." << std::endl;
1175 RAPparams->set(
"compute global constants: temporaries", RAPparams->get(
"compute global constants: temporaries",
false));
1176 RAPparams->set(
"compute global constants",
true);
1179 Ac = MatrixFactory::Build(P->getDomainMap(), Teuchos::as<LocalOrdinal>(0));
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(),
1191 << (prebuildCoarseGraph ?
"true" :
"false")
1192 <<
"): local = " << Ac->getLocalNumEntries()
1193 <<
", global = " << Ac->getGlobalNumEntries() << std::endl;
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));
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");
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);
1214 RCP<ParameterList> params = rcp(
new ParameterList());
1215 params->set(
"printLoadBalancingInfo",
true);
1216 params->set(
"printCommInfo",
true);
1221 if (!Ac.is_null()) {
1222 std::ostringstream oss;
1224 Ac->setObjectLabel(oss.str());
1226 Set(coarseLevel,
"A", Ac);
1228 RAPparams->set(
"graph", Ac);
1229 Set(coarseLevel,
"RAP reuse data", RAPparams);
1235 MatrixUtils::checkLocalRowMapMatchesColMap(*Ac);
1242 RCP<const FactoryBase> fac = *it;
1243 GetOStream(
Runtime0) <<
"RAPFactory: call transfer factory: " << fac->description() << std::endl;
1244 fac->CallBuild(coarseLevel);