Teko  Version of the Day
 All Classes Files Functions Variables Pages
Teko_Utilities.cpp
1 /*
2 // @HEADER
3 //
4 // ***********************************************************************
5 //
6 // Teko: A package for block and physics based preconditioning
7 // Copyright 2010 Sandia Corporation
8 //
9 // Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
10 // the U.S. Government retains certain rights in this software.
11 //
12 // Redistribution and use in source and binary forms, with or without
13 // modification, are permitted provided that the following conditions are
14 // met:
15 //
16 // 1. Redistributions of source code must retain the above copyright
17 // notice, this list of conditions and the following disclaimer.
18 //
19 // 2. Redistributions in binary form must reproduce the above copyright
20 // notice, this list of conditions and the following disclaimer in the
21 // documentation and/or other materials provided with the distribution.
22 //
23 // 3. Neither the name of the Corporation nor the names of the
24 // contributors may be used to endorse or promote products derived from
25 // this software without specific prior written permission.
26 //
27 // THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "AS IS" AND ANY
28 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
30 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SANDIA CORPORATION OR THE
31 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
32 // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
33 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
34 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
35 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
36 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
37 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38 //
39 // Questions? Contact Eric C. Cyr (eccyr@sandia.gov)
40 //
41 // ***********************************************************************
42 //
43 // @HEADER
44 
45 */
46 
47 #include "Teko_Config.h"
48 #include "Teko_Utilities.hpp"
49 
50 // Thyra includes
51 #include "Thyra_MultiVectorStdOps.hpp"
52 #include "Thyra_ZeroLinearOpBase.hpp"
53 #include "Thyra_DefaultDiagonalLinearOp.hpp"
54 #include "Thyra_DefaultAddedLinearOp.hpp"
55 #include "Thyra_DefaultScaledAdjointLinearOp.hpp"
56 #include "Thyra_DefaultMultipliedLinearOp.hpp"
57 #include "Thyra_DefaultZeroLinearOp.hpp"
58 #include "Thyra_DefaultProductMultiVector.hpp"
59 #include "Thyra_DefaultProductVectorSpace.hpp"
60 #include "Thyra_MultiVectorStdOps.hpp"
61 #include "Thyra_VectorStdOps.hpp"
62 #include "Thyra_SpmdVectorBase.hpp"
63 #include <utility>
64 
65 #ifdef TEKO_HAVE_EPETRA
66 #include "Thyra_EpetraExtDiagScaledMatProdTransformer.hpp"
67 #include "Thyra_EpetraExtDiagScalingTransformer.hpp"
68 #include "Thyra_EpetraExtAddTransformer.hpp"
69 #include "Thyra_get_Epetra_Operator.hpp"
70 #include "Thyra_EpetraThyraWrappers.hpp"
71 #include "Thyra_EpetraOperatorWrapper.hpp"
72 #include "Thyra_EpetraLinearOp.hpp"
73 #endif
74 
75 // Teuchos includes
76 #include "Teuchos_Array.hpp"
77 
78 // Epetra includes
79 #ifdef TEKO_HAVE_EPETRA
80 #include "Epetra_Operator.h"
81 #include "Epetra_CrsGraph.h"
82 #include "Epetra_CrsMatrix.h"
83 #include "Epetra_Vector.h"
84 #include "Epetra_Map.h"
85 
86 #include "EpetraExt_Transpose_RowMatrix.h"
87 #include "EpetraExt_MatrixMatrix.h"
88 
89 #include "Teko_EpetraHelpers.hpp"
90 #include "Teko_EpetraOperatorWrapper.hpp"
91 #endif
92 
93 // Anasazi includes
94 #include "AnasaziBasicEigenproblem.hpp"
95 #include "AnasaziThyraAdapter.hpp"
96 #include "AnasaziBlockKrylovSchurSolMgr.hpp"
97 #include "AnasaziBlockKrylovSchur.hpp"
98 #include "AnasaziStatusTestMaxIters.hpp"
99 
100 // Isorropia includes
101 #ifdef Teko_ENABLE_Isorropia
102 #include "Isorropia_EpetraProber.hpp"
103 #endif
104 
105 // Teko includes
106 #include "Teko_TpetraHelpers.hpp"
107 #include "Teko_TpetraOperatorWrapper.hpp"
108 
109 // Tpetra
110 #include "Thyra_TpetraLinearOp.hpp"
111 #include "Tpetra_CrsMatrix.hpp"
112 #include "Tpetra_Vector.hpp"
113 #include "Thyra_TpetraThyraWrappers.hpp"
114 #include "TpetraExt_MatrixMatrix.hpp"
115 #include "Tpetra_RowMatrixTransposer.hpp"
116 
117 #include <cmath>
118 
119 namespace Teko {
120 
121 using Teuchos::rcp;
122 using Teuchos::RCP;
123 using Teuchos::rcp_dynamic_cast;
124 #ifdef Teko_ENABLE_Isorropia
125 using Isorropia::Epetra::Prober;
126 #endif
127 
128 const Teuchos::RCP<Teuchos::FancyOStream> getOutputStream() {
129  Teuchos::RCP<Teuchos::FancyOStream> os = Teuchos::VerboseObjectBase::getDefaultOStream();
130 
131  // os->setShowProcRank(true);
132  // os->setOutputToRootOnly(-1);
133  return os;
134 }
135 
136 // distance function...not parallel...entirely internal to this cpp file
137 inline double dist(int dim, double *coords, int row, int col) {
138  double value = 0.0;
139  for (int i = 0; i < dim; i++)
140  value += std::pow(coords[dim * row + i] - coords[dim * col + i], 2.0);
141 
142  // the distance between the two
143  return std::sqrt(value);
144 }
145 
146 // distance function...not parallel...entirely internal to this cpp file
147 inline double dist(double *x, double *y, double *z, int stride, int row, int col) {
148  double value = 0.0;
149  if (x != 0) value += std::pow(x[stride * row] - x[stride * col], 2.0);
150  if (y != 0) value += std::pow(y[stride * row] - y[stride * col], 2.0);
151  if (z != 0) value += std::pow(z[stride * row] - z[stride * col], 2.0);
152 
153  // the distance between the two
154  return std::sqrt(value);
155 }
156 
175 #ifdef TEKO_HAVE_EPETRA
176 RCP<Epetra_CrsMatrix> buildGraphLaplacian(int dim, double *coords,
177  const Epetra_CrsMatrix &stencil) {
178  // allocate a new matrix with storage for the laplacian...in case of diagonals add one extra
179  // storage
180  RCP<Epetra_CrsMatrix> gl = rcp(
181  new Epetra_CrsMatrix(Copy, stencil.RowMap(), stencil.ColMap(), stencil.MaxNumEntries() + 1),
182  true);
183 
184  // allocate an additional value for the diagonal, if neccessary
185  std::vector<double> rowData(stencil.GlobalMaxNumEntries() + 1);
186  std::vector<int> rowInd(stencil.GlobalMaxNumEntries() + 1);
187 
188  // loop over all the rows
189  for (int j = 0; j < gl->NumMyRows(); j++) {
190  int row = gl->GRID(j);
191  double diagValue = 0.0;
192  int diagInd = -1;
193  int rowSz = 0;
194 
195  // extract a copy of this row...put it in rowData, rowIndicies
196  stencil.ExtractGlobalRowCopy(row, stencil.MaxNumEntries(), rowSz, &rowData[0], &rowInd[0]);
197 
198  // loop over elements of row
199  for (int i = 0; i < rowSz; i++) {
200  int col = rowInd[i];
201 
202  // is this a 0 entry masquerading as some thing else?
203  double value = rowData[i];
204  if (value == 0) continue;
205 
206  // for nondiagonal entries
207  if (row != col) {
208  double d = dist(dim, coords, row, col);
209  rowData[i] = -1.0 / d;
210  diagValue += rowData[i];
211  } else
212  diagInd = i;
213  }
214 
215  // handle diagonal entry
216  if (diagInd < 0) { // diagonal not in row
217  rowData[rowSz] = -diagValue;
218  rowInd[rowSz] = row;
219  rowSz++;
220  } else { // diagonal in row
221  rowData[diagInd] = -diagValue;
222  rowInd[diagInd] = row;
223  }
224 
225  // insert row data into graph Laplacian matrix
226  TEUCHOS_TEST_FOR_EXCEPT(gl->InsertGlobalValues(row, rowSz, &rowData[0], &rowInd[0]));
227  }
228 
229  gl->FillComplete();
230 
231  return gl;
232 }
233 #endif
234 
235 RCP<Tpetra::CrsMatrix<ST, LO, GO, NT>> buildGraphLaplacian(
236  int dim, ST *coords, const Tpetra::CrsMatrix<ST, LO, GO, NT> &stencil) {
237  // allocate a new matrix with storage for the laplacian...in case of diagonals add one extra
238  // storage
239  RCP<Tpetra::CrsMatrix<ST, LO, GO, NT>> gl = rcp(new Tpetra::CrsMatrix<ST, LO, GO, NT>(
240  stencil.getRowMap(), stencil.getColMap(), stencil.getGlobalMaxNumRowEntries() + 1));
241 
242  // allocate an additional value for the diagonal, if neccessary
243  auto rowInd = typename Tpetra::CrsMatrix<ST, LO, GO, NT>::nonconst_global_inds_host_view_type(
244  Kokkos::ViewAllocateWithoutInitializing("rowIndices"),
245  stencil.getGlobalMaxNumRowEntries() + 1);
246  auto rowData = typename Tpetra::CrsMatrix<ST, LO, GO, NT>::nonconst_values_host_view_type(
247  Kokkos::ViewAllocateWithoutInitializing("rowIndices"),
248  stencil.getGlobalMaxNumRowEntries() + 1);
249 
250  // loop over all the rows
251  for (LO j = 0; j < (LO)gl->getLocalNumRows(); j++) {
252  GO row = gl->getRowMap()->getGlobalElement(j);
253  ST diagValue = 0.0;
254  GO diagInd = -1;
255  size_t rowSz = 0;
256 
257  // extract a copy of this row...put it in rowData, rowIndicies
258  stencil.getGlobalRowCopy(row, rowInd, rowData, rowSz);
259 
260  // loop over elements of row
261  for (size_t i = 0; i < rowSz; i++) {
262  GO col = rowInd(i);
263 
264  // is this a 0 entry masquerading as some thing else?
265  ST value = rowData[i];
266  if (value == 0) continue;
267 
268  // for nondiagonal entries
269  if (row != col) {
270  ST d = dist(dim, coords, row, col);
271  rowData[i] = -1.0 / d;
272  diagValue += rowData(i);
273  } else
274  diagInd = i;
275  }
276 
277  // handle diagonal entry
278  if (diagInd < 0) { // diagonal not in row
279  rowData(rowSz) = -diagValue;
280  rowInd(rowSz) = row;
281  rowSz++;
282  } else { // diagonal in row
283  rowData(diagInd) = -diagValue;
284  rowInd(diagInd) = row;
285  }
286 
287  // insert row data into graph Laplacian matrix
288  gl->replaceGlobalValues(row, rowInd, rowData);
289  }
290 
291  gl->fillComplete();
292 
293  return gl;
294 }
295 
318 #ifdef TEKO_HAVE_EPETRA
319 RCP<Epetra_CrsMatrix> buildGraphLaplacian(double *x, double *y, double *z, int stride,
320  const Epetra_CrsMatrix &stencil) {
321  // allocate a new matrix with storage for the laplacian...in case of diagonals add one extra
322  // storage
323  RCP<Epetra_CrsMatrix> gl = rcp(
324  new Epetra_CrsMatrix(Copy, stencil.RowMap(), stencil.ColMap(), stencil.MaxNumEntries() + 1),
325  true);
326 
327  // allocate an additional value for the diagonal, if neccessary
328  std::vector<double> rowData(stencil.GlobalMaxNumEntries() + 1);
329  std::vector<int> rowInd(stencil.GlobalMaxNumEntries() + 1);
330 
331  // loop over all the rows
332  for (int j = 0; j < gl->NumMyRows(); j++) {
333  int row = gl->GRID(j);
334  double diagValue = 0.0;
335  int diagInd = -1;
336  int rowSz = 0;
337 
338  // extract a copy of this row...put it in rowData, rowIndicies
339  stencil.ExtractGlobalRowCopy(row, stencil.MaxNumEntries(), rowSz, &rowData[0], &rowInd[0]);
340 
341  // loop over elements of row
342  for (int i = 0; i < rowSz; i++) {
343  int col = rowInd[i];
344 
345  // is this a 0 entry masquerading as some thing else?
346  double value = rowData[i];
347  if (value == 0) continue;
348 
349  // for nondiagonal entries
350  if (row != col) {
351  double d = dist(x, y, z, stride, row, col);
352  rowData[i] = -1.0 / d;
353  diagValue += rowData[i];
354  } else
355  diagInd = i;
356  }
357 
358  // handle diagonal entry
359  if (diagInd < 0) { // diagonal not in row
360  rowData[rowSz] = -diagValue;
361  rowInd[rowSz] = row;
362  rowSz++;
363  } else { // diagonal in row
364  rowData[diagInd] = -diagValue;
365  rowInd[diagInd] = row;
366  }
367 
368  // insert row data into graph Laplacian matrix
369  TEUCHOS_TEST_FOR_EXCEPT(gl->InsertGlobalValues(row, rowSz, &rowData[0], &rowInd[0]));
370  }
371 
372  gl->FillComplete();
373 
374  return gl;
375 }
376 #endif
377 
378 RCP<Tpetra::CrsMatrix<ST, LO, GO, NT>> buildGraphLaplacian(
379  ST *x, ST *y, ST *z, GO stride, const Tpetra::CrsMatrix<ST, LO, GO, NT> &stencil) {
380  // allocate a new matrix with storage for the laplacian...in case of diagonals add one extra
381  // storage
382  RCP<Tpetra::CrsMatrix<ST, LO, GO, NT>> gl =
383  rcp(new Tpetra::CrsMatrix<ST, LO, GO, NT>(stencil.getRowMap(), stencil.getColMap(),
384  stencil.getGlobalMaxNumRowEntries() + 1),
385  true);
386 
387  // allocate an additional value for the diagonal, if neccessary
388  auto rowInd = typename Tpetra::CrsMatrix<ST, LO, GO, NT>::nonconst_global_inds_host_view_type(
389  Kokkos::ViewAllocateWithoutInitializing("rowIndices"),
390  stencil.getGlobalMaxNumRowEntries() + 1);
391  auto rowData = typename Tpetra::CrsMatrix<ST, LO, GO, NT>::nonconst_values_host_view_type(
392  Kokkos::ViewAllocateWithoutInitializing("rowIndices"),
393  stencil.getGlobalMaxNumRowEntries() + 1);
394 
395  // loop over all the rows
396  for (LO j = 0; j < (LO)gl->getLocalNumRows(); j++) {
397  GO row = gl->getRowMap()->getGlobalElement(j);
398  ST diagValue = 0.0;
399  GO diagInd = -1;
400  size_t rowSz = 0;
401 
402  // extract a copy of this row...put it in rowData, rowIndicies
403  stencil.getGlobalRowCopy(row, rowInd, rowData, rowSz);
404 
405  // loop over elements of row
406  for (size_t i = 0; i < rowSz; i++) {
407  GO col = rowInd(i);
408 
409  // is this a 0 entry masquerading as some thing else?
410  ST value = rowData[i];
411  if (value == 0) continue;
412 
413  // for nondiagonal entries
414  if (row != col) {
415  ST d = dist(x, y, z, stride, row, col);
416  rowData[i] = -1.0 / d;
417  diagValue += rowData(i);
418  } else
419  diagInd = i;
420  }
421 
422  // handle diagonal entry
423  if (diagInd < 0) { // diagonal not in row
424  rowData(rowSz) = -diagValue;
425  rowInd(rowSz) = row;
426  rowSz++;
427  } else { // diagonal in row
428  rowData(diagInd) = -diagValue;
429  rowInd(diagInd) = row;
430  }
431 
432  // insert row data into graph Laplacian matrix
433  gl->replaceGlobalValues(row, rowInd, rowData);
434  }
435 
436  gl->fillComplete();
437 
438  return gl;
439 }
440 
456 void applyOp(const LinearOp &A, const MultiVector &x, MultiVector &y, double alpha, double beta) {
457  Thyra::apply(*A, Thyra::NOTRANS, *x, y.ptr(), alpha, beta);
458 }
459 
475 void applyTransposeOp(const LinearOp &A, const MultiVector &x, MultiVector &y, double alpha,
476  double beta) {
477  Thyra::apply(*A, Thyra::TRANS, *x, y.ptr(), alpha, beta);
478 }
479 
482 void update(double alpha, const MultiVector &x, double beta, MultiVector &y) {
483  Teuchos::Array<double> scale;
484  Teuchos::Array<Teuchos::Ptr<const Thyra::MultiVectorBase<double>>> vec;
485 
486  // build arrays needed for linear combo
487  scale.push_back(alpha);
488  vec.push_back(x.ptr());
489 
490  // compute linear combination
491  Thyra::linear_combination<double>(scale, vec, beta, y.ptr());
492 }
493 
495 BlockedLinearOp getUpperTriBlocks(const BlockedLinearOp &blo, bool callEndBlockFill) {
496  int rows = blockRowCount(blo);
497 
498  TEUCHOS_ASSERT(rows == blockColCount(blo));
499 
500  RCP<const Thyra::ProductVectorSpaceBase<double>> range = blo->productRange();
501  RCP<const Thyra::ProductVectorSpaceBase<double>> domain = blo->productDomain();
502 
503  // allocate new operator
504  BlockedLinearOp upper = createBlockedOp();
505 
506  // build new operator
507  upper->beginBlockFill(rows, rows);
508 
509  for (int i = 0; i < rows; i++) {
510  // put zero operators on the diagonal
511  // this gurantees the vector space of
512  // the new operator are fully defined
513  RCP<const Thyra::LinearOpBase<double>> zed =
514  Thyra::zero<double>(range->getBlock(i), domain->getBlock(i));
515  upper->setBlock(i, i, zed);
516 
517  for (int j = i + 1; j < rows; j++) {
518  // get block i,j
519  LinearOp uij = blo->getBlock(i, j);
520 
521  // stuff it in U
522  if (uij != Teuchos::null) upper->setBlock(i, j, uij);
523  }
524  }
525  if (callEndBlockFill) upper->endBlockFill();
526 
527  return upper;
528 }
529 
531 BlockedLinearOp getLowerTriBlocks(const BlockedLinearOp &blo, bool callEndBlockFill) {
532  int rows = blockRowCount(blo);
533 
534  TEUCHOS_ASSERT(rows == blockColCount(blo));
535 
536  RCP<const Thyra::ProductVectorSpaceBase<double>> range = blo->productRange();
537  RCP<const Thyra::ProductVectorSpaceBase<double>> domain = blo->productDomain();
538 
539  // allocate new operator
540  BlockedLinearOp lower = createBlockedOp();
541 
542  // build new operator
543  lower->beginBlockFill(rows, rows);
544 
545  for (int i = 0; i < rows; i++) {
546  // put zero operators on the diagonal
547  // this gurantees the vector space of
548  // the new operator are fully defined
549  RCP<const Thyra::LinearOpBase<double>> zed =
550  Thyra::zero<double>(range->getBlock(i), domain->getBlock(i));
551  lower->setBlock(i, i, zed);
552 
553  for (int j = 0; j < i; j++) {
554  // get block i,j
555  LinearOp uij = blo->getBlock(i, j);
556 
557  // stuff it in U
558  if (uij != Teuchos::null) lower->setBlock(i, j, uij);
559  }
560  }
561  if (callEndBlockFill) lower->endBlockFill();
562 
563  return lower;
564 }
565 
585 BlockedLinearOp zeroBlockedOp(const BlockedLinearOp &blo) {
586  int rows = blockRowCount(blo);
587 
588  TEUCHOS_ASSERT(rows == blockColCount(blo)); // assert that matrix is square
589 
590  RCP<const Thyra::ProductVectorSpaceBase<double>> range = blo->productRange();
591  RCP<const Thyra::ProductVectorSpaceBase<double>> domain = blo->productDomain();
592 
593  // allocate new operator
594  BlockedLinearOp zeroOp = createBlockedOp();
595 
596  // build new operator
597  zeroOp->beginBlockFill(rows, rows);
598 
599  for (int i = 0; i < rows; i++) {
600  // put zero operators on the diagonal
601  // this gurantees the vector space of
602  // the new operator are fully defined
603  RCP<const Thyra::LinearOpBase<double>> zed =
604  Thyra::zero<double>(range->getBlock(i), domain->getBlock(i));
605  zeroOp->setBlock(i, i, zed);
606  }
607 
608  return zeroOp;
609 }
610 
612 bool isZeroOp(const LinearOp op) {
613  // if operator is null...then its zero!
614  if (op == Teuchos::null) return true;
615 
616  // try to cast it to a zero linear operator
617  LinearOp test = rcp_dynamic_cast<const Thyra::ZeroLinearOpBase<double>>(op);
618 
619  // if it works...then its zero...otherwise its null
620  if (test != Teuchos::null) return true;
621 
622  // See if the operator is a wrapped zero op
623  ST scalar = 0.0;
624  Thyra::EOpTransp transp = Thyra::NOTRANS;
625  RCP<const Thyra::LinearOpBase<ST>> wrapped_op;
626  Thyra::unwrap(op, &scalar, &transp, &wrapped_op);
627  test = rcp_dynamic_cast<const Thyra::ZeroLinearOpBase<double>>(wrapped_op);
628  return test != Teuchos::null;
629 }
630 
631 std::pair<ModifiableLinearOp, bool> getAbsRowSumMatrixEpetra(const LinearOp &op) {
632 #ifndef TEKO_HAVE_EPETRA
633  return std::make_pair(ModifiableLinearOp{}, false);
634 #else
635  RCP<const Epetra_CrsMatrix> eCrsOp;
636 
637  const auto eOp = rcp_dynamic_cast<const Thyra::EpetraLinearOp>(op);
638 
639  if (!eOp) {
640  return std::make_pair(ModifiableLinearOp{}, false);
641  }
642 
643  eCrsOp = rcp_dynamic_cast<const Epetra_CrsMatrix>(eOp->epetra_op(), true);
644 
645  // extract diagonal
646  const auto ptrDiag = rcp(new Epetra_Vector(eCrsOp->RowMap()));
647  Epetra_Vector &diag = *ptrDiag;
648 
649  // compute absolute value row sum
650  diag.PutScalar(0.0);
651  for (int i = 0; i < eCrsOp->NumMyRows(); i++) {
652  double *values = 0;
653  int numEntries;
654  eCrsOp->ExtractMyRowView(i, numEntries, values);
655 
656  // build abs value row sum
657  for (int j = 0; j < numEntries; j++) diag[i] += std::abs(values[j]);
658  }
659 
660  // build Thyra diagonal operator
661  return std::make_pair(Teko::Epetra::thyraDiagOp(ptrDiag, eCrsOp->RowMap(),
662  "absRowSum( " + op->getObjectLabel() + " )"),
663  true);
664 #endif
665 }
666 
667 std::pair<ModifiableLinearOp, bool> getAbsRowSumMatrixTpetra(const LinearOp &op) {
668  RCP<const Tpetra::CrsMatrix<ST, LO, GO, NT>> tCrsOp;
669 
670  const auto tOp = rcp_dynamic_cast<const Thyra::TpetraLinearOp<ST, LO, GO, NT>>(op);
671 
672  tCrsOp = rcp_dynamic_cast<const Tpetra::CrsMatrix<ST, LO, GO, NT>>(tOp->getConstTpetraOperator(),
673  true);
674 
675  // extract diagonal
676  const auto ptrDiag = Tpetra::createVector<ST, LO, GO, NT>(tCrsOp->getRowMap());
677  auto &diag = *ptrDiag;
678 
679  // compute absolute value row sum
680  diag.putScalar(0.0);
681  for (LO i = 0; i < (LO)tCrsOp->getLocalNumRows(); i++) {
682  auto numEntries = tCrsOp->getNumEntriesInLocalRow(i);
683  typename Tpetra::CrsMatrix<ST, LO, GO, NT>::local_inds_host_view_type indices;
684  typename Tpetra::CrsMatrix<ST, LO, GO, NT>::values_host_view_type values;
685  tCrsOp->getLocalRowView(i, indices, values);
686 
687  // build abs value row sum
688  for (size_t j = 0; j < numEntries; j++) diag.sumIntoLocalValue(i, std::abs(values(j)));
689  }
690 
691  // build Thyra diagonal operator
692  return std::make_pair(
693  Teko::TpetraHelpers::thyraDiagOp(ptrDiag, *tCrsOp->getRowMap(),
694  "absRowSum( " + op->getObjectLabel() + " ))"),
695  true);
696 }
697 
706 ModifiableLinearOp getAbsRowSumMatrix(const LinearOp &op) {
707  try {
708  auto eResult = getAbsRowSumMatrixEpetra(op);
709  if (eResult.second) {
710  return eResult.first;
711  }
712 
713  auto tResult = getAbsRowSumMatrixTpetra(op);
714  if (tResult.second) {
715  return tResult.first;
716  } else {
717  throw std::logic_error("Neither Epetra nor Tpetra");
718  }
719  } catch (std::exception &e) {
720  auto out = Teuchos::VerboseObjectBase::getDefaultOStream();
721 
722  *out << "Teko: getAbsRowSumMatrix requires an Epetra_CrsMatrix or a "
723  "Tpetra::CrsMatrix\n";
724  *out << " Could not extract an Epetra_Operator or a Tpetra_Operator "
725  "from a \""
726  << op->description() << std::endl;
727  *out << " OR\n";
728  *out << " Could not cast an Epetra_Operator to a Epetra_CrsMatrix or "
729  "a Tpetra_Operator to a Tpetra::CrsMatrix\n";
730  *out << std::endl;
731  *out << "*** THROWN EXCEPTION ***\n";
732  *out << e.what() << std::endl;
733  *out << "************************\n";
734 
735  throw e;
736  }
737 }
738 
747 ModifiableLinearOp getAbsRowSumInvMatrix(const LinearOp &op) {
748  // if this is a blocked operator, extract diagonals block by block
749  // FIXME: this does not add in values from off-diagonal blocks
750  RCP<const Thyra::PhysicallyBlockedLinearOpBase<double>> blocked_op =
751  rcp_dynamic_cast<const Thyra::PhysicallyBlockedLinearOpBase<double>>(op);
752  if (blocked_op != Teuchos::null) {
753  int numRows = blocked_op->productRange()->numBlocks();
754  TEUCHOS_ASSERT(blocked_op->productDomain()->numBlocks() == numRows);
755  RCP<Thyra::PhysicallyBlockedLinearOpBase<double>> blocked_diag =
756  Thyra::defaultBlockedLinearOp<double>();
757  blocked_diag->beginBlockFill(numRows, numRows);
758  for (int r = 0; r < numRows; ++r) {
759  for (int c = 0; c < numRows; ++c) {
760  if (r == c)
761  blocked_diag->setNonconstBlock(r, c, getAbsRowSumInvMatrix(blocked_op->getBlock(r, c)));
762  else
763  blocked_diag->setBlock(r, c,
764  Thyra::zero<double>(blocked_op->getBlock(r, c)->range(),
765  blocked_op->getBlock(r, c)->domain()));
766  }
767  }
768  blocked_diag->endBlockFill();
769  return blocked_diag;
770  }
771 
772  if (Teko::TpetraHelpers::isTpetraLinearOp(op)) {
773  ST scalar = 0.0;
774  bool transp = false;
775  RCP<const Tpetra::CrsMatrix<ST, LO, GO, NT>> tCrsOp =
776  Teko::TpetraHelpers::getTpetraCrsMatrix(op, &scalar, &transp);
777 
778  // extract diagonal
779  const RCP<Tpetra::Vector<ST, LO, GO, NT>> ptrDiag =
780  Tpetra::createVector<ST, LO, GO, NT>(tCrsOp->getRowMap());
781  Tpetra::Vector<ST, LO, GO, NT> &diag = *ptrDiag;
782 
783  // compute absolute value row sum
784  diag.putScalar(0.0);
785  for (LO i = 0; i < (LO)tCrsOp->getLocalNumRows(); i++) {
786  LO numEntries = tCrsOp->getNumEntriesInLocalRow(i);
787  typename Tpetra::CrsMatrix<ST, LO, GO, NT>::local_inds_host_view_type indices;
788  typename Tpetra::CrsMatrix<ST, LO, GO, NT>::values_host_view_type values;
789  tCrsOp->getLocalRowView(i, indices, values);
790 
791  // build abs value row sum
792  for (LO j = 0; j < numEntries; j++) diag.sumIntoLocalValue(i, std::abs(values(j)));
793  }
794  diag.scale(scalar);
795  diag.reciprocal(diag); // invert entries
796 
797  // build Thyra diagonal operator
798  return Teko::TpetraHelpers::thyraDiagOp(ptrDiag, *tCrsOp->getRowMap(),
799  "absRowSum( " + op->getObjectLabel() + " ))");
800 
801  } else {
802 #ifdef TEKO_HAVE_EPETRA
803  RCP<const Thyra::EpetraLinearOp> eOp = rcp_dynamic_cast<const Thyra::EpetraLinearOp>(op, true);
804  RCP<const Epetra_CrsMatrix> eCrsOp =
805  rcp_dynamic_cast<const Epetra_CrsMatrix>(eOp->epetra_op(), true);
806 
807  // extract diagonal
808  const RCP<Epetra_Vector> ptrDiag = rcp(new Epetra_Vector(eCrsOp->RowMap()));
809  Epetra_Vector &diag = *ptrDiag;
810 
811  // compute absolute value row sum
812  diag.PutScalar(0.0);
813  for (int i = 0; i < eCrsOp->NumMyRows(); i++) {
814  double *values = 0;
815  int numEntries;
816  eCrsOp->ExtractMyRowView(i, numEntries, values);
817 
818  // build abs value row sum
819  for (int j = 0; j < numEntries; j++) diag[i] += std::abs(values[j]);
820  }
821  diag.Reciprocal(diag); // invert entries
822 
823  // build Thyra diagonal operator
824  return Teko::Epetra::thyraDiagOp(ptrDiag, eCrsOp->RowMap(),
825  "absRowSum( " + op->getObjectLabel() + " )");
826 #else
827  throw std::logic_error(
828  "getAbsRowSumInvMatrix is trying to use Epetra "
829  "code, but TEKO_HAVE_EPETRA is disabled!");
830 #endif
831  }
832 }
833 
841 ModifiableLinearOp getLumpedMatrix(const LinearOp &op) {
842  RCP<Thyra::VectorBase<ST>> ones = Thyra::createMember(op->domain());
843  RCP<Thyra::VectorBase<ST>> diag = Thyra::createMember(op->range());
844 
845  // set to all ones
846  Thyra::assign(ones.ptr(), 1.0);
847 
848  // compute lumped diagonal
849  // Thyra::apply(*op,Thyra::NONCONJ_ELE,*ones,&*diag);
850  Thyra::apply(*op, Thyra::NOTRANS, *ones, diag.ptr());
851 
852  return rcp(new Thyra::DefaultDiagonalLinearOp<ST>(diag));
853 }
854 
863 ModifiableLinearOp getInvLumpedMatrix(const LinearOp &op) {
864  RCP<Thyra::VectorBase<ST>> ones = Thyra::createMember(op->domain());
865  RCP<Thyra::VectorBase<ST>> diag = Thyra::createMember(op->range());
866 
867  // set to all ones
868  Thyra::assign(ones.ptr(), 1.0);
869 
870  // compute lumped diagonal
871  Thyra::apply(*op, Thyra::NOTRANS, *ones, diag.ptr());
872  Thyra::reciprocal(*diag, diag.ptr());
873 
874  return rcp(new Thyra::DefaultDiagonalLinearOp<ST>(diag));
875 }
876 
877 const std::pair<ModifiableLinearOp, bool> getDiagonalOpEpetra(const LinearOp &op) {
878 #ifndef TEKO_HAVE_EPETRA
879  return std::make_pair(ModifiableLinearOp{}, false);
880 #else
881  RCP<const Epetra_CrsMatrix> eCrsOp;
882 
883  const auto eOp = rcp_dynamic_cast<const Thyra::EpetraLinearOp>(op);
884  if (!eOp) {
885  return std::make_pair(ModifiableLinearOp{}, false);
886  }
887 
888  eCrsOp = rcp_dynamic_cast<const Epetra_CrsMatrix>(eOp->epetra_op(), true);
889 
890  // extract diagonal
891  const auto diag = rcp(new Epetra_Vector(eCrsOp->RowMap()));
892  TEUCHOS_TEST_FOR_EXCEPT(eCrsOp->ExtractDiagonalCopy(*diag));
893 
894  // build Thyra diagonal operator
895  return std::make_pair(Teko::Epetra::thyraDiagOp(diag, eCrsOp->RowMap(),
896  "inv(diag( " + op->getObjectLabel() + " ))"),
897  true);
898 #endif
899 }
900 
901 const std::pair<ModifiableLinearOp, bool> getDiagonalOpTpetra(const LinearOp &op) {
902  RCP<const Tpetra::CrsMatrix<ST, LO, GO, NT>> tCrsOp;
903 
904  const auto tOp = rcp_dynamic_cast<const Thyra::TpetraLinearOp<ST, LO, GO, NT>>(op);
905  if (!tOp) {
906  return std::make_pair(ModifiableLinearOp{}, false);
907  }
908 
909  tCrsOp = rcp_dynamic_cast<const Tpetra::CrsMatrix<ST, LO, GO, NT>>(tOp->getConstTpetraOperator(),
910  true);
911 
912  // extract diagonal
913  const auto diag = Tpetra::createVector<ST, LO, GO, NT>(tCrsOp->getRowMap());
914  tCrsOp->getLocalDiagCopy(*diag);
915 
916  // build Thyra diagonal operator
917  return std::make_pair(
918  Teko::TpetraHelpers::thyraDiagOp(diag, *tCrsOp->getRowMap(),
919  "inv(diag( " + op->getObjectLabel() + " ))"),
920  true);
921 }
922 
934 const ModifiableLinearOp getDiagonalOp(const LinearOp &op) {
935  try {
936  // get Epetra or Tpetra Operator
937  const auto eDiagOp = getDiagonalOpEpetra(op);
938 
939  if (eDiagOp.second) {
940  return eDiagOp.first;
941  }
942 
943  const auto tDiagOp = getDiagonalOpTpetra(op);
944  if (tDiagOp.second) {
945  return tDiagOp.first;
946  } else
947  throw std::logic_error("Neither Epetra nor Tpetra");
948  } catch (std::exception &e) {
949  RCP<Teuchos::FancyOStream> out = Teuchos::VerboseObjectBase::getDefaultOStream();
950 
951  *out << "Teko: getDiagonalOp requires an Epetra_CrsMatrix or a Tpetra::CrsMatrix\n";
952  *out << " Could not extract an Epetra_Operator or a Tpetra_Operator from a \""
953  << op->description() << std::endl;
954  *out << " OR\n";
955  *out << " Could not cast an Epetra_Operator to a Epetra_CrsMatrix or a Tpetra_Operator to a "
956  "Tpetra::CrsMatrix\n";
957  *out << std::endl;
958  *out << "*** THROWN EXCEPTION ***\n";
959  *out << e.what() << std::endl;
960  *out << "************************\n";
961 
962  throw e;
963  }
964 }
965 
966 const MultiVector getDiagonal(const LinearOp &op) {
967  try {
968  // get Epetra or Tpetra Operator
969  auto diagOp = getDiagonalOpEpetra(op);
970 
971  if (!diagOp.second) {
972  diagOp = getDiagonalOpTpetra(op);
973 
974  if (!diagOp.second) {
975  throw std::logic_error("Neither Epetra nor Tpetra");
976  }
977  }
978 
979  Teuchos::RCP<const Thyra::MultiVectorBase<double>> v =
980  Teuchos::rcp_dynamic_cast<const Thyra::DiagonalLinearOpBase<double>>(diagOp.first)
981  ->getDiag();
982  return Teuchos::rcp_const_cast<Thyra::MultiVectorBase<double>>(v);
983  } catch (std::exception &e) {
984  RCP<Teuchos::FancyOStream> out = Teuchos::VerboseObjectBase::getDefaultOStream();
985 
986  *out << "Teko: getDiagonal requires an Epetra_CrsMatrix or a Tpetra::CrsMatrix\n";
987  *out << " Could not extract an Epetra_Operator or a Tpetra_Operator from a \""
988  << op->description() << std::endl;
989  *out << " OR\n";
990  *out << " Could not cast an Epetra_Operator to a Epetra_CrsMatrix or a Tpetra_Operator to a "
991  "Tpetra::CrsMatrix\n";
992  *out << std::endl;
993  *out << "*** THROWN EXCEPTION ***\n";
994  *out << e.what() << std::endl;
995  *out << "************************\n";
996 
997  throw e;
998  }
999 }
1000 
1001 const MultiVector getDiagonal(const Teko::LinearOp &A, const DiagonalType &dt) {
1002  LinearOp diagOp = Teko::getDiagonalOp(A, dt);
1003 
1004  Teuchos::RCP<const Thyra::MultiVectorBase<double>> v =
1005  Teuchos::rcp_dynamic_cast<const Thyra::DiagonalLinearOpBase<double>>(diagOp)->getDiag();
1006  return Teuchos::rcp_const_cast<Thyra::MultiVectorBase<double>>(v);
1007 }
1008 
1020 const ModifiableLinearOp getInvDiagonalOp(const LinearOp &op) {
1021  // if this is a diagonal linear op already, just take the reciprocal
1022  auto diagonal_op = rcp_dynamic_cast<const Thyra::DiagonalLinearOpBase<double>>(op);
1023  if (diagonal_op != Teuchos::null) {
1024  auto diag = diagonal_op->getDiag();
1025  auto inv_diag = diag->clone_v();
1026  Thyra::reciprocal(*diag, inv_diag.ptr());
1027  return rcp(new Thyra::DefaultDiagonalLinearOp<double>(inv_diag));
1028  }
1029 
1030  // if this is a blocked operator, extract diagonals block by block
1031  RCP<const Thyra::PhysicallyBlockedLinearOpBase<double>> blocked_op =
1032  rcp_dynamic_cast<const Thyra::PhysicallyBlockedLinearOpBase<double>>(op);
1033  if (blocked_op != Teuchos::null) {
1034  int numRows = blocked_op->productRange()->numBlocks();
1035  TEUCHOS_ASSERT(blocked_op->productDomain()->numBlocks() == numRows);
1036  RCP<Thyra::PhysicallyBlockedLinearOpBase<double>> blocked_diag =
1037  Thyra::defaultBlockedLinearOp<double>();
1038  blocked_diag->beginBlockFill(numRows, numRows);
1039  for (int r = 0; r < numRows; ++r) {
1040  for (int c = 0; c < numRows; ++c) {
1041  if (r == c)
1042  blocked_diag->setNonconstBlock(r, c, getInvDiagonalOp(blocked_op->getBlock(r, c)));
1043  else
1044  blocked_diag->setBlock(r, c,
1045  Thyra::zero<double>(blocked_op->getBlock(r, c)->range(),
1046  blocked_op->getBlock(r, c)->domain()));
1047  }
1048  }
1049  blocked_diag->endBlockFill();
1050  return blocked_diag;
1051  }
1052 
1053  if (Teko::TpetraHelpers::isTpetraLinearOp(op)) {
1054  ST scalar = 0.0;
1055  bool transp = false;
1056  RCP<const Tpetra::CrsMatrix<ST, LO, GO, NT>> tCrsOp =
1057  Teko::TpetraHelpers::getTpetraCrsMatrix(op, &scalar, &transp);
1058 
1059  // extract diagonal
1060  const RCP<Tpetra::Vector<ST, LO, GO, NT>> diag =
1061  Tpetra::createVector<ST, LO, GO, NT>(tCrsOp->getRowMap());
1062  diag->scale(scalar);
1063  tCrsOp->getLocalDiagCopy(*diag);
1064  diag->reciprocal(*diag);
1065 
1066  // build Thyra diagonal operator
1067  return Teko::TpetraHelpers::thyraDiagOp(diag, *tCrsOp->getRowMap(),
1068  "inv(diag( " + op->getObjectLabel() + " ))");
1069 
1070  } else {
1071 #ifdef TEKO_HAVE_EPETRA
1072  RCP<const Thyra::EpetraLinearOp> eOp = rcp_dynamic_cast<const Thyra::EpetraLinearOp>(op, true);
1073  RCP<const Epetra_CrsMatrix> eCrsOp =
1074  rcp_dynamic_cast<const Epetra_CrsMatrix>(eOp->epetra_op(), true);
1075 
1076  // extract diagonal
1077  const RCP<Epetra_Vector> diag = rcp(new Epetra_Vector(eCrsOp->RowMap()));
1078  TEUCHOS_TEST_FOR_EXCEPT(eCrsOp->ExtractDiagonalCopy(*diag));
1079  diag->Reciprocal(*diag);
1080 
1081  // build Thyra diagonal operator
1082  return Teko::Epetra::thyraDiagOp(diag, eCrsOp->RowMap(),
1083  "inv(diag( " + op->getObjectLabel() + " ))");
1084 #else
1085  throw std::logic_error(
1086  "getInvDiagonalOp is trying to use Epetra "
1087  "code, but TEKO_HAVE_EPETRA is disabled!");
1088 #endif
1089  }
1090 }
1091 
1104 const LinearOp explicitMultiply(const LinearOp &opl, const LinearOp &opm, const LinearOp &opr) {
1105  // if this is a blocked operator, multiply block by block
1106  // it is possible that not every factor in the product is blocked and these situations are handled
1107  // separately
1108 
1109  bool isBlockedL = isPhysicallyBlockedLinearOp(opl);
1110  bool isBlockedM = isPhysicallyBlockedLinearOp(opm);
1111  bool isBlockedR = isPhysicallyBlockedLinearOp(opr);
1112 
1113  // all factors blocked
1114  if ((isBlockedL && isBlockedM && isBlockedR)) {
1115  double scalarl = 0.0;
1116  bool transpl = false;
1117  RCP<const Thyra::PhysicallyBlockedLinearOpBase<double>> blocked_opl =
1118  getPhysicallyBlockedLinearOp(opl, &scalarl, &transpl);
1119  double scalarm = 0.0;
1120  bool transpm = false;
1121  RCP<const Thyra::PhysicallyBlockedLinearOpBase<double>> blocked_opm =
1122  getPhysicallyBlockedLinearOp(opm, &scalarm, &transpm);
1123  double scalarr = 0.0;
1124  bool transpr = false;
1125  RCP<const Thyra::PhysicallyBlockedLinearOpBase<double>> blocked_opr =
1126  getPhysicallyBlockedLinearOp(opr, &scalarr, &transpr);
1127  double scalar = scalarl * scalarm * scalarr;
1128 
1129  int numRows = blocked_opl->productRange()->numBlocks();
1130  int numCols = blocked_opr->productDomain()->numBlocks();
1131  int numMiddle = blocked_opm->productRange()->numBlocks();
1132 
1133  // Assume that the middle block is block nxn and that it's diagonal. Otherwise use the two
1134  // argument explicitMultiply twice
1135  TEUCHOS_ASSERT(blocked_opm->productDomain()->numBlocks() == numMiddle);
1136  TEUCHOS_ASSERT(blocked_opl->productDomain()->numBlocks() == numMiddle);
1137  TEUCHOS_ASSERT(blocked_opr->productRange()->numBlocks() == numMiddle);
1138 
1139  RCP<Thyra::PhysicallyBlockedLinearOpBase<double>> blocked_product =
1140  Thyra::defaultBlockedLinearOp<double>();
1141  blocked_product->beginBlockFill(numRows, numCols);
1142  for (int r = 0; r < numRows; ++r) {
1143  for (int c = 0; c < numCols; ++c) {
1144  LinearOp product_rc = explicitMultiply(
1145  blocked_opl->getBlock(r, 0), blocked_opm->getBlock(0, 0), blocked_opr->getBlock(0, c));
1146  for (int m = 1; m < numMiddle; ++m) {
1147  LinearOp product_m =
1148  explicitMultiply(blocked_opl->getBlock(r, m), blocked_opm->getBlock(m, m),
1149  blocked_opr->getBlock(m, c));
1150  product_rc = explicitAdd(product_rc, product_m);
1151  }
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  // left and right factors blocked
1160  if (isBlockedL && !isBlockedM && isBlockedR) {
1161  double scalarl = 0.0;
1162  bool transpl = false;
1163  RCP<const Thyra::PhysicallyBlockedLinearOpBase<double>> blocked_opl =
1164  getPhysicallyBlockedLinearOp(opl, &scalarl, &transpl);
1165  double scalarr = 0.0;
1166  bool transpr = false;
1167  RCP<const Thyra::PhysicallyBlockedLinearOpBase<double>> blocked_opr =
1168  getPhysicallyBlockedLinearOp(opr, &scalarr, &transpr);
1169  double scalar = scalarl * scalarr;
1170 
1171  int numRows = blocked_opl->productRange()->numBlocks();
1172  int numCols = blocked_opr->productDomain()->numBlocks();
1173  int numMiddle = 1;
1174 
1175  // Assume that the middle block is 1x1 diagonal. Left must be rx1, right 1xc
1176  TEUCHOS_ASSERT(blocked_opl->productDomain()->numBlocks() == numMiddle);
1177  TEUCHOS_ASSERT(blocked_opr->productRange()->numBlocks() == numMiddle);
1178 
1179  RCP<Thyra::PhysicallyBlockedLinearOpBase<double>> blocked_product =
1180  Thyra::defaultBlockedLinearOp<double>();
1181  blocked_product->beginBlockFill(numRows, numCols);
1182  for (int r = 0; r < numRows; ++r) {
1183  for (int c = 0; c < numCols; ++c) {
1184  LinearOp product_rc =
1185  explicitMultiply(blocked_opl->getBlock(r, 0), opm, blocked_opr->getBlock(0, c));
1186  blocked_product->setBlock(r, c, product_rc);
1187  }
1188  }
1189  blocked_product->endBlockFill();
1190  return Thyra::scale<double>(scalar, blocked_product.getConst());
1191  }
1192 
1193  // only right factor blocked
1194  if (!isBlockedL && !isBlockedM && isBlockedR) {
1195  double scalarr = 0.0;
1196  bool transpr = false;
1197  RCP<const Thyra::PhysicallyBlockedLinearOpBase<double>> blocked_opr =
1198  getPhysicallyBlockedLinearOp(opr, &scalarr, &transpr);
1199  double scalar = scalarr;
1200 
1201  int numRows = 1;
1202  int numCols = blocked_opr->productDomain()->numBlocks();
1203  int numMiddle = 1;
1204 
1205  // Assume that the middle block is 1x1 diagonal, left is 1x1. Right must be 1xc
1206  TEUCHOS_ASSERT(blocked_opr->productRange()->numBlocks() == numMiddle);
1207 
1208  RCP<Thyra::PhysicallyBlockedLinearOpBase<double>> blocked_product =
1209  Thyra::defaultBlockedLinearOp<double>();
1210  blocked_product->beginBlockFill(numRows, numCols);
1211  for (int c = 0; c < numCols; ++c) {
1212  LinearOp product_c = explicitMultiply(opl, opm, blocked_opr->getBlock(0, c));
1213  blocked_product->setBlock(0, c, product_c);
1214  }
1215  blocked_product->endBlockFill();
1216  return Thyra::scale<double>(scalar, blocked_product.getConst());
1217  }
1218 
1219  // TODO: three more cases (only non-blocked - blocked - non-blocked not possible)
1220 
1221  bool isTpetral = Teko::TpetraHelpers::isTpetraLinearOp(opl);
1222  bool isTpetram = Teko::TpetraHelpers::isTpetraLinearOp(opm);
1223  bool isTpetrar = Teko::TpetraHelpers::isTpetraLinearOp(opr);
1224 
1225  if (isTpetral && isTpetram &&
1226  isTpetrar) { // Both operators are Tpetra matrices so explicitly multiply them
1227 
1228  // Get left and right Tpetra crs operators
1229  ST scalarl = 0.0;
1230  bool transpl = false;
1231  RCP<const Tpetra::CrsMatrix<ST, LO, GO, NT>> tCrsOpl =
1232  Teko::TpetraHelpers::getTpetraCrsMatrix(opl, &scalarl, &transpl);
1233  ST scalarm = 0.0;
1234  bool transpm = false;
1235  RCP<const Tpetra::CrsMatrix<ST, LO, GO, NT>> tCrsOpm =
1236  Teko::TpetraHelpers::getTpetraCrsMatrix(opl, &scalarm, &transpm);
1237  ST scalarr = 0.0;
1238  bool transpr = false;
1239  RCP<const Tpetra::CrsMatrix<ST, LO, GO, NT>> tCrsOpr =
1240  Teko::TpetraHelpers::getTpetraCrsMatrix(opr, &scalarr, &transpr);
1241 
1242  // Build output operator
1243  RCP<Thyra::LinearOpBase<ST>> explicitOp = rcp(new Thyra::TpetraLinearOp<ST, LO, GO, NT>());
1244  RCP<Thyra::TpetraLinearOp<ST, LO, GO, NT>> tExplicitOp =
1245  rcp_dynamic_cast<Thyra::TpetraLinearOp<ST, LO, GO, NT>>(explicitOp);
1246 
1247  // Do explicit matrix-matrix multiply
1248  RCP<Tpetra::CrsMatrix<ST, LO, GO, NT>> tCrsOplm =
1249  Tpetra::createCrsMatrix<ST, LO, GO, NT>(tCrsOpl->getRowMap());
1250  RCP<Tpetra::CrsMatrix<ST, LO, GO, NT>> explicitCrsOp =
1251  Tpetra::createCrsMatrix<ST, LO, GO, NT>(tCrsOpl->getRowMap());
1252  Tpetra::MatrixMatrix::Multiply<ST, LO, GO, NT>(*tCrsOpl, transpl, *tCrsOpm, transpm, *tCrsOplm);
1253  Tpetra::MatrixMatrix::Multiply<ST, LO, GO, NT>(*tCrsOplm, false, *tCrsOpr, transpr,
1254  *explicitCrsOp);
1255  explicitCrsOp->resumeFill();
1256  explicitCrsOp->scale(scalarl * scalarm * scalarr);
1257  explicitCrsOp->fillComplete(tCrsOpr->getDomainMap(), tCrsOpl->getRangeMap());
1258  tExplicitOp->initialize(Thyra::tpetraVectorSpace<ST, LO, GO, NT>(explicitCrsOp->getRangeMap()),
1259  Thyra::tpetraVectorSpace<ST, LO, GO, NT>(explicitCrsOp->getDomainMap()),
1260  explicitCrsOp);
1261  return tExplicitOp;
1262 
1263  } else if (isTpetral && !isTpetram && isTpetrar) { // Assume that the middle operator is diagonal
1264 
1265  // Get left and right Tpetra crs operators
1266  ST scalarl = 0.0;
1267  bool transpl = false;
1268  RCP<const Tpetra::CrsMatrix<ST, LO, GO, NT>> tCrsOpl =
1269  Teko::TpetraHelpers::getTpetraCrsMatrix(opl, &scalarl, &transpl);
1270  ST scalarr = 0.0;
1271  bool transpr = false;
1272  RCP<const Tpetra::CrsMatrix<ST, LO, GO, NT>> tCrsOpr =
1273  Teko::TpetraHelpers::getTpetraCrsMatrix(opr, &scalarr, &transpr);
1274 
1275  RCP<const Tpetra::Vector<ST, LO, GO, NT>> diagPtr;
1276 
1277  // Cast middle operator as DiagonalLinearOp and extract diagonal as Vector
1278  RCP<const Thyra::DiagonalLinearOpBase<ST>> dOpm =
1279  rcp_dynamic_cast<const Thyra::DiagonalLinearOpBase<ST>>(opm);
1280  if (dOpm != Teuchos::null) {
1281  RCP<const Thyra::TpetraVector<ST, LO, GO, NT>> tPtr =
1282  rcp_dynamic_cast<const Thyra::TpetraVector<ST, LO, GO, NT>>(dOpm->getDiag(), true);
1283  diagPtr = rcp_dynamic_cast<const Tpetra::Vector<ST, LO, GO, NT>>(tPtr->getConstTpetraVector(),
1284  true);
1285  }
1286  // If it's not diagonal, maybe it's zero
1287  else if (rcp_dynamic_cast<const Thyra::ZeroLinearOpBase<ST>>(opm) != Teuchos::null) {
1288  diagPtr = rcp(new Tpetra::Vector<ST, LO, GO, NT>(tCrsOpl->getDomainMap()));
1289  } else
1290  TEUCHOS_ASSERT(false);
1291 
1292  RCP<Tpetra::CrsMatrix<ST, LO, GO, NT>> tCrsOplm =
1293  Tpetra::importAndFillCompleteCrsMatrix<Tpetra::CrsMatrix<ST, LO, GO, NT>>(
1294  tCrsOpl, Tpetra::Import<LO, GO, NT>(tCrsOpl->getRowMap(), tCrsOpl->getRowMap()));
1295 
1296  // Do the diagonal scaling
1297  tCrsOplm->rightScale(*diagPtr);
1298 
1299  // Build output operator
1300  RCP<Thyra::LinearOpBase<ST>> explicitOp = rcp(new Thyra::TpetraLinearOp<ST, LO, GO, NT>());
1301  RCP<Thyra::TpetraLinearOp<ST, LO, GO, NT>> tExplicitOp =
1302  rcp_dynamic_cast<Thyra::TpetraLinearOp<ST, LO, GO, NT>>(explicitOp);
1303 
1304  // Do explicit matrix-matrix multiply
1305  RCP<Tpetra::CrsMatrix<ST, LO, GO, NT>> explicitCrsOp =
1306  Tpetra::createCrsMatrix<ST, LO, GO, NT>(tCrsOpl->getRowMap());
1307  Tpetra::MatrixMatrix::Multiply<ST, LO, GO, NT>(*tCrsOplm, false, *tCrsOpr, transpr,
1308  *explicitCrsOp);
1309  explicitCrsOp->resumeFill();
1310  explicitCrsOp->scale(scalarl * scalarr);
1311  explicitCrsOp->fillComplete(tCrsOpr->getDomainMap(), tCrsOpl->getRangeMap());
1312  tExplicitOp->initialize(Thyra::tpetraVectorSpace<ST, LO, GO, NT>(explicitCrsOp->getRangeMap()),
1313  Thyra::tpetraVectorSpace<ST, LO, GO, NT>(explicitCrsOp->getDomainMap()),
1314  explicitCrsOp);
1315  return tExplicitOp;
1316 
1317  } else { // Assume Epetra and we can use transformers
1318 #ifdef TEKO_HAVE_EPETRA
1319  // build implicit multiply
1320  const LinearOp implicitOp = Thyra::multiply(opl, opm, opr);
1321 
1322  // build transformer
1323  const RCP<Thyra::LinearOpTransformerBase<double>> prodTrans =
1324  Thyra::epetraExtDiagScaledMatProdTransformer();
1325 
1326  // build operator and multiply
1327  const RCP<Thyra::LinearOpBase<double>> explicitOp = prodTrans->createOutputOp();
1328  prodTrans->transform(*implicitOp, explicitOp.ptr());
1329  explicitOp->setObjectLabel("explicit( " + opl->getObjectLabel() + " * " +
1330  opm->getObjectLabel() + " * " + opr->getObjectLabel() + " )");
1331 
1332  return explicitOp;
1333 #else
1334  throw std::logic_error(
1335  "explicitMultiply is trying to use Epetra "
1336  "code, but TEKO_HAVE_EPETRA is disabled!");
1337 #endif
1338  }
1339 }
1340 
1355 const ModifiableLinearOp explicitMultiply(const LinearOp &opl, const LinearOp &opm,
1356  const LinearOp &opr, const ModifiableLinearOp &destOp) {
1357  bool isTpetral = Teko::TpetraHelpers::isTpetraLinearOp(opl);
1358  bool isTpetram = Teko::TpetraHelpers::isTpetraLinearOp(opm);
1359  bool isTpetrar = Teko::TpetraHelpers::isTpetraLinearOp(opr);
1360 
1361  if (isTpetral && isTpetram &&
1362  isTpetrar) { // Both operators are Tpetra matrices so explicitly multiply them
1363 
1364  // Get left and right Tpetra crs operators
1365  ST scalarl = 0.0;
1366  bool transpl = false;
1367  RCP<const Tpetra::CrsMatrix<ST, LO, GO, NT>> tCrsOpl =
1368  Teko::TpetraHelpers::getTpetraCrsMatrix(opl, &scalarl, &transpl);
1369  ST scalarm = 0.0;
1370  bool transpm = false;
1371  RCP<const Tpetra::CrsMatrix<ST, LO, GO, NT>> tCrsOpm =
1372  Teko::TpetraHelpers::getTpetraCrsMatrix(opm, &scalarm, &transpm);
1373  ST scalarr = 0.0;
1374  bool transpr = false;
1375  RCP<const Tpetra::CrsMatrix<ST, LO, GO, NT>> tCrsOpr =
1376  Teko::TpetraHelpers::getTpetraCrsMatrix(opr, &scalarr, &transpr);
1377 
1378  // Build output operator
1379  auto tExplicitOp = rcp_dynamic_cast<Thyra::TpetraLinearOp<ST, LO, GO, NT>>(destOp);
1380  if (tExplicitOp.is_null()) tExplicitOp = rcp(new Thyra::TpetraLinearOp<ST, LO, GO, NT>());
1381 
1382  // Do explicit matrix-matrix multiply
1383  RCP<Tpetra::CrsMatrix<ST, LO, GO, NT>> tCrsOplm =
1384  Tpetra::createCrsMatrix<ST, LO, GO, NT>(tCrsOpl->getRowMap());
1385  RCP<Tpetra::CrsMatrix<ST, LO, GO, NT>> explicitCrsOp =
1386  Tpetra::createCrsMatrix<ST, LO, GO, NT>(tCrsOpl->getRowMap());
1387  Tpetra::MatrixMatrix::Multiply<ST, LO, GO, NT>(*tCrsOpl, transpl, *tCrsOpm, transpm, *tCrsOplm);
1388  Tpetra::MatrixMatrix::Multiply<ST, LO, GO, NT>(*tCrsOplm, false, *tCrsOpr, transpr,
1389  *explicitCrsOp);
1390  explicitCrsOp->resumeFill();
1391  explicitCrsOp->scale(scalarl * scalarm * scalarr);
1392  explicitCrsOp->fillComplete(tCrsOpr->getDomainMap(), tCrsOpl->getRangeMap());
1393  tExplicitOp->initialize(Thyra::tpetraVectorSpace<ST, LO, GO, NT>(explicitCrsOp->getRangeMap()),
1394  Thyra::tpetraVectorSpace<ST, LO, GO, NT>(explicitCrsOp->getDomainMap()),
1395  explicitCrsOp);
1396  return tExplicitOp;
1397 
1398  } else if (isTpetral && !isTpetram && isTpetrar) { // Assume that the middle operator is diagonal
1399 
1400  // Get left and right Tpetra crs operators
1401  ST scalarl = 0.0;
1402  bool transpl = false;
1403  RCP<const Tpetra::CrsMatrix<ST, LO, GO, NT>> tCrsOpl =
1404  Teko::TpetraHelpers::getTpetraCrsMatrix(opl, &scalarl, &transpl);
1405  ST scalarr = 0.0;
1406  bool transpr = false;
1407  RCP<const Tpetra::CrsMatrix<ST, LO, GO, NT>> tCrsOpr =
1408  Teko::TpetraHelpers::getTpetraCrsMatrix(opr, &scalarr, &transpr);
1409 
1410  // Cast middle operator as DiagonalLinearOp and extract diagonal as Vector
1411  RCP<const Thyra::DiagonalLinearOpBase<ST>> dOpm =
1412  rcp_dynamic_cast<const Thyra::DiagonalLinearOpBase<ST>>(opm, true);
1413  RCP<const Thyra::TpetraVector<ST, LO, GO, NT>> tPtr =
1414  rcp_dynamic_cast<const Thyra::TpetraVector<ST, LO, GO, NT>>(dOpm->getDiag(), true);
1415  RCP<const Tpetra::Vector<ST, LO, GO, NT>> diagPtr =
1416  rcp_dynamic_cast<const Tpetra::Vector<ST, LO, GO, NT>>(tPtr->getConstTpetraVector(), true);
1417  RCP<Tpetra::CrsMatrix<ST, LO, GO, NT>> tCrsOplm =
1418  Tpetra::importAndFillCompleteCrsMatrix<Tpetra::CrsMatrix<ST, LO, GO, NT>>(
1419  tCrsOpl, Tpetra::Import<LO, GO, NT>(tCrsOpl->getRowMap(), tCrsOpl->getRowMap()));
1420 
1421  // Do the diagonal scaling
1422  tCrsOplm->rightScale(*diagPtr);
1423 
1424  // Build output operator
1425  RCP<Thyra::LinearOpBase<ST>> explicitOp = rcp(new Thyra::TpetraLinearOp<ST, LO, GO, NT>());
1426  RCP<Thyra::TpetraLinearOp<ST, LO, GO, NT>> tExplicitOp =
1427  rcp_dynamic_cast<Thyra::TpetraLinearOp<ST, LO, GO, NT>>(explicitOp);
1428 
1429  // Do explicit matrix-matrix multiply
1430  RCP<Tpetra::CrsMatrix<ST, LO, GO, NT>> explicitCrsOp =
1431  Tpetra::createCrsMatrix<ST, LO, GO, NT>(tCrsOpl->getRowMap());
1432  Tpetra::MatrixMatrix::Multiply<ST, LO, GO, NT>(*tCrsOplm, false, *tCrsOpr, transpr,
1433  *explicitCrsOp);
1434  explicitCrsOp->resumeFill();
1435  explicitCrsOp->scale(scalarl * scalarr);
1436  explicitCrsOp->fillComplete(tCrsOpr->getDomainMap(), tCrsOpl->getRangeMap());
1437  tExplicitOp->initialize(Thyra::tpetraVectorSpace<ST, LO, GO, NT>(explicitCrsOp->getRangeMap()),
1438  Thyra::tpetraVectorSpace<ST, LO, GO, NT>(explicitCrsOp->getDomainMap()),
1439  explicitCrsOp);
1440  return tExplicitOp;
1441 
1442  } else { // Assume Epetra and we can use transformers
1443 #ifdef TEKO_HAVE_EPETRA
1444  // build implicit multiply
1445  const LinearOp implicitOp = Thyra::multiply(opl, opm, opr);
1446 
1447  // build transformer
1448  const RCP<Thyra::LinearOpTransformerBase<double>> prodTrans =
1449  Thyra::epetraExtDiagScaledMatProdTransformer();
1450 
1451  // build operator destination operator
1452  ModifiableLinearOp explicitOp;
1453 
1454  // if neccessary build a operator to put the explicit multiply into
1455  if (destOp == Teuchos::null)
1456  explicitOp = prodTrans->createOutputOp();
1457  else
1458  explicitOp = destOp;
1459 
1460  // perform multiplication
1461  prodTrans->transform(*implicitOp, explicitOp.ptr());
1462 
1463  // label it
1464  explicitOp->setObjectLabel("explicit( " + opl->getObjectLabel() + " * " +
1465  opm->getObjectLabel() + " * " + opr->getObjectLabel() + " )");
1466 
1467  return explicitOp;
1468 #else
1469  throw std::logic_error(
1470  "explicitMultiply is trying to use Epetra "
1471  "code, but TEKO_HAVE_EPETRA is disabled!");
1472 #endif
1473  }
1474 }
1475 
1486 const LinearOp explicitMultiply(const LinearOp &opl, const LinearOp &opr) {
1487  // if this is a blocked operator, multiply block by block
1488  // it is possible that not every factor in the product is blocked and these situations are handled
1489  // separately
1490 
1491  bool isBlockedL = isPhysicallyBlockedLinearOp(opl);
1492  bool isBlockedR = isPhysicallyBlockedLinearOp(opr);
1493 
1494  // both factors blocked
1495  if ((isBlockedL && isBlockedR)) {
1496  double scalarl = 0.0;
1497  bool transpl = false;
1498  RCP<const Thyra::PhysicallyBlockedLinearOpBase<double>> blocked_opl =
1499  getPhysicallyBlockedLinearOp(opl, &scalarl, &transpl);
1500  double scalarr = 0.0;
1501  bool transpr = false;
1502  RCP<const Thyra::PhysicallyBlockedLinearOpBase<double>> blocked_opr =
1503  getPhysicallyBlockedLinearOp(opr, &scalarr, &transpr);
1504  double scalar = scalarl * scalarr;
1505 
1506  int numRows = blocked_opl->productRange()->numBlocks();
1507  int numCols = blocked_opr->productDomain()->numBlocks();
1508  int numMiddle = blocked_opl->productDomain()->numBlocks();
1509 
1510  TEUCHOS_ASSERT(blocked_opr->productRange()->numBlocks() == numMiddle);
1511 
1512  RCP<Thyra::PhysicallyBlockedLinearOpBase<double>> blocked_product =
1513  Thyra::defaultBlockedLinearOp<double>();
1514  blocked_product->beginBlockFill(numRows, numCols);
1515  for (int r = 0; r < numRows; ++r) {
1516  for (int c = 0; c < numCols; ++c) {
1517  LinearOp product_rc =
1518  explicitMultiply(blocked_opl->getBlock(r, 0), blocked_opr->getBlock(0, c));
1519  for (int m = 1; m < numMiddle; ++m) {
1520  LinearOp product_m =
1521  explicitMultiply(blocked_opl->getBlock(r, m), blocked_opr->getBlock(m, c));
1522  product_rc = explicitAdd(product_rc, product_m);
1523  }
1524  blocked_product->setBlock(r, c, Thyra::scale(scalar, product_rc));
1525  }
1526  }
1527  blocked_product->endBlockFill();
1528  return blocked_product;
1529  }
1530 
1531  // only left factor blocked
1532  if ((isBlockedL && !isBlockedR)) {
1533  double scalarl = 0.0;
1534  bool transpl = false;
1535  RCP<const Thyra::PhysicallyBlockedLinearOpBase<double>> blocked_opl =
1536  getPhysicallyBlockedLinearOp(opl, &scalarl, &transpl);
1537  double scalar = scalarl;
1538 
1539  int numRows = blocked_opl->productRange()->numBlocks();
1540  int numCols = 1;
1541  int numMiddle = 1;
1542 
1543  TEUCHOS_ASSERT(blocked_opl->productDomain()->numBlocks() == numMiddle);
1544 
1545  RCP<Thyra::PhysicallyBlockedLinearOpBase<double>> blocked_product =
1546  Thyra::defaultBlockedLinearOp<double>();
1547  blocked_product->beginBlockFill(numRows, numCols);
1548  for (int r = 0; r < numRows; ++r) {
1549  LinearOp product_r = explicitMultiply(blocked_opl->getBlock(r, 0), opr);
1550  blocked_product->setBlock(r, 0, Thyra::scale(scalar, product_r));
1551  }
1552  blocked_product->endBlockFill();
1553  return blocked_product;
1554  }
1555 
1556  // only right factor blocked
1557  if ((!isBlockedL && isBlockedR)) {
1558  double scalarr = 0.0;
1559  bool transpr = false;
1560  RCP<const Thyra::PhysicallyBlockedLinearOpBase<double>> blocked_opr =
1561  getPhysicallyBlockedLinearOp(opr, &scalarr, &transpr);
1562  double scalar = scalarr;
1563 
1564  int numRows = 1;
1565  int numCols = blocked_opr->productDomain()->numBlocks();
1566  int numMiddle = 1;
1567 
1568  TEUCHOS_ASSERT(blocked_opr->productRange()->numBlocks() == numMiddle);
1569 
1570  RCP<Thyra::PhysicallyBlockedLinearOpBase<double>> blocked_product =
1571  Thyra::defaultBlockedLinearOp<double>();
1572  blocked_product->beginBlockFill(numRows, numCols);
1573  for (int c = 0; c < numCols; ++c) {
1574  LinearOp product_c = explicitMultiply(opl, blocked_opr->getBlock(0, c));
1575  blocked_product->setBlock(0, c, Thyra::scale(scalar, product_c));
1576  }
1577  blocked_product->endBlockFill();
1578  return blocked_product;
1579  }
1580 
1581  bool isTpetral = Teko::TpetraHelpers::isTpetraLinearOp(opl);
1582  bool isTpetrar = Teko::TpetraHelpers::isTpetraLinearOp(opr);
1583 
1584  if (isTpetral && isTpetrar) { // Both operators are Tpetra matrices so explicitly multiply them
1585  // Get left and right Tpetra crs operators
1586  ST scalarl = 0.0;
1587  bool transpl = false;
1588  RCP<const Tpetra::CrsMatrix<ST, LO, GO, NT>> tCrsOpl =
1589  Teko::TpetraHelpers::getTpetraCrsMatrix(opl, &scalarl, &transpl);
1590  ST scalarr = 0.0;
1591  bool transpr = false;
1592  RCP<const Tpetra::CrsMatrix<ST, LO, GO, NT>> tCrsOpr =
1593  Teko::TpetraHelpers::getTpetraCrsMatrix(opr, &scalarr, &transpr);
1594 
1595  // Build output operator
1596  RCP<Thyra::LinearOpBase<ST>> explicitOp = rcp(new Thyra::TpetraLinearOp<ST, LO, GO, NT>());
1597  RCP<Thyra::TpetraLinearOp<ST, LO, GO, NT>> tExplicitOp =
1598  rcp_dynamic_cast<Thyra::TpetraLinearOp<ST, LO, GO, NT>>(explicitOp);
1599 
1600  // Do explicit matrix-matrix multiply
1601  RCP<Tpetra::CrsMatrix<ST, LO, GO, NT>> explicitCrsOp =
1602  Tpetra::createCrsMatrix<ST, LO, GO, NT>(tCrsOpl->getRowMap());
1603  Tpetra::MatrixMatrix::Multiply<ST, LO, GO, NT>(*tCrsOpl, transpl, *tCrsOpr, transpr,
1604  *explicitCrsOp);
1605  explicitCrsOp->resumeFill();
1606  explicitCrsOp->scale(scalarl * scalarr);
1607  explicitCrsOp->fillComplete(tCrsOpr->getDomainMap(), tCrsOpl->getRangeMap());
1608  tExplicitOp->initialize(Thyra::tpetraVectorSpace<ST, LO, GO, NT>(explicitCrsOp->getRangeMap()),
1609  Thyra::tpetraVectorSpace<ST, LO, GO, NT>(explicitCrsOp->getDomainMap()),
1610  explicitCrsOp);
1611  return tExplicitOp;
1612 
1613  } else if (isTpetral && !isTpetrar) { // Assume that the right operator is diagonal
1614 
1615  // Get left Tpetra crs operator
1616  ST scalarl = 0.0;
1617  bool transpl = false;
1618  RCP<const Tpetra::CrsMatrix<ST, LO, GO, NT>> tCrsOpl =
1619  Teko::TpetraHelpers::getTpetraCrsMatrix(opl, &scalarl, &transpl);
1620 
1621  // Cast right operator as DiagonalLinearOp and extract diagonal as Vector
1622  RCP<const Thyra::DiagonalLinearOpBase<ST>> dOpr =
1623  rcp_dynamic_cast<const Thyra::DiagonalLinearOpBase<ST>>(opr, true);
1624  RCP<const Thyra::TpetraVector<ST, LO, GO, NT>> tPtr =
1625  rcp_dynamic_cast<const Thyra::TpetraVector<ST, LO, GO, NT>>(dOpr->getDiag(), true);
1626  RCP<const Tpetra::Vector<ST, LO, GO, NT>> diagPtr =
1627  rcp_dynamic_cast<const Tpetra::Vector<ST, LO, GO, NT>>(tPtr->getConstTpetraVector(), true);
1628  RCP<Tpetra::CrsMatrix<ST, LO, GO, NT>> explicitCrsOp =
1629  Tpetra::importAndFillCompleteCrsMatrix<Tpetra::CrsMatrix<ST, LO, GO, NT>>(
1630  tCrsOpl, Tpetra::Import<LO, GO, NT>(tCrsOpl->getRowMap(), tCrsOpl->getRowMap()));
1631 
1632  explicitCrsOp->rightScale(*diagPtr);
1633  explicitCrsOp->resumeFill();
1634  explicitCrsOp->scale(scalarl);
1635  explicitCrsOp->fillComplete(tCrsOpl->getDomainMap(), tCrsOpl->getRangeMap());
1636 
1637  return Thyra::constTpetraLinearOp<ST, LO, GO, NT>(
1638  Thyra::tpetraVectorSpace<ST, LO, GO, NT>(explicitCrsOp->getRangeMap()),
1639  Thyra::tpetraVectorSpace<ST, LO, GO, NT>(explicitCrsOp->getDomainMap()), explicitCrsOp);
1640 
1641  } else if (!isTpetral && isTpetrar) { // Assume that the left operator is diagonal
1642 
1643  // Get right Tpetra crs operator
1644  ST scalarr = 0.0;
1645  bool transpr = false;
1646  RCP<const Tpetra::CrsMatrix<ST, LO, GO, NT>> tCrsOpr =
1647  Teko::TpetraHelpers::getTpetraCrsMatrix(opr, &scalarr, &transpr);
1648 
1649  RCP<const Tpetra::Vector<ST, LO, GO, NT>> diagPtr;
1650 
1651  // Cast left operator as DiagonalLinearOp and extract diagonal as Vector
1652  RCP<const Thyra::DiagonalLinearOpBase<ST>> dOpl =
1653  rcp_dynamic_cast<const Thyra::DiagonalLinearOpBase<ST>>(opl);
1654  if (dOpl != Teuchos::null) {
1655  RCP<const Thyra::TpetraVector<ST, LO, GO, NT>> tPtr =
1656  rcp_dynamic_cast<const Thyra::TpetraVector<ST, LO, GO, NT>>(dOpl->getDiag(), true);
1657  diagPtr = rcp_dynamic_cast<const Tpetra::Vector<ST, LO, GO, NT>>(tPtr->getConstTpetraVector(),
1658  true);
1659  }
1660  // If it's not diagonal, maybe it's zero
1661  else if (rcp_dynamic_cast<const Thyra::ZeroLinearOpBase<ST>>(opl) != Teuchos::null) {
1662  diagPtr = rcp(new Tpetra::Vector<ST, LO, GO, NT>(tCrsOpr->getRangeMap()));
1663  } else
1664  TEUCHOS_ASSERT(false);
1665 
1666  RCP<Tpetra::CrsMatrix<ST, LO, GO, NT>> explicitCrsOp =
1667  Tpetra::importAndFillCompleteCrsMatrix<Tpetra::CrsMatrix<ST, LO, GO, NT>>(
1668  tCrsOpr, Tpetra::Import<LO, GO, NT>(tCrsOpr->getRowMap(), tCrsOpr->getRowMap()));
1669 
1670  explicitCrsOp->leftScale(*diagPtr);
1671  explicitCrsOp->resumeFill();
1672  explicitCrsOp->scale(scalarr);
1673  explicitCrsOp->fillComplete(tCrsOpr->getDomainMap(), tCrsOpr->getRangeMap());
1674 
1675  return Thyra::constTpetraLinearOp<ST, LO, GO, NT>(
1676  Thyra::tpetraVectorSpace<ST, LO, GO, NT>(explicitCrsOp->getRangeMap()),
1677  Thyra::tpetraVectorSpace<ST, LO, GO, NT>(explicitCrsOp->getDomainMap()), explicitCrsOp);
1678 
1679  } else { // Assume Epetra and we can use transformers
1680 #ifdef TEKO_HAVE_EPETRA
1681  // build implicit multiply
1682  const LinearOp implicitOp = Thyra::multiply(opl, opr);
1683 
1684  // build a scaling transformer
1685  RCP<Thyra::LinearOpTransformerBase<double>> prodTrans =
1686  Thyra::epetraExtDiagScalingTransformer();
1687 
1688  // check to see if a scaling transformer works: if not use the
1689  // DiagScaledMatrixProduct transformer
1690  if (not prodTrans->isCompatible(*implicitOp))
1691  prodTrans = Thyra::epetraExtDiagScaledMatProdTransformer();
1692 
1693  // build operator and multiply
1694  const RCP<Thyra::LinearOpBase<double>> explicitOp = prodTrans->createOutputOp();
1695  prodTrans->transform(*implicitOp, explicitOp.ptr());
1696  explicitOp->setObjectLabel("explicit( " + opl->getObjectLabel() + " * " +
1697  opr->getObjectLabel() + " )");
1698 
1699  return explicitOp;
1700 #else
1701  throw std::logic_error(
1702  "explicitMultiply is trying to use Epetra "
1703  "code, but TEKO_HAVE_EPETRA is disabled!");
1704 #endif
1705  }
1706 }
1707 
1721 const ModifiableLinearOp explicitMultiply(const LinearOp &opl, const LinearOp &opr,
1722  const ModifiableLinearOp &destOp) {
1723  // if this is a blocked operator, multiply block by block
1724  // it is possible that not every factor in the product is blocked and these situations are handled
1725  // separately
1726 
1727  bool isBlockedL = isPhysicallyBlockedLinearOp(opl);
1728  bool isBlockedR = isPhysicallyBlockedLinearOp(opr);
1729 
1730  // both factors blocked
1731  if ((isBlockedL && isBlockedR)) {
1732  double scalarl = 0.0;
1733  bool transpl = false;
1734  RCP<const Thyra::PhysicallyBlockedLinearOpBase<double>> blocked_opl =
1735  getPhysicallyBlockedLinearOp(opl, &scalarl, &transpl);
1736  double scalarr = 0.0;
1737  bool transpr = false;
1738  RCP<const Thyra::PhysicallyBlockedLinearOpBase<double>> blocked_opr =
1739  getPhysicallyBlockedLinearOp(opr, &scalarr, &transpr);
1740  double scalar = scalarl * scalarr;
1741 
1742  int numRows = blocked_opl->productRange()->numBlocks();
1743  int numCols = blocked_opr->productDomain()->numBlocks();
1744  int numMiddle = blocked_opl->productDomain()->numBlocks();
1745 
1746  TEUCHOS_ASSERT(blocked_opr->productRange()->numBlocks() == numMiddle);
1747 
1748  RCP<Thyra::PhysicallyBlockedLinearOpBase<double>> blocked_product =
1749  Thyra::defaultBlockedLinearOp<double>();
1750  blocked_product->beginBlockFill(numRows, numCols);
1751  for (int r = 0; r < numRows; ++r) {
1752  for (int c = 0; c < numCols; ++c) {
1753  LinearOp product_rc =
1754  explicitMultiply(blocked_opl->getBlock(r, 0), blocked_opr->getBlock(0, c));
1755  for (int m = 1; m < numMiddle; ++m) {
1756  LinearOp product_m =
1757  explicitMultiply(blocked_opl->getBlock(r, m), blocked_opr->getBlock(m, c));
1758  product_rc = explicitAdd(product_rc, product_m);
1759  }
1760  blocked_product->setBlock(r, c, Thyra::scale(scalar, product_rc));
1761  }
1762  }
1763  blocked_product->endBlockFill();
1764  return blocked_product;
1765  }
1766 
1767  // only left factor blocked
1768  if ((isBlockedL && !isBlockedR)) {
1769  double scalarl = 0.0;
1770  bool transpl = false;
1771  RCP<const Thyra::PhysicallyBlockedLinearOpBase<double>> blocked_opl =
1772  getPhysicallyBlockedLinearOp(opl, &scalarl, &transpl);
1773  double scalar = scalarl;
1774 
1775  int numRows = blocked_opl->productRange()->numBlocks();
1776  int numCols = 1;
1777  int numMiddle = 1;
1778 
1779  TEUCHOS_ASSERT(blocked_opl->productDomain()->numBlocks() == numMiddle);
1780 
1781  RCP<Thyra::PhysicallyBlockedLinearOpBase<double>> blocked_product =
1782  Thyra::defaultBlockedLinearOp<double>();
1783  blocked_product->beginBlockFill(numRows, numCols);
1784  for (int r = 0; r < numRows; ++r) {
1785  LinearOp product_r = explicitMultiply(blocked_opl->getBlock(r, 0), opr);
1786  blocked_product->setBlock(r, 0, Thyra::scale(scalar, product_r));
1787  }
1788  blocked_product->endBlockFill();
1789  return blocked_product;
1790  }
1791 
1792  // only right factor blocked
1793  if ((!isBlockedL && isBlockedR)) {
1794  double scalarr = 0.0;
1795  bool transpr = false;
1796  RCP<const Thyra::PhysicallyBlockedLinearOpBase<double>> blocked_opr =
1797  getPhysicallyBlockedLinearOp(opr, &scalarr, &transpr);
1798  double scalar = scalarr;
1799 
1800  int numRows = 1;
1801  int numCols = blocked_opr->productDomain()->numBlocks();
1802  int numMiddle = 1;
1803 
1804  TEUCHOS_ASSERT(blocked_opr->productRange()->numBlocks() == numMiddle);
1805 
1806  RCP<Thyra::PhysicallyBlockedLinearOpBase<double>> blocked_product =
1807  Thyra::defaultBlockedLinearOp<double>();
1808  blocked_product->beginBlockFill(numRows, numCols);
1809  for (int c = 0; c < numCols; ++c) {
1810  LinearOp product_c = explicitMultiply(opl, blocked_opr->getBlock(0, c));
1811  blocked_product->setBlock(0, c, Thyra::scale(scalar, product_c));
1812  }
1813  blocked_product->endBlockFill();
1814  return blocked_product;
1815  }
1816 
1817  bool isTpetral = Teko::TpetraHelpers::isTpetraLinearOp(opl);
1818  bool isTpetrar = Teko::TpetraHelpers::isTpetraLinearOp(opr);
1819 
1820  if (isTpetral && isTpetrar) { // Both operators are Tpetra matrices so use the explicit Tpetra
1821  // matrix-matrix multiply
1822 
1823  // Get left and right Tpetra crs operators
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  ST scalarr = 0.0;
1829  bool transpr = false;
1830  RCP<const Tpetra::CrsMatrix<ST, LO, GO, NT>> tCrsOpr =
1831  Teko::TpetraHelpers::getTpetraCrsMatrix(opr, &scalarr, &transpr);
1832 
1833  // Build output operator
1834  RCP<Thyra::LinearOpBase<ST>> explicitOp;
1835  if (destOp != Teuchos::null)
1836  explicitOp = destOp;
1837  else
1838  explicitOp = rcp(new Thyra::TpetraLinearOp<ST, LO, GO, NT>());
1839  RCP<Thyra::TpetraLinearOp<ST, LO, GO, NT>> tExplicitOp =
1840  rcp_dynamic_cast<Thyra::TpetraLinearOp<ST, LO, GO, NT>>(explicitOp);
1841 
1842  // Do explicit matrix-matrix multiply
1843  RCP<Tpetra::CrsMatrix<ST, LO, GO, NT>> explicitCrsOp =
1844  Tpetra::createCrsMatrix<ST, LO, GO, NT>(tCrsOpl->getRowMap());
1845  Tpetra::MatrixMatrix::Multiply<ST, LO, GO, NT>(*tCrsOpl, transpl, *tCrsOpr, transpr,
1846  *explicitCrsOp);
1847  explicitCrsOp->resumeFill();
1848  explicitCrsOp->scale(scalarl * scalarr);
1849  explicitCrsOp->fillComplete(tCrsOpr->getDomainMap(), tCrsOpl->getRangeMap());
1850  tExplicitOp->initialize(Thyra::tpetraVectorSpace<ST, LO, GO, NT>(explicitCrsOp->getRangeMap()),
1851  Thyra::tpetraVectorSpace<ST, LO, GO, NT>(explicitCrsOp->getDomainMap()),
1852  explicitCrsOp);
1853  return tExplicitOp;
1854 
1855  } else if (isTpetral && !isTpetrar) { // Assume that the right operator is diagonal
1856 
1857  // Get left Tpetra crs operator
1858  ST scalarl = 0.0;
1859  bool transpl = false;
1860  RCP<const Tpetra::CrsMatrix<ST, LO, GO, NT>> tCrsOpl =
1861  Teko::TpetraHelpers::getTpetraCrsMatrix(opl, &scalarl, &transpl);
1862 
1863  // Cast right operator as DiagonalLinearOp and extract diagonal as Vector
1864  RCP<const Thyra::DiagonalLinearOpBase<ST>> dOpr =
1865  rcp_dynamic_cast<const Thyra::DiagonalLinearOpBase<ST>>(opr);
1866  RCP<const Thyra::TpetraVector<ST, LO, GO, NT>> tPtr =
1867  rcp_dynamic_cast<const Thyra::TpetraVector<ST, LO, GO, NT>>(dOpr->getDiag(), true);
1868  RCP<const Tpetra::Vector<ST, LO, GO, NT>> diagPtr =
1869  rcp_dynamic_cast<const Tpetra::Vector<ST, LO, GO, NT>>(tPtr->getConstTpetraVector(), true);
1870 
1871  // Scale by the diagonal operator
1872  RCP<Tpetra::CrsMatrix<ST, LO, GO, NT>> explicitCrsOp =
1873  Tpetra::importAndFillCompleteCrsMatrix<Tpetra::CrsMatrix<ST, LO, GO, NT>>(
1874  tCrsOpl, Tpetra::Import<LO, GO, NT>(tCrsOpl->getRowMap(), tCrsOpl->getRowMap()));
1875  explicitCrsOp->rightScale(*diagPtr);
1876  explicitCrsOp->resumeFill();
1877  explicitCrsOp->scale(scalarl);
1878  explicitCrsOp->fillComplete(tCrsOpl->getDomainMap(), tCrsOpl->getRangeMap());
1879  return Thyra::tpetraLinearOp<ST, LO, GO, NT>(
1880  Thyra::tpetraVectorSpace<ST, LO, GO, NT>(explicitCrsOp->getRangeMap()),
1881  Thyra::tpetraVectorSpace<ST, LO, GO, NT>(explicitCrsOp->getDomainMap()), explicitCrsOp);
1882 
1883  } else if (!isTpetral && isTpetrar) { // Assume that the left operator is diagonal
1884 
1885  // Get right Tpetra crs operator
1886  ST scalarr = 0.0;
1887  bool transpr = false;
1888  RCP<const Tpetra::CrsMatrix<ST, LO, GO, NT>> tCrsOpr =
1889  Teko::TpetraHelpers::getTpetraCrsMatrix(opr, &scalarr, &transpr);
1890 
1891  // Cast leftt operator as DiagonalLinearOp and extract diagonal as Vector
1892  RCP<const Thyra::DiagonalLinearOpBase<ST>> dOpl =
1893  rcp_dynamic_cast<const Thyra::DiagonalLinearOpBase<ST>>(opl, true);
1894  RCP<const Thyra::TpetraVector<ST, LO, GO, NT>> tPtr =
1895  rcp_dynamic_cast<const Thyra::TpetraVector<ST, LO, GO, NT>>(dOpl->getDiag(), true);
1896  RCP<const Tpetra::Vector<ST, LO, GO, NT>> diagPtr =
1897  rcp_dynamic_cast<const Tpetra::Vector<ST, LO, GO, NT>>(tPtr->getConstTpetraVector(), true);
1898 
1899  // Scale by the diagonal operator
1900  RCP<Tpetra::CrsMatrix<ST, LO, GO, NT>> explicitCrsOp =
1901  Tpetra::importAndFillCompleteCrsMatrix<Tpetra::CrsMatrix<ST, LO, GO, NT>>(
1902  tCrsOpr, Tpetra::Import<LO, GO, NT>(tCrsOpr->getRowMap(), tCrsOpr->getRowMap()));
1903  explicitCrsOp->leftScale(*diagPtr);
1904  explicitCrsOp->resumeFill();
1905  explicitCrsOp->scale(scalarr);
1906  explicitCrsOp->fillComplete(tCrsOpr->getDomainMap(), tCrsOpr->getRangeMap());
1907  return Thyra::tpetraLinearOp<ST, LO, GO, NT>(
1908  Thyra::tpetraVectorSpace<ST, LO, GO, NT>(explicitCrsOp->getRangeMap()),
1909  Thyra::tpetraVectorSpace<ST, LO, GO, NT>(explicitCrsOp->getDomainMap()), explicitCrsOp);
1910 
1911  } else { // Assume Epetra and we can use transformers
1912 #ifdef TEKO_HAVE_EPETRA
1913  // build implicit multiply
1914  const LinearOp implicitOp = Thyra::multiply(opl, opr);
1915 
1916  // build a scaling transformer
1917 
1918  RCP<Thyra::LinearOpTransformerBase<double>> prodTrans =
1919  Thyra::epetraExtDiagScalingTransformer();
1920 
1921  // check to see if a scaling transformer works: if not use the
1922  // DiagScaledMatrixProduct transformer
1923  if (not prodTrans->isCompatible(*implicitOp))
1924  prodTrans = Thyra::epetraExtDiagScaledMatProdTransformer();
1925 
1926  // build operator destination operator
1927  ModifiableLinearOp explicitOp;
1928 
1929  // if neccessary build a operator to put the explicit multiply into
1930  if (destOp == Teuchos::null)
1931  explicitOp = prodTrans->createOutputOp();
1932  else
1933  explicitOp = destOp;
1934 
1935  // perform multiplication
1936  prodTrans->transform(*implicitOp, explicitOp.ptr());
1937 
1938  // label it
1939  explicitOp->setObjectLabel("explicit( " + opl->getObjectLabel() + " * " +
1940  opr->getObjectLabel() + " )");
1941 
1942  return explicitOp;
1943 #else
1944  throw std::logic_error(
1945  "explicitMultiply is trying to use Epetra "
1946  "code, but TEKO_HAVE_EPETRA is disabled!");
1947 #endif
1948  }
1949 }
1950 
1961 const LinearOp explicitAdd(const LinearOp &opl_in, const LinearOp &opr_in) {
1962  // if both blocked, add block by block
1963  if (isPhysicallyBlockedLinearOp(opl_in) && isPhysicallyBlockedLinearOp(opr_in)) {
1964  double scalarl = 0.0;
1965  bool transpl = false;
1966  RCP<const Thyra::PhysicallyBlockedLinearOpBase<double>> blocked_opl =
1967  getPhysicallyBlockedLinearOp(opl_in, &scalarl, &transpl);
1968 
1969  double scalarr = 0.0;
1970  bool transpr = false;
1971  RCP<const Thyra::PhysicallyBlockedLinearOpBase<double>> blocked_opr =
1972  getPhysicallyBlockedLinearOp(opr_in, &scalarr, &transpr);
1973 
1974  int numRows = blocked_opl->productRange()->numBlocks();
1975  int numCols = blocked_opl->productDomain()->numBlocks();
1976  TEUCHOS_ASSERT(blocked_opr->productRange()->numBlocks() == numRows);
1977  TEUCHOS_ASSERT(blocked_opr->productDomain()->numBlocks() == numCols);
1978 
1979  RCP<Thyra::PhysicallyBlockedLinearOpBase<double>> blocked_sum =
1980  Thyra::defaultBlockedLinearOp<double>();
1981  blocked_sum->beginBlockFill(numRows, numCols);
1982  for (int r = 0; r < numRows; ++r)
1983  for (int c = 0; c < numCols; ++c)
1984  blocked_sum->setBlock(r, c,
1985  explicitAdd(Thyra::scale(scalarl, blocked_opl->getBlock(r, c)),
1986  Thyra::scale(scalarr, blocked_opr->getBlock(r, c))));
1987  blocked_sum->endBlockFill();
1988  return blocked_sum;
1989  }
1990 
1991  // if only one is blocked, it must be 1x1
1992  LinearOp opl = opl_in;
1993  LinearOp opr = opr_in;
1994  if (isPhysicallyBlockedLinearOp(opl_in)) {
1995  double scalarl = 0.0;
1996  bool transpl = false;
1997  RCP<const Thyra::PhysicallyBlockedLinearOpBase<double>> blocked_opl =
1998  getPhysicallyBlockedLinearOp(opl_in, &scalarl, &transpl);
1999  TEUCHOS_ASSERT(blocked_opl->productRange()->numBlocks() == 1);
2000  TEUCHOS_ASSERT(blocked_opl->productDomain()->numBlocks() == 1);
2001  opl = Thyra::scale(scalarl, blocked_opl->getBlock(0, 0));
2002  }
2003  if (isPhysicallyBlockedLinearOp(opr_in)) {
2004  double scalarr = 0.0;
2005  bool transpr = false;
2006  RCP<const Thyra::PhysicallyBlockedLinearOpBase<double>> blocked_opr =
2007  getPhysicallyBlockedLinearOp(opr_in, &scalarr, &transpr);
2008  TEUCHOS_ASSERT(blocked_opr->productRange()->numBlocks() == 1);
2009  TEUCHOS_ASSERT(blocked_opr->productDomain()->numBlocks() == 1);
2010  opr = Thyra::scale(scalarr, blocked_opr->getBlock(0, 0));
2011  }
2012 
2013  bool isTpetral = Teko::TpetraHelpers::isTpetraLinearOp(opl);
2014  bool isTpetrar = Teko::TpetraHelpers::isTpetraLinearOp(opr);
2015 
2016  // if one of the operators in the sum is a thyra zero op
2017  if (isZeroOp(opl)) {
2018  if (isZeroOp(opr)) return opr; // return a zero op if both are zero
2019  if (isTpetrar) { // if other op is tpetra, replace this with a zero crs matrix
2020  ST scalar = 0.0;
2021  bool transp = false;
2022  auto crs_op = Teko::TpetraHelpers::getTpetraCrsMatrix(opr, &scalar, &transp);
2023  auto zero_crs = Tpetra::createCrsMatrix<ST, LO, GO, NT>(crs_op->getRowMap());
2024  zero_crs->fillComplete();
2025  opl = Thyra::constTpetraLinearOp<ST, LO, GO, NT>(
2026  Thyra::tpetraVectorSpace<ST, LO, GO, NT>(crs_op->getRangeMap()),
2027  Thyra::tpetraVectorSpace<ST, LO, GO, NT>(crs_op->getDomainMap()), zero_crs);
2028  isTpetral = true;
2029  } else
2030  return opr->clone();
2031  }
2032  if (isZeroOp(opr)) {
2033  if (isTpetral) { // if other op is tpetra, replace this with a zero crs matrix
2034  ST scalar = 0.0;
2035  bool transp = false;
2036  auto crs_op = Teko::TpetraHelpers::getTpetraCrsMatrix(opr, &scalar, &transp);
2037  auto zero_crs = Tpetra::createCrsMatrix<ST, LO, GO, NT>(crs_op->getRowMap());
2038  zero_crs->fillComplete();
2039  opr = Thyra::constTpetraLinearOp<ST, LO, GO, NT>(
2040  Thyra::tpetraVectorSpace<ST, LO, GO, NT>(crs_op->getRangeMap()),
2041  Thyra::tpetraVectorSpace<ST, LO, GO, NT>(crs_op->getDomainMap()), zero_crs);
2042  isTpetrar = true;
2043  } else
2044  return opl->clone();
2045  }
2046 
2047  if (isTpetral && isTpetrar) { // Both operators are Tpetra matrices so use the explicit Tpetra
2048  // matrix-matrix add
2049 
2050  // Get left and right Tpetra crs operators
2051  ST scalarl = 0.0;
2052  bool transpl = false;
2053  RCP<const Tpetra::CrsMatrix<ST, LO, GO, NT>> tCrsOpl =
2054  Teko::TpetraHelpers::getTpetraCrsMatrix(opl, &scalarl, &transpl);
2055  ST scalarr = 0.0;
2056  bool transpr = false;
2057  RCP<const Tpetra::CrsMatrix<ST, LO, GO, NT>> tCrsOpr =
2058  Teko::TpetraHelpers::getTpetraCrsMatrix(opr, &scalarr, &transpr);
2059 
2060  // Build output operator
2061  RCP<Thyra::LinearOpBase<ST>> explicitOp = rcp(new Thyra::TpetraLinearOp<ST, LO, GO, NT>());
2062  RCP<Thyra::TpetraLinearOp<ST, LO, GO, NT>> tExplicitOp =
2063  rcp_dynamic_cast<Thyra::TpetraLinearOp<ST, LO, GO, NT>>(explicitOp);
2064 
2065  // Do explicit matrix-matrix add
2066  RCP<Tpetra::CrsMatrix<ST, LO, GO, NT>> explicitCrsOp =
2067  Tpetra::MatrixMatrix::add<ST, LO, GO, NT>(scalarl, transpl, *tCrsOpl, scalarr, transpr,
2068  *tCrsOpr);
2069  tExplicitOp->initialize(Thyra::tpetraVectorSpace<ST, LO, GO, NT>(explicitCrsOp->getRangeMap()),
2070  Thyra::tpetraVectorSpace<ST, LO, GO, NT>(explicitCrsOp->getDomainMap()),
2071  explicitCrsOp);
2072  return tExplicitOp;
2073 
2074  } else { // Assume Epetra
2075 #ifdef TEKO_HAVE_EPETRA
2076  // build implicit add
2077  const LinearOp implicitOp = Thyra::add(opl, opr);
2078 
2079  // build transformer
2080  const RCP<Thyra::LinearOpTransformerBase<double>> prodTrans = Thyra::epetraExtAddTransformer();
2081 
2082  // build operator and add
2083  const RCP<Thyra::LinearOpBase<double>> explicitOp = prodTrans->createOutputOp();
2084  prodTrans->transform(*implicitOp, explicitOp.ptr());
2085  explicitOp->setObjectLabel("explicit( " + opl->getObjectLabel() + " + " +
2086  opr->getObjectLabel() + " )");
2087 
2088  return explicitOp;
2089 #else
2090  throw std::logic_error(
2091  "explicitAdd is trying to use Epetra "
2092  "code, but TEKO_HAVE_EPETRA is disabled!");
2093 #endif
2094  }
2095 }
2096 
2109 const ModifiableLinearOp explicitAdd(const LinearOp &opl_in, const LinearOp &opr_in,
2110  const ModifiableLinearOp &destOp) {
2111  // if blocked, add block by block
2112  if (isPhysicallyBlockedLinearOp(opl_in) && isPhysicallyBlockedLinearOp(opr_in)) {
2113  double scalarl = 0.0;
2114  bool transpl = false;
2115  RCP<const Thyra::PhysicallyBlockedLinearOpBase<double>> blocked_opl =
2116  getPhysicallyBlockedLinearOp(opl_in, &scalarl, &transpl);
2117 
2118  double scalarr = 0.0;
2119  bool transpr = false;
2120  RCP<const Thyra::PhysicallyBlockedLinearOpBase<double>> blocked_opr =
2121  getPhysicallyBlockedLinearOp(opr_in, &scalarr, &transpr);
2122 
2123  int numRows = blocked_opl->productRange()->numBlocks();
2124  int numCols = blocked_opl->productDomain()->numBlocks();
2125  TEUCHOS_ASSERT(blocked_opr->productRange()->numBlocks() == numRows);
2126  TEUCHOS_ASSERT(blocked_opr->productDomain()->numBlocks() == numCols);
2127 
2128  RCP<Thyra::PhysicallyBlockedLinearOpBase<double>> blocked_sum =
2129  Teuchos::rcp_dynamic_cast<Thyra::DefaultBlockedLinearOp<double>>(destOp);
2130  if (blocked_sum.is_null()) {
2131  // take care of the null case, this means we must alllocate memory
2132  blocked_sum = Thyra::defaultBlockedLinearOp<double>();
2133 
2134  blocked_sum->beginBlockFill(numRows, numCols);
2135  for (int r = 0; r < numRows; ++r) {
2136  for (int c = 0; c < numCols; ++c) {
2137  auto block =
2138  explicitAdd(Thyra::scale(scalarl, blocked_opl->getBlock(r, c)),
2139  Thyra::scale(scalarr, blocked_opr->getBlock(r, c)), Teuchos::null);
2140  blocked_sum->setNonconstBlock(r, c, block);
2141  }
2142  }
2143  blocked_sum->endBlockFill();
2144 
2145  } else {
2146  // in this case memory can be reused
2147  for (int r = 0; r < numRows; ++r)
2148  for (int c = 0; c < numCols; ++c)
2149  explicitAdd(Thyra::scale(scalarl, blocked_opl->getBlock(r, c)),
2150  Thyra::scale(scalarr, blocked_opr->getBlock(r, c)),
2151  blocked_sum->getNonconstBlock(r, c));
2152  }
2153 
2154  return blocked_sum;
2155  }
2156 
2157  LinearOp opl = opl_in;
2158  LinearOp opr = opr_in;
2159  // if only one is blocked, it must be 1x1
2160  if (isPhysicallyBlockedLinearOp(opl)) {
2161  double scalarl = 0.0;
2162  bool transpl = false;
2163  RCP<const Thyra::PhysicallyBlockedLinearOpBase<double>> blocked_opl =
2164  getPhysicallyBlockedLinearOp(opl, &scalarl, &transpl);
2165  TEUCHOS_ASSERT(blocked_opl->productRange()->numBlocks() == 1);
2166  TEUCHOS_ASSERT(blocked_opl->productDomain()->numBlocks() == 1);
2167  opl = Thyra::scale(scalarl, blocked_opl->getBlock(0, 0));
2168  }
2169  if (isPhysicallyBlockedLinearOp(opr)) {
2170  double scalarr = 0.0;
2171  bool transpr = false;
2172  RCP<const Thyra::PhysicallyBlockedLinearOpBase<double>> blocked_opr =
2173  getPhysicallyBlockedLinearOp(opr, &scalarr, &transpr);
2174  TEUCHOS_ASSERT(blocked_opr->productRange()->numBlocks() == 1);
2175  TEUCHOS_ASSERT(blocked_opr->productDomain()->numBlocks() == 1);
2176  opr = Thyra::scale(scalarr, blocked_opr->getBlock(0, 0));
2177  }
2178 
2179  bool isTpetral = Teko::TpetraHelpers::isTpetraLinearOp(opl);
2180  bool isTpetrar = Teko::TpetraHelpers::isTpetraLinearOp(opr);
2181 
2182  if (isTpetral && isTpetrar) { // Both operators are Tpetra matrices so use the explicit
2183  // Tpetra matrix-matrix add
2184 
2185  // Get left and right Tpetra crs operators
2186  ST scalarl = 0.0;
2187  bool transpl = false;
2188  RCP<const Tpetra::CrsMatrix<ST, LO, GO, NT>> tCrsOpl =
2189  Teko::TpetraHelpers::getTpetraCrsMatrix(opl, &scalarl, &transpl);
2190  ST scalarr = 0.0;
2191  bool transpr = false;
2192  RCP<const Tpetra::CrsMatrix<ST, LO, GO, NT>> tCrsOpr =
2193  Teko::TpetraHelpers::getTpetraCrsMatrix(opr, &scalarr, &transpr);
2194 
2195  // Build output operator
2196  RCP<Thyra::LinearOpBase<ST>> explicitOp;
2197  RCP<Tpetra::CrsMatrix<ST, LO, GO, NT>> explicitCrsOp;
2198  if (!destOp.is_null()) {
2199  explicitOp = destOp;
2200  RCP<Thyra::TpetraLinearOp<ST, LO, GO, NT>> tOp =
2201  rcp_dynamic_cast<Thyra::TpetraLinearOp<ST, LO, GO, NT>>(destOp);
2202  if (!tOp.is_null())
2203  explicitCrsOp =
2204  rcp_dynamic_cast<Tpetra::CrsMatrix<ST, LO, GO, NT>>(tOp->getTpetraOperator());
2205  bool needNewTpetraMatrix =
2206  (explicitCrsOp.is_null()) || (tCrsOpl == explicitCrsOp) || (tCrsOpr == explicitCrsOp);
2207  if (!needNewTpetraMatrix) {
2208  try {
2209  // try to reuse matrix sparsity with Add. If it fails, build new operator with add
2210  Tpetra::MatrixMatrix::Add<ST, LO, GO, NT>(*tCrsOpl, transpl, scalarl, *tCrsOpr, transpr,
2211  scalarr, explicitCrsOp);
2212  } catch (std::logic_error &e) {
2213  RCP<Teuchos::FancyOStream> out = Teuchos::VerboseObjectBase::getDefaultOStream();
2214  *out << "*** THROWN EXCEPTION ***\n";
2215  *out << e.what() << std::endl;
2216  *out << "************************\n";
2217  *out << "Teko: explicitAdd unable to reuse existing operator. Creating new operator.\n"
2218  << std::endl;
2219  needNewTpetraMatrix = true;
2220  }
2221  }
2222  if (needNewTpetraMatrix)
2223  // Do explicit matrix-matrix add
2224  explicitCrsOp = Tpetra::MatrixMatrix::add<ST, LO, GO, NT>(scalarl, transpl, *tCrsOpl,
2225  scalarr, transpr, *tCrsOpr);
2226  } else {
2227  explicitOp = rcp(new Thyra::TpetraLinearOp<ST, LO, GO, NT>());
2228  // Do explicit matrix-matrix add
2229  explicitCrsOp = Tpetra::MatrixMatrix::add<ST, LO, GO, NT>(scalarl, transpl, *tCrsOpl, scalarr,
2230  transpr, *tCrsOpr);
2231  }
2232  RCP<Thyra::TpetraLinearOp<ST, LO, GO, NT>> tExplicitOp =
2233  rcp_dynamic_cast<Thyra::TpetraLinearOp<ST, LO, GO, NT>>(explicitOp);
2234 
2235  tExplicitOp->initialize(Thyra::tpetraVectorSpace<ST, LO, GO, NT>(explicitCrsOp->getRangeMap()),
2236  Thyra::tpetraVectorSpace<ST, LO, GO, NT>(explicitCrsOp->getDomainMap()),
2237  explicitCrsOp);
2238  return tExplicitOp;
2239 
2240  } else { // Assume Epetra
2241 #ifdef TEKO_HAVE_EPETRA
2242  // build implicit add
2243  const LinearOp implicitOp = Thyra::add(opl, opr);
2244 
2245  // build transformer
2246  const RCP<Thyra::LinearOpTransformerBase<double>> prodTrans = Thyra::epetraExtAddTransformer();
2247 
2248  // build or reuse destination operator
2249  RCP<Thyra::LinearOpBase<double>> explicitOp;
2250  if (destOp != Teuchos::null)
2251  explicitOp = destOp;
2252  else
2253  explicitOp = prodTrans->createOutputOp();
2254 
2255  // perform add
2256  prodTrans->transform(*implicitOp, explicitOp.ptr());
2257  explicitOp->setObjectLabel("explicit( " + opl->getObjectLabel() + " + " +
2258  opr->getObjectLabel() + " )");
2259 
2260  return explicitOp;
2261 #else
2262  throw std::logic_error(
2263  "explicitAdd is trying to use Epetra "
2264  "code, but TEKO_HAVE_EPETRA is disabled!");
2265 #endif
2266  }
2267 }
2268 
2273 const ModifiableLinearOp explicitSum(const LinearOp &op, const ModifiableLinearOp &destOp) {
2274 #ifdef TEKO_HAVE_EPETRA
2275  // convert operators to Epetra_CrsMatrix
2276  const RCP<const Epetra_CrsMatrix> epetraOp =
2277  rcp_dynamic_cast<const Epetra_CrsMatrix>(get_Epetra_Operator(*op), true);
2278 
2279  if (destOp == Teuchos::null) {
2280  Teuchos::RCP<Epetra_Operator> epetraDest = Teuchos::rcp(new Epetra_CrsMatrix(*epetraOp));
2281 
2282  return Thyra::nonconstEpetraLinearOp(epetraDest);
2283  }
2284 
2285  const RCP<Epetra_CrsMatrix> epetraDest =
2286  rcp_dynamic_cast<Epetra_CrsMatrix>(get_Epetra_Operator(*destOp), true);
2287 
2288  EpetraExt::MatrixMatrix::Add(*epetraOp, false, 1.0, *epetraDest, 1.0);
2289 
2290  return destOp;
2291 #else
2292  throw std::logic_error(
2293  "explicitSum is trying to use Epetra "
2294  "code, but TEKO_HAVE_EPETRA is disabled!");
2295 #endif
2296 }
2297 
2298 const LinearOp explicitTranspose(const LinearOp &op) {
2299  if (Teko::TpetraHelpers::isTpetraLinearOp(op)) {
2300  RCP<const Thyra::TpetraLinearOp<ST, LO, GO, NT>> tOp =
2301  rcp_dynamic_cast<const Thyra::TpetraLinearOp<ST, LO, GO, NT>>(op, true);
2302  RCP<const Tpetra::CrsMatrix<ST, LO, GO, NT>> tCrsOp =
2303  rcp_dynamic_cast<const Tpetra::CrsMatrix<ST, LO, GO, NT>>(tOp->getConstTpetraOperator(),
2304  true);
2305 
2306  Tpetra::RowMatrixTransposer<ST, LO, GO, NT> transposer(tCrsOp);
2307  RCP<Tpetra::CrsMatrix<ST, LO, GO, NT>> transOp = transposer.createTranspose();
2308 
2309  return Thyra::tpetraLinearOp<ST, LO, GO, NT>(
2310  Thyra::tpetraVectorSpace<ST, LO, GO, NT>(transOp->getRangeMap()),
2311  Thyra::tpetraVectorSpace<ST, LO, GO, NT>(transOp->getDomainMap()), transOp);
2312 
2313  } else {
2314 #ifdef TEKO_HAVE_EPETRA
2315  RCP<const Epetra_Operator> eOp = Thyra::get_Epetra_Operator(*op);
2316  TEUCHOS_TEST_FOR_EXCEPTION(eOp == Teuchos::null, std::logic_error,
2317  "Teko::explicitTranspose Not an Epetra_Operator");
2318  RCP<const Epetra_RowMatrix> eRowMatrixOp =
2319  Teuchos::rcp_dynamic_cast<const Epetra_RowMatrix>(eOp);
2320  TEUCHOS_TEST_FOR_EXCEPTION(eRowMatrixOp == Teuchos::null, std::logic_error,
2321  "Teko::explicitTranspose Not an Epetra_RowMatrix");
2322 
2323  // we now have a delete transpose operator
2324  EpetraExt::RowMatrix_Transpose tranposeOp;
2325  Epetra_RowMatrix &eMat = tranposeOp(const_cast<Epetra_RowMatrix &>(*eRowMatrixOp));
2326 
2327  // this copy is because of a poor implementation of the EpetraExt::Transform
2328  // implementation
2329  Teuchos::RCP<Epetra_CrsMatrix> crsMat =
2330  Teuchos::rcp(new Epetra_CrsMatrix(dynamic_cast<Epetra_CrsMatrix &>(eMat)));
2331 
2332  return Thyra::epetraLinearOp(crsMat);
2333 #else
2334  throw std::logic_error(
2335  "explicitTranspose is trying to use Epetra "
2336  "code, but TEKO_HAVE_EPETRA is disabled!");
2337 #endif
2338  }
2339 }
2340 
2341 const LinearOp explicitScale(double scalar, const LinearOp &op) {
2342  if (Teko::TpetraHelpers::isTpetraLinearOp(op)) {
2343  RCP<const Thyra::TpetraLinearOp<ST, LO, GO, NT>> tOp =
2344  rcp_dynamic_cast<const Thyra::TpetraLinearOp<ST, LO, GO, NT>>(op, true);
2345  RCP<const Tpetra::CrsMatrix<ST, LO, GO, NT>> tCrsOp =
2346  rcp_dynamic_cast<const Tpetra::CrsMatrix<ST, LO, GO, NT>>(tOp->getConstTpetraOperator(),
2347  true);
2348  auto crsOpNew = rcp(new Tpetra::CrsMatrix<ST, LO, GO, NT>(*tCrsOp, Teuchos::Copy));
2349  crsOpNew->scale(scalar);
2350  return Thyra::tpetraLinearOp<ST, LO, GO, NT>(
2351  Thyra::createVectorSpace<ST, LO, GO, NT>(crsOpNew->getRangeMap()),
2352  Thyra::createVectorSpace<ST, LO, GO, NT>(crsOpNew->getDomainMap()), crsOpNew);
2353  } else {
2354 #ifdef TEKO_HAVE_EPETRA
2355  RCP<const Thyra::EpetraLinearOp> eOp = rcp_dynamic_cast<const Thyra::EpetraLinearOp>(op, true);
2356  RCP<const Epetra_CrsMatrix> eCrsOp =
2357  rcp_dynamic_cast<const Epetra_CrsMatrix>(eOp->epetra_op(), true);
2358  Teuchos::RCP<Epetra_CrsMatrix> crsMat = Teuchos::rcp(new Epetra_CrsMatrix(*eCrsOp));
2359 
2360  crsMat->Scale(scalar);
2361 
2362  return Thyra::epetraLinearOp(crsMat);
2363 #else
2364  throw std::logic_error(
2365  "explicitScale is trying to use Epetra "
2366  "code, but TEKO_HAVE_EPETRA is disabled!");
2367 #endif
2368  }
2369 }
2370 
2371 double frobeniusNorm(const LinearOp &op_in) {
2372  LinearOp op;
2373  double scalar = 1.0;
2374 
2375  // if blocked, must be 1x1
2376  if (isPhysicallyBlockedLinearOp(op_in)) {
2377  bool transp = false;
2378  RCP<const Thyra::PhysicallyBlockedLinearOpBase<double>> blocked_op =
2379  getPhysicallyBlockedLinearOp(op_in, &scalar, &transp);
2380  TEUCHOS_ASSERT(blocked_op->productRange()->numBlocks() == 1);
2381  TEUCHOS_ASSERT(blocked_op->productDomain()->numBlocks() == 1);
2382  op = blocked_op->getBlock(0, 0);
2383  } else
2384  op = op_in;
2385 
2386  if (Teko::TpetraHelpers::isTpetraLinearOp(op)) {
2387  const RCP<const Thyra::TpetraLinearOp<ST, LO, GO, NT>> tOp =
2388  rcp_dynamic_cast<const Thyra::TpetraLinearOp<ST, LO, GO, NT>>(op);
2389  const RCP<const Tpetra::CrsMatrix<ST, LO, GO, NT>> crsOp =
2390  rcp_dynamic_cast<const Tpetra::CrsMatrix<ST, LO, GO, NT>>(tOp->getConstTpetraOperator(),
2391  true);
2392  return crsOp->getFrobeniusNorm();
2393  } else {
2394 #ifdef TEKO_HAVE_EPETRA
2395  const RCP<const Epetra_Operator> epOp = Thyra::get_Epetra_Operator(*op);
2396  const RCP<const Epetra_CrsMatrix> crsOp = rcp_dynamic_cast<const Epetra_CrsMatrix>(epOp, true);
2397  return crsOp->NormFrobenius();
2398 #else
2399  throw std::logic_error(
2400  "frobeniusNorm is trying to use Epetra "
2401  "code, but TEKO_HAVE_EPETRA is disabled!");
2402 #endif
2403  }
2404 }
2405 
2406 double oneNorm(const LinearOp &op) {
2407  if (Teko::TpetraHelpers::isTpetraLinearOp(op)) {
2408  TEUCHOS_TEST_FOR_EXCEPTION(true, std::logic_error,
2409  "One norm not currently implemented for Tpetra matrices");
2410 
2411  } else {
2412 #ifdef TEKO_HAVE_EPETRA
2413  const RCP<const Epetra_Operator> epOp = Thyra::get_Epetra_Operator(*op);
2414  const RCP<const Epetra_CrsMatrix> crsOp = rcp_dynamic_cast<const Epetra_CrsMatrix>(epOp, true);
2415  return crsOp->NormOne();
2416 #else
2417  throw std::logic_error(
2418  "oneNorm is trying to use Epetra "
2419  "code, but TEKO_HAVE_EPETRA is disabled!");
2420 #endif
2421  }
2422 }
2423 
2424 double infNorm(const LinearOp &op) {
2425  if (Teko::TpetraHelpers::isTpetraLinearOp(op)) {
2426  ST scalar = 0.0;
2427  bool transp = false;
2428  RCP<const Tpetra::CrsMatrix<ST, LO, GO, NT>> tCrsOp =
2429  Teko::TpetraHelpers::getTpetraCrsMatrix(op, &scalar, &transp);
2430 
2431  // extract diagonal
2432  const RCP<Tpetra::Vector<ST, LO, GO, NT>> ptrDiag =
2433  Tpetra::createVector<ST, LO, GO, NT>(tCrsOp->getRowMap());
2434  Tpetra::Vector<ST, LO, GO, NT> &diag = *ptrDiag;
2435 
2436  // compute absolute value row sum
2437  diag.putScalar(0.0);
2438  for (LO i = 0; i < (LO)tCrsOp->getLocalNumRows(); i++) {
2439  LO numEntries = tCrsOp->getNumEntriesInLocalRow(i);
2440  typename Tpetra::CrsMatrix<ST, LO, GO, NT>::local_inds_host_view_type indices;
2441  typename Tpetra::CrsMatrix<ST, LO, GO, NT>::values_host_view_type values;
2442  tCrsOp->getLocalRowView(i, indices, values);
2443 
2444  // build abs value row sum
2445  for (LO j = 0; j < numEntries; j++) diag.sumIntoLocalValue(i, std::abs(values(j)));
2446  }
2447  return diag.normInf() * scalar;
2448 
2449  } else {
2450 #ifdef TEKO_HAVE_EPETRA
2451  const RCP<const Epetra_Operator> epOp = Thyra::get_Epetra_Operator(*op);
2452  const RCP<const Epetra_CrsMatrix> crsOp = rcp_dynamic_cast<const Epetra_CrsMatrix>(epOp, true);
2453  return crsOp->NormInf();
2454 #else
2455  throw std::logic_error(
2456  "infNorm is trying to use Epetra "
2457  "code, but TEKO_HAVE_EPETRA is disabled!");
2458 #endif
2459  }
2460 }
2461 
2462 const LinearOp buildDiagonal(const MultiVector &src, const std::string &lbl) {
2463  RCP<Thyra::VectorBase<double>> dst = Thyra::createMember(src->range());
2464  Thyra::copy(*src->col(0), dst.ptr());
2465 
2466  return Thyra::diagonal<double>(dst, lbl);
2467 }
2468 
2469 const LinearOp buildInvDiagonal(const MultiVector &src, const std::string &lbl) {
2470  const RCP<const Thyra::VectorBase<double>> srcV = src->col(0);
2471  RCP<Thyra::VectorBase<double>> dst = Thyra::createMember(srcV->range());
2472  Thyra::reciprocal<double>(*srcV, dst.ptr());
2473 
2474  return Thyra::diagonal<double>(dst, lbl);
2475 }
2476 
2478 BlockedMultiVector buildBlockedMultiVector(const std::vector<MultiVector> &mvv) {
2479  Teuchos::Array<MultiVector> mvA;
2480  Teuchos::Array<VectorSpace> vsA;
2481 
2482  // build arrays of multi vectors and vector spaces
2483  std::vector<MultiVector>::const_iterator itr;
2484  for (itr = mvv.begin(); itr != mvv.end(); ++itr) {
2485  mvA.push_back(*itr);
2486  vsA.push_back((*itr)->range());
2487  }
2488 
2489  // construct the product vector space
2490  const RCP<const Thyra::DefaultProductVectorSpace<double>> vs =
2491  Thyra::productVectorSpace<double>(vsA);
2492 
2493  return Thyra::defaultProductMultiVector<double>(vs, mvA);
2494 }
2495 
2506 Teuchos::RCP<Thyra::VectorBase<double>> indicatorVector(const std::vector<int> &indices,
2507  const VectorSpace &vs, double onValue,
2508  double offValue)
2509 
2510 {
2511  using Teuchos::RCP;
2512 
2513  // create a new vector
2514  RCP<Thyra::VectorBase<double>> v = Thyra::createMember<double>(vs);
2515  Thyra::put_scalar<double>(offValue, v.ptr()); // fill it with "off" values
2516 
2517  // set on values
2518  for (std::size_t i = 0; i < indices.size(); i++)
2519  Thyra::set_ele<double>(indices[i], onValue, v.ptr());
2520 
2521  return v;
2522 }
2523 
2548 double computeSpectralRad(const RCP<const Thyra::LinearOpBase<double>> &A, double tol,
2549  bool isHermitian, int numBlocks, int restart, int verbosity) {
2550  typedef Thyra::LinearOpBase<double> OP;
2551  typedef Thyra::MultiVectorBase<double> MV;
2552 
2553  int startVectors = 1;
2554 
2555  // construct an initial guess
2556  const RCP<MV> ivec = Thyra::createMember(A->domain());
2557  Thyra::randomize(-1.0, 1.0, ivec.ptr());
2558 
2559  RCP<Anasazi::BasicEigenproblem<double, MV, OP>> eigProb =
2560  rcp(new Anasazi::BasicEigenproblem<double, MV, OP>(A, ivec));
2561  eigProb->setNEV(1);
2562  eigProb->setHermitian(isHermitian);
2563 
2564  // set the problem up
2565  if (not eigProb->setProblem()) {
2566  // big time failure!
2567  return Teuchos::ScalarTraits<double>::nan();
2568  }
2569 
2570  // we want largert magnitude eigenvalue
2571  std::string which("LM"); // largest magnitude
2572 
2573  // Create the parameter list for the eigensolver
2574  // verbosity+=Anasazi::TimingDetails;
2575  Teuchos::ParameterList MyPL;
2576  MyPL.set("Verbosity", verbosity);
2577  MyPL.set("Which", which);
2578  MyPL.set("Block Size", startVectors);
2579  MyPL.set("Num Blocks", numBlocks);
2580  MyPL.set("Maximum Restarts", restart);
2581  MyPL.set("Convergence Tolerance", tol);
2582 
2583  // build status test manager
2584  // RCP<Anasazi::StatusTestMaxIters<double,MV,OP> > statTest
2585  // = rcp(new Anasazi::StatusTestMaxIters<double,MV,OP>(numBlocks*(restart+1)));
2586 
2587  // Create the Block Krylov Schur solver
2588  // This takes as inputs the eigenvalue problem and the solver parameters
2589  Anasazi::BlockKrylovSchurSolMgr<double, MV, OP> MyBlockKrylovSchur(eigProb, MyPL);
2590 
2591  // Solve the eigenvalue problem, and save the return code
2592  Anasazi::ReturnType solverreturn = MyBlockKrylovSchur.solve();
2593 
2594  if (solverreturn == Anasazi::Unconverged) {
2595  double real = MyBlockKrylovSchur.getRitzValues().begin()->realpart;
2596  double comp = MyBlockKrylovSchur.getRitzValues().begin()->imagpart;
2597 
2598  return -std::abs(std::sqrt(real * real + comp * comp));
2599 
2600  // cout << "Anasazi::BlockKrylovSchur::solve() did not converge!" << std::endl;
2601  // return -std::abs(MyBlockKrylovSchur.getRitzValues().begin()->realpart);
2602  } else { // solverreturn==Anasazi::Converged
2603  double real = eigProb->getSolution().Evals.begin()->realpart;
2604  double comp = eigProb->getSolution().Evals.begin()->imagpart;
2605 
2606  return std::abs(std::sqrt(real * real + comp * comp));
2607 
2608  // cout << "Anasazi::BlockKrylovSchur::solve() converged!" << endl;
2609  // return std::abs(eigProb->getSolution().Evals.begin()->realpart);
2610  }
2611 }
2612 
2636 double computeSmallestMagEig(const RCP<const Thyra::LinearOpBase<double>> &A, double tol,
2637  bool isHermitian, int numBlocks, int restart, int verbosity) {
2638  typedef Thyra::LinearOpBase<double> OP;
2639  typedef Thyra::MultiVectorBase<double> MV;
2640 
2641  int startVectors = 1;
2642 
2643  // construct an initial guess
2644  const RCP<MV> ivec = Thyra::createMember(A->domain());
2645  Thyra::randomize(-1.0, 1.0, ivec.ptr());
2646 
2647  RCP<Anasazi::BasicEigenproblem<double, MV, OP>> eigProb =
2648  rcp(new Anasazi::BasicEigenproblem<double, MV, OP>(A, ivec));
2649  eigProb->setNEV(1);
2650  eigProb->setHermitian(isHermitian);
2651 
2652  // set the problem up
2653  if (not eigProb->setProblem()) {
2654  // big time failure!
2655  return Teuchos::ScalarTraits<double>::nan();
2656  }
2657 
2658  // we want largert magnitude eigenvalue
2659  std::string which("SM"); // smallest magnitude
2660 
2661  // Create the parameter list for the eigensolver
2662  Teuchos::ParameterList MyPL;
2663  MyPL.set("Verbosity", verbosity);
2664  MyPL.set("Which", which);
2665  MyPL.set("Block Size", startVectors);
2666  MyPL.set("Num Blocks", numBlocks);
2667  MyPL.set("Maximum Restarts", restart);
2668  MyPL.set("Convergence Tolerance", tol);
2669 
2670  // build status test manager
2671  // RCP<Anasazi::StatusTestMaxIters<double,MV,OP> > statTest
2672  // = rcp(new Anasazi::StatusTestMaxIters<double,MV,OP>(10));
2673 
2674  // Create the Block Krylov Schur solver
2675  // This takes as inputs the eigenvalue problem and the solver parameters
2676  Anasazi::BlockKrylovSchurSolMgr<double, MV, OP> MyBlockKrylovSchur(eigProb, MyPL);
2677 
2678  // Solve the eigenvalue problem, and save the return code
2679  Anasazi::ReturnType solverreturn = MyBlockKrylovSchur.solve();
2680 
2681  if (solverreturn == Anasazi::Unconverged) {
2682  // cout << "Anasazi::BlockKrylovSchur::solve() did not converge! " << std::endl;
2683  return -std::abs(MyBlockKrylovSchur.getRitzValues().begin()->realpart);
2684  } else { // solverreturn==Anasazi::Converged
2685  // cout << "Anasazi::BlockKrylovSchur::solve() converged!" << endl;
2686  return std::abs(eigProb->getSolution().Evals.begin()->realpart);
2687  }
2688 }
2689 
2698 ModifiableLinearOp getDiagonalOp(const Teko::LinearOp &A, const DiagonalType &dt) {
2699  switch (dt) {
2700  case Diagonal: return getDiagonalOp(A);
2701  case Lumped: return getLumpedMatrix(A);
2702  case AbsRowSum: return getAbsRowSumMatrix(A);
2703  case NotDiag:
2704  default: TEUCHOS_TEST_FOR_EXCEPT(true);
2705  };
2706 
2707  return Teuchos::null;
2708 }
2709 
2718 ModifiableLinearOp getInvDiagonalOp(const Teko::LinearOp &A, const Teko::DiagonalType &dt) {
2719  switch (dt) {
2720  case Diagonal: return getInvDiagonalOp(A);
2721  case Lumped: return getInvLumpedMatrix(A);
2722  case AbsRowSum: return getAbsRowSumInvMatrix(A);
2723  case NotDiag:
2724  default: TEUCHOS_TEST_FOR_EXCEPT(true);
2725  };
2726 
2727  return Teuchos::null;
2728 }
2729 
2736 std::string getDiagonalName(const DiagonalType &dt) {
2737  switch (dt) {
2738  case Diagonal: return "Diagonal";
2739  case Lumped: return "Lumped";
2740  case AbsRowSum: return "AbsRowSum";
2741  case NotDiag: return "NotDiag";
2742  case BlkDiag: return "BlkDiag";
2743  };
2744 
2745  return "<error>";
2746 }
2747 
2756 DiagonalType getDiagonalType(std::string name) {
2757  if (name == "Diagonal") return Diagonal;
2758  if (name == "Lumped") return Lumped;
2759  if (name == "AbsRowSum") return AbsRowSum;
2760  if (name == "BlkDiag") return BlkDiag;
2761 
2762  return NotDiag;
2763 }
2764 
2765 #ifdef TEKO_HAVE_EPETRA
2766 LinearOp probe(Teuchos::RCP<const Epetra_CrsGraph> &G, const LinearOp &Op) {
2767 #ifdef Teko_ENABLE_Isorropia
2768  Teuchos::ParameterList probeList;
2769  Prober prober(G, probeList, true);
2770  Teuchos::RCP<Epetra_CrsMatrix> Mat = rcp(new Epetra_CrsMatrix(Copy, *G));
2772  prober.probe(Mwrap, *Mat);
2773  return Thyra::epetraLinearOp(Mat);
2774 #else
2775  (void)G;
2776  (void)Op;
2777  TEUCHOS_TEST_FOR_EXCEPTION(true, std::runtime_error, "Probe requires Isorropia");
2778 #endif
2779 }
2780 #endif
2781 
2782 double norm_1(const MultiVector &v, std::size_t col) {
2783  Teuchos::Array<double> n(v->domain()->dim());
2784  Thyra::norms_1<double>(*v, n);
2785 
2786  return n[col];
2787 }
2788 
2789 double norm_2(const MultiVector &v, std::size_t col) {
2790  Teuchos::Array<double> n(v->domain()->dim());
2791  Thyra::norms_2<double>(*v, n);
2792 
2793  return n[col];
2794 }
2795 
2796 #ifdef TEKO_HAVE_EPETRA
2797 void putScalar(const ModifiableLinearOp &op, double scalar) {
2798  try {
2799  // get Epetra_Operator
2800  RCP<Epetra_Operator> eOp = Thyra::get_Epetra_Operator(*op);
2801 
2802  // cast it to a CrsMatrix
2803  RCP<Epetra_CrsMatrix> eCrsOp = rcp_dynamic_cast<Epetra_CrsMatrix>(eOp, true);
2804 
2805  eCrsOp->PutScalar(scalar);
2806  } catch (std::exception &e) {
2807  RCP<Teuchos::FancyOStream> out = Teuchos::VerboseObjectBase::getDefaultOStream();
2808 
2809  *out << "Teko: putScalar requires an Epetra_CrsMatrix\n";
2810  *out << " Could not extract an Epetra_Operator from a \"" << op->description() << std::endl;
2811  *out << " OR\n";
2812  *out << " Could not cast an Epetra_Operator to a Epetra_CrsMatrix\n";
2813  *out << std::endl;
2814  *out << "*** THROWN EXCEPTION ***\n";
2815  *out << e.what() << std::endl;
2816  *out << "************************\n";
2817 
2818  throw e;
2819  }
2820 }
2821 #endif
2822 
2823 void clipLower(MultiVector &v, double lowerBound) {
2824  using Teuchos::RCP;
2825  using Teuchos::rcp_dynamic_cast;
2826 
2827  // cast so entries are accessible
2828  // RCP<Thyra::SpmdMultiVectorBase<double> > spmdMVec
2829  // = rcp_dynamic_cast<Thyra::DefaultSpmdMultiVector<double> >(v);
2830 
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] < lowerBound) values[j] = lowerBound;
2840  }
2841  }
2842 }
2843 
2844 void clipUpper(MultiVector &v, double upperBound) {
2845  using Teuchos::RCP;
2846  using Teuchos::rcp_dynamic_cast;
2847 
2848  // cast so entries are accessible
2849  // RCP<Thyra::SpmdMultiVectorBase<double> > spmdMVec
2850  // = rcp_dynamic_cast<Thyra::DefaultSpmdMultiVector<double> >(v);
2851  for (Thyra::Ordinal i = 0; i < v->domain()->dim(); i++) {
2852  RCP<Thyra::SpmdVectorBase<double>> spmdVec =
2853  rcp_dynamic_cast<Thyra::SpmdVectorBase<double>>(v->col(i), true);
2854 
2855  Teuchos::ArrayRCP<double> values;
2856  // spmdMVec->getNonconstLocalData(Teuchos::ptrFromRef(values),Teuchos::ptrFromRef(i));
2857  spmdVec->getNonconstLocalData(Teuchos::ptrFromRef(values));
2858  for (Teuchos::ArrayRCP<double>::size_type j = 0; j < values.size(); j++) {
2859  if (values[j] > upperBound) values[j] = upperBound;
2860  }
2861  }
2862 }
2863 
2864 void replaceValue(MultiVector &v, double currentValue, double newValue) {
2865  using Teuchos::RCP;
2866  using Teuchos::rcp_dynamic_cast;
2867 
2868  // cast so entries are accessible
2869  // RCP<Thyra::SpmdMultiVectorBase<double> > spmdMVec
2870  // = rcp_dynamic_cast<Thyra::SpmdMultiVectorBase<double> >(v,true);
2871  for (Thyra::Ordinal i = 0; i < v->domain()->dim(); i++) {
2872  RCP<Thyra::SpmdVectorBase<double>> spmdVec =
2873  rcp_dynamic_cast<Thyra::SpmdVectorBase<double>>(v->col(i), true);
2874 
2875  Teuchos::ArrayRCP<double> values;
2876  // spmdMVec->getNonconstLocalData(Teuchos::ptrFromRef(values),Teuchos::ptrFromRef(i));
2877  spmdVec->getNonconstLocalData(Teuchos::ptrFromRef(values));
2878  for (Teuchos::ArrayRCP<double>::size_type j = 0; j < values.size(); j++) {
2879  if (values[j] == currentValue) values[j] = newValue;
2880  }
2881  }
2882 }
2883 
2884 void columnAverages(const MultiVector &v, std::vector<double> &averages) {
2885  averages.resize(v->domain()->dim());
2886 
2887  // sum over each column
2888  Thyra::sums<double>(*v, averages);
2889 
2890  // build averages
2891  Thyra::Ordinal rows = v->range()->dim();
2892  for (std::size_t i = 0; i < averages.size(); i++) averages[i] = averages[i] / rows;
2893 }
2894 
2895 double average(const MultiVector &v) {
2896  Thyra::Ordinal rows = v->range()->dim();
2897  Thyra::Ordinal cols = v->domain()->dim();
2898 
2899  std::vector<double> averages;
2900  columnAverages(v, averages);
2901 
2902  double sum = 0.0;
2903  for (std::size_t i = 0; i < averages.size(); i++) sum += averages[i] * rows;
2904 
2905  return sum / (rows * cols);
2906 }
2907 
2908 bool isPhysicallyBlockedLinearOp(const LinearOp &op) {
2909  // See if the operator is a PBLO
2910  RCP<const Thyra::PhysicallyBlockedLinearOpBase<double>> pblo =
2911  rcp_dynamic_cast<const Thyra::PhysicallyBlockedLinearOpBase<double>>(op);
2912  if (!pblo.is_null()) return true;
2913 
2914  // See if the operator is a wrapped PBLO
2915  ST scalar = 0.0;
2916  Thyra::EOpTransp transp = Thyra::NOTRANS;
2917  RCP<const Thyra::LinearOpBase<ST>> wrapped_op;
2918  Thyra::unwrap(op, &scalar, &transp, &wrapped_op);
2919  pblo = rcp_dynamic_cast<const Thyra::PhysicallyBlockedLinearOpBase<double>>(wrapped_op);
2920  if (!pblo.is_null()) return true;
2921 
2922  return false;
2923 }
2924 
2925 RCP<const Thyra::PhysicallyBlockedLinearOpBase<double>> getPhysicallyBlockedLinearOp(
2926  const LinearOp &op, ST *scalar, bool *transp) {
2927  // If the operator is a TpetraLinearOp
2928  RCP<const Thyra::PhysicallyBlockedLinearOpBase<double>> pblo =
2929  rcp_dynamic_cast<const Thyra::PhysicallyBlockedLinearOpBase<double>>(op);
2930  if (!pblo.is_null()) {
2931  *scalar = 1.0;
2932  *transp = false;
2933  return pblo;
2934  }
2935 
2936  // If the operator is a wrapped TpetraLinearOp
2937  RCP<const Thyra::LinearOpBase<ST>> wrapped_op;
2938  Thyra::EOpTransp eTransp = Thyra::NOTRANS;
2939  Thyra::unwrap(op, scalar, &eTransp, &wrapped_op);
2940  pblo = rcp_dynamic_cast<const Thyra::PhysicallyBlockedLinearOpBase<double>>(wrapped_op, true);
2941  if (!pblo.is_null()) {
2942  *transp = true;
2943  if (eTransp == Thyra::NOTRANS) *transp = false;
2944  return pblo;
2945  }
2946 
2947  return Teuchos::null;
2948 }
2949 
2950 std::string formatBlockName(const std::string &prefix, int i, int j, int nrow) {
2951  unsigned digits = 0;
2952  auto blockId = nrow - 1;
2953  do {
2954  blockId /= 10;
2955  digits++;
2956  } while (blockId);
2957 
2958  std::ostringstream ss;
2959  ss << prefix << "_";
2960  ss << std::setfill('0') << std::setw(digits) << i;
2961  ss << "_";
2962  ss << std::setfill('0') << std::setw(digits) << j;
2963  ss << ".mm";
2964  return ss.str();
2965 }
2966 
2967 } // namespace Teko
Implements the Epetra_Operator interface with a Thyra LinearOperator. This enables the use of absrtac...