Teko Version of the Day
Loading...
Searching...
No Matches
Teko_Utilities.cpp
1// @HEADER
2// *****************************************************************************
3// Teko: A package for block and physics based preconditioning
4//
5// Copyright 2010 NTESS and the Teko contributors.
6// SPDX-License-Identifier: BSD-3-Clause
7// *****************************************************************************
8// @HEADER
9
10#include "Teko_Config.h"
11#include "Teko_Utilities.hpp"
12
13// Thyra includes
14#include "Thyra_MultiVectorStdOps.hpp"
15#include "Thyra_ZeroLinearOpBase.hpp"
16#include "Thyra_DefaultDiagonalLinearOp.hpp"
17#include "Thyra_DefaultAddedLinearOp.hpp"
18#include "Thyra_DefaultScaledAdjointLinearOp.hpp"
19#include "Thyra_DefaultMultipliedLinearOp.hpp"
20#include "Thyra_DefaultZeroLinearOp.hpp"
21#include "Thyra_DefaultProductMultiVector.hpp"
22#include "Thyra_DefaultProductVectorSpace.hpp"
23#include "Thyra_MultiVectorStdOps.hpp"
24#include "Thyra_VectorStdOps.hpp"
25#include "Thyra_SpmdVectorBase.hpp"
26#include <utility>
27
28#ifdef TEKO_HAVE_EPETRA
29#include "Thyra_EpetraExtDiagScaledMatProdTransformer.hpp"
30#include "Thyra_EpetraExtDiagScalingTransformer.hpp"
31#include "Thyra_EpetraExtAddTransformer.hpp"
32#include "Thyra_get_Epetra_Operator.hpp"
33#include "Thyra_EpetraThyraWrappers.hpp"
34#include "Thyra_EpetraOperatorWrapper.hpp"
35#include "Thyra_EpetraLinearOp.hpp"
36#endif
37
38// Teuchos includes
39#include "Teuchos_Array.hpp"
40
41// Epetra includes
42#ifdef TEKO_HAVE_EPETRA
43#include "Epetra_Operator.h"
44#include "Epetra_CrsGraph.h"
45#include "Epetra_CrsMatrix.h"
46#include "Epetra_Vector.h"
47#include "Epetra_Map.h"
48
49#include "EpetraExt_Transpose_RowMatrix.h"
50#include "EpetraExt_MatrixMatrix.h"
51#include <EpetraExt_BlockMapOut.h>
52#include <EpetraExt_RowMatrixOut.h>
53
54#include "Teko_EpetraHelpers.hpp"
55#include "Teko_EpetraOperatorWrapper.hpp"
56#endif
57
58// Anasazi includes
59#include "AnasaziBasicEigenproblem.hpp"
60#include "AnasaziThyraAdapter.hpp"
61#include "AnasaziBlockKrylovSchurSolMgr.hpp"
62#include "AnasaziBlockKrylovSchur.hpp"
63#include "AnasaziStatusTestMaxIters.hpp"
64
65// Isorropia includes
66#ifdef Teko_ENABLE_Isorropia
67#include "Isorropia_EpetraProber.hpp"
68#endif
69
70// Teko includes
71#include "Teko_TpetraHelpers.hpp"
72#include "Teko_TpetraOperatorWrapper.hpp"
73
74// Tpetra
75#include "Thyra_TpetraLinearOp.hpp"
76#include "Tpetra_CrsMatrix.hpp"
77#include "Tpetra_Vector.hpp"
78#include "Thyra_TpetraThyraWrappers.hpp"
79#include "TpetraExt_MatrixMatrix.hpp"
80#include "Tpetra_RowMatrixTransposer.hpp"
81#include "MatrixMarket_Tpetra.hpp"
82
83#include <cmath>
84
85namespace Teko {
86
87using Teuchos::rcp;
88using Teuchos::RCP;
89using Teuchos::rcp_dynamic_cast;
90#ifdef Teko_ENABLE_Isorropia
91using Isorropia::Epetra::Prober;
92#endif
93
94const Teuchos::RCP<Teuchos::FancyOStream> getOutputStream() {
95 Teuchos::RCP<Teuchos::FancyOStream> os = Teuchos::VerboseObjectBase::getDefaultOStream();
96
97 // os->setShowProcRank(true);
98 // os->setOutputToRootOnly(-1);
99 return os;
100}
101
102// distance function...not parallel...entirely internal to this cpp file
103inline double dist(int dim, double *coords, int row, int col) {
104 double value = 0.0;
105 for (int i = 0; i < dim; i++)
106 value += std::pow(coords[dim * row + i] - coords[dim * col + i], 2.0);
107
108 // the distance between the two
109 return std::sqrt(value);
110}
111
112// distance function...not parallel...entirely internal to this cpp file
113inline double dist(double *x, double *y, double *z, int stride, int row, int col) {
114 double value = 0.0;
115 if (x != 0) value += std::pow(x[stride * row] - x[stride * col], 2.0);
116 if (y != 0) value += std::pow(y[stride * row] - y[stride * col], 2.0);
117 if (z != 0) value += std::pow(z[stride * row] - z[stride * col], 2.0);
118
119 // the distance between the two
120 return std::sqrt(value);
121}
122
141#ifdef TEKO_HAVE_EPETRA
142RCP<Epetra_CrsMatrix> buildGraphLaplacian(int dim, double *coords,
143 const Epetra_CrsMatrix &stencil) {
144 // allocate a new matrix with storage for the laplacian...in case of diagonals add one extra
145 // storage
146 RCP<Epetra_CrsMatrix> gl = rcp(
147 new Epetra_CrsMatrix(Copy, stencil.RowMap(), stencil.ColMap(), stencil.MaxNumEntries() + 1),
148 true);
149
150 // allocate an additional value for the diagonal, if neccessary
151 std::vector<double> rowData(stencil.GlobalMaxNumEntries() + 1);
152 std::vector<int> rowInd(stencil.GlobalMaxNumEntries() + 1);
153
154 // loop over all the rows
155 for (int j = 0; j < gl->NumMyRows(); j++) {
156 int row = gl->GRID(j);
157 double diagValue = 0.0;
158 int diagInd = -1;
159 int rowSz = 0;
160
161 // extract a copy of this row...put it in rowData, rowIndicies
162 stencil.ExtractGlobalRowCopy(row, stencil.MaxNumEntries(), rowSz, &rowData[0], &rowInd[0]);
163
164 // loop over elements of row
165 for (int i = 0; i < rowSz; i++) {
166 int col = rowInd[i];
167
168 // is this a 0 entry masquerading as some thing else?
169 double value = rowData[i];
170 if (value == 0) continue;
171
172 // for nondiagonal entries
173 if (row != col) {
174 double d = dist(dim, coords, row, col);
175 rowData[i] = -1.0 / d;
176 diagValue += rowData[i];
177 } else
178 diagInd = i;
179 }
180
181 // handle diagonal entry
182 if (diagInd < 0) { // diagonal not in row
183 rowData[rowSz] = -diagValue;
184 rowInd[rowSz] = row;
185 rowSz++;
186 } else { // diagonal in row
187 rowData[diagInd] = -diagValue;
188 rowInd[diagInd] = row;
189 }
190
191 // insert row data into graph Laplacian matrix
192 TEUCHOS_TEST_FOR_EXCEPT(gl->InsertGlobalValues(row, rowSz, &rowData[0], &rowInd[0]));
193 }
194
195 gl->FillComplete();
196
197 return gl;
198}
199#endif
200
201RCP<Tpetra::CrsMatrix<ST, LO, GO, NT>> buildGraphLaplacian(
202 int dim, ST *coords, const Tpetra::CrsMatrix<ST, LO, GO, NT> &stencil) {
203 // allocate a new matrix with storage for the laplacian...in case of diagonals add one extra
204 // storage
205 RCP<Tpetra::CrsMatrix<ST, LO, GO, NT>> gl = rcp(new Tpetra::CrsMatrix<ST, LO, GO, NT>(
206 stencil.getRowMap(), stencil.getColMap(), stencil.getGlobalMaxNumRowEntries() + 1));
207
208 // allocate an additional value for the diagonal, if neccessary
209 auto rowInd = typename Tpetra::CrsMatrix<ST, LO, GO, NT>::nonconst_global_inds_host_view_type(
210 Kokkos::ViewAllocateWithoutInitializing("rowIndices"),
211 stencil.getGlobalMaxNumRowEntries() + 1);
212 auto rowData = typename Tpetra::CrsMatrix<ST, LO, GO, NT>::nonconst_values_host_view_type(
213 Kokkos::ViewAllocateWithoutInitializing("rowIndices"),
214 stencil.getGlobalMaxNumRowEntries() + 1);
215
216 // loop over all the rows
217 for (LO j = 0; j < (LO)gl->getLocalNumRows(); j++) {
218 GO row = gl->getRowMap()->getGlobalElement(j);
219 ST diagValue = 0.0;
220 GO diagInd = -1;
221 size_t rowSz = 0;
222
223 // extract a copy of this row...put it in rowData, rowIndicies
224 stencil.getGlobalRowCopy(row, rowInd, rowData, rowSz);
225
226 // loop over elements of row
227 for (size_t i = 0; i < rowSz; i++) {
228 GO col = rowInd(i);
229
230 // is this a 0 entry masquerading as some thing else?
231 ST value = rowData[i];
232 if (value == 0) continue;
233
234 // for nondiagonal entries
235 if (row != col) {
236 ST d = dist(dim, coords, row, col);
237 rowData[i] = -1.0 / d;
238 diagValue += rowData(i);
239 } else
240 diagInd = i;
241 }
242
243 // handle diagonal entry
244 if (diagInd < 0) { // diagonal not in row
245 rowData(rowSz) = -diagValue;
246 rowInd(rowSz) = row;
247 rowSz++;
248 } else { // diagonal in row
249 rowData(diagInd) = -diagValue;
250 rowInd(diagInd) = row;
251 }
252
253 // insert row data into graph Laplacian matrix
254 gl->replaceGlobalValues(row, rowInd, rowData);
255 }
256
257 gl->fillComplete();
258
259 return gl;
260}
261
284#ifdef TEKO_HAVE_EPETRA
285RCP<Epetra_CrsMatrix> buildGraphLaplacian(double *x, double *y, double *z, int stride,
286 const Epetra_CrsMatrix &stencil) {
287 // allocate a new matrix with storage for the laplacian...in case of diagonals add one extra
288 // storage
289 RCP<Epetra_CrsMatrix> gl = rcp(
290 new Epetra_CrsMatrix(Copy, stencil.RowMap(), stencil.ColMap(), stencil.MaxNumEntries() + 1),
291 true);
292
293 // allocate an additional value for the diagonal, if neccessary
294 std::vector<double> rowData(stencil.GlobalMaxNumEntries() + 1);
295 std::vector<int> rowInd(stencil.GlobalMaxNumEntries() + 1);
296
297 // loop over all the rows
298 for (int j = 0; j < gl->NumMyRows(); j++) {
299 int row = gl->GRID(j);
300 double diagValue = 0.0;
301 int diagInd = -1;
302 int rowSz = 0;
303
304 // extract a copy of this row...put it in rowData, rowIndicies
305 stencil.ExtractGlobalRowCopy(row, stencil.MaxNumEntries(), rowSz, &rowData[0], &rowInd[0]);
306
307 // loop over elements of row
308 for (int i = 0; i < rowSz; i++) {
309 int col = rowInd[i];
310
311 // is this a 0 entry masquerading as some thing else?
312 double value = rowData[i];
313 if (value == 0) continue;
314
315 // for nondiagonal entries
316 if (row != col) {
317 double d = dist(x, y, z, stride, row, col);
318 rowData[i] = -1.0 / d;
319 diagValue += rowData[i];
320 } else
321 diagInd = i;
322 }
323
324 // handle diagonal entry
325 if (diagInd < 0) { // diagonal not in row
326 rowData[rowSz] = -diagValue;
327 rowInd[rowSz] = row;
328 rowSz++;
329 } else { // diagonal in row
330 rowData[diagInd] = -diagValue;
331 rowInd[diagInd] = row;
332 }
333
334 // insert row data into graph Laplacian matrix
335 TEUCHOS_TEST_FOR_EXCEPT(gl->InsertGlobalValues(row, rowSz, &rowData[0], &rowInd[0]));
336 }
337
338 gl->FillComplete();
339
340 return gl;
341}
342#endif
343
344RCP<Tpetra::CrsMatrix<ST, LO, GO, NT>> buildGraphLaplacian(
345 ST *x, ST *y, ST *z, GO stride, const Tpetra::CrsMatrix<ST, LO, GO, NT> &stencil) {
346 // allocate a new matrix with storage for the laplacian...in case of diagonals add one extra
347 // storage
348 RCP<Tpetra::CrsMatrix<ST, LO, GO, NT>> gl =
349 rcp(new Tpetra::CrsMatrix<ST, LO, GO, NT>(stencil.getRowMap(), stencil.getColMap(),
350 stencil.getGlobalMaxNumRowEntries() + 1),
351 true);
352
353 // allocate an additional value for the diagonal, if neccessary
354 auto rowInd = typename Tpetra::CrsMatrix<ST, LO, GO, NT>::nonconst_global_inds_host_view_type(
355 Kokkos::ViewAllocateWithoutInitializing("rowIndices"),
356 stencil.getGlobalMaxNumRowEntries() + 1);
357 auto rowData = typename Tpetra::CrsMatrix<ST, LO, GO, NT>::nonconst_values_host_view_type(
358 Kokkos::ViewAllocateWithoutInitializing("rowIndices"),
359 stencil.getGlobalMaxNumRowEntries() + 1);
360
361 // loop over all the rows
362 for (LO j = 0; j < (LO)gl->getLocalNumRows(); j++) {
363 GO row = gl->getRowMap()->getGlobalElement(j);
364 ST diagValue = 0.0;
365 GO diagInd = -1;
366 size_t rowSz = 0;
367
368 // extract a copy of this row...put it in rowData, rowIndicies
369 stencil.getGlobalRowCopy(row, rowInd, rowData, rowSz);
370
371 // loop over elements of row
372 for (size_t i = 0; i < rowSz; i++) {
373 GO col = rowInd(i);
374
375 // is this a 0 entry masquerading as some thing else?
376 ST value = rowData[i];
377 if (value == 0) continue;
378
379 // for nondiagonal entries
380 if (row != col) {
381 ST d = dist(x, y, z, stride, row, col);
382 rowData[i] = -1.0 / d;
383 diagValue += rowData(i);
384 } else
385 diagInd = i;
386 }
387
388 // handle diagonal entry
389 if (diagInd < 0) { // diagonal not in row
390 rowData(rowSz) = -diagValue;
391 rowInd(rowSz) = row;
392 rowSz++;
393 } else { // diagonal in row
394 rowData(diagInd) = -diagValue;
395 rowInd(diagInd) = row;
396 }
397
398 // insert row data into graph Laplacian matrix
399 gl->replaceGlobalValues(row, rowInd, rowData);
400 }
401
402 gl->fillComplete();
403
404 return gl;
405}
406
422void applyOp(const LinearOp &A, const MultiVector &x, MultiVector &y, double alpha, double beta) {
423 Thyra::apply(*A, Thyra::NOTRANS, *x, y.ptr(), alpha, beta);
424}
425
441void applyTransposeOp(const LinearOp &A, const MultiVector &x, MultiVector &y, double alpha,
442 double beta) {
443 Thyra::apply(*A, Thyra::TRANS, *x, y.ptr(), alpha, beta);
444}
445
448void update(double alpha, const MultiVector &x, double beta, MultiVector &y) {
449 Teuchos::Array<double> scale;
450 Teuchos::Array<Teuchos::Ptr<const Thyra::MultiVectorBase<double>>> vec;
451
452 // build arrays needed for linear combo
453 scale.push_back(alpha);
454 vec.push_back(x.ptr());
455
456 // compute linear combination
457 Thyra::linear_combination<double>(scale, vec, beta, y.ptr());
458}
459
461BlockedLinearOp getUpperTriBlocks(const BlockedLinearOp &blo, bool callEndBlockFill) {
462 int rows = blockRowCount(blo);
463
464 TEUCHOS_ASSERT(rows == blockColCount(blo));
465
466 RCP<const Thyra::ProductVectorSpaceBase<double>> range = blo->productRange();
467 RCP<const Thyra::ProductVectorSpaceBase<double>> domain = blo->productDomain();
468
469 // allocate new operator
470 BlockedLinearOp upper = createBlockedOp();
471
472 // build new operator
473 upper->beginBlockFill(rows, rows);
474
475 for (int i = 0; i < rows; i++) {
476 // put zero operators on the diagonal
477 // this gurantees the vector space of
478 // the new operator are fully defined
479 RCP<const Thyra::LinearOpBase<double>> zed =
480 Thyra::zero<double>(range->getBlock(i), domain->getBlock(i));
481 upper->setBlock(i, i, zed);
482
483 for (int j = i + 1; j < rows; j++) {
484 // get block i,j
485 LinearOp uij = blo->getBlock(i, j);
486
487 // stuff it in U
488 if (uij != Teuchos::null) upper->setBlock(i, j, uij);
489 }
490 }
491 if (callEndBlockFill) upper->endBlockFill();
492
493 return upper;
494}
495
497BlockedLinearOp getLowerTriBlocks(const BlockedLinearOp &blo, bool callEndBlockFill) {
498 int rows = blockRowCount(blo);
499
500 TEUCHOS_ASSERT(rows == blockColCount(blo));
501
502 RCP<const Thyra::ProductVectorSpaceBase<double>> range = blo->productRange();
503 RCP<const Thyra::ProductVectorSpaceBase<double>> domain = blo->productDomain();
504
505 // allocate new operator
506 BlockedLinearOp lower = createBlockedOp();
507
508 // build new operator
509 lower->beginBlockFill(rows, rows);
510
511 for (int i = 0; i < rows; i++) {
512 // put zero operators on the diagonal
513 // this gurantees the vector space of
514 // the new operator are fully defined
515 RCP<const Thyra::LinearOpBase<double>> zed =
516 Thyra::zero<double>(range->getBlock(i), domain->getBlock(i));
517 lower->setBlock(i, i, zed);
518
519 for (int j = 0; j < i; j++) {
520 // get block i,j
521 LinearOp uij = blo->getBlock(i, j);
522
523 // stuff it in U
524 if (uij != Teuchos::null) lower->setBlock(i, j, uij);
525 }
526 }
527 if (callEndBlockFill) lower->endBlockFill();
528
529 return lower;
530}
531
551BlockedLinearOp zeroBlockedOp(const BlockedLinearOp &blo) {
552 int rows = blockRowCount(blo);
553
554 TEUCHOS_ASSERT(rows == blockColCount(blo)); // assert that matrix is square
555
556 RCP<const Thyra::ProductVectorSpaceBase<double>> range = blo->productRange();
557 RCP<const Thyra::ProductVectorSpaceBase<double>> domain = blo->productDomain();
558
559 // allocate new operator
560 BlockedLinearOp zeroOp = createBlockedOp();
561
562 // build new operator
563 zeroOp->beginBlockFill(rows, rows);
564
565 for (int i = 0; i < rows; i++) {
566 // put zero operators on the diagonal
567 // this gurantees the vector space of
568 // the new operator are fully defined
569 RCP<const Thyra::LinearOpBase<double>> zed =
570 Thyra::zero<double>(range->getBlock(i), domain->getBlock(i));
571 zeroOp->setBlock(i, i, zed);
572 }
573
574 return zeroOp;
575}
576
578bool isZeroOp(const LinearOp op) {
579 // if operator is null...then its zero!
580 if (op == Teuchos::null) return true;
581
582 // try to cast it to a zero linear operator
583 LinearOp test = rcp_dynamic_cast<const Thyra::ZeroLinearOpBase<double>>(op);
584
585 // if it works...then its zero...otherwise its null
586 if (test != Teuchos::null) return true;
587
588 // See if the operator is a wrapped zero op
589 ST scalar = 0.0;
590 Thyra::EOpTransp transp = Thyra::NOTRANS;
591 RCP<const Thyra::LinearOpBase<ST>> wrapped_op;
592 Thyra::unwrap(op, &scalar, &transp, &wrapped_op);
593 test = rcp_dynamic_cast<const Thyra::ZeroLinearOpBase<double>>(wrapped_op);
594 return test != Teuchos::null;
595}
596
597std::pair<ModifiableLinearOp, bool> getAbsRowSumMatrixEpetra(const LinearOp &op) {
598#ifndef TEKO_HAVE_EPETRA
599 return std::make_pair(ModifiableLinearOp{}, false);
600#else
601 RCP<const Epetra_CrsMatrix> eCrsOp;
602
603 const auto eOp = rcp_dynamic_cast<const Thyra::EpetraLinearOp>(op);
604
605 if (!eOp) {
606 return std::make_pair(ModifiableLinearOp{}, false);
607 }
608
609 eCrsOp = rcp_dynamic_cast<const Epetra_CrsMatrix>(eOp->epetra_op(), true);
610
611 // extract diagonal
612 const auto ptrDiag = rcp(new Epetra_Vector(eCrsOp->RowMap()));
613 Epetra_Vector &diag = *ptrDiag;
614
615 // compute absolute value row sum
616 diag.PutScalar(0.0);
617 for (int i = 0; i < eCrsOp->NumMyRows(); i++) {
618 double *values = 0;
619 int numEntries;
620 eCrsOp->ExtractMyRowView(i, numEntries, values);
621
622 // build abs value row sum
623 for (int j = 0; j < numEntries; j++) diag[i] += std::abs(values[j]);
624 }
625
626 // build Thyra diagonal operator
627 return std::make_pair(Teko::Epetra::thyraDiagOp(ptrDiag, eCrsOp->RowMap(),
628 "absRowSum( " + op->getObjectLabel() + " )"),
629 true);
630#endif
631}
632
633std::pair<ModifiableLinearOp, bool> getAbsRowSumMatrixTpetra(const LinearOp &op) {
634 RCP<const Tpetra::CrsMatrix<ST, LO, GO, NT>> tCrsOp;
635
636 const auto tOp = rcp_dynamic_cast<const Thyra::TpetraLinearOp<ST, LO, GO, NT>>(op);
637
638 tCrsOp = rcp_dynamic_cast<const Tpetra::CrsMatrix<ST, LO, GO, NT>>(tOp->getConstTpetraOperator(),
639 true);
640
641 // extract diagonal
642 const auto ptrDiag = Tpetra::createVector<ST, LO, GO, NT>(tCrsOp->getRowMap());
643 auto &diag = *ptrDiag;
644
645 // compute absolute value row sum
646 diag.putScalar(0.0);
647 for (LO i = 0; i < (LO)tCrsOp->getLocalNumRows(); i++) {
648 auto numEntries = tCrsOp->getNumEntriesInLocalRow(i);
649 typename Tpetra::CrsMatrix<ST, LO, GO, NT>::local_inds_host_view_type indices;
650 typename Tpetra::CrsMatrix<ST, LO, GO, NT>::values_host_view_type values;
651 tCrsOp->getLocalRowView(i, indices, values);
652
653 // build abs value row sum
654 for (size_t j = 0; j < numEntries; j++) diag.sumIntoLocalValue(i, std::abs(values(j)));
655 }
656
657 // build Thyra diagonal operator
658 return std::make_pair(
659 Teko::TpetraHelpers::thyraDiagOp(ptrDiag, *tCrsOp->getRowMap(),
660 "absRowSum( " + op->getObjectLabel() + " ))"),
661 true);
662}
663
672ModifiableLinearOp getAbsRowSumMatrix(const LinearOp &op) {
673 try {
674 auto eResult = getAbsRowSumMatrixEpetra(op);
675 if (eResult.second) {
676 return eResult.first;
677 }
678
679 auto tResult = getAbsRowSumMatrixTpetra(op);
680 if (tResult.second) {
681 return tResult.first;
682 } else {
683 throw std::logic_error("Neither Epetra nor Tpetra");
684 }
685 } catch (std::exception &e) {
686 auto out = Teuchos::VerboseObjectBase::getDefaultOStream();
687
688 *out << "Teko: getAbsRowSumMatrix requires an Epetra_CrsMatrix or a "
689 "Tpetra::CrsMatrix\n";
690 *out << " Could not extract an Epetra_Operator or a Tpetra_Operator "
691 "from a \""
692 << op->description() << std::endl;
693 *out << " OR\n";
694 *out << " Could not cast an Epetra_Operator to a Epetra_CrsMatrix or "
695 "a Tpetra_Operator to a Tpetra::CrsMatrix\n";
696 *out << std::endl;
697 *out << "*** THROWN EXCEPTION ***\n";
698 *out << e.what() << std::endl;
699 *out << "************************\n";
700
701 throw e;
702 }
703}
704
713ModifiableLinearOp getAbsRowSumInvMatrix(const LinearOp &op) {
714 // if this is a blocked operator, extract diagonals block by block
715 // FIXME: this does not add in values from off-diagonal blocks
716 RCP<const Thyra::PhysicallyBlockedLinearOpBase<double>> blocked_op =
717 rcp_dynamic_cast<const Thyra::PhysicallyBlockedLinearOpBase<double>>(op);
718 if (blocked_op != Teuchos::null) {
719 int numRows = blocked_op->productRange()->numBlocks();
720 TEUCHOS_ASSERT(blocked_op->productDomain()->numBlocks() == numRows);
721 RCP<Thyra::PhysicallyBlockedLinearOpBase<double>> blocked_diag =
722 Thyra::defaultBlockedLinearOp<double>();
723 blocked_diag->beginBlockFill(numRows, numRows);
724 for (int r = 0; r < numRows; ++r) {
725 for (int c = 0; c < numRows; ++c) {
726 if (r == c)
727 blocked_diag->setNonconstBlock(r, c, getAbsRowSumInvMatrix(blocked_op->getBlock(r, c)));
728 else
729 blocked_diag->setBlock(r, c,
730 Thyra::zero<double>(blocked_op->getBlock(r, c)->range(),
731 blocked_op->getBlock(r, c)->domain()));
732 }
733 }
734 blocked_diag->endBlockFill();
735 return blocked_diag;
736 }
737
738 if (Teko::TpetraHelpers::isTpetraLinearOp(op)) {
739 ST scalar = 0.0;
740 bool transp = false;
741 RCP<const Tpetra::CrsMatrix<ST, LO, GO, NT>> tCrsOp =
742 Teko::TpetraHelpers::getTpetraCrsMatrix(op, &scalar, &transp);
743
744 // extract diagonal
745 const RCP<Tpetra::Vector<ST, LO, GO, NT>> ptrDiag =
746 Tpetra::createVector<ST, LO, GO, NT>(tCrsOp->getRowMap());
747 Tpetra::Vector<ST, LO, GO, NT> &diag = *ptrDiag;
748
749 // compute absolute value row sum
750 diag.putScalar(0.0);
751 for (LO i = 0; i < (LO)tCrsOp->getLocalNumRows(); i++) {
752 LO numEntries = tCrsOp->getNumEntriesInLocalRow(i);
753 typename Tpetra::CrsMatrix<ST, LO, GO, NT>::local_inds_host_view_type indices;
754 typename Tpetra::CrsMatrix<ST, LO, GO, NT>::values_host_view_type values;
755 tCrsOp->getLocalRowView(i, indices, values);
756
757 // build abs value row sum
758 for (LO j = 0; j < numEntries; j++) diag.sumIntoLocalValue(i, std::abs(values(j)));
759 }
760 diag.scale(scalar);
761 diag.reciprocal(diag); // invert entries
762
763 // build Thyra diagonal operator
764 return Teko::TpetraHelpers::thyraDiagOp(ptrDiag, *tCrsOp->getRowMap(),
765 "absRowSum( " + op->getObjectLabel() + " ))");
766
767 } else {
768#ifdef TEKO_HAVE_EPETRA
769 RCP<const Thyra::EpetraLinearOp> eOp = rcp_dynamic_cast<const Thyra::EpetraLinearOp>(op, true);
770 RCP<const Epetra_CrsMatrix> eCrsOp =
771 rcp_dynamic_cast<const Epetra_CrsMatrix>(eOp->epetra_op(), true);
772
773 // extract diagonal
774 const RCP<Epetra_Vector> ptrDiag = rcp(new Epetra_Vector(eCrsOp->RowMap()));
775 Epetra_Vector &diag = *ptrDiag;
776
777 // compute absolute value row sum
778 diag.PutScalar(0.0);
779 for (int i = 0; i < eCrsOp->NumMyRows(); i++) {
780 double *values = 0;
781 int numEntries;
782 eCrsOp->ExtractMyRowView(i, numEntries, values);
783
784 // build abs value row sum
785 for (int j = 0; j < numEntries; j++) diag[i] += std::abs(values[j]);
786 }
787 diag.Reciprocal(diag); // invert entries
788
789 // build Thyra diagonal operator
790 return Teko::Epetra::thyraDiagOp(ptrDiag, eCrsOp->RowMap(),
791 "absRowSum( " + op->getObjectLabel() + " )");
792#else
793 throw std::logic_error(
794 "getAbsRowSumInvMatrix is trying to use Epetra "
795 "code, but TEKO_HAVE_EPETRA is disabled!");
796#endif
797 }
798}
799
807ModifiableLinearOp getLumpedMatrix(const LinearOp &op) {
808 RCP<Thyra::VectorBase<ST>> ones = Thyra::createMember(op->domain());
809 RCP<Thyra::VectorBase<ST>> diag = Thyra::createMember(op->range());
810
811 // set to all ones
812 Thyra::assign(ones.ptr(), 1.0);
813
814 // compute lumped diagonal
815 // Thyra::apply(*op,Thyra::NONCONJ_ELE,*ones,&*diag);
816 Thyra::apply(*op, Thyra::NOTRANS, *ones, diag.ptr());
817
818 return rcp(new Thyra::DefaultDiagonalLinearOp<ST>(diag));
819}
820
829ModifiableLinearOp getInvLumpedMatrix(const LinearOp &op) {
830 RCP<Thyra::VectorBase<ST>> ones = Thyra::createMember(op->domain());
831 RCP<Thyra::VectorBase<ST>> diag = Thyra::createMember(op->range());
832
833 // set to all ones
834 Thyra::assign(ones.ptr(), 1.0);
835
836 // compute lumped diagonal
837 Thyra::apply(*op, Thyra::NOTRANS, *ones, diag.ptr());
838 Thyra::reciprocal(*diag, diag.ptr());
839
840 return rcp(new Thyra::DefaultDiagonalLinearOp<ST>(diag));
841}
842
843const std::pair<ModifiableLinearOp, bool> getDiagonalOpEpetra(const LinearOp &op) {
844#ifndef TEKO_HAVE_EPETRA
845 return std::make_pair(ModifiableLinearOp{}, false);
846#else
847 RCP<const Epetra_CrsMatrix> eCrsOp;
848
849 const auto eOp = rcp_dynamic_cast<const Thyra::EpetraLinearOp>(op);
850 if (!eOp) {
851 return std::make_pair(ModifiableLinearOp{}, false);
852 }
853
854 eCrsOp = rcp_dynamic_cast<const Epetra_CrsMatrix>(eOp->epetra_op(), true);
855
856 // extract diagonal
857 const auto diag = rcp(new Epetra_Vector(eCrsOp->RowMap()));
858 TEUCHOS_TEST_FOR_EXCEPT(eCrsOp->ExtractDiagonalCopy(*diag));
859
860 // build Thyra diagonal operator
861 return std::make_pair(Teko::Epetra::thyraDiagOp(diag, eCrsOp->RowMap(),
862 "inv(diag( " + op->getObjectLabel() + " ))"),
863 true);
864#endif
865}
866
867const std::pair<ModifiableLinearOp, bool> getDiagonalOpTpetra(const LinearOp &op) {
868 RCP<const Tpetra::CrsMatrix<ST, LO, GO, NT>> tCrsOp;
869
870 const auto tOp = rcp_dynamic_cast<const Thyra::TpetraLinearOp<ST, LO, GO, NT>>(op);
871 if (!tOp) {
872 return std::make_pair(ModifiableLinearOp{}, false);
873 }
874
875 tCrsOp = rcp_dynamic_cast<const Tpetra::CrsMatrix<ST, LO, GO, NT>>(tOp->getConstTpetraOperator(),
876 true);
877
878 // extract diagonal
879 const auto diag = Tpetra::createVector<ST, LO, GO, NT>(tCrsOp->getRowMap());
880 tCrsOp->getLocalDiagCopy(*diag);
881
882 // build Thyra diagonal operator
883 return std::make_pair(
884 Teko::TpetraHelpers::thyraDiagOp(diag, *tCrsOp->getRowMap(),
885 "inv(diag( " + op->getObjectLabel() + " ))"),
886 true);
887}
888
900const ModifiableLinearOp getDiagonalOp(const LinearOp &op) {
901 try {
902 // get Epetra or Tpetra Operator
903 const auto eDiagOp = getDiagonalOpEpetra(op);
904
905 if (eDiagOp.second) {
906 return eDiagOp.first;
907 }
908
909 const auto tDiagOp = getDiagonalOpTpetra(op);
910 if (tDiagOp.second) {
911 return tDiagOp.first;
912 } else
913 throw std::logic_error("Neither Epetra nor Tpetra");
914 } catch (std::exception &e) {
915 RCP<Teuchos::FancyOStream> out = Teuchos::VerboseObjectBase::getDefaultOStream();
916
917 *out << "Teko: getDiagonalOp requires an Epetra_CrsMatrix or a Tpetra::CrsMatrix\n";
918 *out << " Could not extract an Epetra_Operator or a Tpetra_Operator from a \""
919 << op->description() << std::endl;
920 *out << " OR\n";
921 *out << " Could not cast an Epetra_Operator to a Epetra_CrsMatrix or a Tpetra_Operator to a "
922 "Tpetra::CrsMatrix\n";
923 *out << std::endl;
924 *out << "*** THROWN EXCEPTION ***\n";
925 *out << e.what() << std::endl;
926 *out << "************************\n";
927
928 throw e;
929 }
930}
931
932const MultiVector getDiagonal(const LinearOp &op) {
933 try {
934 // get Epetra or Tpetra Operator
935 auto diagOp = getDiagonalOpEpetra(op);
936
937 if (!diagOp.second) {
938 diagOp = getDiagonalOpTpetra(op);
939
940 if (!diagOp.second) {
941 throw std::logic_error("Neither Epetra nor Tpetra");
942 }
943 }
944
945 Teuchos::RCP<const Thyra::MultiVectorBase<double>> v =
946 Teuchos::rcp_dynamic_cast<const Thyra::DiagonalLinearOpBase<double>>(diagOp.first)
947 ->getDiag();
948 return Teuchos::rcp_const_cast<Thyra::MultiVectorBase<double>>(v);
949 } catch (std::exception &e) {
950 RCP<Teuchos::FancyOStream> out = Teuchos::VerboseObjectBase::getDefaultOStream();
951
952 *out << "Teko: getDiagonal requires an Epetra_CrsMatrix or a Tpetra::CrsMatrix\n";
953 *out << " Could not extract an Epetra_Operator or a Tpetra_Operator from a \""
954 << op->description() << std::endl;
955 *out << " OR\n";
956 *out << " Could not cast an Epetra_Operator to a Epetra_CrsMatrix or a Tpetra_Operator to a "
957 "Tpetra::CrsMatrix\n";
958 *out << std::endl;
959 *out << "*** THROWN EXCEPTION ***\n";
960 *out << e.what() << std::endl;
961 *out << "************************\n";
962
963 throw e;
964 }
965}
966
967const MultiVector getDiagonal(const Teko::LinearOp &A, const DiagonalType &dt) {
968 LinearOp diagOp = Teko::getDiagonalOp(A, dt);
969
970 Teuchos::RCP<const Thyra::MultiVectorBase<double>> v =
971 Teuchos::rcp_dynamic_cast<const Thyra::DiagonalLinearOpBase<double>>(diagOp)->getDiag();
972 return Teuchos::rcp_const_cast<Thyra::MultiVectorBase<double>>(v);
973}
974
986const ModifiableLinearOp getInvDiagonalOp(const LinearOp &op) {
987 // if this is a diagonal linear op already, just take the reciprocal
988 auto diagonal_op = rcp_dynamic_cast<const Thyra::DiagonalLinearOpBase<double>>(op);
989 if (diagonal_op != Teuchos::null) {
990 auto diag = diagonal_op->getDiag();
991 auto inv_diag = diag->clone_v();
992 Thyra::reciprocal(*diag, inv_diag.ptr());
993 return rcp(new Thyra::DefaultDiagonalLinearOp<double>(inv_diag));
994 }
995
996 // if this is a blocked operator, extract diagonals block by block
997 RCP<const Thyra::PhysicallyBlockedLinearOpBase<double>> blocked_op =
998 rcp_dynamic_cast<const Thyra::PhysicallyBlockedLinearOpBase<double>>(op);
999 if (blocked_op != Teuchos::null) {
1000 int numRows = blocked_op->productRange()->numBlocks();
1001 TEUCHOS_ASSERT(blocked_op->productDomain()->numBlocks() == numRows);
1002 RCP<Thyra::PhysicallyBlockedLinearOpBase<double>> blocked_diag =
1003 Thyra::defaultBlockedLinearOp<double>();
1004 blocked_diag->beginBlockFill(numRows, numRows);
1005 for (int r = 0; r < numRows; ++r) {
1006 for (int c = 0; c < numRows; ++c) {
1007 if (r == c)
1008 blocked_diag->setNonconstBlock(r, c, getInvDiagonalOp(blocked_op->getBlock(r, c)));
1009 else
1010 blocked_diag->setBlock(r, c,
1011 Thyra::zero<double>(blocked_op->getBlock(r, c)->range(),
1012 blocked_op->getBlock(r, c)->domain()));
1013 }
1014 }
1015 blocked_diag->endBlockFill();
1016 return blocked_diag;
1017 }
1018
1019 if (Teko::TpetraHelpers::isTpetraLinearOp(op)) {
1020 ST scalar = 0.0;
1021 bool transp = false;
1022 RCP<const Tpetra::CrsMatrix<ST, LO, GO, NT>> tCrsOp =
1023 Teko::TpetraHelpers::getTpetraCrsMatrix(op, &scalar, &transp);
1024
1025 // extract diagonal
1026 const RCP<Tpetra::Vector<ST, LO, GO, NT>> diag =
1027 Tpetra::createVector<ST, LO, GO, NT>(tCrsOp->getRowMap());
1028 diag->scale(scalar);
1029 tCrsOp->getLocalDiagCopy(*diag);
1030 diag->reciprocal(*diag);
1031
1032 // build Thyra diagonal operator
1033 return Teko::TpetraHelpers::thyraDiagOp(diag, *tCrsOp->getRowMap(),
1034 "inv(diag( " + op->getObjectLabel() + " ))");
1035
1036 } else {
1037#ifdef TEKO_HAVE_EPETRA
1038 RCP<const Thyra::EpetraLinearOp> eOp = rcp_dynamic_cast<const Thyra::EpetraLinearOp>(op, true);
1039 RCP<const Epetra_CrsMatrix> eCrsOp =
1040 rcp_dynamic_cast<const Epetra_CrsMatrix>(eOp->epetra_op(), true);
1041
1042 // extract diagonal
1043 const RCP<Epetra_Vector> diag = rcp(new Epetra_Vector(eCrsOp->RowMap()));
1044 TEUCHOS_TEST_FOR_EXCEPT(eCrsOp->ExtractDiagonalCopy(*diag));
1045 diag->Reciprocal(*diag);
1046
1047 // build Thyra diagonal operator
1048 return Teko::Epetra::thyraDiagOp(diag, eCrsOp->RowMap(),
1049 "inv(diag( " + op->getObjectLabel() + " ))");
1050#else
1051 throw std::logic_error(
1052 "getInvDiagonalOp is trying to use Epetra "
1053 "code, but TEKO_HAVE_EPETRA is disabled!");
1054#endif
1055 }
1056}
1057
1070const LinearOp explicitMultiply(const LinearOp &opl, const LinearOp &opm, const LinearOp &opr) {
1071 // if this is a blocked operator, multiply block by block
1072 // it is possible that not every factor in the product is blocked and these situations are handled
1073 // separately
1074
1075 bool isBlockedL = isPhysicallyBlockedLinearOp(opl);
1076 bool isBlockedM = isPhysicallyBlockedLinearOp(opm);
1077 bool isBlockedR = isPhysicallyBlockedLinearOp(opr);
1078
1079 // all factors blocked
1080 if ((isBlockedL && isBlockedM && isBlockedR)) {
1081 double scalarl = 0.0;
1082 bool transpl = false;
1083 RCP<const Thyra::PhysicallyBlockedLinearOpBase<double>> blocked_opl =
1084 getPhysicallyBlockedLinearOp(opl, &scalarl, &transpl);
1085 double scalarm = 0.0;
1086 bool transpm = false;
1087 RCP<const Thyra::PhysicallyBlockedLinearOpBase<double>> blocked_opm =
1088 getPhysicallyBlockedLinearOp(opm, &scalarm, &transpm);
1089 double scalarr = 0.0;
1090 bool transpr = false;
1091 RCP<const Thyra::PhysicallyBlockedLinearOpBase<double>> blocked_opr =
1092 getPhysicallyBlockedLinearOp(opr, &scalarr, &transpr);
1093 double scalar = scalarl * scalarm * scalarr;
1094
1095 int numRows = blocked_opl->productRange()->numBlocks();
1096 int numCols = blocked_opr->productDomain()->numBlocks();
1097 int numMiddle = blocked_opm->productRange()->numBlocks();
1098
1099 // Assume that the middle block is block nxn and that it's diagonal. Otherwise use the two
1100 // argument explicitMultiply twice
1101 TEUCHOS_ASSERT(blocked_opm->productDomain()->numBlocks() == numMiddle);
1102 TEUCHOS_ASSERT(blocked_opl->productDomain()->numBlocks() == numMiddle);
1103 TEUCHOS_ASSERT(blocked_opr->productRange()->numBlocks() == numMiddle);
1104
1105 RCP<Thyra::PhysicallyBlockedLinearOpBase<double>> blocked_product =
1106 Thyra::defaultBlockedLinearOp<double>();
1107 blocked_product->beginBlockFill(numRows, numCols);
1108 for (int r = 0; r < numRows; ++r) {
1109 for (int c = 0; c < numCols; ++c) {
1110 LinearOp product_rc = explicitMultiply(
1111 blocked_opl->getBlock(r, 0), blocked_opm->getBlock(0, 0), blocked_opr->getBlock(0, c));
1112 for (int m = 1; m < numMiddle; ++m) {
1113 LinearOp product_m =
1114 explicitMultiply(blocked_opl->getBlock(r, m), blocked_opm->getBlock(m, m),
1115 blocked_opr->getBlock(m, c));
1116 product_rc = explicitAdd(product_rc, product_m);
1117 }
1118 blocked_product->setBlock(r, c, product_rc);
1119 }
1120 }
1121 blocked_product->endBlockFill();
1122 return Thyra::scale<double>(scalar, blocked_product.getConst());
1123 }
1124
1125 // left and right factors blocked
1126 if (isBlockedL && !isBlockedM && isBlockedR) {
1127 double scalarl = 0.0;
1128 bool transpl = false;
1129 RCP<const Thyra::PhysicallyBlockedLinearOpBase<double>> blocked_opl =
1130 getPhysicallyBlockedLinearOp(opl, &scalarl, &transpl);
1131 double scalarr = 0.0;
1132 bool transpr = false;
1133 RCP<const Thyra::PhysicallyBlockedLinearOpBase<double>> blocked_opr =
1134 getPhysicallyBlockedLinearOp(opr, &scalarr, &transpr);
1135 double scalar = scalarl * scalarr;
1136
1137 int numRows = blocked_opl->productRange()->numBlocks();
1138 int numCols = blocked_opr->productDomain()->numBlocks();
1139 int numMiddle = 1;
1140
1141 // Assume that the middle block is 1x1 diagonal. Left must be rx1, right 1xc
1142 TEUCHOS_ASSERT(blocked_opl->productDomain()->numBlocks() == numMiddle);
1143 TEUCHOS_ASSERT(blocked_opr->productRange()->numBlocks() == numMiddle);
1144
1145 RCP<Thyra::PhysicallyBlockedLinearOpBase<double>> blocked_product =
1146 Thyra::defaultBlockedLinearOp<double>();
1147 blocked_product->beginBlockFill(numRows, numCols);
1148 for (int r = 0; r < numRows; ++r) {
1149 for (int c = 0; c < numCols; ++c) {
1150 LinearOp product_rc =
1151 explicitMultiply(blocked_opl->getBlock(r, 0), opm, blocked_opr->getBlock(0, c));
1152 blocked_product->setBlock(r, c, product_rc);
1153 }
1154 }
1155 blocked_product->endBlockFill();
1156 return Thyra::scale<double>(scalar, blocked_product.getConst());
1157 }
1158
1159 // only right factor blocked
1160 if (!isBlockedL && !isBlockedM && isBlockedR) {
1161 double scalarr = 0.0;
1162 bool transpr = false;
1163 RCP<const Thyra::PhysicallyBlockedLinearOpBase<double>> blocked_opr =
1164 getPhysicallyBlockedLinearOp(opr, &scalarr, &transpr);
1165 double scalar = scalarr;
1166
1167 int numRows = 1;
1168 int numCols = blocked_opr->productDomain()->numBlocks();
1169 int numMiddle = 1;
1170
1171 // Assume that the middle block is 1x1 diagonal, left is 1x1. Right must be 1xc
1172 TEUCHOS_ASSERT(blocked_opr->productRange()->numBlocks() == numMiddle);
1173
1174 RCP<Thyra::PhysicallyBlockedLinearOpBase<double>> blocked_product =
1175 Thyra::defaultBlockedLinearOp<double>();
1176 blocked_product->beginBlockFill(numRows, numCols);
1177 for (int c = 0; c < numCols; ++c) {
1178 LinearOp product_c = explicitMultiply(opl, opm, blocked_opr->getBlock(0, c));
1179 blocked_product->setBlock(0, c, product_c);
1180 }
1181 blocked_product->endBlockFill();
1182 return Thyra::scale<double>(scalar, blocked_product.getConst());
1183 }
1184
1185 // TODO: three more cases (only non-blocked - blocked - non-blocked not possible)
1186
1187 bool isTpetral = Teko::TpetraHelpers::isTpetraLinearOp(opl);
1188 bool isTpetram = Teko::TpetraHelpers::isTpetraLinearOp(opm);
1189 bool isTpetrar = Teko::TpetraHelpers::isTpetraLinearOp(opr);
1190
1191 if (isTpetral && isTpetram &&
1192 isTpetrar) { // Both operators are Tpetra matrices so explicitly multiply them
1193
1194 // Get left and right Tpetra crs operators
1195 ST scalarl = 0.0;
1196 bool transpl = false;
1197 RCP<const Tpetra::CrsMatrix<ST, LO, GO, NT>> tCrsOpl =
1198 Teko::TpetraHelpers::getTpetraCrsMatrix(opl, &scalarl, &transpl);
1199 ST scalarm = 0.0;
1200 bool transpm = false;
1201 RCP<const Tpetra::CrsMatrix<ST, LO, GO, NT>> tCrsOpm =
1202 Teko::TpetraHelpers::getTpetraCrsMatrix(opl, &scalarm, &transpm);
1203 ST scalarr = 0.0;
1204 bool transpr = false;
1205 RCP<const Tpetra::CrsMatrix<ST, LO, GO, NT>> tCrsOpr =
1206 Teko::TpetraHelpers::getTpetraCrsMatrix(opr, &scalarr, &transpr);
1207
1208 // Build output operator
1209 RCP<Thyra::LinearOpBase<ST>> explicitOp = rcp(new Thyra::TpetraLinearOp<ST, LO, GO, NT>());
1210 RCP<Thyra::TpetraLinearOp<ST, LO, GO, NT>> tExplicitOp =
1211 rcp_dynamic_cast<Thyra::TpetraLinearOp<ST, LO, GO, NT>>(explicitOp);
1212
1213 // Do explicit matrix-matrix multiply
1214 RCP<Tpetra::CrsMatrix<ST, LO, GO, NT>> tCrsOplm =
1215 Tpetra::createCrsMatrix<ST, LO, GO, NT>(tCrsOpl->getRowMap());
1216 RCP<Tpetra::CrsMatrix<ST, LO, GO, NT>> explicitCrsOp =
1217 Tpetra::createCrsMatrix<ST, LO, GO, NT>(tCrsOpl->getRowMap());
1218 Tpetra::MatrixMatrix::Multiply<ST, LO, GO, NT>(*tCrsOpl, transpl, *tCrsOpm, transpm, *tCrsOplm);
1219 Tpetra::MatrixMatrix::Multiply<ST, LO, GO, NT>(*tCrsOplm, false, *tCrsOpr, transpr,
1220 *explicitCrsOp);
1221 explicitCrsOp->resumeFill();
1222 explicitCrsOp->scale(scalarl * scalarm * scalarr);
1223 explicitCrsOp->fillComplete(tCrsOpr->getDomainMap(), tCrsOpl->getRangeMap());
1224 tExplicitOp->initialize(Thyra::tpetraVectorSpace<ST, LO, GO, NT>(explicitCrsOp->getRangeMap()),
1225 Thyra::tpetraVectorSpace<ST, LO, GO, NT>(explicitCrsOp->getDomainMap()),
1226 explicitCrsOp);
1227 return tExplicitOp;
1228
1229 } else if (isTpetral && !isTpetram && isTpetrar) { // Assume that the middle operator is diagonal
1230
1231 // Get left and right Tpetra crs operators
1232 ST scalarl = 0.0;
1233 bool transpl = false;
1234 RCP<const Tpetra::CrsMatrix<ST, LO, GO, NT>> tCrsOpl =
1235 Teko::TpetraHelpers::getTpetraCrsMatrix(opl, &scalarl, &transpl);
1236 ST scalarr = 0.0;
1237 bool transpr = false;
1238 RCP<const Tpetra::CrsMatrix<ST, LO, GO, NT>> tCrsOpr =
1239 Teko::TpetraHelpers::getTpetraCrsMatrix(opr, &scalarr, &transpr);
1240
1241 RCP<const Tpetra::Vector<ST, LO, GO, NT>> diagPtr;
1242
1243 // Cast middle operator as DiagonalLinearOp and extract diagonal as Vector
1244 RCP<const Thyra::DiagonalLinearOpBase<ST>> dOpm =
1245 rcp_dynamic_cast<const Thyra::DiagonalLinearOpBase<ST>>(opm);
1246 if (dOpm != Teuchos::null) {
1247 RCP<const Thyra::TpetraVector<ST, LO, GO, NT>> tPtr =
1248 rcp_dynamic_cast<const Thyra::TpetraVector<ST, LO, GO, NT>>(dOpm->getDiag(), true);
1249 diagPtr = rcp_dynamic_cast<const Tpetra::Vector<ST, LO, GO, NT>>(tPtr->getConstTpetraVector(),
1250 true);
1251 }
1252 // If it's not diagonal, maybe it's zero
1253 else if (rcp_dynamic_cast<const Thyra::ZeroLinearOpBase<ST>>(opm) != Teuchos::null) {
1254 diagPtr = rcp(new Tpetra::Vector<ST, LO, GO, NT>(tCrsOpl->getDomainMap()));
1255 } else
1256 TEUCHOS_ASSERT(false);
1257
1258 RCP<Tpetra::CrsMatrix<ST, LO, GO, NT>> tCrsOplm =
1259 Tpetra::importAndFillCompleteCrsMatrix<Tpetra::CrsMatrix<ST, LO, GO, NT>>(
1260 tCrsOpl, Tpetra::Import<LO, GO, NT>(tCrsOpl->getRowMap(), tCrsOpl->getRowMap()));
1261
1262 // Do the diagonal scaling
1263 tCrsOplm->rightScale(*diagPtr);
1264
1265 // Build output operator
1266 RCP<Thyra::LinearOpBase<ST>> explicitOp = rcp(new Thyra::TpetraLinearOp<ST, LO, GO, NT>());
1267 RCP<Thyra::TpetraLinearOp<ST, LO, GO, NT>> tExplicitOp =
1268 rcp_dynamic_cast<Thyra::TpetraLinearOp<ST, LO, GO, NT>>(explicitOp);
1269
1270 // Do explicit matrix-matrix multiply
1271 RCP<Tpetra::CrsMatrix<ST, LO, GO, NT>> explicitCrsOp =
1272 Tpetra::createCrsMatrix<ST, LO, GO, NT>(tCrsOpl->getRowMap());
1273 Tpetra::MatrixMatrix::Multiply<ST, LO, GO, NT>(*tCrsOplm, false, *tCrsOpr, transpr,
1274 *explicitCrsOp);
1275 explicitCrsOp->resumeFill();
1276 explicitCrsOp->scale(scalarl * scalarr);
1277 explicitCrsOp->fillComplete(tCrsOpr->getDomainMap(), tCrsOpl->getRangeMap());
1278 tExplicitOp->initialize(Thyra::tpetraVectorSpace<ST, LO, GO, NT>(explicitCrsOp->getRangeMap()),
1279 Thyra::tpetraVectorSpace<ST, LO, GO, NT>(explicitCrsOp->getDomainMap()),
1280 explicitCrsOp);
1281 return tExplicitOp;
1282
1283 } else { // Assume Epetra and we can use transformers
1284#ifdef TEKO_HAVE_EPETRA
1285 // build implicit multiply
1286 const LinearOp implicitOp = Thyra::multiply(opl, opm, opr);
1287
1288 // build transformer
1289 const RCP<Thyra::LinearOpTransformerBase<double>> prodTrans =
1290 Thyra::epetraExtDiagScaledMatProdTransformer();
1291
1292 // build operator and multiply
1293 const RCP<Thyra::LinearOpBase<double>> explicitOp = prodTrans->createOutputOp();
1294 prodTrans->transform(*implicitOp, explicitOp.ptr());
1295 explicitOp->setObjectLabel("explicit( " + opl->getObjectLabel() + " * " +
1296 opm->getObjectLabel() + " * " + opr->getObjectLabel() + " )");
1297
1298 return explicitOp;
1299#else
1300 throw std::logic_error(
1301 "explicitMultiply is trying to use Epetra "
1302 "code, but TEKO_HAVE_EPETRA is disabled!");
1303#endif
1304 }
1305}
1306
1321const ModifiableLinearOp explicitMultiply(const LinearOp &opl, const LinearOp &opm,
1322 const LinearOp &opr, const ModifiableLinearOp &destOp) {
1323 bool isTpetral = Teko::TpetraHelpers::isTpetraLinearOp(opl);
1324 bool isTpetram = Teko::TpetraHelpers::isTpetraLinearOp(opm);
1325 bool isTpetrar = Teko::TpetraHelpers::isTpetraLinearOp(opr);
1326
1327 if (isTpetral && isTpetram &&
1328 isTpetrar) { // Both operators are Tpetra matrices so explicitly multiply them
1329
1330 // Get left and right Tpetra crs operators
1331 ST scalarl = 0.0;
1332 bool transpl = false;
1333 RCP<const Tpetra::CrsMatrix<ST, LO, GO, NT>> tCrsOpl =
1334 Teko::TpetraHelpers::getTpetraCrsMatrix(opl, &scalarl, &transpl);
1335 ST scalarm = 0.0;
1336 bool transpm = false;
1337 RCP<const Tpetra::CrsMatrix<ST, LO, GO, NT>> tCrsOpm =
1338 Teko::TpetraHelpers::getTpetraCrsMatrix(opm, &scalarm, &transpm);
1339 ST scalarr = 0.0;
1340 bool transpr = false;
1341 RCP<const Tpetra::CrsMatrix<ST, LO, GO, NT>> tCrsOpr =
1342 Teko::TpetraHelpers::getTpetraCrsMatrix(opr, &scalarr, &transpr);
1343
1344 // Build output operator
1345 auto tExplicitOp = rcp_dynamic_cast<Thyra::TpetraLinearOp<ST, LO, GO, NT>>(destOp);
1346 if (tExplicitOp.is_null()) tExplicitOp = rcp(new Thyra::TpetraLinearOp<ST, LO, GO, NT>());
1347
1348 // Do explicit matrix-matrix multiply
1349 RCP<Tpetra::CrsMatrix<ST, LO, GO, NT>> tCrsOplm =
1350 Tpetra::createCrsMatrix<ST, LO, GO, NT>(tCrsOpl->getRowMap());
1351 RCP<Tpetra::CrsMatrix<ST, LO, GO, NT>> explicitCrsOp =
1352 Tpetra::createCrsMatrix<ST, LO, GO, NT>(tCrsOpl->getRowMap());
1353 Tpetra::MatrixMatrix::Multiply<ST, LO, GO, NT>(*tCrsOpl, transpl, *tCrsOpm, transpm, *tCrsOplm);
1354 Tpetra::MatrixMatrix::Multiply<ST, LO, GO, NT>(*tCrsOplm, false, *tCrsOpr, transpr,
1355 *explicitCrsOp);
1356 explicitCrsOp->resumeFill();
1357 explicitCrsOp->scale(scalarl * scalarm * scalarr);
1358 explicitCrsOp->fillComplete(tCrsOpr->getDomainMap(), tCrsOpl->getRangeMap());
1359 tExplicitOp->initialize(Thyra::tpetraVectorSpace<ST, LO, GO, NT>(explicitCrsOp->getRangeMap()),
1360 Thyra::tpetraVectorSpace<ST, LO, GO, NT>(explicitCrsOp->getDomainMap()),
1361 explicitCrsOp);
1362 return tExplicitOp;
1363
1364 } else if (isTpetral && !isTpetram && isTpetrar) { // Assume that the middle operator is diagonal
1365
1366 // Get left and right Tpetra crs operators
1367 ST scalarl = 0.0;
1368 bool transpl = false;
1369 RCP<const Tpetra::CrsMatrix<ST, LO, GO, NT>> tCrsOpl =
1370 Teko::TpetraHelpers::getTpetraCrsMatrix(opl, &scalarl, &transpl);
1371 ST scalarr = 0.0;
1372 bool transpr = false;
1373 RCP<const Tpetra::CrsMatrix<ST, LO, GO, NT>> tCrsOpr =
1374 Teko::TpetraHelpers::getTpetraCrsMatrix(opr, &scalarr, &transpr);
1375
1376 // Cast middle operator as DiagonalLinearOp and extract diagonal as Vector
1377 RCP<const Thyra::DiagonalLinearOpBase<ST>> dOpm =
1378 rcp_dynamic_cast<const Thyra::DiagonalLinearOpBase<ST>>(opm, true);
1379 RCP<const Thyra::TpetraVector<ST, LO, GO, NT>> tPtr =
1380 rcp_dynamic_cast<const Thyra::TpetraVector<ST, LO, GO, NT>>(dOpm->getDiag(), true);
1381 RCP<const Tpetra::Vector<ST, LO, GO, NT>> diagPtr =
1382 rcp_dynamic_cast<const Tpetra::Vector<ST, LO, GO, NT>>(tPtr->getConstTpetraVector(), true);
1383 RCP<Tpetra::CrsMatrix<ST, LO, GO, NT>> tCrsOplm =
1384 Tpetra::importAndFillCompleteCrsMatrix<Tpetra::CrsMatrix<ST, LO, GO, NT>>(
1385 tCrsOpl, Tpetra::Import<LO, GO, NT>(tCrsOpl->getRowMap(), tCrsOpl->getRowMap()));
1386
1387 // Do the diagonal scaling
1388 tCrsOplm->rightScale(*diagPtr);
1389
1390 // Build output operator
1391 RCP<Thyra::LinearOpBase<ST>> explicitOp = rcp(new Thyra::TpetraLinearOp<ST, LO, GO, NT>());
1392 RCP<Thyra::TpetraLinearOp<ST, LO, GO, NT>> tExplicitOp =
1393 rcp_dynamic_cast<Thyra::TpetraLinearOp<ST, LO, GO, NT>>(explicitOp);
1394
1395 // Do explicit matrix-matrix multiply
1396 RCP<Tpetra::CrsMatrix<ST, LO, GO, NT>> explicitCrsOp =
1397 Tpetra::createCrsMatrix<ST, LO, GO, NT>(tCrsOpl->getRowMap());
1398 Tpetra::MatrixMatrix::Multiply<ST, LO, GO, NT>(*tCrsOplm, false, *tCrsOpr, transpr,
1399 *explicitCrsOp);
1400 explicitCrsOp->resumeFill();
1401 explicitCrsOp->scale(scalarl * scalarr);
1402 explicitCrsOp->fillComplete(tCrsOpr->getDomainMap(), tCrsOpl->getRangeMap());
1403 tExplicitOp->initialize(Thyra::tpetraVectorSpace<ST, LO, GO, NT>(explicitCrsOp->getRangeMap()),
1404 Thyra::tpetraVectorSpace<ST, LO, GO, NT>(explicitCrsOp->getDomainMap()),
1405 explicitCrsOp);
1406 return tExplicitOp;
1407
1408 } else { // Assume Epetra and we can use transformers
1409#ifdef TEKO_HAVE_EPETRA
1410 // build implicit multiply
1411 const LinearOp implicitOp = Thyra::multiply(opl, opm, opr);
1412
1413 // build transformer
1414 const RCP<Thyra::LinearOpTransformerBase<double>> prodTrans =
1415 Thyra::epetraExtDiagScaledMatProdTransformer();
1416
1417 // build operator destination operator
1418 ModifiableLinearOp explicitOp;
1419
1420 // if neccessary build a operator to put the explicit multiply into
1421 if (destOp == Teuchos::null)
1422 explicitOp = prodTrans->createOutputOp();
1423 else
1424 explicitOp = destOp;
1425
1426 // perform multiplication
1427 prodTrans->transform(*implicitOp, explicitOp.ptr());
1428
1429 // label it
1430 explicitOp->setObjectLabel("explicit( " + opl->getObjectLabel() + " * " +
1431 opm->getObjectLabel() + " * " + opr->getObjectLabel() + " )");
1432
1433 return explicitOp;
1434#else
1435 throw std::logic_error(
1436 "explicitMultiply is trying to use Epetra "
1437 "code, but TEKO_HAVE_EPETRA is disabled!");
1438#endif
1439 }
1440}
1441
1452const LinearOp explicitMultiply(const LinearOp &opl, const LinearOp &opr) {
1453 // if this is a blocked operator, multiply block by block
1454 // it is possible that not every factor in the product is blocked and these situations are handled
1455 // separately
1456
1457 bool isBlockedL = isPhysicallyBlockedLinearOp(opl);
1458 bool isBlockedR = isPhysicallyBlockedLinearOp(opr);
1459
1460 // both factors blocked
1461 if ((isBlockedL && isBlockedR)) {
1462 double scalarl = 0.0;
1463 bool transpl = false;
1464 RCP<const Thyra::PhysicallyBlockedLinearOpBase<double>> blocked_opl =
1465 getPhysicallyBlockedLinearOp(opl, &scalarl, &transpl);
1466 double scalarr = 0.0;
1467 bool transpr = false;
1468 RCP<const Thyra::PhysicallyBlockedLinearOpBase<double>> blocked_opr =
1469 getPhysicallyBlockedLinearOp(opr, &scalarr, &transpr);
1470 double scalar = scalarl * scalarr;
1471
1472 int numRows = blocked_opl->productRange()->numBlocks();
1473 int numCols = blocked_opr->productDomain()->numBlocks();
1474 int numMiddle = blocked_opl->productDomain()->numBlocks();
1475
1476 TEUCHOS_ASSERT(blocked_opr->productRange()->numBlocks() == numMiddle);
1477
1478 RCP<Thyra::PhysicallyBlockedLinearOpBase<double>> blocked_product =
1479 Thyra::defaultBlockedLinearOp<double>();
1480 blocked_product->beginBlockFill(numRows, numCols);
1481 for (int r = 0; r < numRows; ++r) {
1482 for (int c = 0; c < numCols; ++c) {
1483 LinearOp product_rc =
1484 explicitMultiply(blocked_opl->getBlock(r, 0), blocked_opr->getBlock(0, c));
1485 for (int m = 1; m < numMiddle; ++m) {
1486 LinearOp product_m =
1487 explicitMultiply(blocked_opl->getBlock(r, m), blocked_opr->getBlock(m, c));
1488 product_rc = explicitAdd(product_rc, product_m);
1489 }
1490 blocked_product->setBlock(r, c, Thyra::scale(scalar, product_rc));
1491 }
1492 }
1493 blocked_product->endBlockFill();
1494 return blocked_product;
1495 }
1496
1497 // only left factor blocked
1498 if ((isBlockedL && !isBlockedR)) {
1499 double scalarl = 0.0;
1500 bool transpl = false;
1501 RCP<const Thyra::PhysicallyBlockedLinearOpBase<double>> blocked_opl =
1502 getPhysicallyBlockedLinearOp(opl, &scalarl, &transpl);
1503 double scalar = scalarl;
1504
1505 int numRows = blocked_opl->productRange()->numBlocks();
1506 int numCols = 1;
1507 int numMiddle = 1;
1508
1509 TEUCHOS_ASSERT(blocked_opl->productDomain()->numBlocks() == numMiddle);
1510
1511 RCP<Thyra::PhysicallyBlockedLinearOpBase<double>> blocked_product =
1512 Thyra::defaultBlockedLinearOp<double>();
1513 blocked_product->beginBlockFill(numRows, numCols);
1514 for (int r = 0; r < numRows; ++r) {
1515 LinearOp product_r = explicitMultiply(blocked_opl->getBlock(r, 0), opr);
1516 blocked_product->setBlock(r, 0, Thyra::scale(scalar, product_r));
1517 }
1518 blocked_product->endBlockFill();
1519 return blocked_product;
1520 }
1521
1522 // only right factor blocked
1523 if ((!isBlockedL && isBlockedR)) {
1524 double scalarr = 0.0;
1525 bool transpr = false;
1526 RCP<const Thyra::PhysicallyBlockedLinearOpBase<double>> blocked_opr =
1527 getPhysicallyBlockedLinearOp(opr, &scalarr, &transpr);
1528 double scalar = scalarr;
1529
1530 int numRows = 1;
1531 int numCols = blocked_opr->productDomain()->numBlocks();
1532 int numMiddle = 1;
1533
1534 TEUCHOS_ASSERT(blocked_opr->productRange()->numBlocks() == numMiddle);
1535
1536 RCP<Thyra::PhysicallyBlockedLinearOpBase<double>> blocked_product =
1537 Thyra::defaultBlockedLinearOp<double>();
1538 blocked_product->beginBlockFill(numRows, numCols);
1539 for (int c = 0; c < numCols; ++c) {
1540 LinearOp product_c = explicitMultiply(opl, blocked_opr->getBlock(0, c));
1541 blocked_product->setBlock(0, c, Thyra::scale(scalar, product_c));
1542 }
1543 blocked_product->endBlockFill();
1544 return blocked_product;
1545 }
1546
1547 bool isTpetral = Teko::TpetraHelpers::isTpetraLinearOp(opl);
1548 bool isTpetrar = Teko::TpetraHelpers::isTpetraLinearOp(opr);
1549
1550 if (isTpetral && isTpetrar) { // Both operators are Tpetra matrices so explicitly multiply them
1551 // Get left and right Tpetra crs operators
1552 ST scalarl = 0.0;
1553 bool transpl = false;
1554 RCP<const Tpetra::CrsMatrix<ST, LO, GO, NT>> tCrsOpl =
1555 Teko::TpetraHelpers::getTpetraCrsMatrix(opl, &scalarl, &transpl);
1556 ST scalarr = 0.0;
1557 bool transpr = false;
1558 RCP<const Tpetra::CrsMatrix<ST, LO, GO, NT>> tCrsOpr =
1559 Teko::TpetraHelpers::getTpetraCrsMatrix(opr, &scalarr, &transpr);
1560
1561 // Build output operator
1562 RCP<Thyra::LinearOpBase<ST>> explicitOp = rcp(new Thyra::TpetraLinearOp<ST, LO, GO, NT>());
1563 RCP<Thyra::TpetraLinearOp<ST, LO, GO, NT>> tExplicitOp =
1564 rcp_dynamic_cast<Thyra::TpetraLinearOp<ST, LO, GO, NT>>(explicitOp);
1565
1566 // Do explicit matrix-matrix multiply
1567 RCP<Tpetra::CrsMatrix<ST, LO, GO, NT>> explicitCrsOp =
1568 Tpetra::createCrsMatrix<ST, LO, GO, NT>(tCrsOpl->getRowMap());
1569 Tpetra::MatrixMatrix::Multiply<ST, LO, GO, NT>(*tCrsOpl, transpl, *tCrsOpr, transpr,
1570 *explicitCrsOp);
1571 explicitCrsOp->resumeFill();
1572 explicitCrsOp->scale(scalarl * scalarr);
1573 explicitCrsOp->fillComplete(tCrsOpr->getDomainMap(), tCrsOpl->getRangeMap());
1574 tExplicitOp->initialize(Thyra::tpetraVectorSpace<ST, LO, GO, NT>(explicitCrsOp->getRangeMap()),
1575 Thyra::tpetraVectorSpace<ST, LO, GO, NT>(explicitCrsOp->getDomainMap()),
1576 explicitCrsOp);
1577 return tExplicitOp;
1578
1579 } else if (isTpetral && !isTpetrar) { // Assume that the right operator is diagonal
1580
1581 // Get left Tpetra crs operator
1582 ST scalarl = 0.0;
1583 bool transpl = false;
1584 RCP<const Tpetra::CrsMatrix<ST, LO, GO, NT>> tCrsOpl =
1585 Teko::TpetraHelpers::getTpetraCrsMatrix(opl, &scalarl, &transpl);
1586
1587 // Cast right operator as DiagonalLinearOp and extract diagonal as Vector
1588 RCP<const Thyra::DiagonalLinearOpBase<ST>> dOpr =
1589 rcp_dynamic_cast<const Thyra::DiagonalLinearOpBase<ST>>(opr, true);
1590 RCP<const Thyra::TpetraVector<ST, LO, GO, NT>> tPtr =
1591 rcp_dynamic_cast<const Thyra::TpetraVector<ST, LO, GO, NT>>(dOpr->getDiag(), true);
1592 RCP<const Tpetra::Vector<ST, LO, GO, NT>> diagPtr =
1593 rcp_dynamic_cast<const Tpetra::Vector<ST, LO, GO, NT>>(tPtr->getConstTpetraVector(), true);
1594 RCP<Tpetra::CrsMatrix<ST, LO, GO, NT>> explicitCrsOp =
1595 Tpetra::importAndFillCompleteCrsMatrix<Tpetra::CrsMatrix<ST, LO, GO, NT>>(
1596 tCrsOpl, Tpetra::Import<LO, GO, NT>(tCrsOpl->getRowMap(), tCrsOpl->getRowMap()));
1597
1598 explicitCrsOp->rightScale(*diagPtr);
1599 explicitCrsOp->resumeFill();
1600 explicitCrsOp->scale(scalarl);
1601 explicitCrsOp->fillComplete(tCrsOpl->getDomainMap(), tCrsOpl->getRangeMap());
1602
1603 return Thyra::constTpetraLinearOp<ST, LO, GO, NT>(
1604 Thyra::tpetraVectorSpace<ST, LO, GO, NT>(explicitCrsOp->getRangeMap()),
1605 Thyra::tpetraVectorSpace<ST, LO, GO, NT>(explicitCrsOp->getDomainMap()), explicitCrsOp);
1606
1607 } else if (!isTpetral && isTpetrar) { // Assume that the left operator is diagonal
1608
1609 // Get right Tpetra crs operator
1610 ST scalarr = 0.0;
1611 bool transpr = false;
1612 RCP<const Tpetra::CrsMatrix<ST, LO, GO, NT>> tCrsOpr =
1613 Teko::TpetraHelpers::getTpetraCrsMatrix(opr, &scalarr, &transpr);
1614
1615 RCP<const Tpetra::Vector<ST, LO, GO, NT>> diagPtr;
1616
1617 // Cast left operator as DiagonalLinearOp and extract diagonal as Vector
1618 RCP<const Thyra::DiagonalLinearOpBase<ST>> dOpl =
1619 rcp_dynamic_cast<const Thyra::DiagonalLinearOpBase<ST>>(opl);
1620 if (dOpl != Teuchos::null) {
1621 RCP<const Thyra::TpetraVector<ST, LO, GO, NT>> tPtr =
1622 rcp_dynamic_cast<const Thyra::TpetraVector<ST, LO, GO, NT>>(dOpl->getDiag(), true);
1623 diagPtr = rcp_dynamic_cast<const Tpetra::Vector<ST, LO, GO, NT>>(tPtr->getConstTpetraVector(),
1624 true);
1625 }
1626 // If it's not diagonal, maybe it's zero
1627 else if (rcp_dynamic_cast<const Thyra::ZeroLinearOpBase<ST>>(opl) != Teuchos::null) {
1628 diagPtr = rcp(new Tpetra::Vector<ST, LO, GO, NT>(tCrsOpr->getRangeMap()));
1629 } else
1630 TEUCHOS_ASSERT(false);
1631
1632 RCP<Tpetra::CrsMatrix<ST, LO, GO, NT>> explicitCrsOp =
1633 Tpetra::importAndFillCompleteCrsMatrix<Tpetra::CrsMatrix<ST, LO, GO, NT>>(
1634 tCrsOpr, Tpetra::Import<LO, GO, NT>(tCrsOpr->getRowMap(), tCrsOpr->getRowMap()));
1635
1636 explicitCrsOp->leftScale(*diagPtr);
1637 explicitCrsOp->resumeFill();
1638 explicitCrsOp->scale(scalarr);
1639 explicitCrsOp->fillComplete(tCrsOpr->getDomainMap(), tCrsOpr->getRangeMap());
1640
1641 return Thyra::constTpetraLinearOp<ST, LO, GO, NT>(
1642 Thyra::tpetraVectorSpace<ST, LO, GO, NT>(explicitCrsOp->getRangeMap()),
1643 Thyra::tpetraVectorSpace<ST, LO, GO, NT>(explicitCrsOp->getDomainMap()), explicitCrsOp);
1644
1645 } else { // Assume Epetra and we can use transformers
1646#ifdef TEKO_HAVE_EPETRA
1647 // build implicit multiply
1648 const LinearOp implicitOp = Thyra::multiply(opl, opr);
1649
1650 // build a scaling transformer
1651 RCP<Thyra::LinearOpTransformerBase<double>> prodTrans =
1652 Thyra::epetraExtDiagScalingTransformer();
1653
1654 // check to see if a scaling transformer works: if not use the
1655 // DiagScaledMatrixProduct transformer
1656 if (not prodTrans->isCompatible(*implicitOp))
1657 prodTrans = Thyra::epetraExtDiagScaledMatProdTransformer();
1658
1659 // build operator and multiply
1660 const RCP<Thyra::LinearOpBase<double>> explicitOp = prodTrans->createOutputOp();
1661 prodTrans->transform(*implicitOp, explicitOp.ptr());
1662 explicitOp->setObjectLabel("explicit( " + opl->getObjectLabel() + " * " +
1663 opr->getObjectLabel() + " )");
1664
1665 return explicitOp;
1666#else
1667 throw std::logic_error(
1668 "explicitMultiply is trying to use Epetra "
1669 "code, but TEKO_HAVE_EPETRA is disabled!");
1670#endif
1671 }
1672}
1673
1687const ModifiableLinearOp explicitMultiply(const LinearOp &opl, const LinearOp &opr,
1688 const ModifiableLinearOp &destOp) {
1689 // if this is a blocked operator, multiply block by block
1690 // it is possible that not every factor in the product is blocked and these situations are handled
1691 // separately
1692
1693 bool isBlockedL = isPhysicallyBlockedLinearOp(opl);
1694 bool isBlockedR = isPhysicallyBlockedLinearOp(opr);
1695
1696 // both factors blocked
1697 if ((isBlockedL && isBlockedR)) {
1698 double scalarl = 0.0;
1699 bool transpl = false;
1700 RCP<const Thyra::PhysicallyBlockedLinearOpBase<double>> blocked_opl =
1701 getPhysicallyBlockedLinearOp(opl, &scalarl, &transpl);
1702 double scalarr = 0.0;
1703 bool transpr = false;
1704 RCP<const Thyra::PhysicallyBlockedLinearOpBase<double>> blocked_opr =
1705 getPhysicallyBlockedLinearOp(opr, &scalarr, &transpr);
1706 double scalar = scalarl * scalarr;
1707
1708 int numRows = blocked_opl->productRange()->numBlocks();
1709 int numCols = blocked_opr->productDomain()->numBlocks();
1710 int numMiddle = blocked_opl->productDomain()->numBlocks();
1711
1712 TEUCHOS_ASSERT(blocked_opr->productRange()->numBlocks() == numMiddle);
1713
1714 RCP<Thyra::PhysicallyBlockedLinearOpBase<double>> blocked_product =
1715 Thyra::defaultBlockedLinearOp<double>();
1716 blocked_product->beginBlockFill(numRows, numCols);
1717 for (int r = 0; r < numRows; ++r) {
1718 for (int c = 0; c < numCols; ++c) {
1719 LinearOp product_rc =
1720 explicitMultiply(blocked_opl->getBlock(r, 0), blocked_opr->getBlock(0, c));
1721 for (int m = 1; m < numMiddle; ++m) {
1722 LinearOp product_m =
1723 explicitMultiply(blocked_opl->getBlock(r, m), blocked_opr->getBlock(m, c));
1724 product_rc = explicitAdd(product_rc, product_m);
1725 }
1726 blocked_product->setBlock(r, c, Thyra::scale(scalar, product_rc));
1727 }
1728 }
1729 blocked_product->endBlockFill();
1730 return blocked_product;
1731 }
1732
1733 // only left factor blocked
1734 if ((isBlockedL && !isBlockedR)) {
1735 double scalarl = 0.0;
1736 bool transpl = false;
1737 RCP<const Thyra::PhysicallyBlockedLinearOpBase<double>> blocked_opl =
1738 getPhysicallyBlockedLinearOp(opl, &scalarl, &transpl);
1739 double scalar = scalarl;
1740
1741 int numRows = blocked_opl->productRange()->numBlocks();
1742 int numCols = 1;
1743 int numMiddle = 1;
1744
1745 TEUCHOS_ASSERT(blocked_opl->productDomain()->numBlocks() == numMiddle);
1746
1747 RCP<Thyra::PhysicallyBlockedLinearOpBase<double>> blocked_product =
1748 Thyra::defaultBlockedLinearOp<double>();
1749 blocked_product->beginBlockFill(numRows, numCols);
1750 for (int r = 0; r < numRows; ++r) {
1751 LinearOp product_r = explicitMultiply(blocked_opl->getBlock(r, 0), opr);
1752 blocked_product->setBlock(r, 0, Thyra::scale(scalar, product_r));
1753 }
1754 blocked_product->endBlockFill();
1755 return blocked_product;
1756 }
1757
1758 // only right factor blocked
1759 if ((!isBlockedL && isBlockedR)) {
1760 double scalarr = 0.0;
1761 bool transpr = false;
1762 RCP<const Thyra::PhysicallyBlockedLinearOpBase<double>> blocked_opr =
1763 getPhysicallyBlockedLinearOp(opr, &scalarr, &transpr);
1764 double scalar = scalarr;
1765
1766 int numRows = 1;
1767 int numCols = blocked_opr->productDomain()->numBlocks();
1768 int numMiddle = 1;
1769
1770 TEUCHOS_ASSERT(blocked_opr->productRange()->numBlocks() == numMiddle);
1771
1772 RCP<Thyra::PhysicallyBlockedLinearOpBase<double>> blocked_product =
1773 Thyra::defaultBlockedLinearOp<double>();
1774 blocked_product->beginBlockFill(numRows, numCols);
1775 for (int c = 0; c < numCols; ++c) {
1776 LinearOp product_c = explicitMultiply(opl, blocked_opr->getBlock(0, c));
1777 blocked_product->setBlock(0, c, Thyra::scale(scalar, product_c));
1778 }
1779 blocked_product->endBlockFill();
1780 return blocked_product;
1781 }
1782
1783 bool isTpetral = Teko::TpetraHelpers::isTpetraLinearOp(opl);
1784 bool isTpetrar = Teko::TpetraHelpers::isTpetraLinearOp(opr);
1785
1786 if (isTpetral && isTpetrar) { // Both operators are Tpetra matrices so use the explicit Tpetra
1787 // matrix-matrix multiply
1788
1789 // Get left and right Tpetra crs operators
1790 ST scalarl = 0.0;
1791 bool transpl = false;
1792 RCP<const Tpetra::CrsMatrix<ST, LO, GO, NT>> tCrsOpl =
1793 Teko::TpetraHelpers::getTpetraCrsMatrix(opl, &scalarl, &transpl);
1794 ST scalarr = 0.0;
1795 bool transpr = false;
1796 RCP<const Tpetra::CrsMatrix<ST, LO, GO, NT>> tCrsOpr =
1797 Teko::TpetraHelpers::getTpetraCrsMatrix(opr, &scalarr, &transpr);
1798
1799 // Build output operator
1800 RCP<Thyra::LinearOpBase<ST>> explicitOp;
1801 if (destOp != Teuchos::null)
1802 explicitOp = destOp;
1803 else
1804 explicitOp = rcp(new Thyra::TpetraLinearOp<ST, LO, GO, NT>());
1805 RCP<Thyra::TpetraLinearOp<ST, LO, GO, NT>> tExplicitOp =
1806 rcp_dynamic_cast<Thyra::TpetraLinearOp<ST, LO, GO, NT>>(explicitOp);
1807
1808 // Do explicit matrix-matrix multiply
1809 RCP<Tpetra::CrsMatrix<ST, LO, GO, NT>> explicitCrsOp =
1810 Tpetra::createCrsMatrix<ST, LO, GO, NT>(tCrsOpl->getRowMap());
1811 Tpetra::MatrixMatrix::Multiply<ST, LO, GO, NT>(*tCrsOpl, transpl, *tCrsOpr, transpr,
1812 *explicitCrsOp);
1813 explicitCrsOp->resumeFill();
1814 explicitCrsOp->scale(scalarl * scalarr);
1815 explicitCrsOp->fillComplete(tCrsOpr->getDomainMap(), tCrsOpl->getRangeMap());
1816 tExplicitOp->initialize(Thyra::tpetraVectorSpace<ST, LO, GO, NT>(explicitCrsOp->getRangeMap()),
1817 Thyra::tpetraVectorSpace<ST, LO, GO, NT>(explicitCrsOp->getDomainMap()),
1818 explicitCrsOp);
1819 return tExplicitOp;
1820
1821 } else if (isTpetral && !isTpetrar) { // Assume that the right operator is diagonal
1822
1823 // Get left Tpetra crs operator
1824 ST scalarl = 0.0;
1825 bool transpl = false;
1826 RCP<const Tpetra::CrsMatrix<ST, LO, GO, NT>> tCrsOpl =
1827 Teko::TpetraHelpers::getTpetraCrsMatrix(opl, &scalarl, &transpl);
1828
1829 // Cast right operator as DiagonalLinearOp and extract diagonal as Vector
1830 RCP<const Thyra::DiagonalLinearOpBase<ST>> dOpr =
1831 rcp_dynamic_cast<const Thyra::DiagonalLinearOpBase<ST>>(opr);
1832 RCP<const Thyra::TpetraVector<ST, LO, GO, NT>> tPtr =
1833 rcp_dynamic_cast<const Thyra::TpetraVector<ST, LO, GO, NT>>(dOpr->getDiag(), true);
1834 RCP<const Tpetra::Vector<ST, LO, GO, NT>> diagPtr =
1835 rcp_dynamic_cast<const Tpetra::Vector<ST, LO, GO, NT>>(tPtr->getConstTpetraVector(), true);
1836
1837 // Scale by the diagonal operator
1838 RCP<Tpetra::CrsMatrix<ST, LO, GO, NT>> explicitCrsOp =
1839 Tpetra::importAndFillCompleteCrsMatrix<Tpetra::CrsMatrix<ST, LO, GO, NT>>(
1840 tCrsOpl, Tpetra::Import<LO, GO, NT>(tCrsOpl->getRowMap(), tCrsOpl->getRowMap()));
1841 explicitCrsOp->rightScale(*diagPtr);
1842 explicitCrsOp->resumeFill();
1843 explicitCrsOp->scale(scalarl);
1844 explicitCrsOp->fillComplete(tCrsOpl->getDomainMap(), tCrsOpl->getRangeMap());
1845 return Thyra::tpetraLinearOp<ST, LO, GO, NT>(
1846 Thyra::tpetraVectorSpace<ST, LO, GO, NT>(explicitCrsOp->getRangeMap()),
1847 Thyra::tpetraVectorSpace<ST, LO, GO, NT>(explicitCrsOp->getDomainMap()), explicitCrsOp);
1848
1849 } else if (!isTpetral && isTpetrar) { // Assume that the left operator is diagonal
1850
1851 // Get right Tpetra crs operator
1852 ST scalarr = 0.0;
1853 bool transpr = false;
1854 RCP<const Tpetra::CrsMatrix<ST, LO, GO, NT>> tCrsOpr =
1855 Teko::TpetraHelpers::getTpetraCrsMatrix(opr, &scalarr, &transpr);
1856
1857 // Cast leftt operator as DiagonalLinearOp and extract diagonal as Vector
1858 RCP<const Thyra::DiagonalLinearOpBase<ST>> dOpl =
1859 rcp_dynamic_cast<const Thyra::DiagonalLinearOpBase<ST>>(opl, true);
1860 RCP<const Thyra::TpetraVector<ST, LO, GO, NT>> tPtr =
1861 rcp_dynamic_cast<const Thyra::TpetraVector<ST, LO, GO, NT>>(dOpl->getDiag(), true);
1862 RCP<const Tpetra::Vector<ST, LO, GO, NT>> diagPtr =
1863 rcp_dynamic_cast<const Tpetra::Vector<ST, LO, GO, NT>>(tPtr->getConstTpetraVector(), true);
1864
1865 // Scale by the diagonal operator
1866 RCP<Tpetra::CrsMatrix<ST, LO, GO, NT>> explicitCrsOp =
1867 Tpetra::importAndFillCompleteCrsMatrix<Tpetra::CrsMatrix<ST, LO, GO, NT>>(
1868 tCrsOpr, Tpetra::Import<LO, GO, NT>(tCrsOpr->getRowMap(), tCrsOpr->getRowMap()));
1869 explicitCrsOp->leftScale(*diagPtr);
1870 explicitCrsOp->resumeFill();
1871 explicitCrsOp->scale(scalarr);
1872 explicitCrsOp->fillComplete(tCrsOpr->getDomainMap(), tCrsOpr->getRangeMap());
1873 return Thyra::tpetraLinearOp<ST, LO, GO, NT>(
1874 Thyra::tpetraVectorSpace<ST, LO, GO, NT>(explicitCrsOp->getRangeMap()),
1875 Thyra::tpetraVectorSpace<ST, LO, GO, NT>(explicitCrsOp->getDomainMap()), explicitCrsOp);
1876
1877 } else { // Assume Epetra and we can use transformers
1878#ifdef TEKO_HAVE_EPETRA
1879 // build implicit multiply
1880 const LinearOp implicitOp = Thyra::multiply(opl, opr);
1881
1882 // build a scaling transformer
1883
1884 RCP<Thyra::LinearOpTransformerBase<double>> prodTrans =
1885 Thyra::epetraExtDiagScalingTransformer();
1886
1887 // check to see if a scaling transformer works: if not use the
1888 // DiagScaledMatrixProduct transformer
1889 if (not prodTrans->isCompatible(*implicitOp))
1890 prodTrans = Thyra::epetraExtDiagScaledMatProdTransformer();
1891
1892 // build operator destination operator
1893 ModifiableLinearOp explicitOp;
1894
1895 // if neccessary build a operator to put the explicit multiply into
1896 if (destOp == Teuchos::null)
1897 explicitOp = prodTrans->createOutputOp();
1898 else
1899 explicitOp = destOp;
1900
1901 // perform multiplication
1902 prodTrans->transform(*implicitOp, explicitOp.ptr());
1903
1904 // label it
1905 explicitOp->setObjectLabel("explicit( " + opl->getObjectLabel() + " * " +
1906 opr->getObjectLabel() + " )");
1907
1908 return explicitOp;
1909#else
1910 throw std::logic_error(
1911 "explicitMultiply is trying to use Epetra "
1912 "code, but TEKO_HAVE_EPETRA is disabled!");
1913#endif
1914 }
1915}
1916
1927const LinearOp explicitAdd(const LinearOp &opl_in, const LinearOp &opr_in) {
1928 // if both blocked, add block by block
1929 if (isPhysicallyBlockedLinearOp(opl_in) && isPhysicallyBlockedLinearOp(opr_in)) {
1930 double scalarl = 0.0;
1931 bool transpl = false;
1932 RCP<const Thyra::PhysicallyBlockedLinearOpBase<double>> blocked_opl =
1933 getPhysicallyBlockedLinearOp(opl_in, &scalarl, &transpl);
1934
1935 double scalarr = 0.0;
1936 bool transpr = false;
1937 RCP<const Thyra::PhysicallyBlockedLinearOpBase<double>> blocked_opr =
1938 getPhysicallyBlockedLinearOp(opr_in, &scalarr, &transpr);
1939
1940 int numRows = blocked_opl->productRange()->numBlocks();
1941 int numCols = blocked_opl->productDomain()->numBlocks();
1942 TEUCHOS_ASSERT(blocked_opr->productRange()->numBlocks() == numRows);
1943 TEUCHOS_ASSERT(blocked_opr->productDomain()->numBlocks() == numCols);
1944
1945 RCP<Thyra::PhysicallyBlockedLinearOpBase<double>> blocked_sum =
1946 Thyra::defaultBlockedLinearOp<double>();
1947 blocked_sum->beginBlockFill(numRows, numCols);
1948 for (int r = 0; r < numRows; ++r)
1949 for (int c = 0; c < numCols; ++c)
1950 blocked_sum->setBlock(r, c,
1951 explicitAdd(Thyra::scale(scalarl, blocked_opl->getBlock(r, c)),
1952 Thyra::scale(scalarr, blocked_opr->getBlock(r, c))));
1953 blocked_sum->endBlockFill();
1954 return blocked_sum;
1955 }
1956
1957 // if only one is blocked, it must be 1x1
1958 LinearOp opl = opl_in;
1959 LinearOp opr = opr_in;
1960 if (isPhysicallyBlockedLinearOp(opl_in)) {
1961 double scalarl = 0.0;
1962 bool transpl = false;
1963 RCP<const Thyra::PhysicallyBlockedLinearOpBase<double>> blocked_opl =
1964 getPhysicallyBlockedLinearOp(opl_in, &scalarl, &transpl);
1965 TEUCHOS_ASSERT(blocked_opl->productRange()->numBlocks() == 1);
1966 TEUCHOS_ASSERT(blocked_opl->productDomain()->numBlocks() == 1);
1967 opl = Thyra::scale(scalarl, blocked_opl->getBlock(0, 0));
1968 }
1969 if (isPhysicallyBlockedLinearOp(opr_in)) {
1970 double scalarr = 0.0;
1971 bool transpr = false;
1972 RCP<const Thyra::PhysicallyBlockedLinearOpBase<double>> blocked_opr =
1973 getPhysicallyBlockedLinearOp(opr_in, &scalarr, &transpr);
1974 TEUCHOS_ASSERT(blocked_opr->productRange()->numBlocks() == 1);
1975 TEUCHOS_ASSERT(blocked_opr->productDomain()->numBlocks() == 1);
1976 opr = Thyra::scale(scalarr, blocked_opr->getBlock(0, 0));
1977 }
1978
1979 bool isTpetral = Teko::TpetraHelpers::isTpetraLinearOp(opl);
1980 bool isTpetrar = Teko::TpetraHelpers::isTpetraLinearOp(opr);
1981
1982 // if one of the operators in the sum is a thyra zero op
1983 if (isZeroOp(opl)) {
1984 if (isZeroOp(opr)) return opr; // return a zero op if both are zero
1985 if (isTpetrar) return Teko::TpetraHelpers::materializeTpetraLinearOp(opr, Teuchos::null);
1986 return opr->clone();
1987 }
1988 if (isZeroOp(opr)) {
1989 if (isTpetral) return Teko::TpetraHelpers::materializeTpetraLinearOp(opl, Teuchos::null);
1990 return opl->clone();
1991 }
1992
1993 if (isTpetral && isTpetrar) { // Both operators are Tpetra matrices so use the explicit Tpetra
1994 // matrix-matrix add
1995
1996 // Get left and right Tpetra crs operators
1997 ST scalarl = 0.0;
1998 bool transpl = false;
1999 RCP<const Tpetra::CrsMatrix<ST, LO, GO, NT>> tCrsOpl =
2000 Teko::TpetraHelpers::getTpetraCrsMatrix(opl, &scalarl, &transpl);
2001 ST scalarr = 0.0;
2002 bool transpr = false;
2003 RCP<const Tpetra::CrsMatrix<ST, LO, GO, NT>> tCrsOpr =
2004 Teko::TpetraHelpers::getTpetraCrsMatrix(opr, &scalarr, &transpr);
2005
2006 // Build output operator
2007 RCP<Thyra::LinearOpBase<ST>> explicitOp = rcp(new Thyra::TpetraLinearOp<ST, LO, GO, NT>());
2008 RCP<Thyra::TpetraLinearOp<ST, LO, GO, NT>> tExplicitOp =
2009 rcp_dynamic_cast<Thyra::TpetraLinearOp<ST, LO, GO, NT>>(explicitOp);
2010
2011 // Do explicit matrix-matrix add
2012 RCP<Tpetra::CrsMatrix<ST, LO, GO, NT>> explicitCrsOp =
2013 Tpetra::MatrixMatrix::add<ST, LO, GO, NT>(scalarl, transpl, *tCrsOpl, scalarr, transpr,
2014 *tCrsOpr);
2015 tExplicitOp->initialize(Thyra::tpetraVectorSpace<ST, LO, GO, NT>(explicitCrsOp->getRangeMap()),
2016 Thyra::tpetraVectorSpace<ST, LO, GO, NT>(explicitCrsOp->getDomainMap()),
2017 explicitCrsOp);
2018 return tExplicitOp;
2019
2020 } else { // Assume Epetra
2021#ifdef TEKO_HAVE_EPETRA
2022 // build implicit add
2023 const LinearOp implicitOp = Thyra::add(opl, opr);
2024
2025 // build transformer
2026 const RCP<Thyra::LinearOpTransformerBase<double>> prodTrans = Thyra::epetraExtAddTransformer();
2027
2028 // build operator and add
2029 const RCP<Thyra::LinearOpBase<double>> explicitOp = prodTrans->createOutputOp();
2030 prodTrans->transform(*implicitOp, explicitOp.ptr());
2031 explicitOp->setObjectLabel("explicit( " + opl->getObjectLabel() + " + " +
2032 opr->getObjectLabel() + " )");
2033
2034 return explicitOp;
2035#else
2036 throw std::logic_error(
2037 "explicitAdd is trying to use Epetra "
2038 "code, but TEKO_HAVE_EPETRA is disabled!");
2039#endif
2040 }
2041}
2042
2055const ModifiableLinearOp explicitAdd(const LinearOp &opl_in, const LinearOp &opr_in,
2056 const ModifiableLinearOp &destOp) {
2057 // if blocked, add block by block
2058 if (isPhysicallyBlockedLinearOp(opl_in) && isPhysicallyBlockedLinearOp(opr_in)) {
2059 double scalarl = 0.0;
2060 bool transpl = false;
2061 RCP<const Thyra::PhysicallyBlockedLinearOpBase<double>> blocked_opl =
2062 getPhysicallyBlockedLinearOp(opl_in, &scalarl, &transpl);
2063
2064 double scalarr = 0.0;
2065 bool transpr = false;
2066 RCP<const Thyra::PhysicallyBlockedLinearOpBase<double>> blocked_opr =
2067 getPhysicallyBlockedLinearOp(opr_in, &scalarr, &transpr);
2068
2069 int numRows = blocked_opl->productRange()->numBlocks();
2070 int numCols = blocked_opl->productDomain()->numBlocks();
2071 TEUCHOS_ASSERT(blocked_opr->productRange()->numBlocks() == numRows);
2072 TEUCHOS_ASSERT(blocked_opr->productDomain()->numBlocks() == numCols);
2073
2074 RCP<Thyra::PhysicallyBlockedLinearOpBase<double>> blocked_sum =
2075 Teuchos::rcp_dynamic_cast<Thyra::DefaultBlockedLinearOp<double>>(destOp);
2076 if (blocked_sum.is_null()) {
2077 // take care of the null case, this means we must alllocate memory
2078 blocked_sum = Thyra::defaultBlockedLinearOp<double>();
2079
2080 blocked_sum->beginBlockFill(numRows, numCols);
2081 for (int r = 0; r < numRows; ++r) {
2082 for (int c = 0; c < numCols; ++c) {
2083 auto block =
2084 explicitAdd(Thyra::scale(scalarl, blocked_opl->getBlock(r, c)),
2085 Thyra::scale(scalarr, blocked_opr->getBlock(r, c)), Teuchos::null);
2086 blocked_sum->setNonconstBlock(r, c, block);
2087 }
2088 }
2089 blocked_sum->endBlockFill();
2090
2091 } else {
2092 // in this case memory can be reused
2093 for (int r = 0; r < numRows; ++r)
2094 for (int c = 0; c < numCols; ++c)
2095 explicitAdd(Thyra::scale(scalarl, blocked_opl->getBlock(r, c)),
2096 Thyra::scale(scalarr, blocked_opr->getBlock(r, c)),
2097 blocked_sum->getNonconstBlock(r, c));
2098 }
2099
2100 return blocked_sum;
2101 }
2102
2103 LinearOp opl = opl_in;
2104 LinearOp opr = opr_in;
2105 // if only one is blocked, it must be 1x1
2106 if (isPhysicallyBlockedLinearOp(opl)) {
2107 double scalarl = 0.0;
2108 bool transpl = false;
2109 RCP<const Thyra::PhysicallyBlockedLinearOpBase<double>> blocked_opl =
2110 getPhysicallyBlockedLinearOp(opl, &scalarl, &transpl);
2111 TEUCHOS_ASSERT(blocked_opl->productRange()->numBlocks() == 1);
2112 TEUCHOS_ASSERT(blocked_opl->productDomain()->numBlocks() == 1);
2113 opl = Thyra::scale(scalarl, blocked_opl->getBlock(0, 0));
2114 }
2115 if (isPhysicallyBlockedLinearOp(opr)) {
2116 double scalarr = 0.0;
2117 bool transpr = false;
2118 RCP<const Thyra::PhysicallyBlockedLinearOpBase<double>> blocked_opr =
2119 getPhysicallyBlockedLinearOp(opr, &scalarr, &transpr);
2120 TEUCHOS_ASSERT(blocked_opr->productRange()->numBlocks() == 1);
2121 TEUCHOS_ASSERT(blocked_opr->productDomain()->numBlocks() == 1);
2122 opr = Thyra::scale(scalarr, blocked_opr->getBlock(0, 0));
2123 }
2124
2125 bool isTpetral = Teko::TpetraHelpers::isTpetraLinearOp(opl);
2126 bool isTpetrar = Teko::TpetraHelpers::isTpetraLinearOp(opr);
2127
2128 // if one of the operators in the sum is a thyra zero op (as happens with a true
2129 // zero (2,2) block in a saddle-point system), short-circuit before the backend
2130 // add path, which cannot unwrap a Thyra::DefaultZeroLinearOp. Materialize the
2131 // nonzero operand directly rather than assembling an explicit zero matrix.
2132 if (isZeroOp(opl)) {
2133 if (isZeroOp(opr)) return Teuchos::rcp_const_cast<Thyra::LinearOpBase<ST>>(opr);
2134 if (isTpetrar) return Teko::TpetraHelpers::materializeTpetraLinearOp(opr, destOp);
2135 return Teuchos::rcp_const_cast<Thyra::LinearOpBase<ST>>(opr->clone());
2136 }
2137 if (isZeroOp(opr)) {
2138 if (isTpetral) return Teko::TpetraHelpers::materializeTpetraLinearOp(opl, destOp);
2139 return Teuchos::rcp_const_cast<Thyra::LinearOpBase<ST>>(opl->clone());
2140 }
2141
2142 if (isTpetral && isTpetrar) { // Both operators are Tpetra matrices so use the explicit
2143 // Tpetra matrix-matrix add
2144
2145 // Get left and right Tpetra crs operators
2146 ST scalarl = 0.0;
2147 bool transpl = false;
2148 RCP<const Tpetra::CrsMatrix<ST, LO, GO, NT>> tCrsOpl =
2149 Teko::TpetraHelpers::getTpetraCrsMatrix(opl, &scalarl, &transpl);
2150 ST scalarr = 0.0;
2151 bool transpr = false;
2152 RCP<const Tpetra::CrsMatrix<ST, LO, GO, NT>> tCrsOpr =
2153 Teko::TpetraHelpers::getTpetraCrsMatrix(opr, &scalarr, &transpr);
2154
2155 // Build output operator
2156 RCP<Thyra::LinearOpBase<ST>> explicitOp;
2157 RCP<Tpetra::CrsMatrix<ST, LO, GO, NT>> explicitCrsOp;
2158 if (!destOp.is_null()) {
2159 explicitOp = destOp;
2160 RCP<Thyra::TpetraLinearOp<ST, LO, GO, NT>> tOp =
2161 rcp_dynamic_cast<Thyra::TpetraLinearOp<ST, LO, GO, NT>>(destOp);
2162 if (!tOp.is_null())
2163 explicitCrsOp =
2164 rcp_dynamic_cast<Tpetra::CrsMatrix<ST, LO, GO, NT>>(tOp->getTpetraOperator());
2165 bool needNewTpetraMatrix =
2166 (explicitCrsOp.is_null()) || (tCrsOpl == explicitCrsOp) || (tCrsOpr == explicitCrsOp);
2167 if (!needNewTpetraMatrix) {
2168 try {
2169 // try to reuse matrix sparsity with Add. If it fails, build new operator with add
2170 Tpetra::MatrixMatrix::Add<ST, LO, GO, NT>(*tCrsOpl, transpl, scalarl, *tCrsOpr, transpr,
2171 scalarr, explicitCrsOp);
2172 } catch (std::logic_error &e) {
2173 RCP<Teuchos::FancyOStream> out = Teuchos::VerboseObjectBase::getDefaultOStream();
2174 *out << "*** THROWN EXCEPTION ***\n";
2175 *out << e.what() << std::endl;
2176 *out << "************************\n";
2177 *out << "Teko: explicitAdd unable to reuse existing operator. Creating new operator.\n"
2178 << std::endl;
2179 needNewTpetraMatrix = true;
2180 }
2181 }
2182 if (needNewTpetraMatrix)
2183 // Do explicit matrix-matrix add
2184 explicitCrsOp = Tpetra::MatrixMatrix::add<ST, LO, GO, NT>(scalarl, transpl, *tCrsOpl,
2185 scalarr, transpr, *tCrsOpr);
2186 } else {
2187 explicitOp = rcp(new Thyra::TpetraLinearOp<ST, LO, GO, NT>());
2188 // Do explicit matrix-matrix add
2189 explicitCrsOp = Tpetra::MatrixMatrix::add<ST, LO, GO, NT>(scalarl, transpl, *tCrsOpl, scalarr,
2190 transpr, *tCrsOpr);
2191 }
2192 RCP<Thyra::TpetraLinearOp<ST, LO, GO, NT>> tExplicitOp =
2193 rcp_dynamic_cast<Thyra::TpetraLinearOp<ST, LO, GO, NT>>(explicitOp);
2194
2195 tExplicitOp->initialize(Thyra::tpetraVectorSpace<ST, LO, GO, NT>(explicitCrsOp->getRangeMap()),
2196 Thyra::tpetraVectorSpace<ST, LO, GO, NT>(explicitCrsOp->getDomainMap()),
2197 explicitCrsOp);
2198 return tExplicitOp;
2199
2200 } else { // Assume Epetra
2201#ifdef TEKO_HAVE_EPETRA
2202 // build implicit add
2203 const LinearOp implicitOp = Thyra::add(opl, opr);
2204
2205 // build transformer
2206 const RCP<Thyra::LinearOpTransformerBase<double>> prodTrans = Thyra::epetraExtAddTransformer();
2207
2208 // build or reuse destination operator
2209 RCP<Thyra::LinearOpBase<double>> explicitOp;
2210 if (destOp != Teuchos::null)
2211 explicitOp = destOp;
2212 else
2213 explicitOp = prodTrans->createOutputOp();
2214
2215 // perform add
2216 prodTrans->transform(*implicitOp, explicitOp.ptr());
2217 explicitOp->setObjectLabel("explicit( " + opl->getObjectLabel() + " + " +
2218 opr->getObjectLabel() + " )");
2219
2220 return explicitOp;
2221#else
2222 throw std::logic_error(
2223 "explicitAdd is trying to use Epetra "
2224 "code, but TEKO_HAVE_EPETRA is disabled!");
2225#endif
2226 }
2227}
2228
2233const ModifiableLinearOp explicitSum(const LinearOp &op, const ModifiableLinearOp &destOp) {
2234#ifdef TEKO_HAVE_EPETRA
2235 // convert operators to Epetra_CrsMatrix
2236 const RCP<const Epetra_CrsMatrix> epetraOp =
2237 rcp_dynamic_cast<const Epetra_CrsMatrix>(get_Epetra_Operator(*op), true);
2238
2239 if (destOp == Teuchos::null) {
2240 Teuchos::RCP<Epetra_Operator> epetraDest = Teuchos::rcp(new Epetra_CrsMatrix(*epetraOp));
2241
2242 return Thyra::nonconstEpetraLinearOp(epetraDest);
2243 }
2244
2245 const RCP<Epetra_CrsMatrix> epetraDest =
2246 rcp_dynamic_cast<Epetra_CrsMatrix>(get_Epetra_Operator(*destOp), true);
2247
2248 EpetraExt::MatrixMatrix::Add(*epetraOp, false, 1.0, *epetraDest, 1.0);
2249
2250 return destOp;
2251#else
2252 throw std::logic_error(
2253 "explicitSum is trying to use Epetra "
2254 "code, but TEKO_HAVE_EPETRA is disabled!");
2255#endif
2256}
2257
2258const LinearOp explicitTranspose(const LinearOp &op) {
2259 if (Teko::TpetraHelpers::isTpetraLinearOp(op)) {
2260 RCP<const Thyra::TpetraLinearOp<ST, LO, GO, NT>> tOp =
2261 rcp_dynamic_cast<const Thyra::TpetraLinearOp<ST, LO, GO, NT>>(op, true);
2262 RCP<const Tpetra::CrsMatrix<ST, LO, GO, NT>> tCrsOp =
2263 rcp_dynamic_cast<const Tpetra::CrsMatrix<ST, LO, GO, NT>>(tOp->getConstTpetraOperator(),
2264 true);
2265
2266 Tpetra::RowMatrixTransposer<ST, LO, GO, NT> transposer(tCrsOp);
2267 RCP<Tpetra::CrsMatrix<ST, LO, GO, NT>> transOp = transposer.createTranspose();
2268
2269 return Thyra::tpetraLinearOp<ST, LO, GO, NT>(
2270 Thyra::tpetraVectorSpace<ST, LO, GO, NT>(transOp->getRangeMap()),
2271 Thyra::tpetraVectorSpace<ST, LO, GO, NT>(transOp->getDomainMap()), transOp);
2272
2273 } else {
2274#ifdef TEKO_HAVE_EPETRA
2275 RCP<const Epetra_Operator> eOp = Thyra::get_Epetra_Operator(*op);
2276 TEUCHOS_TEST_FOR_EXCEPTION(eOp == Teuchos::null, std::logic_error,
2277 "Teko::explicitTranspose Not an Epetra_Operator");
2278 RCP<const Epetra_RowMatrix> eRowMatrixOp =
2279 Teuchos::rcp_dynamic_cast<const Epetra_RowMatrix>(eOp);
2280 TEUCHOS_TEST_FOR_EXCEPTION(eRowMatrixOp == Teuchos::null, std::logic_error,
2281 "Teko::explicitTranspose Not an Epetra_RowMatrix");
2282
2283 // we now have a delete transpose operator
2284 EpetraExt::RowMatrix_Transpose tranposeOp;
2285 Epetra_RowMatrix &eMat = tranposeOp(const_cast<Epetra_RowMatrix &>(*eRowMatrixOp));
2286
2287 // this copy is because of a poor implementation of the EpetraExt::Transform
2288 // implementation
2289 Teuchos::RCP<Epetra_CrsMatrix> crsMat =
2290 Teuchos::rcp(new Epetra_CrsMatrix(dynamic_cast<Epetra_CrsMatrix &>(eMat)));
2291
2292 return Thyra::epetraLinearOp(crsMat);
2293#else
2294 throw std::logic_error(
2295 "explicitTranspose is trying to use Epetra "
2296 "code, but TEKO_HAVE_EPETRA is disabled!");
2297#endif
2298 }
2299}
2300
2301const LinearOp explicitScale(double scalar, const LinearOp &op) {
2302 if (Teko::TpetraHelpers::isTpetraLinearOp(op)) {
2303 RCP<const Thyra::TpetraLinearOp<ST, LO, GO, NT>> tOp =
2304 rcp_dynamic_cast<const Thyra::TpetraLinearOp<ST, LO, GO, NT>>(op, true);
2305 RCP<const Tpetra::CrsMatrix<ST, LO, GO, NT>> tCrsOp =
2306 rcp_dynamic_cast<const Tpetra::CrsMatrix<ST, LO, GO, NT>>(tOp->getConstTpetraOperator(),
2307 true);
2308 auto crsOpNew = rcp(new Tpetra::CrsMatrix<ST, LO, GO, NT>(*tCrsOp, Teuchos::Copy));
2309 crsOpNew->scale(scalar);
2310 return Thyra::tpetraLinearOp<ST, LO, GO, NT>(
2311 Thyra::createVectorSpace<ST, LO, GO, NT>(crsOpNew->getRangeMap()),
2312 Thyra::createVectorSpace<ST, LO, GO, NT>(crsOpNew->getDomainMap()), crsOpNew);
2313 } else {
2314#ifdef TEKO_HAVE_EPETRA
2315 RCP<const Thyra::EpetraLinearOp> eOp = rcp_dynamic_cast<const Thyra::EpetraLinearOp>(op, true);
2316 RCP<const Epetra_CrsMatrix> eCrsOp =
2317 rcp_dynamic_cast<const Epetra_CrsMatrix>(eOp->epetra_op(), true);
2318 Teuchos::RCP<Epetra_CrsMatrix> crsMat = Teuchos::rcp(new Epetra_CrsMatrix(*eCrsOp));
2319
2320 crsMat->Scale(scalar);
2321
2322 return Thyra::epetraLinearOp(crsMat);
2323#else
2324 throw std::logic_error(
2325 "explicitScale is trying to use Epetra "
2326 "code, but TEKO_HAVE_EPETRA is disabled!");
2327#endif
2328 }
2329}
2330
2331double frobeniusNorm(const LinearOp &op_in) {
2332 LinearOp op;
2333 double scalar = 1.0;
2334
2335 // if blocked, must be 1x1
2336 if (isPhysicallyBlockedLinearOp(op_in)) {
2337 bool transp = false;
2338 RCP<const Thyra::PhysicallyBlockedLinearOpBase<double>> blocked_op =
2339 getPhysicallyBlockedLinearOp(op_in, &scalar, &transp);
2340 TEUCHOS_ASSERT(blocked_op->productRange()->numBlocks() == 1);
2341 TEUCHOS_ASSERT(blocked_op->productDomain()->numBlocks() == 1);
2342 op = blocked_op->getBlock(0, 0);
2343 } else
2344 op = op_in;
2345
2346 if (Teko::TpetraHelpers::isTpetraLinearOp(op)) {
2347 const RCP<const Thyra::TpetraLinearOp<ST, LO, GO, NT>> tOp =
2348 rcp_dynamic_cast<const Thyra::TpetraLinearOp<ST, LO, GO, NT>>(op);
2349 const RCP<const Tpetra::CrsMatrix<ST, LO, GO, NT>> crsOp =
2350 rcp_dynamic_cast<const Tpetra::CrsMatrix<ST, LO, GO, NT>>(tOp->getConstTpetraOperator(),
2351 true);
2352 return crsOp->getFrobeniusNorm();
2353 } else {
2354#ifdef TEKO_HAVE_EPETRA
2355 const RCP<const Epetra_Operator> epOp = Thyra::get_Epetra_Operator(*op);
2356 const RCP<const Epetra_CrsMatrix> crsOp = rcp_dynamic_cast<const Epetra_CrsMatrix>(epOp, true);
2357 return crsOp->NormFrobenius();
2358#else
2359 throw std::logic_error(
2360 "frobeniusNorm is trying to use Epetra "
2361 "code, but TEKO_HAVE_EPETRA is disabled!");
2362#endif
2363 }
2364}
2365
2366double oneNorm(const LinearOp &op) {
2367 if (Teko::TpetraHelpers::isTpetraLinearOp(op)) {
2368 TEUCHOS_TEST_FOR_EXCEPTION(true, std::logic_error,
2369 "One norm not currently implemented for Tpetra matrices");
2370
2371 } else {
2372#ifdef TEKO_HAVE_EPETRA
2373 const RCP<const Epetra_Operator> epOp = Thyra::get_Epetra_Operator(*op);
2374 const RCP<const Epetra_CrsMatrix> crsOp = rcp_dynamic_cast<const Epetra_CrsMatrix>(epOp, true);
2375 return crsOp->NormOne();
2376#else
2377 throw std::logic_error(
2378 "oneNorm is trying to use Epetra "
2379 "code, but TEKO_HAVE_EPETRA is disabled!");
2380#endif
2381 }
2382}
2383
2384double infNorm(const LinearOp &op) {
2385 if (Teko::TpetraHelpers::isTpetraLinearOp(op)) {
2386 ST scalar = 0.0;
2387 bool transp = false;
2388 RCP<const Tpetra::CrsMatrix<ST, LO, GO, NT>> tCrsOp =
2389 Teko::TpetraHelpers::getTpetraCrsMatrix(op, &scalar, &transp);
2390
2391 // extract diagonal
2392 const RCP<Tpetra::Vector<ST, LO, GO, NT>> ptrDiag =
2393 Tpetra::createVector<ST, LO, GO, NT>(tCrsOp->getRowMap());
2394 Tpetra::Vector<ST, LO, GO, NT> &diag = *ptrDiag;
2395
2396 // compute absolute value row sum
2397 diag.putScalar(0.0);
2398 for (LO i = 0; i < (LO)tCrsOp->getLocalNumRows(); i++) {
2399 LO numEntries = tCrsOp->getNumEntriesInLocalRow(i);
2400 typename Tpetra::CrsMatrix<ST, LO, GO, NT>::local_inds_host_view_type indices;
2401 typename Tpetra::CrsMatrix<ST, LO, GO, NT>::values_host_view_type values;
2402 tCrsOp->getLocalRowView(i, indices, values);
2403
2404 // build abs value row sum
2405 for (LO j = 0; j < numEntries; j++) diag.sumIntoLocalValue(i, std::abs(values(j)));
2406 }
2407 return diag.normInf() * scalar;
2408
2409 } else {
2410#ifdef TEKO_HAVE_EPETRA
2411 const RCP<const Epetra_Operator> epOp = Thyra::get_Epetra_Operator(*op);
2412 const RCP<const Epetra_CrsMatrix> crsOp = rcp_dynamic_cast<const Epetra_CrsMatrix>(epOp, true);
2413 return crsOp->NormInf();
2414#else
2415 throw std::logic_error(
2416 "infNorm is trying to use Epetra "
2417 "code, but TEKO_HAVE_EPETRA is disabled!");
2418#endif
2419 }
2420}
2421
2422const LinearOp buildDiagonal(const MultiVector &src, const std::string &lbl) {
2423 RCP<Thyra::VectorBase<double>> dst = Thyra::createMember(src->range());
2424 Thyra::copy(*src->col(0), dst.ptr());
2425
2426 return Thyra::diagonal<double>(dst, lbl);
2427}
2428
2429const LinearOp buildInvDiagonal(const MultiVector &src, const std::string &lbl) {
2430 const RCP<const Thyra::VectorBase<double>> srcV = src->col(0);
2431 RCP<Thyra::VectorBase<double>> dst = Thyra::createMember(srcV->range());
2432 Thyra::reciprocal<double>(*srcV, dst.ptr());
2433
2434 return Thyra::diagonal<double>(dst, lbl);
2435}
2436
2438BlockedMultiVector buildBlockedMultiVector(const std::vector<MultiVector> &mvv) {
2439 Teuchos::Array<MultiVector> mvA;
2440 Teuchos::Array<VectorSpace> vsA;
2441
2442 // build arrays of multi vectors and vector spaces
2443 std::vector<MultiVector>::const_iterator itr;
2444 for (itr = mvv.begin(); itr != mvv.end(); ++itr) {
2445 mvA.push_back(*itr);
2446 vsA.push_back((*itr)->range());
2447 }
2448
2449 // construct the product vector space
2450 const RCP<const Thyra::DefaultProductVectorSpace<double>> vs =
2451 Thyra::productVectorSpace<double>(vsA);
2452
2453 return Thyra::defaultProductMultiVector<double>(vs, mvA);
2454}
2455
2466Teuchos::RCP<Thyra::VectorBase<double>> indicatorVector(const std::vector<int> &indices,
2467 const VectorSpace &vs, double onValue,
2468 double offValue)
2469
2470{
2471 using Teuchos::RCP;
2472
2473 // create a new vector
2474 RCP<Thyra::VectorBase<double>> v = Thyra::createMember<double>(vs);
2475 Thyra::put_scalar<double>(offValue, v.ptr()); // fill it with "off" values
2476
2477 // set on values
2478 for (std::size_t i = 0; i < indices.size(); i++)
2479 Thyra::set_ele<double>(indices[i], onValue, v.ptr());
2480
2481 return v;
2482}
2483
2508double computeSpectralRad(const RCP<const Thyra::LinearOpBase<double>> &A, double tol,
2509 bool isHermitian, int numBlocks, int restart, int verbosity) {
2510 typedef Thyra::LinearOpBase<double> OP;
2511 typedef Thyra::MultiVectorBase<double> MV;
2512
2513 int startVectors = 1;
2514
2515 // construct an initial guess
2516 const RCP<MV> ivec = Thyra::createMember(A->domain());
2517 Thyra::randomize(-1.0, 1.0, ivec.ptr());
2518
2519 RCP<Anasazi::BasicEigenproblem<double, MV, OP>> eigProb =
2520 rcp(new Anasazi::BasicEigenproblem<double, MV, OP>(A, ivec));
2521 eigProb->setNEV(1);
2522 eigProb->setHermitian(isHermitian);
2523
2524 // set the problem up
2525 if (not eigProb->setProblem()) {
2526 // big time failure!
2527 return Teuchos::ScalarTraits<double>::nan();
2528 }
2529
2530 // we want largert magnitude eigenvalue
2531 std::string which("LM"); // largest magnitude
2532
2533 // Create the parameter list for the eigensolver
2534 // verbosity+=Anasazi::TimingDetails;
2535 Teuchos::ParameterList MyPL;
2536 MyPL.set("Verbosity", verbosity);
2537 MyPL.set("Which", which);
2538 MyPL.set("Block Size", startVectors);
2539 MyPL.set("Num Blocks", numBlocks);
2540 MyPL.set("Maximum Restarts", restart);
2541 MyPL.set("Convergence Tolerance", tol);
2542
2543 // build status test manager
2544 // RCP<Anasazi::StatusTestMaxIters<double,MV,OP> > statTest
2545 // = rcp(new Anasazi::StatusTestMaxIters<double,MV,OP>(numBlocks*(restart+1)));
2546
2547 // Create the Block Krylov Schur solver
2548 // This takes as inputs the eigenvalue problem and the solver parameters
2549 Anasazi::BlockKrylovSchurSolMgr<double, MV, OP> MyBlockKrylovSchur(eigProb, MyPL);
2550
2551 // Solve the eigenvalue problem, and save the return code
2552 Anasazi::ReturnType solverreturn = MyBlockKrylovSchur.solve();
2553
2554 if (solverreturn == Anasazi::Unconverged) {
2555 double real = MyBlockKrylovSchur.getRitzValues().begin()->realpart;
2556 double comp = MyBlockKrylovSchur.getRitzValues().begin()->imagpart;
2557
2558 return -std::abs(std::sqrt(real * real + comp * comp));
2559
2560 // cout << "Anasazi::BlockKrylovSchur::solve() did not converge!" << std::endl;
2561 // return -std::abs(MyBlockKrylovSchur.getRitzValues().begin()->realpart);
2562 } else { // solverreturn==Anasazi::Converged
2563 double real = eigProb->getSolution().Evals.begin()->realpart;
2564 double comp = eigProb->getSolution().Evals.begin()->imagpart;
2565
2566 return std::abs(std::sqrt(real * real + comp * comp));
2567
2568 // cout << "Anasazi::BlockKrylovSchur::solve() converged!" << endl;
2569 // return std::abs(eigProb->getSolution().Evals.begin()->realpart);
2570 }
2571}
2572
2596double computeSmallestMagEig(const RCP<const Thyra::LinearOpBase<double>> &A, double tol,
2597 bool isHermitian, int numBlocks, int restart, int verbosity) {
2598 typedef Thyra::LinearOpBase<double> OP;
2599 typedef Thyra::MultiVectorBase<double> MV;
2600
2601 int startVectors = 1;
2602
2603 // construct an initial guess
2604 const RCP<MV> ivec = Thyra::createMember(A->domain());
2605 Thyra::randomize(-1.0, 1.0, ivec.ptr());
2606
2607 RCP<Anasazi::BasicEigenproblem<double, MV, OP>> eigProb =
2608 rcp(new Anasazi::BasicEigenproblem<double, MV, OP>(A, ivec));
2609 eigProb->setNEV(1);
2610 eigProb->setHermitian(isHermitian);
2611
2612 // set the problem up
2613 if (not eigProb->setProblem()) {
2614 // big time failure!
2615 return Teuchos::ScalarTraits<double>::nan();
2616 }
2617
2618 // we want largert magnitude eigenvalue
2619 std::string which("SM"); // smallest magnitude
2620
2621 // Create the parameter list for the eigensolver
2622 Teuchos::ParameterList MyPL;
2623 MyPL.set("Verbosity", verbosity);
2624 MyPL.set("Which", which);
2625 MyPL.set("Block Size", startVectors);
2626 MyPL.set("Num Blocks", numBlocks);
2627 MyPL.set("Maximum Restarts", restart);
2628 MyPL.set("Convergence Tolerance", tol);
2629
2630 // build status test manager
2631 // RCP<Anasazi::StatusTestMaxIters<double,MV,OP> > statTest
2632 // = rcp(new Anasazi::StatusTestMaxIters<double,MV,OP>(10));
2633
2634 // Create the Block Krylov Schur solver
2635 // This takes as inputs the eigenvalue problem and the solver parameters
2636 Anasazi::BlockKrylovSchurSolMgr<double, MV, OP> MyBlockKrylovSchur(eigProb, MyPL);
2637
2638 // Solve the eigenvalue problem, and save the return code
2639 Anasazi::ReturnType solverreturn = MyBlockKrylovSchur.solve();
2640
2641 if (solverreturn == Anasazi::Unconverged) {
2642 // cout << "Anasazi::BlockKrylovSchur::solve() did not converge! " << std::endl;
2643 return -std::abs(MyBlockKrylovSchur.getRitzValues().begin()->realpart);
2644 } else { // solverreturn==Anasazi::Converged
2645 // cout << "Anasazi::BlockKrylovSchur::solve() converged!" << endl;
2646 return std::abs(eigProb->getSolution().Evals.begin()->realpart);
2647 }
2648}
2649
2658ModifiableLinearOp getDiagonalOp(const Teko::LinearOp &A, const DiagonalType &dt) {
2659 switch (dt) {
2660 case Diagonal: return getDiagonalOp(A);
2661 case Lumped: return getLumpedMatrix(A);
2662 case AbsRowSum: return getAbsRowSumMatrix(A);
2663 case NotDiag:
2664 default: TEUCHOS_TEST_FOR_EXCEPT(true);
2665 };
2666
2667 return Teuchos::null;
2668}
2669
2678ModifiableLinearOp getInvDiagonalOp(const Teko::LinearOp &A, const Teko::DiagonalType &dt) {
2679 switch (dt) {
2680 case Diagonal: return getInvDiagonalOp(A);
2681 case Lumped: return getInvLumpedMatrix(A);
2682 case AbsRowSum: return getAbsRowSumInvMatrix(A);
2683 case NotDiag:
2684 default: TEUCHOS_TEST_FOR_EXCEPT(true);
2685 };
2686
2687 return Teuchos::null;
2688}
2689
2696std::string getDiagonalName(const DiagonalType &dt) {
2697 switch (dt) {
2698 case Diagonal: return "Diagonal";
2699 case Lumped: return "Lumped";
2700 case AbsRowSum: return "AbsRowSum";
2701 case NotDiag: return "NotDiag";
2702 case BlkDiag: return "BlkDiag";
2703 };
2704
2705 return "<error>";
2706}
2707
2716DiagonalType getDiagonalType(std::string name) {
2717 if (name == "Diagonal") return Diagonal;
2718 if (name == "Lumped") return Lumped;
2719 if (name == "AbsRowSum") return AbsRowSum;
2720 if (name == "BlkDiag") return BlkDiag;
2721
2722 return NotDiag;
2723}
2724
2725#ifdef TEKO_HAVE_EPETRA
2726LinearOp probe(Teuchos::RCP<const Epetra_CrsGraph> &G, const LinearOp &Op) {
2727#ifdef Teko_ENABLE_Isorropia
2728 Teuchos::ParameterList probeList;
2729 Prober prober(G, probeList, true);
2730 Teuchos::RCP<Epetra_CrsMatrix> Mat = rcp(new Epetra_CrsMatrix(Copy, *G));
2732 prober.probe(Mwrap, *Mat);
2733 return Thyra::epetraLinearOp(Mat);
2734#else
2735 (void)G;
2736 (void)Op;
2737 TEUCHOS_TEST_FOR_EXCEPTION(true, std::runtime_error, "Probe requires Isorropia");
2738#endif
2739}
2740#endif
2741
2742double norm_1(const MultiVector &v, std::size_t col) {
2743 Teuchos::Array<double> n(v->domain()->dim());
2744 Thyra::norms_1<double>(*v, n);
2745
2746 return n[col];
2747}
2748
2749double norm_2(const MultiVector &v, std::size_t col) {
2750 Teuchos::Array<double> n(v->domain()->dim());
2751 Thyra::norms_2<double>(*v, n);
2752
2753 return n[col];
2754}
2755
2756#ifdef TEKO_HAVE_EPETRA
2757void putScalar(const ModifiableLinearOp &op, double scalar) {
2758 try {
2759 // get Epetra_Operator
2760 RCP<Epetra_Operator> eOp = Thyra::get_Epetra_Operator(*op);
2761
2762 // cast it to a CrsMatrix
2763 RCP<Epetra_CrsMatrix> eCrsOp = rcp_dynamic_cast<Epetra_CrsMatrix>(eOp, true);
2764
2765 eCrsOp->PutScalar(scalar);
2766 } catch (std::exception &e) {
2767 RCP<Teuchos::FancyOStream> out = Teuchos::VerboseObjectBase::getDefaultOStream();
2768
2769 *out << "Teko: putScalar requires an Epetra_CrsMatrix\n";
2770 *out << " Could not extract an Epetra_Operator from a \"" << op->description() << std::endl;
2771 *out << " OR\n";
2772 *out << " Could not cast an Epetra_Operator to a Epetra_CrsMatrix\n";
2773 *out << std::endl;
2774 *out << "*** THROWN EXCEPTION ***\n";
2775 *out << e.what() << std::endl;
2776 *out << "************************\n";
2777
2778 throw e;
2779 }
2780}
2781#endif
2782
2783void clipLower(MultiVector &v, double lowerBound) {
2784 using Teuchos::RCP;
2785 using Teuchos::rcp_dynamic_cast;
2786
2787 // cast so entries are accessible
2788 // RCP<Thyra::SpmdMultiVectorBase<double> > spmdMVec
2789 // = rcp_dynamic_cast<Thyra::DefaultSpmdMultiVector<double> >(v);
2790
2791 for (Thyra::Ordinal i = 0; i < v->domain()->dim(); i++) {
2792 RCP<Thyra::SpmdVectorBase<double>> spmdVec =
2793 rcp_dynamic_cast<Thyra::SpmdVectorBase<double>>(v->col(i), true);
2794
2795 Teuchos::ArrayRCP<double> values;
2796 // spmdMVec->getNonconstLocalData(Teuchos::ptrFromRef(values),Teuchos::ptrFromRef(i));
2797 spmdVec->getNonconstLocalData(Teuchos::ptrFromRef(values));
2798 for (Teuchos::ArrayRCP<double>::size_type j = 0; j < values.size(); j++) {
2799 if (values[j] < lowerBound) values[j] = lowerBound;
2800 }
2801 }
2802}
2803
2804void clipUpper(MultiVector &v, double upperBound) {
2805 using Teuchos::RCP;
2806 using Teuchos::rcp_dynamic_cast;
2807
2808 // cast so entries are accessible
2809 // RCP<Thyra::SpmdMultiVectorBase<double> > spmdMVec
2810 // = rcp_dynamic_cast<Thyra::DefaultSpmdMultiVector<double> >(v);
2811 for (Thyra::Ordinal i = 0; i < v->domain()->dim(); i++) {
2812 RCP<Thyra::SpmdVectorBase<double>> spmdVec =
2813 rcp_dynamic_cast<Thyra::SpmdVectorBase<double>>(v->col(i), true);
2814
2815 Teuchos::ArrayRCP<double> values;
2816 // spmdMVec->getNonconstLocalData(Teuchos::ptrFromRef(values),Teuchos::ptrFromRef(i));
2817 spmdVec->getNonconstLocalData(Teuchos::ptrFromRef(values));
2818 for (Teuchos::ArrayRCP<double>::size_type j = 0; j < values.size(); j++) {
2819 if (values[j] > upperBound) values[j] = upperBound;
2820 }
2821 }
2822}
2823
2824void replaceValue(MultiVector &v, double currentValue, double newValue) {
2825 using Teuchos::RCP;
2826 using Teuchos::rcp_dynamic_cast;
2827
2828 // cast so entries are accessible
2829 // RCP<Thyra::SpmdMultiVectorBase<double> > spmdMVec
2830 // = rcp_dynamic_cast<Thyra::SpmdMultiVectorBase<double> >(v,true);
2831 for (Thyra::Ordinal i = 0; i < v->domain()->dim(); i++) {
2832 RCP<Thyra::SpmdVectorBase<double>> spmdVec =
2833 rcp_dynamic_cast<Thyra::SpmdVectorBase<double>>(v->col(i), true);
2834
2835 Teuchos::ArrayRCP<double> values;
2836 // spmdMVec->getNonconstLocalData(Teuchos::ptrFromRef(values),Teuchos::ptrFromRef(i));
2837 spmdVec->getNonconstLocalData(Teuchos::ptrFromRef(values));
2838 for (Teuchos::ArrayRCP<double>::size_type j = 0; j < values.size(); j++) {
2839 if (values[j] == currentValue) values[j] = newValue;
2840 }
2841 }
2842}
2843
2844void columnAverages(const MultiVector &v, std::vector<double> &averages) {
2845 averages.resize(v->domain()->dim());
2846
2847 // sum over each column
2848 Thyra::sums<double>(*v, averages);
2849
2850 // build averages
2851 Thyra::Ordinal rows = v->range()->dim();
2852 for (std::size_t i = 0; i < averages.size(); i++) averages[i] = averages[i] / rows;
2853}
2854
2855double average(const MultiVector &v) {
2856 Thyra::Ordinal rows = v->range()->dim();
2857 Thyra::Ordinal cols = v->domain()->dim();
2858
2859 std::vector<double> averages;
2860 columnAverages(v, averages);
2861
2862 double sum = 0.0;
2863 for (std::size_t i = 0; i < averages.size(); i++) sum += averages[i] * rows;
2864
2865 return sum / (rows * cols);
2866}
2867
2868bool isPhysicallyBlockedLinearOp(const LinearOp &op) {
2869 // See if the operator is a PBLO
2870 RCP<const Thyra::PhysicallyBlockedLinearOpBase<double>> pblo =
2871 rcp_dynamic_cast<const Thyra::PhysicallyBlockedLinearOpBase<double>>(op);
2872 if (!pblo.is_null()) return true;
2873
2874 // See if the operator is a wrapped PBLO
2875 ST scalar = 0.0;
2876 Thyra::EOpTransp transp = Thyra::NOTRANS;
2877 RCP<const Thyra::LinearOpBase<ST>> wrapped_op;
2878 Thyra::unwrap(op, &scalar, &transp, &wrapped_op);
2879 pblo = rcp_dynamic_cast<const Thyra::PhysicallyBlockedLinearOpBase<double>>(wrapped_op);
2880 if (!pblo.is_null()) return true;
2881
2882 return false;
2883}
2884
2885RCP<const Thyra::PhysicallyBlockedLinearOpBase<double>> getPhysicallyBlockedLinearOp(
2886 const LinearOp &op, ST *scalar, bool *transp) {
2887 // If the operator is a TpetraLinearOp
2888 RCP<const Thyra::PhysicallyBlockedLinearOpBase<double>> pblo =
2889 rcp_dynamic_cast<const Thyra::PhysicallyBlockedLinearOpBase<double>>(op);
2890 if (!pblo.is_null()) {
2891 *scalar = 1.0;
2892 *transp = false;
2893 return pblo;
2894 }
2895
2896 // If the operator is a wrapped TpetraLinearOp
2897 RCP<const Thyra::LinearOpBase<ST>> wrapped_op;
2898 Thyra::EOpTransp eTransp = Thyra::NOTRANS;
2899 Thyra::unwrap(op, scalar, &eTransp, &wrapped_op);
2900 pblo = rcp_dynamic_cast<const Thyra::PhysicallyBlockedLinearOpBase<double>>(wrapped_op, true);
2901 if (!pblo.is_null()) {
2902 *transp = true;
2903 if (eTransp == Thyra::NOTRANS) *transp = false;
2904 return pblo;
2905 }
2906
2907 return Teuchos::null;
2908}
2909
2910std::string formatBlockName(const std::string &prefix, int i, int j, int nrow) {
2911 unsigned digits = 0;
2912 auto blockId = nrow - 1;
2913 do {
2914 blockId /= 10;
2915 digits++;
2916 } while (blockId);
2917
2918 std::ostringstream ss;
2919 ss << prefix << "_";
2920 ss << std::setfill('0') << std::setw(digits) << i;
2921 ss << "_";
2922 ss << std::setfill('0') << std::setw(digits) << j;
2923 ss << ".mm";
2924 return ss.str();
2925}
2926
2927void writeMatrix(const std::string &filename, const Teko::LinearOp &op) {
2928 using Teuchos::RCP;
2929 using Teuchos::rcp_dynamic_cast;
2930#ifdef TEKO_HAVE_EPETRA
2931 const RCP<const Thyra::EpetraLinearOp> eOp = rcp_dynamic_cast<const Thyra::EpetraLinearOp>(op);
2932#endif
2933
2934 if (Teko::TpetraHelpers::isTpetraLinearOp(op)) {
2935 const RCP<const Thyra::TpetraLinearOp<ST, LO, GO, NT>> tOp =
2936 rcp_dynamic_cast<const Thyra::TpetraLinearOp<ST, LO, GO, NT>>(op);
2937 const RCP<const Tpetra::CrsMatrix<ST, LO, GO, NT>> crsOp =
2938 rcp_dynamic_cast<const Tpetra::CrsMatrix<ST, LO, GO, NT>>(tOp->getConstTpetraOperator(),
2939 true);
2940 using Writer = Tpetra::MatrixMarket::Writer<Tpetra::CrsMatrix<ST, LO, GO, NT>>;
2941 Writer::writeMapFile(("rowmap_" + filename).c_str(), *(crsOp->getRowMap()));
2942 Writer::writeMapFile(("colmap_" + filename).c_str(), *(crsOp->getColMap()));
2943 Writer::writeMapFile(("domainmap_" + filename).c_str(), *(crsOp->getDomainMap()));
2944 Writer::writeMapFile(("rangemap_" + filename).c_str(), *(crsOp->getRangeMap()));
2945 Writer::writeSparseFile(filename.c_str(), crsOp);
2946 }
2947#ifdef TEKO_HAVE_EPETRA
2948 else if (eOp != Teuchos::null) {
2949 const RCP<const Epetra_CrsMatrix> crsOp =
2950 rcp_dynamic_cast<const Epetra_CrsMatrix>(eOp->epetra_op(), true);
2951 EpetraExt::BlockMapToMatrixMarketFile(("rowmap_" + filename).c_str(), crsOp->RowMap());
2952 EpetraExt::BlockMapToMatrixMarketFile(("colmap_" + filename).c_str(), crsOp->ColMap());
2953 EpetraExt::BlockMapToMatrixMarketFile(("domainmap_" + filename).c_str(), crsOp->DomainMap());
2954 EpetraExt::BlockMapToMatrixMarketFile(("rangemap_" + filename).c_str(), crsOp->RangeMap());
2955 EpetraExt::RowMatrixToMatrixMarketFile(filename.c_str(), *crsOp);
2956 }
2957#endif
2958 else if (isPhysicallyBlockedLinearOp(op)) {
2959 double scalar = 0.0;
2960 bool transp = false;
2961 RCP<const Thyra::PhysicallyBlockedLinearOpBase<double>> blocked_op =
2962 getPhysicallyBlockedLinearOp(op, &scalar, &transp);
2963
2964 int numRows = blocked_op->productRange()->numBlocks();
2965 int numCols = blocked_op->productDomain()->numBlocks();
2966
2967 for (int r = 0; r < numRows; ++r)
2968 for (int c = 0; c < numCols; ++c) {
2969 auto block = Teko::explicitScale(scalar, blocked_op->getBlock(r, c));
2970 if (transp) block = Teko::explicitTranspose(block);
2971 writeMatrix(formatBlockName(filename, r, c, numRows), block);
2972 }
2973 } else {
2974 TEUCHOS_ASSERT(false);
2975 }
2976}
2977
2978} // namespace Teko
void scale(const double alpha, MultiVector &x)
Scale a multivector by a constant.
DiagonalType
Type describing the type of diagonal to construct.
@ BlkDiag
Specifies that a block diagonal approximation is to be used.
@ NotDiag
For user convenience, if Teko recieves this value, exceptions will be thrown.
@ AbsRowSum
Specifies that the diagonal entry is .
@ Diagonal
Specifies that just the diagonal is used.
@ Lumped
Specifies that row sum is used to form a diagonal.
int blockRowCount(const BlockedLinearOp &blo)
Get the row count in a block linear operator.
int blockColCount(const BlockedLinearOp &blo)
Get the column count in a block linear operator.
BlockedLinearOp createBlockedOp()
Build a new blocked linear operator.
Implements the Epetra_Operator interface with a Thyra LinearOperator. This enables the use of absrtac...