Tpetra parallel linear algebra Version of the Day
Loading...
Searching...
No Matches
Tpetra_Import_Util2.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_IMPORT_UTIL2_HPP
11#define TPETRA_IMPORT_UTIL2_HPP
12
17
18#include "Tpetra_ConfigDefs.hpp"
19#include "Tpetra_Import.hpp"
20#include "Tpetra_HashTable.hpp"
21#include "Tpetra_Map.hpp"
22#include "Tpetra_Util.hpp"
23#include "Tpetra_Distributor.hpp"
26#include "Tpetra_Vector.hpp"
27#include "Kokkos_DualView.hpp"
28#include "KokkosSparse_SortCrs.hpp"
29#include <Teuchos_Array.hpp>
31#include <Kokkos_UnorderedMap.hpp>
32#include <unordered_map>
33#include <utility>
34#include <set>
35
37
38#include <Kokkos_Core.hpp>
39#include <Kokkos_Sort.hpp>
40
41namespace Tpetra {
42namespace Import_Util {
43
46template <typename Scalar, typename Ordinal>
47void sortCrsEntries(const Teuchos::ArrayView<size_t>& CRS_rowptr,
48 const Teuchos::ArrayView<Ordinal>& CRS_colind,
49 const Teuchos::ArrayView<Scalar>& CRS_vals);
50
51template <typename Ordinal>
52void sortCrsEntries(const Teuchos::ArrayView<size_t>& CRS_rowptr,
53 const Teuchos::ArrayView<Ordinal>& CRS_colind);
54
55template <typename rowptr_array_type, typename colind_array_type, typename vals_array_type>
56void sortCrsEntries(const rowptr_array_type& CRS_rowptr,
57 const colind_array_type& CRS_colind,
58 const vals_array_type& CRS_vals);
59
60template <typename rowptr_array_type, typename colind_array_type>
61void sortCrsEntries(const rowptr_array_type& CRS_rowptr,
62 const colind_array_type& CRS_colind);
63
68template <typename Scalar, typename Ordinal>
69void sortAndMergeCrsEntries(const Teuchos::ArrayView<size_t>& CRS_rowptr,
70 const Teuchos::ArrayView<Ordinal>& CRS_colind,
71 const Teuchos::ArrayView<Scalar>& CRS_vals);
72
73template <typename Ordinal>
74void sortAndMergeCrsEntries(const Teuchos::ArrayView<size_t>& CRS_rowptr,
75 const Teuchos::ArrayView<Ordinal>& CRS_colind);
76
77template <class rowptr_view_type, class colind_view_type, class vals_view_type>
78void sortAndMergeCrsEntries(const rowptr_view_type& CRS_rowptr,
79 const colind_view_type& CRS_colind,
80 const vals_view_type& CRS_vals);
81
97template <typename LocalOrdinal, typename GlobalOrdinal, typename Node>
99 const Teuchos::ArrayView<const size_t>& rowptr,
100 const Teuchos::ArrayView<LocalOrdinal>& colind_LID,
101 const Teuchos::ArrayView<GlobalOrdinal>& colind_GID,
102 const Teuchos::RCP<const Tpetra::Map<LocalOrdinal, GlobalOrdinal, Node>>& domainMapRCP,
103 const Teuchos::ArrayView<const int>& owningPIDs,
104 Teuchos::Array<int>& remotePIDs,
105 Teuchos::RCP<const Tpetra::Map<LocalOrdinal, GlobalOrdinal, Node>>& colMap);
106
111template <typename LocalOrdinal, typename GlobalOrdinal, typename Node>
113 const Kokkos::View<size_t*, typename Node::device_type> rowptr_view,
114 const Kokkos::View<LocalOrdinal*, typename Node::device_type> colind_LID_view,
115 const Kokkos::View<GlobalOrdinal*, typename Node::device_type> colind_GID_view,
116 const Teuchos::RCP<const Tpetra::Map<LocalOrdinal, GlobalOrdinal, Node>>& domainMapRCP,
117 const Teuchos::ArrayView<const int>& owningPIDs,
118 Teuchos::Array<int>& remotePIDs,
119 Teuchos::RCP<const Tpetra::Map<LocalOrdinal, GlobalOrdinal, Node>>& colMap);
120
134template <typename LocalOrdinal, typename GlobalOrdinal, typename Node>
135void getTwoTransferOwnershipVector(const ::Tpetra::Details::Transfer<LocalOrdinal, GlobalOrdinal, Node>& transferThatDefinesOwnership,
136 bool useReverseModeForOwnership,
137 const ::Tpetra::Details::Transfer<LocalOrdinal, GlobalOrdinal, Node>& transferForMigratingData,
138 bool useReverseModeForMigration,
140
141} // namespace Import_Util
142} // namespace Tpetra
143
144//
145// Implementations
146//
147
148namespace Tpetra {
149namespace Import_Util {
150
151template <typename PID, typename GlobalOrdinal>
152bool sort_PID_then_GID(const std::pair<PID, GlobalOrdinal>& a,
153 const std::pair<PID, GlobalOrdinal>& b) {
154 if (a.first != b.first)
155 return (a.first < b.first);
156 return (a.second < b.second);
157}
158
159template <typename PID,
160 typename GlobalOrdinal,
161 typename LocalOrdinal>
162bool sort_PID_then_pair_GID_LID(const std::pair<PID, std::pair<GlobalOrdinal, LocalOrdinal>>& a,
163 const std::pair<PID, std::pair<GlobalOrdinal, LocalOrdinal>>& b) {
164 if (a.first != b.first)
165 return a.first < b.first;
166 else
167 return (a.second.first < b.second.first);
168}
169
170template <typename Scalar,
171 typename LocalOrdinal,
172 typename GlobalOrdinal,
173 typename Node>
174void reverseNeighborDiscovery(const CrsMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>& SourceMatrix,
175 const typename CrsMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>::row_ptrs_host_view_type& rowptr,
176 const typename CrsMatrix<Scalar, LocalOrdinal, GlobalOrdinal, Node>::local_inds_host_view_type& colind,
177 const Tpetra::Details::Transfer<LocalOrdinal, GlobalOrdinal, Node>& RowTransfer,
178 Teuchos::RCP<const Tpetra::Import<LocalOrdinal, GlobalOrdinal, Node>> MyImporter,
179 Teuchos::RCP<const Tpetra::Map<LocalOrdinal, GlobalOrdinal, Node>> MyDomainMap,
180 Teuchos::ArrayRCP<int>& type3PIDs,
181 Teuchos::ArrayRCP<LocalOrdinal>& type3LIDs,
182 Teuchos::RCP<const Teuchos::Comm<int>>& rcomm) {
183#ifdef HAVE_TPETRACORE_MPI
184 using Teuchos::TimeMonitor;
185 using ::Tpetra::Details::Behavior;
186 typedef LocalOrdinal LO;
187 typedef GlobalOrdinal GO;
188 typedef std::pair<GO, GO> pidgidpair_t;
189 using Teuchos::RCP;
190 const std::string prefix{" Import_Util2::ReverseND:: "};
191 const std::string label("IU2::Neighbor");
192
193 // There can be no neighbor discovery if you don't have an importer
194 if (MyImporter.is_null()) return;
195
196 std::ostringstream errstr;
197 bool error = false;
198 auto const comm = MyDomainMap->getComm();
199
200 MPI_Comm rawComm = getRawMpiComm(*comm);
201 const int MyPID = rcomm->getRank();
202
203 // Things related to messages I am sending in forward mode (RowTransfer)
204 // *** Note: this will be incorrect for transferAndFillComplete if it is in reverse mode. FIXME cbl.
205 auto ExportPIDs = RowTransfer.getExportPIDs();
206 auto ExportLIDs = RowTransfer.getExportLIDs();
207 auto NumExportLIDs = RowTransfer.getNumExportIDs();
208
209 Distributor& Distor = MyImporter->getDistributor();
210 const size_t NumRecvs = Distor.getNumReceives();
211 const size_t NumSends = Distor.getNumSends();
212 auto RemoteLIDs = MyImporter->getRemoteLIDs();
213 auto const ProcsFrom = Distor.getProcsFrom();
214 auto const ProcsTo = Distor.getProcsTo();
215
216 auto LengthsFrom = Distor.getLengthsFrom();
217 auto MyColMap = SourceMatrix.getColMap();
218 const size_t numCols = MyColMap->getLocalNumElements();
219 RCP<const Tpetra::Map<LocalOrdinal, GlobalOrdinal, Node>> target = MyImporter->getTargetMap();
220
221 // Get the owning pids in a special way,
222 // s.t. ProcsFrom[RemotePIDs[i]] is the proc that owns RemoteLIDs[j]....
223 Teuchos::Array<int> RemotePIDOrder(numCols, -1);
224
225 // For each remote ID, record index into ProcsFrom, who owns it.
226 for (size_t i = 0, j = 0; i < NumRecvs; ++i) {
227 for (size_t k = 0; k < LengthsFrom[i]; ++k) {
228 const int pid = ProcsFrom[i];
229 if (pid != MyPID) {
230 RemotePIDOrder[RemoteLIDs[j]] = i;
231 }
232 j++;
233 }
234 }
235
236 // Step One: Start tacking the (GID,PID) pairs on the std sets
237 //
238 // For each index in ProcsFrom, we will insert into a set of (PID,
239 // GID) pairs, in order to build a list of such pairs for each of
240 // those processes. Since this is building a reverse, we will send
241 // to these processes.
242 Teuchos::Array<int> ReverseSendSizes(NumRecvs, 0);
243 // do this as C array to avoid Teuchos::Array value initialization of all reserved memory
244 Teuchos::Array<Teuchos::ArrayRCP<pidgidpair_t>> RSB(NumRecvs);
245
246 {
247#ifdef HAVE_TPETRA_MMM_TIMINGS
248 TimeMonitor set_all(*TimeMonitor::getNewTimer(prefix + std::string("isMMallSetRSB")));
249#endif
250
251 // 25 Jul 2018: CBL
252 // todo:std::unordered_set (hash table),
253 // with an adequate prereservation ("bucket count").
254 // An onordered_set has to have a custom hasher for pid/gid pair
255 // However, when pidsets is copied to RSB, it will be in key
256 // order _not_ in pid,gid order. (unlike std::set).
257 // Impliment this with a reserve, and time BOTH building pidsets
258 // _and_ the sort after the receive. Even if unordered_set saves
259 // time, if it causes the sort to be longer, it's not a win.
260
261 Teuchos::Array<std::set<pidgidpair_t>> pidsets(NumRecvs);
262 {
263#ifdef HAVE_TPETRA_MMM_TIMINGS
264 TimeMonitor set_insert(*TimeMonitor::getNewTimer(prefix + std::string("isMMallSetRSBinsert")));
265#endif
266 for (size_t i = 0; i < NumExportLIDs; i++) {
267 LO lid = ExportLIDs[i];
268 GO exp_pid = ExportPIDs[i];
269 for (auto j = rowptr[lid]; j < rowptr[lid + 1]; j++) {
270 int pid_order = RemotePIDOrder[colind[j]];
271 if (pid_order != -1) {
272 GO gid = MyColMap->getGlobalElement(colind[j]); // Epetra SM.GCID46 =>sm->graph-> {colmap(colind)}
273 auto tpair = pidgidpair_t(exp_pid, gid);
274 pidsets[pid_order].insert(pidsets[pid_order].end(), tpair);
275 }
276 }
277 }
278 }
279
280 {
281#ifdef HAVE_TPETRA_MMM_TIMINGS
282 TimeMonitor set_cpy(*TimeMonitor::getNewTimer(prefix + std::string("isMMallSetRSBcpy")));
283#endif
284 int jj = 0;
285 for (auto&& ps : pidsets) {
286 auto s = ps.size();
287 RSB[jj] = Teuchos::arcp(new pidgidpair_t[s], 0, s, true);
288 std::copy(ps.begin(), ps.end(), RSB[jj]);
289 ReverseSendSizes[jj] = s;
290 ++jj;
291 }
292 }
293 } // end of set based packing.
294
295 Teuchos::Array<int> ReverseRecvSizes(NumSends, -1);
296 Teuchos::Array<MPI_Request> rawBreq(ProcsFrom.size() + ProcsTo.size(), MPI_REQUEST_NULL);
297 // 25 Jul 2018: MPI_TAG_UB is the largest tag value; could be < 32768.
298 const int mpi_tag_base_ = 3;
299
300 int mpireq_idx = 0;
301 for (int i = 0; i < ProcsTo.size(); ++i) {
302 int Rec_Tag = mpi_tag_base_ + ProcsTo[i];
303 int* thisrecv = (int*)(&ReverseRecvSizes[i]);
304 MPI_Request rawRequest = MPI_REQUEST_NULL;
305 MPI_Irecv(const_cast<int*>(thisrecv),
306 1,
307 MPI_INT,
308 ProcsTo[i],
309 Rec_Tag,
310 rawComm,
311 &rawRequest);
312 rawBreq[mpireq_idx++] = rawRequest;
313 }
314 for (int i = 0; i < ProcsFrom.size(); ++i) {
315 int Send_Tag = mpi_tag_base_ + MyPID;
316 int* mysend = (int*)(&ReverseSendSizes[i]);
317 MPI_Request rawRequest = MPI_REQUEST_NULL;
318 MPI_Isend(mysend,
319 1,
320 MPI_INT,
321 ProcsFrom[i],
322 Send_Tag,
323 rawComm,
324 &rawRequest);
325 rawBreq[mpireq_idx++] = rawRequest;
326 }
327 Teuchos::Array<MPI_Status> rawBstatus(rawBreq.size());
328#ifdef HAVE_TPETRA_DEBUG
329 const int err1 =
330#endif
331 MPI_Waitall(rawBreq.size(), rawBreq.getRawPtr(),
332 rawBstatus.getRawPtr());
333
334#ifdef HAVE_TPETRA_DEBUG
335 if (err1) {
336 errstr << MyPID << "sE1 reverseNeighborDiscovery Mpi_Waitall error on send ";
337 error = true;
338 std::cerr << errstr.str() << std::flush;
339 }
340#endif
341
342 int totalexportpairrecsize = 0;
343 for (size_t i = 0; i < NumSends; ++i) {
344 totalexportpairrecsize += ReverseRecvSizes[i];
345#ifdef HAVE_TPETRA_DEBUG
346 if (ReverseRecvSizes[i] < 0) {
347 errstr << MyPID << "E4 reverseNeighborDiscovery: got < 0 for receive size " << ReverseRecvSizes[i] << std::endl;
348 error = true;
349 }
350#endif
351 }
352 Teuchos::ArrayRCP<pidgidpair_t> AllReverseRecv = Teuchos::arcp(new pidgidpair_t[totalexportpairrecsize], 0, totalexportpairrecsize, true);
353 int offset = 0;
354 mpireq_idx = 0;
355 for (int i = 0; i < ProcsTo.size(); ++i) {
356 int recv_data_size = ReverseRecvSizes[i] * 2;
357 int recvData_MPI_Tag = mpi_tag_base_ * 2 + ProcsTo[i];
358 MPI_Request rawRequest = MPI_REQUEST_NULL;
359 GO* rec_bptr = (GO*)(&AllReverseRecv[offset]);
360 offset += ReverseRecvSizes[i];
361 MPI_Irecv(rec_bptr,
362 recv_data_size,
363 ::Tpetra::Details::MpiTypeTraits<GO>::getType(rec_bptr[0]),
364 ProcsTo[i],
365 recvData_MPI_Tag,
366 rawComm,
367 &rawRequest);
368 rawBreq[mpireq_idx++] = rawRequest;
369 }
370 for (int ii = 0; ii < ProcsFrom.size(); ++ii) {
371 GO* send_bptr = (GO*)(RSB[ii].getRawPtr());
372 MPI_Request rawSequest = MPI_REQUEST_NULL;
373 int send_data_size = ReverseSendSizes[ii] * 2; // 2 == count of pair
374 int sendData_MPI_Tag = mpi_tag_base_ * 2 + MyPID;
375 MPI_Isend(send_bptr,
376 send_data_size,
377 ::Tpetra::Details::MpiTypeTraits<GO>::getType(send_bptr[0]),
378 ProcsFrom[ii],
379 sendData_MPI_Tag,
380 rawComm,
381 &rawSequest);
382
383 rawBreq[mpireq_idx++] = rawSequest;
384 }
385#ifdef HAVE_TPETRA_DEBUG
386 const int err =
387#endif
388 MPI_Waitall(rawBreq.size(),
389 rawBreq.getRawPtr(),
390 rawBstatus.getRawPtr());
391#ifdef HAVE_TPETRA_DEBUG
392 if (err) {
393 errstr << MyPID << "E3.r reverseNeighborDiscovery Mpi_Waitall error on receive ";
394 error = true;
395 std::cerr << errstr.str() << std::flush;
396 }
397#endif
398 std::sort(AllReverseRecv.begin(), AllReverseRecv.end(), Tpetra::Import_Util::sort_PID_then_GID<GlobalOrdinal, GlobalOrdinal>);
399
400 auto newEndOfPairs = std::unique(AllReverseRecv.begin(), AllReverseRecv.end());
401 // don't resize to remove non-unique, just use the end-of-unique iterator
402 if (AllReverseRecv.begin() == newEndOfPairs) return;
403 int ARRsize = std::distance(AllReverseRecv.begin(), newEndOfPairs);
404 auto rPIDs = Teuchos::arcp(new int[ARRsize], 0, ARRsize, true);
405 auto rLIDs = Teuchos::arcp(new LocalOrdinal[ARRsize], 0, ARRsize, true);
406
407 int tsize = 0;
408 for (auto itr = AllReverseRecv.begin(); itr != newEndOfPairs; ++itr) {
409 if ((int)(itr->first) != MyPID) {
410 rPIDs[tsize] = (int)itr->first;
411 LocalOrdinal lid = MyDomainMap->getLocalElement(itr->second);
412 rLIDs[tsize] = lid;
413 tsize++;
414 }
415 }
416
417 type3PIDs = rPIDs.persistingView(0, tsize);
418 type3LIDs = rLIDs.persistingView(0, tsize);
419
420 if (error) {
421 std::cerr << errstr.str() << std::flush;
422 comm->barrier();
423 comm->barrier();
424 comm->barrier();
425 MPI_Abort(MPI_COMM_WORLD, -1);
426 }
427#endif
428}
429
430// Note: This should get merged with the other Tpetra sort routines eventually.
431template <typename Scalar, typename Ordinal>
432void sortCrsEntries(const Teuchos::ArrayView<size_t>& CRS_rowptr,
433 const Teuchos::ArrayView<Ordinal>& CRS_colind,
434 const Teuchos::ArrayView<Scalar>& CRS_vals) {
435 auto rowptr_k = Kokkos::View<size_t*, Kokkos::HostSpace, Kokkos::MemoryUnmanaged>(CRS_rowptr.data(), CRS_rowptr.size());
436 auto colind_k = Kokkos::View<Ordinal*, Kokkos::HostSpace, Kokkos::MemoryUnmanaged>(CRS_colind.data(), CRS_colind.size());
437 auto vals_k = Kokkos::View<Scalar*, Kokkos::HostSpace, Kokkos::MemoryUnmanaged>(CRS_vals.data(), CRS_vals.size());
438 sortCrsEntries(rowptr_k, colind_k, vals_k);
439}
440
441template <typename Ordinal>
442void sortCrsEntries(const Teuchos::ArrayView<size_t>& CRS_rowptr,
443 const Teuchos::ArrayView<Ordinal>& CRS_colind) {
444 auto rowptr_k = Kokkos::View<size_t*, Kokkos::HostSpace, Kokkos::MemoryUnmanaged>(CRS_rowptr.data(), CRS_rowptr.size());
445 auto colind_k = Kokkos::View<Ordinal*, Kokkos::HostSpace, Kokkos::MemoryUnmanaged>(CRS_colind.data(), CRS_colind.size());
446 sortCrsEntries(rowptr_k, colind_k);
447}
448
449template <typename rowptr_array_type, typename colind_array_type, typename vals_array_type>
450void sortCrsEntries(const rowptr_array_type& CRS_rowptr,
451 const colind_array_type& CRS_colind,
452 const vals_array_type& CRS_vals) {
453 KokkosSparse::sort_crs_matrix(CRS_rowptr, CRS_colind, CRS_vals);
454}
455
456template <typename rowptr_array_type, typename colind_array_type>
457void sortCrsEntries(const rowptr_array_type& CRS_rowptr,
458 const colind_array_type& CRS_colind) {
459 KokkosSparse::sort_crs_graph(CRS_rowptr, CRS_colind);
460}
461
462// Note: This should get merged with the other Tpetra sort routines eventually.
463template <typename Scalar, typename Ordinal>
464void sortAndMergeCrsEntries(const Teuchos::ArrayView<size_t>& CRS_rowptr,
465 const Teuchos::ArrayView<Ordinal>& CRS_colind,
466 const Teuchos::ArrayView<Scalar>& CRS_vals) {
467 // For each row, sort column entries from smallest to largest,
468 // merging column ids that are identify by adding values. Use shell
469 // sort. Stable sort so it is fast if indices are already sorted.
470 // Code copied from Epetra_CrsMatrix::SortEntries()
471
472 if (CRS_rowptr.size() == 0) {
473 return; // no rows, so nothing to sort
474 }
475 const size_t NumRows = CRS_rowptr.size() - 1;
476 const size_t nnz = CRS_colind.size();
477 size_t new_curr = CRS_rowptr[0];
478 size_t old_curr = CRS_rowptr[0];
479
480 const bool permute_values_array = CRS_vals.size() > 0;
481
482 for (size_t i = 0; i < NumRows; i++) {
483 const size_t old_rowptr_i = CRS_rowptr[i];
484 CRS_rowptr[i] = old_curr;
485 if (old_rowptr_i >= nnz) continue;
486
487 size_t NumEntries = CRS_rowptr[i + 1] - old_rowptr_i;
488 Teuchos::ArrayRCP<Scalar> locValues;
489 if (permute_values_array)
490 locValues = Teuchos::arcp<Scalar>(&CRS_vals[old_rowptr_i], 0, NumEntries, false);
491 Teuchos::ArrayRCP<Ordinal> locIndices(&CRS_colind[old_rowptr_i], 0, NumEntries, false);
492
493 // Sort phase
494 Ordinal n = NumEntries;
495 Ordinal m = n / 2;
496
497 while (m > 0) {
498 Ordinal max = n - m;
499 for (Ordinal j = 0; j < max; j++) {
500 for (Ordinal k = j; k >= 0; k -= m) {
501 if (locIndices[k + m] >= locIndices[k])
502 break;
503 if (permute_values_array) {
504 Scalar dtemp = locValues[k + m];
505 locValues[k + m] = locValues[k];
506 locValues[k] = dtemp;
507 }
508 Ordinal itemp = locIndices[k + m];
509 locIndices[k + m] = locIndices[k];
510 locIndices[k] = itemp;
511 }
512 }
513 m = m / 2;
514 }
515
516 // Merge & shrink
517 for (size_t j = old_rowptr_i; j < CRS_rowptr[i + 1]; j++) {
518 if (j > old_rowptr_i && CRS_colind[j] == CRS_colind[new_curr - 1]) {
519 if (permute_values_array) CRS_vals[new_curr - 1] += CRS_vals[j];
520 } else if (new_curr == j) {
521 new_curr++;
522 } else {
523 CRS_colind[new_curr] = CRS_colind[j];
524 if (permute_values_array) CRS_vals[new_curr] = CRS_vals[j];
525 new_curr++;
526 }
527 }
528 old_curr = new_curr;
529 }
530
531 CRS_rowptr[NumRows] = new_curr;
532}
533
534template <typename Ordinal>
535void sortAndMergeCrsEntries(const Teuchos::ArrayView<size_t>& CRS_rowptr,
536 const Teuchos::ArrayView<Ordinal>& CRS_colind) {
537 Teuchos::ArrayView<Tpetra::Details::DefaultTypes::scalar_type> CRS_vals;
538 return sortAndMergeCrsEntries<Tpetra::Details::DefaultTypes::scalar_type, Ordinal>(CRS_rowptr, CRS_colind, CRS_vals);
539}
540
541template <class rowptr_view_type, class colind_view_type, class vals_view_type>
542void sortAndMergeCrsEntries(rowptr_view_type& CRS_rowptr,
543 colind_view_type& CRS_colind,
544 vals_view_type& CRS_vals) {
545 using execution_space = typename vals_view_type::execution_space;
546
547 auto CRS_rowptr_in = CRS_rowptr;
548 auto CRS_colind_in = CRS_colind;
549 auto CRS_vals_in = CRS_vals;
550
551 KokkosSparse::sort_and_merge_matrix<execution_space, rowptr_view_type,
552 colind_view_type, vals_view_type>(CRS_rowptr_in, CRS_colind_in, CRS_vals_in,
553 CRS_rowptr, CRS_colind, CRS_vals);
554}
555
556template <typename LocalOrdinal, typename GlobalOrdinal, typename Node>
557void lowCommunicationMakeColMapAndReindexSerial(const Teuchos::ArrayView<const size_t>& rowptr,
558 const Teuchos::ArrayView<LocalOrdinal>& colind_LID,
559 const Teuchos::ArrayView<GlobalOrdinal>& colind_GID,
560 const Teuchos::RCP<const Tpetra::Map<LocalOrdinal, GlobalOrdinal, Node>>& domainMapRCP,
561 const Teuchos::ArrayView<const int>& owningPIDs,
562 Teuchos::Array<int>& remotePIDs,
563 Teuchos::RCP<const Tpetra::Map<LocalOrdinal, GlobalOrdinal, Node>>& colMap) {
564 using Teuchos::rcp;
565 typedef LocalOrdinal LO;
566 typedef GlobalOrdinal GO;
567 typedef Tpetra::global_size_t GST;
568 typedef Tpetra::Map<LO, GO, Node> map_type;
569 const char prefix[] = "lowCommunicationMakeColMapAndReindexSerial: ";
570
571 // The domainMap is an RCP because there is a shortcut for a
572 // (common) special case to return the columnMap = domainMap.
573 const map_type& domainMap = *domainMapRCP;
574
575 // Scan all column indices and sort into two groups:
576 // Local: those whose GID matches a GID of the domain map on this processor and
577 // Remote: All others.
578 const size_t numDomainElements = domainMap.getLocalNumElements();
579 Teuchos::Array<bool> LocalGIDs;
580 if (numDomainElements > 0) {
581 LocalGIDs.resize(numDomainElements, false); // Assume domain GIDs are not local
582 }
583
584 // In principle it is good to have RemoteGIDs and RemotGIDList be as
585 // long as the number of remote GIDs on this processor, but this
586 // would require two passes through the column IDs, so we make it
587 // the max of 100 and the number of block rows.
588 //
589 // FIXME (mfh 11 Feb 2015) Tpetra::Details::HashTable can hold at
590 // most INT_MAX entries, but it's possible to have more rows than
591 // that (if size_t is 64 bits and int is 32 bits).
592 const size_t numMyRows = rowptr.size() - 1;
593 const int hashsize = std::max(static_cast<int>(numMyRows), 100);
594
595 Tpetra::Details::HashTable<GO, LO> RemoteGIDs(hashsize);
596 Teuchos::Array<GO> RemoteGIDList;
597 RemoteGIDList.reserve(hashsize);
598 Teuchos::Array<int> PIDList;
599 PIDList.reserve(hashsize);
600
601 // Here we start using the *LocalOrdinal* colind_LID array. This is
602 // safe even if both columnIndices arrays are actually the same
603 // (because LocalOrdinal==GO). For *local* GID's set
604 // colind_LID with with their LID in the domainMap. For *remote*
605 // GIDs, we set colind_LID with (numDomainElements+NumRemoteColGIDs)
606 // before the increment of the remote count. These numberings will
607 // be separate because no local LID is greater than
608 // numDomainElements.
609
610 size_t NumLocalColGIDs = 0;
611 LO NumRemoteColGIDs = 0;
612 for (size_t i = 0; i < numMyRows; ++i) {
613 for (size_t j = rowptr[i]; j < rowptr[i + 1]; ++j) {
614 const GO GID = colind_GID[j];
615 // Check if GID matches a row GID
616 const LO LID = domainMap.getLocalElement(GID);
617 if (LID != -1) {
618 const bool alreadyFound = LocalGIDs[LID];
619 if (!alreadyFound) {
620 LocalGIDs[LID] = true; // There is a column in the graph associated with this domain map GID
621 NumLocalColGIDs++;
622 }
623 colind_LID[j] = LID;
624 } else {
625 const LO hash_value = RemoteGIDs.get(GID);
626 if (hash_value == -1) { // This means its a new remote GID
627 const int PID = owningPIDs[j];
628 TEUCHOS_TEST_FOR_EXCEPTION(
629 PID == -1, std::invalid_argument, prefix << "Cannot figure out if "
630 "PID is owned.");
631 colind_LID[j] = static_cast<LO>(numDomainElements + NumRemoteColGIDs);
632 RemoteGIDs.add(GID, NumRemoteColGIDs);
633 RemoteGIDList.push_back(GID);
634 PIDList.push_back(PID);
635 NumRemoteColGIDs++;
636 } else {
637 colind_LID[j] = static_cast<LO>(numDomainElements + hash_value);
638 }
639 }
640 }
641 }
642
643 // Possible short-circuit: If all domain map GIDs are present as
644 // column indices, then set ColMap=domainMap and quit.
645 if (domainMap.getComm()->getSize() == 1) {
646 // Sanity check: When there is only one process, there can be no
647 // remoteGIDs.
648 TEUCHOS_TEST_FOR_EXCEPTION(
649 NumRemoteColGIDs != 0, std::runtime_error, prefix << "There is only one "
650 "process in the domain Map's communicator, which means that there are no "
651 "\"remote\" indices. Nevertheless, some column indices are not in the "
652 "domain Map.");
653 if (static_cast<size_t>(NumLocalColGIDs) == numDomainElements) {
654 // In this case, we just use the domainMap's indices, which is,
655 // not coincidently, what we clobbered colind with up above
656 // anyway. No further reindexing is needed.
657 colMap = domainMapRCP;
658 return;
659 }
660 }
661
662 // Now build the array containing column GIDs
663 // Build back end, containing remote GIDs, first
664 const LO numMyCols = NumLocalColGIDs + NumRemoteColGIDs;
665 Teuchos::Array<GO> ColIndices;
666 GO* RemoteColIndices = NULL;
667 if (numMyCols > 0) {
668 ColIndices.resize(numMyCols);
669 if (NumLocalColGIDs != static_cast<size_t>(numMyCols)) {
670 RemoteColIndices = &ColIndices[NumLocalColGIDs]; // Points to back half of ColIndices
671 }
672 }
673
674 for (LO i = 0; i < NumRemoteColGIDs; ++i) {
675 RemoteColIndices[i] = RemoteGIDList[i];
676 }
677
678 // Build permute array for *remote* reindexing.
679 Teuchos::Array<LO> RemotePermuteIDs(NumRemoteColGIDs);
680 for (LO i = 0; i < NumRemoteColGIDs; ++i) {
681 RemotePermuteIDs[i] = i;
682 }
683
684 // Sort External column indices so that all columns coming from a
685 // given remote processor are contiguous. This is a sort with two
686 // auxilary arrays: RemoteColIndices and RemotePermuteIDs.
687 Tpetra::sort3(PIDList.begin(), PIDList.end(),
688 ColIndices.begin() + NumLocalColGIDs,
689 RemotePermuteIDs.begin());
690
691 // Stash the RemotePIDs.
692 //
693 // Note: If Teuchos::Array had a shrink_to_fit like std::vector,
694 // we'd call it here.
695 remotePIDs = PIDList;
696
697 // Sort external column indices so that columns from a given remote
698 // processor are not only contiguous but also in ascending
699 // order. NOTE: I don't know if the number of externals associated
700 // with a given remote processor is known at this point ... so I
701 // count them here.
702
703 // NTS: Only sort the RemoteColIndices this time...
704 LO StartCurrent = 0, StartNext = 1;
705 while (StartNext < NumRemoteColGIDs) {
706 if (PIDList[StartNext] == PIDList[StartNext - 1]) {
707 StartNext++;
708 } else {
709 Tpetra::sort2(ColIndices.begin() + NumLocalColGIDs + StartCurrent,
710 ColIndices.begin() + NumLocalColGIDs + StartNext,
711 RemotePermuteIDs.begin() + StartCurrent);
712 StartCurrent = StartNext;
713 StartNext++;
714 }
715 }
716 Tpetra::sort2(ColIndices.begin() + NumLocalColGIDs + StartCurrent,
717 ColIndices.begin() + NumLocalColGIDs + StartNext,
718 RemotePermuteIDs.begin() + StartCurrent);
719
720 // Reverse the permutation to get the information we actually care about
721 Teuchos::Array<LO> ReverseRemotePermuteIDs(NumRemoteColGIDs);
722 for (LO i = 0; i < NumRemoteColGIDs; ++i) {
723 ReverseRemotePermuteIDs[RemotePermuteIDs[i]] = i;
724 }
725
726 // Build permute array for *local* reindexing.
727 bool use_local_permute = false;
728 Teuchos::Array<LO> LocalPermuteIDs(numDomainElements);
729
730 // Now fill front end. Two cases:
731 //
732 // (1) If the number of Local column GIDs is the same as the number
733 // of Local domain GIDs, we can simply read the domain GIDs into
734 // the front part of ColIndices, otherwise
735 //
736 // (2) We step through the GIDs of the domainMap, checking to see if
737 // each domain GID is a column GID. we want to do this to
738 // maintain a consistent ordering of GIDs between the columns
739 // and the domain.
740 Teuchos::ArrayView<const GO> domainGlobalElements = domainMap.getLocalElementList();
741 if (static_cast<size_t>(NumLocalColGIDs) == numDomainElements) {
742 if (NumLocalColGIDs > 0) {
743 // Load Global Indices into first numMyCols elements column GID list
744 std::copy(domainGlobalElements.begin(), domainGlobalElements.end(),
745 ColIndices.begin());
746 }
747 } else {
748 LO NumLocalAgain = 0;
749 use_local_permute = true;
750 for (size_t i = 0; i < numDomainElements; ++i) {
751 if (LocalGIDs[i]) {
752 LocalPermuteIDs[i] = NumLocalAgain;
753 ColIndices[NumLocalAgain++] = domainGlobalElements[i];
754 }
755 }
756 TEUCHOS_TEST_FOR_EXCEPTION(
757 static_cast<size_t>(NumLocalAgain) != NumLocalColGIDs,
758 std::runtime_error, prefix << "Local ID count test failed.");
759 }
760
761 // Make column Map
762 const GST minus_one = Teuchos::OrdinalTraits<GST>::invalid();
763 colMap = rcp(new map_type(minus_one, ColIndices, domainMap.getIndexBase(),
764 domainMap.getComm()));
765
766 // Low-cost reindex of the matrix
767 for (size_t i = 0; i < numMyRows; ++i) {
768 for (size_t j = rowptr[i]; j < rowptr[i + 1]; ++j) {
769 const LO ID = colind_LID[j];
770 if (static_cast<size_t>(ID) < numDomainElements) {
771 if (use_local_permute) {
772 colind_LID[j] = LocalPermuteIDs[colind_LID[j]];
773 }
774 // In the case where use_local_permute==false, we just copy
775 // the DomainMap's ordering, which it so happens is what we
776 // put in colind_LID to begin with.
777 } else {
778 colind_LID[j] = NumLocalColGIDs + ReverseRemotePermuteIDs[colind_LID[j] - numDomainElements];
779 }
780 }
781 }
782}
783
784template <typename LocalOrdinal, typename GlobalOrdinal, typename Node>
786 const Teuchos::ArrayView<const size_t>& rowptr,
787 const Teuchos::ArrayView<LocalOrdinal>& colind_LID,
788 const Teuchos::ArrayView<GlobalOrdinal>& colind_GID,
789 const Teuchos::RCP<const Tpetra::Map<LocalOrdinal, GlobalOrdinal, Node>>& domainMapRCP,
790 const Teuchos::ArrayView<const int>& owningPIDs,
791 Teuchos::Array<int>& remotePIDs,
792 Teuchos::RCP<const Tpetra::Map<LocalOrdinal, GlobalOrdinal, Node>>& colMap) {
793 using Teuchos::rcp;
794 typedef LocalOrdinal LO;
795 typedef GlobalOrdinal GO;
796 typedef Tpetra::global_size_t GST;
798 const char prefix[] = "lowCommunicationMakeColMapAndReindex: ";
799
800 typedef typename Node::device_type DT;
801 using execution_space = typename DT::execution_space;
802 execution_space exec;
803 using team_policy = Kokkos::TeamPolicy<execution_space, Kokkos::Schedule<Kokkos::Dynamic>>;
804 typedef typename map_type::local_map_type local_map_type;
805
806 // Create device mirror and host mirror views from function parameters
807 // When we pass in views instead of Teuchos::ArrayViews, we can avoid copying views
808 auto colind_LID_view = Details::create_mirror_view_from_raw_host_array(exec, colind_LID.getRawPtr(), colind_LID.size(), true, "colind_LID");
809 auto rowptr_view = Details::create_mirror_view_from_raw_host_array(exec, rowptr.getRawPtr(), rowptr.size(), true, "rowptr");
810 auto colind_GID_view = Details::create_mirror_view_from_raw_host_array(exec, colind_GID.getRawPtr(), colind_GID.size(), true, "colind_GID");
811 auto owningPIDs_view = Details::create_mirror_view_from_raw_host_array(exec, owningPIDs.getRawPtr(), owningPIDs.size(), true, "owningPIDs");
812
813 typename decltype(colind_LID_view)::host_mirror_type colind_LID_host(colind_LID.getRawPtr(), colind_LID.size());
814 typename decltype(colind_GID_view)::host_mirror_type colind_GID_host(colind_GID.getRawPtr(), colind_GID.size());
815
816 Kokkos::deep_copy(colind_LID_view, colind_LID_host);
817 Kokkos::deep_copy(colind_GID_view, colind_GID_host);
818
819 // The domainMap is an RCP because there is a shortcut for a
820 // (common) special case to return the columnMap = domainMap.
821 const map_type& domainMap = *domainMapRCP;
822
823 Kokkos::UnorderedMap<LO, bool, DT> LocalGIDs_view_map(colind_LID.size());
824 Kokkos::UnorderedMap<GO, LO, DT> RemoteGIDs_view_map(colind_LID.size());
825
826 const size_t numMyRows = rowptr.size() - 1;
827 local_map_type domainMap_local = domainMap.getLocalMap();
828
829 const size_t numDomainElements = domainMap.getLocalNumElements();
830 Kokkos::View<bool*, DT> LocalGIDs_view("LocalGIDs", numDomainElements);
831 auto LocalGIDs_host = Kokkos::create_mirror_view(LocalGIDs_view);
832
833 size_t NumLocalColGIDs = 0;
834
835 // Scan all column indices and sort into two groups:
836 // Local: those whose GID matches a GID of the domain map on this processor and
837 // Remote: All others.
838 // Kokkos::Parallel_reduce sums up NumLocalColGIDs, while we use the size of the Remote GIDs map to find NumRemoteColGIDs
839 Kokkos::parallel_reduce(
840 team_policy(numMyRows, Kokkos::AUTO), KOKKOS_LAMBDA(const typename team_policy::member_type& member, size_t& update) {
841 const int i = member.league_rank();
842 size_t NumLocalColGIDs_temp = 0;
843 size_t rowptr_start = rowptr_view[i];
844 size_t rowptr_end = rowptr_view[i + 1];
845 Kokkos::parallel_reduce(
846 Kokkos::TeamThreadRange(member, rowptr_start, rowptr_end), [&](const size_t j, size_t& innerUpdate) {
847 const GO GID = colind_GID_view[j];
848 // Check if GID matches a row GID in local domain map
849 const LO LID = domainMap_local.getLocalElement(GID);
850 if (LID != -1) {
851 auto outcome = LocalGIDs_view_map.insert(LID);
852 // Fresh insert
853 if (outcome.success()) {
854 LocalGIDs_view[LID] = true;
855 innerUpdate++;
856 }
857 } else {
858 const int PID = owningPIDs_view[j];
859 auto outcome = RemoteGIDs_view_map.insert(GID, PID);
860 if (outcome.success() && PID == -1) {
861 Kokkos::abort("Cannot figure out if ID is owned.\n");
862 }
863 }
864 },
865 NumLocalColGIDs_temp);
866 if (member.team_rank() == 0) update += NumLocalColGIDs_temp;
867 },
868 NumLocalColGIDs);
869
870 LO NumRemoteColGIDs = RemoteGIDs_view_map.size();
871
872 Kokkos::View<int*, DT> PIDList_view("PIDList", NumRemoteColGIDs);
873 auto PIDList_host = Kokkos::create_mirror_view(PIDList_view);
874
875 Kokkos::View<GO*, DT> RemoteGIDList_view("RemoteGIDList", NumRemoteColGIDs);
876 auto RemoteGIDList_host = Kokkos::create_mirror_view(RemoteGIDList_view);
877
878 // For each index in RemoteGIDs_map that contains a GID, use "update" to indicate the number of GIDs "before" this GID
879 // This maps each element in the RemoteGIDs hash table to an index in RemoteGIDList / PIDList without any overwriting or empty spaces between indices
880 Kokkos::parallel_scan(
881 Kokkos::RangePolicy<execution_space>(0, RemoteGIDs_view_map.capacity()), KOKKOS_LAMBDA(const int i, GO& update, const bool final) {
882 if (final && RemoteGIDs_view_map.valid_at(i)) {
883 RemoteGIDList_view[update] = RemoteGIDs_view_map.key_at(i);
884 PIDList_view[update] = RemoteGIDs_view_map.value_at(i);
885 }
886 if (RemoteGIDs_view_map.valid_at(i)) {
887 update += 1;
888 }
889 });
890
891 // Possible short-circuit: If all domain map GIDs are present as
892 // column indices, then set ColMap=domainMap and quit.
893 if (domainMap.getComm()->getSize() == 1) {
894 // Sanity check: When there is only one process, there can be no
895 // remoteGIDs.
896 TEUCHOS_TEST_FOR_EXCEPTION(
897 NumRemoteColGIDs != 0, std::runtime_error, prefix << "There is only one "
898 "process in the domain Map's communicator, which means that there are no "
899 "\"remote\" indices. Nevertheless, some column indices are not in the "
900 "domain Map.");
901 if (static_cast<size_t>(NumLocalColGIDs) == numDomainElements) {
902 // In this case, we just use the domainMap's indices, which is,
903 // not coincidently, what we clobbered colind with up above
904 // anyway. No further reindexing is needed.
905 colMap = domainMapRCP;
906
907 // Fill out local colMap (which should only contain local GIDs)
908 auto localColMap = colMap->getLocalMap();
909 Kokkos::parallel_for(
910 Kokkos::RangePolicy<execution_space>(0, colind_GID.size()), KOKKOS_LAMBDA(const int i) {
911 colind_LID_view[i] = localColMap.getLocalElement(colind_GID_view[i]);
912 });
913 Kokkos::deep_copy(execution_space(), colind_LID_host, colind_LID_view);
914 return;
915 }
916 }
917
918 // Now build the array containing column GIDs
919 // Build back end, containing remote GIDs, first
920 const LO numMyCols = NumLocalColGIDs + NumRemoteColGIDs;
921 Kokkos::View<GO*, DT> ColIndices_view("ColIndices", numMyCols);
922
923 // We don't need to load the backend of ColIndices or sort if there are no remote GIDs
924 if (NumRemoteColGIDs > 0) {
925 if (NumLocalColGIDs != static_cast<size_t>(numMyCols)) {
926 Kokkos::parallel_for(
927 Kokkos::RangePolicy<execution_space>(0, NumRemoteColGIDs), KOKKOS_LAMBDA(const int i) {
928 ColIndices_view[NumLocalColGIDs + i] = RemoteGIDList_view[i];
929 });
930 }
931
932 // Find the largest PID for bin sorting purposes
933 int PID_max = 0;
934 Kokkos::parallel_reduce(
935 Kokkos::RangePolicy<execution_space>(0, PIDList_host.size()), KOKKOS_LAMBDA(const int i, int& max) {
936 if (max < PIDList_view[i]) max = PIDList_view[i];
937 },
938 Kokkos::Max<int>(PID_max));
939
940 using KeyViewTypePID = decltype(PIDList_view);
941 using BinSortOpPID = Kokkos::BinOp1D<KeyViewTypePID>;
942
943 // Make a subview of ColIndices for remote GID sorting
944 auto ColIndices_subview = Kokkos::subview(ColIndices_view, Kokkos::make_pair(NumLocalColGIDs, ColIndices_view.size()));
945
946 // Make binOp with bins = PID_max + 1, min = 0, max = PID_max
947 BinSortOpPID binOp2(PID_max + 1, 0, PID_max);
948
949 // Sort External column indices so that all columns coming from a
950 // given remote processor are contiguous. This is a sort with one
951 // auxilary array: RemoteColIndices
952 Kokkos::BinSort<KeyViewTypePID, BinSortOpPID> bin_sort2(PIDList_view, 0, PIDList_view.size(), binOp2, false);
953 bin_sort2.create_permute_vector(exec);
954 bin_sort2.sort(exec, PIDList_view);
955 bin_sort2.sort(exec, ColIndices_subview);
956
957 // Deep copy back from device to host
958 Kokkos::deep_copy(exec, PIDList_host, PIDList_view);
959
960 // Stash the RemotePIDs. Once remotePIDs is changed to become a Kokkos view, we can remove this and copy directly.
961 // Note: If Teuchos::Array had a shrink_to_fit like std::vector,
962 // we'd call it here.
963
964 exec.fence("fence before setting PIDList");
965 Teuchos::Array<int> PIDList(NumRemoteColGIDs);
966 for (LO i = 0; i < NumRemoteColGIDs; ++i) {
967 PIDList[i] = PIDList_host[i];
968 }
969
970 remotePIDs = PIDList;
971
972 // Sort external column indices so that columns from a given remote
973 // processor are not only contiguous but also in ascending
974 // order. NOTE: I don't know if the number of externals associated
975 // with a given remote processor is known at this point ... so I
976 // count them here.
977 LO StartCurrent = 0, StartNext = 1;
978 while (StartNext < NumRemoteColGIDs) {
979 if (PIDList_host[StartNext] == PIDList_host[StartNext - 1]) {
980 StartNext++;
981 } else {
982 Kokkos::sort(ColIndices_view, NumLocalColGIDs + StartCurrent, NumLocalColGIDs + StartNext);
983 StartCurrent = StartNext;
984 StartNext++;
985 }
986 }
987
988 Kokkos::sort(ColIndices_view, NumLocalColGIDs + StartCurrent, NumLocalColGIDs + StartNext);
989 }
990
991 // Build permute array for *local* reindexing.
992
993 // Now fill front end. Two cases:
994 //
995 // (1) If the number of Local column GIDs is the same as the number
996 // of Local domain GIDs, we can simply read the domain GIDs into
997 // the front part of ColIndices, otherwise
998 //
999 // (2) We step through the GIDs of the domainMap, checking to see if
1000 // each domain GID is a column GID. we want to do this to
1001 // maintain a consistent ordering of GIDs between the columns
1002 // and the domain.
1003 if (static_cast<size_t>(NumLocalColGIDs) == numDomainElements) {
1004 if (NumLocalColGIDs > 0) {
1005 // Load Global Indices into first numMyCols elements column GID list
1006 Kokkos::parallel_for(
1007 Kokkos::RangePolicy<execution_space>(0, numDomainElements), KOKKOS_LAMBDA(const int i) {
1008 ColIndices_view[i] = domainMap_local.getGlobalElement(i);
1009 });
1010 }
1011 } else {
1012 // This part isn't actually tested in the unit tests
1013 LO NumLocalAgain = 0;
1014 Kokkos::parallel_scan(
1015 Kokkos::RangePolicy<execution_space>(0, numDomainElements), KOKKOS_LAMBDA(const int i, LO& update, const bool final) {
1016 if (final && LocalGIDs_view[i]) {
1017 ColIndices_view[update] = domainMap_local.getGlobalElement(i);
1018 }
1019 if (LocalGIDs_view[i]) {
1020 update++;
1021 }
1022 },
1023 NumLocalAgain);
1024
1025 TEUCHOS_TEST_FOR_EXCEPTION(
1026 static_cast<size_t>(NumLocalAgain) != NumLocalColGIDs,
1027 std::runtime_error, prefix << "Local ID count test failed.");
1028 }
1029
1030 // Make column Map
1031 const GST minus_one = Teuchos::OrdinalTraits<GST>::invalid();
1032
1033 colMap = rcp(new map_type(minus_one, ColIndices_view, domainMap.getIndexBase(),
1034 domainMap.getComm()));
1035
1036 // Fill out colind_LID using local map
1037 auto localColMap = colMap->getLocalMap();
1038 Kokkos::parallel_for(
1039 Kokkos::RangePolicy<execution_space>(0, colind_GID.size()), KOKKOS_LAMBDA(const int i) {
1040 colind_LID_view[i] = localColMap.getLocalElement(colind_GID_view[i]);
1041 });
1042
1043 // For now, we copy back into colind_LID_host (which also overwrites the colind_LID Tuechos array)
1044 // When colind_LID becomes a Kokkos View we can delete this
1045 Kokkos::deep_copy(exec, colind_LID_host, colind_LID_view);
1046}
1047
1048template <typename LocalOrdinal, typename GlobalOrdinal, typename Node>
1049void lowCommunicationMakeColMapAndReindex(
1050 const Kokkos::View<size_t*, typename Node::device_type> rowptr_view,
1051 const Kokkos::View<LocalOrdinal*, typename Node::device_type> colind_LID_view,
1052 const Kokkos::View<GlobalOrdinal*, typename Node::device_type> colind_GID_view,
1053 const Teuchos::RCP<const Tpetra::Map<LocalOrdinal, GlobalOrdinal, Node>>& domainMapRCP,
1054 const Kokkos::View<int*, typename Node::device_type> owningPIDs_view,
1055 Teuchos::Array<int>& remotePIDs,
1056 Teuchos::RCP<const Tpetra::Map<LocalOrdinal, GlobalOrdinal, Node>>& colMap) {
1057 using Teuchos::rcp;
1058 typedef LocalOrdinal LO;
1059 typedef GlobalOrdinal GO;
1060 typedef Tpetra::global_size_t GST;
1062 const char prefix[] = "lowCommunicationMakeColMapAndReindex: ";
1063
1064 typedef typename Node::device_type DT;
1065 using execution_space = typename DT::execution_space;
1066 execution_space exec;
1067 using team_policy = Kokkos::TeamPolicy<execution_space, Kokkos::Schedule<Kokkos::Dynamic>>;
1068 typedef typename map_type::local_map_type local_map_type;
1069
1070 // The domainMap is an RCP because there is a shortcut for a
1071 // (common) special case to return the columnMap = domainMap.
1072 const map_type& domainMap = *domainMapRCP;
1073
1074 Kokkos::UnorderedMap<LO, bool, DT> LocalGIDs_view_map(colind_LID_view.size());
1075 Kokkos::UnorderedMap<GO, LO, DT> RemoteGIDs_view_map(colind_LID_view.size());
1076
1077 const size_t numMyRows = rowptr_view.size() - 1;
1078 local_map_type domainMap_local = domainMap.getLocalMap();
1079
1080 const size_t numDomainElements = domainMap.getLocalNumElements();
1081 Kokkos::View<bool*, DT> LocalGIDs_view("LocalGIDs", numDomainElements);
1082 auto LocalGIDs_host = Kokkos::create_mirror_view(LocalGIDs_view);
1083
1084 size_t NumLocalColGIDs = 0;
1085
1086 // Scan all column indices and sort into two groups:
1087 // Local: those whose GID matches a GID of the domain map on this processor and
1088 // Remote: All others.
1089 // Kokkos::Parallel_reduce sums up NumLocalColGIDs, while we use the size of the Remote GIDs map to find NumRemoteColGIDs
1090 Kokkos::parallel_reduce(
1091 team_policy(numMyRows, Kokkos::AUTO), KOKKOS_LAMBDA(const typename team_policy::member_type& member, size_t& update) {
1092 const int i = member.league_rank();
1093 size_t NumLocalColGIDs_temp = 0;
1094 size_t rowptr_start = rowptr_view[i];
1095 size_t rowptr_end = rowptr_view[i + 1];
1096 Kokkos::parallel_reduce(
1097 Kokkos::TeamThreadRange(member, rowptr_start, rowptr_end), [&](const size_t j, size_t& innerUpdate) {
1098 const GO GID = colind_GID_view[j];
1099 // Check if GID matches a row GID in local domain map
1100 const LO LID = domainMap_local.getLocalElement(GID);
1101 if (LID != -1) {
1102 auto outcome = LocalGIDs_view_map.insert(LID);
1103 // Fresh insert
1104 if (outcome.success()) {
1105 LocalGIDs_view[LID] = true;
1106 innerUpdate++;
1107 }
1108 } else {
1109 const int PID = owningPIDs_view[j];
1110 auto outcome = RemoteGIDs_view_map.insert(GID, PID);
1111 if (outcome.success() && PID == -1) {
1112 Kokkos::abort("Cannot figure out if ID is owned.\n");
1113 }
1114 }
1115 },
1116 NumLocalColGIDs_temp);
1117 if (member.team_rank() == 0) update += NumLocalColGIDs_temp;
1118 },
1119 NumLocalColGIDs);
1120
1121 LO NumRemoteColGIDs = RemoteGIDs_view_map.size();
1122
1123 Kokkos::View<int*, DT> PIDList_view("PIDList_d", NumRemoteColGIDs);
1124
1125 Kokkos::View<GO*, DT> RemoteGIDList_view("RemoteGIDList", NumRemoteColGIDs);
1126 auto RemoteGIDList_host = Kokkos::create_mirror_view(RemoteGIDList_view);
1127
1128 // For each index in RemoteGIDs_map that contains a GID, use "update" to indicate the number of GIDs "before" this GID
1129 // This maps each element in the RemoteGIDs hash table to an index in RemoteGIDList / PIDList without any overwriting or empty spaces between indices
1130 Kokkos::parallel_scan(
1131 Kokkos::RangePolicy<execution_space>(0, RemoteGIDs_view_map.capacity()), KOKKOS_LAMBDA(const int i, GO& update, const bool final) {
1132 if (final && RemoteGIDs_view_map.valid_at(i)) {
1133 RemoteGIDList_view[update] = RemoteGIDs_view_map.key_at(i);
1134 PIDList_view[update] = RemoteGIDs_view_map.value_at(i);
1135 }
1136 if (RemoteGIDs_view_map.valid_at(i)) {
1137 update += 1;
1138 }
1139 });
1140
1141 // Possible short-circuit: If all domain map GIDs are present as
1142 // column indices, then set ColMap=domainMap and quit.
1143 if (domainMap.getComm()->getSize() == 1) {
1144 // Sanity check: When there is only one process, there can be no
1145 // remoteGIDs.
1146 TEUCHOS_TEST_FOR_EXCEPTION(
1147 NumRemoteColGIDs != 0, std::runtime_error, prefix << "There is only one "
1148 "process in the domain Map's communicator, which means that there are no "
1149 "\"remote\" indices. Nevertheless, some column indices are not in the "
1150 "domain Map.");
1151 if (static_cast<size_t>(NumLocalColGIDs) == numDomainElements) {
1152 // In this case, we just use the domainMap's indices, which is,
1153 // not coincidently, what we clobbered colind with up above
1154 // anyway. No further reindexing is needed.
1155 colMap = domainMapRCP;
1156
1157 // Fill out local colMap (which should only contain local GIDs)
1158 auto localColMap = colMap->getLocalMap();
1159 Kokkos::parallel_for(
1160 Kokkos::RangePolicy<execution_space>(0, colind_GID_view.size()), KOKKOS_LAMBDA(const int i) {
1161 colind_LID_view[i] = localColMap.getLocalElement(colind_GID_view[i]);
1162 });
1163 return;
1164 }
1165 }
1166
1167 // Now build the array containing column GIDs
1168 // Build back end, containing remote GIDs, first
1169 const LO numMyCols = NumLocalColGIDs + NumRemoteColGIDs;
1170 Kokkos::View<GO*, DT> ColIndices_view("ColIndices", numMyCols);
1171
1172 // We don't need to load the backend of ColIndices or sort if there are no remote GIDs
1173 if (NumRemoteColGIDs > 0) {
1174 if (NumLocalColGIDs != static_cast<size_t>(numMyCols)) {
1175 Kokkos::parallel_for(
1176 Kokkos::RangePolicy<execution_space>(0, NumRemoteColGIDs), KOKKOS_LAMBDA(const int i) {
1177 ColIndices_view[NumLocalColGIDs + i] = RemoteGIDList_view[i];
1178 });
1179 }
1180
1181 // Find the largest PID for bin sorting purposes
1182 int PID_max = 0;
1183 Kokkos::parallel_reduce(
1184 Kokkos::RangePolicy<execution_space>(0, PIDList_view.size()), KOKKOS_LAMBDA(const int i, int& max) {
1185 if (max < PIDList_view[i]) max = PIDList_view[i];
1186 },
1187 Kokkos::Max<int>(PID_max));
1188
1189 using KeyViewTypePID = decltype(PIDList_view);
1190 using BinSortOpPID = Kokkos::BinOp1D<KeyViewTypePID>;
1191
1192 // Make a subview of ColIndices for remote GID sorting
1193 auto ColIndices_subview = Kokkos::subview(ColIndices_view, Kokkos::make_pair(NumLocalColGIDs, ColIndices_view.size()));
1194
1195 // Make binOp with bins = PID_max + 1, min = 0, max = PID_max
1196 BinSortOpPID binOp2(PID_max + 1, 0, PID_max);
1197
1198 // Sort External column indices so that all columns coming from a
1199 // given remote processor are contiguous. This is a sort with one
1200 // auxilary array: RemoteColIndices
1201 Kokkos::BinSort<KeyViewTypePID, BinSortOpPID> bin_sort2(PIDList_view, 0, PIDList_view.size(), binOp2, false);
1202 bin_sort2.create_permute_vector(exec);
1203 bin_sort2.sort(exec, PIDList_view);
1204 bin_sort2.sort(exec, ColIndices_subview);
1205
1206 // Deep copy back from device to host
1207 // Stash the RemotePIDs. Once remotePIDs is changed to become a Kokkos view, we can remove this and copy directly.
1208 Teuchos::Array<int> PIDList(NumRemoteColGIDs);
1209 Kokkos::View<int*, Kokkos::HostSpace> PIDList_host(PIDList.data(), PIDList.size());
1210 Kokkos::deep_copy(exec, PIDList_host, PIDList_view);
1211 exec.fence();
1212
1213 remotePIDs = PIDList;
1214
1215 // Sort external column indices so that columns from a given remote
1216 // processor are not only contiguous but also in ascending
1217 // order. NOTE: I don't know if the number of externals associated
1218 // with a given remote processor is known at this point ... so I
1219 // count them here.
1220 LO StartCurrent = 0, StartNext = 1;
1221 while (StartNext < NumRemoteColGIDs) {
1222 if (PIDList_host[StartNext] == PIDList_host[StartNext - 1]) {
1223 StartNext++;
1224 } else {
1225 Kokkos::sort(ColIndices_view, NumLocalColGIDs + StartCurrent, NumLocalColGIDs + StartNext);
1226 StartCurrent = StartNext;
1227 StartNext++;
1228 }
1229 }
1230
1231 Kokkos::sort(ColIndices_view, NumLocalColGIDs + StartCurrent, NumLocalColGIDs + StartNext);
1232 }
1233
1234 // Build permute array for *local* reindexing.
1235
1236 // Now fill front end. Two cases:
1237 //
1238 // (1) If the number of Local column GIDs is the same as the number
1239 // of Local domain GIDs, we can simply read the domain GIDs into
1240 // the front part of ColIndices, otherwise
1241 //
1242 // (2) We step through the GIDs of the domainMap, checking to see if
1243 // each domain GID is a column GID. we want to do this to
1244 // maintain a consistent ordering of GIDs between the columns
1245 // and the domain.
1246 if (static_cast<size_t>(NumLocalColGIDs) == numDomainElements) {
1247 if (NumLocalColGIDs > 0) {
1248 // Load Global Indices into first numMyCols elements column GID list
1249 Kokkos::parallel_for(
1250 Kokkos::RangePolicy<execution_space>(0, numDomainElements), KOKKOS_LAMBDA(const int i) {
1251 ColIndices_view[i] = domainMap_local.getGlobalElement(i);
1252 });
1253 }
1254 } else {
1255 // This part isn't actually tested in the unit tests
1256 LO NumLocalAgain = 0;
1257 Kokkos::parallel_scan(
1258 Kokkos::RangePolicy<execution_space>(0, numDomainElements), KOKKOS_LAMBDA(const int i, LO& update, const bool final) {
1259 if (final && LocalGIDs_view[i]) {
1260 ColIndices_view[update] = domainMap_local.getGlobalElement(i);
1261 }
1262 if (LocalGIDs_view[i]) {
1263 update++;
1264 }
1265 },
1266 NumLocalAgain);
1267
1268 TEUCHOS_TEST_FOR_EXCEPTION(
1269 static_cast<size_t>(NumLocalAgain) != NumLocalColGIDs,
1270 std::runtime_error, prefix << "Local ID count test failed.");
1271 }
1272
1273 // Make column Map
1274 const GST minus_one = Teuchos::OrdinalTraits<GST>::invalid();
1275
1276 colMap = rcp(new map_type(minus_one, ColIndices_view, domainMap.getIndexBase(),
1277 domainMap.getComm()));
1278
1279 // Fill out colind_LID using local map
1280 auto localColMap = colMap->getLocalMap();
1281 Kokkos::parallel_for(
1282 Kokkos::RangePolicy<execution_space>(0, colind_GID_view.size()), KOKKOS_LAMBDA(const int i) {
1283 colind_LID_view[i] = localColMap.getLocalElement(colind_GID_view[i]);
1284 });
1285}
1286
1287// Generates an list of owning PIDs based on two transfer (aka import/export objects)
1288// Let:
1289// OwningMap = useReverseModeForOwnership ? transferThatDefinesOwnership.getTargetMap() : transferThatDefinesOwnership.getSourceMap();
1290// MapAo = useReverseModeForOwnership ? transferThatDefinesOwnership.getSourceMap() : transferThatDefinesOwnership.getTargetMap();
1291// MapAm = useReverseModeForMigration ? transferThatDefinesMigration.getTargetMap() : transferThatDefinesMigration.getSourceMap();
1292// VectorMap = useReverseModeForMigration ? transferThatDefinesMigration.getSourceMap() : transferThatDefinesMigration.getTargetMap();
1293// Precondition:
1294// 1) MapAo.isSameAs(*MapAm) - map compatibility between transfers
1295// 2) VectorMap->isSameAs(*owningPIDs->getMap()) - map compabibility between transfer & vector
1296// 3) OwningMap->isOneToOne() - owning map is 1-to-1
1297// --- Precondition 3 is only checked in DEBUG mode ---
1298// Postcondition:
1299// owningPIDs[VectorMap->getLocalElement(GID i)] = j iff (OwningMap->isLocalElement(GID i) on rank j)
1300template <typename LocalOrdinal, typename GlobalOrdinal, typename Node>
1301void getTwoTransferOwnershipVector(const ::Tpetra::Details::Transfer<LocalOrdinal, GlobalOrdinal, Node>& transferThatDefinesOwnership,
1302 bool useReverseModeForOwnership,
1303 const ::Tpetra::Details::Transfer<LocalOrdinal, GlobalOrdinal, Node>& transferThatDefinesMigration,
1304 bool useReverseModeForMigration,
1308
1309 Teuchos::RCP<const Tpetra::Map<LocalOrdinal, GlobalOrdinal, Node>> OwningMap = useReverseModeForOwnership ? transferThatDefinesOwnership.getTargetMap() : transferThatDefinesOwnership.getSourceMap();
1310 Teuchos::RCP<const Tpetra::Map<LocalOrdinal, GlobalOrdinal, Node>> MapAo = useReverseModeForOwnership ? transferThatDefinesOwnership.getSourceMap() : transferThatDefinesOwnership.getTargetMap();
1311 Teuchos::RCP<const Tpetra::Map<LocalOrdinal, GlobalOrdinal, Node>> MapAm = useReverseModeForMigration ? transferThatDefinesMigration.getTargetMap() : transferThatDefinesMigration.getSourceMap();
1312 Teuchos::RCP<const Tpetra::Map<LocalOrdinal, GlobalOrdinal, Node>> VectorMap = useReverseModeForMigration ? transferThatDefinesMigration.getSourceMap() : transferThatDefinesMigration.getTargetMap();
1313
1314 TEUCHOS_TEST_FOR_EXCEPTION(!MapAo->isSameAs(*MapAm), std::runtime_error, "Tpetra::Import_Util::getTwoTransferOwnershipVector map mismatch between transfers");
1315 TEUCHOS_TEST_FOR_EXCEPTION(!VectorMap->isSameAs(*owningPIDs.getMap()), std::runtime_error, "Tpetra::Import_Util::getTwoTransferOwnershipVector map mismatch transfer and vector");
1316#ifdef HAVE_TPETRA_DEBUG
1317 TEUCHOS_TEST_FOR_EXCEPTION(!OwningMap->isOneToOne(), std::runtime_error, "Tpetra::Import_Util::getTwoTransferOwnershipVector owner must be 1-to-1");
1318#endif
1319
1320 int rank = OwningMap->getComm()->getRank();
1321 // Generate "A" vector and fill it with owning information. We can read this from transferThatDefinesOwnership w/o communication
1322 // Note: Due to the 1-to-1 requirement, several of these options throw
1324 const import_type* ownAsImport = dynamic_cast<const import_type*>(&transferThatDefinesOwnership);
1325 const export_type* ownAsExport = dynamic_cast<const export_type*>(&transferThatDefinesOwnership);
1326
1327 Teuchos::ArrayRCP<int> pids = temp.getDataNonConst();
1328 Teuchos::ArrayView<int> v_pids = pids();
1329 if (ownAsImport && useReverseModeForOwnership) {
1330 TEUCHOS_TEST_FOR_EXCEPTION(1, std::runtime_error, "Tpetra::Import_Util::getTwoTransferOwnershipVector owner must be 1-to-1");
1331 } else if (ownAsImport && !useReverseModeForOwnership)
1332 getPids(*ownAsImport, v_pids, false);
1333 else if (ownAsExport && useReverseModeForMigration) {
1334 TEUCHOS_TEST_FOR_EXCEPTION(1, std::runtime_error, "Tpetra::Import_Util::getTwoTransferOwnershipVector this option not yet implemented");
1335 } else {
1336 TEUCHOS_TEST_FOR_EXCEPTION(1, std::runtime_error, "Tpetra::Import_Util::getTwoTransferOwnershipVector owner must be 1-to-1");
1337 }
1338
1339 const import_type* xferAsImport = dynamic_cast<const import_type*>(&transferThatDefinesMigration);
1340 const export_type* xferAsExport = dynamic_cast<const export_type*>(&transferThatDefinesMigration);
1341 TEUCHOS_TEST_FOR_EXCEPTION(!xferAsImport && !xferAsExport, std::runtime_error, "Tpetra::Import_Util::getTwoTransferOwnershipVector transfer undefined");
1342
1343 // Migrate from "A" vector to output vector
1344 owningPIDs.putScalar(rank);
1345 if (xferAsImport && useReverseModeForMigration)
1346 owningPIDs.doExport(temp, *xferAsImport, Tpetra::REPLACE);
1347 else if (xferAsImport && !useReverseModeForMigration)
1348 owningPIDs.doImport(temp, *xferAsImport, Tpetra::REPLACE);
1349 else if (xferAsExport && useReverseModeForMigration)
1350 owningPIDs.doImport(temp, *xferAsExport, Tpetra::REPLACE);
1351 else
1352 owningPIDs.doExport(temp, *xferAsExport, Tpetra::REPLACE);
1353}
1354
1355} // namespace Import_Util
1356} // namespace Tpetra
1357
1358#endif // TPETRA_IMPORT_UTIL_HPP
Declaration of the Tpetra::CrsMatrix class.
Add specializations of Teuchos::Details::MpiTypeTraits for Kokkos::complex<float> and Kokkos::complex...
Functions that wrap Kokkos::create_mirror_view, in order to avoid deep copies when not necessary,...
Declaration and definition of Tpetra::Details::reallocDualViewIfNeeded, an implementation detail of T...
void lowCommunicationMakeColMapAndReindex(const Teuchos::ArrayView< const size_t > &rowptr, const Teuchos::ArrayView< LocalOrdinal > &colind_LID, const Teuchos::ArrayView< GlobalOrdinal > &colind_GID, const Teuchos::RCP< const Tpetra::Map< LocalOrdinal, GlobalOrdinal, Node > > &domainMapRCP, const Teuchos::ArrayView< const int > &owningPIDs, Teuchos::Array< int > &remotePIDs, Teuchos::RCP< const Tpetra::Map< LocalOrdinal, GlobalOrdinal, Node > > &colMap)
lowCommunicationMakeColMapAndReindex
void getTwoTransferOwnershipVector(const ::Tpetra::Details::Transfer< LocalOrdinal, GlobalOrdinal, Node > &transferThatDefinesOwnership, bool useReverseModeForOwnership, const ::Tpetra::Details::Transfer< LocalOrdinal, GlobalOrdinal, Node > &transferForMigratingData, bool useReverseModeForMigration, Tpetra::Vector< int, LocalOrdinal, GlobalOrdinal, Node > &owningPIDs)
Generates an list of owning PIDs based on two transfer (aka import/export objects) Let: OwningMap = u...
void sortAndMergeCrsEntries(const Teuchos::ArrayView< size_t > &CRS_rowptr, const Teuchos::ArrayView< Ordinal > &CRS_colind, const Teuchos::ArrayView< Scalar > &CRS_vals)
Sort and merge the entries of the (raw CSR) matrix by column index within each row.
void sortCrsEntries(const Teuchos::ArrayView< size_t > &CRS_rowptr, const Teuchos::ArrayView< Ordinal > &CRS_colind, const Teuchos::ArrayView< Scalar > &CRS_vals)
Sort the entries of the (raw CSR) matrix by column index within each row.
void getPids(const Tpetra::Import< LocalOrdinal, GlobalOrdinal, Node > &Importer, Teuchos::Array< int > &pids, bool use_minus_one_for_local)
Like getPidGidPairs, but just gets the PIDs, ordered by the column Map.
Stand-alone utility functions and macros.
Teuchos::RCP< const Teuchos::Comm< int > > getComm() const override
The communicator over which the matrix is distributed.
GlobalOrdinal getIndexBase() const override
The index base for global indices for this matrix.
Teuchos::ArrayView< const LO > getExportLIDs() const
List of entries in the source Map that will be sent to other processes.
size_t getNumExportIDs() const
Number of entries that must be sent by the calling process to other processes.
Teuchos::ArrayView< const int > getExportPIDs() const
List of processes to which entries will be sent.
void doImport(const SrcDistObject &source, const Import< LocalOrdinal, GlobalOrdinal, Node > &importer, const CombineMode CM, const bool restrictedMode=false)
Import data into this object using an Import object ("forward mode").
void doExport(const SrcDistObject &source, const Export< LocalOrdinal, GlobalOrdinal, Node > &exporter, const CombineMode CM, const bool restrictedMode=false)
Export data into this object using an Export object ("forward mode").
virtual Teuchos::RCP< const map_type > getMap() const
The Map describing the parallel distribution of this object.
Communication plan for data redistribution from a (possibly) multiply-owned to a uniquely-owned distr...
Communication plan for data redistribution from a uniquely-owned to a (possibly) multiply-owned distr...
A parallel distribution of indices over processes.
void putScalar(const Scalar &value)
Set all values in the multivector with the given value.
A distributed dense vector.
Teuchos::ArrayRCP< Scalar > getDataNonConst()
View of the local values of this vector.
Impl::CreateMirrorViewFromUnmanagedHostArray< ValueType, OutputDeviceType >::output_view_type create_mirror_view_from_raw_host_array(const OutputDeviceType &, ValueType *inPtr, const size_t inSize, const bool copy=true, const char label[]="")
Variant of Kokkos::create_mirror_view that takes a raw host 1-d array as input.
Namespace Tpetra contains the class and methods constituting the Tpetra library.
void sort2(const IT1 &first1, const IT1 &last1, const IT2 &first2, const bool stableSort=false)
Sort the first array, and apply the resulting permutation to the second array.
size_t global_size_t
Global size_t object.
void sort3(const IT1 &first1, const IT1 &last1, const IT2 &first2, const IT3 &first3, const bool stableSort=false)
Sort the first array, and apply the same permutation to the second and third arrays.
@ REPLACE
Replace existing values with new values.