MueLu Version of the Day
Loading...
Searching...
No Matches
MueLu_TentativePFactory_kokkos_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_KOKKOS_DEF_HPP
11#define MUELU_TENTATIVEPFACTORY_KOKKOS_DEF_HPP
12
13#include <Xpetra_CrsGraphFactory.hpp>
14
16
17#include "MueLu_Aggregates.hpp"
18#include "MueLu_AmalgamationInfo.hpp"
19#include "MueLu_AmalgamationFactory.hpp"
20
21#include "MueLu_MasterList.hpp"
22#include "MueLu_Monitor.hpp"
23#include "MueLu_PerfUtils.hpp"
24#include "MueLu_Utilities.hpp"
25#include "MueLu_LocalQR.hpp"
26
27namespace MueLu {
28
29template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
31
32template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
34
35template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
37 RCP<ParameterList> validParamList = rcp(new ParameterList());
38
39#define SET_VALID_ENTRY(name) validParamList->setEntry(name, MasterList::getEntry(name))
40 SET_VALID_ENTRY("tentative: calculate qr");
41 SET_VALID_ENTRY("tentative: build coarse coordinates");
42 SET_VALID_ENTRY("sa: keep tentative prolongator");
43 // SET_VALID_ENTRY("tentative: constant column sums"); // Needs to be implemented to be able to replace the non-Kokkos TentativePFactory.
44#undef SET_VALID_ENTRY
45 validParamList->set<std::string>("Nullspace name", "Nullspace", "Name for the input nullspace");
46
47 validParamList->set<RCP<const FactoryBase>>("A", Teuchos::null, "Generating factory of the matrix A");
48 validParamList->set<RCP<const FactoryBase>>("Aggregates", Teuchos::null, "Generating factory of the aggregates");
49 validParamList->set<RCP<const FactoryBase>>("Nullspace", Teuchos::null, "Generating factory of the nullspace");
50 validParamList->set<RCP<const FactoryBase>>("Scaled Nullspace", Teuchos::null, "Generating factory of the scaled nullspace");
51 validParamList->set<RCP<const FactoryBase>>("UnAmalgamationInfo", Teuchos::null, "Generating factory of UnAmalgamationInfo");
52 validParamList->set<RCP<const FactoryBase>>("CoarseMap", Teuchos::null, "Generating factory of the coarse map");
53 validParamList->set<RCP<const FactoryBase>>("Coordinates", Teuchos::null, "Generating factory of the coordinates");
54 validParamList->set<RCP<const FactoryBase>>("Node Comm", Teuchos::null, "Generating factory of the node level communicator");
55
56 // Make sure we don't recursively validate options for the matrixmatrix kernels
57 ParameterList norecurse;
58 norecurse.disableRecursiveValidation();
59 validParamList->set<ParameterList>("matrixmatrix: kernel params", norecurse, "MatrixMatrix kernel parameters");
60
61 return validParamList;
62}
63
64template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
66 const ParameterList& pL = GetParameterList();
67 // NOTE: This guy can only either be 'Nullspace' or 'Scaled Nullspace' or else the validator above will cause issues
68 std::string nspName = "Nullspace";
69 if (pL.isParameter("Nullspace name")) nspName = pL.get<std::string>("Nullspace name");
70
71 Input(fineLevel, "A");
72 Input(fineLevel, "Aggregates");
73 Input(fineLevel, nspName);
74 Input(fineLevel, "UnAmalgamationInfo");
75 Input(fineLevel, "CoarseMap");
76 if (fineLevel.GetLevelID() == 0 &&
77 fineLevel.IsAvailable("Coordinates", NoFactory::get()) && // we have coordinates (provided by user app)
78 pL.get<bool>("tentative: build coarse coordinates")) { // and we want coordinates on other levels
79 bTransferCoordinates_ = true; // then set the transfer coordinates flag to true
80 Input(fineLevel, "Coordinates");
81 } else if (bTransferCoordinates_) {
82 Input(fineLevel, "Coordinates");
83 }
84}
85
86template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
88 return BuildP(fineLevel, coarseLevel);
89}
90
91template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
93 FactoryMonitor m(*this, "Build", coarseLevel);
94
95 typedef typename Teuchos::ScalarTraits<Scalar>::coordinateType coordinate_type;
96 typedef Xpetra::MultiVector<coordinate_type, LO, GO, NO> RealValuedMultiVector;
97 typedef Xpetra::MultiVectorFactory<coordinate_type, LO, GO, NO> RealValuedMultiVectorFactory;
98
99 const ParameterList& pL = GetParameterList();
100 std::string nspName = "Nullspace";
101 if (pL.isParameter("Nullspace name")) nspName = pL.get<std::string>("Nullspace name");
102
103 RCP<Matrix> Ptentative;
104 auto A = Get<RCP<Matrix>>(fineLevel, "A");
105 auto aggregates = Get<RCP<Aggregates>>(fineLevel, "Aggregates");
106 // No coarse DoFs so we need to bail by setting Ptentative to null and returning
107 // This level will ultimately be removed in MueLu_Hierarchy_defs.h via a resize()
108 if (aggregates->GetNumGlobalAggregatesComputeIfNeeded() == 0) {
109 Ptentative = Teuchos::null;
110 Set(coarseLevel, "P", Ptentative);
111 return;
112 }
113
114 auto amalgInfo = Get<RCP<AmalgamationInfo>>(fineLevel, "UnAmalgamationInfo");
115 auto fineNullspace = Get<RCP<MultiVector>>(fineLevel, nspName);
116 auto coarseMap = Get<RCP<const Map>>(fineLevel, "CoarseMap");
117 RCP<RealValuedMultiVector> fineCoords;
119 fineCoords = Get<RCP<RealValuedMultiVector>>(fineLevel, "Coordinates");
120 }
121
122 // FIXME: We should remove the NodeComm on levels past the threshold
123 if (fineLevel.IsAvailable("Node Comm")) {
124 RCP<const Teuchos::Comm<int>> nodeComm = Get<RCP<const Teuchos::Comm<int>>>(fineLevel, "Node Comm");
125 Set<RCP<const Teuchos::Comm<int>>>(coarseLevel, "Node Comm", nodeComm);
126 }
127
128 // NOTE: We check DomainMap here rather than RowMap because those are different for BlockCrs matrices
129 TEUCHOS_TEST_FOR_EXCEPTION(A->getDomainMap()->getLocalNumElements() != fineNullspace->getMap()->getLocalNumElements(),
130 Exceptions::RuntimeError, "MueLu::TentativePFactory::MakeTentative: Size mismatch between A and Nullspace");
131
132 RCP<MultiVector> coarseNullspace;
133 RCP<RealValuedMultiVector> coarseCoords;
134
136 RCP<const Map> coarseCoordMap;
137
138 LO blkSize = 1;
139 if (rcp_dynamic_cast<const StridedMap>(coarseMap) != Teuchos::null)
140 blkSize = rcp_dynamic_cast<const StridedMap>(coarseMap)->getFixedBlockSize();
141
142 if (blkSize == 1) {
143 // Scalar system
144 // No amalgamation required, we can use the coarseMap
145 coarseCoordMap = coarseMap;
146 } else {
147 // Vector system
148 AmalgamationFactory<SC, LO, GO, NO>::AmalgamateMap(rcp_dynamic_cast<const StridedMap>(coarseMap), coarseCoordMap);
149 }
150
151 coarseCoords = RealValuedMultiVectorFactory::Build(coarseCoordMap, fineCoords->getNumVectors(), false);
152
153 // Create overlapped fine coordinates to reduce global communication
154 auto uniqueMap = fineCoords->getMap();
155 RCP<RealValuedMultiVector> ghostedCoords = fineCoords;
156 if (aggregates->AggregatesCrossProcessors()) {
157 auto nonUniqueMap = aggregates->GetMap();
158 auto importer = ImportFactory::Build(uniqueMap, nonUniqueMap);
159
160 ghostedCoords = RealValuedMultiVectorFactory::Build(nonUniqueMap, fineCoords->getNumVectors(), false);
161 ghostedCoords->doImport(*fineCoords, *importer, Xpetra::INSERT);
162 }
163
164 // The good new is that his graph has already been constructed for the
165 // TentativePFactory and was cached in Aggregates. So this is a no-op.
166 auto aggGraph = aggregates->GetGraph();
167 auto numAggs = aggGraph.numRows();
168
169 auto fineCoordsView = fineCoords->getLocalViewDevice(Tpetra::Access::ReadOnly);
170 auto coarseCoordsView = coarseCoords->getLocalViewDevice(Tpetra::Access::OverwriteAll);
171
172 // Fill in coarse coordinates
173 {
174 SubFactoryMonitor m2(*this, "AverageCoords", coarseLevel);
175
176 const auto dim = fineCoords->getNumVectors();
177
178 typename AppendTrait<decltype(fineCoordsView), Kokkos::RandomAccess>::type fineCoordsRandomView = fineCoordsView;
179 for (size_t j = 0; j < dim; j++) {
180 Kokkos::parallel_for(
181 "MueLu::TentativeP::BuildCoords", Kokkos::RangePolicy<LocalOrdinal, execution_space>(0, numAggs),
182 KOKKOS_LAMBDA(const LO i) {
183 // A row in this graph represents all node ids in the aggregate
184 // Therefore, averaging is very easy
185
186 auto aggregate = aggGraph.rowConst(i);
187
188 coordinate_type sum = 0.0; // do not use Scalar here (Stokhos)
189 for (size_t colID = 0; colID < static_cast<size_t>(aggregate.length); colID++)
190 sum += fineCoordsRandomView(aggregate(colID), j);
191
192 coarseCoordsView(i, j) = sum / aggregate.length;
193 });
194 }
195 }
196 }
197
198 if (!aggregates->AggregatesCrossProcessors()) {
199 if (Xpetra::Helpers<SC, LO, GO, NO>::isTpetraBlockCrs(A)) {
200 BuildPuncoupledBlockCrs(coarseLevel, A, aggregates, amalgInfo, fineNullspace, coarseMap, Ptentative, coarseNullspace,
201 coarseLevel.GetLevelID());
202 } else {
203 BuildPuncoupled(coarseLevel, A, aggregates, amalgInfo, fineNullspace, coarseMap, Ptentative, coarseNullspace, coarseLevel.GetLevelID());
204 }
205 } else
206 BuildPcoupled(A, aggregates, amalgInfo, fineNullspace, coarseMap, Ptentative, coarseNullspace);
207
208 // If available, use striding information of fine level matrix A for range
209 // map and coarseMap as domain map; otherwise use plain range map of
210 // Ptent = plain range map of A for range map and coarseMap as domain map.
211 // NOTE:
212 // The latter is not really safe, since there is no striding information
213 // for the range map. This is not really a problem, since striding
214 // information is always available on the intermedium levels and the
215 // coarsest levels.
216 if (A->IsView("stridedMaps") == true)
217 Ptentative->CreateView("stridedMaps", A->getRowMap("stridedMaps"), coarseMap);
218
220 Set(coarseLevel, "Coordinates", coarseCoords);
221 }
222 Set(coarseLevel, "Nullspace", coarseNullspace);
223 Set(coarseLevel, "P", Ptentative);
224
225 if (pL.get<bool>("sa: keep tentative prolongator")) {
226 coarseLevel.Set("Ptent", Ptentative, NoFactory::get());
227 coarseLevel.AddKeepFlag("Ptent", NoFactory::get(), MueLu::Final);
228 }
229
230 if (IsPrint(Statistics2)) {
231 RCP<ParameterList> params = rcp(new ParameterList());
232 params->set("printLoadBalancingInfo", true);
233 GetOStream(Statistics2) << PerfUtils::PrintMatrixInfo(*Ptentative, "Ptent", params);
234 }
235}
236
237template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
239 BuildPuncoupled(Level& coarseLevel, RCP<Matrix> A, RCP<Aggregates> aggregates,
240 RCP<AmalgamationInfo> amalgInfo, RCP<MultiVector> fineNullspace,
241 RCP<const Map> coarseMap, RCP<Matrix>& Ptentative,
242 RCP<MultiVector>& coarseNullspace, const int levelID) const {
243 auto rowMap = A->getRowMap();
244 auto colMap = A->getColMap();
245
246 const size_t numRows = rowMap->getLocalNumElements();
247 const size_t NSDim = fineNullspace->getNumVectors();
248
249 typedef KokkosKernels::ArithTraits<SC> ATS;
250 using impl_SC = typename ATS::val_type;
251 using impl_ATS = KokkosKernels::ArithTraits<impl_SC>;
252 const impl_SC zero = impl_ATS::zero();
253 const impl_SC one = impl_ATS::one();
254
255 const LO INVALID = Teuchos::OrdinalTraits<LO>::invalid();
256
257 typename Aggregates::local_graph_type aggGraph;
258 {
259 SubFactoryMonitor m2(*this, "Get Aggregates graph", coarseLevel);
260 aggGraph = aggregates->GetGraph();
261 }
262 auto aggRows = aggGraph.row_map;
263 auto aggCols = aggGraph.entries;
264
265 // Aggregates map is based on the amalgamated column map
266 // We can skip global-to-local conversion if LIDs in row map are
267 // same as LIDs in column map
268 bool goodMap;
269 {
270 SubFactoryMonitor m2(*this, "Check good map", coarseLevel);
271 goodMap = Utilities::MapsAreNested(*rowMap, *colMap);
272 }
273 // FIXME_KOKKOS: need to proofread later code for bad maps
274 TEUCHOS_TEST_FOR_EXCEPTION(!goodMap, Exceptions::RuntimeError,
275 "MueLu: TentativePFactory_kokkos: for now works only with good maps "
276 "(i.e. \"matching\" row and column maps)");
277
278 // STEP 1: do unamalgamation
279 // The non-kokkos version uses member functions from the AmalgamationInfo
280 // container class to unamalgamate the data. In contrast, the kokkos
281 // version of TentativePFactory does the unamalgamation here and only uses
282 // the data of the AmalgamationInfo container class
283
284 // Extract information for unamalgamation
285 LO fullBlockSize;
286 LO blockID;
287 LO stridingOffset;
288 LO stridedBlockSize;
289 GO indexBase;
290 amalgInfo->GetStridingInformation(fullBlockSize, blockID, stridingOffset, stridedBlockSize, indexBase);
291 GO globalOffset = amalgInfo->GlobalOffset();
292
293 // Extract aggregation info (already in Kokkos host views)
294 auto procWinner = aggregates->GetProcWinner()->getLocalViewDevice(Tpetra::Access::ReadOnly);
295 auto vertex2AggId = aggregates->GetVertex2AggId()->getLocalViewDevice(Tpetra::Access::ReadOnly);
296 const size_t numAggregates = aggregates->GetNumAggregates();
297
298 int myPID = aggregates->GetMap()->getComm()->getRank();
299
300 // Create Kokkos::View (on the device) to store the aggreate dof sizes
301 // Later used to get aggregate dof offsets
302 // NOTE: This zeros itself on construction
303 typedef typename Aggregates::aggregates_sizes_type::non_const_type AggSizeType;
304 AggSizeType aggDofSizes;
305
306 if (stridedBlockSize == 1) {
307 SubFactoryMonitor m2(*this, "Calc AggSizes", coarseLevel);
308
309 // FIXME_KOKKOS: use ViewAllocateWithoutInitializing + set a single value
310 aggDofSizes = AggSizeType("agg_dof_sizes", numAggregates + 1);
311
312 auto sizesConst = aggregates->ComputeAggregateSizes();
313 Kokkos::deep_copy(Kokkos::subview(aggDofSizes, Kokkos::make_pair(static_cast<size_t>(1), numAggregates + 1)), sizesConst);
314
315 } else {
316 SubFactoryMonitor m2(*this, "Calc AggSizes", coarseLevel);
317
318 // FIXME_KOKKOS: use ViewAllocateWithoutInitializing + set a single value
319 aggDofSizes = AggSizeType("agg_dof_sizes", numAggregates + 1);
320
321 auto nodeMap = aggregates->GetMap()->getLocalMap();
322 auto dofMap = colMap->getLocalMap();
323
324 Kokkos::parallel_for(
325 "MueLu:TentativePF:Build:compute_agg_sizes", range_type(0, numAggregates),
326 KOKKOS_LAMBDA(const LO agg) {
327 auto aggRowView = aggGraph.rowConst(agg);
328
329 size_t size = 0;
330 for (LO colID = 0; colID < aggRowView.length; colID++) {
331 GO nodeGID = nodeMap.getGlobalElement(aggRowView(colID));
332
333 for (LO k = 0; k < stridedBlockSize; k++) {
334 GO dofGID = (nodeGID - indexBase) * fullBlockSize + k + indexBase + globalOffset + stridingOffset;
335
336 if (dofMap.getLocalElement(dofGID) != INVALID)
337 size++;
338 }
339 }
340 aggDofSizes(agg + 1) = size;
341 });
342 }
343
344 // Find maximum dof size for aggregates
345 // Later used to reserve enough scratch space for local QR decompositions
346 LO maxAggSize = 0;
347 LocalQR::ReduceMaxFunctor<LO, decltype(aggDofSizes)> reduceMax(aggDofSizes);
348 Kokkos::parallel_reduce("MueLu:TentativePF:Build:max_agg_size", range_type(0, aggDofSizes.extent(0)), reduceMax, maxAggSize);
349
350 // parallel_scan (exclusive)
351 // The aggDofSizes View then contains the aggregate dof offsets
352 Kokkos::parallel_scan(
353 "MueLu:TentativePF:Build:aggregate_sizes:stage1_scan", range_type(0, numAggregates + 1),
354 KOKKOS_LAMBDA(const LO i, LO& update, const bool& final_pass) {
355 update += aggDofSizes(i);
356 if (final_pass)
357 aggDofSizes(i) = update;
358 });
359
360 // Create Kokkos::View on the device to store mapping
361 // between (local) aggregate id and row map ids (LIDs)
362 Kokkos::View<LO*, DeviceType> agg2RowMapLO(Kokkos::ViewAllocateWithoutInitializing("agg2row_map_LO"), numRows);
363 {
364 SubFactoryMonitor m2(*this, "Create Agg2RowMap", coarseLevel);
365
366 AggSizeType aggOffsets(Kokkos::ViewAllocateWithoutInitializing("aggOffsets"), numAggregates);
367 Kokkos::deep_copy(aggOffsets, Kokkos::subview(aggDofSizes, Kokkos::make_pair(static_cast<size_t>(0), numAggregates)));
368
369 Kokkos::parallel_for(
370 "MueLu:TentativePF:Build:createAgg2RowMap", range_type(0, vertex2AggId.extent(0)),
371 KOKKOS_LAMBDA(const LO lnode) {
372 if (procWinner(lnode, 0) == myPID) {
373 // No need for atomics, it's one-to-one
374 auto aggID = vertex2AggId(lnode, 0);
375
376 auto offset = Kokkos::atomic_fetch_add(&aggOffsets(aggID), stridedBlockSize);
377 // FIXME: I think this may be wrong
378 // We unconditionally add the whole block here. When we calculated
379 // aggDofSizes, we did the isLocalElement check. Something's fishy.
380 for (LO k = 0; k < stridedBlockSize; k++)
381 agg2RowMapLO(offset + k) = lnode * stridedBlockSize + k;
382 }
383 });
384 }
385
386 // STEP 2: prepare local QR decomposition
387 // Reserve memory for tentative prolongation operator
388 coarseNullspace = MultiVectorFactory::Build(coarseMap, NSDim, true);
389
390 // Pull out the nullspace vectors so that we can have random access (on the device)
391 auto fineNS = fineNullspace->getLocalViewDevice(Tpetra::Access::ReadWrite);
392 auto coarseNS = coarseNullspace->getLocalViewDevice(Tpetra::Access::ReadWrite);
393
394 size_t nnz = 0; // actual number of nnz
395
396 typedef typename Xpetra::Matrix<SC, LO, GO, NO>::local_matrix_device_type local_matrix_type;
397 typedef typename local_matrix_type::row_map_type::non_const_type rows_type;
398 typedef typename local_matrix_type::index_type::non_const_type cols_type;
399 typedef typename local_matrix_type::values_type::non_const_type vals_type;
400
401 // Device View for status (error messages...)
402 typedef Kokkos::View<int[10], DeviceType> status_type;
403 status_type status("status");
404
405 typename AppendTrait<decltype(fineNS), Kokkos::RandomAccess>::type fineNSRandom = fineNS;
406 typename AppendTrait<status_type, Kokkos::Atomic>::type statusAtomic = status;
407
408 const ParameterList& pL = GetParameterList();
409 const bool& doQRStep = pL.get<bool>("tentative: calculate qr");
410 if (!doQRStep) {
411 GetOStream(Runtime1) << "TentativePFactory : bypassing local QR phase" << std::endl;
412 if (NSDim > 1)
413 GetOStream(Warnings0) << "TentativePFactor : for nontrivial nullspace, this may degrade performance" << std::endl;
414 }
415
416 size_t nnzEstimate = numRows * NSDim;
417 rows_type rowsAux(Kokkos::ViewAllocateWithoutInitializing("Ptent_aux_rows"), numRows + 1);
418 cols_type colsAux(Kokkos::ViewAllocateWithoutInitializing("Ptent_aux_cols"), nnzEstimate);
419 vals_type valsAux("Ptent_aux_vals", nnzEstimate);
420 rows_type rows("Ptent_rows", numRows + 1);
421 {
422 // Stage 0: fill in views.
423 SubFactoryMonitor m2(*this, "Stage 0 (InitViews)", coarseLevel);
424
425 // The main thing to notice is initialization of vals with INVALID. These
426 // values will later be used to compress the arrays
427 Kokkos::parallel_for(
428 "MueLu:TentativePF:BuildPuncoupled:for1", range_type(0, numRows + 1),
429 KOKKOS_LAMBDA(const LO row) {
430 rowsAux(row) = row * NSDim;
431 });
432 Kokkos::deep_copy(colsAux, INVALID);
433 }
434
435 if (NSDim == 1) {
436 // 1D is special, as it is the easiest. We don't even need to the QR,
437 // just normalize an array. Plus, no worries abot small aggregates. In
438 // addition, we do not worry about compression. It is unlikely that
439 // nullspace will have zeros. If it does, a prolongator row would be
440 // zero and we'll get singularity anyway.
441 SubFactoryMonitor m2(*this, "Stage 1 (LocalQR)", coarseLevel);
442
443 // Set up team policy with numAggregates teams and one thread per team.
444 // Each team handles a slice of the data associated with one aggregate
445 // and performs a local QR decomposition (in this case real QR is
446 // unnecessary).
447 const Kokkos::TeamPolicy<execution_space> policy(numAggregates, 1);
448
449 if (doQRStep) {
450 Kokkos::parallel_for(
451 "MueLu:TentativePF:BuildUncoupled:main_loop", policy,
452 KOKKOS_LAMBDA(const typename Kokkos::TeamPolicy<execution_space>::member_type& thread) {
453 auto agg = thread.league_rank();
454
455 // size of the aggregate (number of DOFs in aggregate)
456 LO aggSize = aggRows(agg + 1) - aggRows(agg);
457
458 // Extract the piece of the nullspace corresponding to the aggregate, and
459 // put it in the flat array, "localQR" (in column major format) for the
460 // QR routine. Trivial in 1D.
461 auto norm = impl_ATS::magnitude(zero);
462
463 // Calculate QR by hand
464 // FIXME: shouldn't there be stridedblock here?
465 // FIXME_KOKKOS: shouldn't there be stridedblock here?
466 for (decltype(aggSize) k = 0; k < aggSize; k++) {
467 auto dnorm = impl_ATS::magnitude(fineNSRandom(agg2RowMapLO(aggRows(agg) + k), 0));
468 norm += dnorm * dnorm;
469 }
470 norm = sqrt(norm);
471
472 if (norm == zero) {
473 // zero column; terminate the execution
474 statusAtomic(1) = true;
475 return;
476 }
477
478 // R = norm
479 coarseNS(agg, 0) = norm;
480
481 // Q = localQR(:,0)/norm
482 for (decltype(aggSize) k = 0; k < aggSize; k++) {
483 LO localRow = agg2RowMapLO(aggRows(agg) + k);
484 impl_SC localVal = fineNSRandom(agg2RowMapLO(aggRows(agg) + k), 0) / norm;
485
486 rows(localRow + 1) = 1;
487 colsAux(localRow) = agg;
488 valsAux(localRow) = localVal;
489 }
490 });
491
492 typename status_type::host_mirror_type statusHost = Kokkos::create_mirror_view(status);
493 Kokkos::deep_copy(statusHost, status);
494 for (decltype(statusHost.size()) i = 0; i < statusHost.size(); i++)
495 if (statusHost(i)) {
496 std::ostringstream oss;
497 oss << "MueLu::TentativePFactory::MakeTentative: ";
498 switch (i) {
499 case 0: oss << "!goodMap is not implemented"; break;
500 case 1: oss << "fine level NS part has a zero column"; break;
501 }
502 throw Exceptions::RuntimeError(oss.str());
503 }
504
505 } else {
506 Kokkos::parallel_for(
507 "MueLu:TentativePF:BuildUncoupled:main_loop_noqr", policy,
508 KOKKOS_LAMBDA(const typename Kokkos::TeamPolicy<execution_space>::member_type& thread) {
509 auto agg = thread.league_rank();
510
511 // size of the aggregate (number of DOFs in aggregate)
512 LO aggSize = aggRows(agg + 1) - aggRows(agg);
513
514 // R = norm
515 coarseNS(agg, 0) = one;
516
517 // Q = localQR(:,0)/norm
518 for (decltype(aggSize) k = 0; k < aggSize; k++) {
519 LO localRow = agg2RowMapLO(aggRows(agg) + k);
520 impl_SC localVal = fineNSRandom(agg2RowMapLO(aggRows(agg) + k), 0);
521
522 rows(localRow + 1) = 1;
523 colsAux(localRow) = agg;
524 valsAux(localRow) = localVal;
525 }
526 });
527 }
528
529 Kokkos::parallel_reduce(
530 "MueLu:TentativeP:CountNNZ", range_type(0, numRows + 1),
531 KOKKOS_LAMBDA(const LO i, size_t& nnz_count) {
532 nnz_count += rows(i);
533 },
534 nnz);
535
536 } else { // NSdim > 1
537 // FIXME_KOKKOS: This code branch is completely unoptimized.
538 // Work to do:
539 // - Optimize QR decomposition
540 // - Remove INVALID usage similarly to CoalesceDropFactory_kokkos by
541 // packing new values in the beginning of each row
542 // We do use auxilary view in this case, so keep a second rows view for
543 // counting nonzeros in rows
544
545 {
546 SubFactoryMonitor m2 = SubFactoryMonitor(*this, doQRStep ? "Stage 1 (LocalQR)" : "Stage 1 (Fill coarse nullspace and tentative P)", coarseLevel);
547 // Set up team policy with numAggregates teams and one thread per team.
548 // Each team handles a slice of the data associated with one aggregate
549 // and performs a local QR decomposition
550 Kokkos::TeamPolicy<execution_space> policy(numAggregates, 1); // numAggregates teams a 1 thread
551 using LocalQrFunctorType = LocalQR::LocalQRDecompFunctor<LocalOrdinal, GlobalOrdinal, Scalar, DeviceType, decltype(fineNSRandom),
552 decltype(aggDofSizes /*aggregate sizes in dofs*/), decltype(maxAggSize), decltype(agg2RowMapLO),
553 decltype(statusAtomic), decltype(rows), decltype(rowsAux), decltype(colsAux),
554 decltype(valsAux)>;
555 int scratchLevel = -1;
556 if (doQRStep) {
557 using shared_matrix = typename LocalQrFunctorType::shared_matrix;
558 using shared_vector = typename LocalQrFunctorType::shared_vector;
559 int m = maxAggSize;
560 int n = fineNSRandom.extent(1);
561 int size = shared_matrix::shmem_size(m, n) + // r
562 shared_matrix::shmem_size(m, m) + // q
563 shared_vector::shmem_size(m) + // work
564 shared_vector::shmem_size(n); // tau
565
566 if (size < policy.scratch_size_max(/*level=*/(int)0))
567 scratchLevel = 0;
568 else if (size < policy.scratch_size_max(/*level=*/(int)1))
569 scratchLevel = 1;
570 else
571 throw Exceptions::RuntimeError("Neither L0 scratch memory (max size " + std::to_string(policy.scratch_size_max((int)0)) +
572 "), nor L1 scratch memory (max size " + std::to_string(policy.scratch_size_max((int)1)) +
573 ") is large enough for requested allocation of size " + std::to_string(size));
574 policy.set_scratch_size(scratchLevel, Kokkos::PerTeam(size));
575 }
576 LocalQrFunctorType localQRFunctor(fineNSRandom, coarseNS, aggDofSizes, maxAggSize, agg2RowMapLO, statusAtomic,
577 rows, rowsAux, colsAux, valsAux, doQRStep, scratchLevel);
578
579 Kokkos::parallel_reduce("MueLu:TentativePF:BuildUncoupled:main_qr_loop", policy, localQRFunctor, nnz);
580 }
581
582 typename status_type::host_mirror_type statusHost = Kokkos::create_mirror_view(status);
583 Kokkos::deep_copy(statusHost, status);
584 for (decltype(statusHost.size()) i = 0; i < statusHost.size(); i++)
585 if (statusHost(i)) {
586 std::ostringstream oss;
587 oss << "MueLu::TentativePFactory::MakeTentative: ";
588 switch (i) {
589 case 0: oss << "!goodMap is not implemented"; break;
590 case 1: oss << "fine level NS part has a zero column"; break;
591 }
592 throw Exceptions::RuntimeError(oss.str());
593 }
594 }
595
596 // Compress the cols and vals by ignoring INVALID column entries that correspond
597 // to 0 in QR.
598
599 // The real cols and vals are constructed using calculated (not estimated) nnz
600 cols_type cols;
601 vals_type vals;
602
603 if (nnz != nnzEstimate) {
604 {
605 // Stage 2: compress the arrays
606 SubFactoryMonitor m2(*this, "Stage 2 (CompressRows)", coarseLevel);
607
608 Kokkos::parallel_scan(
609 "MueLu:TentativePF:Build:compress_rows", range_type(0, numRows + 1),
610 KOKKOS_LAMBDA(const LO i, LO& upd, const bool& final) {
611 upd += rows(i);
612 if (final)
613 rows(i) = upd;
614 });
615 }
616
617 {
618 SubFactoryMonitor m2(*this, "Stage 2 (CompressCols)", coarseLevel);
619
620 cols = cols_type("Ptent_cols", nnz);
621 vals = vals_type("Ptent_vals", nnz);
622
623 // FIXME_KOKKOS: this can be spedup by moving correct cols and vals values
624 // to the beginning of rows. See CoalesceDropFactory_kokkos for
625 // example.
626 Kokkos::parallel_for(
627 "MueLu:TentativePF:Build:compress_cols_vals", range_type(0, numRows),
628 KOKKOS_LAMBDA(const LO i) {
629 LO rowStart = rows(i);
630
631 size_t lnnz = 0;
632 for (auto j = rowsAux(i); j < rowsAux(i + 1); j++)
633 if (colsAux(j) != INVALID) {
634 cols(rowStart + lnnz) = colsAux(j);
635 vals(rowStart + lnnz) = valsAux(j);
636 lnnz++;
637 }
638 });
639 }
640
641 } else {
642 rows = rowsAux;
643 cols = colsAux;
644 vals = valsAux;
645 }
646
647 GetOStream(Runtime1) << "TentativePFactory : aggregates do not cross process boundaries" << std::endl;
648
649 {
650 // Stage 3: construct Xpetra::Matrix
651 SubFactoryMonitor m2(*this, "Stage 3 (LocalMatrix+FillComplete)", coarseLevel);
652
653 local_matrix_type lclMatrix = local_matrix_type("A", numRows, coarseMap->getLocalNumElements(), nnz, vals, rows, cols);
654
655 // Managing labels & constants for ESFC
656 RCP<ParameterList> FCparams;
657 if (pL.isSublist("matrixmatrix: kernel params"))
658 FCparams = rcp(new ParameterList(pL.sublist("matrixmatrix: kernel params")));
659 else
660 FCparams = rcp(new ParameterList);
661
662 // By default, we don't need global constants for TentativeP
663 FCparams->set("compute global constants", FCparams->get("compute global constants", false));
664 FCparams->set("Timer Label", std::string("MueLu::TentativeP-") + toString(levelID));
665
666 auto PtentCrs = CrsMatrixFactory::Build(lclMatrix, rowMap, coarseMap, coarseMap, A->getDomainMap());
667 Ptentative = rcp(new CrsMatrixWrap(PtentCrs));
668 }
669}
670
671template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
673 BuildPuncoupledBlockCrs(Level& coarseLevel, RCP<Matrix> A, RCP<Aggregates> aggregates,
674 RCP<AmalgamationInfo> amalgInfo, RCP<MultiVector> fineNullspace,
675 RCP<const Map> coarsePointMap, RCP<Matrix>& Ptentative,
676 RCP<MultiVector>& coarseNullspace, const int levelID) const {
677 /* 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
678 be generalized later, if we ever need to do so:
679 1) Null space dimension === block size of matrix: So no elasticity right now
680 2) QR is not supported: Under assumption #1, this shouldn't cause problems.
681 3) Maps are "good": Aka the first chunk of the ColMap is the RowMap.
682
683 These assumptions keep our code way simpler and still support the use cases we actually care about.
684 */
685
686 RCP<const Map> rowMap = A->getRowMap();
687 RCP<const Map> rangeMap = A->getRangeMap();
688 RCP<const Map> colMap = A->getColMap();
689 // const size_t numFinePointRows = rangeMap->getLocalNumElements();
690 const size_t numFineBlockRows = rowMap->getLocalNumElements();
691
692 // typedef Teuchos::ScalarTraits<SC> STS;
693 // typedef typename STS::magnitudeType Magnitude;
694 const LO INVALID = Teuchos::OrdinalTraits<LO>::invalid();
695
696 typedef KokkosKernels::ArithTraits<SC> ATS;
697 using impl_SC = typename ATS::val_type;
698 using impl_ATS = KokkosKernels::ArithTraits<impl_SC>;
699 const impl_SC one = impl_ATS::one();
700
701 // const GO numAggs = aggregates->GetNumAggregates();
702 const size_t NSDim = fineNullspace->getNumVectors();
703 auto aggSizes = aggregates->ComputeAggregateSizes();
704
705 typename Aggregates::local_graph_type aggGraph;
706 {
707 SubFactoryMonitor m2(*this, "Get Aggregates graph", coarseLevel);
708 aggGraph = aggregates->GetGraph();
709 }
710 auto aggRows = aggGraph.row_map;
711 auto aggCols = aggGraph.entries;
712
713 // Need to generate the coarse block map
714 // NOTE: We assume NSDim == block size here
715 // NOTE: We also assume that coarseMap has contiguous GIDs
716 // const size_t numCoarsePointRows = coarsePointMap->getLocalNumElements();
717 const size_t numCoarseBlockRows = coarsePointMap->getLocalNumElements() / NSDim;
718 RCP<const Map> coarseBlockMap = MapFactory::Build(coarsePointMap->lib(),
719 Teuchos::OrdinalTraits<Xpetra::global_size_t>::invalid(),
720 numCoarseBlockRows,
721 coarsePointMap->getIndexBase(),
722 coarsePointMap->getComm());
723 // Sanity checking
724 const ParameterList& pL = GetParameterList();
725 // const bool &doQRStep = pL.get<bool>("tentative: calculate qr");
726
727 // The aggregates use the amalgamated column map, which in this case is what we want
728
729 // Aggregates map is based on the amalgamated column map
730 // We can skip global-to-local conversion if LIDs in row map are
731 // same as LIDs in column map
732 bool goodMap = Utilities::MapsAreNested(*rowMap, *colMap);
733 TEUCHOS_TEST_FOR_EXCEPTION(!goodMap, Exceptions::RuntimeError,
734 "MueLu: TentativePFactory_kokkos: for now works only with good maps "
735 "(i.e. \"matching\" row and column maps)");
736
737 // STEP 1: do unamalgamation
738 // The non-kokkos version uses member functions from the AmalgamationInfo
739 // container class to unamalgamate the data. In contrast, the kokkos
740 // version of TentativePFactory does the unamalgamation here and only uses
741 // the data of the AmalgamationInfo container class
742
743 // Extract information for unamalgamation
744 LO fullBlockSize;
745 LO blockID;
746 LO stridingOffset;
747 LO stridedBlockSize;
748 GO indexBase;
749 amalgInfo->GetStridingInformation(fullBlockSize, blockID, stridingOffset, stridedBlockSize, indexBase);
750 // GO globalOffset = amalgInfo->GlobalOffset();
751
752 // Extract aggregation info (already in Kokkos host views)
753 auto procWinner = aggregates->GetProcWinner()->getLocalViewDevice(Tpetra::Access::ReadOnly);
754 auto vertex2AggId = aggregates->GetVertex2AggId()->getLocalViewDevice(Tpetra::Access::ReadOnly);
755 const size_t numAggregates = aggregates->GetNumAggregates();
756
757 int myPID = aggregates->GetMap()->getComm()->getRank();
758
759 // Create Kokkos::View (on the device) to store the aggreate dof sizes
760 // Later used to get aggregate dof offsets
761 // NOTE: This zeros itself on construction
762 typedef typename Aggregates::aggregates_sizes_type::non_const_type AggSizeType;
763 AggSizeType aggDofSizes; // This turns into "starts" after the parallel_scan
764
765 {
766 SubFactoryMonitor m2(*this, "Calc AggSizes", coarseLevel);
767
768 // FIXME_KOKKOS: use ViewAllocateWithoutInitializing + set a single value
769 aggDofSizes = AggSizeType("agg_dof_sizes", numAggregates + 1);
770
771 Kokkos::deep_copy(Kokkos::subview(aggDofSizes, Kokkos::make_pair(static_cast<size_t>(1), numAggregates + 1)), aggSizes);
772 }
773
774 // Find maximum dof size for aggregates
775 // Later used to reserve enough scratch space for local QR decompositions
776 LO maxAggSize = 0;
777 LocalQR::ReduceMaxFunctor<LO, decltype(aggDofSizes)> reduceMax(aggDofSizes);
778 Kokkos::parallel_reduce("MueLu:TentativePF:Build:max_agg_size", range_type(0, aggDofSizes.extent(0)), reduceMax, maxAggSize);
779
780 // parallel_scan (exclusive)
781 // The aggDofSizes View then contains the aggregate dof offsets
782 Kokkos::parallel_scan(
783 "MueLu:TentativePF:Build:aggregate_sizes:stage1_scan", range_type(0, numAggregates + 1),
784 KOKKOS_LAMBDA(const LO i, LO& update, const bool& final_pass) {
785 update += aggDofSizes(i);
786 if (final_pass)
787 aggDofSizes(i) = update;
788 });
789
790 // Create Kokkos::View on the device to store mapping
791 // between (local) aggregate id and row map ids (LIDs)
792 Kokkos::View<LO*, DeviceType> aggToRowMapLO(Kokkos::ViewAllocateWithoutInitializing("aggtorow_map_LO"), numFineBlockRows);
793 {
794 SubFactoryMonitor m2(*this, "Create AggToRowMap", coarseLevel);
795
796 AggSizeType aggOffsets(Kokkos::ViewAllocateWithoutInitializing("aggOffsets"), numAggregates);
797 Kokkos::deep_copy(aggOffsets, Kokkos::subview(aggDofSizes, Kokkos::make_pair(static_cast<size_t>(0), numAggregates)));
798
799 Kokkos::parallel_for(
800 "MueLu:TentativePF:Build:createAgg2RowMap", range_type(0, vertex2AggId.extent(0)),
801 KOKKOS_LAMBDA(const LO lnode) {
802 if (procWinner(lnode, 0) == myPID) {
803 // No need for atomics, it's one-to-one
804 auto aggID = vertex2AggId(lnode, 0);
805
806 auto offset = Kokkos::atomic_fetch_add(&aggOffsets(aggID), stridedBlockSize);
807 // FIXME: I think this may be wrong
808 // We unconditionally add the whole block here. When we calculated
809 // aggDofSizes, we did the isLocalElement check. Something's fishy.
810 for (LO k = 0; k < stridedBlockSize; k++)
811 aggToRowMapLO(offset + k) = lnode * stridedBlockSize + k;
812 }
813 });
814 }
815
816 // STEP 2: prepare local QR decomposition
817 // Reserve memory for tentative prolongation operator
818 coarseNullspace = MultiVectorFactory::Build(coarsePointMap, NSDim, true);
819
820 // Pull out the nullspace vectors so that we can have random access (on the device)
821 auto fineNS = fineNullspace->getLocalViewDevice(Tpetra::Access::ReadWrite);
822 auto coarseNS = coarseNullspace->getLocalViewDevice(Tpetra::Access::ReadWrite);
823
824 typedef typename Xpetra::Matrix<SC, LO, GO, NO>::local_matrix_device_type local_matrix_type;
825 typedef typename local_matrix_type::row_map_type::non_const_type rows_type;
826 typedef typename local_matrix_type::index_type::non_const_type cols_type;
827 // typedef typename local_matrix_type::values_type::non_const_type vals_type;
828
829 // Device View for status (error messages...)
830 typedef Kokkos::View<int[10], DeviceType> status_type;
831 status_type status("status");
832
833 typename AppendTrait<decltype(fineNS), Kokkos::RandomAccess>::type fineNSRandom = fineNS;
834 typename AppendTrait<status_type, Kokkos::Atomic>::type statusAtomic = status;
835
836 // We're going to bypass QR in the BlockCrs version of the code regardless of what the user asks for
837 GetOStream(Runtime1) << "TentativePFactory : bypassing local QR phase" << std::endl;
838
839 // BlockCrs requires that we build the (block) graph first, so let's do that...
840
841 // NOTE: Because we're assuming that the NSDim == BlockSize, we only have one
842 // block non-zero per row in the matrix;
843 rows_type ia(Kokkos::ViewAllocateWithoutInitializing("BlockGraph_rowptr"), numFineBlockRows + 1);
844 cols_type ja(Kokkos::ViewAllocateWithoutInitializing("BlockGraph_colind"), numFineBlockRows);
845
846 Kokkos::parallel_for(
847 "MueLu:TentativePF:BlockCrs:graph_init", range_type(0, numFineBlockRows),
848 KOKKOS_LAMBDA(const LO j) {
849 ia[j] = j;
850 ja[j] = INVALID;
851
852 if (j == (LO)numFineBlockRows - 1)
853 ia[numFineBlockRows] = numFineBlockRows;
854 });
855
856 // Fill Graph
857 const Kokkos::TeamPolicy<execution_space> policy(numAggregates, 1);
858 Kokkos::parallel_for(
859 "MueLu:TentativePF:BlockCrs:fillGraph", policy,
860 KOKKOS_LAMBDA(const typename Kokkos::TeamPolicy<execution_space>::member_type& thread) {
861 auto agg = thread.league_rank();
862 Xpetra::global_size_t offset = agg;
863
864 // size of the aggregate (number of DOFs in aggregate)
865 LO aggSize = aggRows(agg + 1) - aggRows(agg);
866
867 for (LO j = 0; j < aggSize; j++) {
868 // FIXME: Allow for bad maps
869 const LO localRow = aggToRowMapLO[aggDofSizes[agg] + j];
870 const size_t rowStart = ia[localRow];
871 ja[rowStart] = offset;
872 }
873 });
874
875 // Compress storage (remove all INVALID, which happen when we skip zeros)
876 // We do that in-place
877 {
878 // Stage 2: compress the arrays
879 SubFactoryMonitor m2(*this, "Stage 2 (CompressData)", coarseLevel);
880 // Fill i_temp with the correct row starts
881 rows_type i_temp(Kokkos::ViewAllocateWithoutInitializing("BlockGraph_rowptr"), numFineBlockRows + 1);
882 LO nnz = 0;
883 Kokkos::parallel_scan(
884 "MueLu:TentativePF:BlockCrs:compress_rows", range_type(0, numFineBlockRows),
885 KOKKOS_LAMBDA(const LO i, LO& upd, const bool& final) {
886 if (final)
887 i_temp[i] = upd;
888 for (auto j = ia[i]; j < ia[i + 1]; j++)
889 if (ja[j] != INVALID)
890 upd++;
891 if (final && i == (LO)numFineBlockRows - 1)
892 i_temp[numFineBlockRows] = upd;
893 },
894 nnz);
895
896 cols_type j_temp(Kokkos::ViewAllocateWithoutInitializing("BlockGraph_colind"), nnz);
897
898 Kokkos::parallel_for(
899 "MueLu:TentativePF:BlockCrs:compress_cols", range_type(0, numFineBlockRows),
900 KOKKOS_LAMBDA(const LO i) {
901 size_t rowStart = i_temp[i];
902 size_t lnnz = 0;
903 for (auto j = ia[i]; j < ia[i + 1]; j++)
904 if (ja[j] != INVALID) {
905 j_temp[rowStart + lnnz] = ja[j];
906 lnnz++;
907 }
908 });
909
910 ia = i_temp;
911 ja = j_temp;
912 }
913
914 RCP<CrsGraph> BlockGraph = CrsGraphFactory::Build(rowMap, coarseBlockMap, ia, ja);
915
916 // Managing labels & constants for ESFC
917 {
918 RCP<ParameterList> FCparams;
919 if (pL.isSublist("matrixmatrix: kernel params"))
920 FCparams = rcp(new ParameterList(pL.sublist("matrixmatrix: kernel params")));
921 else
922 FCparams = rcp(new ParameterList);
923 // By default, we don't need global constants for TentativeP
924 FCparams->set("compute global constants", FCparams->get("compute global constants", false));
925 std::string levelIDs = toString(levelID);
926 FCparams->set("Timer Label", std::string("MueLu::TentativeP-") + levelIDs);
927 RCP<const Export> dummy_e;
928 RCP<const Import> dummy_i;
929 BlockGraph->expertStaticFillComplete(coarseBlockMap, rowMap, dummy_i, dummy_e, FCparams);
930 }
931
932 // We can't leave the ia/ja pointers floating around, because of host/device view counting, so
933 // we clear them here
934 ia = rows_type();
935 ja = cols_type();
936
937 // Now let's make a BlockCrs Matrix
938 // NOTE: Assumes block size== NSDim
939 RCP<Xpetra::CrsMatrix<SC, LO, GO, NO>> P_xpetra = Xpetra::CrsMatrixFactory<SC, LO, GO, NO>::BuildBlock(BlockGraph, coarsePointMap, rangeMap, NSDim);
940 RCP<Xpetra::TpetraBlockCrsMatrix<SC, LO, GO, NO>> P_tpetra = rcp_dynamic_cast<Xpetra::TpetraBlockCrsMatrix<SC, LO, GO, NO>>(P_xpetra);
941 if (P_tpetra.is_null()) throw std::runtime_error("BuildPUncoupled: Matrix factory did not return a Tpetra::BlockCrsMatrix");
942 RCP<CrsMatrixWrap> P_wrap = rcp(new CrsMatrixWrap(P_xpetra));
943
944 auto values = P_tpetra->getTpetra_BlockCrsMatrix()->getValuesDeviceNonConst();
945 const LO stride = NSDim * NSDim;
946
947 Kokkos::parallel_for(
948 "MueLu:TentativePF:BlockCrs:main_loop_noqr", policy,
949 KOKKOS_LAMBDA(const typename Kokkos::TeamPolicy<execution_space>::member_type& thread) {
950 auto agg = thread.league_rank();
951
952 // size of the aggregate (number of DOFs in aggregate)
953 LO aggSize = aggRows(agg + 1) - aggRows(agg);
954 Xpetra::global_size_t offset = agg * NSDim;
955
956 // Q = localQR(:,0)/norm
957 for (LO j = 0; j < aggSize; j++) {
958 LO localBlockRow = aggToRowMapLO(aggRows(agg) + j);
959 LO rowStart = localBlockRow * stride;
960 for (LO r = 0; r < (LO)NSDim; r++) {
961 LO localPointRow = localBlockRow * NSDim + r;
962 for (LO c = 0; c < (LO)NSDim; c++) {
963 values[rowStart + r * NSDim + c] = fineNSRandom(localPointRow, c);
964 }
965 }
966 }
967
968 // R = norm
969 for (LO j = 0; j < (LO)NSDim; j++)
970 coarseNS(offset + j, j) = one;
971 });
972
973 Ptentative = P_wrap;
974}
975
976template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
978 BuildPcoupled(RCP<Matrix> /* A */, RCP<Aggregates> /* aggregates */,
979 RCP<AmalgamationInfo> /* amalgInfo */, RCP<MultiVector> /* fineNullspace */,
980 RCP<const Map> /* coarseMap */, RCP<Matrix>& /* Ptentative */,
981 RCP<MultiVector>& /* coarseNullspace */) const {
982 throw Exceptions::RuntimeError("MueLu: Construction of coupled tentative P is not implemented");
983}
984
985} // namespace MueLu
986
987#define MUELU_TENTATIVEPFACTORY_KOKKOS_SHORT
988#endif // MUELU_TENTATIVEPFACTORY_KOKKOS_DEF_HPP
#define SET_VALID_ENTRY(name)
MueLu::DefaultLocalOrdinal LocalOrdinal
MueLu::DefaultScalar Scalar
MueLu::DefaultGlobalOrdinal GlobalOrdinal
typename LWGraph_kokkos::local_graph_type local_graph_type
static void AmalgamateMap(const Map &sourceMap, const Matrix &A, RCP< const Map > &amalgamatedMap, Array< LO > &translation)
Method to create merged map for systems of PDEs.
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)
Timer to be used in factories. Similar to SubMonitor but adds a timer level by level.
void DeclareInput(Level &fineLevel, Level &coarseLevel) const override
Input.
RCP< const ParameterList > GetValidParameterList() const override
Return a const parameter list of valid parameters that setParameterList() will accept.
TentativePFactory_kokkos()
Constructor.
void BuildP(Level &fineLevel, Level &coarseLevel) const override
Abstract Build method.
void BuildPuncoupled(Level &coarseLevel, RCP< Matrix > A, RCP< Aggregates > aggregates, RCP< AmalgamationInfo > amalgInfo, RCP< MultiVector > fineNullspace, RCP< const Map > coarseMap, RCP< Matrix > &Ptentative, RCP< MultiVector > &coarseNullspace, int levelID) const
~TentativePFactory_kokkos() override
Destructor.
void BuildPuncoupledBlockCrs(Level &coarseLevel, RCP< Matrix > A, RCP< Aggregates > aggregates, RCP< AmalgamationInfo > amalgInfo, RCP< MultiVector > fineNullspace, RCP< const Map > coarsePointMap, RCP< Matrix > &Ptentative, RCP< MultiVector > &coarseNullspace, int levelID) const
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
void Build(Level &fineLevel, Level &coarseLevel) const override
Build an object with this factory.
Kokkos::RangePolicy< LocalOrdinal, execution_space > range_type
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.