|
Ifpack2 Templated Preconditioning Package
Version 1.0
|
00001 /*@HEADER 00002 // *********************************************************************** 00003 // 00004 // Ifpack2: Tempated Object-Oriented Algebraic Preconditioner Package 00005 // Copyright (2009) Sandia Corporation 00006 // 00007 // Under terms of Contract DE-AC04-94AL85000, there is a non-exclusive 00008 // license for use of this work by or on behalf of the U.S. Government. 00009 // 00010 // Redistribution and use in source and binary forms, with or without 00011 // modification, are permitted provided that the following conditions are 00012 // met: 00013 // 00014 // 1. Redistributions of source code must retain the above copyright 00015 // notice, this list of conditions and the following disclaimer. 00016 // 00017 // 2. Redistributions in binary form must reproduce the above copyright 00018 // notice, this list of conditions and the following disclaimer in the 00019 // documentation and/or other materials provided with the distribution. 00020 // 00021 // 3. Neither the name of the Corporation nor the names of the 00022 // contributors may be used to endorse or promote products derived from 00023 // this software without specific prior written permission. 00024 // 00025 // THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "AS IS" AND ANY 00026 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 00027 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 00028 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SANDIA CORPORATION OR THE 00029 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 00030 // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 00031 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 00032 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 00033 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 00034 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 00035 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 00036 // 00037 // Questions? Contact Michael A. Heroux (maherou@sandia.gov) 00038 // 00039 // *********************************************************************** 00040 //@HEADER 00041 */ 00042 00043 #ifndef IFPACK2_DENSECONTAINER_DEF_HPP 00044 #define IFPACK2_DENSECONTAINER_DEF_HPP 00045 00046 #include "Ifpack2_TriDiContainer_decl.hpp" 00047 #include "Teuchos_LAPACK.hpp" 00048 00049 #ifdef HAVE_MPI 00050 # include <mpi.h> 00051 # include "Teuchos_DefaultMpiComm.hpp" 00052 #else 00053 # include "Teuchos_DefaultSerialComm.hpp" 00054 #endif // HAVE_MPI 00055 00056 00057 namespace Ifpack2 { 00058 00059 //============================================================================== 00060 template<class MatrixType, class LocalScalarType> 00061 TriDiContainer<MatrixType, LocalScalarType>:: 00062 TriDiContainer (const Teuchos::RCP<const row_matrix_type>& matrix, 00063 const Teuchos::ArrayView<const local_ordinal_type>& localRows) : 00064 Container<MatrixType> (matrix, localRows), 00065 numRows_ (localRows.size ()), 00066 diagBlock_ (numRows_, numRows_), 00067 ipiv_ (numRows_, 0) 00068 { 00069 using Teuchos::Array; 00070 using Teuchos::ArrayView; 00071 using Teuchos::RCP; 00072 using Teuchos::rcp; 00073 using Teuchos::toString; 00074 typedef Tpetra::Map<local_ordinal_type, global_ordinal_type, node_type> map_type; 00075 typedef typename ArrayView<const local_ordinal_type>::size_type size_type; 00076 TEUCHOS_TEST_FOR_EXCEPTION( 00077 ! matrix->hasColMap (), std::invalid_argument, "Ifpack2::TriDiContainer: " 00078 "The constructor's input matrix must have a column Map."); 00079 00080 // Check whether the input set of local row indices is correct. 00081 const map_type& rowMap = * (matrix->getRowMap ()); 00082 const size_type numRows = localRows.size (); 00083 bool rowIndicesValid = true; 00084 Array<local_ordinal_type> invalidLocalRowIndices; 00085 for (size_type i = 0; i < numRows; ++i) { 00086 if (! rowMap.isNodeLocalElement (localRows[i])) { 00087 rowIndicesValid = false; 00088 invalidLocalRowIndices.push_back (localRows[i]); 00089 break; 00090 } 00091 } 00092 TEUCHOS_TEST_FOR_EXCEPTION( 00093 ! rowIndicesValid, std::invalid_argument, "Ifpack2::TriDiContainer: " 00094 "On process " << rowMap.getComm ()->getRank () << " of " 00095 << rowMap.getComm ()->getSize () << ", in the given set of local row " 00096 "indices localRows = " << toString (localRows) << ", the following " 00097 "entries are not valid local row indices on the calling process: " 00098 << toString (invalidLocalRowIndices) << "."); 00099 00100 #ifdef HAVE_MPI 00101 RCP<const Teuchos::Comm<int> > localComm = 00102 rcp (new Teuchos::MpiComm<int> (MPI_COMM_SELF)); 00103 #else 00104 RCP<const Teuchos::Comm<int> > localComm = 00105 rcp (new Teuchos::SerialComm<int> ()); 00106 #endif // HAVE_MPI 00107 00108 // FIXME (mfh 25 Aug 2013) What if the matrix's row Map has a 00109 // different index base than zero? 00110 const global_ordinal_type indexBase = 0; 00111 localMap_ = rcp (new map_type (numRows_, indexBase, localComm)); 00112 } 00113 00114 //============================================================================== 00115 template<class MatrixType, class LocalScalarType> 00116 TriDiContainer<MatrixType, LocalScalarType>::~TriDiContainer() 00117 {} 00118 00119 //============================================================================== 00120 template<class MatrixType, class LocalScalarType> 00121 size_t TriDiContainer<MatrixType, LocalScalarType>::getNumRows () const 00122 { 00123 return numRows_; 00124 } 00125 00126 //============================================================================== 00127 template<class MatrixType, class LocalScalarType> 00128 bool TriDiContainer<MatrixType, LocalScalarType>::isInitialized () const 00129 { 00130 return IsInitialized_; 00131 } 00132 00133 //============================================================================== 00134 template<class MatrixType, class LocalScalarType> 00135 bool TriDiContainer<MatrixType, LocalScalarType>::isComputed() const 00136 { 00137 return IsComputed_; 00138 } 00139 00140 //============================================================================== 00141 template<class MatrixType, class LocalScalarType> 00142 void TriDiContainer<MatrixType, LocalScalarType>:: 00143 setParameters (const Teuchos::ParameterList& List) 00144 { 00145 (void) List; // the solver doesn't currently take any parameters 00146 } 00147 00148 //============================================================================== 00149 template<class MatrixType, class LocalScalarType> 00150 void TriDiContainer<MatrixType, LocalScalarType>::initialize () 00151 { 00152 using Teuchos::null; 00153 using Teuchos::rcp; 00154 00155 // We assume that if you called this method, you intend to recompute 00156 // everything. 00157 IsInitialized_ = false; 00158 IsComputed_ = false; 00159 00160 // Fill the diagonal block and LU permutation array with zeros. 00161 diagBlock_.putScalar (Teuchos::ScalarTraits<local_scalar_type>::zero ()); 00162 std::fill (ipiv_.begin (), ipiv_.end (), 0); 00163 00164 IsInitialized_ = true; 00165 } 00166 00167 //============================================================================== 00168 template<class MatrixType, class LocalScalarType> 00169 void TriDiContainer<MatrixType, LocalScalarType>::compute () 00170 { 00171 TEUCHOS_TEST_FOR_EXCEPTION( 00172 static_cast<size_t> (ipiv_.size ()) != numRows_, std::logic_error, 00173 "Ifpack2::TriDiContainer::compute: ipiv_ array has the wrong size. " 00174 "Please report this bug to the Ifpack2 developers."); 00175 00176 IsComputed_ = false; 00177 if (! this->isInitialized ()) { 00178 this->initialize (); 00179 } 00180 00181 // Extract the submatrix. 00182 extract (this->getMatrix ()); // extract the submatrix 00183 factor (); // factor the submatrix 00184 00185 IsComputed_ = true; 00186 } 00187 00188 template<class MatrixType, class LocalScalarType> 00189 void TriDiContainer<MatrixType, LocalScalarType>::factor () 00190 { 00191 Teuchos::LAPACK<int, local_scalar_type> lapack; 00192 int INFO = 0; 00193 lapack.GTTRF (diagBlock_.numRowsCols (), 00194 diagBlock_.DL(), 00195 diagBlock_.D(), 00196 diagBlock_.DU(), 00197 diagBlock_.DU2(), 00198 ipiv_.getRawPtr (), &INFO); 00199 // INFO < 0 is a bug. 00200 TEUCHOS_TEST_FOR_EXCEPTION( 00201 INFO < 0, std::logic_error, "Ifpack2::TriDiContainer::factor: " 00202 "LAPACK's _GTTRF (LU factorization with partial pivoting) was called " 00203 "incorrectly. INFO = " << INFO << " < 0. " 00204 "Please report this bug to the Ifpack2 developers."); 00205 // INFO > 0 means the matrix is singular. This is probably an issue 00206 // either with the choice of rows the rows we extracted, or with the 00207 // input matrix itself. 00208 TEUCHOS_TEST_FOR_EXCEPTION( 00209 INFO > 0, std::runtime_error, "Ifpack2::TriDiContainer::factor: " 00210 "LAPACK's _GTTRF (LU factorization with partial pivoting) reports that the " 00211 "computed U factor is exactly singular. U(" << INFO << "," << INFO << ") " 00212 "(one-based index i) is exactly zero. This probably means that the input " 00213 "matrix has a singular diagonal block."); 00214 } 00215 00216 //============================================================================== 00217 template<class MatrixType, class LocalScalarType> 00218 void TriDiContainer<MatrixType, LocalScalarType>:: 00219 applyImpl (const local_mv_type& X, 00220 local_mv_type& Y, 00221 Teuchos::ETransp mode, 00222 LocalScalarType alpha, 00223 LocalScalarType beta) const 00224 { 00225 using Teuchos::ArrayRCP; 00226 using Teuchos::RCP; 00227 using Teuchos::rcp; 00228 using Teuchos::rcpFromRef; 00229 00230 TEUCHOS_TEST_FOR_EXCEPTION( 00231 X.getLocalLength () != Y.getLocalLength (), 00232 std::logic_error, "Ifpack2::TriDiContainer::applyImpl: X and Y have " 00233 "incompatible dimensions (" << X.getLocalLength () << " resp. " 00234 << Y.getLocalLength () << "). Please report this bug to " 00235 "the Ifpack2 developers."); 00236 TEUCHOS_TEST_FOR_EXCEPTION( 00237 localMap_->getNodeNumElements () != X.getLocalLength (), 00238 std::logic_error, "Ifpack2::TriDiContainer::applyImpl: The inverse " 00239 "operator and X have incompatible dimensions (" << 00240 localMap_->getNodeNumElements () << " resp. " 00241 << X.getLocalLength () << "). Please report this bug to " 00242 "the Ifpack2 developers."); 00243 TEUCHOS_TEST_FOR_EXCEPTION( 00244 localMap_->getNodeNumElements () != Y.getLocalLength (), 00245 std::logic_error, "Ifpack2::TriDiContainer::applyImpl: The inverse " 00246 "operator and Y have incompatible dimensions (" << 00247 localMap_->getNodeNumElements () << " resp. " 00248 << Y.getLocalLength () << "). Please report this bug to " 00249 "the Ifpack2 developers."); 00250 TEUCHOS_TEST_FOR_EXCEPTION( 00251 X.getLocalLength () != static_cast<size_t> (diagBlock_.numRowsCols()), 00252 std::logic_error, "Ifpack2::TriDiContainer::applyImpl: The input " 00253 "multivector X has incompatible dimensions from those of the " 00254 "inverse operator (" << X.getLocalLength () << " vs. " 00255 << (mode == Teuchos::NO_TRANS ? diagBlock_.numRowsCols () : diagBlock_.numRowsCols()) 00256 << "). Please report this bug to the Ifpack2 developers."); 00257 TEUCHOS_TEST_FOR_EXCEPTION( 00258 X.getLocalLength () != static_cast<size_t> (diagBlock_.numRowsCols()), 00259 std::logic_error, "Ifpack2::TriDiContainer::applyImpl: The output " 00260 "multivector Y has incompatible dimensions from those of the " 00261 "inverse operator (" << Y.getLocalLength () << " vs. " 00262 << (mode == Teuchos::NO_TRANS ? diagBlock_.numRowsCols() : diagBlock_.numRowsCols ()) 00263 << "). Please report this bug to the Ifpack2 developers."); 00264 00265 typedef Teuchos::ScalarTraits<local_scalar_type> STS; 00266 const int numVecs = static_cast<int> (X.getNumVectors ()); 00267 if (alpha == STS::zero ()) { // don't need to solve the linear system 00268 if (beta == STS::zero ()) { 00269 // Use BLAS AXPY semantics for beta == 0: overwrite, clobbering 00270 // any Inf or NaN values in Y (rather than multiplying them by 00271 // zero, resulting in NaN values). 00272 Y.putScalar (STS::zero ()); 00273 } 00274 else { // beta != 0 00275 Y.scale (beta); 00276 } 00277 } 00278 else { // alpha != 0; must solve the linear system 00279 Teuchos::LAPACK<int, local_scalar_type> lapack; 00280 // If beta is nonzero or Y is not constant stride, we have to use 00281 // a temporary output multivector. It gets a copy of X, since 00282 // GETRS overwrites its (multi)vector input with its output. 00283 RCP<local_mv_type> Y_tmp; 00284 if (beta == STS::zero () ){ 00285 Y = X; 00286 Y_tmp = rcpFromRef (Y); 00287 } 00288 else { 00289 Y_tmp = rcp (new local_mv_type (X)); // constructor copies X 00290 } 00291 const int Y_stride = static_cast<int> (Y_tmp->getStride ()); 00292 ArrayRCP<local_scalar_type> Y_view = Y_tmp->get1dViewNonConst (); 00293 local_scalar_type* const Y_ptr = Y_view.getRawPtr (); 00294 00295 int INFO = 0; 00296 const char trans = 00297 (mode == Teuchos::CONJ_TRANS ? 'C' : (mode == Teuchos::TRANS ? 'T' : 'N')); 00298 lapack.GTTRS (trans, diagBlock_.numRowsCols(),numVecs, 00299 diagBlock_.DL(), 00300 diagBlock_.D(), 00301 diagBlock_.DU(), 00302 diagBlock_.DU2(), 00303 ipiv_.getRawPtr (), Y_ptr, Y_stride, &INFO); 00304 TEUCHOS_TEST_FOR_EXCEPTION( 00305 INFO != 0, std::runtime_error, "Ifpack2::TriDiContainer::applyImpl: " 00306 "LAPACK's _GETRS (solve using LU factorization with partial pivoting) " 00307 "failed with INFO = " << INFO << " != 0."); 00308 00309 if (beta != STS::zero ()) { 00310 Y.update (alpha, *Y_tmp, beta); 00311 } 00312 else if (! Y.isConstantStride ()) { 00313 Y = *Y_tmp; 00314 } 00315 } 00316 } 00317 00318 //============================================================================== 00319 template<class MatrixType, class LocalScalarType> 00320 void TriDiContainer<MatrixType, LocalScalarType>:: 00321 apply (const Tpetra::MultiVector<scalar_type,local_ordinal_type,global_ordinal_type,node_type>& X, 00322 Tpetra::MultiVector<scalar_type,local_ordinal_type,global_ordinal_type,node_type>& Y, 00323 Teuchos::ETransp mode, 00324 scalar_type alpha, 00325 scalar_type beta) const 00326 { 00327 using Teuchos::ArrayView; 00328 using Teuchos::as; 00329 using Teuchos::RCP; 00330 using Teuchos::rcp; 00331 00332 // The local operator might have a different Scalar type than 00333 // MatrixType. This means that we might have to convert X and Y to 00334 // the Tpetra::MultiVector specialization that the local operator 00335 // wants. This class' X_ and Y_ internal fields are of the right 00336 // type for the local operator, so we can use those as targets. 00337 00338 // Tpetra::MultiVector specialization corresponding to MatrixType. 00339 typedef Tpetra::MultiVector<scalar_type, local_ordinal_type, 00340 global_ordinal_type, node_type> global_mv_type; 00341 Details::MultiVectorLocalGatherScatter<global_mv_type, local_mv_type> mvgs; 00342 const size_t numVecs = X.getNumVectors (); 00343 00344 TEUCHOS_TEST_FOR_EXCEPTION( 00345 ! IsComputed_, std::runtime_error, "Ifpack2::TriDiContainer::apply: " 00346 "You must have called the compute() method before you may call apply(). " 00347 "You may call the apply() method as many times as you want after calling " 00348 "compute() once, but you must have called compute() at least once."); 00349 TEUCHOS_TEST_FOR_EXCEPTION( 00350 numVecs != Y.getNumVectors (), std::runtime_error, 00351 "Ifpack2::TriDiContainer::apply: X and Y have different numbers of " 00352 "vectors. X has " << X.getNumVectors () 00353 << ", but Y has " << X.getNumVectors () << "."); 00354 00355 if (numVecs == 0) { 00356 return; // done! nothing to do 00357 } 00358 00359 // The local operator works on a permuted subset of the local parts 00360 // of X and Y. The subset and permutation are defined by the index 00361 // array returned by getLocalRows(). If the permutation is trivial 00362 // and the subset is exactly equal to the local indices, then we 00363 // could use the local parts of X and Y exactly, without needing to 00364 // permute. Otherwise, we have to use temporary storage to permute 00365 // X and Y. For now, we always use temporary storage. 00366 // 00367 // Create temporary permuted versions of the input and output. 00368 // (Re)allocate X_ and/or Y_ only if necessary. We'll use them to 00369 // store the permuted versions of X resp. Y. Note that X_local has 00370 // the domain Map of the operator, which may be a permuted subset of 00371 // the local Map corresponding to X.getMap(). Similarly, Y_local 00372 // has the range Map of the operator, which may be a permuted subset 00373 // of the local Map corresponding to Y.getMap(). numRows_ here 00374 // gives the number of rows in the row Map of the local Inverse_ 00375 // operator. 00376 // 00377 // FIXME (mfh 20 Aug 2013) There might be an implicit assumption 00378 // here that the row Map and the range Map of that operator are 00379 // the same. 00380 // 00381 // FIXME (mfh 20 Aug 2013) This "local permutation" functionality 00382 // really belongs in Tpetra. 00383 00384 if (X_.is_null ()) { 00385 X_ = rcp (new local_mv_type (localMap_, numVecs)); 00386 } 00387 RCP<local_mv_type> X_local = X_; 00388 TEUCHOS_TEST_FOR_EXCEPTION( 00389 X_local->getLocalLength () != numRows_, std::logic_error, 00390 "Ifpack2::TriDiContainer::apply: " 00391 "X_local has length " << X_local->getLocalLength () << ", which does " 00392 "not match numRows_ = " << numRows_ << ". Please report this bug to " 00393 "the Ifpack2 developers."); 00394 ArrayView<const local_ordinal_type> localRows = this->getLocalRows (); 00395 mvgs.gather (*X_local, X, localRows); 00396 00397 // We must gather the contents of the output multivector Y even on 00398 // input to applyImpl(), since the inverse operator might use it as 00399 // an initial guess for a linear solve. We have no way of knowing 00400 // whether it does or does not. 00401 00402 if (Y_.is_null ()) { 00403 Y_ = rcp (new local_mv_type (localMap_, numVecs)); 00404 } 00405 RCP<local_mv_type> Y_local = Y_; 00406 TEUCHOS_TEST_FOR_EXCEPTION( 00407 Y_local->getLocalLength () != numRows_, std::logic_error, 00408 "Ifpack2::TriDiContainer::apply: " 00409 "Y_local has length " << X_local->getLocalLength () << ", which does " 00410 "not match numRows_ = " << numRows_ << ". Please report this bug to " 00411 "the Ifpack2 developers."); 00412 mvgs.gather (*Y_local, Y, localRows); 00413 00414 // Apply the local operator: 00415 // Y_local := beta*Y_local + alpha*M^{-1}*X_local 00416 this->applyImpl (*X_local, *Y_local, mode, as<local_scalar_type> (alpha), 00417 as<local_scalar_type> (beta)); 00418 00419 // Scatter the permuted subset output vector Y_local back into the 00420 // original output multivector Y. 00421 mvgs.scatter (Y, *Y_local, localRows); 00422 } 00423 00424 //============================================================================== 00425 template<class MatrixType, class LocalScalarType> 00426 void TriDiContainer<MatrixType,LocalScalarType>:: 00427 weightedApply (const Tpetra::MultiVector<scalar_type,local_ordinal_type,global_ordinal_type,node_type>& X, 00428 Tpetra::MultiVector<scalar_type,local_ordinal_type,global_ordinal_type,node_type>& Y, 00429 const Tpetra::Vector<scalar_type,local_ordinal_type,global_ordinal_type,node_type>& D, 00430 Teuchos::ETransp mode, 00431 scalar_type alpha, 00432 scalar_type beta) const 00433 { 00434 using Teuchos::ArrayRCP; 00435 using Teuchos::ArrayView; 00436 using Teuchos::Range1D; 00437 using Teuchos::RCP; 00438 using Teuchos::rcp; 00439 using Teuchos::rcp_const_cast; 00440 using std::cerr; 00441 using std::endl; 00442 typedef Teuchos::ScalarTraits<scalar_type> STS; 00443 00444 // The local operator template parameter might have a different 00445 // Scalar type than MatrixType. This means that we might have to 00446 // convert X and Y to the Tpetra::MultiVector specialization that 00447 // the local operator wants. This class' X_ and Y_ internal fields 00448 // are of the right type for the local operator, so we can use those 00449 // as targets. 00450 00451 // Tpetra::MultiVector specialization corresponding to the global operator. 00452 typedef Tpetra::MultiVector<scalar_type, local_ordinal_type, 00453 global_ordinal_type, node_type> global_mv_type; 00454 // Tpetra::Vector specialization corresponding to the local 00455 // operator. We will use this for the subset permutation of the 00456 // diagonal scaling D. 00457 typedef Tpetra::Vector<local_scalar_type, local_ordinal_type, 00458 global_ordinal_type, node_type> local_vec_type; 00459 00460 Details::MultiVectorLocalGatherScatter<global_mv_type, local_mv_type> mvgs; 00461 const size_t numVecs = X.getNumVectors (); 00462 00463 TEUCHOS_TEST_FOR_EXCEPTION( 00464 ! IsComputed_, std::runtime_error, "Ifpack2::TriDiContainer::" 00465 "weightedApply: You must have called the compute() method before you may " 00466 "call apply(). You may call the apply() method as many times as you want " 00467 "after calling compute() once, but you must have called compute() at least " 00468 "once."); 00469 TEUCHOS_TEST_FOR_EXCEPTION( 00470 numVecs != Y.getNumVectors (), std::runtime_error, 00471 "Ifpack2::TriDiContainer::weightedApply: X and Y have different numbers " 00472 "of vectors. X has " << X.getNumVectors () << ", but Y has " 00473 << X.getNumVectors () << "."); 00474 00475 if (numVecs == 0) { 00476 return; // done! nothing to do 00477 } 00478 00479 // The local operator works on a permuted subset of the local parts 00480 // of X and Y. The subset and permutation are defined by the index 00481 // array returned by getLocalRows(). If the permutation is trivial 00482 // and the subset is exactly equal to the local indices, then we 00483 // could use the local parts of X and Y exactly, without needing to 00484 // permute. Otherwise, we have to use temporary storage to permute 00485 // X and Y. For now, we always use temporary storage. 00486 // 00487 // Create temporary permuted versions of the input and output. 00488 // (Re)allocate X_ and/or Y_ only if necessary. We'll use them to 00489 // store the permuted versions of X resp. Y. Note that X_local has 00490 // the domain Map of the operator, which may be a permuted subset of 00491 // the local Map corresponding to X.getMap(). Similarly, Y_local 00492 // has the range Map of the operator, which may be a permuted subset 00493 // of the local Map corresponding to Y.getMap(). numRows_ here 00494 // gives the number of rows in the row Map of the local operator. 00495 // 00496 // FIXME (mfh 20 Aug 2013) There might be an implicit assumption 00497 // here that the row Map and the range Map of that operator are 00498 // the same. 00499 // 00500 // FIXME (mfh 20 Aug 2013) This "local permutation" functionality 00501 // really belongs in Tpetra. 00502 00503 if (X_.is_null ()) { 00504 X_ = rcp (new local_mv_type (localMap_, numVecs)); 00505 } 00506 RCP<local_mv_type> X_local = X_; 00507 TEUCHOS_TEST_FOR_EXCEPTION( 00508 X_local->getLocalLength () != numRows_, std::logic_error, 00509 "Ifpack2::TriDiContainer::weightedApply: " 00510 "X_local has length " << X_local->getLocalLength () << ", which does " 00511 "not match numRows_ = " << numRows_ << ". Please report this bug to " 00512 "the Ifpack2 developers."); 00513 ArrayView<const local_ordinal_type> localRows = this->getLocalRows (); 00514 mvgs.gather (*X_local, X, localRows); 00515 00516 // We must gather the output multivector Y even on input to 00517 // applyImpl(), since the local operator might use it as an initial 00518 // guess for a linear solve. We have no way of knowing whether it 00519 // does or does not. 00520 00521 if (Y_.is_null ()) { 00522 Y_ = rcp (new local_mv_type (localMap_, numVecs)); 00523 } 00524 RCP<local_mv_type> Y_local = Y_; 00525 TEUCHOS_TEST_FOR_EXCEPTION( 00526 Y_local->getLocalLength () != numRows_, std::logic_error, 00527 "Ifpack2::TriDiContainer::weightedApply: " 00528 "Y_local has length " << X_local->getLocalLength () << ", which does " 00529 "not match numRows_ = " << numRows_ << ". Please report this bug to " 00530 "the Ifpack2 developers."); 00531 mvgs.gather (*Y_local, Y, localRows); 00532 00533 // Apply the diagonal scaling D to the input X. It's our choice 00534 // whether the result has the original input Map of X, or the 00535 // permuted subset Map of X_local. If the latter, we also need to 00536 // gather D into the permuted subset Map. We choose the latter, to 00537 // save memory and computation. Thus, we do the following: 00538 // 00539 // 1. Gather D into a temporary vector D_local. 00540 // 2. Create a temporary X_scaled to hold diag(D_local) * X_local. 00541 // 3. Compute X_scaled := diag(D_loca) * X_local. 00542 00543 RCP<local_vec_type> D_local = rcp (new local_vec_type (localMap_)); 00544 TEUCHOS_TEST_FOR_EXCEPTION( 00545 D_local->getLocalLength () != numRows_, std::logic_error, 00546 "Ifpack2::TriDiContainer::weightedApply: " 00547 "D_local has length " << X_local->getLocalLength () << ", which does " 00548 "not match numRows_ = " << numRows_ << ". Please report this bug to " 00549 "the Ifpack2 developers."); 00550 mvgs.gather (*D_local, D, localRows); 00551 RCP<local_mv_type> X_scaled = rcp (new local_mv_type (localMap_, numVecs)); 00552 X_scaled->elementWiseMultiply (STS::one (), *D_local, *X_local, STS::zero ()); 00553 00554 // Y_temp will hold the result of M^{-1}*X_scaled. If beta == 0, we 00555 // can write the result of Inverse_->apply() directly to Y_local, so 00556 // Y_temp may alias Y_local. Otherwise, if beta != 0, we need 00557 // temporary storage for M^{-1}*X_scaled, so Y_temp must be 00558 // different than Y_local. 00559 RCP<local_mv_type> Y_temp; 00560 if (beta == STS::zero ()) { 00561 Y_temp = Y_local; 00562 } else { 00563 Y_temp = rcp (new local_mv_type (localMap_, numVecs)); 00564 } 00565 00566 // Apply the local operator: Y_temp := M^{-1} * X_scaled 00567 applyImpl (*X_scaled, *Y_temp, mode, STS::one (), STS::zero ()); 00568 // Y_local := beta * Y_local + alpha * diag(D_local) * Y_temp. 00569 // 00570 // Note that we still use the permuted subset scaling D_local here, 00571 // because Y_temp has the same permuted subset Map. That's good, in 00572 // fact, because it's a subset: less data to read and multiply. 00573 Y_local->elementWiseMultiply (alpha, *D_local, *Y_temp, beta); 00574 00575 // Copy the permuted subset output vector Y_local into the original 00576 // output multivector Y. 00577 mvgs.scatter (Y, *Y_local, localRows); 00578 } 00579 00580 //============================================================================== 00581 template<class MatrixType, class LocalScalarType> 00582 std::ostream& TriDiContainer<MatrixType,LocalScalarType>::print(std::ostream& os) const 00583 { 00584 Teuchos::FancyOStream fos(Teuchos::rcp(&os,false)); 00585 fos.setOutputToRootOnly(0); 00586 describe(fos); 00587 return(os); 00588 } 00589 00590 //============================================================================== 00591 template<class MatrixType, class LocalScalarType> 00592 std::string TriDiContainer<MatrixType,LocalScalarType>::description() const 00593 { 00594 std::ostringstream oss; 00595 oss << Teuchos::Describable::description(); 00596 if (isInitialized()) { 00597 if (isComputed()) { 00598 oss << "{status = initialized, computed"; 00599 } 00600 else { 00601 oss << "{status = initialized, not computed"; 00602 } 00603 } 00604 else { 00605 oss << "{status = not initialized, not computed"; 00606 } 00607 00608 oss << "}"; 00609 return oss.str(); 00610 } 00611 00612 //============================================================================== 00613 template<class MatrixType, class LocalScalarType> 00614 void TriDiContainer<MatrixType,LocalScalarType>::describe(Teuchos::FancyOStream &os, const Teuchos::EVerbosityLevel verbLevel) const 00615 { 00616 using std::endl; 00617 if(verbLevel==Teuchos::VERB_NONE) return; 00618 os << "================================================================================" << endl; 00619 os << "Ifpack2::TriDiContainer" << endl; 00620 os << "Number of rows = " << numRows_ << endl; 00621 os << "isInitialized() = " << IsInitialized_ << endl; 00622 os << "isComputed() = " << IsComputed_ << endl; 00623 os << "================================================================================" << endl; 00624 os << endl; 00625 } 00626 00627 //============================================================================== 00628 template<class MatrixType, class LocalScalarType> 00629 void TriDiContainer<MatrixType,LocalScalarType>:: 00630 extract (const Teuchos::RCP<const row_matrix_type>& globalMatrix) 00631 { 00632 using Teuchos::Array; 00633 using Teuchos::ArrayView; 00634 using Teuchos::toString; 00635 typedef local_ordinal_type LO; 00636 typedef global_ordinal_type GO; 00637 typedef Tpetra::Map<LO, GO, node_type> map_type; 00638 const size_t inputMatrixNumRows = globalMatrix->getNodeNumRows (); 00639 // We only use the rank of the calling process and the number of MPI 00640 // processes for generating error messages. Extraction itself is 00641 // entirely local to each participating MPI process. 00642 const int myRank = globalMatrix->getRowMap ()->getComm ()->getRank (); 00643 const int numProcs = globalMatrix->getRowMap ()->getComm ()->getSize (); 00644 00645 // Sanity check that the local row indices to extract fall within 00646 // the valid range of local row indices for the input matrix. 00647 ArrayView<const LO> localRows = this->getLocalRows (); 00648 for (size_t j = 0; j < numRows_; ++j) { 00649 TEUCHOS_TEST_FOR_EXCEPTION( 00650 localRows[j] < 0 || 00651 static_cast<size_t> (localRows[j]) >= inputMatrixNumRows, 00652 std::runtime_error, "Ifpack2::TriDiContainer::extract: On process " << 00653 myRank << " of " << numProcs << ", localRows[j=" << j << "] = " << 00654 localRows[j] << ", which is out of the valid range of local row indices " 00655 "indices [0, " << (inputMatrixNumRows - 1) << "] for the input matrix."); 00656 } 00657 00658 // Convert the local row indices we want into local column indices. 00659 // For every local row ii_local = localRows[i] we take, we also want 00660 // to take the corresponding column. To find the corresponding 00661 // column, we use the row Map to convert the local row index 00662 // ii_local into a global index ii_global, and then use the column 00663 // Map to convert ii_global into a local column index jj_local. If 00664 // the input matrix doesn't have a column Map, we need to be using 00665 // global indices anyway... 00666 00667 // We use the domain Map to exclude off-process global entries. 00668 const map_type& globalRowMap = * (globalMatrix->getRowMap ()); 00669 const map_type& globalColMap = * (globalMatrix->getColMap ()); 00670 const map_type& globalDomMap = * (globalMatrix->getDomainMap ()); 00671 00672 bool rowIndsValid = true; 00673 bool colIndsValid = true; 00674 Array<LO> localCols (numRows_); 00675 // For error messages, collect the sets of invalid row indices and 00676 // invalid column indices. They are otherwise not useful. 00677 Array<LO> invalidLocalRowInds; 00678 Array<GO> invalidGlobalColInds; 00679 for (size_t i = 0; i < numRows_; ++i) { 00680 // ii_local is the (local) row index we want to look up. 00681 const LO ii_local = localRows[i]; 00682 // Find the global index jj_global corresponding to ii_local. 00683 // Global indices are the same (rather, are required to be the 00684 // same) in all three Maps, which is why we use jj (suggesting a 00685 // column index, which is how we will use it below). 00686 const GO jj_global = globalRowMap.getGlobalElement (ii_local); 00687 if (jj_global == Teuchos::OrdinalTraits<GO>::invalid ()) { 00688 // If ii_local is not a local index in the row Map on the 00689 // calling process, that means localRows is incorrect. We've 00690 // already checked for this in the constructor, but we might as 00691 // well check again here, since it's cheap to do so (just an 00692 // integer comparison, since we need jj_global anyway). 00693 rowIndsValid = false; 00694 invalidLocalRowInds.push_back (ii_local); 00695 break; 00696 } 00697 // Exclude "off-process" entries: that is, those in the column Map 00698 // on this process that are not in the domain Map on this process. 00699 if (globalDomMap.isNodeGlobalElement (jj_global)) { 00700 // jj_global is not an off-process entry. Look up its local 00701 // index in the column Map; we want to extract this column index 00702 // from the input matrix. If jj_global is _not_ in the column 00703 // Map on the calling process, that could mean that the column 00704 // in question is empty on this process. That would be bad for 00705 // solving linear systems with the extract submatrix. We could 00706 // solve the resulting singular linear systems in a minimum-norm 00707 // least-squares sense, but for now we simply raise an exception. 00708 const LO jj_local = globalColMap.getLocalElement (jj_global); 00709 if (jj_local == Teuchos::OrdinalTraits<local_ordinal_type>::invalid ()) { 00710 colIndsValid = false; 00711 invalidGlobalColInds.push_back (jj_global); 00712 break; 00713 } 00714 localCols[i] = jj_local; 00715 } 00716 } 00717 TEUCHOS_TEST_FOR_EXCEPTION( 00718 ! rowIndsValid, std::logic_error, "Ifpack2::TriDiContainer::extract: " 00719 "On process " << myRank << ", at least one row index in the set of local " 00720 "row indices given to the constructor is not a valid local row index in " 00721 "the input matrix's row Map on this process. This should be impossible " 00722 "because the constructor checks for this case. Here is the complete set " 00723 "of invalid local row indices: " << toString (invalidLocalRowInds) << ". " 00724 "Please report this bug to the Ifpack2 developers."); 00725 TEUCHOS_TEST_FOR_EXCEPTION( 00726 ! colIndsValid, std::runtime_error, "Ifpack2::TriDiContainer::extract: " 00727 "On process " << myRank << ", " 00728 "At least one row index in the set of row indices given to the constructor " 00729 "does not have a corresponding column index in the input matrix's column " 00730 "Map. This probably means that the column(s) in question is/are empty on " 00731 "this process, which would make the submatrix to extract structurally " 00732 "singular. Here is the compete set of invalid global column indices: " 00733 << toString (invalidGlobalColInds) << "."); 00734 00735 diagBlock_.putScalar (Teuchos::ScalarTraits<local_scalar_type>::zero ()); 00736 00737 const size_t maxNumEntriesInRow = globalMatrix->getNodeMaxNumRowEntries (); 00738 Array<scalar_type> val (maxNumEntriesInRow); 00739 Array<local_ordinal_type> ind (maxNumEntriesInRow); 00740 00741 const local_ordinal_type INVALID = 00742 Teuchos::OrdinalTraits<local_ordinal_type>::invalid (); 00743 for (size_t i = 0; i < numRows_; ++i) { 00744 const local_ordinal_type localRow = localRows[i]; 00745 size_t numEntries; 00746 globalMatrix->getLocalRowCopy (localRow, ind (), val (), numEntries); 00747 00748 for (size_t k = 0; k < numEntries; ++k) { 00749 const local_ordinal_type localCol = ind[k]; 00750 00751 // Skip off-process elements 00752 // 00753 // FIXME (mfh 24 Aug 2013) This assumes the following: 00754 // 00755 // 1. The column and row Maps begin with the same set of 00756 // on-process entries, in the same order. That is, 00757 // on-process row and column indices are the same. 00758 // 2. All off-process indices in the column Map of the input 00759 // matrix occur after that initial set. 00760 if (localCol >= 0 && static_cast<size_t> (localCol) < inputMatrixNumRows) { 00761 // for local column IDs, look for each ID in the list 00762 // of columns hosted by this object 00763 local_ordinal_type jj = INVALID; 00764 for (size_t kk = 0; kk < numRows_; ++kk) { 00765 if (localRows[kk] == localCol) { 00766 jj = kk; 00767 } 00768 } 00769 if (jj != INVALID) { 00770 diagBlock_ (i, jj) += val[k]; // ??? 00771 } 00772 } 00773 } 00774 } 00775 } 00776 00777 } // namespace Ifpack2 00778 00779 #define IFPACK2_TRIDICONTAINER_INSTANT(S,LO,GO,N) \ 00780 template class Ifpack2::TriDiContainer< Tpetra::CrsMatrix<S, LO, GO, N>, S >; 00781 00782 #endif // IFPACK2_SPARSECONTAINER_HPP
1.7.6.1