MueLu Version of the Day
Loading...
Searching...
No Matches
MueLu_TentativePFactory_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_TENTATIVEPFACTORY_DEF_HPP
11#define MUELU_TENTATIVEPFACTORY_DEF_HPP
12
13#include <Xpetra_MapFactory.hpp>
14#include <Xpetra_Map.hpp>
15#include <Xpetra_CrsMatrix.hpp>
16#include <Xpetra_CrsGraphFactory.hpp>
17#include <Xpetra_Matrix.hpp>
18#include <Xpetra_MatrixMatrix.hpp>
19#include <Xpetra_MultiVector.hpp>
20#include <Xpetra_MultiVectorFactory.hpp>
21#include <Xpetra_VectorFactory.hpp>
22#include <Xpetra_Import.hpp>
23#include <Xpetra_ImportFactory.hpp>
24#include <Xpetra_CrsMatrixWrap.hpp>
25#include <Xpetra_StridedMap.hpp>
26#include <Xpetra_StridedMapFactory.hpp>
27
28#include "MueLu_KeepType.hpp"
29#include "Xpetra_TpetraBlockCrsMatrix.hpp"
30
32
33#include "MueLu_Aggregates.hpp"
34#include "MueLu_AmalgamationInfo.hpp"
35#include "MueLu_MasterList.hpp"
36#include "MueLu_Monitor.hpp"
37#include "MueLu_PerfUtils.hpp"
38#include "MueLu_Utilities.hpp"
39
40namespace MueLu {
41
42template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
44
45template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
47
48template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
50 RCP<ParameterList> validParamList = rcp(new ParameterList());
51
52#define SET_VALID_ENTRY(name) validParamList->setEntry(name, MasterList::getEntry(name))
53 SET_VALID_ENTRY("tentative: calculate qr");
54 SET_VALID_ENTRY("tentative: build coarse coordinates");
55 SET_VALID_ENTRY("tentative: constant column sums");
56 SET_VALID_ENTRY("sa: keep tentative prolongator");
57#undef SET_VALID_ENTRY
58 validParamList->set<std::string>("Nullspace name", "Nullspace", "Name for the input nullspace");
59
60 validParamList->set<RCP<const FactoryBase>>("A", Teuchos::null, "Generating factory of the matrix A");
61 validParamList->set<RCP<const FactoryBase>>("Aggregates", Teuchos::null, "Generating factory of the aggregates");
62 validParamList->set<RCP<const FactoryBase>>("Nullspace", Teuchos::null, "Generating factory of the nullspace");
63 validParamList->set<RCP<const FactoryBase>>("Scaled Nullspace", Teuchos::null, "Generating factory of the scaled nullspace");
64 validParamList->set<RCP<const FactoryBase>>("UnAmalgamationInfo", Teuchos::null, "Generating factory of UnAmalgamationInfo");
65 validParamList->set<RCP<const FactoryBase>>("CoarseMap", Teuchos::null, "Generating factory of the coarse map");
66 validParamList->set<RCP<const FactoryBase>>("Coordinates", Teuchos::null, "Generating factory of the coordinates");
67 validParamList->set<RCP<const FactoryBase>>("Node Comm", Teuchos::null, "Generating factory of the node level communicator");
68
69 // Make sure we don't recursively validate options for the matrixmatrix kernels
70 ParameterList norecurse;
71 norecurse.disableRecursiveValidation();
72 validParamList->set<ParameterList>("matrixmatrix: kernel params", norecurse, "MatrixMatrix kernel parameters");
73
74 return validParamList;
75}
76
77template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
79 const ParameterList& pL = GetParameterList();
80 // NOTE: This guy can only either be 'Nullspace' or 'Scaled Nullspace' or else the validator above will cause issues
81 std::string nspName = "Nullspace";
82 if (pL.isParameter("Nullspace name")) nspName = pL.get<std::string>("Nullspace name");
83
84 Input(fineLevel, "A");
85 Input(fineLevel, "Aggregates");
86 Input(fineLevel, nspName);
87 Input(fineLevel, "UnAmalgamationInfo");
88 Input(fineLevel, "CoarseMap");
89 if (fineLevel.GetLevelID() == 0 &&
90 fineLevel.IsAvailable("Coordinates", NoFactory::get()) && // we have coordinates (provided by user app)
91 pL.get<bool>("tentative: build coarse coordinates")) { // and we want coordinates on other levels
92 bTransferCoordinates_ = true; // then set the transfer coordinates flag to true
93 Input(fineLevel, "Coordinates");
94 } else if (bTransferCoordinates_) {
95 Input(fineLevel, "Coordinates");
96 }
97}
98
99template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
101 return BuildP(fineLevel, coarseLevel);
102}
103
104template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
106 FactoryMonitor m(*this, "Build", coarseLevel);
107
108 typedef typename Teuchos::ScalarTraits<Scalar>::coordinateType coordinate_type;
109 typedef Xpetra::MultiVector<coordinate_type, LO, GO, NO> RealValuedMultiVector;
110 typedef Xpetra::MultiVectorFactory<coordinate_type, LO, GO, NO> RealValuedMultiVectorFactory;
111
112 const ParameterList& pL = GetParameterList();
113 std::string nspName = "Nullspace";
114 if (pL.isParameter("Nullspace name")) nspName = pL.get<std::string>("Nullspace name");
115
116 RCP<Matrix> Ptentative;
117 auto A = Get<RCP<Matrix>>(fineLevel, "A");
118 auto aggregates = Get<RCP<Aggregates>>(fineLevel, "Aggregates");
119 // No coarse DoFs so we need to bail by setting Ptentattive to null and returning
120 // This level will ultimately be removed in MueLu_Hierarchy_defs.h via a resize()
121 if (aggregates->GetNumGlobalAggregatesComputeIfNeeded() == 0) {
122 Ptentative = Teuchos::null;
123 Set(coarseLevel, "P", Ptentative);
124 return;
125 }
126
127 auto amalgInfo = Get<RCP<AmalgamationInfo>>(fineLevel, "UnAmalgamationInfo");
128 auto fineNullspace = Get<RCP<MultiVector>>(fineLevel, nspName);
129 auto coarseMap = Get<RCP<const Map>>(fineLevel, "CoarseMap");
130 RCP<RealValuedMultiVector> fineCoords;
131 if (bTransferCoordinates_) {
132 fineCoords = Get<RCP<RealValuedMultiVector>>(fineLevel, "Coordinates");
133 }
134
135 // FIXME: We should remove the NodeComm on levels past the threshold
136 if (fineLevel.IsAvailable("Node Comm")) {
137 RCP<const Teuchos::Comm<int>> nodeComm = Get<RCP<const Teuchos::Comm<int>>>(fineLevel, "Node Comm");
138 Set<RCP<const Teuchos::Comm<int>>>(coarseLevel, "Node Comm", nodeComm);
139 }
140
141 // NOTE: We check DomainMap here rather than RowMap because those are different for BlockCrs matrices
142 TEUCHOS_TEST_FOR_EXCEPTION(A->getDomainMap()->getLocalNumElements() != fineNullspace->getMap()->getLocalNumElements(),
143 Exceptions::RuntimeError, "MueLu::TentativePFactory::MakeTentative: Size mismatch between A and Nullspace");
144
145 RCP<MultiVector> coarseNullspace;
146 RCP<RealValuedMultiVector> coarseCoords;
147
148 if (bTransferCoordinates_) {
149 //*** Create the coarse coordinates ***
150 // First create the coarse map and coarse multivector
151 ArrayView<const GO> elementAList = coarseMap->getLocalElementList();
152 LO blkSize = 1;
153 if (rcp_dynamic_cast<const StridedMap>(coarseMap) != Teuchos::null) {
154 blkSize = rcp_dynamic_cast<const StridedMap>(coarseMap)->getFixedBlockSize();
155 }
156 GO indexBase = coarseMap->getIndexBase();
157 LO numCoarseNodes = Teuchos::as<LO>(elementAList.size() / blkSize);
158 Array<GO> nodeList(numCoarseNodes);
159 const int numDimensions = fineCoords->getNumVectors();
160
161 for (LO i = 0; i < numCoarseNodes; i++) {
162 nodeList[i] = (elementAList[i * blkSize] - indexBase) / blkSize + indexBase;
163 }
164 Teuchos::RCP<Teuchos::ParameterList> params = Teuchos::rcp(new Teuchos::ParameterList());
165 params->set("compute global constants", false);
166 RCP<const Map> coarseCoordsMap = MapFactory::Build(fineCoords->getMap()->lib(),
167 blkSize * coarseMap->getGlobalNumElements(),
168 nodeList,
169 indexBase,
170 fineCoords->getMap()->getComm(),
171 params);
172 coarseCoords = RealValuedMultiVectorFactory::Build(coarseCoordsMap, numDimensions);
173
174 // Create overlapped fine coordinates to reduce global communication
175 RCP<RealValuedMultiVector> ghostedCoords;
176 if (aggregates->AggregatesCrossProcessors()) {
177 RCP<const Map> aggMap = aggregates->GetMap();
178 RCP<const Import> importer = ImportFactory::Build(fineCoords->getMap(), aggMap);
179
180 ghostedCoords = RealValuedMultiVectorFactory::Build(aggMap, numDimensions);
181 ghostedCoords->doImport(*fineCoords, *importer, Xpetra::INSERT);
182 } else {
183 ghostedCoords = fineCoords;
184 }
185
186 // Get some info about aggregates
187 int myPID = coarseCoordsMap->getComm()->getRank();
188 LO numAggs = aggregates->GetNumAggregates();
189 ArrayRCP<LO> aggSizes = aggregates->ComputeAggregateSizesArrayRCP();
190 const ArrayRCP<const LO> vertex2AggID = aggregates->GetVertex2AggId()->getData(0);
191 const ArrayRCP<const LO> procWinner = aggregates->GetProcWinner()->getData(0);
192
193 // Fill in coarse coordinates
194 for (int dim = 0; dim < numDimensions; ++dim) {
195 ArrayRCP<const coordinate_type> fineCoordsData = ghostedCoords->getData(dim);
196 ArrayRCP<coordinate_type> coarseCoordsData = coarseCoords->getDataNonConst(dim);
197
198 for (LO lnode = 0; lnode < Teuchos::as<LO>(vertex2AggID.size()); lnode++) {
199 if (procWinner[lnode] == myPID &&
200 lnode < fineCoordsData.size() &&
201 vertex2AggID[lnode] < coarseCoordsData.size() &&
202 Teuchos::ScalarTraits<coordinate_type>::isnaninf(fineCoordsData[lnode]) == false) {
203 coarseCoordsData[vertex2AggID[lnode]] += fineCoordsData[lnode];
204 }
205 }
206 for (LO agg = 0; agg < numAggs; agg++) {
207 coarseCoordsData[agg] /= aggSizes[agg];
208 }
209 }
210 }
211
212 if (!aggregates->AggregatesCrossProcessors()) {
213 if (Xpetra::Helpers<SC, LO, GO, NO>::isTpetraBlockCrs(A)) {
214 BuildPuncoupledBlockCrs(A, aggregates, amalgInfo, fineNullspace, coarseMap, Ptentative, coarseNullspace, coarseLevel.GetLevelID());
215 } else {
216 BuildPuncoupled(A, aggregates, amalgInfo, fineNullspace, coarseMap, Ptentative, coarseNullspace, coarseLevel.GetLevelID());
217 }
218 } else
219 BuildPcoupled(A, aggregates, amalgInfo, fineNullspace, coarseMap, Ptentative, coarseNullspace);
220
221 // If available, use striding information of fine level matrix A for range
222 // map and coarseMap as domain map; otherwise use plain range map of
223 // Ptent = plain range map of A for range map and coarseMap as domain map.
224 // NOTE:
225 // The latter is not really safe, since there is no striding information
226 // for the range map. This is not really a problem, since striding
227 // information is always available on the intermedium levels and the
228 // coarsest levels.
229 if (A->IsView("stridedMaps") == true)
230 Ptentative->CreateView("stridedMaps", A->getRowMap("stridedMaps"), coarseMap);
231
232 if (bTransferCoordinates_) {
233 Set(coarseLevel, "Coordinates", coarseCoords);
234 }
235 Set(coarseLevel, "Nullspace", coarseNullspace);
236 Set(coarseLevel, "P", Ptentative);
237
238 if (pL.get<bool>("sa: keep tentative prolongator")) {
239 coarseLevel.Set("Ptent", Ptentative, NoFactory::get());
240 coarseLevel.AddKeepFlag("Ptent", NoFactory::get(), MueLu::Final);
241 }
242
243 if (IsPrint(Statistics2)) {
244 RCP<ParameterList> params = rcp(new ParameterList());
245 params->set("printLoadBalancingInfo", true);
246 GetOStream(Statistics2) << PerfUtils::PrintMatrixInfo(*Ptentative, "Ptent", params);
247 }
248}
249
250template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
252 BuildPuncoupled(RCP<Matrix> A, RCP<Aggregates> aggregates, RCP<AmalgamationInfo> amalgInfo, RCP<MultiVector> fineNullspace,
253 RCP<const Map> coarseMap, RCP<Matrix>& Ptentative, RCP<MultiVector>& coarseNullspace, const int levelID) const {
254 RCP<const Map> rowMap = A->getRowMap();
255 RCP<const Map> colMap = A->getColMap();
256 const size_t numRows = rowMap->getLocalNumElements();
257
258 typedef Teuchos::ScalarTraits<SC> STS;
259 typedef typename STS::magnitudeType Magnitude;
260 const SC zero = STS::zero();
261 const SC one = STS::one();
262 const LO INVALID = Teuchos::OrdinalTraits<LO>::invalid();
263
264 const GO numAggs = aggregates->GetNumAggregates();
265 const size_t NSDim = fineNullspace->getNumVectors();
266 ArrayRCP<LO> aggSizes = aggregates->ComputeAggregateSizesArrayRCP();
267
268 // Sanity checking
269 const ParameterList& pL = GetParameterList();
270 const bool& doQRStep = pL.get<bool>("tentative: calculate qr");
271 const bool& constantColSums = pL.get<bool>("tentative: constant column sums");
272
273 TEUCHOS_TEST_FOR_EXCEPTION(doQRStep && constantColSums, Exceptions::RuntimeError,
274 "MueLu::TentativePFactory::MakeTentative: cannot use 'constant column sums' and 'calculate qr' at the same time");
275
276 // Aggregates map is based on the amalgamated column map
277 // We can skip global-to-local conversion if LIDs in row map are
278 // same as LIDs in column map
279 bool goodMap = MueLu::Utilities<SC, LO, GO, NO>::MapsAreNested(*rowMap, *colMap);
280
281 // Create a lookup table to determine the rows (fine DOFs) that belong to a given aggregate.
282 // aggStart is a pointer into aggToRowMapLO
283 // aggStart[i]..aggStart[i+1] are indices into aggToRowMapLO
284 // aggToRowMapLO[aggStart[i]]..aggToRowMapLO[aggStart[i+1]-1] are the DOFs in aggregate i
285 ArrayRCP<LO> aggStart;
286 ArrayRCP<LO> aggToRowMapLO;
287 ArrayRCP<GO> aggToRowMapGO;
288 if (goodMap) {
289 amalgInfo->UnamalgamateAggregatesLO(*aggregates, aggStart, aggToRowMapLO);
290 GetOStream(Runtime1) << "Column map is consistent with the row map, good." << std::endl;
291
292 } else {
293 amalgInfo->UnamalgamateAggregates(*aggregates, aggStart, aggToRowMapGO);
294 GetOStream(Warnings0) << "Column map is not consistent with the row map\n"
295 << "using GO->LO conversion with performance penalty" << std::endl;
296 }
297 coarseNullspace = MultiVectorFactory::Build(coarseMap, NSDim);
298
299 // Pull out the nullspace vectors so that we can have random access.
300 ArrayRCP<ArrayRCP<const SC>> fineNS(NSDim);
301 ArrayRCP<ArrayRCP<SC>> coarseNS(NSDim);
302 for (size_t i = 0; i < NSDim; i++) {
303 fineNS[i] = fineNullspace->getData(i);
304 if (coarseMap->getLocalNumElements() > 0)
305 coarseNS[i] = coarseNullspace->getDataNonConst(i);
306 }
307
308 size_t nnzEstimate = numRows * NSDim;
309
310 // Time to construct the matrix and fill in the values
311 Ptentative = rcp(new CrsMatrixWrap(rowMap, coarseMap, 0));
312 RCP<CrsMatrix> PtentCrs = toCrsMatrix(Ptentative);
313
314 ArrayRCP<size_t> iaPtent;
315 ArrayRCP<LO> jaPtent;
316 ArrayRCP<SC> valPtent;
317
318 PtentCrs->allocateAllValues(nnzEstimate, iaPtent, jaPtent, valPtent);
319
320 ArrayView<size_t> ia = iaPtent();
321 ArrayView<LO> ja = jaPtent();
322 ArrayView<SC> val = valPtent();
323
324 ia[0] = 0;
325 for (size_t i = 1; i <= numRows; i++)
326 ia[i] = ia[i - 1] + NSDim;
327
328 for (size_t j = 0; j < nnzEstimate; j++) {
329 ja[j] = INVALID;
330 val[j] = zero;
331 }
332
333 if (doQRStep) {
335 // Standard aggregate-wise QR //
337 for (GO agg = 0; agg < numAggs; agg++) {
338 LO aggSize = aggStart[agg + 1] - aggStart[agg];
339
340 Xpetra::global_size_t offset = agg * NSDim;
341
342 // Extract the piece of the nullspace corresponding to the aggregate, and
343 // put it in the flat array, "localQR" (in column major format) for the
344 // QR routine.
345 Teuchos::SerialDenseMatrix<LO, SC> localQR(aggSize, NSDim);
346 if (goodMap) {
347 for (size_t j = 0; j < NSDim; j++)
348 for (LO k = 0; k < aggSize; k++)
349 localQR(k, j) = fineNS[j][aggToRowMapLO[aggStart[agg] + k]];
350 } else {
351 for (size_t j = 0; j < NSDim; j++)
352 for (LO k = 0; k < aggSize; k++)
353 localQR(k, j) = fineNS[j][rowMap->getLocalElement(aggToRowMapGO[aggStart[agg] + k])];
354 }
355
356 // Test for zero columns
357 for (size_t j = 0; j < NSDim; j++) {
358 bool bIsZeroNSColumn = true;
359
360 for (LO k = 0; k < aggSize; k++)
361 if (localQR(k, j) != zero)
362 bIsZeroNSColumn = false;
363
364 TEUCHOS_TEST_FOR_EXCEPTION(bIsZeroNSColumn == true, Exceptions::RuntimeError,
365 "MueLu::TentativePFactory::MakeTentative: fine level NS part has a zero column in NS column " << j);
366 }
367
368 // Calculate QR decomposition (standard)
369 // NOTE: Q is stored in localQR and R is stored in coarseNS
370 if (aggSize >= Teuchos::as<LO>(NSDim)) {
371 if (NSDim == 1) {
372 // Only one nullspace vector, calculate Q and R by hand
373 Magnitude norm = STS::magnitude(zero);
374 for (size_t k = 0; k < Teuchos::as<size_t>(aggSize); k++)
375 norm += STS::magnitude(localQR(k, 0) * localQR(k, 0));
376 norm = Teuchos::ScalarTraits<Magnitude>::squareroot(norm);
377
378 // R = norm
379 coarseNS[0][offset] = norm;
380
381 // Q = localQR(:,0)/norm
382 for (LO i = 0; i < aggSize; i++)
383 localQR(i, 0) /= norm;
384
385 } else {
386 Teuchos::SerialQRDenseSolver<LO, SC> qrSolver;
387 qrSolver.setMatrix(Teuchos::rcp(&localQR, false));
388 qrSolver.factor();
389
390 // R = upper triangular part of localQR
391 for (size_t j = 0; j < NSDim; j++)
392 for (size_t k = 0; k <= j; k++)
393 coarseNS[j][offset + k] = localQR(k, j); // TODO is offset+k the correct local ID?!
394
395 // Calculate Q, the tentative prolongator.
396 // The Lapack GEQRF call only works for myAggsize >= NSDim
397 qrSolver.formQ();
398 Teuchos::RCP<Teuchos::SerialDenseMatrix<LO, SC>> qFactor = qrSolver.getQ();
399 for (size_t j = 0; j < NSDim; j++)
400 for (size_t i = 0; i < Teuchos::as<size_t>(aggSize); i++)
401 localQR(i, j) = (*qFactor)(i, j);
402 }
403
404 } else {
405 // Special handling for aggSize < NSDim (i.e. single node aggregates in structural mechanics)
406
407 // The local QR decomposition is not possible in the "overconstrained"
408 // case (i.e. number of columns in localQR > number of rows), which
409 // corresponds to #DOFs in Aggregate < NSDim. For usual problems this
410 // is only possible for single node aggregates in structural mechanics.
411 // (Similar problems may arise in discontinuous Galerkin problems...)
412 // We bypass the QR decomposition and use an identity block in the
413 // tentative prolongator for the single node aggregate and transfer the
414 // corresponding fine level null space information 1-to-1 to the coarse
415 // level null space part.
416
417 // NOTE: The resulting tentative prolongation operator has
418 // (aggSize*DofsPerNode-NSDim) zero columns leading to a singular
419 // coarse level operator A. To deal with that one has the following
420 // options:
421 // - Use the "RepairMainDiagonal" flag in the RAPFactory (default:
422 // false) to add some identity block to the diagonal of the zero rows
423 // in the coarse level operator A, such that standard level smoothers
424 // can be used again.
425 // - Use special (projection-based) level smoothers, which can deal
426 // with singular matrices (very application specific)
427 // - Adapt the code below to avoid zero columns. However, we do not
428 // support a variable number of DOFs per node in MueLu/Xpetra which
429 // makes the implementation really hard.
430
431 // R = extended (by adding identity rows) localQR
432 for (size_t j = 0; j < NSDim; j++)
433 for (size_t k = 0; k < NSDim; k++)
434 if (k < as<size_t>(aggSize))
435 coarseNS[j][offset + k] = localQR(k, j);
436 else
437 coarseNS[j][offset + k] = (k == j ? one : zero);
438
439 // Q = I (rectangular)
440 for (size_t i = 0; i < as<size_t>(aggSize); i++)
441 for (size_t j = 0; j < NSDim; j++)
442 localQR(i, j) = (j == i ? one : zero);
443 }
444
445 // Process each row in the local Q factor
446 // FIXME: What happens if maps are blocked?
447 for (LO j = 0; j < aggSize; j++) {
448 LO localRow = (goodMap ? aggToRowMapLO[aggStart[agg] + j] : rowMap->getLocalElement(aggToRowMapGO[aggStart[agg] + j]));
449
450 size_t rowStart = ia[localRow];
451 for (size_t k = 0, lnnz = 0; k < NSDim; k++) {
452 // Skip zeros (there may be plenty of them, i.e., NSDim > 1 or boundary conditions)
453 if (localQR(j, k) != zero) {
454 ja[rowStart + lnnz] = offset + k;
455 val[rowStart + lnnz] = localQR(j, k);
456 lnnz++;
457 }
458 }
459 }
460 }
461
462 } else {
463 GetOStream(Runtime1) << "TentativePFactory : bypassing local QR phase" << std::endl;
464 if (NSDim > 1)
465 GetOStream(Warnings0) << "TentativePFactory : for nontrivial nullspace, this may degrade performance" << std::endl;
467 // "no-QR" option //
469 // Local Q factor is just the fine nullspace support over the current aggregate.
470 // Local R factor is the identity.
471 // TODO I have not implemented any special handling for aggregates that are too
472 // TODO small to locally support the nullspace, as is done in the standard QR
473 // TODO case above.
474 if (goodMap) {
475 for (GO agg = 0; agg < numAggs; agg++) {
476 const LO aggSize = aggStart[agg + 1] - aggStart[agg];
477 Xpetra::global_size_t offset = agg * NSDim;
478
479 // Process each row in the local Q factor
480 // FIXME: What happens if maps are blocked?
481 for (LO j = 0; j < aggSize; j++) {
482 // TODO Here I do not check for a zero nullspace column on the aggregate.
483 // as is done in the standard QR case.
484
485 const LO localRow = aggToRowMapLO[aggStart[agg] + j];
486
487 const size_t rowStart = ia[localRow];
488
489 for (size_t k = 0, lnnz = 0; k < NSDim; k++) {
490 // Skip zeros (there may be plenty of them, i.e., NSDim > 1 or boundary conditions)
491 SC qr_jk = fineNS[k][aggToRowMapLO[aggStart[agg] + j]];
492 if (constantColSums) qr_jk = qr_jk / (Magnitude)aggSizes[agg];
493 if (qr_jk != zero) {
494 ja[rowStart + lnnz] = offset + k;
495 val[rowStart + lnnz] = qr_jk;
496 lnnz++;
497 }
498 }
499 }
500 for (size_t j = 0; j < NSDim; j++)
501 coarseNS[j][offset + j] = one;
502 } // for (GO agg = 0; agg < numAggs; agg++)
503
504 } else {
505 for (GO agg = 0; agg < numAggs; agg++) {
506 const LO aggSize = aggStart[agg + 1] - aggStart[agg];
507 Xpetra::global_size_t offset = agg * NSDim;
508 for (LO j = 0; j < aggSize; j++) {
509 const LO localRow = rowMap->getLocalElement(aggToRowMapGO[aggStart[agg] + j]);
510
511 const size_t rowStart = ia[localRow];
512
513 for (size_t k = 0, lnnz = 0; k < NSDim; ++k) {
514 // Skip zeros (there may be plenty of them, i.e., NSDim > 1 or boundary conditions)
515 SC qr_jk = fineNS[k][rowMap->getLocalElement(aggToRowMapGO[aggStart[agg] + j])];
516 if (constantColSums) qr_jk = qr_jk / (Magnitude)aggSizes[agg];
517 if (qr_jk != zero) {
518 ja[rowStart + lnnz] = offset + k;
519 val[rowStart + lnnz] = qr_jk;
520 lnnz++;
521 }
522 }
523 }
524 for (size_t j = 0; j < NSDim; j++)
525 coarseNS[j][offset + j] = one;
526 } // for (GO agg = 0; agg < numAggs; agg++)
527
528 } // if (goodmap) else ...
529
530 } // if doQRStep ... else
531
532 // Compress storage (remove all INVALID, which happen when we skip zeros)
533 // We do that in-place
534 size_t ia_tmp = 0, nnz = 0;
535 for (size_t i = 0; i < numRows; i++) {
536 for (size_t j = ia_tmp; j < ia[i + 1]; j++)
537 if (ja[j] != INVALID) {
538 ja[nnz] = ja[j];
539 val[nnz] = val[j];
540 nnz++;
541 }
542 ia_tmp = ia[i + 1];
543 ia[i + 1] = nnz;
544 }
545 if (rowMap->lib() == Xpetra::UseTpetra) {
546 // - Cannot resize for Epetra, as it checks for same pointers
547 // - Need to resize for Tpetra, as it check ().size() == ia[numRows]
548 // NOTE: these invalidate ja and val views
549 jaPtent.resize(nnz);
550 valPtent.resize(nnz);
551 }
552
553 GetOStream(Runtime1) << "TentativePFactory : aggregates do not cross process boundaries" << std::endl;
554
555 PtentCrs->setAllValues(iaPtent, jaPtent, valPtent);
556
557 // Managing labels & constants for ESFC
558 RCP<ParameterList> FCparams;
559 if (pL.isSublist("matrixmatrix: kernel params"))
560 FCparams = rcp(new ParameterList(pL.sublist("matrixmatrix: kernel params")));
561 else
562 FCparams = rcp(new ParameterList);
563 // By default, we don't need global constants for TentativeP
564 FCparams->set("compute global constants", FCparams->get("compute global constants", false));
565 std::string levelIDs = toString(levelID);
566 FCparams->set("Timer Label", std::string("MueLu::TentativeP-") + levelIDs);
567 RCP<const Export> dummy_e;
568 RCP<const Import> dummy_i;
569
570 PtentCrs->expertStaticFillComplete(coarseMap, A->getDomainMap(), dummy_i, dummy_e, FCparams);
571}
572
573template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
575 BuildPuncoupledBlockCrs(RCP<Matrix> A, RCP<Aggregates> aggregates, RCP<AmalgamationInfo> amalgInfo, RCP<MultiVector> fineNullspace,
576 RCP<const Map> coarsePointMap, RCP<Matrix>& Ptentative, RCP<MultiVector>& coarseNullspace, const int levelID) const {
577 /* This routine generates a BlockCrs P for a BlockCrs A. There are a few assumptions here, which meet the use cases we care about, but could
578 be generalized later, if we ever need to do so:
579 1) Null space dimension === block size of matrix: So no elasticity right now
580 2) QR is not supported: Under assumption #1, this shouldn't cause problems.
581 3) Maps are "good": Aka the first chunk of the ColMap is the RowMap.
582
583 These assumptions keep our code way simpler and still support the use cases we actually care about.
584 */
585
586 RCP<const Map> rowMap = A->getRowMap();
587 RCP<const Map> rangeMap = A->getRangeMap();
588 RCP<const Map> colMap = A->getColMap();
589 // const size_t numFinePointRows = rangeMap->getLocalNumElements();
590 const size_t numFineBlockRows = rowMap->getLocalNumElements();
591
592 typedef Teuchos::ScalarTraits<SC> STS;
593 // typedef typename STS::magnitudeType Magnitude;
594 const SC zero = STS::zero();
595 const SC one = STS::one();
596 const LO INVALID = Teuchos::OrdinalTraits<LO>::invalid();
597
598 const GO numAggs = aggregates->GetNumAggregates();
599 const size_t NSDim = fineNullspace->getNumVectors();
600 ArrayRCP<LO> aggSizes = aggregates->ComputeAggregateSizesArrayRCP();
601
602 // Need to generate the coarse block map
603 // NOTE: We assume NSDim == block size here
604 // NOTE: We also assume that coarseMap has contiguous GIDs
605 // const size_t numCoarsePointRows = coarsePointMap->getLocalNumElements();
606 const size_t numCoarseBlockRows = coarsePointMap->getLocalNumElements() / NSDim;
607 RCP<const Map> coarseBlockMap = MapFactory::Build(coarsePointMap->lib(),
608 Teuchos::OrdinalTraits<Xpetra::global_size_t>::invalid(),
609 numCoarseBlockRows,
610 coarsePointMap->getIndexBase(),
611 coarsePointMap->getComm());
612 // Sanity checking
613 const ParameterList& pL = GetParameterList();
614 const bool& doQRStep = pL.get<bool>("tentative: calculate qr");
615 const bool& constantColSums = pL.get<bool>("tentative: constant column sums");
616
617 TEUCHOS_TEST_FOR_EXCEPTION(doQRStep && constantColSums, Exceptions::RuntimeError,
618 "MueLu::TentativePFactory::MakeTentative: cannot use 'constant column sums' and 'calculate qr' at the same time");
619
620 // The aggregates use the amalgamated column map, which in this case is what we want
621
622 // Aggregates map is based on the amalgamated column map
623 // We can skip global-to-local conversion if LIDs in row map are
624 // same as LIDs in column map
625 bool goodMap = MueLu::Utilities<SC, LO, GO, NO>::MapsAreNested(*rowMap, *colMap);
626
627 // Create a lookup table to determine the rows (fine DOFs) that belong to a given aggregate.
628 // aggStart is a pointer into aggToRowMapLO
629 // aggStart[i]..aggStart[i+1] are indices into aggToRowMapLO
630 // aggToRowMapLO[aggStart[i]]..aggToRowMapLO[aggStart[i+1]-1] are the DOFs in aggregate i
631 ArrayRCP<LO> aggStart;
632 ArrayRCP<LO> aggToRowMapLO;
633 ArrayRCP<GO> aggToRowMapGO;
634 if (goodMap) {
635 amalgInfo->UnamalgamateAggregatesLO(*aggregates, aggStart, aggToRowMapLO);
636 GetOStream(Runtime1) << "Column map is consistent with the row map, good." << std::endl;
637 } else {
638 throw std::runtime_error("TentativePFactory::PuncoupledBlockCrs: Inconsistent maps not currently supported");
639 }
640
641 coarseNullspace = MultiVectorFactory::Build(coarsePointMap, NSDim);
642
643 // Pull out the nullspace vectors so that we can have random access.
644 ArrayRCP<ArrayRCP<const SC>> fineNS(NSDim);
645 ArrayRCP<ArrayRCP<SC>> coarseNS(NSDim);
646 for (size_t i = 0; i < NSDim; i++) {
647 fineNS[i] = fineNullspace->getData(i);
648 if (coarsePointMap->getLocalNumElements() > 0)
649 coarseNS[i] = coarseNullspace->getDataNonConst(i);
650 }
651
652 // BlockCrs requires that we build the (block) graph first, so let's do that...
653 // NOTE: Because we're assuming that the NSDim == BlockSize, we only have one
654 // block non-zero per row in the matrix;
655 RCP<CrsGraph> BlockGraph = CrsGraphFactory::Build(rowMap, coarseBlockMap, 0);
656 ArrayRCP<size_t> iaPtent;
657 ArrayRCP<LO> jaPtent;
658 BlockGraph->allocateAllIndices(numFineBlockRows, iaPtent, jaPtent);
659 ArrayView<size_t> ia = iaPtent();
660 ArrayView<LO> ja = jaPtent();
661
662 for (size_t i = 0; i < numFineBlockRows; i++) {
663 ia[i] = i;
664 ja[i] = INVALID;
665 }
666 ia[numCoarseBlockRows] = numCoarseBlockRows;
667
668 for (GO agg = 0; agg < numAggs; agg++) {
669 LO aggSize = aggStart[agg + 1] - aggStart[agg];
670 Xpetra::global_size_t offset = agg;
671
672 for (LO j = 0; j < aggSize; j++) {
673 // FIXME: Allow for bad maps
674 const LO localRow = aggToRowMapLO[aggStart[agg] + j];
675 const size_t rowStart = ia[localRow];
676 ja[rowStart] = offset;
677 }
678 }
679
680 // Compress storage (remove all INVALID, which happen when we skip zeros)
681 // We do that in-place
682 size_t ia_tmp = 0, nnz = 0;
683 for (size_t i = 0; i < numFineBlockRows; i++) {
684 for (size_t j = ia_tmp; j < ia[i + 1]; j++)
685 if (ja[j] != INVALID) {
686 ja[nnz] = ja[j];
687 nnz++;
688 }
689 ia_tmp = ia[i + 1];
690 ia[i + 1] = nnz;
691 }
692
693 if (rowMap->lib() == Xpetra::UseTpetra) {
694 // - Cannot resize for Epetra, as it checks for same pointers
695 // - Need to resize for Tpetra, as it check ().size() == ia[numRows]
696 // NOTE: these invalidate ja and val views
697 jaPtent.resize(nnz);
698 }
699
700 GetOStream(Runtime1) << "TentativePFactory : generating block graph" << std::endl;
701 BlockGraph->setAllIndices(iaPtent, jaPtent);
702
703 // Managing labels & constants for ESFC
704 {
705 RCP<ParameterList> FCparams;
706 if (pL.isSublist("matrixmatrix: kernel params"))
707 FCparams = rcp(new ParameterList(pL.sublist("matrixmatrix: kernel params")));
708 else
709 FCparams = rcp(new ParameterList);
710 // By default, we don't need global constants for TentativeP, but we do want it for the graph
711 // if we're printing statistics, so let's leave it on for now.
712 FCparams->set("compute global constants", FCparams->get("compute global constants", true));
713 std::string levelIDs = toString(levelID);
714 FCparams->set("Timer Label", std::string("MueLu::TentativeP-") + levelIDs);
715 RCP<const Export> dummy_e;
716 RCP<const Import> dummy_i;
717 BlockGraph->expertStaticFillComplete(coarseBlockMap, rowMap, dummy_i, dummy_e, FCparams);
718 }
719
720 // Now let's make a BlockCrs Matrix
721 // NOTE: Assumes block size== NSDim
722 RCP<Xpetra::CrsMatrix<SC, LO, GO, NO>> P_xpetra = Xpetra::CrsMatrixFactory<SC, LO, GO, NO>::BuildBlock(BlockGraph, coarsePointMap, rangeMap, NSDim);
723 RCP<Xpetra::TpetraBlockCrsMatrix<SC, LO, GO, NO>> P_tpetra = rcp_dynamic_cast<Xpetra::TpetraBlockCrsMatrix<SC, LO, GO, NO>>(P_xpetra);
724 if (P_tpetra.is_null()) throw std::runtime_error("BuildPUncoupled: Matrix factory did not return a Tpetra::BlockCrsMatrix");
725 RCP<CrsMatrixWrap> P_wrap = rcp(new CrsMatrixWrap(P_xpetra));
726
728 // "no-QR" option //
730 // Local Q factor is just the fine nullspace support over the current aggregate.
731 // Local R factor is the identity.
732 // NOTE: We're not going to do a QR here as we're assuming that blocksize == NSDim
733 // NOTE: "goodMap" case only
734 Teuchos::Array<Scalar> block(NSDim * NSDim, zero);
735 Teuchos::Array<LO> bcol(1);
736
737 GetOStream(Runtime1) << "TentativePFactory : bypassing local QR phase" << std::endl;
738 for (LO agg = 0; agg < numAggs; agg++) {
739 bcol[0] = agg;
740 const LO aggSize = aggStart[agg + 1] - aggStart[agg];
741 Xpetra::global_size_t offset = agg * NSDim;
742
743 // Process each row in the local Q factor
744 // NOTE: Blocks are in row-major order
745 for (LO j = 0; j < aggSize; j++) {
746 const LO localBlockRow = aggToRowMapLO[aggStart[agg] + j];
747
748 for (size_t r = 0; r < NSDim; r++) {
749 LO localPointRow = localBlockRow * NSDim + r;
750 for (size_t c = 0; c < NSDim; c++)
751 block[r * NSDim + c] = fineNS[c][localPointRow];
752 }
753 // NOTE: Assumes columns==aggs and are ordered sequentially
754 P_tpetra->replaceLocalValues(localBlockRow, bcol(), block());
755
756 } // end aggSize
757
758 for (size_t j = 0; j < NSDim; j++)
759 coarseNS[j][offset + j] = one;
760
761 } // for (GO agg = 0; agg < numAggs; agg++)
762
763 Ptentative = P_wrap;
764}
765
766template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
768 BuildPcoupled(RCP<Matrix> A, RCP<Aggregates> aggregates, RCP<AmalgamationInfo> amalgInfo, RCP<MultiVector> fineNullspace,
769 RCP<const Map> coarseMap, RCP<Matrix>& Ptentative, RCP<MultiVector>& coarseNullspace) const {
770 typedef Teuchos::ScalarTraits<SC> STS;
771 typedef typename STS::magnitudeType Magnitude;
772 const SC zero = STS::zero();
773 const SC one = STS::one();
774
775 // number of aggregates
776 GO numAggs = aggregates->GetNumAggregates();
777
778 // Create a lookup table to determine the rows (fine DOFs) that belong to a given aggregate.
779 // aggStart is a pointer into aggToRowMap
780 // aggStart[i]..aggStart[i+1] are indices into aggToRowMap
781 // aggToRowMap[aggStart[i]]..aggToRowMap[aggStart[i+1]-1] are the DOFs in aggregate i
782 ArrayRCP<LO> aggStart;
783 ArrayRCP<GO> aggToRowMap;
784 amalgInfo->UnamalgamateAggregates(*aggregates, aggStart, aggToRowMap);
785
786 // find size of the largest aggregate
787 LO maxAggSize = 0;
788 for (GO i = 0; i < numAggs; ++i) {
789 LO sizeOfThisAgg = aggStart[i + 1] - aggStart[i];
790 if (sizeOfThisAgg > maxAggSize) maxAggSize = sizeOfThisAgg;
791 }
792
793 // dimension of fine level nullspace
794 const size_t NSDim = fineNullspace->getNumVectors();
795
796 // index base for coarse Dof map (usually 0)
797 GO indexBase = A->getRowMap()->getIndexBase();
798
799 const RCP<const Map> nonUniqueMap = amalgInfo->ComputeUnamalgamatedImportDofMap(*aggregates);
800 const RCP<const Map> uniqueMap = A->getDomainMap();
801 RCP<const Import> importer = ImportFactory::Build(uniqueMap, nonUniqueMap);
802 RCP<MultiVector> fineNullspaceWithOverlap = MultiVectorFactory::Build(nonUniqueMap, NSDim);
803 fineNullspaceWithOverlap->doImport(*fineNullspace, *importer, Xpetra::INSERT);
804
805 // Pull out the nullspace vectors so that we can have random access.
806 ArrayRCP<ArrayRCP<const SC>> fineNS(NSDim);
807 for (size_t i = 0; i < NSDim; ++i)
808 fineNS[i] = fineNullspaceWithOverlap->getData(i);
809
810 // Allocate storage for the coarse nullspace.
811 coarseNullspace = MultiVectorFactory::Build(coarseMap, NSDim);
812
813 ArrayRCP<ArrayRCP<SC>> coarseNS(NSDim);
814 for (size_t i = 0; i < NSDim; ++i)
815 if (coarseMap->getLocalNumElements() > 0) coarseNS[i] = coarseNullspace->getDataNonConst(i);
816
817 // This makes the rowmap of Ptent the same as that of A->
818 // This requires moving some parts of some local Q's to other processors
819 // because aggregates can span processors.
820 RCP<const Map> rowMapForPtent = A->getRowMap();
821 const Map& rowMapForPtentRef = *rowMapForPtent;
822
823 // Set up storage for the rows of the local Qs that belong to other processors.
824 // FIXME This is inefficient and could be done within the main loop below with std::vector's.
825 RCP<const Map> colMap = A->getColMap();
826
827 RCP<const Map> ghostQMap;
828 RCP<MultiVector> ghostQvalues;
829 Array<RCP<Xpetra::Vector<GO, LO, GO, Node>>> ghostQcolumns;
830 RCP<Xpetra::Vector<GO, LO, GO, Node>> ghostQrowNums;
831 ArrayRCP<ArrayRCP<SC>> ghostQvals;
832 ArrayRCP<ArrayRCP<GO>> ghostQcols;
833 ArrayRCP<GO> ghostQrows;
834
835 Array<GO> ghostGIDs;
836 for (LO j = 0; j < numAggs; ++j) {
837 for (LO k = aggStart[j]; k < aggStart[j + 1]; ++k) {
838 if (rowMapForPtentRef.isNodeGlobalElement(aggToRowMap[k]) == false) {
839 ghostGIDs.push_back(aggToRowMap[k]);
840 }
841 }
842 }
843 ghostQMap = MapFactory::Build(A->getRowMap()->lib(),
844 Teuchos::OrdinalTraits<Xpetra::global_size_t>::invalid(),
845 ghostGIDs,
846 indexBase, A->getRowMap()->getComm()); // JG:Xpetra::global_size_t>?
847 // Vector to hold bits of Q that go to other processors.
848 ghostQvalues = MultiVectorFactory::Build(ghostQMap, NSDim);
849 // Note that Epetra does not support MultiVectors templated on Scalar != double.
850 // So to work around this, we allocate an array of Vectors. This shouldn't be too
851 // expensive, as the number of Vectors is NSDim.
852 ghostQcolumns.resize(NSDim);
853 for (size_t i = 0; i < NSDim; ++i)
854 ghostQcolumns[i] = Xpetra::VectorFactory<GO, LO, GO, Node>::Build(ghostQMap);
855 ghostQrowNums = Xpetra::VectorFactory<GO, LO, GO, Node>::Build(ghostQMap);
856 if (ghostQvalues->getLocalLength() > 0) {
857 ghostQvals.resize(NSDim);
858 ghostQcols.resize(NSDim);
859 for (size_t i = 0; i < NSDim; ++i) {
860 ghostQvals[i] = ghostQvalues->getDataNonConst(i);
861 ghostQcols[i] = ghostQcolumns[i]->getDataNonConst(0);
862 }
863 ghostQrows = ghostQrowNums->getDataNonConst(0);
864 }
865
866 // importer to handle moving Q
867 importer = ImportFactory::Build(ghostQMap, A->getRowMap());
868
869 // Dense QR solver
870 Teuchos::SerialQRDenseSolver<LO, SC> qrSolver;
871
872 // Allocate temporary storage for the tentative prolongator.
873 Array<GO> globalColPtr(maxAggSize * NSDim, 0);
874 Array<LO> localColPtr(maxAggSize * NSDim, 0);
875 Array<SC> valPtr(maxAggSize * NSDim, 0.);
876
877 // Create column map for Ptent, estimate local #nonzeros in Ptent, and create Ptent itself.
878 const Map& coarseMapRef = *coarseMap;
879
880 // For the 3-arrays constructor
881 ArrayRCP<size_t> ptent_rowptr;
882 ArrayRCP<LO> ptent_colind;
883 ArrayRCP<Scalar> ptent_values;
884
885 // Because ArrayRCPs are slow...
886 ArrayView<size_t> rowptr_v;
887 ArrayView<LO> colind_v;
888 ArrayView<Scalar> values_v;
889
890 // For temporary usage
891 Array<size_t> rowptr_temp;
892 Array<LO> colind_temp;
893 Array<Scalar> values_temp;
894
895 RCP<CrsMatrix> PtentCrs;
896
897 RCP<CrsMatrixWrap> PtentCrsWrap = rcp(new CrsMatrixWrap(rowMapForPtent, NSDim));
898 PtentCrs = PtentCrsWrap->getCrsMatrix();
899 Ptentative = PtentCrsWrap;
900
901 //*****************************************************************
902 // Loop over all aggregates and calculate local QR decompositions.
903 //*****************************************************************
904 GO qctr = 0; // for indexing into Ptent data vectors
905 const Map& nonUniqueMapRef = *nonUniqueMap;
906
907 size_t total_nnz_count = 0;
908
909 for (GO agg = 0; agg < numAggs; ++agg) {
910 LO myAggSize = aggStart[agg + 1] - aggStart[agg];
911 // For each aggregate, extract the corresponding piece of the nullspace and put it in the flat array,
912 // "localQR" (in column major format) for the QR routine.
913 Teuchos::SerialDenseMatrix<LO, SC> localQR(myAggSize, NSDim);
914 for (size_t j = 0; j < NSDim; ++j) {
915 bool bIsZeroNSColumn = true;
916 for (LO k = 0; k < myAggSize; ++k) {
917 // aggToRowMap[aggPtr[i]+k] is the kth DOF in the ith aggregate
918 // fineNS[j][n] is the nth entry in the jth NS vector
919 try {
920 SC nsVal = fineNS[j][nonUniqueMapRef.getLocalElement(aggToRowMap[aggStart[agg] + k])]; // extract information from fine level NS
921 localQR(k, j) = nsVal;
922 if (nsVal != zero) bIsZeroNSColumn = false;
923 } catch (...) {
924 GetOStream(Runtime1, -1) << "length of fine level nsp: " << fineNullspace->getGlobalLength() << std::endl;
925 GetOStream(Runtime1, -1) << "length of fine level nsp w overlap: " << fineNullspaceWithOverlap->getGlobalLength() << std::endl;
926 GetOStream(Runtime1, -1) << "(local?) aggId=" << agg << std::endl;
927 GetOStream(Runtime1, -1) << "aggSize=" << myAggSize << std::endl;
928 GetOStream(Runtime1, -1) << "agg DOF=" << k << std::endl;
929 GetOStream(Runtime1, -1) << "NS vector j=" << j << std::endl;
930 GetOStream(Runtime1, -1) << "j*myAggSize + k = " << j * myAggSize + k << std::endl;
931 GetOStream(Runtime1, -1) << "aggToRowMap[" << agg << "][" << k << "] = " << aggToRowMap[aggStart[agg] + k] << std::endl;
932 GetOStream(Runtime1, -1) << "id aggToRowMap[agg][k]=" << aggToRowMap[aggStart[agg] + k] << " is global element in nonUniqueMap = " << nonUniqueMapRef.isNodeGlobalElement(aggToRowMap[aggStart[agg] + k]) << std::endl;
933 GetOStream(Runtime1, -1) << "colMap local id aggToRowMap[agg][k]=" << nonUniqueMapRef.getLocalElement(aggToRowMap[aggStart[agg] + k]) << std::endl;
934 GetOStream(Runtime1, -1) << "fineNS...=" << fineNS[j][nonUniqueMapRef.getLocalElement(aggToRowMap[aggStart[agg] + k])] << std::endl;
935 GetOStream(Errors, -1) << "caught an error!" << std::endl;
936 }
937 } // for (LO k=0 ...
938 TEUCHOS_TEST_FOR_EXCEPTION(bIsZeroNSColumn == true, Exceptions::RuntimeError, "MueLu::TentativePFactory::MakeTentative: fine level NS part has a zero column. Error.");
939 } // for (LO j=0 ...
940
941 Xpetra::global_size_t offset = agg * NSDim;
942
943 if (myAggSize >= Teuchos::as<LocalOrdinal>(NSDim)) {
944 // calculate QR decomposition (standard)
945 // R is stored in localQR (size: myAggSize x NSDim)
946
947 // Householder multiplier
948 SC tau = localQR(0, 0);
949
950 if (NSDim == 1) {
951 // Only one nullspace vector, so normalize by hand
952 Magnitude dtemp = 0;
953 for (size_t k = 0; k < Teuchos::as<size_t>(myAggSize); ++k) {
954 Magnitude tmag = STS::magnitude(localQR(k, 0));
955 dtemp += tmag * tmag;
956 }
957 dtemp = Teuchos::ScalarTraits<Magnitude>::squareroot(dtemp);
958 tau = localQR(0, 0);
959 localQR(0, 0) = dtemp;
960 } else {
961 qrSolver.setMatrix(Teuchos::rcp(&localQR, false));
962 qrSolver.factor();
963 }
964
965 // Extract R, the coarse nullspace. This is stored in upper triangular part of localQR.
966 // Note: coarseNS[i][.] is the ith coarse nullspace vector, which may be counter to your intuition.
967 // This stores the (offset+k)th entry only if it is local according to the coarseMap.
968 for (size_t j = 0; j < NSDim; ++j) {
969 for (size_t k = 0; k <= j; ++k) {
970 try {
971 if (coarseMapRef.isNodeLocalElement(offset + k)) {
972 coarseNS[j][offset + k] = localQR(k, j); // TODO is offset+k the correct local ID?!
973 }
974 } catch (...) {
975 GetOStream(Errors, -1) << "caught error in coarseNS insert, j=" << j << ", offset+k = " << offset + k << std::endl;
976 }
977 }
978 }
979
980 // Calculate Q, the tentative prolongator.
981 // The Lapack GEQRF call only works for myAggsize >= NSDim
982
983 if (NSDim == 1) {
984 // Only one nullspace vector, so calculate Q by hand
985 Magnitude dtemp = Teuchos::ScalarTraits<SC>::magnitude(localQR(0, 0));
986 localQR(0, 0) = tau;
987 dtemp = 1 / dtemp;
988 for (LocalOrdinal i = 0; i < myAggSize; ++i) {
989 localQR(i, 0) *= dtemp;
990 }
991 } else {
992 qrSolver.formQ();
993 Teuchos::RCP<Teuchos::SerialDenseMatrix<LO, SC>> qFactor = qrSolver.getQ();
994 for (size_t j = 0; j < NSDim; j++) {
995 for (size_t i = 0; i < Teuchos::as<size_t>(myAggSize); i++) {
996 localQR(i, j) = (*qFactor)(i, j);
997 }
998 }
999 }
1000
1001 // end default case (myAggSize >= NSDim)
1002 } else { // special handling for myAggSize < NSDim (i.e. 1pt nodes)
1003 // See comments for the uncoupled case
1004
1005 // R = extended (by adding identity rows) localQR
1006 for (size_t j = 0; j < NSDim; j++)
1007 for (size_t k = 0; k < NSDim; k++) {
1008 TEUCHOS_TEST_FOR_EXCEPTION(!coarseMapRef.isNodeLocalElement(offset + k), Exceptions::RuntimeError,
1009 "Caught error in coarseNS insert, j=" << j << ", offset+k = " << offset + k);
1010
1011 if (k < as<size_t>(myAggSize))
1012 coarseNS[j][offset + k] = localQR(k, j);
1013 else
1014 coarseNS[j][offset + k] = (k == j ? one : zero);
1015 }
1016
1017 // Q = I (rectangular)
1018 for (size_t i = 0; i < as<size_t>(myAggSize); i++)
1019 for (size_t j = 0; j < NSDim; j++)
1020 localQR(i, j) = (j == i ? one : zero);
1021 } // end else (special handling for 1pt aggregates)
1022
1023 // Process each row in the local Q factor. If the row is local to the current processor
1024 // according to the rowmap, insert it into Ptentative. Otherwise, save it in ghostQ
1025 // to be communicated later to the owning processor.
1026 // FIXME -- what happens if maps are blocked?
1027 for (GO j = 0; j < myAggSize; ++j) {
1028 // This loop checks whether row associated with current DOF is local, according to rowMapForPtent.
1029 // If it is, the row is inserted. If not, the row number, columns, and values are saved in
1030 // MultiVectors that will be sent to other processors.
1031 GO globalRow = aggToRowMap[aggStart[agg] + j];
1032
1033 // TODO is the use of Xpetra::global_size_t below correct?
1034 if (rowMapForPtentRef.isNodeGlobalElement(globalRow) == false) {
1035 ghostQrows[qctr] = globalRow;
1036 for (size_t k = 0; k < NSDim; ++k) {
1037 ghostQcols[k][qctr] = coarseMapRef.getGlobalElement(agg * NSDim + k);
1038 ghostQvals[k][qctr] = localQR(j, k);
1039 }
1040 ++qctr;
1041 } else {
1042 size_t nnz = 0;
1043 for (size_t k = 0; k < NSDim; ++k) {
1044 try {
1045 if (localQR(j, k) != Teuchos::ScalarTraits<SC>::zero()) {
1046 localColPtr[nnz] = agg * NSDim + k;
1047 globalColPtr[nnz] = coarseMapRef.getGlobalElement(localColPtr[nnz]);
1048 valPtr[nnz] = localQR(j, k);
1049 ++total_nnz_count;
1050 ++nnz;
1051 }
1052 } catch (...) {
1053 GetOStream(Errors, -1) << "caught error in colPtr/valPtr insert, current index=" << nnz << std::endl;
1054 }
1055 } // for (size_t k=0; k<NSDim; ++k)
1056
1057 try {
1058 Ptentative->insertGlobalValues(globalRow, globalColPtr.view(0, nnz), valPtr.view(0, nnz));
1059 } catch (...) {
1060 GetOStream(Errors, -1) << "pid " << A->getRowMap()->getComm()->getRank()
1061 << "caught error during Ptent row insertion, global row "
1062 << globalRow << std::endl;
1063 }
1064 }
1065 } // for (GO j=0; j<myAggSize; ++j)
1066
1067 } // for (LO agg=0; agg<numAggs; ++agg)
1068
1069 // ***********************************************************
1070 // ************* end of aggregate-wise QR ********************
1071 // ***********************************************************
1072 GetOStream(Runtime1) << "TentativePFactory : aggregates may cross process boundaries" << std::endl;
1073 // Import ghost parts of Q factors and insert into Ptentative.
1074 // First import just the global row numbers.
1075 RCP<Xpetra::Vector<GO, LO, GO, Node>> targetQrowNums = Xpetra::VectorFactory<GO, LO, GO, Node>::Build(rowMapForPtent);
1076 targetQrowNums->putScalar(-1);
1077 targetQrowNums->doImport(*ghostQrowNums, *importer, Xpetra::INSERT);
1078 ArrayRCP<GO> targetQrows = targetQrowNums->getDataNonConst(0);
1079
1080 // Now create map based on just the row numbers imported.
1081 Array<GO> gidsToImport;
1082 gidsToImport.reserve(targetQrows.size());
1083 for (typename ArrayRCP<GO>::iterator r = targetQrows.begin(); r != targetQrows.end(); ++r) {
1084 if (*r > -1) {
1085 gidsToImport.push_back(*r);
1086 }
1087 }
1088 RCP<const Map> reducedMap = MapFactory::Build(A->getRowMap()->lib(),
1089 Teuchos::OrdinalTraits<Xpetra::global_size_t>::invalid(),
1090 gidsToImport, indexBase, A->getRowMap()->getComm());
1091
1092 // Import using the row numbers that this processor will receive.
1093 importer = ImportFactory::Build(ghostQMap, reducedMap);
1094
1095 Array<RCP<Xpetra::Vector<GO, LO, GO, Node>>> targetQcolumns(NSDim);
1096 for (size_t i = 0; i < NSDim; ++i) {
1097 targetQcolumns[i] = Xpetra::VectorFactory<GO, LO, GO, Node>::Build(reducedMap);
1098 targetQcolumns[i]->doImport(*(ghostQcolumns[i]), *importer, Xpetra::INSERT);
1099 }
1100 RCP<MultiVector> targetQvalues = MultiVectorFactory::Build(reducedMap, NSDim);
1101 targetQvalues->doImport(*ghostQvalues, *importer, Xpetra::INSERT);
1102
1103 ArrayRCP<ArrayRCP<SC>> targetQvals;
1104 ArrayRCP<ArrayRCP<GO>> targetQcols;
1105 if (targetQvalues->getLocalLength() > 0) {
1106 targetQvals.resize(NSDim);
1107 targetQcols.resize(NSDim);
1108 for (size_t i = 0; i < NSDim; ++i) {
1109 targetQvals[i] = targetQvalues->getDataNonConst(i);
1110 targetQcols[i] = targetQcolumns[i]->getDataNonConst(0);
1111 }
1112 }
1113
1114 valPtr = Array<SC>(NSDim, 0.);
1115 globalColPtr = Array<GO>(NSDim, 0);
1116 for (typename Array<GO>::iterator r = gidsToImport.begin(); r != gidsToImport.end(); ++r) {
1117 if (targetQvalues->getLocalLength() > 0) {
1118 for (size_t j = 0; j < NSDim; ++j) {
1119 valPtr[j] = targetQvals[j][reducedMap->getLocalElement(*r)];
1120 globalColPtr[j] = targetQcols[j][reducedMap->getLocalElement(*r)];
1121 }
1122 Ptentative->insertGlobalValues(*r, globalColPtr.view(0, NSDim), valPtr.view(0, NSDim));
1123 } // if (targetQvalues->getLocalLength() > 0)
1124 }
1125
1126 Ptentative->fillComplete(coarseMap, A->getDomainMap());
1127}
1128
1129} // namespace MueLu
1130
1131// TODO ReUse: If only P or Nullspace is missing, TentativePFactory can be smart and skip part of the computation.
1132
1133#define MUELU_TENTATIVEPFACTORY_SHORT
1134#endif // MUELU_TENTATIVEPFACTORY_DEF_HPP
#define SET_VALID_ENTRY(name)
MueLu::DefaultLocalOrdinal LocalOrdinal
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.
int GetLevelID() const
Return level number.
void AddKeepFlag(const std::string &ename, const FactoryBase *factory=NoFactory::get(), KeepType keep=MueLu::Keep)
void Set(const std::string &ename, const T &entry, const FactoryBase *factory=NoFactory::get())
static const NoFactory * get()
static std::string PrintMatrixInfo(const Matrix &A, const std::string &msgTag, RCP< const Teuchos::ParameterList > params=Teuchos::null)
void BuildPuncoupled(RCP< Matrix > A, RCP< Aggregates > aggregates, RCP< AmalgamationInfo > amalgInfo, RCP< MultiVector > fineNullspace, RCP< const Map > coarseMap, RCP< Matrix > &Ptentative, RCP< MultiVector > &coarseNullspace, const int levelID) const
void DeclareInput(Level &fineLevel, Level &coarseLevel) const override
Input.
~TentativePFactory() override
Destructor.
TentativePFactory()
Constructor.
RCP< const ParameterList > GetValidParameterList() const override
Return a const parameter list of valid parameters that setParameterList() will accept.
void BuildPuncoupledBlockCrs(RCP< Matrix > A, RCP< Aggregates > aggregates, RCP< AmalgamationInfo > amalgInfo, RCP< MultiVector > fineNullspace, RCP< const Map > coarseMap, RCP< Matrix > &Ptentative, RCP< MultiVector > &coarseNullspace, const int levelID) const
void BuildP(Level &fineLevel, Level &coarseLevel) const override
Abstract Build method.
void Build(Level &fineLevel, Level &coarseLevel) const override
Build an object with this factory.
void BuildPcoupled(RCP< Matrix > A, RCP< Aggregates > aggregates, RCP< AmalgamationInfo > amalgInfo, RCP< MultiVector > fineNullspace, RCP< const Map > coarseMap, RCP< Matrix > &Ptentative, RCP< MultiVector > &coarseNullspace) const
static bool MapsAreNested(const Xpetra::Map< LocalOrdinal, GlobalOrdinal, Node > &rowMap, const Xpetra::Map< LocalOrdinal, GlobalOrdinal, Node > &colMap)
Namespace for MueLu classes and methods.
@ Final
Keep data only for this run. Used to keep data useful for Hierarchy::Iterate(). Data will be deleted ...
@ Warnings0
Important warning messages (one line)
@ Statistics2
Print even more statistics.
@ Runtime1
Description of what is happening (more verbose)
std::string toString(const T &what)
Little helper function to convert non-string types to strings.