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;
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
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
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.
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
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()
virtual const Teuchos::ParameterList & GetParameterList() const
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< DefaultLocalOrdinal, DefaultGlobalOrdinal, DefaultNode > &rowMap, const Xpetra::Map< DefaultLocalOrdinal, DefaultGlobalOrdinal, DefaultNode > &colMap)
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.
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.