MueLu Version of the Day
Loading...
Searching...
No Matches
MueLu_SparseConstraint_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_SPARSECONSTRAINT_DEF_HPP
11#define MUELU_SPARSECONSTRAINT_DEF_HPP
12
13#include <Xpetra_Map.hpp>
14#include <Xpetra_MapFactory.hpp>
15#include <Xpetra_Matrix.hpp>
16#include <Xpetra_MultiVector.hpp>
17#include <Xpetra_CrsGraph.hpp>
18#include <Xpetra_MatrixMatrix.hpp>
19#include "Kokkos_Pair.hpp"
20#include "Teuchos_ScalarTraits.hpp"
21#include "Teuchos_VerbosityLevel.hpp"
22#include "Xpetra_MatrixFactory.hpp"
23
24#include "MueLu_Exceptions.hpp"
26#include "MueLu_Utilities.hpp"
27#include "Xpetra_MatrixFactory.hpp"
28#include "MueLu_Monitor.hpp"
29
30namespace MueLu {
31template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
33 SparseConstraint(const RCP<Matrix>& P_nodal,
34 const RCP<Matrix>& D,
35 const RCP<Matrix>& Dc,
36 RCP<const CrsGraph> Ppattern,
37 const std::string& solverType) {
38 this->SetPattern(Ppattern);
39 P_nodal_ = P_nodal;
40 D_ = D;
41 Dc_ = Dc;
42 this->SetProcRankVerbose(Ppattern->getRowMap()->getComm()->getRank());
43 Setup();
44 this->PrepareLeastSquaresSolve(solverType, /*detect_singular_blocks=*/true);
45}
46
47template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
49 using graph_t = typename CrsGraph::local_graph_type;
50 using matrix_t = typename CrsMatrix::local_matrix_type;
51 using lno_view_t = typename graph_t::row_map_type::non_const_type;
52 using lno_nnz_view_t = typename graph_t::entries_type::non_const_type;
53 using scalar_view_t = typename matrix_t::values_type::non_const_type;
54 using range_type = Kokkos::RangePolicy<LocalOrdinal, typename Node::execution_space>;
55
56 auto INVALID = Teuchos::OrdinalTraits<Xpetra::global_size_t>::invalid();
57
58 Monitor m(*this, "Setup");
59
60 auto D = D_;
61 auto Dc = Dc_;
62 auto Ppattern = this->GetPattern();
63
64 // The constraint on Pe (with graph Ppattern) takes the form
65 //
66 // Pe * Dc = D * Pn.
67 //
68 // This means that we have nnz(Pe * Dc) constraints for nnz(Ppattern)
69 // unknowns.
70 //
71 // A single constraint corresponds to an entry (i,j) of Pe * D0c and is
72 // written out via the sparse matrix-matrix products between Pe and D0c:
73 //
74 // sum_{k} Pe_{i,k} Dc_{k,j} = (D * Pn)_{i,j}
75 //
76 // We map (i,j) to its offset I in (Pe * D0c) and (i,k) to its offset J in Pe.
77 //
78 // The constraint matrix X then has the entry
79 // X_{I,J} = Dc_{k,j}.
80
81 auto lib = Ppattern->getRowMap()->lib();
82
83 // If we rebalanced then Dc lives on a smaller communicator than D.
84 // Since we need to perform matrix-matrix multiplications with Dc, we construct a version of it that lives on the same communicator.
85 auto comm = Ppattern->getRowMap()->getComm();
86 if (Dc.is_null() || Dc->getRowMap()->getComm()->getSize() < comm->getSize()) {
87 if (Dc.is_null()) {
88 Kokkos::View<GlobalOrdinal*, typename Node::memory_space> dummy("", 0);
89 auto big_coarse_nodal_map = MapFactory::Build(lib, INVALID, dummy, 0, comm);
90 auto big_coarse_edge_map = MapFactory::Build(lib, INVALID, dummy, 0, comm);
91 auto big_coarse_nodal_colmap = MapFactory::Build(lib, INVALID, dummy, 0, comm);
92
93 typename Matrix::local_matrix_device_type dummyLocalMatrix;
94 big_Dc_ = MatrixFactory::Build(dummyLocalMatrix, big_coarse_edge_map, big_coarse_nodal_colmap, big_coarse_nodal_map, big_coarse_edge_map);
95
96 } else {
97 auto big_coarse_nodal_map = MapFactory::Build(lib, INVALID, Dc->getDomainMap()->getMyGlobalIndicesDevice(), 0, comm);
98 auto big_coarse_edge_map = MapFactory::Build(lib, INVALID, Dc->getRangeMap()->getMyGlobalIndicesDevice(), 0, comm);
99 auto big_coarse_nodal_colmap = MapFactory::Build(lib, INVALID, Dc->getColMap()->getMyGlobalIndicesDevice(), 0, comm);
100
101 big_Dc_ = MatrixFactory::Build(Dc->getLocalMatrixDevice(), big_coarse_edge_map, big_coarse_nodal_colmap, big_coarse_nodal_map, big_coarse_edge_map);
102 }
103 } else {
104 big_Dc_ = Dc;
105 }
106
107 TEUCHOS_TEST_FOR_EXCEPTION(!D->getRangeMap()->isSameAs(*Ppattern->getRangeMap()),
109 "Maps are incompatible");
110 TEUCHOS_TEST_FOR_EXCEPTION(!big_Dc_->getRangeMap()->isSameAs(*Ppattern->getDomainMap()),
112 "Maps are incompatible");
113
114 // Construct auxiliary graph via Ppattern * Dc
115 RCP<const CrsGraph> auxGraph;
116 {
117 const auto one = Teuchos::ScalarTraits<Scalar>::one();
118 auto absP = MatrixFactory::Build(Ppattern);
119 absP->setAllToScalar(one);
120 absP->fillComplete();
121
122 auto absDc = MatrixFactory::BuildCopy(big_Dc_);
123 absDc->setAllToScalar(one);
124
125 auto P_Dc = MatrixMatrix::Multiply(*absP, false, *absDc, false, this->GetOStream(Statistics2), true, true);
126 auxGraph = P_Dc->getCrsGraph();
127 }
128 RHS_pattern_ = auxGraph;
129
130 GlobalOrdinal indexBase = Ppattern->getRowMap()->getIndexBase();
131 const size_t numUnknowns = Ppattern->getLocalNumEntries();
132 const size_t numRows = Ppattern->getLocalNumRows();
133 Xpetra::global_size_t global_numConstraints = auxGraph->getGlobalNumEntries();
134 Xpetra::global_size_t global_numUnknowns = Ppattern->getGlobalNumEntries();
135 const size_t numConstraints = auxGraph->getLocalNumEntries();
136 auto constraint_rowmap = MapFactory::Build(lib, global_numConstraints, numConstraints, indexBase, comm);
137 auto constraint_domainmap = MapFactory::Build(lib, global_numUnknowns, numUnknowns, indexBase, comm);
138
139 RCP<Matrix> ghostedDc;
140 if (!Ppattern->getImporter().is_null())
141 ghostedDc = MatrixFactory::Build(big_Dc_, *Ppattern->getImporter());
142 else
143 ghostedDc = big_Dc_;
144
145 RCP<Matrix> X;
146 {
147 auto lclPattern = Ppattern->getLocalGraphDevice();
148 auto lclD0 = ghostedDc->getLocalMatrixDevice();
149 auto lclAuxGraph = auxGraph->getLocalGraphDevice();
150
151 // Over-allocate by 1. Makes the logic a bit easier in what follows.
152 lno_view_t rowptr("constraint_rowptr", numConstraints + 2);
153
154 Kokkos::parallel_for(
155 "MueLu::SparseConstraint::sparse_constraint_num_entries_per_row",
156 range_type(0, numRows),
157 KOKKOS_LAMBDA(const size_t pattern_i) {
158 for (size_t pattern_jj = lclPattern.row_map(pattern_i); pattern_jj < lclPattern.row_map(pattern_i + 1); ++pattern_jj) {
159 auto pattern_j = lclPattern.entries(pattern_jj);
160 // entry (pattern_i, pattern_j) in Ppattern
161
162 for (size_t D0_jj = lclD0.graph.row_map(pattern_j); D0_jj < lclD0.graph.row_map(pattern_j + 1); ++D0_jj) {
163 auto D0_j = lclD0.graph.entries(D0_jj);
164 // entry (pattern_j, D0_j) in ghosted D0
165
166 // Find entry (pattern_i, D0_j) in tempGraph
167 size_t constraint_I;
168 for (constraint_I = lclAuxGraph.row_map(pattern_i); constraint_I < lclAuxGraph.row_map(pattern_i + 1); ++constraint_I) {
169 if (lclAuxGraph.entries(constraint_I) == D0_j)
170 break;
171 }
172#ifdef HAVE_MUELU_DEBUG
173 if (lclAuxGraph.entries(constraint_I) != D0_j)
174 ::Kokkos::abort("Did not find entry in row of tempGraph.");
175#endif
176 // Need an entry in row constraint_I.
177 // We offset by 2 since we do not want to compute the final rowptr just yet.
178 // That will happen during fill.
179 Kokkos::atomic_add(&rowptr(constraint_I + 2), 1);
180 }
181 }
182 });
183
184 // The usual prefix sum.
185 size_t nnz = 0;
186 Kokkos::parallel_scan(
187 "MueLu::SparseConstraint::sparse_constraint_prefix_sum",
188 range_type(1, numConstraints + 2),
189 KOKKOS_LAMBDA(const size_t constraint_i, size_t& partial_nnz, bool is_final) {
190 partial_nnz += rowptr(constraint_i);
191 if (is_final)
192 rowptr(constraint_i) = partial_nnz;
193 },
194 nnz);
195
196 // allocate indices and values
197 lno_nnz_view_t colind(Kokkos::ViewAllocateWithoutInitializing("constraint_indices"), nnz);
198 scalar_view_t values(Kokkos::ViewAllocateWithoutInitializing("constraint_values"), nnz);
199
200 // fill indices and values
201 Kokkos::parallel_for(
202 "MueLu::SparseConstraint::sparse_constraint_fill",
203 range_type(0, numRows),
204 KOKKOS_LAMBDA(const size_t pattern_i) {
205 for (size_t pattern_jj = lclPattern.row_map(pattern_i); pattern_jj < lclPattern.row_map(pattern_i + 1); ++pattern_jj) {
206 auto pattern_j = lclPattern.entries(pattern_jj);
207 // entry (pattern_i, pattern_j) in Ppattern
208
209 for (size_t D0_jj = lclD0.graph.row_map(pattern_j); D0_jj < lclD0.graph.row_map(pattern_j + 1); ++D0_jj) {
210 auto D0_j = lclD0.graph.entries(D0_jj);
211 auto D0_val = lclD0.values(D0_jj);
212 // entry (pattern_j, D0_j) in ghosted D0
213
214 // Find entry (pattern_i, D0_j) in tempGraph
215 size_t constraint_I;
216 for (constraint_I = lclAuxGraph.row_map(pattern_i); constraint_I < lclAuxGraph.row_map(pattern_i + 1); ++constraint_I) {
217 if (lclAuxGraph.entries(constraint_I) == D0_j)
218 break;
219 }
220#ifdef HAVE_MUELU_DEBUG
221 if (lclAuxGraph.entries(constraint_I) != D0_j)
222 ::Kokkos::abort("Did not find entry in row of tempGraph.");
223#endif
224 // Enter data into constraint matrix.
225 // (constraint_I, pattern_jj) -> D0_val
226 // After this the rowptr will be correct.
227 // This is why we had to offset the index by 2 earlier on.
228 auto constraint_jj = Kokkos::atomic_fetch_inc(&rowptr(constraint_I + 1));
229 colind(constraint_jj) = pattern_jj;
230 values(constraint_jj) = D0_val;
231 }
232 }
233 });
234
235 auto lclConstraintGraph = graph_t(colind, Kokkos::subview(rowptr, Kokkos::make_pair(size_t(0), numConstraints + 1)));
236 auto lclConstraint = matrix_t("constraint", numUnknowns, values, lclConstraintGraph);
237 X = MatrixFactory::Build(lclConstraint, constraint_rowmap, constraint_domainmap, constraint_domainmap, constraint_rowmap);
238 }
239 this->SetConstraintsMatrix(X);
240}
241
242template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
243typename Xpetra::CrsGraph<LocalOrdinal, GlobalOrdinal, Node>::local_graph_type SparseConstraint<Scalar, LocalOrdinal, GlobalOrdinal, Node>::FindBlocks(RCP<const Xpetra::CrsGraph<LocalOrdinal, GlobalOrdinal, Node>>& /*XXt*/) {
244 using execution_space = typename Node::execution_space;
245 using range_type = Kokkos::RangePolicy<LocalOrdinal, execution_space>;
246
247 auto lclGraph = RHS_pattern_->getLocalGraphDevice();
248
249 LocalOrdinal numEmptyRows;
250 Kokkos::parallel_reduce(
251 "MueLu::SparseConstraint::FindBlocks::CountEmptyRows", range_type(0, lclGraph.numRows()), KOKKOS_LAMBDA(const LocalOrdinal rowId, LocalOrdinal& emptyRows) {
252 if (lclGraph.row_map(rowId + 1) == lclGraph.row_map(rowId))
253 ++emptyRows;
254 },
255 numEmptyRows);
256
257 auto numConstraints = lclGraph.entries.extent(0);
258 using graph_type = typename CrsGraph::local_graph_type;
259 typename graph_type::row_map_type::non_const_type rowptr("blocks_rowptr", lclGraph.numRows() + 1 - numEmptyRows);
260 typename graph_type::entries_type::non_const_type indices("blocks_indices", numConstraints);
261
262 Kokkos::parallel_scan(
263 "MueLu::SparseConstraint::FindBlocks::GenerateBlockRowPtr", range_type(0, lclGraph.numRows()), KOKKOS_LAMBDA(const LocalOrdinal rowId, LocalOrdinal& rowIdNew, const bool is_final) {
264 if (lclGraph.row_map(rowId + 1) != lclGraph.row_map(rowId)) {
265 if (is_final)
266 rowptr(rowIdNew + 1) = lclGraph.row_map(rowId + 1);
267 ++rowIdNew;
268 }
269 });
270
271 Kokkos::parallel_for(
272 "MueLu::SparseConstraint::FindBlocks::FillBlockIndices", range_type(0, numConstraints), KOKKOS_LAMBDA(const LocalOrdinal constraintId) {
273 indices(constraintId) = constraintId;
274 });
275
276 return graph_type(indices, rowptr);
277}
278
279template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
280typename Teuchos::ScalarTraits<Scalar>::magnitudeType
282 const auto one = Teuchos::ScalarTraits<Scalar>::one();
283
284 // P*Dc
285 RCP<Matrix> temp;
286 temp = MatrixMatrix::Multiply(*P, false,
287 *big_Dc_, false,
288 temp,
289 this->GetOStream(Runtime0), true, true);
290 // D*P_nodal
291 RCP<Matrix> temp2;
292 temp2 = MatrixMatrix::Multiply(*D_, false,
293 *P_nodal_, false,
294 temp2,
295 this->GetOStream(Runtime0), true, true);
296
297 // D*P_nodal - P*Dc
298 RCP<Matrix> residual;
299 MatrixMatrix::TwoMatrixAdd(*temp2, false, one,
300 *temp, false, -one,
301 residual,
302 this->GetOStream(Runtime0));
303 residual->fillComplete();
304 return Teuchos::ScalarTraits<MagnitudeType>::squareroot(Teuchos::ScalarTraits<Scalar>::magnitude(Utilities::Frobenius(*residual, *residual)));
305}
306
307template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
309 MultiVector& vecC) const {
310 this->AssignMatrixEntriesToVector(A, RHS_pattern_, vecC);
311}
312
313} // namespace MueLu
314
315#endif // ifndef MUELU_SPARSECONSTRAINT_DEF_HPP
MueLu::DefaultLocalOrdinal LocalOrdinal
MueLu::DefaultGlobalOrdinal GlobalOrdinal
Exception throws to report incompatible objects (like maps).
Timer to be used in non-factories.
void AssignMatrixEntriesToConstraintVector(const Matrix &A, MultiVector &vecC) const
CrsGraph::local_graph_type FindBlocks(RCP< const CrsGraph > &) override
MagnitudeType ResidualNorm(RCP< const Matrix > P) const override
static Scalar Frobenius(const Xpetra::Matrix< Scalar, LocalOrdinal, GlobalOrdinal, Node > &A, const Xpetra::Matrix< Scalar, LocalOrdinal, GlobalOrdinal, Node > &B)
Frobenius inner product of two matrices.
Namespace for MueLu classes and methods.
@ Statistics2
Print even more statistics.
@ Runtime0
One-liner description of what is happening.