MueLu Version of the Day
Loading...
Searching...
No Matches
MueLu_ReitzingerPFactory_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_REITZINGERPFACTORY_DEF_HPP
11#define MUELU_REITZINGERPFACTORY_DEF_HPP
12
13#include <Xpetra_MapFactory.hpp>
14#include <Xpetra_Map.hpp>
15#include <Xpetra_CrsMatrix.hpp>
16#include <Xpetra_Matrix.hpp>
17#include <Xpetra_MatrixMatrix.hpp>
18#include <Xpetra_MultiVector.hpp>
19#include <Xpetra_VectorFactory.hpp>
20#include <Xpetra_Import.hpp>
21#include <Xpetra_ImportFactory.hpp>
22#include <Xpetra_CrsMatrixWrap.hpp>
23// #include <Xpetra_IO.hpp>
24
26
27#include <Teuchos_ScalarTraits.hpp>
28
29#include "MueLu_MasterList.hpp"
30#include "MueLu_Monitor.hpp"
31#include "MueLu_Utilities.hpp"
32#include "MueLu_ImportUtils.hpp"
33
34#include "MueLu_Behavior.hpp"
35
36namespace MueLu {
37
38template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
40 RCP<ParameterList> validParamList = rcp(new ParameterList());
41
42#define SET_VALID_ENTRY(name) validParamList->setEntry(name, MasterList::getEntry(name))
43 SET_VALID_ENTRY("repartition: enable");
44 SET_VALID_ENTRY("repartition: use subcommunicators");
45 SET_VALID_ENTRY("tentative: calculate qr");
46 SET_VALID_ENTRY("tentative: constant column sums");
47#undef SET_VALID_ENTRY
48
49 validParamList->set<RCP<const FactoryBase> >("D0", Teuchos::null, "Generating factory of the matrix D0");
50 validParamList->set<RCP<const FactoryBase> >("NodeAggMatrix", Teuchos::null, "Generating factory of the matrix NodeAggMatrix");
51 validParamList->set<RCP<const FactoryBase> >("Pnodal", Teuchos::null, "Generating factory of the matrix P");
52
53 // Make sure we don't recursively validate options for the matrixmatrix kernels
54 ParameterList norecurse;
55 norecurse.disableRecursiveValidation();
56 validParamList->set<ParameterList>("matrixmatrix: kernel params", norecurse, "MatrixMatrix kernel parameters");
57
58 return validParamList;
59}
60
61template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
63 Input(fineLevel, "D0");
64 Input(coarseLevel, "NodeAggMatrix");
65 Input(coarseLevel, "Pnodal");
66}
67
68template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
70 return BuildP(fineLevel, coarseLevel);
71}
72
73template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
75 FactoryMonitor m(*this, "Build", coarseLevel);
76
77 using XMM = Xpetra::MatrixMatrix<SC, LO, GO, NO>;
78 using local_matrix_type = typename Xpetra::Matrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>::local_matrix_type;
79 using rowptr_type = typename local_matrix_type::row_map_type::non_const_type;
80 using colidx_type = typename local_matrix_type::index_type::non_const_type;
81 using values_type = typename local_matrix_type::values_type::non_const_type;
82
83 using impl_scalar_type = typename Matrix::impl_scalar_type;
84 using ATS = KokkosKernels::ArithTraits<impl_scalar_type>;
85 using mag_type = typename KokkosKernels::ArithTraits<impl_scalar_type>::magnitudeType;
86 using magATS = KokkosKernels::ArithTraits<mag_type>;
87
88 using execution_space = typename Node::execution_space;
89 using memory_space = typename Node::memory_space;
90
91 const auto one_Scalar = Teuchos::ScalarTraits<Scalar>::one();
92 const auto one_impl_scalar = ATS::one();
93 const auto zero_impl_scalar = ATS::zero();
94 const auto zero_LO = KokkosKernels::ArithTraits<LocalOrdinal>::zero();
95 const auto one_LO = KokkosKernels::ArithTraits<LocalOrdinal>::one();
96 const auto one_mag = magATS::one();
97 const auto eps_mag = magATS::epsilon();
98 const auto INVALID_GO = Teuchos::OrdinalTraits<GlobalOrdinal>::invalid();
99
100 // Using a nodal prolongator Pn and the discrete gradient matrix D0, this factory constructs
101 // a coarse discrete gradient matrix D0H and an edge prolongator Pe such that the commuting
102 // relationship
103 //
104 // D0 * Pn = Pe * D0H
105 //
106 // holds.
107
108 // The construction of the coarse discrete gradient works as follows.
109 // We create edges between aggregates that contain at least one pair of connected nodes.
110 // This boils down to computing the matrix
111 //
112 // Z := (D0*Pn)^T * (D0 * Pn).
113 //
114 // If Z_ij != 0 we create an edge e between the nodal aggregates i and j.
115 // Z is clearly symmetric. We only create a single edge between i and j.
116 // In the distributed case, we also need to decide which rank owns the coarse edge e.
117 // If both endpoints i and j live on process proc0 then proc0 should obiously own the edge e.
118 // If i lives on proc0 and j lives on proc1, we tie-break based on the rule
119 //
120 // min{proc0, proc1} if proc0+proc1 is odd,
121 // max{proc0, proc1} if proc0+proc1 is even.
122 //
123 // The orientation of the edge (encoded in the values 1 and -1) is determined by the GIDs of the endpoints.
124 // All edges point from smaller GID to larger GID, i.e. i < j.
125
126 // We also perform detection of boundary condtions and add additional edges to the coarse discrete gradient.
127 // We detect all edges in D0 that only connect to a single node.
128
129 Teuchos::FancyOStream& out0 = GetBlackHole();
130 const ParameterList& pL = GetParameterList();
131
132 bool update_communicators = pL.get<bool>("repartition: enable") && pL.get<bool>("repartition: use subcommunicators");
133
134 RCP<Matrix> D0 = Get<RCP<Matrix> >(fineLevel, "D0");
135 RCP<Matrix> Pn = Get<RCP<Matrix> >(coarseLevel, "Pnodal");
136
137 // This needs to be an Operator because if NodeMatrix gets repartitioned away, we get an Operator on the level
138 RCP<Operator> CoarseNodeMatrix = Get<RCP<Operator> >(coarseLevel, "NodeAggMatrix");
139
140 // Matrix matrix params
141 RCP<ParameterList> mm_params = rcp(new ParameterList);
142 if (pL.isSublist("matrixmatrix: kernel params"))
143 mm_params->sublist("matrixmatrix: kernel params") = pL.sublist("matrixmatrix: kernel params");
144
145 if (Behavior::debug()) { // Check that Pn is piecewise constant
146 // TODO: Should this be a debug-only check?
147
148 auto vec_ones = VectorFactory::Build(Pn->getDomainMap(), false);
149 vec_ones->putScalar(one_Scalar);
150 auto vec_rowsums = VectorFactory::Build(Pn->getRangeMap(), false);
151 Pn->apply(*vec_ones, *vec_rowsums, Teuchos::NO_TRANS);
152
153 auto lclPn = Pn->getLocalMatrixDevice();
154 auto lclRowSums = vec_rowsums->getLocalViewDevice(Tpetra::Access::ReadOnly);
155
156 bool all_entries_ok = true;
157 Kokkos::parallel_reduce(
158 Kokkos::RangePolicy<execution_space>(0, lclPn.numRows()), KOKKOS_LAMBDA(const LocalOrdinal rlid, bool& entries_ok) {
159 // rowsums are 1
160 entries_ok = entries_ok && (ATS::magnitude(lclRowSums(rlid, 0) - one_impl_scalar) < eps_mag);
161
162 // all nonzero entries are 1
163 auto row = lclPn.rowConst(rlid);
164 for (LocalOrdinal k = 0; k < row.length; ++k) {
165 entries_ok = entries_ok && (ATS::magnitude(row.value(k)-one_impl_scalar) < eps_mag);
166
167 } }, Kokkos::LAnd<bool>(all_entries_ok));
168
169 TEUCHOS_TEST_FOR_EXCEPTION(!all_entries_ok, std::runtime_error, "The prolongator needs to be piecewise constant and all entries need to be 1.");
170 }
171
172 RCP<Matrix> D0_Pn;
173 RCP<Matrix> D0H;
174 LocalOrdinal numCoarseEdges = 0;
175 LocalOrdinal numCoarseRegularEdges = 0;
176 LocalOrdinal numCoarseDirichletEdges = 0;
177 auto isDirichletFineEdge = Xpetra::VectorFactory<LocalOrdinal, LocalOrdinal, GlobalOrdinal, Node>::Build(D0->getRowMap(), false);
178 auto numFineEdges = isDirichletFineEdge->getMap()->getLocalNumElements();
179 {
180 // Construct D0*Pn and Z := (D0*Pn)^T * (D0*Pn)
181 RCP<Matrix> dummy;
182 D0_Pn = XMM::Multiply(*D0, false, *Pn, false, dummy, GetOStream(Runtime0), true, true);
183 RCP<Matrix> Z = XMM::Multiply(*D0_Pn, true, *D0_Pn, false, dummy, GetOStream(Runtime0), true, true);
184
185 auto rowMap = Z->getRowMap();
186 auto colMap = Z->getColMap();
187 auto lclRowMap = rowMap->getLocalMap();
188 auto lclColMap = colMap->getLocalMap();
189 auto lclZ = Z->getLocalMatrixDevice();
190 auto numLocalRows = lclZ.numRows();
191
192 if (Behavior::debug()) {
193 TEUCHOS_ASSERT(Utilities::MapsAreNested(*rowMap, *colMap));
194 }
195
196 auto importer = Z->getCrsGraph()->getImporter();
197 // TODO: replace with Kokkos once PID data lives on device
198 Teuchos::Array<int> Z_col_pids;
199 Kokkos::View<int*, memory_space> Z_col_pids_d;
200 if (!importer.is_null()) {
202 utils.getPids(*importer, Z_col_pids, false);
203 Kokkos::View<int*, Kokkos::HostSpace, Kokkos::MemoryTraits<Kokkos::Unmanaged> > Z_col_pids_h(Z_col_pids.data(), Z_col_pids.size());
204 Z_col_pids_d = Kokkos::View<int*, memory_space>("Z_col_pids_d", Z_col_pids.size());
205 Kokkos::deep_copy(Z_col_pids_d, Z_col_pids_h);
206 }
207
208 int myProcId = rowMap->getComm()->getRank();
209
210 // Tie-break criterion for owner of coarse edges
211 auto tie_break = KOKKOS_LAMBDA(int proc0, int proc1) {
212 if ((proc0 + proc1) % 2 == 1) {
213 return Kokkos::min(proc0, proc1);
214 } else {
215 return Kokkos::max(proc0, proc1);
216 }
217 };
218
219 // Utility function to determine whether we need to add a coarse edge for entry
220 // (rlid, clid) of Z.
221 auto add_edge = KOKKOS_LAMBDA(LocalOrdinal rlid, LocalOrdinal clid) {
222 if (clid < numLocalRows) {
223 // Both row and column index are local, this process owns the new edge.
224 // Only create one edge between rlid and clid and ignore the transposed entry.
225 return (rlid < clid);
226 } else {
227 // Column index is nonlocal. Need to decide if this process owns the new edge.
228 int otherProcId = Z_col_pids_d(clid);
229 int owner = tie_break(myProcId, otherProcId);
230 return (owner == myProcId);
231 }
232 };
233
234 // Count up how many coarse regular edges we are creating.
235 Kokkos::parallel_reduce(
236 Kokkos::RangePolicy<execution_space>(0, numLocalRows), KOKKOS_LAMBDA(const LocalOrdinal rlid, LocalOrdinal& ne) {
237 auto row = lclZ.rowConst(rlid);
238 // Loop over entries in row of Z
239 for (LocalOrdinal k = 0; k < row.length; ++k) {
240 auto clid = row.colidx(k);
241 if (add_edge(rlid, clid))
242 ++ne;
243 }
244 },
245 numCoarseRegularEdges);
246
247 // Mark as singleParents any D0 edges with only one node (so these are
248 // edges that connect an interior node with a Dirichlet node).
249 // isDirichletFineEdge is 1 for edges with a single endpoint and 0 otherwise.
250 using LOMatrix = Tpetra::CrsMatrix<LocalOrdinal, LocalOrdinal, GlobalOrdinal, Node>;
251 RCP<LOMatrix> D0_LocalOrdinal;
252 {
253 // We want to do a apply using D0 on vectors with Scalar=LocalOrdinal.
254 // Something like this could work and would not require any memory allocations, but it requires
255 // "convert" to be ETI'd for all possible scalar types.
256
257 // toTpetra(D0)->template convert<LocalOrdinal>()->apply(*toTpetra(oneVec), *toTpetra(isDirichletFineEdge), Teuchos::NO_TRANS);
258
259 using lo_local_matrix_type = typename LOMatrix::local_matrix_device_type;
260
261 auto lclGraph = D0->getCrsGraph()->getLocalGraphDevice();
262 Kokkos::View<LocalOrdinal*, memory_space> values(Kokkos::ViewAllocateWithoutInitializing("values_LocalOrdinal"), D0->getLocalNumEntries());
263 {
264 auto values_scalar = D0->getLocalMatrixDevice().values;
265 Kokkos::parallel_for(
266 "MueLu::ReitzingerPFactory::convert", Kokkos::RangePolicy<execution_space>(0, values.extent(0)), KOKKOS_LAMBDA(const size_t i) {
267 if (values_scalar(i) == one_impl_scalar)
268 values(i) = one_LO;
269 else if (values_scalar(i) == -one_impl_scalar)
270 values(i) = -one_LO;
271 else if (values_scalar(i) == zero_impl_scalar)
272 values(i) = zero_LO;
273 else
274 Kokkos::abort("D0 contains bad values");
275 });
276 }
277 lo_local_matrix_type lclMatrix("D0_LocalOrdinal", D0->getLocalMatrixDevice().numCols(), values, lclGraph);
278
279 D0_LocalOrdinal = rcp(new LOMatrix(lclMatrix, toTpetra(D0->getRowMap()), toTpetra(D0->getColMap()), toTpetra(D0->getDomainMap()), toTpetra(D0->getRangeMap())));
280 }
281 {
282 auto oneVec = Xpetra::VectorFactory<LocalOrdinal, LocalOrdinal, GlobalOrdinal, Node>::Build(D0->getDomainMap(), false);
283 oneVec->putScalar(KokkosKernels::ArithTraits<LocalOrdinal>::one());
284 D0_LocalOrdinal->apply(*toTpetra(oneVec), *toTpetra(isDirichletFineEdge), Teuchos::NO_TRANS);
285
286 auto lcl_isDirichletFineEdge = isDirichletFineEdge->getLocalViewDevice(Tpetra::Access::ReadWrite);
287 auto lcl_D0_Pn = D0_Pn->getLocalMatrixDevice();
288 Kokkos::parallel_for(
289 Kokkos::RangePolicy<execution_space>(0, lcl_isDirichletFineEdge.extent(0)), KOKKOS_LAMBDA(const LocalOrdinal i) {
290 if (ATS::magnitude(ATS::magnitude(lcl_isDirichletFineEdge(i, 0)) - one_mag) > eps_mag) {
291 // This is a regular edge with two end points.
292 lcl_isDirichletFineEdge(i, 0) = zero_LO;
293 } else {
294 // This is an edge with one endpoint.
295 lcl_isDirichletFineEdge(i, 0) = one_LO;
296 }
297 });
298 }
299
300 // Count the number of fine Dirichlet edges that are connected to every coarse nodal aggregate via
301 // the graph of D0*Pn.
302 auto numberConnectedFineDirichletEdgesToCoarseNode = Xpetra::VectorFactory<LocalOrdinal, LocalOrdinal, GlobalOrdinal, Node>::Build(D0_Pn->getDomainMap(), false);
303 {
304 auto abs_D0_Pn = LOMatrix(toTpetra(D0_Pn->getCrsGraph()));
305 abs_D0_Pn.fillComplete(toTpetra(D0_Pn->getDomainMap()), toTpetra(D0_Pn->getRangeMap()));
306 abs_D0_Pn.setAllToScalar(KokkosKernels::ArithTraits<LocalOrdinal>::one());
307 abs_D0_Pn.apply(*toTpetra(isDirichletFineEdge), *toTpetra(numberConnectedFineDirichletEdgesToCoarseNode), Teuchos::TRANS);
308 }
309
310 // Count local Dirichlet coarse edges
311 {
312 auto lcl_numberConnectedFineDirichletEdgesToCoarseNode = numberConnectedFineDirichletEdgesToCoarseNode->getLocalViewDevice(Tpetra::Access::ReadOnly);
313
314 Kokkos::parallel_reduce(
315 Kokkos::RangePolicy<execution_space>(0, lcl_numberConnectedFineDirichletEdgesToCoarseNode.extent(0)),
316 KOKKOS_LAMBDA(const LocalOrdinal i, LocalOrdinal& ne) {
317 if (ATS::magnitude(lcl_numberConnectedFineDirichletEdgesToCoarseNode(i, 0)) > eps_mag) {
318 ++ne;
319 }
320 },
321 numCoarseDirichletEdges);
322 }
323
324 if (IsPrint(Statistics0)) {
325 LocalOrdinal numGlobalRegularEdges;
326 LocalOrdinal numGlobalDirichletEdges;
327 MueLu_sumAll(rowMap->getComm(), numCoarseRegularEdges, numGlobalRegularEdges);
328 MueLu_sumAll(rowMap->getComm(), numCoarseDirichletEdges, numGlobalDirichletEdges);
329 GetOStream(Statistics0) << "regular edges: " << numGlobalRegularEdges << ", Dirichlet edges: " << numGlobalDirichletEdges << std::endl;
330 }
331
332 numCoarseEdges = numCoarseRegularEdges + numCoarseDirichletEdges;
333 rowptr_type rowptr(Kokkos::ViewAllocateWithoutInitializing("rowptr D0H"), numCoarseEdges + 1);
334 // 2 entries per regular edge, 1 entry per Dirichlet edge
335 LocalOrdinal nnz = 2 * numCoarseRegularEdges + numCoarseDirichletEdges;
336 colidx_type colidx(Kokkos::ViewAllocateWithoutInitializing("colidx D0H"), nnz);
337 values_type values(Kokkos::ViewAllocateWithoutInitializing("values D0H"), nnz);
338
339 // Fill regular edges
340 Kokkos::parallel_scan(
341 Kokkos::RangePolicy<execution_space>(0, numLocalRows),
342 KOKKOS_LAMBDA(const LocalOrdinal rlid, LocalOrdinal& ne, const bool update) {
343 auto row = lclZ.rowConst(rlid);
344 if (!update) {
345 // First pass: figure out offsets for entries.
346 for (LocalOrdinal k = 0; k < row.length; ++k) {
347 auto clid = row.colidx(k);
348 if (add_edge(rlid, clid))
349 ++ne;
350 }
351 } else {
352 // Second pass: enter entries
353
354 // initialize
355 if (rlid == 0)
356 rowptr(rlid) = 0;
357
358 auto rgid = lclRowMap.getGlobalElement(rlid);
359 auto rclid = lclColMap.getLocalElement(rgid);
360
361 // loop over entries in row of Z
362 for (LocalOrdinal k = 0; k < row.length; ++k) {
363 auto clid = row.colidx(k);
364 if (add_edge(rlid, clid)) {
365 auto cgid = lclColMap.getGlobalElement(clid);
366 // enter the two end-points of the edge, orient edge based on GIDs of nodal endpoints
367 colidx(2 * ne) = rclid;
368 colidx(2 * ne + 1) = clid;
369 if (rgid < cgid) {
370 values(2 * ne) = -one_impl_scalar;
371 values(2 * ne + 1) = one_impl_scalar;
372 } else {
373 values(2 * ne) = one_impl_scalar;
374 values(2 * ne + 1) = -one_impl_scalar;
375 }
376 ++ne;
377 rowptr(ne) = 2 * ne;
378 }
379 }
380 }
381 });
382
383 // Fill Dirichlet edges
384 // Create one coarse Dirichlet edge for every nodal aggregate that is connected to at least one fine Dirichlet edge.
385 {
386 auto lcl_numberConnectedFineDirichletEdgesToCoarseNode = numberConnectedFineDirichletEdgesToCoarseNode->getLocalViewDevice(Tpetra::Access::ReadOnly);
387 Kokkos::parallel_scan(
388 Kokkos::RangePolicy<execution_space>(0, lcl_numberConnectedFineDirichletEdgesToCoarseNode.extent(0)),
389 KOKKOS_LAMBDA(const LocalOrdinal agg_lid, LocalOrdinal& ne, const bool update) {
390 if (ATS::magnitude(lcl_numberConnectedFineDirichletEdgesToCoarseNode(agg_lid, 0)) > eps_mag) {
391 if (!update) {
392 // First pass: figure out offsets
393 ++ne;
394 } else {
395 // Second pass: fill
396 colidx(2 * numCoarseRegularEdges + ne) = agg_lid;
397 values(2 * numCoarseRegularEdges + ne) = one_impl_scalar;
398 ++ne;
399 rowptr(numCoarseRegularEdges + ne) = 2 * numCoarseRegularEdges + ne;
400 }
401 }
402 });
403 }
404
405 auto D0H_rowmap = MapFactory::Build(rowMap->lib(), INVALID_GO, numCoarseEdges, 0, rowMap->getComm());
406 auto lclD0H = local_matrix_type("D0H", numCoarseEdges, colMap->getLocalNumElements(), nnz, values, rowptr, colidx);
407
408 // Construct distributed matrix
409 D0H = MatrixFactory::Build(lclD0H, D0H_rowmap, colMap, Z->getDomainMap(), D0H_rowmap);
410 }
411
412 const bool needToBuildPe = (coarseLevel.IsRequested("P", this) ||
413 coarseLevel.IsRequested("Ptent", this));
414 RCP<Matrix> Pe;
415 if (needToBuildPe) {
416 // Create the Pe matrix, but with the extra entries. From ML's notes:
417 /* The general idea is that the matrix */
418 /* T_h P_n T_H^* */
419 /* is almost Pe. If we make sure that P_n contains 1's and -1's, the*/
420 /* matrix triple product will yield a matrix with +/- 1 and +/- 2's.*/
421 /* If we remove all the 1's and divide the 2's by 2. we arrive at Pe*/
422
423 RCP<Matrix> D0_Pn_D0HT;
424 {
425 SubFactoryMonitor m2(*this, "Generate Pe (pre-fix)", coarseLevel);
426#if 0
427 {
428 // If you're concerned about processor / rank mismatches, this debugging code might help
429 int rank = D0->getRowMap()->getComm()->getRank();
430 int fine_level = fineLevel.GetLevelID();
431 printf("[%d] Level %d Checkpoint #2 Pn = %d/%d/%d/%d D0c = %d/%d/%d/%d D0 = %d/%d/%d/%d\n",rank,fine_level,
432 Pn->getRangeMap()->getComm()->getSize(),
433 Pn->getRowMap()->getComm()->getSize(),
434 Pn->getColMap()->getComm()->getSize(),
435 Pn->getDomainMap()->getComm()->getSize(),
436 D0H->getRangeMap()->getComm()->getSize(),
437 D0H->getRowMap()->getComm()->getSize(),
438 D0H->getColMap()->getComm()->getSize(),
439 D0H->getDomainMap()->getComm()->getSize(),
440 D0->getRangeMap()->getComm()->getSize(),
441 D0->getRowMap()->getComm()->getSize(),
442 D0->getColMap()->getComm()->getSize(),
443 D0->getDomainMap()->getComm()->getSize());
444 fflush(stdout);
445 D0->getRowMap()->getComm()->barrier();
446 }
447#endif
448 RCP<Matrix> dummy;
449 RCP<Matrix> Pn_D0cT = XMM::Multiply(*Pn, false, *D0H, true, dummy, out0, true, true, "Pn*D0c'", mm_params);
450
451 // We don't want this guy getting accidently used later
452 if (!mm_params.is_null()) mm_params->remove("importer", false);
453
454 D0_Pn_D0HT = XMM::Multiply(*D0, false, *Pn_D0cT, false, dummy, out0, true, true, "D0*(Pn*D0c')", mm_params);
455
456 // TODO: Something like this *might* work. But this specifically, doesn't
457 // Pe = XMM::Multiply(*D0_Pn_nonghosted,false,*D0H,true,dummy,out0,true,true,"(D0*Pn)*D0c'",mm_params);
458 }
459
460 {
461 auto lcl_D0_Pn = D0_Pn->getLocalMatrixDevice();
462 auto lcl_D0_Pn_D0HT = D0_Pn_D0HT->getLocalMatrixDevice();
463 auto lcl_isDirichletFineEdge = isDirichletFineEdge->getLocalViewDevice(Tpetra::Access::ReadOnly);
464
465 auto lcl_colmap_D0_Pn_D0HT = D0_Pn_D0HT->getColMap()->getLocalMap();
466
467 const auto half = one_impl_scalar / (one_impl_scalar + one_impl_scalar);
468
469 // overallocate by 1 to allow for easier counting
470 rowptr_type Pe_rowptr("Pe_rowptr", numFineEdges + 2);
471
472 // count entries per row
473 Kokkos::parallel_for(
474 "Pe_count_entries", Kokkos::RangePolicy<execution_space>(0, numFineEdges), KOKKOS_LAMBDA(const LocalOrdinal fineEdge) {
475 if (lcl_isDirichletFineEdge(fineEdge, 0) != one_LO) {
476 // regular fine edge
477 auto row = lcl_D0_Pn_D0HT.rowConst(fineEdge);
478 for (int k = 0; k < row.length; ++k) {
479 auto val = row.value(k);
480 // filter out entries +-1 and 0
481 if (!((ATS::magnitude(val - one_impl_scalar) < eps_mag) || (ATS::magnitude(val + one_impl_scalar) < eps_mag) || (ATS::magnitude(val) < eps_mag))) {
482 // add entry (fineEdge, clid) -> val/2.
483 ++Pe_rowptr(fineEdge + 2);
484 }
485 }
486 } else {
487 // Dirichlet interior fine edge
488 ++Pe_rowptr(fineEdge + 2);
489 }
490 });
491
492 // prefix sum
493 LocalOrdinal Pe_nnz;
494 Kokkos::parallel_scan(
495 "Pe_prefix_sum", Kokkos::RangePolicy<execution_space>(0, numFineEdges + 2), KOKKOS_LAMBDA(const LocalOrdinal rlid, LocalOrdinal& nnz, const bool update) {
496 nnz += Pe_rowptr(rlid);
497 if (update) {
498 Pe_rowptr(rlid) = nnz;
499 }
500 },
501 Pe_nnz);
502
503 // allocate view for indices and values
504 colidx_type Pe_colidx("Pe_colidx", Pe_nnz);
505 values_type Pe_values("Pe_values", Pe_nnz);
506
507 // We build the mapping from coarse nodes lids wrt column map of D0_Pn to coarse edge gids.
508 RCP<GOVector> map_coarseNodes_colMap_D0_Pn_to_coarseEdges;
509 {
510 auto map_coarseEdges_rowMap_D0H_to_coarseEdges = Xpetra::VectorFactory<GlobalOrdinal, LocalOrdinal, GlobalOrdinal, Node>::Build(D0H->getRowMap());
511 {
512 auto lcl_map_coarseEdges_rowMap_D0H_to_coarseEdges = map_coarseEdges_rowMap_D0H_to_coarseEdges->getLocalViewDevice(Tpetra::Access::OverwriteAll);
513 auto lclMap = D0H->getRowMap()->getLocalMap();
514 Kokkos::parallel_for(
515 Kokkos::RangePolicy<execution_space>(numCoarseRegularEdges, numCoarseEdges), KOKKOS_LAMBDA(const LocalOrdinal coarseEdge) {
516 lcl_map_coarseEdges_rowMap_D0H_to_coarseEdges(coarseEdge, 0) = lclMap.getGlobalElement(coarseEdge);
517 });
518 }
519 auto map_coarseNodes_domainMap_D0_Pn_to_coarseEdges = Xpetra::VectorFactory<GlobalOrdinal, LocalOrdinal, GlobalOrdinal, Node>::Build(D0H->getDomainMap());
520 {
521 // We want to do a transpose apply using D0H on vectors with Scalar=GlobalOrdinal.
522 // Something like this could work and would not require any memory allocations, but it requires
523 // "convert" to be ETI'd for all possible scalar types.
524
525 // toTpetra(D0H)->template convert<GlobalOrdinal>()->apply(*toTpetra(map_coarseEdges_rowMap_D0H_to_coarseEdges), *toTpetra(map_coarseNodes_domainMap_D0_Pn_to_coarseEdges), Teuchos::TRANS);
526
527 using GOMatrix = Tpetra::CrsMatrix<GlobalOrdinal, LocalOrdinal, GlobalOrdinal, Node>;
528 using go_local_matrix_type = typename GOMatrix::local_matrix_device_type;
529
530 auto lclGraph = D0H->getCrsGraph()->getLocalGraphDevice();
531 typename go_local_matrix_type::values_type::non_const_type ones("ones_GlobalOrdinal", D0H->getLocalNumEntries());
532 const auto one_GO = KokkosKernels::ArithTraits<typename go_local_matrix_type::values_type::value_type>::one();
533 Kokkos::deep_copy(ones, one_GO);
534
535 go_local_matrix_type lclMatrix("D0H_GlobalOrdinal", D0H->getLocalMatrixDevice().numCols(), ones, lclGraph);
536
537 auto D0H_GlobalOrdinal = GOMatrix(lclMatrix, toTpetra(D0H->getRowMap()), toTpetra(D0H->getColMap()), toTpetra(D0H->getDomainMap()), toTpetra(D0H->getRangeMap()));
538 D0H_GlobalOrdinal.apply(*toTpetra(map_coarseEdges_rowMap_D0H_to_coarseEdges), *toTpetra(map_coarseNodes_domainMap_D0_Pn_to_coarseEdges), Teuchos::TRANS);
539 }
540
541 auto importer = D0_Pn->getCrsGraph()->getImporter();
542 if (!importer.is_null()) {
543 map_coarseNodes_colMap_D0_Pn_to_coarseEdges = Xpetra::VectorFactory<GlobalOrdinal, LocalOrdinal, GlobalOrdinal, Node>::Build(D0_Pn->getColMap());
544 map_coarseNodes_colMap_D0_Pn_to_coarseEdges->doImport(*map_coarseNodes_domainMap_D0_Pn_to_coarseEdges, *importer, Xpetra::INSERT);
545 } else {
546 map_coarseNodes_colMap_D0_Pn_to_coarseEdges = map_coarseNodes_domainMap_D0_Pn_to_coarseEdges;
547 }
548 }
549 {
550 auto lcl_map_coarseNodes_colMap_D0_Pn_to_coarseEdges = map_coarseNodes_colMap_D0_Pn_to_coarseEdges->getLocalViewDevice(Tpetra::Access::ReadOnly);
551
552 // fill
553 Kokkos::parallel_for(
554 "Pe_fill", Kokkos::RangePolicy<execution_space>(0, numFineEdges), KOKKOS_LAMBDA(const LocalOrdinal fineEdge_lid) {
555 if (lcl_isDirichletFineEdge(fineEdge_lid, 0) != one_LO) {
556 // regular fine edge
557 auto row = lcl_D0_Pn_D0HT.rowConst(fineEdge_lid);
558 for (int k = 0; k < row.length; ++k) {
559 auto val = row.value(k);
560 if (!((ATS::magnitude(val - one_impl_scalar) < eps_mag) || (ATS::magnitude(val + one_impl_scalar) < eps_mag) || (ATS::magnitude(val) < eps_mag))) {
561 auto clid = row.colidx(k);
562 // add entry (fineEdge_lid, clid) -> val/2.
563 auto offset = Pe_rowptr(fineEdge_lid + 1);
564 Pe_colidx(offset) = clid;
565 Pe_values(offset) = val * half;
566 ++Pe_rowptr(fineEdge_lid + 1);
567 }
568 }
569 } else {
570 // Dirichlet interior fine edge
571 // Only one nonzero entry in row of D0_Pn: (fineEdge_lid, coarseNode_lid_D0_Pn) -> val_D0_Pn
572 for (auto offset_D0_Pn = lcl_D0_Pn.graph.row_map(fineEdge_lid); offset_D0_Pn < lcl_D0_Pn.graph.row_map(fineEdge_lid + 1); ++offset_D0_Pn) {
573 LocalOrdinal coarseNode_lid_D0_Pn = lcl_D0_Pn.graph.entries(offset_D0_Pn);
574 impl_scalar_type val_D0_Pn = lcl_D0_Pn.values(offset_D0_Pn);
575 if (ATS::magnitude(val_D0_Pn) > eps_mag) {
576 GlobalOrdinal coarseEdge_gid = lcl_map_coarseNodes_colMap_D0_Pn_to_coarseEdges(coarseNode_lid_D0_Pn, 0);
577
578 auto coarseEdge_lid_D0_Pn_D0HT = lcl_colmap_D0_Pn_D0HT.getLocalElement(coarseEdge_gid);
579
580 // We rely on the fact that all coarse interior edges have been created with value 1.
581 const auto val_D0H = one_impl_scalar;
582
583 // add entry (fineEdge_lid, coarseEdge) -> val_D0_Pn/val_D0H to edge prolongator
584 auto offset_Pe = Pe_rowptr(fineEdge_lid + 1);
585 Pe_colidx(offset_Pe) = coarseEdge_lid_D0_Pn_D0HT;
586 Pe_values(offset_Pe) = val_D0_Pn / val_D0H;
587 ++Pe_rowptr(fineEdge_lid + 1);
588 break;
589 }
590 }
591 }
592 });
593 }
594 auto lclPe = local_matrix_type("Pe", numFineEdges, D0_Pn_D0HT->getColMap()->getLocalNumElements(), Pe_nnz, Pe_values, Kokkos::subview(Pe_rowptr, Kokkos::make_pair((decltype(numFineEdges))0, numFineEdges + 1)), Pe_colidx);
595
596 // Construct distributed matrix
597 Pe = MatrixFactory::Build(lclPe, D0->getRowMap(), D0_Pn_D0HT->getColMap(), D0H->getRangeMap(), D0->getRangeMap());
598 }
599
600 if (Behavior::debug()) {
601 /* Check commuting property */
602 CheckCommutingProperty(*Pe, *D0H, *D0, *Pn);
603 }
604 }
605
606 /* If we're repartitioning here, we need to cut down the communicators */
607 // NOTE: We need to do this *after* checking the commuting property, since
608 // that's going to need to fineLevel's communicators, not the repartitioned ones
609 if (update_communicators) {
610 // NOTE: We can only do D0 here. We have to do Ke_coarse=(Re Ke_fine Pe) in RebalanceAcFactory
611 RCP<const Teuchos::Comm<int> > newComm;
612 if (!CoarseNodeMatrix.is_null()) newComm = CoarseNodeMatrix->getDomainMap()->getComm();
613 RCP<const Map> newMap = MapFactory::copyMapWithNewComm(D0H->getRowMap(), newComm);
614 D0H->removeEmptyProcessesInPlace(newMap);
615
616 // The "in place" still leaves a dummy matrix here. That needs to go
617 if (newMap.is_null()) D0H = Teuchos::null;
618
619 Set(coarseLevel, "InPlaceMap", newMap);
620 }
621
622 /* Set output on the level */
623 if (coarseLevel.IsRequested("P", this))
624 Set(coarseLevel, "P", Pe);
625 if (coarseLevel.IsRequested("Ptent", this))
626 Set(coarseLevel, "Ptent", Pe);
627
628 Set(coarseLevel, "D0", D0H);
629
630 /* This needs to be kept for the smoothers */
631 coarseLevel.Set("D0", D0H, NoFactory::get());
632 coarseLevel.AddKeepFlag("D0", NoFactory::get(), MueLu::Final);
633 coarseLevel.RemoveKeepFlag("D0", NoFactory::get(), MueLu::UserData);
634
635#if 0
636 {
637 int numProcs = Pe->getRowMap()->getComm()->getSize();
638 char fname[80];
639
640 sprintf(fname, "Pe_%d_%d.mat", numProcs, fineLevel.GetLevelID());
641 Xpetra::IO<SC, LO, GO, NO>::Write(fname, *Pe);
642 sprintf(fname, "Pn_%d_%d.mat", numProcs, fineLevel.GetLevelID());
643 Xpetra::IO<SC, LO, GO, NO>::Write(fname, *Pn);
644 if (!D0H.is_null()) {
645 sprintf(fname, "D0c_%d_%d.mat", numProcs, fineLevel.GetLevelID());
646 Xpetra::IO<SC, LO, GO, NO>::Write(fname, *D0H);
647 }
648 sprintf(fname, "D0f_%d_%d.mat", numProcs, fineLevel.GetLevelID());
649 Xpetra::IO<SC, LO, GO, NO>::Write(fname, *D0);
650 }
651#endif
652
653} // end Build
654
655template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
657 CheckCommutingProperty(const Matrix& Pe, const Matrix& D0_c, const Matrix& D0_f, const Matrix& Pn) const {
658 if (IsPrint(Statistics0)) {
659 using XMM = MatrixMatrix;
660 auto one = Teuchos::ScalarTraits<SC>::one();
661
662 RCP<Matrix> dummy;
663 RCP<Matrix> left = XMM::Multiply(Pe, false, D0_c, false, dummy, GetOStream(Runtime0));
664 RCP<Matrix> right = XMM::Multiply(D0_f, false, Pn, false, dummy, GetOStream(Runtime0));
665
666 RCP<Matrix> summation;
667 XMM::TwoMatrixAdd(*left, false, one, *right, false, -one, summation, GetOStream(Runtime0));
668 summation->fillComplete(left->getDomainMap(), left->getRangeMap());
669
670 auto norm = summation->getFrobeniusNorm();
671 GetOStream(Statistics0) << "CheckCommutingProperty: || Pe D0_c - D0_f Pn || = " << norm << std::endl;
672 }
673
674} // end CheckCommutingProperty
675
676} // namespace MueLu
677
678#define MUELU_REITZINGERPFACTORY_SHORT
679#endif // MUELU_REITZINGERPFACTORY_DEF_HPP
#define SET_VALID_ENTRY(name)
#define MueLu_sumAll(rcpComm, in, out)
MueLu::DefaultLocalOrdinal LocalOrdinal
MueLu::DefaultGlobalOrdinal GlobalOrdinal
static bool debug()
Whether MueLu is in debug mode.
Timer to be used in factories. Similar to Monitor but with additional timers.
MueLu utility class for Import-related routines.
void getPids(const Xpetra::Import< LocalOrdinal, GlobalOrdinal, Node > &Importer, Teuchos::Array< int > &pids, bool use_minus_one_for_local)
Like getPidGidPairs, but just gets the PIDs, ordered by the column Map.
Class that holds all level-specific information.
void RemoveKeepFlag(const std::string &ename, const FactoryBase *factory, KeepType keep=MueLu::All)
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())
bool IsRequested(const std::string &ename, const FactoryBase *factory=NoFactory::get()) const
Test whether a need has been requested. Note: this tells nothing about whether the need's value exist...
static const NoFactory * get()
void BuildP(Level &fineLevel, Level &coarseLevel) const
Abstract Build method.
void Build(Level &fineLevel, Level &coarseLevel) const
Build an object with this factory.
void CheckCommutingProperty(const Matrix &Pe, const Matrix &D0_c, const Matrix &D0_f, const Matrix &Pn) const
Utility method.
RCP< const ParameterList > GetValidParameterList() const
Return a const parameter list of valid parameters that setParameterList() will accept.
void DeclareInput(Level &fineLevel, Level &coarseLevel) const
Input.
Timer to be used in factories. Similar to SubMonitor but adds a timer level by level.
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 ...
@ UserData
User data are always kept. This flag is set automatically when Level::Set("data", data) is used....
@ Runtime0
One-liner description of what is happening.
@ Statistics0
Print statistics that do not involve significant additional computation.