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;
118 if (bTransferCoordinates_) {
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
135 if (bTransferCoordinates_) {
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
219 if (bTransferCoordinates_) {
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.
Timer to be used in factories. Similar to Monitor but with additional timers.
Class that holds all level-specific information.
bool IsAvailable(const std::string &ename, const FactoryBase *factory=NoFactory::get()) const
Test whether a need's value has been saved.
int GetLevelID() const
Return level number.
void AddKeepFlag(const std::string &ename, const FactoryBase *factory=NoFactory::get(), KeepType keep=MueLu::Keep)
void Set(const std::string &ename, const T &entry, const FactoryBase *factory=NoFactory::get())
static const NoFactory * get()
static std::string PrintMatrixInfo(const Matrix &A, const std::string &msgTag, RCP< const Teuchos::ParameterList > params=Teuchos::null)
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< LocalOrdinal, GlobalOrdinal, Node > &rowMap, const Xpetra::Map< LocalOrdinal, GlobalOrdinal, Node > &colMap)
Namespace for MueLu classes and methods.
@ Final
Keep data only for this run. Used to keep data useful for Hierarchy::Iterate(). Data will be deleted ...
@ Warnings0
Important warning messages (one line)
@ Statistics2
Print even more statistics.
@ Runtime1
Description of what is happening (more verbose)
std::string toString(const T &what)
Little helper function to convert non-string types to strings.