diff --git a/packages/tpetra/core/src/Tpetra_Details_Transfer_decl.hpp b/packages/tpetra/core/src/Tpetra_Details_Transfer_decl.hpp index 085e3600cabe..c936c8f66ada 100644 --- a/packages/tpetra/core/src/Tpetra_Details_Transfer_decl.hpp +++ b/packages/tpetra/core/src/Tpetra_Details_Transfer_decl.hpp @@ -201,6 +201,13 @@ class Transfer : public Teuchos::Describable { /// Maps in the way that you expect. bool isLocallyComplete () const; + + void detectRemoteExportLIDsContiguous() const; + + bool areRemoteLIDsContiguous() const; + + bool areExportLIDsContiguous() const; + /// \brief Describe this object in a human-readable way to the given /// output stream. /// diff --git a/packages/tpetra/core/src/Tpetra_Details_Transfer_def.hpp b/packages/tpetra/core/src/Tpetra_Details_Transfer_def.hpp index e18358d5e20a..cfd5bfa3bbb9 100644 --- a/packages/tpetra/core/src/Tpetra_Details_Transfer_def.hpp +++ b/packages/tpetra/core/src/Tpetra_Details_Transfer_def.hpp @@ -63,6 +63,35 @@ namespace { // (anonymous) return Teuchos::ArrayView (size == 0 ? nullptr : hostView.data (), size); } + template + struct OrderedViewFunctor { + OrderedViewFunctor (const Kokkos::View& viewToCheck) : + viewToCheck_ (viewToCheck) {} + KOKKOS_INLINE_FUNCTION void operator() (const size_t i, unsigned int& isUnordered) const { + isUnordered |= static_cast(viewToCheck_(i)+1 != viewToCheck_(i+1)); + } + Kokkos::View viewToCheck_; + }; + + template + bool + isViewOrdered (const Kokkos::View& viewToCheck) + { + using Kokkos::parallel_reduce; + typedef DeviceType DT; + typedef typename DT::execution_space DES; + typedef Kokkos::RangePolicy range_type; + + const size_t size = viewToCheck.extent (0); + unsigned int isUnordered = 0; + if (size>1) + parallel_reduce ("isViewOrdered", + range_type (0, size-1), + OrderedViewFunctor (viewToCheck), + isUnordered); + return isUnordered == 0; + } + } // namespace (anonymous) namespace Tpetra { @@ -270,6 +299,58 @@ isLocallyComplete () const { return TransferData_->isLocallyComplete_; } +template +void +Transfer:: +detectRemoteExportLIDsContiguous () const { + + // Check that maps are locally fitted + // TODO: We really want to check here that remote LIDs are sorted last. + // The current check is too restrictive in special cases. + bool ordered = (getNumSameIDs() == std::min(getSourceMap()->getNodeNumElements(), + getTargetMap()->getNodeNumElements())); + ordered &= (getTargetMap()->getNodeNumElements() == getNumSameIDs() + getNumRemoteIDs()); + if (ordered) { + const auto& dv = TransferData_->remoteLIDs_; + TEUCHOS_TEST_FOR_EXCEPTION + (dv.need_sync_device (), std::logic_error, + "Tpetra::Details::Transfer::getRemoteLIDs_dv: " + "DualView needs sync to device" ); + auto v_d = dv.view_device (); + ordered &= isViewOrdered(v_d); + } + TransferData_->remoteLIDsContiguous_ = ordered; + + ordered = (getNumSameIDs() == std::min(getSourceMap()->getNodeNumElements(), + getTargetMap()->getNodeNumElements())); + ordered &= (getSourceMap()->getNodeNumElements() == getNumSameIDs() + getNumExportIDs()); + if (ordered) { + const auto& dv = TransferData_->exportLIDs_; + TEUCHOS_TEST_FOR_EXCEPTION + (dv.need_sync_device (), std::logic_error, + "Tpetra::Details::Transfer::getRemoteLIDs_dv: " + "DualView needs sync to device" ); + auto v_d = dv.view_device (); + ordered &= isViewOrdered(v_d); + } + TransferData_->exportLIDsContiguous_ = ordered; +} + +template +bool +Transfer:: +areRemoteLIDsContiguous () const { + return TransferData_->remoteLIDsContiguous_; +} + +template +bool +Transfer:: +areExportLIDsContiguous () const { + return TransferData_->exportLIDsContiguous_; +} + + template void Transfer:: diff --git a/packages/tpetra/core/src/Tpetra_DistObject_decl.hpp b/packages/tpetra/core/src/Tpetra_DistObject_decl.hpp index 5bd91b8dbd00..175686c822bc 100644 --- a/packages/tpetra/core/src/Tpetra_DistObject_decl.hpp +++ b/packages/tpetra/core/src/Tpetra_DistObject_decl.hpp @@ -787,7 +787,9 @@ namespace Tpetra { size_t constantNumPackets, bool commOnHost, ReverseOption revOp, - std::shared_ptr prefix); + std::shared_ptr prefix, + const bool canTryAliasing, + const CombineMode CM); void doWaits(Distributor& distor, ReverseOption revOp); @@ -997,10 +999,12 @@ namespace Tpetra { /// exports_ always gets passed into packAndPrepare() /// by nonconst reference. Thus, that method can resize the /// DualView without needing to call other DistObject methods. - bool + virtual bool reallocImportsIfNeeded (const size_t newSize, const bool verbose, - const std::string* prefix); + const std::string* prefix, + const bool remoteLIDsContiguous=false, + const CombineMode CM=INSERT); /// \brief Number of packets to receive for each receive operation. /// diff --git a/packages/tpetra/core/src/Tpetra_DistObject_def.hpp b/packages/tpetra/core/src/Tpetra_DistObject_def.hpp index fca77903864f..37f568176335 100644 --- a/packages/tpetra/core/src/Tpetra_DistObject_def.hpp +++ b/packages/tpetra/core/src/Tpetra_DistObject_def.hpp @@ -679,7 +679,9 @@ namespace Tpetra { DistObject:: reallocImportsIfNeeded (const size_t newSize, const bool verbose, - const std::string* prefix) + const std::string* prefix, + const bool /*remoteLIDsContiguous*/, + const CombineMode /*CM*/) { if (verbose) { std::ostringstream os; @@ -917,6 +919,10 @@ namespace Tpetra { const_lo_dv_type exportLIDs = (revOp == DoForward) ? transfer.getExportLIDs_dv () : transfer.getRemoteLIDs_dv (); + const bool canTryAliasing = (revOp == DoForward) ? + transfer.areRemoteLIDsContiguous() : + transfer.areExportLIDsContiguous(); + // const bool canTryAliasing = false; ProfilingRegion region_dTN(funcName); #ifdef HAVE_TPETRA_TRANSFER_TIMERS @@ -1068,7 +1074,7 @@ namespace Tpetra { // elements) how many incoming elements we expect, so we can // resize the buffer accordingly. const size_t rbufLen = remoteLIDs.extent (0) * constantNumPackets; - reallocImportsIfNeeded (rbufLen, verbose, prefix.get ()); + reallocImportsIfNeeded (rbufLen, verbose, prefix.get (), canTryAliasing, CM); } // Do we need to do communication (via doPostsAndWaits)? @@ -1116,7 +1122,7 @@ namespace Tpetra { std::cerr << os.str (); } - doPosts(distor, constantNumPackets, commOnHost, revOp, prefix); + doPosts(distor, constantNumPackets, commOnHost, revOp, prefix, canTryAliasing, CM); } // if (needCommunication) } // if (CM != ZERO) } @@ -1267,6 +1273,9 @@ namespace Tpetra { const_lo_dv_type exportLIDs = (revOp == DoForward) ? transfer.getExportLIDs_dv () : transfer.getRemoteLIDs_dv (); + const bool canTryAliasing = (revOp == DoForward) ? + transfer.areRemoteLIDsContiguous() : + transfer.areExportLIDsContiguous(); size_t constantNumPackets = this->constantNumberOfPackets (); @@ -1278,7 +1287,7 @@ namespace Tpetra { // elements) how many incoming elements we expect, so we can // resize the buffer accordingly. const size_t rbufLen = remoteLIDs.extent (0) * constantNumPackets; - reallocImportsIfNeeded (rbufLen, verbose, prefix.get ()); + reallocImportsIfNeeded (rbufLen, verbose, prefix.get (), canTryAliasing, CM); } // Do we need to do communication (via doPostsAndWaits)? @@ -1341,7 +1350,9 @@ namespace Tpetra { size_t constantNumPackets, bool commOnHost, ReverseOption revOp, - std::shared_ptr prefix) + std::shared_ptr prefix, + const bool canTryAliasing, + const CombineMode CM) { using ::Tpetra::Details::dualViewStatusToString; using ::Tpetra::Details::getArrayViewFromDualView; @@ -1432,7 +1443,7 @@ namespace Tpetra { std::cerr << os.str (); } this->reallocImportsIfNeeded (totalImportPackets, verbose, - prefix.get ()); + prefix.get (), canTryAliasing, CM); if (verbose) { std::ostringstream os; os << *prefix << "7.3. Second comm" << std::endl; diff --git a/packages/tpetra/core/src/Tpetra_Export_def.hpp b/packages/tpetra/core/src/Tpetra_Export_def.hpp index 26afa20588c5..558094b85f1a 100644 --- a/packages/tpetra/core/src/Tpetra_Export_def.hpp +++ b/packages/tpetra/core/src/Tpetra_Export_def.hpp @@ -92,6 +92,8 @@ namespace Tpetra { TEUCHOS_ASSERT( ! this->TransferData_->exportLIDs_.need_sync_device () ); TEUCHOS_ASSERT( ! this->TransferData_->exportLIDs_.need_sync_host () ); + this->detectRemoteExportLIDsContiguous(); + if (this->verbose ()) { std::ostringstream os; const int myRank = source->getComm ()->getRank (); diff --git a/packages/tpetra/core/src/Tpetra_ImportExportData_decl.hpp b/packages/tpetra/core/src/Tpetra_ImportExportData_decl.hpp index 50c51a4b6a73..b2f90e461c2b 100644 --- a/packages/tpetra/core/src/Tpetra_ImportExportData_decl.hpp +++ b/packages/tpetra/core/src/Tpetra_ImportExportData_decl.hpp @@ -185,6 +185,12 @@ namespace Tpetra { /// other processes. Kokkos::DualView remoteLIDs_; + //! Whether the remote LIDs are contiguous. + bool remoteLIDsContiguous_ = false; + + //! Whether the export LIDs are contiguous. + bool exportLIDsContiguous_ = false; + /// \brief "Outgoing" local indices. /// /// This array holds the LIDs of the GIDs that are owned by the diff --git a/packages/tpetra/core/src/Tpetra_Import_def.hpp b/packages/tpetra/core/src/Tpetra_Import_def.hpp index 156cc4c69cbf..29f3caab3ee3 100644 --- a/packages/tpetra/core/src/Tpetra_Import_def.hpp +++ b/packages/tpetra/core/src/Tpetra_Import_def.hpp @@ -174,6 +174,8 @@ namespace Tpetra { TEUCHOS_ASSERT( ! this->TransferData_->exportLIDs_.need_sync_device () ); TEUCHOS_ASSERT( ! this->TransferData_->exportLIDs_.need_sync_host () ); + this->detectRemoteExportLIDsContiguous(); + if (this->verbose ()) { std::ostringstream os; os << *verbPrefix << "Done!" << endl; @@ -409,6 +411,8 @@ namespace Tpetra { distributor.createFromSendsAndRecvs (this->TransferData_->exportPIDs_, tRemotePIDs); } + this->detectRemoteExportLIDsContiguous(); + TEUCHOS_ASSERT( ! this->TransferData_->permuteFromLIDs_.need_sync_device () ); TEUCHOS_ASSERT( ! this->TransferData_->permuteFromLIDs_.need_sync_host () ); TEUCHOS_ASSERT( ! this->TransferData_->permuteToLIDs_.need_sync_device () ); @@ -487,6 +491,8 @@ namespace Tpetra { this->TransferData_->exportPIDs_.swap (exportPIDs); this->TransferData_->distributor_.swap (distributor); + this->detectRemoteExportLIDsContiguous(); + TEUCHOS_ASSERT( ! this->TransferData_->permuteFromLIDs_.need_sync_device () ); TEUCHOS_ASSERT( ! this->TransferData_->permuteFromLIDs_.need_sync_host () ); TEUCHOS_ASSERT( ! this->TransferData_->permuteToLIDs_.need_sync_device () ); diff --git a/packages/tpetra/core/src/Tpetra_MultiVector_decl.hpp b/packages/tpetra/core/src/Tpetra_MultiVector_decl.hpp index c0c70fefe703..388453b9f85b 100644 --- a/packages/tpetra/core/src/Tpetra_MultiVector_decl.hpp +++ b/packages/tpetra/core/src/Tpetra_MultiVector_decl.hpp @@ -2574,6 +2574,46 @@ namespace Tpetra { const size_t constantNumPackets, Distributor& /* distor */, const CombineMode CM); + + private: + + // If comm buffers can be aliased to the data view, use this + // implementation. + template + typename std::enable_if::type, + typename NO::device_type::memory_space>::value, bool>::type + reallocImportsIfNeededImpl (const size_t newSize, + const bool verbose, + const std::string* prefix, + const bool areRemoteLIDsContiguous, + const CombineMode CM); + + // If comm buffers cannot be aliased to the data view, use this + // implementation. (Just calls DistObject::reallocImportsIfNeeded.) + template + typename std::enable_if::type, + typename NO::device_type::memory_space>::value, bool>::type + reallocImportsIfNeededImpl (const size_t newSize, + const bool verbose, + const std::string* prefix, + const bool areRemoteLIDsContiguous, + const CombineMode CM); + protected: + + virtual bool + reallocImportsIfNeeded (const size_t newSize, + const bool verbose, + const std::string* prefix, + const bool areRemoteLIDsContiguous=false, + const CombineMode CM=INSERT); + + + public: + bool importsAreAliased(); + + protected: + Kokkos::DualView unaliased_imports_; + //@} }; // class MultiVector diff --git a/packages/tpetra/core/src/Tpetra_MultiVector_def.hpp b/packages/tpetra/core/src/Tpetra_MultiVector_def.hpp index 268069247ec0..c220aaa4e79f 100644 --- a/packages/tpetra/core/src/Tpetra_MultiVector_def.hpp +++ b/packages/tpetra/core/src/Tpetra_MultiVector_def.hpp @@ -1617,6 +1617,120 @@ namespace Tpetra { os << *prefix << "Done!" << endl; std::cerr << os.str (); } + + } + + + template + template + typename std::enable_if::type, + typename NO::device_type::memory_space>::value, + bool>::type + MultiVector:: + reallocImportsIfNeededImpl (const size_t newSize, + const bool verbose, + const std::string* prefix, + const bool areRemoteLIDsContiguous, + const CombineMode CM) + { + // This implementation of reallocImportsIfNeeded is an + // optimization that is specific to MultiVector. We check if the + // imports_ view can be aliased to the end of the data view_. If + // that is the case, we can skip the unpackAndCombine call. + + if (verbose) { + std::ostringstream os; + os << *prefix << "Realloc (if needed) imports_ from " + << this->imports_.extent (0) << " to " << newSize << std::endl; + std::cerr << os.str (); + } + + bool reallocated = false; + + using IST = impl_scalar_type; + using DV = Kokkos::DualView; + + // Conditions for aliasing memory: + // - When both sides of the dual view are in the same memory + // space, we do not need to worry about syncing things. + // - When both memory spaces are different, we only alias if this + // does not incur additional sync'ing. + // - The remote LIDs need to be contiguous, so that we do not need + // to reorder received information. + // - CombineMode needs to be INSERT. + // - The number of vectors needs to be 1, otherwise we need to + // reorder the received data. + if ((dual_view_type::impl_dualview_is_single_device::value || + (Details::Behavior::assumeMpiIsCudaAware () && !this->need_sync_device()) || + (!Details::Behavior::assumeMpiIsCudaAware () && !this->need_sync_host())) && + areRemoteLIDsContiguous && + (CM == INSERT || CM == REPLACE) && + (getNumVectors() == 1) && + (newSize > 0)) { + + size_t offset = getLocalLength () - newSize; + reallocated = this->imports_.d_view.data() != view_.d_view.data() + offset; + if (reallocated) { + typedef std::pair range_type; + this->imports_ = DV(view_, + range_type (offset, getLocalLength () ), + 0); + + if (verbose) { + std::ostringstream os; + os << *prefix << "Aliased imports_ to MV.view_" << std::endl; + std::cerr << os.str (); + } + } + return reallocated; + } + { + using ::Tpetra::Details::reallocDualViewIfNeeded; + reallocated = + reallocDualViewIfNeeded (this->unaliased_imports_, newSize, "imports"); + if (verbose) { + std::ostringstream os; + os << *prefix << "Finished realloc'ing unaliased_imports_" << std::endl; + std::cerr << os.str (); + } + this->imports_ = this->unaliased_imports_; + } + return reallocated; + } + + template + template + typename std::enable_if::type, + typename NO::device_type::memory_space>::value, + bool>::type + MultiVector:: + reallocImportsIfNeededImpl (const size_t newSize, + const bool verbose, + const std::string* prefix, + const bool areRemoteLIDsContiguous, + const CombineMode CM) + { + return DistObject::reallocImportsIfNeeded(newSize, verbose, prefix, areRemoteLIDsContiguous, CM); + } + + template + bool + MultiVector:: + reallocImportsIfNeeded(const size_t newSize, + const bool verbose, + const std::string* prefix, + const bool areRemoteLIDsContiguous, + const CombineMode CM) { + /// return false; + return reallocImportsIfNeededImpl(newSize, verbose, prefix, areRemoteLIDsContiguous, CM); + } + + template + bool + MultiVector:: + importsAreAliased() { + return (this->imports_.d_view.data() + this->imports_.d_view.extent(0) == + view_.d_view.data() + view_.d_view.extent(0)); } @@ -1667,10 +1781,22 @@ namespace Tpetra { if (printDebugOutput) { std::ostringstream os; os << *prefix << "No imports. Done!" << endl; + std::cerr << os.str (); + } + return; + } + + // Check, whether imports_ is a subview of the MV view. + if (importsAreAliased()) { + if (printDebugOutput) { + std::ostringstream os; + os << *prefix << "Skipping unpack, since imports_ is aliased to MV.view_. Done!" << endl; + std::cerr << os.str (); } return; } + const size_t numVecs = getNumVectors (); if (debugCheckIndices) { TEUCHOS_TEST_FOR_EXCEPTION_CLASS_FUNC diff --git a/packages/tpetra/core/test/ImportExport2/ImportExport2_UnitTests.cpp b/packages/tpetra/core/test/ImportExport2/ImportExport2_UnitTests.cpp index 0451932d45d0..e405eaa9eece 100644 --- a/packages/tpetra/core/test/ImportExport2/ImportExport2_UnitTests.cpp +++ b/packages/tpetra/core/test/ImportExport2/ImportExport2_UnitTests.cpp @@ -49,6 +49,7 @@ #include #include "Tpetra_CrsGraph.hpp" #include "Tpetra_CrsMatrix.hpp" +#include "Tpetra_MultiVector.hpp" #include "Tpetra_Core.hpp" #include "Tpetra_Distributor.hpp" #include "Tpetra_Map.hpp" @@ -94,6 +95,7 @@ namespace { using Tpetra::createContigMap; using Tpetra::CrsGraph; using Tpetra::CrsMatrix; + using Tpetra::MultiVector; using Tpetra::Export; using Tpetra::Import; using Tpetra::INSERT; @@ -679,6 +681,170 @@ namespace { } } + TEUCHOS_UNIT_TEST_TEMPLATE_3_DECL( MultiVectorImport, doImport, LO, GO, Scalar ) + { + typedef Tpetra::global_size_t GST; + typedef Map map_type; + typedef MultiVector mv_type; + + out << "(MultiVectorImport,doImport) test" << endl; + OSTab tab1 (out); // Add one tab level + + const GST INVALID = Teuchos::OrdinalTraits::invalid (); + // get a comm + RCP > comm = getDefaultComm(); + const int numImages = comm->getSize(); + const int myImageID = comm->getRank(); + + if (numImages < 2) { + out << "This test is only meaningful if running with multiple MPI " + "processes, but you ran it with only 1 process." << endl; + return; + } + + for (int collectRank = 0; collectRank < 2; collectRank++) { + std::ostringstream err; + int lclErr = 0; + int gblErr = 0; + + OSTab tab2 (out); + const GO indexBase = 0; + const LO src_num_local_elements = 3; + const LO tgt_num_local_elements = (myImageID == collectRank) ? + static_cast (numImages*src_num_local_elements) : + static_cast (0); + + // Create maps for the source and target + RCP src_map = + rcp (new map_type (INVALID, + static_cast (src_num_local_elements), + indexBase, comm)); + RCP tgt_map = + rcp (new map_type (INVALID, + static_cast (tgt_num_local_elements), + indexBase, comm)); + + // Create CrsMatrix objects. + RCP src_mv = rcp(new mv_type(src_map, 1)); + RCP tgt_mv = rcp(new mv_type(tgt_map, 1)); + src_mv->putScalar(Teuchos::ScalarTraits::one ()); + tgt_mv->putScalar(Teuchos::ScalarTraits::zero ()); + + try { + // Create the importer + Import importer (src_map, tgt_map, getImportParameterList ()); + + // Do the import + tgt_mv->doImport (*src_mv, importer, INSERT); + + // When collectRank == 0, rank 0 has the GIDs the source map owns ordered first. + // When collectRank == 1, rank 1 has the GIDs of rank 0 ordered first. + // On all other ranks, there are no remote LIDs. + if ((collectRank == 0) || (myImageID != collectRank)) { + TEUCHOS_ASSERT(importer.areRemoteLIDsContiguous()); + } + else { + TEUCHOS_ASSERT(!importer.areRemoteLIDsContiguous()); + } + + // When there are remote LIDs and they are contiguous, and + // MV::imports_ and MV::view_ have the same memory space, the + // imports_ view is aliased to the data view of the target MV. + if ((myImageID == collectRank) && (myImageID == 0)) { + if (mv_type::dual_view_type::impl_dualview_is_single_device::value) + TEUCHOS_ASSERT(tgt_mv->importsAreAliased()); + // else { + // We do not know if copyAndPermute was run on host or device. + // } + } + else { + TEUCHOS_ASSERT(!tgt_mv->importsAreAliased()); + } + + // Loop through tgt_mv and make sure the import worked. + if (tgt_num_local_elements != 0) { + auto data = tgt_mv->getLocalViewHost(Tpetra::Access::ReadOnly); + for (GO gblRow = tgt_map->getMinGlobalIndex (); + gblRow <= tgt_map->getMaxGlobalIndex (); + ++gblRow) { + const LO lclRow = tgt_map->getLocalElement (gblRow); + + TEST_EQUALITY(data(lclRow,0), Teuchos::ScalarTraits::one ()); + } + } + } + catch (std::exception& e) { // end of the first test + err << "Proc " << myImageID << ": " << e.what () << endl; + lclErr = 1; + } + + reduceAll (*comm, REDUCE_MAX, lclErr, outArg (gblErr)); + TEST_EQUALITY_CONST( gblErr, 0 ); + if (gblErr != 0) { + Tpetra::Details::gathervPrint (out, err.str (), *comm); + out << "Above test failed; aborting further tests" << endl; + return; + } + + try{ + // Create the exporter + Export exporter (tgt_map, src_map, getImportParameterList ()); + + // Do the import using reverse mode + tgt_mv->doImport (*src_mv, exporter, INSERT); + + // When collectRank == 0, rank 0 has the GIDs the source map owns ordered first. + // When collectRank == 1, rank 1 has the GIDs of rank 0 ordered first. + // On all other ranks, there are no remote LIDs. + if ((collectRank == 0) || (myImageID != collectRank)) { + TEUCHOS_ASSERT(exporter.areExportLIDsContiguous()); + } + else { + TEUCHOS_ASSERT(!exporter.areExportLIDsContiguous()); + } + + // When there are export LIDs and they are contiguous, and + // MV::imports_ and MV::view_ have the same memory space, the + // imports_ view is aliased to the data view of the target MV. + if ((myImageID == collectRank) && (myImageID == 0)) { + if (mv_type::dual_view_type::impl_dualview_is_single_device::value) + TEUCHOS_ASSERT(tgt_mv->importsAreAliased()); + // else { + // We do not know if copyAndPermute was run on host or device. + // } + } + else { + TEUCHOS_ASSERT(!tgt_mv->importsAreAliased()); + } + + // Loop through tgt_mv and make sure the import worked. + if (tgt_num_local_elements != 0) { + auto data = tgt_mv->getLocalViewHost(Tpetra::Access::ReadOnly); + for (GO gblRow = tgt_map->getMinGlobalIndex (); + gblRow <= tgt_map->getMaxGlobalIndex (); + ++gblRow) { + const LO lclRow = tgt_map->getLocalElement (gblRow); + + TEST_EQUALITY(data(lclRow,0), Teuchos::ScalarTraits::one ()); + } + } + + } + catch (std::exception& e) { // end of the first test + err << "Proc " << myImageID << ": " << e.what () << endl; + lclErr = 1; + } + + reduceAll (*comm, REDUCE_MAX, lclErr, outArg (gblErr)); + TEST_EQUALITY_CONST( gblErr, 0 ); + if (gblErr != 0) { + Tpetra::Details::gathervPrint (out, err.str (), *comm); + out << "Above test failed; aborting further tests" << endl; + return; + } + } + } + template bool graphs_are_same(const RCP& G1, const RCP& G2) @@ -2847,6 +3013,7 @@ TEUCHOS_UNIT_TEST_TEMPLATE_2_DECL( Import_Util,GetTwoTransferOwnershipVector, LO #define UNIT_TEST_GROUP_SC_LO_GO( SC, LO, GO ) \ TEUCHOS_UNIT_TEST_TEMPLATE_3_INSTANT( CrsMatrixImportExport, doImport, LO, GO, SC ) \ + TEUCHOS_UNIT_TEST_TEMPLATE_3_INSTANT( MultiVectorImport, doImport, LO, GO, SC ) \ TEUCHOS_UNIT_TEST_TEMPLATE_3_INSTANT( FusedImportExport, doImport, LO, GO, SC ) \ TEUCHOS_UNIT_TEST_TEMPLATE_3_INSTANT( Import_Util, UnpackAndCombineWithOwningPIDs, LO, GO, SC ) \ TEUCHOS_UNIT_TEST_TEMPLATE_3_INSTANT( FusedImportExport, MueLuStyle, LO, GO, SC )