Tpetra parallel linear algebra Version of the Day
Loading...
Searching...
No Matches
Tpetra_Details_CooMatrix.hpp
Go to the documentation of this file.
1// @HEADER
2// *****************************************************************************
3// Tpetra: Templated Linear Algebra Services Package
4//
5// Copyright 2008 NTESS and the Tpetra contributors.
6// SPDX-License-Identifier: BSD-3-Clause
7// *****************************************************************************
8// @HEADER
9
10#ifndef TPETRA_DETAILS_COOMATRIX_HPP
11#define TPETRA_DETAILS_COOMATRIX_HPP
12
17
18#include "TpetraCore_config.h"
19#include "Tpetra_Details_PackTriples.hpp"
20#include "Tpetra_DistObject.hpp"
23#include "Teuchos_TypeNameTraits.hpp"
24
25#include <initializer_list>
26#include <map>
27#include <memory>
28#include <string>
29#include <type_traits>
30#include <vector>
31
32
33namespace Tpetra {
34namespace Details {
35
36// Implementation details of Tpetra::Details.
37// So, users REALLY should not use anything in here.
38namespace Impl {
39
42template<class IndexType>
44 IndexType row;
45 IndexType col;
46};
47
52template<class IndexType>
54 bool
55 operator () (const CooGraphEntry<IndexType>& x,
56 const CooGraphEntry<IndexType>& y) const
57 {
58 return (x.row < y.row) || (x.row == y.row && x.col < y.col);
59 }
60};
61
64template<class SC, class GO>
66private:
74 typedef std::map<CooGraphEntry<GO>, SC,
75 CompareCooGraphEntries<GO> > entries_type;
76
79 entries_type entries_;
80
81public:
83 typedef char packet_type;
84
86 CooMatrixImpl () = default;
87
94 void
95 sumIntoGlobalValue (const GO gblRowInd,
96 const GO gblColInd,
97 const SC& val)
98 {
99 // There's no sense in worrying about the insertion hint, since
100 // indices may be all over the place. Users make no promises of
101 // sorting or locality of input.
102 CooGraphEntry<GO> ij {gblRowInd, gblColInd};
103 auto result = this->entries_.insert ({ij, val});
104 if (! result.second) { // already in the map
105 result.first->second += val; // sum in the new value
106 }
107 }
108
117 void
118 sumIntoGlobalValues (const GO gblRowInds[],
119 const GO gblColInds[],
120 const SC vals[],
121 const std::size_t numEnt)
122 {
123 for (std::size_t k = 0; k < numEnt; ++k) {
124 // There's no sense in worrying about the insertion hint, since
125 // indices may be all over the place. Users make no promises of
126 // sorting or locality of input.
127 CooGraphEntry<GO> ij {gblRowInds[k], gblColInds[k]};
128 const SC val = vals[k];
129 auto result = this->entries_.insert ({ij, val});
130 if (! result.second) { // already in the map
131 result.first->second += val; // sum in the new value
132 }
133 }
134 }
135
137 std::size_t
139 {
140 return this->entries_.size ();
141 }
142
146 void
147 forAllEntries (std::function<void (const GO, const GO, const SC&)> f) const
148 {
149 for (auto iter = this->entries_.begin ();
150 iter != this->entries_.end (); ++iter) {
151 f (iter->first.row, iter->first.col, iter->second);
152 }
153 }
154
160 void
161 mergeIntoRow (const GO tgtGblRow,
162 const CooMatrixImpl<SC, GO>& src,
163 const GO srcGblRow)
164 {
165 // const char prefix[] =
166 // "Tpetra::Details::Impl::CooMatrixImpl::mergeIntoRow: ";
167
168 entries_type& tgtEntries = this->entries_;
169 const entries_type& srcEntries = src.entries_;
170
171 // Find all entries with the given global row index. The min GO
172 // value is guaranteed to be the least possible column index, so
173 // beg1 is a lower bound for all (row index, column index) pairs.
174 // Lower bound is inclusive; upper bound is exclusive.
175 auto srcBeg = srcEntries.lower_bound ({srcGblRow, std::numeric_limits<GO>::min ()});
176 auto srcEnd = srcEntries.upper_bound ({srcGblRow+1, std::numeric_limits<GO>::min ()});
177 auto tgtBeg = tgtEntries.lower_bound ({tgtGblRow, std::numeric_limits<GO>::min ()});
178 auto tgtEnd = tgtEntries.upper_bound ({tgtGblRow+1, std::numeric_limits<GO>::min ()});
179
180 // Don't waste time iterating over the current row of *this, if
181 // the current row of src is empty.
182 if (srcBeg != srcEnd) {
183 for (auto tgtCur = tgtBeg; tgtCur != tgtEnd; ++tgtCur) {
184 auto srcCur = srcBeg;
185 while (srcCur != srcEnd && srcCur->first.col < tgtCur->first.col) {
186 ++srcCur;
187 }
188 // At this point, one of the following is true:
189 //
190 // 1. srcCur == srcEnd, thus nothing more to insert.
191 // 2. srcCur->first.col > tgtCur->first.col, thus the current
192 // row of src has no entry matching tgtCur->first, so we
193 // have to insert it. Insertion does not invalidate
194 // tgtEntries iterators, and neither srcEntries nor
195 // tgtEntries have duplicates, so this is safe.
196 // 3. srcCur->first.col == tgtCur->first.col, so add the two entries.
197 if (srcCur != srcEnd) {
198 if (srcCur->first.col == tgtCur->first.col) {
199 tgtCur->second += srcCur->second;
200 }
201 else {
202 // tgtCur is (optimally) right before where we want to put
203 // the new entry, since srcCur points to the first entry
204 // in srcEntries whose column index is greater than that
205 // of the entry to which tgtCur points.
206 (void) tgtEntries.insert (tgtCur, *srcCur);
207 }
208 } // if srcCur != srcEnd
209 } // for each entry in the current row (lclRow) of *this
210 } // if srcBeg != srcEnd
211 }
212
222 int
223 countPackRow (int& numPackets,
224 const GO gblRow,
225 const ::Teuchos::Comm<int>& comm,
226 std::ostream* errStrm = NULL) const
227 {
230 using std::endl;
231 const char prefix[] = "Tpetra::Details::Impl::CooMatrixImpl::countPackRow: ";
232#ifdef HAVE_TPETRACORE_MPI
233 int errCode = MPI_SUCCESS;
234#else
235 int errCode = 0;
236#endif // HAVE_TPETRACORE_MPI
237
238 // Count the number of entries in the given row.
239 const GO minGO = std::numeric_limits<GO>::min ();
240 auto beg = this->entries_.lower_bound ({gblRow, minGO});
241 auto end = this->entries_.upper_bound ({gblRow+1, minGO});
242 int numEnt = 0;
243 for (auto iter = beg; iter != end; ++iter) {
244 ++numEnt;
245 if (numEnt == std::numeric_limits<int>::max ()) {
246 if (errStrm != NULL) {
247 *errStrm << prefix << "In (global) row " << gblRow << ", the number "
248 "of entries thus far, numEnt = " << numEnt << ", has reached the "
249 "maximum int value. We need to store this count as int for MPI's "
250 "sake." << endl;
251 }
252 return -1;
253 }
254 }
255
256 int numPacketsForCount = 0; // output argument of countPackTriplesCount
257 {
258 errCode =
259 countPackTriplesCount (comm, numPacketsForCount, errStrm);
260 if (errCode != 0) {
261 if (errStrm != NULL) {
262 *errStrm << prefix << "countPackTriplesCount "
263 "returned errCode = " << errCode << " != 0." << endl;
264 }
265 return errCode;
266 }
267 if (numPacketsForCount < 0) {
268 if (errStrm != NULL) {
269 *errStrm << prefix << "countPackTriplesCount returned "
270 "numPacketsForCount = " << numPacketsForCount << " < 0." << endl;
271 }
272 return -1;
273 }
274 }
275
276 int numPacketsForTriples = 0; // output argument of countPackTriples
277 {
278 errCode = countPackTriples<SC, GO> (numEnt, comm, numPacketsForTriples);
279 TEUCHOS_TEST_FOR_EXCEPTION
280 (errCode != 0, std::runtime_error, prefix << "countPackTriples "
281 "returned errCode = " << errCode << " != 0.");
282 TEUCHOS_TEST_FOR_EXCEPTION
283 (numPacketsForTriples < 0, std::logic_error, prefix << "countPackTriples "
284 "returned numPacketsForTriples = " << numPacketsForTriples << " < 0.");
285 }
286
287 numPackets = numPacketsForCount + numPacketsForTriples;
288 return errCode;
289 }
290
305 void
307 const int outBufSize,
308 int& outBufCurPos, // in/out argument
309 const ::Teuchos::Comm<int>& comm,
310 std::vector<GO>& gblRowInds,
311 std::vector<GO>& gblColInds,
312 std::vector<SC>& vals,
313 const GO gblRow) const
314 {
317 const char prefix[] = "Tpetra::Details::Impl::CooMatrixImpl::packRow: ";
318
319 const GO minGO = std::numeric_limits<GO>::min ();
320 auto beg = this->entries_.lower_bound ({gblRow, minGO});
321 auto end = this->entries_.upper_bound ({gblRow+1, minGO});
322
323 // This doesn't actually deallocate. Only swapping with an empty
324 // std::vector does that.
325 gblRowInds.resize (0);
326 gblColInds.resize (0);
327 vals.resize (0);
328
329 int numEnt = 0;
330 for (auto iter = beg; iter != end; ++iter) {
331 gblRowInds.push_back (iter->first.row);
332 gblColInds.push_back (iter->first.col);
333 vals.push_back (iter->second);
334 ++numEnt;
335 TEUCHOS_TEST_FOR_EXCEPTION
336 (numEnt == std::numeric_limits<int>::max (), std::runtime_error, prefix
337 << "In (global) row " << gblRow << ", the number of entries thus far, "
338 "numEnt = " << numEnt << ", has reached the maximum int value. "
339 "We need to store this count as int for MPI's sake.");
340 }
341
342 {
343 const int errCode =
344 packTriplesCount (numEnt, outBuf, outBufSize, outBufCurPos, comm);
345 TEUCHOS_TEST_FOR_EXCEPTION
346 (errCode != 0, std::runtime_error, prefix
347 << "In (global) row " << gblRow << ", packTriplesCount returned "
348 "errCode = " << errCode << " != 0.");
349 }
350 {
351 const int errCode =
352 packTriples (gblRowInds.data (),
353 gblColInds.data (),
354 vals.data (),
355 numEnt,
356 outBuf,
357 outBufSize,
358 outBufCurPos, // in/out argument
359 comm);
360 TEUCHOS_TEST_FOR_EXCEPTION
361 (errCode != 0, std::runtime_error, prefix << "In (global) row "
362 << gblRow << ", packTriples returned errCode = " << errCode
363 << " != 0.");
364 }
365 }
366
374 GO
375 getMyGlobalRowIndices (std::vector<GO>& rowInds) const
376 {
377 rowInds.clear ();
378
379 GO lclMinRowInd = std::numeric_limits<GO>::max (); // compute local min
380 for (typename entries_type::const_iterator iter = this->entries_.begin ();
381 iter != this->entries_.end (); ++iter) {
382 const GO rowInd = iter->first.row;
383 if (rowInd < lclMinRowInd) {
384 lclMinRowInd = rowInd;
385 }
386 if (rowInds.empty () || rowInds.back () != rowInd) {
387 rowInds.push_back (rowInd); // don't insert duplicates
388 }
389 }
390 return lclMinRowInd;
391 }
392
393 template<class OffsetType>
394 void
395 buildCrs (std::vector<OffsetType>& rowOffsets,
396 GO gblColInds[],
397 SC vals[]) const
398 {
399 static_assert (std::is_integral<OffsetType>::value,
400 "OffsetType must be a built-in integer type.");
401
402 // clear() doesn't generally free storage; it just resets the
403 // length. Thus, we reuse any existing storage here.
404 rowOffsets.clear ();
405
406 const std::size_t numEnt = this->getLclNumEntries ();
407 if (numEnt == 0) {
408 rowOffsets.push_back (0);
409 }
410 else {
411 typename entries_type::const_iterator iter = this->entries_.begin ();
412 GO prevGblRowInd = iter->first.row;
413
414 OffsetType k = 0;
415 for ( ; iter != this->entries_.end (); ++iter, ++k) {
416 const GO gblRowInd = iter->first.row;
417 if (k == 0 || gblRowInd != prevGblRowInd) {
418 // The row offsets array always has at least one entry. The
419 // first entry is always zero, and the last entry is always
420 // the number of matrix entries.
421 rowOffsets.push_back (k);
422 prevGblRowInd = gblRowInd;
423 }
424 gblColInds[k] = iter->first.col;
425
426 static_assert (std::is_same<typename std::decay<decltype (iter->second)>::type, SC>::value,
427 "The type of iter->second != SC.");
428 vals[k] = iter->second;
429 }
430 rowOffsets.push_back (static_cast<OffsetType> (numEnt));
431 }
432 }
433
446 template<class OffsetType, class LO>
447 void
448 buildLocallyIndexedCrs (std::vector<OffsetType>& rowOffsets,
449 LO lclColInds[],
450 SC vals[],
451 std::function<LO (const GO)> gblToLcl) const
452 {
453 static_assert (std::is_integral<OffsetType>::value,
454 "OffsetType must be a built-in integer type.");
455 static_assert (std::is_integral<LO>::value,
456 "LO must be a built-in integer type.");
457
458 // clear() doesn't generally free storage; it just resets the
459 // length. Thus, we reuse any existing storage here.
460 rowOffsets.clear ();
461
462 const std::size_t numEnt = this->getLclNumEntries ();
463 if (numEnt == 0) {
464 rowOffsets.push_back (0);
465 }
466 else {
467 typename entries_type::const_iterator iter = this->entries_.begin ();
468 GO prevGblRowInd = iter->first.row;
469
470 OffsetType k = 0;
471 for ( ; iter != this->entries_.end (); ++iter, ++k) {
472 const GO gblRowInd = iter->first.row;
473 if (k == 0 || gblRowInd != prevGblRowInd) {
474 // The row offsets array always has at least one entry. The
475 // first entry is always zero, and the last entry is always
476 // the number of matrix entries.
477 rowOffsets.push_back (k);
478 prevGblRowInd = gblRowInd;
479 }
480 lclColInds[k] = gblToLcl (iter->first.col);
481 vals[k] = iter->second;
482 }
483 rowOffsets.push_back (static_cast<OffsetType> (numEnt));
484 }
485 }
486};
487
488} // namespace Impl
489
538template<class SC,
542class CooMatrix : public ::Tpetra::DistObject<char, LO, GO, NT> {
543public:
545 typedef char packet_type;
547 typedef SC scalar_type;
548 typedef LO local_ordinal_type;
549 typedef GO global_ordinal_type;
550 typedef NT node_type;
551 typedef typename NT::device_type device_type;
553 typedef ::Tpetra::Map<local_ordinal_type, global_ordinal_type, node_type> map_type;
554
555private:
557 typedef ::Tpetra::DistObject<packet_type, local_ordinal_type,
558 global_ordinal_type, node_type> base_type;
559
562
563public:
570 base_type (::Teuchos::null),
571 localError_ (new bool (false)),
572 errs_ (new std::shared_ptr<std::ostringstream> ()) // ptr to a null ptr
573 {}
574
582 CooMatrix (const ::Teuchos::RCP<const map_type>& map) :
583 base_type (map),
584 localError_ (new bool (false)),
585 errs_ (new std::shared_ptr<std::ostringstream> ()) // ptr to a null ptr
586 {}
587
589 virtual ~CooMatrix () {}
590
597 void
598 sumIntoGlobalValue (const GO gblRowInd,
599 const GO gblColInd,
600 const SC& val)
601 {
602 this->impl_.sumIntoGlobalValue (gblRowInd, gblColInd, val);
603 }
604
613 void
614 sumIntoGlobalValues (const GO gblRowInds[],
615 const GO gblColInds[],
616 const SC vals[],
617 const std::size_t numEnt)
618 {
619 this->impl_.sumIntoGlobalValues (gblRowInds, gblColInds, vals, numEnt);
620 }
621
623 void
624 sumIntoGlobalValues (std::initializer_list<GO> gblRowInds,
625 std::initializer_list<GO> gblColInds,
626 std::initializer_list<SC> vals,
627 const std::size_t numEnt)
628 {
629 this->impl_.sumIntoGlobalValues (gblRowInds.begin (), gblColInds.begin (),
630 vals.begin (), numEnt);
631 }
632
634 std::size_t
636 {
637 return this->impl_.getLclNumEntries ();
638 }
639
640 template<class OffsetType>
641 void
642 buildCrs (::Kokkos::View<OffsetType*, device_type>& rowOffsets,
643 ::Kokkos::View<GO*, device_type>& gblColInds,
644 ::Kokkos::View<typename ::Kokkos::ArithTraits<SC>::val_type*, device_type>& vals)
645 {
646 static_assert (std::is_integral<OffsetType>::value,
647 "OffsetType must be a built-in integer type.");
648 using ::Kokkos::create_mirror_view;
649 using ::Kokkos::deep_copy;
650 using ::Kokkos::View;
651 typedef typename ::Kokkos::ArithTraits<SC>::val_type ISC;
652
653 const std::size_t numEnt = this->getLclNumEntries ();
654
655 gblColInds = View<GO*, device_type> ("gblColInds", numEnt);
656 vals = View<ISC*, device_type> ("vals", numEnt);
657 auto gblColInds_h = create_mirror_view (gblColInds);
658 auto vals_h = create_mirror_view (vals);
659
660 std::vector<std::size_t> rowOffsetsSV;
661 this->impl_.buildCrs (rowOffsetsSV,
662 gblColInds_h.data (),
663 vals_h.data ());
664 rowOffsets =
665 View<OffsetType*, device_type> ("rowOffsets", rowOffsetsSV.size ());
666 typename View<OffsetType*, device_type>::HostMirror
667 rowOffsets_h (rowOffsetsSV.data (), rowOffsetsSV.size ());
668 deep_copy (rowOffsets, rowOffsets_h);
669
670 deep_copy (gblColInds, gblColInds_h);
671 deep_copy (vals, vals_h);
672 }
673
684 void
685 fillComplete (const ::Teuchos::RCP<const ::Teuchos::Comm<int> >& comm)
686 {
687 if (comm.is_null ()) {
688 this->map_ = ::Teuchos::null;
689 }
690 else {
691 this->map_ = this->buildMap (comm);
692 }
693 }
694
703 void
705 {
706 TEUCHOS_TEST_FOR_EXCEPTION
707 (this->getMap ().is_null (), std::runtime_error, "Tpetra::Details::"
708 "CooMatrix::fillComplete: This object does not yet have a Map. "
709 "You must call the version of fillComplete "
710 "that takes an input communicator.");
711 this->fillComplete (this->getMap ()->getComm ());
712 }
713
728 bool localError () const {
729 return *localError_;
730 }
731
749 std::string errorMessages () const {
750 return ((*errs_).get () == NULL) ? std::string ("") : (*errs_)->str ();
751 }
752
753private:
766 std::shared_ptr<bool> localError_;
767
775 std::shared_ptr<std::shared_ptr<std::ostringstream> > errs_;
776
778 std::ostream&
779 markLocalErrorAndGetStream ()
780 {
781 * (this->localError_) = true;
782 if ((*errs_).get () == NULL) {
783 *errs_ = std::shared_ptr<std::ostringstream> (new std::ostringstream ());
784 }
785 return **errs_;
786 }
787
788public:
791 virtual std::string description () const {
792 using Teuchos::TypeNameTraits;
793
794 std::ostringstream os;
795 os << "\"Tpetra::Details::CooMatrix\": {"
796 << "SC: " << TypeNameTraits<SC>::name ()
797 << ", LO: " << TypeNameTraits<LO>::name ()
798 << ", GO: " << TypeNameTraits<GO>::name ()
799 << ", NT: " << TypeNameTraits<NT>::name ();
800 if (this->getObjectLabel () != "") {
801 os << ", Label: \"" << this->getObjectLabel () << "\"";
802 }
803 os << ", Has Map: " << (this->map_.is_null () ? "false" : "true")
804 << "}";
805 return os.str ();
806 }
807
810 virtual void
811 describe (Teuchos::FancyOStream& out,
812 const Teuchos::EVerbosityLevel verbLevel =
813 Teuchos::Describable::verbLevel_default) const
814 {
816 using ::Teuchos::EVerbosityLevel;
817 using ::Teuchos::OSTab;
818 using ::Teuchos::TypeNameTraits;
819 using ::Teuchos::VERB_DEFAULT;
820 using ::Teuchos::VERB_LOW;
821 using ::Teuchos::VERB_MEDIUM;
822 using std::endl;
823
824 const auto vl = (verbLevel == VERB_DEFAULT) ? VERB_LOW : verbLevel;
825
826 auto comm = this->getMap ().is_null () ?
827 Teuchos::null : this->getMap ()->getComm ();
828 const int myRank = comm.is_null () ? 0 : comm->getRank ();
829 // const int numProcs = comm.is_null () ? 1 : comm->getSize ();
830
831 if (vl != Teuchos::VERB_NONE) {
832 // Convention is to start describe() implementations with a tab.
833 OSTab tab0 (out);
834 if (myRank == 0) {
835 out << "\"Tpetra::Details::CooMatrix\":" << endl;
836 }
837 OSTab tab1 (out);
838 if (myRank == 0) {
839 out << "Template parameters:" << endl;
840 {
841 OSTab tab2 (out);
842 out << "SC: " << TypeNameTraits<SC>::name () << endl
843 << "LO: " << TypeNameTraits<LO>::name () << endl
844 << "GO: " << TypeNameTraits<GO>::name () << endl
845 << "NT: " << TypeNameTraits<NT>::name () << endl;
846 }
847 if (this->getObjectLabel () != "") {
848 out << "Label: \"" << this->getObjectLabel () << "\"" << endl;
849 }
850 out << "Has Map: " << (this->map_.is_null () ? "false" : "true") << endl;
851 } // if myRank == 0
852
853 // Describe the Map, if it exists.
854 if (! this->map_.is_null ()) {
855 if (myRank == 0) {
856 out << "Map:" << endl;
857 }
858 OSTab tab2 (out);
859 this->map_->describe (out, vl);
860 }
861
862 // At verbosity > VERB_LOW, each process prints something.
863 if (vl > VERB_LOW) {
864 std::ostringstream os;
865 os << "Process " << myRank << ":" << endl;
866 //OSTab tab3 (os);
867
868 const std::size_t numEnt = this->impl_.getLclNumEntries ();
869 os << " Local number of entries: " << numEnt << endl;
870 if (vl > VERB_MEDIUM) {
871 os << " Local entries:" << endl;
872 //OSTab tab4 (os);
873 this->impl_.forAllEntries ([&os] (const GO row, const GO col, const SC& val) {
874 os << " {"
875 << "row: " << row
876 << ", col: " << col
877 << ", val: " << val
878 << "}" << endl;
879 });
880 }
881 gathervPrint (out, os.str (), *comm);
882 }
883 } // vl != Teuchos::VERB_NONE
884 }
885
886private:
895 Teuchos::RCP<const map_type>
896 buildMap (const ::Teuchos::RCP<const ::Teuchos::Comm<int> >& comm)
897 {
898 using ::Teuchos::outArg;
899 using ::Teuchos::rcp;
900 using ::Teuchos::REDUCE_MIN;
901 using ::Teuchos::reduceAll;
902 typedef ::Tpetra::global_size_t GST;
903 //const char prefix[] = "Tpetra::Details::CooMatrix::buildMap: ";
904
905 // Processes where comm is null don't participate in the Map.
906 if (comm.is_null ()) {
907 return ::Teuchos::null;
908 }
909
910 // mfh 17 Jan 2017: We just happen to use row indices, because
911 // that's what Tpetra::CrsMatrix currently uses. That's probably
912 // not the best thing to use, but it's not bad for commonly
913 // encountered matrices. A better more general approach might be
914 // to hash (row index, column index) pairs to a global index. One
915 // could make that unique by doing a parallel scan at map
916 // construction time.
917
918 std::vector<GO> rowInds;
919 const GO lclMinGblRowInd = this->impl_.getMyGlobalRowIndices (rowInds);
920
921 // Compute the globally min row index for the "index base."
922 GO gblMinGblRowInd = 0; // output argument
923 reduceAll<int, GO> (*comm, REDUCE_MIN, lclMinGblRowInd,
924 outArg (gblMinGblRowInd));
925 const GO indexBase = gblMinGblRowInd;
926 const GST INV = Tpetra::Details::OrdinalTraits<GST>::invalid ();
927 return rcp (new map_type (INV, rowInds.data (), rowInds.size (),
928 indexBase, comm));
929 }
930
931protected:
935 virtual size_t constantNumberOfPackets () const {
936 return static_cast<size_t> (0);
937 }
938
942 virtual bool
943 checkSizes (const ::Tpetra::SrcDistObject& source)
944 {
945 using std::endl;
946 typedef CooMatrix<SC, LO, GO, NT> this_COO_type;
947 const char prefix[] = "Tpetra::Details::CooMatrix::checkSizes: ";
948
949 const this_COO_type* src = dynamic_cast<const this_COO_type* > (&source);
950
951 if (src == NULL) {
952 std::ostream& err = markLocalErrorAndGetStream ();
953 err << prefix << "The source object of the Import or Export "
954 "must be a CooMatrix with the same template parameters as the "
955 "target object." << endl;
956 }
957 else if (this->map_.is_null ()) {
958 std::ostream& err = markLocalErrorAndGetStream ();
959 err << prefix << "The target object of the Import or Export "
960 "must be a CooMatrix with a nonnull Map." << endl;
961 }
962 return ! (* (this->localError_));
963 }
964
967 typename ::Tpetra::DistObject<char, LO, GO, NT>::buffer_device_type;
968
969 virtual void
970 copyAndPermute
971 (const ::Tpetra::SrcDistObject& sourceObject,
972 const size_t numSameIDs,
973 const Kokkos::DualView<const LO*,
974 buffer_device_type>& permuteToLIDs,
975 const Kokkos::DualView<const LO*,
976 buffer_device_type>& permuteFromLIDs,
977 const CombineMode /* CM */)
978 {
979 using std::endl;
980 using this_COO_type = CooMatrix<SC, LO, GO, NT>;
981 const char prefix[] = "Tpetra::Details::CooMatrix::copyAndPermute: ";
982
983 // There's no communication in this method, so it's safe just to
984 // return on error.
985
986 if (* (this->localError_)) {
987 std::ostream& err = this->markLocalErrorAndGetStream ();
988 err << prefix << "The target object of the Import or Export is "
989 "already in an error state." << endl;
990 return;
991 }
992
993 const this_COO_type* src = dynamic_cast<const this_COO_type*> (&sourceObject);
994 if (src == nullptr) {
995 std::ostream& err = this->markLocalErrorAndGetStream ();
996 err << prefix << "Input argument 'sourceObject' is not a CooMatrix."
997 << endl;
998 return;
999 }
1000
1001 const size_t numPermuteIDs =
1002 static_cast<size_t> (permuteToLIDs.extent (0));
1003 if (numPermuteIDs != static_cast<size_t> (permuteFromLIDs.extent (0))) {
1004 std::ostream& err = this->markLocalErrorAndGetStream ();
1005 err << prefix << "permuteToLIDs.extent(0) = "
1006 << numPermuteIDs << " != permuteFromLIDs.extent(0) = "
1007 << permuteFromLIDs.extent (0) << "." << endl;
1008 return;
1009 }
1010 if (sizeof (int) <= sizeof (size_t) &&
1011 numPermuteIDs > static_cast<size_t> (std::numeric_limits<int>::max ())) {
1012 std::ostream& err = this->markLocalErrorAndGetStream ();
1013 err << prefix << "numPermuteIDs = " << numPermuteIDs
1014 << ", a size_t, overflows int." << endl;
1015 return;
1016 }
1017
1018 // Even though this is an std::set, we can start with numSameIDs
1019 // just by iterating through the first entries of the set.
1020
1021 if (sizeof (int) <= sizeof (size_t) &&
1022 numSameIDs > static_cast<size_t> (std::numeric_limits<int>::max ())) {
1023 std::ostream& err = this->markLocalErrorAndGetStream ();
1024 err << prefix << "numSameIDs = " << numSameIDs
1025 << ", a size_t, overflows int." << endl;
1026 return;
1027 }
1028
1029 //
1030 // Copy in entries from any initial rows with the same global row indices.
1031 //
1032 const LO numSame = static_cast<int> (numSameIDs);
1033 // Count of local row indices encountered here with invalid global
1034 // row indices. If nonzero, something went wrong. If something
1035 // did go wrong, we'll defer responding until the end of this
1036 // method, so we can print as much useful info as possible.
1037 LO numInvalidSameRows = 0;
1038 for (LO lclRow = 0; lclRow < numSame; ++lclRow) {
1039 // All numSame initial rows have the same global index in both
1040 // source and target Maps, so we only need to convert to global
1041 // once.
1042 const GO gblRow = this->map_->getGlobalElement (lclRow);
1043 if (gblRow == ::Tpetra::Details::OrdinalTraits<GO>::invalid ()) {
1044 ++numInvalidSameRows;
1045 continue;
1046 }
1047 else {
1048 this->impl_.mergeIntoRow (gblRow, src->impl_, gblRow);
1049 }
1050 }
1051
1052 //
1053 // Copy in entries from remaining rows that are permutations, that
1054 // is, that live in both the source and target Maps, but aren't
1055 // included in the "same" list (see above).
1056 //
1057 const LO numPermute = static_cast<int> (numPermuteIDs);
1058 // Count of local "from" row indices encountered here with invalid
1059 // global row indices. If nonzero, something went wrong. If
1060 // something did go wrong, we'll defer responding until the end of
1061 // this method, so we can print as much useful info as possible.
1062 LO numInvalidRowsFrom = 0;
1063 // Count of local "to" row indices encountered here with invalid
1064 // global row indices. If nonzero, something went wrong. If
1065 // something did go wrong, we'll defer responding until the end of
1066 // this method, so we can print as much useful info as possible.
1067 LO numInvalidRowsTo = 0;
1068
1069 TEUCHOS_ASSERT( ! permuteFromLIDs.need_sync_host () );
1070 TEUCHOS_ASSERT( ! permuteToLIDs.need_sync_host () );
1071 auto permuteFromLIDs_h = permuteFromLIDs.view_host ();
1072 auto permuteToLIDs_h = permuteToLIDs.view_host ();
1073
1074 for (LO k = 0; k < numPermute; ++k) {
1075 const LO lclRowFrom = permuteFromLIDs_h[k];
1076 const LO lclRowTo = permuteToLIDs_h[k];
1077 const GO gblRowFrom = src->map_->getGlobalElement (lclRowFrom);
1078 const GO gblRowTo = this->map_->getGlobalElement (lclRowTo);
1079
1080 bool bothConversionsValid = true;
1081 if (gblRowFrom == ::Tpetra::Details::OrdinalTraits<GO>::invalid ()) {
1082 ++numInvalidRowsFrom;
1083 bothConversionsValid = false;
1084 }
1085 if (gblRowTo == ::Tpetra::Details::OrdinalTraits<GO>::invalid ()) {
1086 ++numInvalidRowsTo;
1087 bothConversionsValid = false;
1088 }
1089 if (bothConversionsValid) {
1090 this->impl_.mergeIntoRow (gblRowTo, src->impl_, gblRowFrom);
1091 }
1092 }
1093
1094 // Print info if any errors occurred.
1095 if (numInvalidSameRows != 0 || numInvalidRowsFrom != 0 ||
1096 numInvalidRowsTo != 0) {
1097 // Collect and print all the invalid input row indices, for the
1098 // "same," "from," and "to" lists.
1099 std::vector<std::pair<LO, GO> > invalidSameRows;
1100 invalidSameRows.reserve (numInvalidSameRows);
1101 std::vector<std::pair<LO, GO> > invalidRowsFrom;
1102 invalidRowsFrom.reserve (numInvalidRowsFrom);
1103 std::vector<std::pair<LO, GO> > invalidRowsTo;
1104 invalidRowsTo.reserve (numInvalidRowsTo);
1105
1106 for (LO lclRow = 0; lclRow < numSame; ++lclRow) {
1107 // All numSame initial rows have the same global index in both
1108 // source and target Maps, so we only need to convert to global
1109 // once.
1110 const GO gblRow = this->map_->getGlobalElement (lclRow);
1111 if (gblRow == ::Tpetra::Details::OrdinalTraits<GO>::invalid ()) {
1112 invalidSameRows.push_back ({lclRow, gblRow});
1113 }
1114 }
1115
1116 for (LO k = 0; k < numPermute; ++k) {
1117 const LO lclRowFrom = permuteFromLIDs_h[k];
1118 const LO lclRowTo = permuteToLIDs_h[k];
1119 const GO gblRowFrom = src->map_->getGlobalElement (lclRowFrom);
1120 const GO gblRowTo = this->map_->getGlobalElement (lclRowTo);
1121
1122 if (gblRowFrom == ::Tpetra::Details::OrdinalTraits<GO>::invalid ()) {
1123 invalidRowsFrom.push_back ({lclRowFrom, gblRowFrom});
1124 }
1125 if (gblRowTo == ::Tpetra::Details::OrdinalTraits<GO>::invalid ()) {
1126 invalidRowsTo.push_back ({lclRowTo, gblRowTo});
1127 }
1128 }
1129
1130 std::ostringstream os;
1131 if (numInvalidSameRows != 0) {
1132 os << "Invalid permute \"same\" (local, global) index pairs: [";
1133 for (std::size_t k = 0; k < invalidSameRows.size (); ++k) {
1134 const auto& p = invalidSameRows[k];
1135 os << "(" << p.first << "," << p.second << ")";
1136 if (k + 1 < invalidSameRows.size ()) {
1137 os << ", ";
1138 }
1139 }
1140 os << "]" << std::endl;
1141 }
1142 if (numInvalidRowsFrom != 0) {
1143 os << "Invalid permute \"from\" (local, global) index pairs: [";
1144 for (std::size_t k = 0; k < invalidRowsFrom.size (); ++k) {
1145 const auto& p = invalidRowsFrom[k];
1146 os << "(" << p.first << "," << p.second << ")";
1147 if (k + 1 < invalidRowsFrom.size ()) {
1148 os << ", ";
1149 }
1150 }
1151 os << "]" << std::endl;
1152 }
1153 if (numInvalidRowsTo != 0) {
1154 os << "Invalid permute \"to\" (local, global) index pairs: [";
1155 for (std::size_t k = 0; k < invalidRowsTo.size (); ++k) {
1156 const auto& p = invalidRowsTo[k];
1157 os << "(" << p.first << "," << p.second << ")";
1158 if (k + 1 < invalidRowsTo.size ()) {
1159 os << ", ";
1160 }
1161 }
1162 os << "]" << std::endl;
1163 }
1164
1165 std::ostream& err = this->markLocalErrorAndGetStream ();
1166 err << prefix << os.str ();
1167 return;
1168 }
1169 }
1170
1171 virtual void
1172 packAndPrepare
1173 (const ::Tpetra::SrcDistObject& sourceObject,
1174 const Kokkos::DualView<const local_ordinal_type*,
1175 buffer_device_type>& exportLIDs,
1176 Kokkos::DualView<packet_type*,
1177 buffer_device_type>& exports,
1178 Kokkos::DualView<size_t*,
1179 buffer_device_type> numPacketsPerLID,
1180 size_t& constantNumPackets)
1181 {
1182 using Teuchos::Comm;
1183 using Teuchos::RCP;
1184 using std::endl;
1185 using this_COO_type = CooMatrix<SC, LO, GO, NT>;
1186 const char prefix[] = "Tpetra::Details::CooMatrix::packAndPrepare: ";
1187 const char suffix[] = " This should never happen. "
1188 "Please report this bug to the Tpetra developers.";
1189
1190 // Tell the caller that different rows may have different numbers
1191 // of matrix entries.
1192 constantNumPackets = 0;
1193
1194 const this_COO_type* src = dynamic_cast<const this_COO_type*> (&sourceObject);
1195 if (src == nullptr) {
1196 std::ostream& err = this->markLocalErrorAndGetStream ();
1197 err << prefix << "Input argument 'sourceObject' is not a CooMatrix."
1198 << endl;
1199 }
1200 else if (* (src->localError_)) {
1201 std::ostream& err = this->markLocalErrorAndGetStream ();
1202 err << prefix << "The source (input) object of the Import or Export "
1203 "is already in an error state on this process."
1204 << endl;
1205 }
1206 else if (* (this->localError_)) {
1207 std::ostream& err = this->markLocalErrorAndGetStream ();
1208 err << prefix << "The target (output, \"this\") object of the Import "
1209 "or Export is already in an error state on this process." << endl;
1210 }
1211 // Respond to detected error(s) by resizing 'exports' to zero (so
1212 // we won't be tempted to read it later), and filling
1213 // numPacketsPerLID with zeros.
1214 if (* (this->localError_)) {
1215 // Resize 'exports' to zero, so we won't be tempted to read it.
1216 Details::reallocDualViewIfNeeded (exports, 0, "CooMatrix exports");
1217 // Trick to get around const DualView& being const.
1218 {
1219 auto numPacketsPerLID_tmp = numPacketsPerLID;
1220 numPacketsPerLID_tmp.sync_host ();
1221 numPacketsPerLID_tmp.modify_host ();
1222 }
1223 // Fill numPacketsPerLID with zeros.
1224 Kokkos::deep_copy (numPacketsPerLID.view_host(), static_cast<size_t> (0));
1225 return;
1226 }
1227
1228 const size_t numExports = exportLIDs.extent (0);
1229 if (numExports == 0) {
1230 Details::reallocDualViewIfNeeded (exports, 0, exports.view_host().label ());
1231 return; // nothing to send
1232 }
1233 RCP<const Comm<int> > comm = src->getMap ().is_null () ?
1234 Teuchos::null : src->getMap ()->getComm ();
1235 if (comm.is_null () || comm->getSize () == 1) {
1236 if (numExports != static_cast<size_t> (0)) {
1237 std::ostream& err = this->markLocalErrorAndGetStream ();
1238 err << prefix << "The input communicator is either null or "
1239 "has only one process, but numExports = " << numExports << " != 0."
1240 << suffix << endl;
1241 return;
1242 }
1243 // Don't go into the rest of this method unless there are
1244 // actually processes other than the calling process. This is
1245 // because the pack and unpack functions only have nonstub
1246 // implementations if building with MPI.
1247 return;
1248 }
1249
1250 numPacketsPerLID.sync_host ();
1251 numPacketsPerLID.modify_host ();
1252
1253 TEUCHOS_ASSERT( ! exportLIDs.need_sync_host () );
1254 auto exportLIDs_h = exportLIDs.view_host ();
1255
1256 int totalNumPackets = 0;
1257 size_t numInvalidRowInds = 0;
1258 std::ostringstream errStrm; // current loop iteration's error messages
1259 for (size_t k = 0; k < numExports; ++k) {
1260 const LO lclRow = exportLIDs_h[k];
1261 // We're packing the source object's data, so we need to use the
1262 // source object's Map to convert from local to global indices.
1263 const GO gblRow = src->map_->getGlobalElement (lclRow);
1264 if (gblRow == ::Tpetra::Details::OrdinalTraits<GO>::invalid ()) {
1265 // Mark the error later; just count for now.
1266 ++numInvalidRowInds;
1267 numPacketsPerLID.view_host()[k] = 0;
1268 continue;
1269 }
1270
1271 // Count the number of bytes needed to pack the current row of
1272 // the source object.
1273 int numPackets = 0;
1274 const int errCode =
1275 src->impl_.countPackRow (numPackets, gblRow, *comm, &errStrm);
1276 if (errCode != 0) {
1277 std::ostream& err = this->markLocalErrorAndGetStream ();
1278 err << prefix << errStrm.str () << endl;
1279 numPacketsPerLID.view_host()[k] = 0;
1280 continue;
1281 }
1282
1283 // Make sure that the total number of packets fits in int.
1284 // MPI requires this.
1285 const long long newTotalNumPackets =
1286 static_cast<long long> (totalNumPackets) +
1287 static_cast<long long> (numPackets);
1288 if (newTotalNumPackets >
1289 static_cast<long long> (std::numeric_limits<int>::max ())) {
1290 std::ostream& err = this->markLocalErrorAndGetStream ();
1291 err << prefix << "The new total number of packets "
1292 << newTotalNumPackets << " does not fit in int." << endl;
1293 // At this point, we definitely cannot continue. In order to
1294 // leave the output arguments in a rational state, we zero out
1295 // all remaining entries of numPacketsPerLID before returning.
1296 for (size_t k2 = k; k2 < numExports; ++k2) {
1297 numPacketsPerLID.view_host()[k2] = 0;
1298 }
1299 return;
1300 }
1301 numPacketsPerLID.view_host()[k] = static_cast<size_t> (numPackets);
1302 totalNumPackets = static_cast<int> (newTotalNumPackets);
1303 }
1304
1305 // If we found invalid row indices in exportLIDs, go back,
1306 // collect, and report them.
1307 if (numInvalidRowInds != 0) {
1308 std::vector<std::pair<LO, GO> > invalidRowInds;
1309 for (size_t k = 0; k < numExports; ++k) {
1310 const LO lclRow = exportLIDs_h[k];
1311 // We're packing the source object's data, so we need to use
1312 // the source object's Map to convert from local to global
1313 // indices.
1314 const GO gblRow = src->map_->getGlobalElement (lclRow);
1315 if (gblRow == ::Tpetra::Details::OrdinalTraits<GO>::invalid ()) {
1316 invalidRowInds.push_back ({lclRow, gblRow});
1317 }
1318 }
1319 std::ostringstream os;
1320 os << prefix << "We found " << numInvalidRowInds << " invalid row ind"
1321 << (numInvalidRowInds != static_cast<size_t> (1) ? "ices" : "ex")
1322 << " out of " << numExports << " in exportLIDs. Here is the list "
1323 << " of invalid row indices: [";
1324 for (size_t k = 0; k < invalidRowInds.size (); ++k) {
1325 os << "(LID: " << invalidRowInds[k].first << ", GID: "
1326 << invalidRowInds[k].second << ")";
1327 if (k + 1 < invalidRowInds.size ()) {
1328 os << ", ";
1329 }
1330 }
1331 os << "].";
1332
1333 std::ostream& err = this->markLocalErrorAndGetStream ();
1334 err << prefix << os.str () << std::endl;
1335 return;
1336 }
1337
1338 {
1339 const bool reallocated =
1340 Details::reallocDualViewIfNeeded (exports, totalNumPackets,
1341 "CooMatrix exports");
1342 if (reallocated) {
1343 exports.sync_host (); // make sure alloc'd on host
1344 }
1345 }
1346 exports.modify_host ();
1347
1348 // FIXME (mfh 17 Jan 2017) packTriples wants three arrays, not a
1349 // single array of structs. For now, we just copy.
1350 std::vector<GO> gblRowInds;
1351 std::vector<GO> gblColInds;
1352 std::vector<SC> vals;
1353
1354 int outBufCurPos = 0;
1355 packet_type* outBuf = exports.view_host().data ();
1356 for (size_t k = 0; k < numExports; ++k) {
1357 const LO lclRow = exportLIDs.view_host()[k];
1358 // We're packing the source object's data, so we need to use the
1359 // source object's Map to convert from local to global indices.
1360 const GO gblRow = src->map_->getGlobalElement (lclRow);
1361 // Pack the current row of the source object.
1362 src->impl_.packRow (outBuf, totalNumPackets, outBufCurPos, *comm,
1363 gblRowInds, gblColInds, vals, gblRow);
1364 }
1365 }
1366
1367 virtual void
1368 unpackAndCombine
1369 (const Kokkos::DualView<const local_ordinal_type*,
1370 buffer_device_type>& importLIDs,
1371 Kokkos::DualView<packet_type*,
1372 buffer_device_type> imports,
1373 Kokkos::DualView<size_t*,
1374 buffer_device_type> numPacketsPerLID,
1375 const size_t /* constantNumPackets */,
1376 const ::Tpetra::CombineMode /* combineMode */)
1377 {
1378 using Teuchos::Comm;
1379 using Teuchos::RCP;
1380 using std::endl;
1381 const char prefix[] = "Tpetra::Details::CooMatrix::unpackAndCombine: ";
1382 const char suffix[] = " This should never happen. "
1383 "Please report this bug to the Tpetra developers.";
1384
1385 TEUCHOS_ASSERT( ! importLIDs.need_sync_host () );
1386 auto importLIDs_h = importLIDs.view_host ();
1387
1388 const std::size_t numImports = importLIDs.extent (0);
1389 if (numImports == 0) {
1390 return; // nothing to receive
1391 }
1392 else if (imports.extent (0) == 0) {
1393 std::ostream& err = this->markLocalErrorAndGetStream ();
1394 err << prefix << "importLIDs.extent(0) = " << numImports << " != 0, "
1395 << "but imports.extent(0) = 0. This doesn't make sense, because "
1396 << "for every incoming LID, CooMatrix packs at least the count of "
1397 << "triples associated with that LID, even if the count is zero. "
1398 << "importLIDs = [";
1399 for (std::size_t k = 0; k < numImports; ++k) {
1400 err << importLIDs_h[k];
1401 if (k + 1 < numImports) {
1402 err << ", ";
1403 }
1404 }
1405 err << "]. " << suffix << endl;
1406 return;
1407 }
1408
1409 RCP<const Comm<int> > comm = this->getMap ().is_null () ?
1410 Teuchos::null : this->getMap ()->getComm ();
1411 if (comm.is_null () || comm->getSize () == 1) {
1412 if (numImports != static_cast<size_t> (0)) {
1413 std::ostream& err = this->markLocalErrorAndGetStream ();
1414 err << prefix << "The input communicator is either null or "
1415 "has only one process, but numImports = " << numImports << " != 0."
1416 << suffix << endl;
1417 return;
1418 }
1419 // Don't go into the rest of this method unless there are
1420 // actually processes other than the calling process. This is
1421 // because the pack and unpack functions only have nonstub
1422 // implementations if building with MPI.
1423 return;
1424 }
1425
1426 // Make sure that the length of 'imports' fits in int.
1427 // This is ultimately an MPI restriction.
1428 if (static_cast<size_t> (imports.extent (0)) >
1429 static_cast<size_t> (std::numeric_limits<int>::max ())) {
1430 std::ostream& err = this->markLocalErrorAndGetStream ();
1431 err << prefix << "imports.extent(0) = "
1432 << imports.extent (0) << " does not fit in int." << endl;
1433 return;
1434 }
1435 const int inBufSize = static_cast<int> (imports.extent (0));
1436
1437 if (imports.need_sync_host ()) {
1438 imports.sync_host ();
1439 }
1440 if (numPacketsPerLID.need_sync_host ()) {
1441 numPacketsPerLID.sync_host ();
1442 }
1443 auto imports_h = imports.view_host ();
1444 auto numPacketsPerLID_h = numPacketsPerLID.view_host ();
1445
1446 // FIXME (mfh 17,24 Jan 2017) packTriples wants three arrays, not a
1447 // single array of structs. For now, we just copy.
1448 std::vector<GO> gblRowInds;
1449 std::vector<GO> gblColInds;
1450 std::vector<SC> vals;
1451
1452 const packet_type* inBuf = imports_h.data ();
1453 int inBufCurPos = 0;
1454 size_t numInvalidRowInds = 0;
1455 int errCode = 0;
1456 std::ostringstream errStrm; // for unpack* error output.
1457 for (size_t k = 0; k < numImports; ++k) {
1458 const LO lclRow = importLIDs_h(k);
1459 const GO gblRow = this->map_->getGlobalElement (lclRow);
1460 if (gblRow == ::Tpetra::Details::OrdinalTraits<GO>::invalid ()) {
1461 ++numInvalidRowInds;
1462 continue;
1463 }
1464
1465 // Remember where we were, so we don't overrun the buffer
1466 // length. inBufCurPos is an in/out argument of unpackTriples*.
1467 const int origInBufCurPos = inBufCurPos;
1468
1469 int numEnt = 0; // output argument of unpackTriplesCount
1470 errCode = unpackTriplesCount (inBuf, inBufSize, inBufCurPos,
1471 numEnt, *comm, &errStrm);
1472 if (errCode != 0 || numEnt < 0 || inBufCurPos < origInBufCurPos) {
1473 std::ostream& err = this->markLocalErrorAndGetStream ();
1474
1475 err << prefix << "In unpack loop, k=" << k << ": ";
1476 if (errCode != 0) {
1477 err << " unpackTriplesCount returned errCode = " << errCode
1478 << " != 0." << endl;
1479 }
1480 if (numEnt < 0) {
1481 err << " unpackTriplesCount returned errCode = 0, but numEnt = "
1482 << numEnt << " < 0." << endl;
1483 }
1484 if (inBufCurPos < origInBufCurPos) {
1485 err << " After unpackTriplesCount, inBufCurPos = " << inBufCurPos
1486 << " < origInBufCurPos = " << origInBufCurPos << "." << endl;
1487 }
1488 err << " unpackTriplesCount report: " << errStrm.str () << endl;
1489 err << suffix << endl;
1490
1491 // We only continue in a debug build, because the above error
1492 // messages could consume too much memory and cause an
1493 // out-of-memory error, without actually printing. Printing
1494 // everything is useful in a debug build, but not so much in a
1495 // release build.
1496#ifdef HAVE_TPETRA_DEBUG
1497 // Clear out the current error stream, so we don't accumulate
1498 // over loop iterations.
1499 errStrm.str ("");
1500 continue;
1501#else
1502 return;
1503#endif // HAVE_TPETRA_DEBUG
1504 }
1505
1506 // FIXME (mfh 17,24 Jan 2017) packTriples wants three arrays,
1507 // not a single array of structs. For now, we just copy.
1508 gblRowInds.resize (numEnt);
1509 gblColInds.resize (numEnt);
1510 vals.resize (numEnt);
1511
1512 errCode = unpackTriples (inBuf, inBufSize, inBufCurPos,
1513 gblRowInds.data (), gblColInds.data (),
1514 vals.data (), numEnt, *comm, &errStrm);
1515 if (errCode != 0) {
1516 std::ostream& err = this->markLocalErrorAndGetStream ();
1517 err << prefix << "unpackTriples returned errCode = "
1518 << errCode << " != 0. It reports: " << errStrm.str ()
1519 << endl;
1520 // We only continue in a debug build, because the above error
1521 // messages could consume too much memory and cause an
1522 // out-of-memory error, without actually printing. Printing
1523 // everything is useful in a debug build, but not so much in a
1524 // release build.
1525#ifdef HAVE_TPETRA_DEBUG
1526 // Clear out the current error stream, so we don't accumulate
1527 // over loop iterations.
1528 errStrm.str ("");
1529 continue;
1530#else
1531 return;
1532#endif // HAVE_TPETRA_DEBUG
1533 }
1534 this->sumIntoGlobalValues (gblRowInds.data (), gblColInds.data (),
1535 vals.data (), numEnt);
1536 }
1537
1538 // If we found invalid row indices in exportLIDs, go back,
1539 // collect, and report them.
1540 if (numInvalidRowInds != 0) {
1541 // Mark the error now, before we possibly run out of memory.
1542 // The latter could raise an exception (e.g., std::bad_alloc),
1543 // but at least we would get the error state right.
1544 std::ostream& err = this->markLocalErrorAndGetStream ();
1545
1546 std::vector<std::pair<LO, GO> > invalidRowInds;
1547 for (size_t k = 0; k < numImports; ++k) {
1548 const LO lclRow = importLIDs_h(k);
1549 const GO gblRow = this->map_->getGlobalElement (lclRow);
1550 if (gblRow == ::Tpetra::Details::OrdinalTraits<GO>::invalid ()) {
1551 invalidRowInds.push_back ({lclRow, gblRow});
1552 }
1553 }
1554
1555 err << prefix << "We found " << numInvalidRowInds << " invalid row ind"
1556 << (numInvalidRowInds != static_cast<size_t> (1) ? "ices" : "ex")
1557 << " out of " << numImports << " in importLIDs. Here is the list "
1558 << " of invalid row indices: [";
1559 for (size_t k = 0; k < invalidRowInds.size (); ++k) {
1560 err << "(LID: " << invalidRowInds[k].first << ", GID: "
1561 << invalidRowInds[k].second << ")";
1562 if (k + 1 < invalidRowInds.size ()) {
1563 err << ", ";
1564 }
1565 }
1566 err << "].";
1567 return;
1568 }
1569 }
1570};
1571
1572} // namespace Details
1573} // namespace Tpetra
1574
1575#endif // TPETRA_DETAILS_COOMATRIX_HPP
Declaration of a function that prints strings from each process.
Declaration and definition of Tpetra::Details::reallocDualViewIfNeeded, an implementation detail of T...
Sparse matrix used only for file input / output.
void sumIntoGlobalValues(const GO gblRowInds[], const GO gblColInds[], const SC vals[], const std::size_t numEnt)
Insert multiple entries locally into the sparse matrix.
virtual std::string description() const
One-line descriptiion of this object; overrides Teuchos::Describable method.
virtual size_t constantNumberOfPackets() const
By returning 0, tell DistObject that this class may not necessarily have a constant number of "packet...
virtual ~CooMatrix()
Destructor (virtual for memory safety of derived classes).
void fillComplete()
Special version of fillComplete that assumes that the matrix already has a Map, and reuses its commun...
SC scalar_type
Type of each entry (value) in the sparse matrix.
void sumIntoGlobalValues(std::initializer_list< GO > gblRowInds, std::initializer_list< GO > gblColInds, std::initializer_list< SC > vals, const std::size_t numEnt)
Initializer-list overload of the above method (which see).
void fillComplete(const ::Teuchos::RCP< const ::Teuchos::Comm< int > > &comm)
Tell the matrix that you are done inserting entries locally, and that the matrix should build its Map...
void sumIntoGlobalValue(const GO gblRowInd, const GO gblColInd, const SC &val)
Insert one entry locally into the sparse matrix, if it does not exist there yet. If it does exist,...
typename ::Tpetra::DistObject< char, LO, GO, NT >::buffer_device_type buffer_device_type
Kokkos::Device specialization for DistObject communication buffers.
CooMatrix(const ::Teuchos::RCP< const map_type > &map)
Constructor that takes a Map.
bool localError() const
Whether this object had an error on the calling process.
virtual bool checkSizes(const ::Tpetra::SrcDistObject &source)
Compare the source and target (this) objects for compatibility.
std::size_t getLclNumEntries() const
Number of entries in the sparse matrix on the calling process.
virtual void describe(Teuchos::FancyOStream &out, const Teuchos::EVerbosityLevel verbLevel=Teuchos::Describable::verbLevel_default) const
Print a descriptiion of this object to the given output stream; overrides Teuchos::Describable method...
std::string errorMessages() const
The current stream of error messages.
char packet_type
This class transfers data as bytes (MPI_BYTE).
::Tpetra::Map< local_ordinal_type, global_ordinal_type, node_type > map_type
Type of the Map specialization to give to the constructor.
Implementation detail of Tpetra::Details::CooMatrix (which see below).
void sumIntoGlobalValue(const GO gblRowInd, const GO gblColInd, const SC &val)
Insert one entry locally into the sparse matrix, if it does not exist there yet. If it does exist,...
std::size_t getLclNumEntries() const
Number of entries in the sparse matrix on the calling process.
void packRow(packet_type outBuf[], const int outBufSize, int &outBufCurPos, const ::Teuchos::Comm< int > &comm, std::vector< GO > &gblRowInds, std::vector< GO > &gblColInds, std::vector< SC > &vals, const GO gblRow) const
Pack the given row of the matrix.
void sumIntoGlobalValues(const GO gblRowInds[], const GO gblColInds[], const SC vals[], const std::size_t numEnt)
Insert multiple entries locally into the sparse matrix.
void mergeIntoRow(const GO tgtGblRow, const CooMatrixImpl< SC, GO > &src, const GO srcGblRow)
Into global row tgtGblRow of *this, merge global row srcGblRow of src.
void forAllEntries(std::function< void(const GO, const GO, const SC &)> f) const
Execute the given function for all entries of the sparse matrix, sequentially (no thread parallelism)...
GO getMyGlobalRowIndices(std::vector< GO > &rowInds) const
Get the global row indices on this process, sorted and made unique, and return the minimum global row...
char packet_type
Type for packing and unpacking data.
void buildLocallyIndexedCrs(std::vector< OffsetType > &rowOffsets, LO lclColInds[], SC vals[], std::function< LO(const GO)> gblToLcl) const
Build a locally indexed version of CRS storage.
int countPackRow(int &numPackets, const GO gblRow, const ::Teuchos::Comm< int > &comm, std::ostream *errStrm=NULL) const
Count the number of packets (bytes, in this case) needed to pack the given row of the matrix.
CooMatrixImpl()=default
Default constructor.
Base class for distributed Tpetra objects that support data redistribution.
LocalOrdinal local_ordinal_type
The type of local indices.
GlobalOrdinal global_ordinal_type
The type of global indices.
Teuchos::RCP< const map_type > map_
The Map over which this object is distributed.
Node node_type
The Node type. If you don't know what this is, don't use it.
virtual Teuchos::RCP< const map_type > getMap() const
The Map describing the parallel distribution of this object.
Nonmember function that computes a residual Computes R = B - A * X.
int packTriplesCount(const int, char[], const int, int &, const ::Teuchos::Comm< int > &, std::ostream *errStrm)
Pack the count (number) of matrix triples.
int countPackTriplesCount(const ::Teuchos::Comm< int > &, int &size, std::ostream *errStrm)
Compute the buffer size required by packTriples for packing the number of matrix entries ("triples").
int packTriples(const OrdinalType[], const OrdinalType[], const ScalarType[], const int, char[], const int, int &, const ::Teuchos::Comm< int > &, std::ostream *errStrm=NULL)
Pack matrix entries ("triples" (i, j, A(i,j))) into the given output buffer.
int unpackTriplesCount(const char[], const int, int &, int &, const ::Teuchos::Comm< int > &, std::ostream *errStrm)
Unpack just the count of triples from the given input buffer.
int unpackTriples(const char[], const int, int &, OrdinalType[], OrdinalType[], ScalarType[], const int, const ::Teuchos::Comm< int > &, std::ostream *errStrm=NULL)
Unpack matrix entries ("triples" (i, j, A(i,j))) from the given input buffer.
bool reallocDualViewIfNeeded(Kokkos::DualView< ValueType *, DeviceType > &dv, const size_t newSize, const char newLabel[], const size_t tooBigFactor=2, const bool needFenceBeforeRealloc=true)
Reallocate the DualView in/out argument, if needed.
int countPackTriples(const int numEnt, const ::Teuchos::Comm< int > &comm, int &size, std::ostream *errStrm=NULL)
Compute the buffer size required by packTriples for packing numEnt number of (i,j,...
void gathervPrint(std::ostream &out, const std::string &s, const Teuchos::Comm< int > &comm)
On Process 0 in the given communicator, print strings from each process in that communicator,...
Namespace Tpetra contains the class and methods constituting the Tpetra library.
void deep_copy(MultiVector< DS, DL, DG, DN > &dst, const MultiVector< SS, SL, SG, SN > &src)
Copy the contents of the MultiVector src into dst.
size_t global_size_t
Global size_t object.
CombineMode
Rule for combining data in an Import or Export.
Function comparing two CooGraphEntry structs, lexicographically, first by row index,...
Type of each (row index, column index) pair in the Tpetra::Details::CooMatrix (see below).