MueLu Version of the Day
Loading...
Searching...
No Matches
MueLu_FilteredAFactory_def.hpp
Go to the documentation of this file.
1// @HEADER
2//
3// ***********************************************************************
4//
5// MueLu: A package for multigrid based preconditioning
6// Copyright 2012 Sandia Corporation
7//
8// Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
9// the U.S. Government retains certain rights in this software.
10//
11// Redistribution and use in source and binary forms, with or without
12// modification, are permitted provided that the following conditions are
13// met:
14//
15// 1. Redistributions of source code must retain the above copyright
16// notice, this list of conditions and the following disclaimer.
17//
18// 2. Redistributions in binary form must reproduce the above copyright
19// notice, this list of conditions and the following disclaimer in the
20// documentation and/or other materials provided with the distribution.
21//
22// 3. Neither the name of the Corporation nor the names of the
23// contributors may be used to endorse or promote products derived from
24// this software without specific prior written permission.
25//
26// THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "AS IS" AND ANY
27// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SANDIA CORPORATION OR THE
30// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
31// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
32// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
33// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
34// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
35// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
36// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37//
38// Questions? Contact
39// Jonathan Hu (jhu@sandia.gov)
40// Andrey Prokopenko (aprokop@sandia.gov)
41// Ray Tuminaro (rstumin@sandia.gov)
42//
43// ***********************************************************************
44//
45// @HEADER
46#ifndef MUELU_FILTEREDAFACTORY_DEF_HPP
47#define MUELU_FILTEREDAFACTORY_DEF_HPP
48
49#include <Xpetra_Matrix.hpp>
50#include <Xpetra_MatrixFactory.hpp>
51#include <Xpetra_IO.hpp>
52
54
55#include "MueLu_Level.hpp"
56#include "MueLu_MasterList.hpp"
57#include "MueLu_Monitor.hpp"
58#include "MueLu_Aggregates.hpp"
59#include "MueLu_AmalgamationInfo.hpp"
60#include "MueLu_Utilities.hpp"
61
62// Variable to enable lots of debug output
63#define MUELU_FILTEREDAFACTORY_LOTS_OF_PRINTING 0
64
65
66namespace MueLu {
67
68 template <class T>
69 void sort_and_unique(T & array) {
70 std::sort(array.begin(),array.end());
71 std::unique(array.begin(),array.end());
72 }
73
74
75
76 template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
78 RCP<ParameterList> validParamList = rcp(new ParameterList());
79
80#define SET_VALID_ENTRY(name) validParamList->setEntry(name, MasterList::getEntry(name))
81 SET_VALID_ENTRY("filtered matrix: use lumping");
82 SET_VALID_ENTRY("filtered matrix: reuse graph");
83 SET_VALID_ENTRY("filtered matrix: reuse eigenvalue");
84 SET_VALID_ENTRY("filtered matrix: use root stencil");
85 SET_VALID_ENTRY("filtered matrix: use spread lumping");
86 SET_VALID_ENTRY("filtered matrix: spread lumping diag dom growth factor");
87 SET_VALID_ENTRY("filtered matrix: spread lumping diag dom cap");
88 SET_VALID_ENTRY("filtered matrix: Dirichlet threshold");
89#undef SET_VALID_ENTRY
90
91 validParamList->set< RCP<const FactoryBase> >("A", Teuchos::null, "Generating factory of the matrix A used for filtering");
92 validParamList->set< RCP<const FactoryBase> >("Graph", Teuchos::null, "Generating factory for coalesced filtered graph");
93 validParamList->set< RCP<const FactoryBase> >("Filtering", Teuchos::null, "Generating factory for filtering boolean");
94
95
96 // Only need these for the "use root stencil" option
97 validParamList->set< RCP<const FactoryBase> >("Aggregates", Teuchos::null, "Generating factory of the aggregates");
98 validParamList->set< RCP<const FactoryBase> >("UnAmalgamationInfo", Teuchos::null, "Generating factory of UnAmalgamationInfo");
99 return validParamList;
100 }
101
102 template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
104 Input(currentLevel, "A");
105 Input(currentLevel, "Filtering");
106 Input(currentLevel, "Graph");
107 const ParameterList& pL = GetParameterList();
108 if(pL.isParameter("filtered matrix: use root stencil") && pL.get<bool>("filtered matrix: use root stencil") == true){
109 Input(currentLevel, "Aggregates");
110 Input(currentLevel, "UnAmalgamationInfo");
111 }
112 }
113
114 template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
116 FactoryMonitor m(*this, "Matrix filtering", currentLevel);
117
118 RCP<Matrix> A = Get< RCP<Matrix> >(currentLevel, "A");
119 if (Get<bool>(currentLevel, "Filtering") == false) {
120 GetOStream(Runtime0) << "Filtered matrix is not being constructed as no filtering is being done" << std::endl;
121 Set(currentLevel, "A", A);
122 return;
123 }
124
125 const ParameterList& pL = GetParameterList();
126 bool lumping = pL.get<bool>("filtered matrix: use lumping");
127 if (lumping)
128 GetOStream(Runtime0) << "Lumping dropped entries" << std::endl;
129
130 bool use_spread_lumping = pL.get<bool>("filtered matrix: use spread lumping");
131 if (use_spread_lumping && (!lumping) )
132 throw std::runtime_error("Must also request 'filtered matrix: use lumping' in order to use spread lumping");
133
134 if (use_spread_lumping) {
135 GetOStream(Runtime0) << "using spread lumping " << std::endl;
136 }
137
138 double DdomAllowGrowthRate = 1.1;
139 double DdomCap = 2.0;
140 if (use_spread_lumping) {
141 DdomAllowGrowthRate = pL.get<double>("filtered matrix: spread lumping diag dom growth factor");
142 DdomCap = pL.get<double>("filtered matrix: spread lumping diag dom cap");
143 }
144 bool use_root_stencil = lumping && pL.get<bool>("filtered matrix: use root stencil");
145 if (use_root_stencil)
146 GetOStream(Runtime0) << "Using root stencil for dropping" << std::endl;
147 double dirichlet_threshold = pL.get<double>("filtered matrix: Dirichlet threshold");
148 if(dirichlet_threshold >= 0.0)
149 GetOStream(Runtime0) << "Filtering Dirichlet threshold of "<<dirichlet_threshold << std::endl;
150
151 if(use_root_stencil || pL.get<bool>("filtered matrix: reuse graph"))
152 GetOStream(Runtime0) << "Reusing graph"<<std::endl;
153 else
154 GetOStream(Runtime0) << "Generating new graph"<<std::endl;
155
156
157 RCP<GraphBase> G = Get< RCP<GraphBase> >(currentLevel, "Graph");
159 {
160 FILE * f = fopen("graph.dat","w");
161 size_t numGRows = G->GetNodeNumVertices();
162 for (size_t i = 0; i < numGRows; i++) {
163 // Set up filtering array
164 ArrayView<const LO> indsG = G->getNeighborVertices(i);
165 for(size_t j=0; j<(size_t)indsG.size(); j++) {
166 fprintf(f,"%d %d 1.0\n",(int)i,(int)indsG[j]);
167 }
168 }
169 fclose(f);
170 }
171
172 RCP<ParameterList> fillCompleteParams(new ParameterList);
173 fillCompleteParams->set("No Nonlocal Changes", true);
174
175 RCP<Matrix> filteredA;
176 if(use_root_stencil) {
177 filteredA = MatrixFactory::Build(A->getCrsGraph());
178 filteredA->fillComplete(fillCompleteParams);
179 filteredA->resumeFill();
180 BuildNewUsingRootStencil(*A, *G, dirichlet_threshold, currentLevel,*filteredA, use_spread_lumping,DdomAllowGrowthRate, DdomCap);
181 filteredA->fillComplete(fillCompleteParams);
182
183 }
184 else if (pL.get<bool>("filtered matrix: reuse graph")) {
185 filteredA = MatrixFactory::Build(A->getCrsGraph());
186 filteredA->resumeFill();
187 BuildReuse(*A, *G, (lumping != use_spread_lumping), dirichlet_threshold,*filteredA);
188 // only lump inside BuildReuse if lumping is true and use_spread_lumping is false
189 // note: they use_spread_lumping cannot be true if lumping is false
190
191 if (use_spread_lumping) ExperimentalLumping(*A, *filteredA, DdomAllowGrowthRate, DdomCap);
192 filteredA->fillComplete(fillCompleteParams);
193
194 } else {
195
196 filteredA = MatrixFactory::Build(A->getRowMap(), A->getColMap(), A->getLocalMaxNumRowEntries());
197 BuildNew(*A, *G, (lumping != use_spread_lumping), dirichlet_threshold,*filteredA);
198 // only lump inside BuildNew if lumping is true and use_spread_lumping is false
199 // note: they use_spread_lumping cannot be true if lumping is false
200 if (use_spread_lumping) ExperimentalLumping(*A, *filteredA, DdomAllowGrowthRate, DdomCap);
201 filteredA->fillComplete(A->getDomainMap(), A->getRangeMap(), fillCompleteParams);
202 }
203
204
205
207 {
208 Xpetra::IO<SC,LO,GO,NO>::Write("filteredA.dat", *filteredA);
209
210 //original filtered A and actual A
211 Xpetra::IO<SC,LO,GO,NO>::Write("A.dat", *A);
212 RCP<Matrix> origFilteredA = MatrixFactory::Build(A->getRowMap(), A->getColMap(), A->getLocalMaxNumRowEntries());
213 BuildNew(*A, *G, lumping, dirichlet_threshold,*origFilteredA);
214 if (use_spread_lumping) ExperimentalLumping(*A, *origFilteredA, DdomAllowGrowthRate, DdomCap);
215 origFilteredA->fillComplete(A->getDomainMap(), A->getRangeMap(), fillCompleteParams);
216 Xpetra::IO<SC,LO,GO,NO>::Write("origFilteredA.dat", *origFilteredA);
217 }
218
219
220 filteredA->SetFixedBlockSize(A->GetFixedBlockSize());
221
222 if (pL.get<bool>("filtered matrix: reuse eigenvalue")) {
223 // Reuse max eigenvalue from A
224 // It is unclear what eigenvalue is the best for the smoothing, but we already may have
225 // the D^{-1}A estimate in A, may as well use it.
226 // NOTE: ML does that too
227 filteredA->SetMaxEigenvalueEstimate(A->GetMaxEigenvalueEstimate());
228 }
229
230 Set(currentLevel, "A", filteredA);
231 }
232
233// Epetra's API allows direct access to row array.
234// Tpetra's API does not, providing only ArrayView<const .>
235// But in most situations we are currently interested in, it is safe to assume
236// that the view is to the actual data. So this macro directs the code to do
237// const_cast, and modify the entries directly. This allows us to avoid
238// replaceLocalValues() call which is quite expensive due to all the searches.
239//#define ASSUME_DIRECT_ACCESS_TO_ROW // See github issue 10883#issuecomment-1256676340
240
241 // Both Epetra and Tpetra matrix-matrix multiply use the following trick:
242 // if an entry of the left matrix is zero, it does not compute or store the
243 // zero value.
244 //
245 // This trick allows us to bypass constructing a new matrix. Instead, we
246 // make a deep copy of the original one, and fill it in with zeros, which
247 // are ignored during the prolongator smoothing.
248 template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
250 BuildReuse(const Matrix& A, const GraphBase& G, const bool lumping, double dirichletThresh, Matrix& filteredA) const {
251 using TST = typename Teuchos::ScalarTraits<SC>;
252 SC zero = TST::zero();
253
254
255 size_t blkSize = A.GetFixedBlockSize();
256
257 ArrayView<const LO> inds;
258 ArrayView<const SC> valsA;
259#ifdef ASSUME_DIRECT_ACCESS_TO_ROW
260 ArrayView<SC> vals;
261#else
262 Array<SC> vals;
263#endif
264
265 Array<char> filter( std::max(blkSize*G.GetImportMap()->getLocalNumElements(),
266 A.getColMap()->getLocalNumElements()),
267 0);
268
269 size_t numGRows = G.GetNodeNumVertices();
270 for (size_t i = 0; i < numGRows; i++) {
271 // Set up filtering array
272 ArrayView<const LO> indsG = G.getNeighborVertices(i);
273 for (size_t j = 0; j < as<size_t>(indsG.size()); j++)
274 for (size_t k = 0; k < blkSize; k++)
275 filter[indsG[j]*blkSize+k] = 1;
276
277 for (size_t k = 0; k < blkSize; k++) {
278 LO row = i*blkSize + k;
279
280 A.getLocalRowView(row, inds, valsA);
281
282 size_t nnz = inds.size();
283 if (nnz == 0)
284 continue;
285
286#ifdef ASSUME_DIRECT_ACCESS_TO_ROW
287 // Transform ArrayView<const SC> into ArrayView<SC>
288 ArrayView<const SC> vals1;
289 filteredA.getLocalRowView(row, inds, vals1);
290 vals = ArrayView<SC>(const_cast<SC*>(vals1.getRawPtr()), nnz);
291
292 memcpy(vals.getRawPtr(), valsA.getRawPtr(), nnz*sizeof(SC));
293#else
294 vals = Array<SC>(valsA);
295#endif
296
297 SC ZERO = Teuchos::ScalarTraits<SC>::zero();
298 // SC ONE = Teuchos::ScalarTraits<SC>::one();
299 SC A_rowsum = ZERO, F_rowsum = ZERO;
300 for(LO l = 0; l < (LO)inds.size(); l++)
301 A_rowsum += valsA[l];
302
303 if (lumping == false) {
304 for (size_t j = 0; j < nnz; j++)
305 if (!filter[inds[j]])
306 vals[j] = zero;
307
308 } else {
309 LO diagIndex = -1;
310 SC diagExtra = zero;
311
312 for (size_t j = 0; j < nnz; j++) {
313 if (filter[inds[j]]) {
314 if (inds[j] == row) {
315 // Remember diagonal position
316 diagIndex = j;
317 }
318 continue;
319 }
320
321 diagExtra += vals[j];
322
323 vals[j] = zero;
324 }
325
326 // Lump dropped entries
327 // NOTE
328 // * Does it make sense to lump for elasticity?
329 // * Is it different for diffusion and elasticity?
330 //SC diagA = ZERO;
331 if (diagIndex != -1) {
332 //diagA = vals[diagIndex];
333 vals[diagIndex] += diagExtra;
334 if(dirichletThresh >= 0.0 && TST::real(vals[diagIndex]) <= dirichletThresh) {
335
336 // printf("WARNING: row %d diag(Afiltered) = %8.2e diag(A)=%8.2e\n",row,vals[diagIndex],diagA);
337 for(LO l = 0; l < (LO)nnz; l++)
338 F_rowsum += vals[l];
339 // printf(" : A rowsum = %8.2e F rowsum = %8.2e\n",A_rowsum,F_rowsum);
340 vals[diagIndex] = TST::one();
341 }
342 }
343
344 }
345
346#ifndef ASSUME_DIRECT_ACCESS_TO_ROW
347 // Because we used a column map in the construction of the matrix
348 // we can just use insertLocalValues here instead of insertGlobalValues
349 filteredA.replaceLocalValues(row, inds, vals);
350#endif
351 }
352
353 // Reset filtering array
354 for (size_t j = 0; j < as<size_t> (indsG.size()); j++)
355 for (size_t k = 0; k < blkSize; k++)
356 filter[indsG[j]*blkSize+k] = 0;
357 }
358 }
359
360 template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
362 BuildNew(const Matrix& A, const GraphBase& G, const bool lumping, double dirichletThresh, Matrix& filteredA) const {
363 using TST = typename Teuchos::ScalarTraits<SC>;
364 SC zero = Teuchos::ScalarTraits<SC>::zero();
365
366 size_t blkSize = A.GetFixedBlockSize();
367
368 ArrayView<const LO> indsA;
369 ArrayView<const SC> valsA;
370 Array<LO> inds;
371 Array<SC> vals;
372
373 Array<char> filter(blkSize * G.GetImportMap()->getLocalNumElements(), 0);
374
375 size_t numGRows = G.GetNodeNumVertices();
376 for (size_t i = 0; i < numGRows; i++) {
377 // Set up filtering array
378 ArrayView<const LO> indsG = G.getNeighborVertices(i);
379 for (size_t j = 0; j < as<size_t>(indsG.size()); j++)
380 for (size_t k = 0; k < blkSize; k++)
381 filter[indsG[j]*blkSize+k] = 1;
382
383 for (size_t k = 0; k < blkSize; k++) {
384 LO row = i*blkSize + k;
385
386 A.getLocalRowView(row, indsA, valsA);
387
388 size_t nnz = indsA.size();
389 if (nnz == 0)
390 continue;
391
392 inds.resize(indsA.size());
393 vals.resize(valsA.size());
394
395 size_t numInds = 0;
396 if (lumping == false) {
397 for (size_t j = 0; j < nnz; j++)
398 if (filter[indsA[j]]) {
399 inds[numInds] = indsA[j];
400 vals[numInds] = valsA[j];
401 numInds++;
402 }
403
404 } else {
405 LO diagIndex = -1;
406 SC diagExtra = zero;
407
408 for (size_t j = 0; j < nnz; j++) {
409 if (filter[indsA[j]]) {
410 inds[numInds] = indsA[j];
411 vals[numInds] = valsA[j];
412
413 // Remember diagonal position
414 if (inds[numInds] == row)
415 diagIndex = numInds;
416
417 numInds++;
418
419 } else {
420 diagExtra += valsA[j];
421 }
422 }
423
424 // Lump dropped entries
425 // NOTE
426 // * Does it make sense to lump for elasticity?
427 // * Is it different for diffusion and elasticity?
428 if (diagIndex != -1) {
429 vals[diagIndex] += diagExtra;
430 if(dirichletThresh >= 0.0 && TST::real(vals[diagIndex]) <= dirichletThresh) {
431 // SC A_rowsum = ZERO, F_rowsum = ZERO;
432 // printf("WARNING: row %d diag(Afiltered) = %8.2e diag(A)=%8.2e\n",row,vals[diagIndex],diagA);
433 // for(LO l = 0; l < (LO)nnz; l++)
434 // F_rowsum += vals[l];
435 // printf(" : A rowsum = %8.2e F rowsum = %8.2e\n",A_rowsum,F_rowsum);
436 vals[diagIndex] = TST::one();
437 }
438 }
439
440 }
441 inds.resize(numInds);
442 vals.resize(numInds);
443
444
445
446 // Because we used a column map in the construction of the matrix
447 // we can just use insertLocalValues here instead of insertGlobalValues
448 filteredA.insertLocalValues(row, inds, vals);
449 }
450
451 // Reset filtering array
452 for (size_t j = 0; j < as<size_t> (indsG.size()); j++)
453 for (size_t k = 0; k < blkSize; k++)
454 filter[indsG[j]*blkSize+k] = 0;
455 }
456 }
457
458 template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
460 BuildNewUsingRootStencil(const Matrix& A, const GraphBase& G, double dirichletThresh, Level& currentLevel, Matrix& filteredA, bool use_spread_lumping, double DdomAllowGrowthRate, double DdomCap) const {
461 using TST = typename Teuchos::ScalarTraits<SC>;
462 using Teuchos::arcp_const_cast;
463 SC ZERO = Teuchos::ScalarTraits<SC>::zero();
464 SC ONE = Teuchos::ScalarTraits<SC>::one();
465 LO INVALID = Teuchos::OrdinalTraits<LO>::invalid();
466
467 size_t numNodes = G.GetNodeNumVertices();
468 size_t blkSize = A.GetFixedBlockSize();
469 size_t numRows = A.getMap()->getLocalNumElements();
470 ArrayView<const LO> indsA;
471 ArrayView<const SC> valsA;
472 ArrayRCP<const size_t> rowptr;
473 ArrayRCP<const LO> inds;
474 ArrayRCP<const SC> vals_const;
475 ArrayRCP<SC> vals;
476
477 // We're going to grab the vals array from filteredA and then blitz it with NAN as a placeholder for "entries that have
478 // not yey been touched." If I see an entry in the primary loop that has a zero, then I assume it has been nuked by
479 // it's symmetric pair, so I add it to the diagonal. If it has a NAN, process as normal.
480 RCP<CrsMatrix> filteredAcrs = dynamic_cast<const CrsMatrixWrap*>(&filteredA)->getCrsMatrix();
481 filteredAcrs->getAllValues(rowptr,inds,vals_const);
482 vals = arcp_const_cast<SC>(vals_const);
483 Array<bool> vals_dropped_indicator(vals.size(),false);
484
485 // In the badAggNeighbors loop, if the entry has any number besides NAN, I add it to the diagExtra and then zero the guy.
486 RCP<Aggregates> aggregates = Get< RCP<Aggregates> > (currentLevel, "Aggregates");
487 RCP<AmalgamationInfo> amalgInfo = Get< RCP<AmalgamationInfo> > (currentLevel, "UnAmalgamationInfo");
488 LO numAggs = aggregates->GetNumAggregates();
489
490 // Check map nesting
491 RCP<const Map> rowMap = A.getRowMap();
492 RCP<const Map> colMap = A.getColMap();
493 bool goodMap = MueLu::Utilities<SC,LO,GO,NO>::MapsAreNested(*rowMap, *colMap);
494 TEUCHOS_TEST_FOR_EXCEPTION(!goodMap, Exceptions::RuntimeError,"FilteredAFactory: Maps are not nested");
495
496 // Since we're going to symmetrize this
497 Array<LO> diagIndex(numRows,INVALID);
498 Array<SC> diagExtra(numRows,ZERO);
499
500 // Lists of nodes in each aggregate
501 struct {
502 // GH: For now, copy everything to host until we properly set this factory to run device code
503 // Instead, we'll copy data into HostMirrors and run the algorithms on host, saving optimization for later.
504 typename Aggregates::LO_view ptr, nodes, unaggregated;
505 typename Aggregates::LO_view::HostMirror ptr_h, nodes_h, unaggregated_h;
506 } nodesInAgg;
507 aggregates->ComputeNodesInAggregate(nodesInAgg.ptr, nodesInAgg.nodes, nodesInAgg.unaggregated);
508 nodesInAgg.ptr_h = Kokkos::create_mirror_view(nodesInAgg.ptr);
509 nodesInAgg.nodes_h = Kokkos::create_mirror_view(nodesInAgg.nodes);
510 nodesInAgg.unaggregated_h = Kokkos::create_mirror_view(nodesInAgg.unaggregated);
511 Kokkos::deep_copy(nodesInAgg.ptr_h, nodesInAgg.ptr);
512 Kokkos::deep_copy(nodesInAgg.nodes_h, nodesInAgg.nodes);
513 Kokkos::deep_copy(nodesInAgg.unaggregated_h, nodesInAgg.unaggregated);
514 Teuchos::ArrayRCP<const LO> vertex2AggId = aggregates->GetVertex2AggId()->getData(0); // GH: this is needed on device, grab the pointer after we call ComputeNodesInAggregate
515
516 LO graphNumCols = G.GetImportMap()->getLocalNumElements();
517 Array<bool> filter(graphNumCols, false);
518
519 // Loop over the unaggregated nodes. Blitz those rows. We don't want to smooth singletons.
520 for(LO i=0; i< (LO)nodesInAgg.unaggregated_h.extent(0); i++) {
521 for (LO m = 0; m < (LO)blkSize; m++) {
522 LO row = amalgInfo->ComputeLocalDOF(nodesInAgg.unaggregated_h(i),m);
523 if (row >= (LO)numRows) continue;
524 size_t index_start = rowptr[row];
525 A.getLocalRowView(row, indsA, valsA);
526 for(LO k=0; k<(LO)indsA.size(); k++) {
527 if(row == indsA[k]) {
528 vals[index_start+k] = ONE;
529 diagIndex[row] = k;
530 }
531 else
532 vals[index_start+k] = ZERO;
533 }
534 }
535 }//end nodesInAgg.unaggregated.extent(0);
536
537
538 std::vector<LO> badCount(numAggs,0);
539
540 // Find the biggest aggregate size in *nodes*
541 LO maxAggSize=0;
542 for(LO i=0; i<numAggs; i++)
543 maxAggSize = std::max(maxAggSize,nodesInAgg.ptr_h(i+1) - nodesInAgg.ptr_h(i));
544
545
546 // Loop over all the aggregates
547 std::vector<LO> goodAggNeighbors(G.getLocalMaxNumRowEntries());
548 std::vector<LO> badAggNeighbors(std::min(G.getLocalMaxNumRowEntries()*maxAggSize,numNodes));
549
550 size_t numNewDrops=0;
551 size_t numOldDrops=0;
552 size_t numFixedDiags=0;
553 size_t numSymDrops = 0;
554
555 for(LO i=0; i<numAggs; i++) {
556 LO numNodesInAggregate = nodesInAgg.ptr_h(i+1) - nodesInAgg.ptr_h(i);
557 if(numNodesInAggregate == 0) continue;
558
559 // Find the root *node*
560 LO root_node = INVALID;
561 for (LO k=nodesInAgg.ptr_h(i); k < nodesInAgg.ptr_h(i+1); k++) {
562 if(aggregates->IsRoot(nodesInAgg.nodes_h(k))) {
563 root_node = nodesInAgg.nodes_h(k); break;
564 }
565 }
566
567 TEUCHOS_TEST_FOR_EXCEPTION(root_node == INVALID,
568 Exceptions::RuntimeError,"MueLu::FilteredAFactory::BuildNewUsingRootStencil: Cannot find root node");
569
570 // Find the list of "good" node neighbors (aka nodes which border the root node in the Graph G)
571 ArrayView<const LO> goodNodeNeighbors = G.getNeighborVertices(root_node);
572
573 // Now find the list of "good" aggregate neighbors (aka the aggregates neighbor the root node in the Graph G)
574 goodAggNeighbors.resize(0);
575 for(LO k=0; k<(LO) goodNodeNeighbors.size(); k++) {
576 goodAggNeighbors.push_back(vertex2AggId[goodNodeNeighbors[k]]);
577 }
578 sort_and_unique(goodAggNeighbors);
579
580 // Now we get the list of "bad" aggregate neighbors (aka aggregates which border the
581 // root node in the original matrix A, which are not goodNodeNeighbors). Since we
582 // don't have an amalgamated version of the original matrix, we use the matrix directly
583 badAggNeighbors.resize(0);
584 for(LO j = 0; j < (LO)blkSize; j++) {
585 LO row = amalgInfo->ComputeLocalDOF(root_node,j);
586 if (row >= (LO)numRows) continue;
587 A.getLocalRowView(row, indsA, valsA);
588 for(LO k=0; k<(LO)indsA.size(); k++) {
589 if ( (indsA[k] < (LO)numRows) && (TST::magnitude(valsA[k]) != TST::magnitude(ZERO))) {
590 LO node = amalgInfo->ComputeLocalNode(indsA[k]);
591 LO agg = vertex2AggId[node];
592 if(!std::binary_search(goodAggNeighbors.begin(),goodAggNeighbors.end(),agg))
593 badAggNeighbors.push_back(agg);
594 }
595 }
596 }
597 sort_and_unique(badAggNeighbors);
598
599 // Go through the filtered graph and count the number of connections to the badAggNeighbors
600 // if there are 2 or more of these connections, remove them from the bad list.
601
602 for (LO k=nodesInAgg.ptr_h(i); k < nodesInAgg.ptr_h(i+1); k++) {
603 ArrayView<const LO> nodeNeighbors = G.getNeighborVertices(k);
604 for (LO kk=0; kk < nodeNeighbors.size(); kk++) {
605 if ( (vertex2AggId[nodeNeighbors[kk]] >= 0) && (vertex2AggId[nodeNeighbors[kk]] < numAggs))
606 (badCount[vertex2AggId[nodeNeighbors[kk]]])++;
607 }
608 }
609 std::vector<LO> reallyBadAggNeighbors(std::min(G.getLocalMaxNumRowEntries()*maxAggSize,numNodes));
610 reallyBadAggNeighbors.resize(0);
611 for (LO k=0; k < (LO) badAggNeighbors.size(); k++) {
612 if (badCount[badAggNeighbors[k]] <= 1 ) reallyBadAggNeighbors.push_back(badAggNeighbors[k]);
613 }
614 for (LO k=nodesInAgg.ptr_h(i); k < nodesInAgg.ptr_h(i+1); k++) {
615 ArrayView<const LO> nodeNeighbors = G.getNeighborVertices(k);
616 for (LO kk=0; kk < nodeNeighbors.size(); kk++) {
617 if ( (vertex2AggId[nodeNeighbors[kk]] >= 0) && (vertex2AggId[nodeNeighbors[kk]] < numAggs))
618 badCount[vertex2AggId[nodeNeighbors[kk]]] = 0;
619 }
620 }
621
622 // For each of the reallyBadAggNeighbors, we go and blitz their connections to dofs in this aggregate.
623 // We remove the INVALID marker when we do this so we don't wind up doubling this up later
624 for(LO b=0; b<(LO)reallyBadAggNeighbors.size(); b++) {
625 LO bad_agg = reallyBadAggNeighbors[b];
626 for (LO k=nodesInAgg.ptr_h(bad_agg); k < nodesInAgg.ptr_h(bad_agg+1); k++) {
627 LO bad_node = nodesInAgg.nodes_h(k);
628 for(LO j = 0; j < (LO)blkSize; j++) {
629 LO bad_row = amalgInfo->ComputeLocalDOF(bad_node,j);
630 if (bad_row >= (LO)numRows) continue;
631 size_t index_start = rowptr[bad_row];
632 A.getLocalRowView(bad_row, indsA, valsA);
633 for(LO l = 0; l < (LO)indsA.size(); l++) {
634 if(indsA[l] < (LO)numRows && vertex2AggId[amalgInfo->ComputeLocalNode(indsA[l])] == i && vals_dropped_indicator[index_start+l] == false) {
635 vals_dropped_indicator[index_start + l] = true;
636 vals[index_start + l] = ZERO;
637 diagExtra[bad_row] += valsA[l];
638 numSymDrops++;
639 }
640 }
641 }
642 }
643 }
644
645 // Now lets fill the rows in this aggregate and figure out the diagonal lumping
646 // We loop over each node in the aggregate and then over the neighbors of that node
647
648 for(LO k=nodesInAgg.ptr_h(i); k<nodesInAgg.ptr_h(i+1); k++) {
649 LO row_node = nodesInAgg.nodes_h(k);
650
651 // Set up filtering array
652 ArrayView<const LO> indsG = G.getNeighborVertices(row_node);
653 for (size_t j = 0; j < as<size_t>(indsG.size()); j++)
654 filter[indsG[j]]=true;
655
656 for (LO m = 0; m < (LO)blkSize; m++) {
657 LO row = amalgInfo->ComputeLocalDOF(row_node,m);
658 if (row >= (LO)numRows) continue;
659 size_t index_start = rowptr[row];
660 A.getLocalRowView(row, indsA, valsA);
661
662 for(LO l = 0; l < (LO)indsA.size(); l++) {
663 int col_node = amalgInfo->ComputeLocalNode(indsA[l]);
664 bool is_good = filter[col_node];
665 if (indsA[l] == row) {
666 diagIndex[row] = l;
667 vals[index_start + l] = valsA[l];
668 continue;
669 }
670
671 // If we've already dropped this guy (from symmetry above), then continue onward
672 if(vals_dropped_indicator[index_start +l] == true) {
673 if(is_good) numOldDrops++;
674 else numNewDrops++;
675 continue;
676 }
677
678
679 // FIXME: I'm assuming vertex2AggId is only length of the rowmap, so
680 // we won'd do secondary dropping on off-processor neighbors
681 if(is_good && indsA[l] < (LO)numRows) {
682 int agg = vertex2AggId[col_node];
683 if(std::binary_search(reallyBadAggNeighbors.begin(),reallyBadAggNeighbors.end(),agg))
684 is_good = false;
685 }
686
687 if(is_good){
688 vals[index_start+l] = valsA[l];
689 }
690 else {
691 if(!filter[col_node]) numOldDrops++;
692 else numNewDrops++;
693 diagExtra[row] += valsA[l];
694 vals[index_start+l]=ZERO;
695 vals_dropped_indicator[index_start+l]=true;
696 }
697 } //end for l "indsA.size()" loop
698
699 }//end m "blkSize" loop
700
701 // Clear filtering array
702 for (size_t j = 0; j < as<size_t>(indsG.size()); j++)
703 filter[indsG[j]]=false;
704
705 }// end k loop over number of nodes in this agg
706 }//end i loop over numAggs
707
708 if (!use_spread_lumping) {
709 // Now do the diagonal modifications in one, final pass
710 for(LO row=0; row <(LO)numRows; row++) {
711 if (diagIndex[row] != INVALID) {
712 size_t index_start = rowptr[row];
713 size_t diagIndexInMatrix = index_start + diagIndex[row];
714 // printf("diag_vals pre update = %8.2e\n", vals[diagIndex] );
715 vals[diagIndexInMatrix] += diagExtra[row];
716 SC A_rowsum=ZERO, A_absrowsum = ZERO, F_rowsum = ZERO;
717
718
719 if( (dirichletThresh >= 0.0 && TST::real(vals[diagIndexInMatrix]) <= dirichletThresh) || TST::real(vals[diagIndexInMatrix]) == ZERO) {
720
722 A.getLocalRowView(row, indsA, valsA);
723 // SC diagA = valsA[diagIndex[row]];
724 // printf("WARNING: row %d (diagIndex=%d) diag(Afiltered) = %8.2e diag(A)=%8.2e numInds = %d\n",row,diagIndex[row],vals[diagIndexInMatrix],diagA,(LO)indsA.size());
725
726 for(LO l = 0; l < (LO)indsA.size(); l++) {
727 A_rowsum += valsA[l];
728 A_absrowsum+=std::abs(valsA[l]);
729 }
730 for(LO l = 0; l < (LO)indsA.size(); l++)
731 F_rowsum += vals[index_start+l];
732 // printf(" : A rowsum = %8.2e |A| rowsum = %8.2e rowsum = %8.2e\n",A_rowsum,A_absrowsum,F_rowsum);
734 // printf(" Avals =");
735 // for(LO l = 0; l < (LO)indsA.size(); l++)
736 // printf("%d(%8.2e)[%d] ",(LO)indsA[l],valsA[l],(LO)l);
737 // printf("\n");
738 // printf(" Fvals =");
739 // for(LO l = 0; l < (LO)indsA.size(); l++)
740 // if(vals[index_start+l] != ZERO)
741 // printf("%d(%8.2e)[%d] ",(LO)indsA[l],vals[index_start+l],(LO)l);
742 }
743 }
744 // Don't know what to do, so blitz the row and dump a one on the diagonal
745 for(size_t l=rowptr[row]; l<rowptr[row+1]; l++) {
746 vals[l] = ZERO;
747 }
748 vals[diagIndexInMatrix] = TST::one();
749 numFixedDiags++;
750 }
751 }
752 else {
753 GetOStream(Runtime0)<<"WARNING: Row "<<row<<" has no diagonal "<<std::endl;
754 }
755 }/*end row "numRows" loop"*/
756 }
757
758 // Copy all the goop out
759 for(LO row=0; row<(LO)numRows; row++) {
760 filteredA.replaceLocalValues(row, inds(rowptr[row],rowptr[row+1]-rowptr[row]), vals(rowptr[row],rowptr[row+1]-rowptr[row]));
761 }
762 if (use_spread_lumping) ExperimentalLumping(A, filteredA, DdomAllowGrowthRate, DdomCap);
763
764 size_t g_newDrops = 0, g_oldDrops = 0, g_fixedDiags=0;
765
766 MueLu_sumAll(A.getRowMap()->getComm(), numNewDrops, g_newDrops);
767 MueLu_sumAll(A.getRowMap()->getComm(), numOldDrops, g_oldDrops);
768 MueLu_sumAll(A.getRowMap()->getComm(), numFixedDiags, g_fixedDiags);
769 GetOStream(Runtime0)<< "Filtering out "<<g_newDrops<<" edges, in addition to the "<<g_oldDrops<<" edges dropped earlier"<<std::endl;
770 GetOStream(Runtime0)<< "Fixing "<< g_fixedDiags<<" zero diagonal values" <<std::endl;
771 }
772
773 // fancy lumping trying to not just move everything to the diagonal but to also consider moving
774 // some lumping to the kept off-diagonals. We basically aim to not increase the diagonal
775 // dominance in a row. In particular, the goal is that row i satisfies
776 //
777 // lumpedDiagDomMeasure_i <= rho2
778 // or
779 // lumpedDiagDomMeasure <= rho*unlumpedDiagDomMeasure
780 //
781 // NOTE: THIS CODE assumes direct access to a row. See comments above concerning
782 // ASSUME_DIRECT_ACCESS_TO_ROW
783 //
784 template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
786 ExperimentalLumping(const Matrix& A, Matrix& filteredA, double irho, double irho2) const {
787 using TST = typename Teuchos::ScalarTraits<SC>;
788 SC zero = TST::zero();
789 SC one = TST::one();
790
791 ArrayView<const LO> inds;
792 ArrayView<const SC> vals;
793 ArrayView<const LO> finds;
794 ArrayView<SC> fvals;
795
796 SC PosOffSum, NegOffSum, PosOffDropSum, NegOffDropSum;
797 SC diag, gamma, alpha;
798 LO NumPosKept, NumNegKept;
799
800 SC noLumpDdom;
801 SC numer,denom;
802 SC PosFilteredSum, NegFilteredSum;
803 SC Target;
804
805 SC rho = as<Scalar>(irho);
806 SC rho2 = as<Scalar>(irho2);
807
808 for (LO row = 0; row < (LO) A.getRowMap()->getLocalNumElements(); row++) {
809 noLumpDdom = as<Scalar>(10000.0); // only used if diagonal is zero
810 // the whole idea sort of breaks down
811 // when the diagonal is zero. In particular,
812 // the old diag dominance ratio is infinity
813 // ... so what do we want for the new ddom
814 // ratio. Do we want to allow the diagonal
815 // to go negative, just to have a better ddom
816 // ratio? This current choice essentially
817 // changes 'Target' to a large number
818 // meaning that we will allow the new
819 // ddom number to be fairly large (because
820 // the old one was infinity)
821
822 ArrayView<const SC> tvals;
823 A.getLocalRowView(row, inds, vals);
824 size_t nnz = inds.size();
825 if (nnz == 0) continue;
826 filteredA.getLocalRowView(row, finds, tvals);//assume 2 getLocalRowView()s
827 // have things in same order
828 fvals = ArrayView<SC>(const_cast<SC*>(tvals.getRawPtr()), nnz);
829
830 LO diagIndex = -1, fdiagIndex = -1;
831
832 PosOffSum=zero; NegOffSum=zero; PosOffDropSum=zero; NegOffDropSum=zero;
833 diag=zero; NumPosKept=0; NumNegKept=0;
834
835 // first record diagonal, offdiagonal sums and off diag dropped sums
836 for (size_t j = 0; j < nnz; j++) {
837 if (inds[j] == row) {
838 diagIndex = j;
839 diag = vals[j];
840 }
841 else { // offdiagonal
842 if (TST::real(vals[j]) > TST::real(zero) ) PosOffSum += vals[j];
843 else NegOffSum += vals[j];
844 }
845 }
846 PosOffDropSum = PosOffSum;
847 NegOffDropSum = NegOffSum;
848 NumPosKept = 0;
849 NumNegKept = 0;
850 LO j = 0;
851 for (size_t jj = 0; jj < (size_t) finds.size(); jj++) {
852 while( inds[j] != finds[jj] ) j++; // assumes that finds is in the same order as
853 // inds ... but perhaps has some entries missing
854 if (finds[jj] == row) fdiagIndex = jj;
855 else {
856 if (TST::real(vals[j]) > TST::real(zero) ) {
857 PosOffDropSum -= fvals[jj];
858 if (TST::real(fvals[jj]) != TST::real(zero) ) NumPosKept++;
859 }
860 else {
861 NegOffDropSum -= fvals[jj];
862 if (TST::real(fvals[jj]) != TST::real(zero) ) NumNegKept++;
863 }
864 }
865 }
866
867 // measure of diagonal dominance if no lumping is done.
868 if (TST::magnitude(diag) != TST::magnitude(zero) )
869 noLumpDdom = (PosOffSum - NegOffSum)/diag;
870
871 // Target is an acceptable diagonal dominance ratio
872 // which should really be larger than 1
873
874 Target = rho*noLumpDdom;
875 if (TST::magnitude(Target) <= TST::magnitude(rho)) Target = rho2;
876
877 PosFilteredSum = PosOffSum - PosOffDropSum;
878 NegFilteredSum = NegOffSum - NegOffDropSum;
879 // Note: PosNotFilterdSum is not equal to the sum of the
880 // positive entries after lumping. It just reflects the
881 // pos offdiag sum of the filtered matrix before lumping
882 // and does not account for negative dropped terms lumped
883 // to the positive kept terms.
884
885 // dropped positive offdiags always go to the diagonal as these
886 // always improve diagonal dominance.
887
888 diag += PosOffDropSum;
889
890 // now lets work on lumping dropped negative offdiags
891 gamma = -NegOffDropSum - PosFilteredSum;
892
893 if (TST::real(gamma) < TST::real(zero) ) {
894 // the total amount of negative dropping is less than PosFilteredSum,
895 // so we can distribute this dropping to pos offdiags. After lumping
896 // the sum of the pos offdiags is just -gamma so we just assign pos
897 // offdiags proportional to vals[j]/PosFilteredSum
898 // Note: in this case the diagonal is not changed as all lumping
899 // occurs to the pos offdiags
900
901 if (fdiagIndex != -1) fvals[fdiagIndex] = diag;
902 j = 0;
903 for(LO jj = 0; jj < (LO)finds.size(); jj++) {
904 while( inds[j] != finds[jj] ) j++; // assumes that finds is in the same order as
905 // inds ... but perhaps has some entries missing
906 if ((j != diagIndex)&&(TST::real(vals[j]) > TST::real(zero) ) && (TST::magnitude(fvals[jj]) != TST::magnitude(zero)))
907 fvals[jj] = -gamma*(vals[j]/PosFilteredSum);
908
909 }
910 }
911 else {
912 // So there are more negative values that need lumping than kept
913 // positive offdiags. Meaning there is enough negative lumping to
914 // completely clear out all pos offdiags. If we lump all negs
915 // to pos off diags, we'd actually change them to negative. We
916 // only do this if we are desperate. Otherwise, we'll clear out
917 // all the positive kept offdiags and try to lump the rest
918 // somewhere else. We defer the clearing of pos off diags
919 // to see first if we are going to be desperate.
920
921 bool flipPosOffDiagsToNeg = false;
922
923 // Even if we lumped by zeroing positive offdiags, we are still
924 // going to have more lumping to distribute to either
925 // 1) the diagonal
926 // 2) the kept negative offdiags
927 // 3) the kept positive offdiags (desperate)
928
929 // Let's first considering lumping the remaining neg offdiag stuff
930 // to the diagonal ... if this does not increase the diagonal
931 // dominance ratio too much (given by rho).
932
933 if (( TST::real(diag) > TST::real(gamma)) &&
934 ( TST::real((-NegFilteredSum)/(diag - gamma)) <= TST::real(Target))) {
935 // 1st if term above insures that resulting diagonal (=diag-gamma)
936 // is positive. . The left side of 2nd term is the diagonal dominance
937 // if we lump the remaining stuff (gamma) to the diagonal. Recall,
938 // that now there are no positive off-diags so the sum(abs(offdiags))
939 // is just the negative of NegFilteredSum
940
941 if (fdiagIndex != -1) fvals[fdiagIndex] = diag - gamma;
942 }
943 else if (NumNegKept > 0) {
944 // need to do some lumping to neg offdiags to avoid a large
945 // increase in diagonal dominance. We first compute alpha
946 // which measures how much gamma should go to the
947 // negative offdiags. The rest will go to the diagonal
948
949 numer = -NegFilteredSum - Target*(diag-gamma);
950 denom = gamma*(Target - TST::one());
951
952 // make sure that alpha is between 0 and 1 ... and that it doesn't
953 // result in a sign flip
954 // Note: when alpha is set to 1, then the diagonal is not modified
955 // and the negative offdiags just get shifted from those
956 // removed and those kept, meaning that the digaonal dominance
957 // should be the same as before
958 //
959 // can alpha be negative? It looks like denom should always
960 // be positive. The 'if' statement above
961 // Normally, diag-gamma should also be positive (but if it
962 // is negative then numer is guaranteed to be positve).
963 // look at the 'if' above,
964 // if (( TST::real(diag) > TST::real(gamma)) &&
965 // ( TST::real((-NegFilteredSum)/(diag - gamma)) <= TST::real(Target))) {
966 //
967 // Should guarantee that numer is positive. This is obvious when
968 // the second condition is false. When it is the first condition that
969 // is false, it follows that the two indiviudal terms in the numer
970 // formula must be positive.
971
972 if ( TST::magnitude(denom) < TST::magnitude(numer) ) alpha = TST::one();
973 else alpha = numer/denom;
974 if ( TST::real(alpha) < TST::real(zero)) alpha = zero;
975 if ( TST::real(diag) < TST::real((one-alpha)*gamma) ) alpha = TST::one();
976
977 // first change the diagonal
978
979 if (fdiagIndex != -1) fvals[fdiagIndex] = diag - (one-alpha)*gamma;
980
981 // after lumping the sum of neg offdiags will be NegFilteredSum
982 // + alpha*gamma. That is the remaining negative entries altered
983 // by the percent (=alpha) of stuff (=gamma) that needs to be
984 // lumped after taking into account lumping to pos offdiags
985
986 // Do this by assigning a fraction of NegFilteredSum+alpha*gamma
987 // proportional to vals[j]/NegFilteredSum
988
989 SC temp = (NegFilteredSum+alpha*gamma)/NegFilteredSum;
990 j = 0;
991 for(LO jj = 0; jj < (LO)finds.size(); jj++) {
992 while( inds[j] != finds[jj] ) j++; // assumes that finds is in the same order as
993 // inds ... but perhaps has some entries missing
994 if ( (jj != fdiagIndex)&&(TST::magnitude(fvals[jj]) != TST::magnitude(zero) ) &&
995 ( TST::real(vals[j]) < TST::real(zero) ) )
996 fvals[jj] = temp*vals[j];
997 }
998 }
999 else { // desperate case
1000 // So we don't have any kept negative offdiags ...
1001
1002 if (NumPosKept > 0) {
1003 // luckily we can push this stuff to the pos offdiags
1004 // which now makes them negative
1005 flipPosOffDiagsToNeg = true;
1006
1007 j = 0;
1008 for(LO jj = 0; jj < (LO)finds.size(); jj++) {
1009 while( inds[j] != finds[jj] ) j++; // assumes that finds is in the same order as
1010 // inds ... but perhaps has some entries missing
1011 if ( (j != diagIndex)&&(TST::magnitude(fvals[jj]) != TST::magnitude(zero) ) &&
1012 (TST::real(vals[j]) > TST::real(zero) ))
1013 fvals[jj] = -gamma/( (SC) NumPosKept);
1014 }
1015 }
1016 // else abandon rowsum preservation and do nothing
1017
1018 }
1019 if (!flipPosOffDiagsToNeg) { // not desperate so we now zero out
1020 // all pos terms including some
1021 // not originally filtered
1022 // but zeroed due to lumping
1023 j = 0;
1024 for(LO jj = 0; jj < (LO)finds.size(); jj++) {
1025 while( inds[j] != finds[jj] ) j++; // assumes that finds is in the same order as
1026 // inds ... but perhaps has some entries missing
1027 if ((jj != fdiagIndex)&& (TST::real(vals[j]) > TST::real(zero))) fvals[jj] = zero;
1028 }
1029 }
1030 } // positive gamma else
1031
1032 } //loop over all rows
1033 }
1034
1035
1036} //namespace MueLu
1037
1038#endif // MUELU_FILTEREDAFACTORY_DEF_HPP
#define SET_VALID_ENTRY(name)
#define MUELU_FILTEREDAFACTORY_LOTS_OF_PRINTING
#define MueLu_sumAll(rcpComm, in, out)
Exception throws to report errors in the internal logical of the program.
Timer to be used in factories. Similar to Monitor but with additional timers.
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
RCP< const ParameterList > GetValidParameterList() const
Return a const parameter list of valid parameters that setParameterList() will accept.
void DeclareInput(Level &currentLevel) const
Input.
void BuildNew(const Matrix &A, const GraphBase &G, const bool lumping, double dirichletThresh, Matrix &filteredA) const
void BuildNewUsingRootStencil(const Matrix &A, const GraphBase &G, double dirichletThresh, Level &currentLevel, Matrix &filteredA, bool use_spread_lumping, double DdomAllowGrowthRate, double DdomCap) const
void ExperimentalLumping(const Matrix &A, Matrix &filteredA, double rho, double rho2) const
void Build(Level &currentLevel) const
Build method.
void BuildReuse(const Matrix &A, const GraphBase &G, const bool lumping, double dirichletThresh, Matrix &filteredA) const
MueLu representation of a graph.
virtual const RCP< const Map > GetImportMap() const =0
virtual Teuchos::ArrayView< const LocalOrdinal > getNeighborVertices(LocalOrdinal v) const =0
Return the list of vertices adjacent to the vertex 'v'.
virtual size_t getLocalMaxNumRowEntries() const =0
virtual size_t GetNodeNumVertices() const =0
Return number of vertices owned by the calling node.
Class that holds all level-specific information.
virtual const Teuchos::ParameterList & GetParameterList() const
static bool MapsAreNested(const Xpetra::Map< DefaultLocalOrdinal, DefaultGlobalOrdinal, DefaultNode > &rowMap, const Xpetra::Map< DefaultLocalOrdinal, DefaultGlobalOrdinal, DefaultNode > &colMap)
Teuchos::FancyOStream & GetOStream(MsgType type, int thisProcRankOnly=0) const
Get an output stream for outputting the input message type.
Namespace for MueLu classes and methods.
@ Runtime0
One-liner description of what is happening.
void sort_and_unique(T &array)