MueLu Version of the Day
Loading...
Searching...
No Matches
MueLu_SaPFactory_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_SAPFACTORY_DEF_HPP
11#define MUELU_SAPFACTORY_DEF_HPP
12
13#include "KokkosKernels_ArithTraits.hpp"
15
16#include <Xpetra_Matrix.hpp>
17
19#include "MueLu_Level.hpp"
20#include "MueLu_MasterList.hpp"
21#include "MueLu_Monitor.hpp"
22#include "MueLu_PerfUtils.hpp"
23#include "MueLu_TentativePFactory.hpp"
24#include "MueLu_IteratorOps.hpp"
25#include "MueLu_Utilities.hpp"
26
27#include <sstream>
28
29namespace MueLu {
30
31template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
33
34template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
36
37template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
39 RCP<ParameterList> validParamList = rcp(new ParameterList());
40
41#define SET_VALID_ENTRY(name) validParamList->setEntry(name, MasterList::getEntry(name))
42 SET_VALID_ENTRY("sa: damping factor");
43 SET_VALID_ENTRY("sa: nodal damping factor");
44 SET_VALID_ENTRY("sa: calculate eigenvalue estimate");
45 SET_VALID_ENTRY("sa: eigenvalue estimate num iterations");
46 SET_VALID_ENTRY("sa: use rowsumabs diagonal scaling");
47 SET_VALID_ENTRY("sa: enforce constraints");
48 SET_VALID_ENTRY("tentative: calculate qr");
49 SET_VALID_ENTRY("sa: max eigenvalue");
50 SET_VALID_ENTRY("sa: diagonal replacement tolerance");
51 SET_VALID_ENTRY("sa: rowsumabs diagonal replacement tolerance");
52 SET_VALID_ENTRY("sa: rowsumabs diagonal replacement value");
53 SET_VALID_ENTRY("sa: rowsumabs replace single entry row with zero");
54 SET_VALID_ENTRY("sa: rowsumabs use automatic diagonal tolerance");
55 SET_VALID_ENTRY("use kokkos refactor");
56#undef SET_VALID_ENTRY
57 validParamList->set("sa: maxwell1 smoothing", false);
58
59 validParamList->set<RCP<const FactoryBase>>("A", Teuchos::null, "Generating factory of the matrix A used during the prolongator smoothing process");
60 validParamList->set<RCP<const FactoryBase>>("P", Teuchos::null, "Tentative prolongator factory");
61
62 validParamList->set<RCP<const FactoryBase>>("CurlCurl", Teuchos::null, "Generating factory of the matrix CurlCurl used during the prolongator smoothing process for Maxwell1");
63
64 // Make sure we don't recursively validate options for the matrixmatrix kernels
65 ParameterList norecurse;
66 norecurse.disableRecursiveValidation();
67 validParamList->set<ParameterList>("matrixmatrix: kernel params", norecurse, "MatrixMatrix kernel parameters");
68
69 return validParamList;
70}
71
72template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
74 Input(fineLevel, "A");
75
76 // Get default tentative prolongator factory
77 // Getting it that way ensure that the same factory instance will be used for both SaPFactory and NullspaceFactory.
78 RCP<const FactoryBase> initialPFact = GetFactory("P");
79 if (initialPFact == Teuchos::null) {
80 initialPFact = coarseLevel.GetFactoryManager()->GetFactory("Ptent");
81 }
82 coarseLevel.DeclareInput("P", initialPFact.get(), this);
83
84 const ParameterList& pL = GetParameterList();
85 if (pL.get<bool>("sa: maxwell1 smoothing") && pL.get<double>("sa: damping factor") != 0.0) {
86 auto curlCurlFact = GetFactory("CurlCurl");
87 if (!curlCurlFact.is_null())
88 fineLevel.DeclareInput("CurlCurl", GetFactory("CurlCurl").get(), this);
89 }
90}
91
92template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
94 return BuildP(fineLevel, coarseLevel);
95}
96
97template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
99 FactoryMonitor m(*this, "Prolongator smoothing", coarseLevel);
100
101 std::string levelIDs = toString(coarseLevel.GetLevelID());
102
103 const std::string prefix = "MueLu::SaPFactory(" + levelIDs + "): ";
104
105 typedef typename Teuchos::ScalarTraits<SC>::coordinateType Coordinate;
106 typedef typename Teuchos::ScalarTraits<SC>::magnitudeType MT;
107
108 // Get default tentative prolongator factory
109 // Getting it that way ensure that the same factory instance will be used for both SaPFactory and NullspaceFactory.
110 // -- Warning: Do not use directly initialPFact_. Use initialPFact instead everywhere!
111 RCP<const FactoryBase> initialPFact = GetFactory("P");
112 if (initialPFact == Teuchos::null) {
113 initialPFact = coarseLevel.GetFactoryManager()->GetFactory("Ptent");
114 }
115 const ParameterList& pL = GetParameterList();
116
117 // Level Get
118 RCP<Matrix> A = Get<RCP<Matrix>>(fineLevel, "A");
119 RCP<Matrix> Ptent = coarseLevel.Get<RCP<Matrix>>("P", initialPFact.get());
120 RCP<Matrix> finalP;
121 // If Tentative facctory bailed out (e.g., number of global aggregates is 0), then SaPFactory bails
122 // This level will ultimately be removed in MueLu_Hierarchy_defs.h via a resize()
123 if (Ptent == Teuchos::null) {
124 finalP = Teuchos::null;
125 Set(coarseLevel, "P", finalP);
126 return;
127 }
128
129 if (restrictionMode_) {
130 SubFactoryMonitor m2(*this, "Transpose A", coarseLevel);
131 A = Utilities::Transpose(*A, true); // build transpose of A explicitly
132 }
133
134 // Build final prolongator
135
136 // Reuse pattern if available
137 RCP<ParameterList> APparams;
138 if (pL.isSublist("matrixmatrix: kernel params"))
139 APparams = rcp(new ParameterList(pL.sublist("matrixmatrix: kernel params")));
140 else
141 APparams = rcp(new ParameterList);
142 if (coarseLevel.IsAvailable("AP reuse data", this)) {
143 GetOStream(static_cast<MsgType>(Runtime0 | Test)) << "Reusing previous AP data" << std::endl;
144
145 APparams = coarseLevel.Get<RCP<ParameterList>>("AP reuse data", this);
146
147 if (APparams->isParameter("graph"))
148 finalP = APparams->get<RCP<Matrix>>("graph");
149 }
150 // By default, we don't need global constants for SaP
151 APparams->set("compute global constants: temporaries", APparams->get("compute global constants: temporaries", false));
152 APparams->set("compute global constants", APparams->get("compute global constants", false));
153
154 const SC dampingFactor = as<SC>(pL.get<double>("sa: damping factor"));
155 const LO maxEigenIterations = as<LO>(pL.get<int>("sa: eigenvalue estimate num iterations"));
156 const bool estimateMaxEigen = pL.get<bool>("sa: calculate eigenvalue estimate");
157 const bool useAbsValueRowSum = pL.get<bool>("sa: use rowsumabs diagonal scaling");
158 const bool doQRStep = pL.get<bool>("tentative: calculate qr");
159 const bool enforceConstraints = pL.get<bool>("sa: enforce constraints");
160 const MT userDefinedMaxEigen = as<MT>(pL.get<double>("sa: max eigenvalue"));
161 double dTol = pL.get<double>("sa: diagonal replacement tolerance");
162 double dTol_rs = pL.get<double>("sa: rowsumabs diagonal replacement tolerance");
163 const MT diagonalReplacementTolerance = (dTol == as<double>(-1) ? Teuchos::ScalarTraits<MT>::eps() * 100 : as<MT>(pL.get<double>("sa: diagonal replacement tolerance")));
164 const MT diagonalReplacementTolerance_rs = (dTol_rs == as<double>(-1) ? Teuchos::ScalarTraits<MT>::eps() * 100 : as<MT>(pL.get<double>("sa: rowsumabs diagonal replacement tolerance")));
165
166 const SC diagonalReplacementValue = as<SC>(pL.get<double>("sa: rowsumabs diagonal replacement value"));
167 const bool replaceSingleEntryRowWithZero = pL.get<bool>("sa: rowsumabs replace single entry row with zero");
168 const bool useAutomaticDiagTol = pL.get<bool>("sa: rowsumabs use automatic diagonal tolerance");
169
170 // Sanity checking
171 TEUCHOS_TEST_FOR_EXCEPTION(doQRStep && enforceConstraints, Exceptions::RuntimeError,
172 "MueLu::TentativePFactory::MakeTentative: cannot use 'enforce constraints' and 'calculate qr' at the same time");
173
174 // Are we using the special prolongator smoothing for smoothed Reitzinger-Schoeberl?
175 const bool specialMaxwell1Smoothing = pL.get<bool>("sa: maxwell1 smoothing");
176
177 if (!specialMaxwell1Smoothing) {
178 if (dampingFactor != Teuchos::ScalarTraits<SC>::zero()) {
179 Scalar lambdaMax;
180 Teuchos::RCP<Vector> invDiag;
181 if (userDefinedMaxEigen == -1.) {
182 SubFactoryMonitor m2(*this, "Eigenvalue estimate", coarseLevel);
183 lambdaMax = A->GetMaxEigenvalueEstimate();
184 if (lambdaMax == -Teuchos::ScalarTraits<SC>::one() || estimateMaxEigen) {
185 GetOStream(Statistics1) << "Calculating max eigenvalue estimate now (max iters = " << maxEigenIterations << ((useAbsValueRowSum) ? ", use rowSumAbs diagonal)" : ", use point diagonal)") << std::endl;
186 Coordinate stopTol = 1e-4;
187 if (useAbsValueRowSum) {
188 const bool returnReciprocal = true;
189 invDiag = Utilities::GetLumpedMatrixDiagonal(*A, returnReciprocal,
190 diagonalReplacementTolerance_rs,
191 diagonalReplacementValue,
192 replaceSingleEntryRowWithZero,
193 useAutomaticDiagTol);
194 TEUCHOS_TEST_FOR_EXCEPTION(invDiag.is_null(), Exceptions::RuntimeError,
195 "SaPFactory: eigenvalue estimate: diagonal reciprocal is null.");
196 lambdaMax = Utilities::PowerMethod(*A, invDiag, maxEigenIterations, stopTol);
197 } else
198 lambdaMax = Utilities::PowerMethod(*A, true, maxEigenIterations, stopTol, diagonalReplacementTolerance);
199 A->SetMaxEigenvalueEstimate(lambdaMax);
200 } else {
201 GetOStream(Statistics1) << "Using cached max eigenvalue estimate" << std::endl;
202 }
203 } else {
204 lambdaMax = userDefinedMaxEigen;
205 A->SetMaxEigenvalueEstimate(lambdaMax);
206 }
207 MT omega = Teuchos::ScalarTraits<SC>::magnitude(dampingFactor) / Teuchos::ScalarTraits<SC>::magnitude(lambdaMax);
208 GetOStream(Statistics1) << "Prolongator damping factor = " << omega << " (|" << dampingFactor << " / " << lambdaMax << "|)" << std::endl;
209
210 {
211 SubFactoryMonitor m2(*this, "Fused (I-omega*D^{-1} A)*Ptent", coarseLevel);
212 if (!useAbsValueRowSum)
213 invDiag = Utilities::GetMatrixDiagonalInverse(*A); // default
214 else if (invDiag == Teuchos::null) {
215 GetOStream(Runtime0) << "Using rowsumabs diagonal" << std::endl;
216 const bool returnReciprocal = true;
217 invDiag = Utilities::GetLumpedMatrixDiagonal(*A, returnReciprocal,
218 diagonalReplacementTolerance_rs,
219 diagonalReplacementValue,
220 replaceSingleEntryRowWithZero,
221 useAutomaticDiagTol);
222 TEUCHOS_TEST_FOR_EXCEPTION(invDiag.is_null(), Exceptions::RuntimeError, "SaPFactory: diagonal reciprocal is null.");
223 }
224
225 TEUCHOS_TEST_FOR_EXCEPTION(!std::isfinite(omega), Exceptions::RuntimeError, "Prolongator damping factor needs to be finite.");
226
227 {
228 SubFactoryMonitor m3(*this, "MueLu::IteratorOps::Jacobi", coarseLevel);
229 // finalP = (I - \omega D^{-1}A) Ptent
230 finalP = MueLu::IteratorOps<SC, LO, GO, NO>::Jacobi(omega, *invDiag, *A, *Ptent, finalP, GetOStream(Statistics2), std::string("MueLu::SaP-") + toString(coarseLevel.GetLevelID()), APparams);
231 if (enforceConstraints) {
232 if (!pL.get<bool>("use kokkos refactor")) {
233 if (A->GetFixedBlockSize() == 1)
235 else
237 } else {
238 // if (A->GetFixedBlockSize() == 1)
239 // optimalSatisfyPConstraintsForScalarPDEs(finalP);
240 // else
241 SatisfyPConstraints(A, finalP);
242 }
243 }
244 }
245 }
246
247 } else {
248 finalP = Ptent;
249 }
250 } else {
251 // We are on the (1,1) hierarchy for Maxwell1.
252 // Instead of smoothing with I - omega * Dinv(A) * A, we are smoothing with
253 // I - beta * D0 * Dinv(NodeMatrix) * D0^T * A
254 // I - alpha * Dinv(CurlCurl) * CurlCurl, and
255 // where beta matches the damping that was used for the nodal operator.
256
257 const SC nodalDampingFactor = as<SC>(pL.get<double>("sa: nodal damping factor"));
258
259 const bool nodalAndEdgeSmoothing = ((nodalDampingFactor != Teuchos::ScalarTraits<SC>::zero()) && (fineLevel.Get<RCP<Matrix>>("NodeMatrix")->GetMaxEigenvalueEstimate() != -Teuchos::ScalarTraits<SC>::one()));
260 const bool edgeSmoothing = (dampingFactor != Teuchos::ScalarTraits<SC>::zero());
261
262 RCP<Matrix> P_afterNodalAndEdgeSmoothing;
263 if (nodalAndEdgeSmoothing) {
264 auto D0 = fineLevel.Get<RCP<Matrix>>("D0");
265 auto NodeMatrix = fineLevel.Get<RCP<Matrix>>("NodeMatrix");
266
267 auto lambdaMaxNodal = NodeMatrix->GetMaxEigenvalueEstimate();
268 MT beta = Teuchos::ScalarTraits<SC>::magnitude(nodalDampingFactor) / Teuchos::ScalarTraits<SC>::magnitude(lambdaMaxNodal);
269 GetOStream(Statistics1) << "Maxwell1 prolongator nodal damping factor = " << beta << " (|" << nodalDampingFactor << " / " << lambdaMaxNodal << "|)" << std::endl;
270 TEUCHOS_TEST_FOR_EXCEPTION(lambdaMaxNodal == -Teuchos::ScalarTraits<SC>::one(), Exceptions::RuntimeError, "Prolongator damping factor obtained from NodeMatrix is -1.")
271
272 TEUCHOS_TEST_FOR_EXCEPTION(!std::isfinite(beta), Exceptions::RuntimeError, "Prolongator damping factor needs to be finite.");
273
274 {
275 SubFactoryMonitor m3(*this, "MueLu::IteratorOps::JacobiMaxwell1", coarseLevel);
276 P_afterNodalAndEdgeSmoothing = JacobiMaxwell1(A, NodeMatrix, D0, Ptent, beta);
277 }
278 } else {
279 if (nodalDampingFactor == Teuchos::ScalarTraits<SC>::zero())
280 GetOStream(Statistics1) << "Skipping nodal&edge smoothing step since nodal damping factor is zero\n";
281 else if (fineLevel.Get<RCP<Matrix>>("NodeMatrix")->GetMaxEigenvalueEstimate() == -Teuchos::ScalarTraits<SC>::one())
282 GetOStream(Warnings) << "Skipping nodal&edge smoothing step since NodeMatrix matrix has invalid eigenvalue estimate\n";
283 P_afterNodalAndEdgeSmoothing = Ptent;
284 }
285
286 if (edgeSmoothing) {
287 RCP<Matrix> CurlCurl;
288 if (!GetFactory("CurlCurl").is_null())
289 CurlCurl = fineLevel.Get<RCP<Matrix>>("CurlCurl", GetFactory("CurlCurl").get());
290 else {
291 GetOStream(Warnings0) << "Using edge matrix for edge-only prolongator smoothing. This generally violates the commuting relationship." << std::endl;
292 CurlCurl = A;
293 }
294
295 Scalar lambdaMax;
296 Teuchos::RCP<Vector> invDiag;
297 if (userDefinedMaxEigen == -1.) {
298 SubFactoryMonitor m2(*this, "Eigenvalue estimate", coarseLevel);
299 lambdaMax = CurlCurl->GetMaxEigenvalueEstimate();
300 if (lambdaMax == -Teuchos::ScalarTraits<SC>::one() || estimateMaxEigen) {
301 GetOStream(Statistics1) << "Calculating max eigenvalue estimate now (max iters = " << maxEigenIterations << ((useAbsValueRowSum) ? ", use rowSumAbs diagonal)" : ", use point diagonal)") << std::endl;
302 Coordinate stopTol = 1e-4;
303 if (useAbsValueRowSum) {
304 const bool returnReciprocal = true;
305 invDiag = Utilities::GetLumpedMatrixDiagonal(*CurlCurl, returnReciprocal,
306 diagonalReplacementTolerance_rs,
307 diagonalReplacementValue,
308 replaceSingleEntryRowWithZero,
309 useAutomaticDiagTol);
310 TEUCHOS_TEST_FOR_EXCEPTION(invDiag.is_null(), Exceptions::RuntimeError,
311 "SaPFactory: eigenvalue estimate: diagonal reciprocal is null.");
312 lambdaMax = Utilities::PowerMethod(*CurlCurl, invDiag, maxEigenIterations, stopTol);
313 } else
314 lambdaMax = Utilities::PowerMethod(*CurlCurl, true, maxEigenIterations, stopTol, diagonalReplacementTolerance);
315 CurlCurl->SetMaxEigenvalueEstimate(lambdaMax);
316 } else {
317 GetOStream(Statistics1) << "Using cached max eigenvalue estimate" << std::endl;
318 }
319 } else {
320 lambdaMax = userDefinedMaxEigen;
321 CurlCurl->SetMaxEigenvalueEstimate(lambdaMax);
322 }
323
324 if (!useAbsValueRowSum)
325 invDiag = Utilities::GetMatrixDiagonalInverse(*CurlCurl); // default
326 else if (invDiag == Teuchos::null) {
327 GetOStream(Runtime0) << "Using rowsumabs diagonal" << std::endl;
328 const bool returnReciprocal = true;
329 invDiag = Utilities::GetLumpedMatrixDiagonal(*CurlCurl, returnReciprocal,
330 diagonalReplacementTolerance_rs,
331 diagonalReplacementValue,
332 replaceSingleEntryRowWithZero,
333 useAutomaticDiagTol);
334 TEUCHOS_TEST_FOR_EXCEPTION(invDiag.is_null(), Exceptions::RuntimeError, "SaPFactory: diagonal reciprocal is null.");
335 }
336
337 MT alpha = Teuchos::ScalarTraits<SC>::magnitude(dampingFactor) / Teuchos::ScalarTraits<SC>::magnitude(lambdaMax);
338 TEUCHOS_TEST_FOR_EXCEPTION(!std::isfinite(alpha), Exceptions::RuntimeError, "Prolongator damping factor needs to be finite.");
339
340 GetOStream(Statistics1) << "Maxwell1 prolongator edge damping factor = " << alpha << " (|" << dampingFactor << " / " << lambdaMax << "|)" << std::endl;
341
342 {
343 SubFactoryMonitor m3(*this, "MueLu::IteratorOps::Jacobi", coarseLevel);
344 // finalP = (I - \omega D^{-1}CurlCurl) P_afterNodalAndEdgeSmoothing
345 finalP = MueLu::IteratorOps<SC, LO, GO, NO>::Jacobi(alpha, *invDiag, *CurlCurl, *P_afterNodalAndEdgeSmoothing, finalP, GetOStream(Statistics2), std::string("MueLu::SaP-") + toString(coarseLevel.GetLevelID()), APparams);
346 }
347 } else {
348 GetOStream(Statistics1) << "Maxwell1 prolongator edge smoothing skipped" << std::endl;
349 finalP = P_afterNodalAndEdgeSmoothing;
350 }
351 }
352
353 // Level Set
354 RCP<Matrix> R;
355 if (!restrictionMode_) {
356 // prolongation factory is in prolongation mode
357 if (!finalP.is_null()) {
358 std::ostringstream oss;
359 oss << "P_" << coarseLevel.GetLevelID();
360 finalP->setObjectLabel(oss.str());
361 }
362 Set(coarseLevel, "P", finalP);
363
364 APparams->set("graph", finalP);
365 Set(coarseLevel, "AP reuse data", APparams);
366
367 // NOTE: EXPERIMENTAL
368 if (Ptent->IsView("stridedMaps"))
369 finalP->CreateView("stridedMaps", Ptent);
370
371 } else {
372 // prolongation factory is in restriction mode
373 {
374 SubFactoryMonitor m2(*this, "Transpose P", coarseLevel);
375 R = Utilities::Transpose(*finalP, true);
376 if (!R.is_null()) {
377 std::ostringstream oss;
378 oss << "R_" << coarseLevel.GetLevelID();
379 R->setObjectLabel(oss.str());
380 }
381 }
382
383 Set(coarseLevel, "R", R);
384
385 // NOTE: EXPERIMENTAL
386 if (Ptent->IsView("stridedMaps"))
387 R->CreateView("stridedMaps", Ptent, true /*transposeA*/);
388 }
389
390 if (IsPrint(Statistics2)) {
391 RCP<ParameterList> params = rcp(new ParameterList());
392 params->set("printLoadBalancingInfo", true);
393 params->set("printCommInfo", true);
394 GetOStream(Statistics2) << PerfUtils::PrintMatrixInfo((!restrictionMode_ ? *finalP : *R), (!restrictionMode_ ? "P" : "R"), params);
395 }
396
397} // Build()
398
399// Analyze the grid transfer produced by smoothed aggregation and make
400// modifications if it does not look right. In particular, if there are
401// negative entries or entries larger than 1, modify P's rows.
402//
403// Note: this kind of evaluation probably only makes sense if not doing QR
404// when constructing tentative P.
405//
406// For entries that do not satisfy the two constraints (>= 0 or <=1) we set
407// these entries to the constraint value and modify the rest of the row
408// so that the row sum remains the same as before by adding an equal
409// amount to each remaining entry. However, if the original row sum value
410// violates the constraints, we set the row sum back to 1 (the row sum of
411// tentative P). After doing the modification to a row, we need to check
412// again the entire row to make sure that the modified row does not violate
413// the constraints.
414
415template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
417 const Scalar zero = Teuchos::ScalarTraits<Scalar>::zero();
418 const Scalar one = Teuchos::ScalarTraits<Scalar>::one();
419 LO nPDEs = A->GetFixedBlockSize();
420 Teuchos::ArrayRCP<Scalar> ConstraintViolationSum(nPDEs);
421 Teuchos::ArrayRCP<Scalar> Rsum(nPDEs);
422 Teuchos::ArrayRCP<size_t> nPositive(nPDEs);
423 for (size_t k = 0; k < (size_t)nPDEs; k++) ConstraintViolationSum[k] = zero;
424 for (size_t k = 0; k < (size_t)nPDEs; k++) Rsum[k] = zero;
425 for (size_t k = 0; k < (size_t)nPDEs; k++) nPositive[k] = 0;
426
427 for (size_t i = 0; i < as<size_t>(P->getRowMap()->getLocalNumElements()); i++) {
428 Teuchos::ArrayView<const LocalOrdinal> indices;
429 Teuchos::ArrayView<const Scalar> vals1;
430 Teuchos::ArrayView<Scalar> vals;
431 P->getLocalRowView((LO)i, indices, vals1);
432 size_t nnz = indices.size();
433 if (nnz == 0) continue;
434
435 vals = ArrayView<Scalar>(const_cast<SC*>(vals1.getRawPtr()), nnz);
436
437 bool checkRow = true;
438
439 while (checkRow) {
440 // check constraints and compute the row sum
441
442 for (LO j = 0; j < indices.size(); j++) {
443 Rsum[j % nPDEs] += vals[j];
444 if (Teuchos::ScalarTraits<SC>::real(vals[j]) < Teuchos::ScalarTraits<SC>::real(zero)) {
445 ConstraintViolationSum[j % nPDEs] += vals[j];
446 vals[j] = zero;
447 } else {
448 if (Teuchos::ScalarTraits<SC>::real(vals[j]) != Teuchos::ScalarTraits<SC>::real(zero))
449 (nPositive[j % nPDEs])++;
450
451 if (Teuchos::ScalarTraits<SC>::real(vals[j]) > Teuchos::ScalarTraits<SC>::real(1.00001)) {
452 ConstraintViolationSum[j % nPDEs] += (vals[j] - one);
453 vals[j] = one;
454 }
455 }
456 }
457
458 checkRow = false;
459
460 // take into account any row sum that violates the contraints
461
462 for (size_t k = 0; k < (size_t)nPDEs; k++) {
463 if (Teuchos::ScalarTraits<SC>::real(Rsum[k]) < Teuchos::ScalarTraits<SC>::magnitude(zero)) {
464 ConstraintViolationSum[k] += (-Rsum[k]); // rstumin
465 } else if (Teuchos::ScalarTraits<SC>::real(Rsum[k]) > Teuchos::ScalarTraits<SC>::magnitude(1.00001)) {
466 ConstraintViolationSum[k] += (one - Rsum[k]); // rstumin
467 }
468 }
469
470 // check if row need modification
471 for (size_t k = 0; k < (size_t)nPDEs; k++) {
472 if (Teuchos::ScalarTraits<SC>::magnitude(ConstraintViolationSum[k]) != Teuchos::ScalarTraits<SC>::magnitude(zero))
473 checkRow = true;
474 }
475 // modify row
476 if (checkRow) {
477 for (LO j = 0; j < indices.size(); j++) {
478 if (Teuchos::ScalarTraits<SC>::real(vals[j]) > Teuchos::ScalarTraits<SC>::real(zero)) {
479 vals[j] += (ConstraintViolationSum[j % nPDEs] / (as<Scalar>(nPositive[j % nPDEs])));
480 }
481 }
482 for (size_t k = 0; k < (size_t)nPDEs; k++) ConstraintViolationSum[k] = zero;
483 }
484 for (size_t k = 0; k < (size_t)nPDEs; k++) Rsum[k] = zero;
485 for (size_t k = 0; k < (size_t)nPDEs; k++) nPositive[k] = 0;
486 } // while (checkRow) ...
487 } // for (size_t i = 0; i < as<size_t>(P->getRowMap()->getNumNodeElements()); i++) ...
488} // SatsifyPConstraints()
489
490template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
492 const Scalar zero = Teuchos::ScalarTraits<Scalar>::zero();
493 const Scalar one = Teuchos::ScalarTraits<Scalar>::one();
494
495 LocalOrdinal maxEntriesPerRow = 100; // increased later if needed
496 Teuchos::ArrayRCP<Scalar> scalarData(3 * maxEntriesPerRow);
497 bool hasFeasible;
498
499 for (size_t i = 0; i < as<size_t>(P->getRowMap()->getLocalNumElements()); i++) {
500 Teuchos::ArrayView<const LocalOrdinal> indices;
501 Teuchos::ArrayView<const Scalar> vals1;
502 Teuchos::ArrayView<Scalar> vals;
503 P->getLocalRowView((LocalOrdinal)i, indices, vals1);
504 size_t nnz = indices.size();
505 if (nnz != 0) {
506 vals = ArrayView<Scalar>(const_cast<SC*>(vals1.getRawPtr()), nnz);
507 Scalar rsumTarget = zero;
508 for (size_t j = 0; j < nnz; j++) rsumTarget += vals[j];
509
510 if (nnz > as<size_t>(maxEntriesPerRow)) {
511 maxEntriesPerRow = nnz * 3;
512 scalarData.resize(3 * maxEntriesPerRow);
513 }
514 hasFeasible = constrainRow(vals.getRawPtr(), as<LocalOrdinal>(nnz), zero, one, rsumTarget, vals.getRawPtr(), scalarData.getRawPtr());
515
516 if (!hasFeasible) { // just set all entries to the same value giving a row sum of 1
517 for (size_t j = 0; j < nnz; j++) vals[j] = one / as<Scalar>(nnz);
518 }
519 }
520
521 } // for (size_t i = 0; i < as<size_t>(P->getRowMap()->getNumNodeElements()); i++) ...
522} // SatsifyPConstraints()
523
524template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
525bool SaPFactory<Scalar, LocalOrdinal, GlobalOrdinal, Node>::constrainRow(Scalar* orig, LocalOrdinal nEntries, Scalar leftBound, Scalar rghtBound, Scalar rsumTarget, Scalar* fixedUnsorted, Scalar* scalarData) const {
526 /*
527 Input
528 orig data that should be adjusted to satisfy bound constraints and
529 row sum constraint. orig is not modified by this function.
530
531 nEntries length or 'orig'
532
533 leftBound, define bound constraints for the results.
534 rghtBound
535
536 rsumTarget defines an equality constraint for the row sum
537
538 fixedUnsorted on output, if a feasible solutuion exists then
539 || orig - fixedUnsorted || = min when also
540 leftBound <= fixedUnsorted[i] <= rghtBound for all i
541 and sum(fixedUnsorted) = rsumTarget.
542
543 Note: it is possible to use the same pointer for
544 fixedUnsorted and orig. In this case, orig gets
545 overwritten with the new constraint satisfying values.
546
547 scalarData a work array that should be 3x nEntries.
548
549 On return constrain() indicates whether or not a feasible solution exists.
550 */
551
552 /*
553 Given a sequence of numbers o1 ... on, fix these so that they are as
554 close as possible to the original but satisfy bound constraints and also
555 have the same row sum as the oi's. If we know who is going to lie on a
556 bound, then the "best" answer (i.e., || o - f ||_2 = min) perturbs
557 each element that doesn't lie on a bound by the same amount.
558
559 We can represent the oi's by considering scattered points on a number line
560
561 | |
562 | |
563 o o o | o o o o o o |o o
564 | |
565
566 \____/ \____/
567 <---- <----
568 delta delta
569
570 Bounds are shown by vertical lines. The fi's must lie within the bounds, so
571 the 3 leftmost points must be shifted to the right and the 2 rightmost must
572 be shifted to the left. If these fi points are all shifted to the bounds
573 while the others remain in the same location, the row sum constraint is
574 likely not satisfied and so more shifting is necessary. In the figure, the f
575 rowsum is too large and so there must be more shifting to the left.
576
577 To minimize || o - f ||_2, we basically shift all "interiors" by the same
578 amount, denoted delta. The only trick is that some points near bounds are
579 still affected by the bounds and so these points might be shifted more or less
580 than delta. In the example,t he 3 rightmost points are shifted in the opposite
581 direction as delta to the bound. The 4th point is shifted by something less
582 than delta so it does not violate the lower bound. The rightmost point is
583 shifted to the bound by some amount larger than delta. However, the 2nd point
584 is shifted by delta (i.e., it lies inside the two bounds).
585
586 If we know delta, we can figure out everything. If we know which points
587 are special (not shifted by delta), we can also figure out everything.
588 The problem is these two things (delta and the special points) are
589 inter-connected. An algorithm for computing follows.
590
591 1) move exterior points to the bounds and compute how much the row sum is off
592 (rowSumDeviation). We assume now that the new row sum is high, so interior
593 points must be shifted left.
594
595 2) Mark closest point just left of the leftmost bound, closestToLeftBound,
596 and compute its distance to the leftmost bound. Mark closest point to the
597 left of the rightmost bound, closestToRghtBound, and compute its distance to
598 right bound. There are two cases to consider.
599
600 3) Case 1: closestToLeftBound is closer than closestToRghtBound.
601 Assume that shifting by delta does not move closestToLeftBound past the
602 left bound. This means that it will be shifted by delta. However,
603 closestToRghtBound will be shifted by more than delta. So the total
604 number of points shifted by delta (|interiorToBounds|) includes
605 closestToLeftBound up to and including the point just to the left of
606 closestToRghtBound. So
607
608 delta = rowSumDeviation/ |interiorToBounds| .
609
610 Recall that rowSumDeviation already accounts for the non-delta shift of
611 of closestToRightBound. Now check whether our assumption is valid.
612
613 If delta <= closestToLeftBoundDist, assumption is true so delta can be
614 applied to interiorToBounds ... and we are done.
615 Else assumption is false. Shift closestToLeftBound to the left bound.
616 Update rowSumDeviation, interiorToBounds, and identify new
617 closestToLeftBound. Repeat step 3).
618
619 Case 2: closestToRghtBound is closer than closestToLeftBound.
620 Assume that shifting by delta does not move closestToRghtBound past right
621 bound. This means that it must be shifted by more than delta to right
622 bound. So the total number of points shifted by delta again includes
623 closestToLeftBound up to and including the point just to the left of
624 closestToRghtBound. So again compute
625
626 delta = rowSumDeviation/ |interiorToBounds| .
627
628 If delta <= closestToRghtBoundDist, assumption is true so delta is
629 can be applied to interiorToBounds ... and we are done
630 Else assumption is false. Put closestToRghtBound in the
631 interiorToBounds set. Remove it's contribution to rowSumDeviation,
632 identify new closestToRghtBound. Repeat step 3)
633
634
635 To implement, sort the oi's so things like closestToLeftBound is just index
636 into sorted array. Updaing closestToLeftBound or closestToRghtBound requires
637 increment by 1. |interiorToBounds|= closestToRghtBound - closestToLeftBound
638 To handle the case when the rowsum is low (requiring right interior shifts),
639 just flip the signs on data and use the left-shift code (and then flip back
640 before exiting function.
641 */
642 bool hasFeasibleSol;
643 Scalar notFlippedLeftBound, notFlippedRghtBound, aBigNumber, *origSorted;
644 Scalar rowSumDeviation, temp, *fixedSorted, delta;
645 Scalar closestToLeftBoundDist, closestToRghtBoundDist;
646 LocalOrdinal closestToLeftBound, closestToRghtBound;
647 LocalOrdinal* inds;
648 bool flipped;
649
650 notFlippedLeftBound = leftBound;
651 notFlippedRghtBound = rghtBound;
652
653 if ((Teuchos::ScalarTraits<SC>::real(rsumTarget) >= Teuchos::ScalarTraits<SC>::real(leftBound * as<Scalar>(nEntries))) &&
654 (Teuchos::ScalarTraits<SC>::real(rsumTarget) <= Teuchos::ScalarTraits<SC>::real(rghtBound * as<Scalar>(nEntries))))
655 hasFeasibleSol = true;
656 else {
657 hasFeasibleSol = false;
658 return hasFeasibleSol;
659 }
660 flipped = false;
661 // compute aBigNumber to handle some corner cases where we need
662 // something large so that an if statement will be false
663 aBigNumber = Teuchos::ScalarTraits<SC>::zero();
664 for (LocalOrdinal i = 0; i < nEntries; i++) {
665 if (Teuchos::ScalarTraits<SC>::magnitude(orig[i]) > Teuchos::ScalarTraits<SC>::magnitude(aBigNumber))
666 aBigNumber = Teuchos::ScalarTraits<SC>::magnitude(orig[i]);
667 }
668 aBigNumber = aBigNumber + (Teuchos::ScalarTraits<SC>::magnitude(leftBound) + Teuchos::ScalarTraits<SC>::magnitude(rghtBound)) * as<Scalar>(100.0);
669
670 origSorted = &scalarData[0];
671 fixedSorted = &(scalarData[nEntries]);
672 inds = (LocalOrdinal*)&(scalarData[2 * nEntries]);
673
674 for (LocalOrdinal i = 0; i < nEntries; i++) inds[i] = i;
675 for (LocalOrdinal i = 0; i < nEntries; i++) origSorted[i] = orig[i]; /* orig no longer used */
676
677 // sort so that orig[inds] is sorted.
678 std::sort(inds, inds + nEntries,
679 [origSorted](LocalOrdinal leftIndex, LocalOrdinal rightIndex) { return Teuchos::ScalarTraits<SC>::real(origSorted[leftIndex]) < Teuchos::ScalarTraits<SC>::real(origSorted[rightIndex]); });
680
681 for (LocalOrdinal i = 0; i < nEntries; i++) origSorted[i] = orig[inds[i]];
682 // find entry in origSorted just to the right of the leftBound
683 closestToLeftBound = 0;
684 while ((closestToLeftBound < nEntries) && (Teuchos::ScalarTraits<SC>::real(origSorted[closestToLeftBound]) <= Teuchos::ScalarTraits<SC>::real(leftBound))) closestToLeftBound++;
685
686 // find entry in origSorted just to the right of the rghtBound
687 closestToRghtBound = closestToLeftBound;
688 while ((closestToRghtBound < nEntries) && (Teuchos::ScalarTraits<SC>::real(origSorted[closestToRghtBound]) <= Teuchos::ScalarTraits<SC>::real(rghtBound))) closestToRghtBound++;
689
690 // compute distance between closestToLeftBound and the left bound and the
691 // distance between closestToRghtBound and the right bound.
692
693 closestToLeftBoundDist = origSorted[closestToLeftBound] - leftBound;
694 if (closestToRghtBound == nEntries)
695 closestToRghtBoundDist = aBigNumber;
696 else
697 closestToRghtBoundDist = origSorted[closestToRghtBound] - rghtBound;
698
699 // compute how far the rowSum is off from the target row sum taking into account
700 // numbers that have been shifted to satisfy bound constraint
701
702 rowSumDeviation = leftBound * as<Scalar>(closestToLeftBound) + as<Scalar>((nEntries - closestToRghtBound)) * rghtBound - rsumTarget;
703 for (LocalOrdinal i = closestToLeftBound; i < closestToRghtBound; i++) rowSumDeviation += origSorted[i];
704
705 // the code that follow after this if statement assumes that rowSumDeviation is positive. If this
706 // is not the case, flip the signs of everything so that rowSumDeviation is now positive.
707 // Later we will flip the data back to its original form.
708 if (Teuchos::ScalarTraits<SC>::real(rowSumDeviation) < Teuchos::ScalarTraits<SC>::real(Teuchos::ScalarTraits<SC>::zero())) {
709 flipped = true;
710 temp = leftBound;
711 leftBound = -rghtBound;
712 rghtBound = temp;
713
714 /* flip sign of origSorted and reverse ordering so that the negative version is sorted */
715
716 if ((nEntries % 2) == 1) origSorted[(nEntries / 2)] = -origSorted[(nEntries / 2)];
717 for (LocalOrdinal i = 0; i < nEntries / 2; i++) {
718 temp = origSorted[i];
719 origSorted[i] = -origSorted[nEntries - 1 - i];
720 origSorted[nEntries - i - 1] = -temp;
721 }
722
723 /* reverse bounds */
724
725 LocalOrdinal itemp = closestToLeftBound;
726 closestToLeftBound = nEntries - closestToRghtBound;
727 closestToRghtBound = nEntries - itemp;
728 closestToLeftBoundDist = origSorted[closestToLeftBound] - leftBound;
729 if (closestToRghtBound == nEntries)
730 closestToRghtBoundDist = aBigNumber;
731 else
732 closestToRghtBoundDist = origSorted[closestToRghtBound] - rghtBound;
733
734 rowSumDeviation = -rowSumDeviation;
735 }
736
737 // initial fixedSorted so bounds are satisfied and interiors correspond to origSorted
738
739 for (LocalOrdinal i = 0; i < closestToLeftBound; i++) fixedSorted[i] = leftBound;
740 for (LocalOrdinal i = closestToLeftBound; i < closestToRghtBound; i++) fixedSorted[i] = origSorted[i];
741 for (LocalOrdinal i = closestToRghtBound; i < nEntries; i++) fixedSorted[i] = rghtBound;
742
743 while ((Teuchos::ScalarTraits<SC>::magnitude(rowSumDeviation) > Teuchos::ScalarTraits<SC>::magnitude(as<Scalar>(1.e-10) * rsumTarget))) { // && ( (closestToLeftBound < nEntries ) || (closestToRghtBound < nEntries))) {
744 if (closestToRghtBound != closestToLeftBound)
745 delta = rowSumDeviation / as<Scalar>(closestToRghtBound - closestToLeftBound);
746 else
747 delta = aBigNumber;
748
749 if (Teuchos::ScalarTraits<SC>::magnitude(closestToLeftBoundDist) <= Teuchos::ScalarTraits<SC>::magnitude(closestToRghtBoundDist)) {
750 if (Teuchos::ScalarTraits<SC>::magnitude(delta) <= Teuchos::ScalarTraits<SC>::magnitude(closestToLeftBoundDist)) {
751 rowSumDeviation = Teuchos::ScalarTraits<SC>::zero();
752 for (LocalOrdinal i = closestToLeftBound; i < closestToRghtBound; i++) fixedSorted[i] = origSorted[i] - delta;
753 } else {
754 rowSumDeviation = rowSumDeviation - closestToLeftBoundDist;
755 fixedSorted[closestToLeftBound] = leftBound;
756 closestToLeftBound++;
757 if (closestToLeftBound < nEntries)
758 closestToLeftBoundDist = origSorted[closestToLeftBound] - leftBound;
759 else
760 closestToLeftBoundDist = aBigNumber;
761 }
762 } else {
763 if (Teuchos::ScalarTraits<SC>::magnitude(delta) <= Teuchos::ScalarTraits<SC>::magnitude(closestToRghtBoundDist)) {
764 rowSumDeviation = 0;
765 for (LocalOrdinal i = closestToLeftBound; i < closestToRghtBound; i++) fixedSorted[i] = origSorted[i] - delta;
766 } else {
767 rowSumDeviation = rowSumDeviation + closestToRghtBoundDist;
768 // if (closestToRghtBound < nEntries) {
769 fixedSorted[closestToRghtBound] = origSorted[closestToRghtBound];
770 closestToRghtBound++;
771 // }
772 if (closestToRghtBound >= nEntries)
773 closestToRghtBoundDist = aBigNumber;
774 else
775 closestToRghtBoundDist = origSorted[closestToRghtBound] - rghtBound;
776 }
777 }
778 }
779
780 if (flipped) {
781 /* flip sign of fixedSorted and reverse ordering so that the positve version is sorted */
782
783 if ((nEntries % 2) == 1) fixedSorted[(nEntries / 2)] = -fixedSorted[(nEntries / 2)];
784 for (LocalOrdinal i = 0; i < nEntries / 2; i++) {
785 temp = fixedSorted[i];
786 fixedSorted[i] = -fixedSorted[nEntries - 1 - i];
787 fixedSorted[nEntries - i - 1] = -temp;
788 }
789 }
790 for (LocalOrdinal i = 0; i < nEntries; i++) fixedUnsorted[inds[i]] = fixedSorted[i];
791
792 /* check that no constraints are violated */
793
794 bool lowerViolation = false;
795 bool upperViolation = false;
796 bool sumViolation = false;
797 using TST = Teuchos::ScalarTraits<SC>;
798 temp = TST::zero();
799 for (LocalOrdinal i = 0; i < nEntries; i++) {
800 if (TST::real(fixedUnsorted[i]) < TST::real(notFlippedLeftBound)) lowerViolation = true;
801 if (TST::real(fixedUnsorted[i]) > TST::real(notFlippedRghtBound)) upperViolation = true;
802 temp += fixedUnsorted[i];
803 }
804 SC tol = as<Scalar>(std::max(1.0e-8, as<double>(100 * TST::eps())));
805 if (TST::magnitude(temp - rsumTarget) > TST::magnitude(tol * rsumTarget)) sumViolation = true;
806
807 TEUCHOS_TEST_FOR_EXCEPTION(lowerViolation, Exceptions::RuntimeError, "MueLu::SaPFactory::constrainRow: feasible solution but computation resulted in a lower bound violation??? ");
808 TEUCHOS_TEST_FOR_EXCEPTION(upperViolation, Exceptions::RuntimeError, "MueLu::SaPFactory::constrainRow: feasible solution but computation resulted in an upper bound violation??? ");
809 TEUCHOS_TEST_FOR_EXCEPTION(sumViolation, Exceptions::RuntimeError, "MueLu::SaPFactory::constrainRow: feasible solution but computation resulted in a row sum violation??? ");
810
811 return hasFeasibleSol;
812}
813
814template <typename local_matrix_type>
816 using Scalar = typename local_matrix_type::non_const_value_type;
817 using SC = Scalar;
818 using LO = typename local_matrix_type::non_const_ordinal_type;
819 using Device = typename local_matrix_type::device_type;
820 using KAT = KokkosKernels::ArithTraits<SC>;
821 const Scalar zero = KAT::zero();
822 const Scalar one = KAT::one();
824 local_matrix_type localP;
825 Kokkos::View<SC**, Device> ConstraintViolationSum;
826 Kokkos::View<SC**, Device> Rsum;
827 Kokkos::View<size_t**, Device> nPositive;
828
829 constraintKernel(LO nPDEs_, local_matrix_type localP_)
830 : nPDEs(nPDEs_)
831 , localP(localP_) {
832 ConstraintViolationSum = Kokkos::View<SC**, Device>("ConstraintViolationSum", localP_.numRows(), nPDEs);
833 Rsum = Kokkos::View<SC**, Device>("Rsum", localP_.numRows(), nPDEs);
834 nPositive = Kokkos::View<size_t**, Device>("nPositive", localP_.numRows(), nPDEs);
835 }
836
837 KOKKOS_INLINE_FUNCTION
838 void operator()(const size_t rowIdx) const {
839 auto rowPtr = localP.graph.row_map;
840 auto values = localP.values;
841
842 bool checkRow = true;
843
844 if (rowPtr(rowIdx + 1) == rowPtr(rowIdx)) checkRow = false;
845
846 while (checkRow) {
847 // check constraints and compute the row sum
848
849 for (auto entryIdx = rowPtr(rowIdx); entryIdx < rowPtr(rowIdx + 1); entryIdx++) {
850 Rsum(rowIdx, entryIdx % nPDEs) += values(entryIdx);
851 if (KAT::real(values(entryIdx)) < KAT::real(zero)) {
852 ConstraintViolationSum(rowIdx, entryIdx % nPDEs) += values(entryIdx);
853 values(entryIdx) = zero;
854 } else {
855 if (KAT::real(values(entryIdx)) != KAT::real(zero))
856 nPositive(rowIdx, entryIdx % nPDEs) = nPositive(rowIdx, entryIdx % nPDEs) + 1;
857
858 if (KAT::real(values(entryIdx)) > KAT::real(1.00001)) {
859 ConstraintViolationSum(rowIdx, entryIdx % nPDEs) += (values(entryIdx) - one);
860 values(entryIdx) = one;
861 }
862 }
863 }
864
865 checkRow = false;
866
867 // take into account any row sum that violates the contraints
868
869 for (size_t k = 0; k < (size_t)nPDEs; k++) {
870 if (KAT::real(Rsum(rowIdx, k)) < KAT::magnitude(zero)) {
871 ConstraintViolationSum(rowIdx, k) = ConstraintViolationSum(rowIdx, k) - Rsum(rowIdx, k); // rstumin
872 } else if (KAT::real(Rsum(rowIdx, k)) > KAT::magnitude(1.00001)) {
873 ConstraintViolationSum(rowIdx, k) = ConstraintViolationSum(rowIdx, k) + (one - Rsum(rowIdx, k)); // rstumin
874 }
875 }
876
877 // check if row need modification
878 for (size_t k = 0; k < (size_t)nPDEs; k++) {
879 if (KAT::magnitude(ConstraintViolationSum(rowIdx, k)) != KAT::magnitude(zero))
880 checkRow = true;
881 }
882 // modify row
883 if (checkRow) {
884 for (auto entryIdx = rowPtr(rowIdx); entryIdx < rowPtr(rowIdx + 1); entryIdx++) {
885 if (KAT::real(values(entryIdx)) > KAT::real(zero)) {
886 values(entryIdx) = values(entryIdx) +
887 (ConstraintViolationSum(rowIdx, entryIdx % nPDEs) / (Scalar(nPositive(rowIdx, entryIdx % nPDEs)) != zero ? Scalar(nPositive(rowIdx, entryIdx % nPDEs)) : one));
888 }
889 }
890 for (size_t k = 0; k < (size_t)nPDEs; k++) ConstraintViolationSum(rowIdx, k) = zero;
891 }
892 for (size_t k = 0; k < (size_t)nPDEs; k++) Rsum(rowIdx, k) = zero;
893 for (size_t k = 0; k < (size_t)nPDEs; k++) nPositive(rowIdx, k) = 0;
894 } // while (checkRow) ...
895 }
896};
897
898template <typename local_matrix_type>
900 using Scalar = typename local_matrix_type::non_const_value_type;
901 using SC = Scalar;
902 using LO = typename local_matrix_type::non_const_ordinal_type;
903 using Device = typename local_matrix_type::device_type;
904 using KAT = KokkosKernels::ArithTraits<SC>;
905 const Scalar zero = KAT::zero();
906 const Scalar one = KAT::one();
908 local_matrix_type localP;
909 Kokkos::View<SC**, Device> origSorted;
910 Kokkos::View<SC**, Device> fixedSorted;
911 Kokkos::View<LO**, Device> inds;
912
913 optimalSatisfyConstraintsForScalarPDEsKernel(LO nPDEs_, LO maxRowEntries_, local_matrix_type localP_)
914 : nPDEs(nPDEs_)
915 , localP(localP_) {
916 origSorted = Kokkos::View<SC**, Device>("origSorted", localP_.numRows(), maxRowEntries_);
917 fixedSorted = Kokkos::View<SC**, Device>("fixedSorted", localP_.numRows(), maxRowEntries_);
918 inds = Kokkos::View<LO**, Device>("inds", localP_.numRows(), maxRowEntries_);
919 }
920
921 KOKKOS_INLINE_FUNCTION
922 void operator()(const size_t rowIdx) const {
923 auto rowPtr = localP.graph.row_map;
924 auto values = localP.values;
925
926 auto nnz = rowPtr(rowIdx + 1) - rowPtr(rowIdx);
927
928 if (nnz != 0) {
929 Scalar rsumTarget = zero;
930 for (auto entryIdx = rowPtr(rowIdx); entryIdx < rowPtr(rowIdx + 1); entryIdx++) rsumTarget += values(entryIdx);
931
932 {
933 SC aBigNumber;
934 SC rowSumDeviation, temp, delta;
935 SC closestToLeftBoundDist, closestToRghtBoundDist;
936 LO closestToLeftBound, closestToRghtBound;
937 bool flipped;
938
939 SC leftBound = zero;
940 SC rghtBound = one;
941 if ((KAT::real(rsumTarget) >= KAT::real(leftBound * (static_cast<SC>(nnz)))) &&
942 (KAT::real(rsumTarget) <= KAT::real(rghtBound * (static_cast<SC>(nnz))))) { // has Feasible solution
943
944 flipped = false;
945 // compute aBigNumber to handle some corner cases where we need
946 // something large so that an if statement will be false
947 aBigNumber = KAT::zero();
948 for (auto entryIdx = rowPtr(rowIdx); entryIdx < rowPtr(rowIdx + 1); entryIdx++) {
949 if (KAT::magnitude(values(entryIdx)) > KAT::magnitude(aBigNumber))
950 aBigNumber = KAT::magnitude(values(entryIdx));
951 }
952 aBigNumber = aBigNumber + (KAT::magnitude(leftBound) + KAT::magnitude(rghtBound)) * (100 * one);
953
954 LO ind = 0;
955 for (auto entryIdx = rowPtr(rowIdx); entryIdx < rowPtr(rowIdx + 1); entryIdx++) {
956 origSorted(rowIdx, ind) = values(entryIdx);
957 inds(rowIdx, ind) = ind;
958 ind++;
959 }
960
961 auto sortVals = Kokkos::subview(origSorted, rowIdx, Kokkos::ALL());
962 auto sortInds = Kokkos::subview(inds, rowIdx, Kokkos::make_pair(0, ind));
963 // Need permutation indices to sort row values from smallest to largest,
964 // and unsort once row constraints are applied.
965 // serial insertion sort workaround from https://github.com/kokkos/kokkos/issues/645
966 for (LO i = 1; i < static_cast<LO>(nnz); ++i) {
967 ind = sortInds(i);
968 LO j = i;
969
970 if (KAT::real(sortVals(sortInds(i))) < KAT::real(sortVals(sortInds(0)))) {
971 for (; j > 0; --j) sortInds(j) = sortInds(j - 1);
972
973 sortInds(0) = ind;
974 } else {
975 for (; KAT::real(sortVals(ind)) < KAT::real(sortVals(sortInds(j - 1))); --j) sortInds(j) = sortInds(j - 1);
976
977 sortInds(j) = ind;
978 }
979 }
980
981 for (LO i = 0; i < static_cast<LO>(nnz); i++) origSorted(rowIdx, i) = values(rowPtr(rowIdx) + inds(rowIdx, i)); // values is no longer used
982 // find entry in origSorted just to the right of the leftBound
983 closestToLeftBound = 0;
984 while ((closestToLeftBound < static_cast<LO>(nnz)) && (KAT::real(origSorted(rowIdx, closestToLeftBound)) <= KAT::real(leftBound))) closestToLeftBound++;
985
986 // find entry in origSorted just to the right of the rghtBound
987 closestToRghtBound = closestToLeftBound;
988 while ((closestToRghtBound < static_cast<LO>(nnz)) && (KAT::real(origSorted(rowIdx, closestToRghtBound)) <= KAT::real(rghtBound))) closestToRghtBound++;
989
990 // compute distance between closestToLeftBound and the left bound and the
991 // distance between closestToRghtBound and the right bound.
992
993 closestToLeftBoundDist = origSorted(rowIdx, closestToLeftBound) - leftBound;
994 if (closestToRghtBound == static_cast<LO>(nnz))
995 closestToRghtBoundDist = aBigNumber;
996 else
997 closestToRghtBoundDist = origSorted(rowIdx, closestToRghtBound) - rghtBound;
998
999 // compute how far the rowSum is off from the target row sum taking into account
1000 // numbers that have been shifted to satisfy bound constraint
1001
1002 rowSumDeviation = leftBound * (static_cast<SC>(closestToLeftBound)) + (static_cast<SC>(nnz - closestToRghtBound)) * rghtBound - rsumTarget;
1003 for (LO i = closestToLeftBound; i < closestToRghtBound; i++) rowSumDeviation += origSorted(rowIdx, i);
1004
1005 // the code that follow after this if statement assumes that rowSumDeviation is positive. If this
1006 // is not the case, flip the signs of everything so that rowSumDeviation is now positive.
1007 // Later we will flip the data back to its original form.
1008 if (KAT::real(rowSumDeviation) < KAT::real(KAT::zero())) {
1009 flipped = true;
1010 temp = leftBound;
1011 leftBound = -rghtBound;
1012 rghtBound = temp;
1013
1014 /* flip sign of origSorted and reverse ordering so that the negative version is sorted */
1015
1016 // TODO: the following is bad for GPU performance. Switch to bit shifting if brave.
1017 if ((nnz % 2) == 1) origSorted(rowIdx, (nnz / 2)) = -origSorted(rowIdx, (nnz / 2));
1018 for (LO i = 0; i < static_cast<LO>(nnz / 2); i++) {
1019 temp = origSorted(rowIdx, i);
1020 origSorted(rowIdx, i) = -origSorted(rowIdx, nnz - 1 - i);
1021 origSorted(rowIdx, nnz - i - 1) = -temp;
1022 }
1023
1024 /* reverse bounds */
1025
1026 LO itemp = closestToLeftBound;
1027 closestToLeftBound = nnz - closestToRghtBound;
1028 closestToRghtBound = nnz - itemp;
1029 closestToLeftBoundDist = origSorted(rowIdx, closestToLeftBound) - leftBound;
1030 if (closestToRghtBound == static_cast<LO>(nnz))
1031 closestToRghtBoundDist = aBigNumber;
1032 else
1033 closestToRghtBoundDist = origSorted(rowIdx, closestToRghtBound) - rghtBound;
1034
1035 rowSumDeviation = -rowSumDeviation;
1036 }
1037
1038 // initial fixedSorted so bounds are satisfied and interiors correspond to origSorted
1039
1040 for (LO i = 0; i < closestToLeftBound; i++) fixedSorted(rowIdx, i) = leftBound;
1041 for (LO i = closestToLeftBound; i < closestToRghtBound; i++) fixedSorted(rowIdx, i) = origSorted(rowIdx, i);
1042 for (LO i = closestToRghtBound; i < static_cast<LO>(nnz); i++) fixedSorted(rowIdx, i) = rghtBound;
1043
1044 while ((KAT::magnitude(rowSumDeviation) > KAT::magnitude((one * 1.e-10) * rsumTarget))) { // && ( (closestToLeftBound < nEntries ) || (closestToRghtBound < nEntries))) {
1045 if (closestToRghtBound != closestToLeftBound)
1046 delta = rowSumDeviation / static_cast<SC>(closestToRghtBound - closestToLeftBound);
1047 else
1048 delta = aBigNumber;
1049
1050 if (KAT::magnitude(closestToLeftBoundDist) <= KAT::magnitude(closestToRghtBoundDist)) {
1051 if (KAT::magnitude(delta) <= KAT::magnitude(closestToLeftBoundDist)) {
1052 rowSumDeviation = zero;
1053 for (LO i = closestToLeftBound; i < closestToRghtBound; i++) fixedSorted(rowIdx, i) = origSorted(rowIdx, i) - delta;
1054 } else {
1055 rowSumDeviation = rowSumDeviation - closestToLeftBoundDist;
1056 fixedSorted(rowIdx, closestToLeftBound) = leftBound;
1057 closestToLeftBound++;
1058 if (closestToLeftBound < static_cast<LO>(nnz))
1059 closestToLeftBoundDist = origSorted(rowIdx, closestToLeftBound) - leftBound;
1060 else
1061 closestToLeftBoundDist = aBigNumber;
1062 }
1063 } else {
1064 if (KAT::magnitude(delta) <= KAT::magnitude(closestToRghtBoundDist)) {
1065 rowSumDeviation = 0;
1066 for (LO i = closestToLeftBound; i < closestToRghtBound; i++) fixedSorted(rowIdx, i) = origSorted(rowIdx, i) - delta;
1067 } else {
1068 rowSumDeviation = rowSumDeviation + closestToRghtBoundDist;
1069 // if (closestToRghtBound < nEntries) {
1070 fixedSorted(rowIdx, closestToRghtBound) = origSorted(rowIdx, closestToRghtBound);
1071 closestToRghtBound++;
1072 // }
1073 if (closestToRghtBound >= static_cast<LO>(nnz))
1074 closestToRghtBoundDist = aBigNumber;
1075 else
1076 closestToRghtBoundDist = origSorted(rowIdx, closestToRghtBound) - rghtBound;
1077 }
1078 }
1079 }
1080
1081 auto rowStart = rowPtr(rowIdx);
1082 if (flipped) {
1083 /* flip sign of fixedSorted and reverse ordering so that the positve version is sorted */
1084
1085 if ((nnz % 2) == 1) fixedSorted(rowIdx, (nnz / 2)) = -fixedSorted(rowIdx, (nnz / 2));
1086 for (LO i = 0; i < static_cast<LO>(nnz / 2); i++) {
1087 temp = fixedSorted(rowIdx, i);
1088 fixedSorted(rowIdx, i) = -fixedSorted(rowIdx, nnz - 1 - i);
1089 fixedSorted(rowIdx, nnz - i - 1) = -temp;
1090 }
1091 }
1092 // unsort and update row values with new values
1093 for (LO i = 0; i < static_cast<LO>(nnz); i++) values(rowStart + inds(rowIdx, i)) = fixedSorted(rowIdx, i);
1094
1095 } else { // row does not have feasible solution to match constraint
1096 // just set all entries to the same value giving a row sum of 1
1097 for (auto entryIdx = rowPtr(rowIdx); entryIdx < rowPtr(rowIdx + 1); entryIdx++) values(entryIdx) = one / (static_cast<SC>(nnz));
1098 }
1099 }
1100 }
1101 }
1102};
1103
1104template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1106 using Device = typename Matrix::local_matrix_device_type::device_type;
1107 LO nPDEs = A->GetFixedBlockSize();
1108
1109 using local_mat_type = typename Matrix::local_matrix_device_type;
1110 constraintKernel<local_mat_type> myKernel(nPDEs, P->getLocalMatrixDevice());
1111 Kokkos::parallel_for("enforce constraint", Kokkos::RangePolicy<typename Device::execution_space>(0, P->getRowMap()->getLocalNumElements()),
1112 myKernel);
1113
1114} // SatsifyPConstraints()
1115
1116template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1118 using Device = typename Matrix::local_matrix_device_type::device_type;
1119 LO nPDEs = 1; // A->GetFixedBlockSize();
1120
1121 using local_mat_type = typename Matrix::local_matrix_device_type;
1122 optimalSatisfyConstraintsForScalarPDEsKernel<local_mat_type> myKernel(nPDEs, P->getLocalMaxNumRowEntries(), P->getLocalMatrixDevice());
1123 Kokkos::parallel_for("enforce constraint", Kokkos::RangePolicy<typename Device::execution_space>(0, P->getLocalNumRows()),
1124 myKernel);
1125
1126} // SatsifyPConstraints()
1127
1128template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1129RCP<Xpetra::Matrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>> SaPFactory<Scalar, LocalOrdinal, GlobalOrdinal, Node>::JacobiMaxwell1(const RCP<Matrix>& SM,
1130 const RCP<Matrix>& Kn,
1131 const RCP<Matrix>& D0,
1132 const RCP<Matrix>& Pe_tent,
1133 const Scalar beta) const {
1134 // Compute Pe := (I - \beta D0 * diag(Kn)^-1 * D0^T SM) * Pe_tent
1135 using XMM = Xpetra::MatrixMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>;
1136 auto one = Teuchos::ScalarTraits<Scalar>::one();
1137
1138 auto diagKnInv = Utilities::GetMatrixDiagonalInverse(*Kn);
1139
1140 auto D0_diagKnInv = Xpetra::MatrixFactory<Scalar, LocalOrdinal, GlobalOrdinal, Node>::BuildCopy(D0, Teuchos::Copy);
1141 D0_diagKnInv->rightScale(*diagKnInv);
1142
1143 RCP<Xpetra::Matrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>> dummy;
1144 auto D0_diagKnInv_D0T = XMM::Multiply(*D0_diagKnInv, false, *D0, true, dummy, GetOStream(Runtime0));
1145 D0_diagKnInv = Teuchos::null;
1146
1147 auto SM_Pe_tent = XMM::Multiply(*SM, false, *Pe_tent, false, dummy, GetOStream(Runtime0));
1148 auto D0_diagKnInv_D0T_SM_Pe_tent = XMM::Multiply(*D0_diagKnInv_D0T, false, *SM_Pe_tent, false, dummy, GetOStream(Runtime0));
1149
1150 RCP<Xpetra::Matrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>> Pe;
1151 XMM::TwoMatrixAdd(*Pe_tent, false, one, *D0_diagKnInv_D0T_SM_Pe_tent, false, -beta, Pe, GetOStream(Runtime0));
1152 Pe->fillComplete(Pe_tent->getDomainMap(), Pe_tent->getRangeMap());
1153
1154 return Pe;
1155}
1156
1157} // namespace MueLu
1158
1159#endif // MUELU_SAPFACTORY_DEF_HPP
1160
1161// TODO: restrictionMode_ should use the parameter list.
#define SET_VALID_ENTRY(name)
MueLu::DefaultLocalOrdinal LocalOrdinal
MueLu::DefaultScalar Scalar
Exception throws to report errors in the internal logical of the program.
void Input(Level &level, const std::string &varName) const
T Get(Level &level, const std::string &varName) const
void Set(Level &level, const std::string &varName, const T &data) const
const RCP< const FactoryBase > GetFactory(const std::string &varName) const
Default implementation of FactoryAcceptor::GetFactory().
Timer to be used in factories. Similar to Monitor but with additional timers.
static RCP< Xpetra::Matrix< Scalar, LocalOrdinal, GlobalOrdinal, Node > > Jacobi(typename Teuchos::ScalarTraits< Scalar >::magnitudeType omega, const Xpetra::Vector< Scalar, LocalOrdinal, GlobalOrdinal, Node > &Dinv, const Xpetra::Matrix< Scalar, LocalOrdinal, GlobalOrdinal, Node > &A, const Xpetra::Matrix< Scalar, LocalOrdinal, GlobalOrdinal, Node > &B, RCP< Xpetra::Matrix< Scalar, LocalOrdinal, GlobalOrdinal, Node > > C_in, Teuchos::FancyOStream &fos, const std::string &label, RCP< ParameterList > &params)
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.
void DeclareInput(const std::string &ename, const FactoryBase *factory, const FactoryBase *requestedBy=NoFactory::get())
Callback from FactoryBase::CallDeclareInput() and FactoryBase::DeclareInput().
const RCP< const FactoryManagerBase > GetFactoryManager()
returns the current factory manager
int GetLevelID() const
Return level number.
T & Get(const std::string &ename, const FactoryBase *factory=NoFactory::get())
Get data without decrementing associated storage counter (i.e., read-only access)....
virtual const Teuchos::ParameterList & GetParameterList() const
static std::string PrintMatrixInfo(const Matrix &A, const std::string &msgTag, RCP< const Teuchos::ParameterList > params=Teuchos::null)
void SatisfyPConstraints(RCP< Matrix > A, RCP< Matrix > &P) const
void Build(Level &fineLevel, Level &coarseLevel) const
virtual ~SaPFactory()
Destructor.
void optimalSatisfyPConstraintsForScalarPDEsNonKokkos(RCP< Matrix > &P) const
RCP< const ParameterList > GetValidParameterList() const
Return a const parameter list of valid parameters that setParameterList() will accept.
void optimalSatisfyPConstraintsForScalarPDEs(RCP< Matrix > &P) const
bool constrainRow(Scalar *orig, LocalOrdinal nEntries, Scalar leftBound, Scalar rghtBound, Scalar rsumTarget, Scalar *fixedUnsorted, Scalar *scalarData) const
SaPFactory()
Constructor. User can supply a factory for generating the tentative prolongator.
void BuildP(Level &fineLevel, Level &coarseLevel) const
Abstract Build method.
void SatisfyPConstraintsNonKokkos(RCP< Matrix > A, RCP< Matrix > &P) const
void DeclareInput(Level &fineLevel, Level &coarseLevel) const
Input.
RCP< Matrix > JacobiMaxwell1(const RCP< Matrix > &SM, const RCP< Matrix > &Kn, const RCP< Matrix > &D0, const RCP< Matrix > &Pe_tent, const Scalar beta) const
Timer to be used in factories. Similar to SubMonitor but adds a timer level by level.
static Scalar PowerMethod(const Matrix &A, bool scaleByDiag=true, DefaultLocalOrdinal niters=10, Magnitude tolerance=1e-2, Magnitude diagonalReplacementTol=Teuchos::ScalarTraits< Scalar >::eps() *100, bool verbose=false, unsigned int seed=123)
static RCP< Vector > GetMatrixDiagonalInverse(const Matrix &A, Magnitude tol=Teuchos::ScalarTraits< Scalar >::eps() *100, Scalar valReplacement=Teuchos::ScalarTraits< Scalar >::zero(), const bool doLumped=false)
static Teuchos::RCP< Vector > GetLumpedMatrixDiagonal(Matrix const &A, const bool doReciprocal=false, Magnitude tol=Teuchos::ScalarTraits< Scalar >::magnitude(Teuchos::ScalarTraits< Scalar >::zero()), Scalar valReplacement=Teuchos::ScalarTraits< Scalar >::zero(), const bool replaceSingleEntryRowWithZero=false, const bool useAverageAbsDiagVal=false)
static RCP< Xpetra::Matrix< Scalar, LocalOrdinal, GlobalOrdinal, Node > > Transpose(Xpetra::Matrix< Scalar, LocalOrdinal, GlobalOrdinal, Node > &Op, bool optimizeTranspose=false, const std::string &label=std::string(), const Teuchos::RCP< Teuchos::ParameterList > &params=Teuchos::null)
Teuchos::FancyOStream & GetOStream(MsgType type, int thisProcRankOnly=0) const
Get an output stream for outputting the input message type.
bool IsPrint(MsgType type, int thisProcRankOnly=-1) const
Find out whether we need to print out information for a specific message type.
Namespace for MueLu classes and methods.
@ Warnings0
Important warning messages (one line).
@ Statistics2
Print even more statistics.
@ Warnings
Print all warning messages.
@ Statistics1
Print more statistics.
@ Runtime0
One-liner description of what is happening.
std::string toString(const T &what)
Little helper function to convert non-string types to strings.
typename local_matrix_type::device_type Device
typename local_matrix_type::non_const_value_type Scalar
KokkosKernels::ArithTraits< SC > KAT
Kokkos::View< SC **, Device > ConstraintViolationSum
Kokkos::View< size_t **, Device > nPositive
constraintKernel(LO nPDEs_, local_matrix_type localP_)
typename local_matrix_type::non_const_ordinal_type LO
KOKKOS_INLINE_FUNCTION void operator()(const size_t rowIdx) const
Kokkos::View< SC **, Device > Rsum
typename local_matrix_type::non_const_value_type Scalar
KOKKOS_INLINE_FUNCTION void operator()(const size_t rowIdx) const
optimalSatisfyConstraintsForScalarPDEsKernel(LO nPDEs_, LO maxRowEntries_, local_matrix_type localP_)
typename local_matrix_type::non_const_ordinal_type LO