MueLu Version of the Day
Loading...
Searching...
No Matches
MueLu_LocalQR.hpp
Go to the documentation of this file.
1#ifndef MUELU_LOCALQR_HPP
2#define MUELU_LOCALQR_HPP
3
4#include <Kokkos_Core.hpp>
5#include <KokkosKernels_ArithTraits.hpp>
6#include "Xpetra_ConfigDefs.hpp"
7
8#include "KokkosBlas1_set.hpp"
9#include "KokkosBatched_ApplyQ_Decl.hpp"
10#include "KokkosBatched_SetIdentity_Decl.hpp"
11#include "KokkosBatched_SetIdentity_Impl.hpp"
12#include "Kokkos_DualView.hpp"
13#include "Kokkos_Pair.hpp"
14#include "KokkosBatched_QR_Decl.hpp"
15
16namespace MueLu::LocalQR {
17
18template <class LocalOrdinal, class View>
20 public:
22 : view_(view) {}
23
24 KOKKOS_INLINE_FUNCTION
25 void operator()(const LocalOrdinal& i, LocalOrdinal& vmax) const {
26 if (vmax < view_(i))
27 vmax = view_(i);
28 }
29
30 KOKKOS_INLINE_FUNCTION
31 void join(LocalOrdinal& dst, const LocalOrdinal& src) const {
32 if (dst < src) {
33 dst = src;
34 }
35 }
36
37 KOKKOS_INLINE_FUNCTION
38 void init(LocalOrdinal& dst) const {
39 dst = 0;
40 }
41
42 private:
43 View view_;
44};
45
46// local QR decomposition
47template <class LOType, class GOType, class SCType, class DeviceType, class NspType, class aggRowsType, class maxAggDofSizeType, class agg2RowMapLOType, class statusType, class rowsType, class rowsAuxType, class colsAuxType, class valsAuxType>
49 private:
50 typedef LOType LO;
51 typedef GOType GO;
52 typedef SCType SC;
53
54 typedef typename DeviceType::execution_space execution_space;
55 typedef typename KokkosKernels::ArithTraits<SC>::val_type impl_SC;
56 typedef KokkosKernels::ArithTraits<impl_SC> impl_ATS;
57 typedef typename impl_ATS::magnitudeType Magnitude;
58
59 public:
60 typedef Kokkos::View<impl_SC**, typename execution_space::scratch_memory_space, Kokkos::MemoryUnmanaged> shared_matrix;
61 typedef Kokkos::View<impl_SC*, typename execution_space::scratch_memory_space, Kokkos::MemoryUnmanaged> shared_vector;
62
63 private:
64 NspType fineNS;
65 NspType coarseNS;
66 aggRowsType aggRows;
67 maxAggDofSizeType maxAggDofSize; //< maximum number of dofs in aggregate (max size of aggregate * numDofsPerNode)
68 agg2RowMapLOType agg2RowMapLO;
69 statusType statusAtomic;
70 rowsType rows;
71 rowsAuxType rowsAux;
72 colsAuxType colsAux;
73 valsAuxType valsAux;
76
77 public:
78 LocalQRDecompFunctor(NspType fineNS_, NspType coarseNS_, aggRowsType aggRows_, maxAggDofSizeType maxAggDofSize_, agg2RowMapLOType agg2RowMapLO_, statusType statusAtomic_, rowsType rows_, rowsAuxType rowsAux_, colsAuxType colsAux_, valsAuxType valsAux_, bool doQRStep_, int scratchLevel_)
79 : fineNS(fineNS_)
80 , coarseNS(coarseNS_)
81 , aggRows(aggRows_)
82 , maxAggDofSize(maxAggDofSize_)
83 , agg2RowMapLO(agg2RowMapLO_)
84 , statusAtomic(statusAtomic_)
85 , rows(rows_)
86 , rowsAux(rowsAux_)
87 , colsAux(colsAux_)
88 , valsAux(valsAux_)
89 , doQRStep(doQRStep_)
90 , scratchLevel(scratchLevel_) {}
91
92 KOKKOS_INLINE_FUNCTION
93 void operator()(const typename Kokkos::TeamPolicy<execution_space>::member_type& thread, size_t& nnz) const {
94 auto agg = thread.league_rank();
95
96 const auto aggOffset = aggRows(agg);
97 // size of aggregate: number of DOFs in aggregate
98 const auto aggSize = aggRows(agg + 1) - aggOffset;
99
100 const impl_SC one = impl_ATS::one();
101 const impl_SC zero = impl_ATS::zero();
102
103 const int m = aggSize;
104 const int n = fineNS.extent(1);
105
106 // calculate row offset for coarse nullspace
107 Xpetra::global_size_t offset = agg * n;
108
109 if (doQRStep) {
110 // A is m x n
111 // Q is m x m
112 // R is m x n
113
114 // A (initially) gets overwritten with R in QR
115 shared_matrix r(thread.team_scratch(scratchLevel), m, n);
116 // Q
117 shared_matrix q(thread.team_scratch(scratchLevel), m, m);
118
119 // Extract the piece of the nullspace corresponding to the aggregate
120 for (int j = 0; j < n; j++)
121 for (int k = 0; k < m; k++)
122 r(k, j) = fineNS(agg2RowMapLO(aggOffset + k), j);
123
124 if (m >= n) {
125 // tau has size n
126 shared_vector tau(thread.team_scratch(scratchLevel), n);
127
128 // work has size m
129 shared_vector work(thread.team_scratch(scratchLevel), m);
130
131 // Calculate QR. After this call R will be in the upper triangular part of r.
132 // Q is in encoded in the strictly lower triangular part of r and tau. We will
133 // need to explicitly form Q as a matrix below.
134 KokkosBatched::SerialQR<KokkosBlas::Algo::QR::Unblocked>::invoke(r, tau, work);
135
136 // Initialize Q to an identity matrix
137 KokkosBatched::SerialSetIdentity::invoke(q);
138
139 // Form Q as a matrix but multiplying against the identity
140 KokkosBatched::SerialApplyQ<KokkosBatched::Side::Left, KokkosBlas::Trans::NoTranspose, KokkosBlas::Algo::ApplyQ::Unblocked>::invoke(r, tau, q, work);
141
142 // Build coarse nullspace using the upper triangular part of R
143 for (int j = 0; j < n; j++) {
144 for (int k = 0; k < n; k++)
145 coarseNS(offset + k, j) = (k <= j) ? r(k, j) : zero;
146 }
147
148 // Process each row in the local Q factor and fill helper arrays to assemble P
149 for (int j = 0; j < m; j++) {
150 LO localRow = agg2RowMapLO(aggRows(agg) + j);
151 size_t rowStart = rowsAux(localRow);
152 size_t lnnz = 0;
153 for (int k = 0; k < n; k++) {
154 // skip zeros
155 if (q(j, k) != zero) {
156 colsAux(rowStart + lnnz) = offset + k;
157 valsAux(rowStart + lnnz) = q(j, k);
158 lnnz++;
159 }
160 }
161 rows(localRow + 1) = lnnz;
162 nnz += lnnz;
163 }
164
165 } else {
166 // Special handling for m < n (i.e. single node aggregates in structural mechanics)
167
168 // The local QR decomposition is not possible in the "overconstrained"
169 // case (i.e. number of columns in qr > number of rowsAux), which
170 // corresponds to #DOFs in Aggregate < n. For usual problems this
171 // is only possible for single node aggregates in structural mechanics.
172 // (Similar problems may arise in discontinuous Galerkin problems...)
173 // We bypass the QR decomposition and use an identity block in the
174 // tentative prolongator for the single node aggregate and transfer the
175 // corresponding fine level null space information 1-to-1 to the coarse
176 // level null space part.
177
178 // NOTE: The resulting tentative prolongation operator has
179 // (m*DofsPerNode-n) zero columns leading to a singular
180 // coarse level operator A. To deal with that one has the following
181 // options:
182 // - Use the "RepairMainDiagonal" flag in the RAPFactory (default:
183 // false) to add some identity block to the diagonal of the zero rowsAux
184 // in the coarse level operator A, such that standard level smoothers
185 // can be used again.
186 // - Use special (projection-based) level smoothers, which can deal
187 // with singular matrices (very application specific)
188 // - Adapt the code below to avoid zero columns. However, we do not
189 // support a variable number of DOFs per node in MueLu/Xpetra which
190 // makes the implementation really hard.
191 //
192 // FIXME: do we need to check for singularity here somehow? Zero
193 // columns would be easy but linear dependency would require proper QR.
194
195 // R = extended (by adding identity rowsAux) qr
196 for (int j = 0; j < n; j++)
197 for (int k = 0; k < n; k++)
198 if (k < m)
199 coarseNS(offset + k, j) = r(k, j);
200 else
201 coarseNS(offset + k, j) = (k == j ? one : zero);
202
203 // Q = I (rectangular)
204 // no need to actually fill q
205
206 // Process each row in the local Q factor and fill helper arrays to assemble P
207 for (int j = 0; j < m; j++) {
208 LO localRow = agg2RowMapLO(aggRows(agg) + j);
209 size_t rowStart = rowsAux(localRow);
210 colsAux(rowStart) = offset + j;
211 valsAux(rowStart) = one;
212 rows(localRow + 1) = 1;
213 nnz += 1;
214 }
215 }
216 } else {
218 // "no-QR" option //
220 // Local Q factor is just the fine nullspace support over the current aggregate.
221 // Local R factor is the identity.
222 // TODO I have not implemented any special handling for aggregates that are too
223 // TODO small to locally support the nullspace, as is done in the standard QR
224 // TODO case above.
225
226 for (int j = 0; j < m; j++) {
227 LO localRow = agg2RowMapLO(aggRows(agg) + j);
228 size_t rowStart = rowsAux(localRow);
229 size_t lnnz = 0;
230 for (int k = 0; k < n; k++) {
231 const impl_SC qr_jk = fineNS(localRow, k);
232 // skip zeros
233 if (qr_jk != zero) {
234 colsAux(rowStart + lnnz) = offset + k;
235 valsAux(rowStart + lnnz) = qr_jk;
236 lnnz++;
237 }
238 }
239 rows(localRow + 1) = lnnz;
240 nnz += lnnz;
241 }
242
243 for (int j = 0; j < n; j++)
244 coarseNS(offset + j, j) = one;
245 }
246 }
247};
248
249} // namespace MueLu::LocalQR
250
251#endif
MueLu::DefaultLocalOrdinal LocalOrdinal
Kokkos::View< impl_SC **, typename execution_space::scratch_memory_space, Kokkos::MemoryUnmanaged > shared_matrix
LocalQRDecompFunctor(NspType fineNS_, NspType coarseNS_, aggRowsType aggRows_, maxAggDofSizeType maxAggDofSize_, agg2RowMapLOType agg2RowMapLO_, statusType statusAtomic_, rowsType rows_, rowsAuxType rowsAux_, colsAuxType colsAux_, valsAuxType valsAux_, bool doQRStep_, int scratchLevel_)
KokkosKernels::ArithTraits< impl_SC > impl_ATS
DeviceType::execution_space execution_space
Kokkos::View< impl_SC *, typename execution_space::scratch_memory_space, Kokkos::MemoryUnmanaged > shared_vector
KokkosKernels::ArithTraits< SC >::val_type impl_SC
KOKKOS_INLINE_FUNCTION void operator()(const typename Kokkos::TeamPolicy< execution_space >::member_type &thread, size_t &nnz) const
KOKKOS_INLINE_FUNCTION void join(LocalOrdinal &dst, const LocalOrdinal &src) const
KOKKOS_INLINE_FUNCTION void init(LocalOrdinal &dst) const
KOKKOS_INLINE_FUNCTION void operator()(const LocalOrdinal &i, LocalOrdinal &vmax) const