Tpetra parallel linear algebra Version of the Day
Loading...
Searching...
No Matches
Tpetra_DirectoryImpl_def.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_DIRECTORYIMPL_DEF_HPP
11#define TPETRA_DIRECTORYIMPL_DEF_HPP
12
15
16#include "Teuchos_TestForException.hpp"
17#include "Tpetra_Distributor.hpp"
18#include "Tpetra_Map.hpp"
19#include "Tpetra_TieBreak.hpp"
20#include "Tpetra_Util.hpp"
21#include "Tpetra_Details_FixedHashTable.hpp"
22#include "Teuchos_Comm.hpp"
23#include <memory>
24#include <sstream>
25
26// FIXME (mfh 16 Apr 2013) GIANT HACK BELOW
27#ifdef HAVE_TPETRACORE_MPI
28#include <mpi.h>
29#endif // HAVE_TPETRACORE_MPI
30// FIXME (mfh 16 Apr 2013) GIANT HACK ABOVE
31
32namespace Tpetra {
33namespace Details {
34template <class LO, class GO, class NT>
38 const Teuchos::ArrayView<const GO>& globalIDs,
39 const Teuchos::ArrayView<int>& nodeIDs,
40 const Teuchos::ArrayView<LO>& localIDs,
41 const bool computeLIDs) const {
42 // Ensure that globalIDs, nodeIDs, and localIDs (if applicable)
43 // all have the same size, before modifying any output arguments.
45 std::invalid_argument, Teuchos::typeName(*this) << "::getEntries(): "
46 "Output arrays do not have the right sizes. nodeIDs.size() = "
47 << nodeIDs.size() << " != globalIDs.size() = " << globalIDs.size() << ".");
49 computeLIDs && localIDs.size() != globalIDs.size(),
50 std::invalid_argument, Teuchos::typeName(*this) << "::getEntries(): "
51 "Output array do not have the right sizes. localIDs.size() = "
52 << localIDs.size() << " != globalIDs.size() = " << globalIDs.size() << ".");
53
54 // Initially, fill nodeIDs and localIDs (if applicable) with
55 // invalid values. The "invalid" process ID is -1 (this means
56 // the same thing as MPI_ANY_SOURCE to Teuchos, so it's an
57 // "invalid" process ID); the invalid local ID comes from
58 // OrdinalTraits.
59 std::fill(nodeIDs.begin(), nodeIDs.end(), -1);
60 if (computeLIDs) {
61 std::fill(localIDs.begin(), localIDs.end(),
62 Teuchos::OrdinalTraits<LO>::invalid());
63 }
64 // Actually do the work.
65 return this->getEntriesImpl(map, globalIDs, nodeIDs, localIDs, computeLIDs);
66}
67
68template <class LO, class GO, class NT>
71 : numProcs_(map.getComm()->getSize()) {}
72
73template <class LO, class GO, class NT>
75 isOneToOne(const Teuchos::Comm<int>& /* comm */) const {
76 // A locally replicated Map is one-to-one only if there is no
77 // replication, that is, only if the Map's communicator only has
78 // one process.
79 return (numProcs_ == 1);
80}
81
82template <class LO, class GO, class NT>
83std::string
85 std::ostringstream os;
86 os << "ReplicatedDirectory"
87 << "<" << Teuchos::TypeNameTraits<LO>::name()
88 << ", " << Teuchos::TypeNameTraits<GO>::name()
89 << ", " << Teuchos::TypeNameTraits<NT>::name() << ">";
90 return os.str();
91}
92
93template <class LO, class GO, class NT>
95 ContiguousUniformDirectory(const map_type& map) {
96 TEUCHOS_TEST_FOR_EXCEPTION(!map.isContiguous(), std::invalid_argument,
97 Teuchos::typeName(*this) << " constructor: Map is not contiguous.");
98 TEUCHOS_TEST_FOR_EXCEPTION(!map.isUniform(), std::invalid_argument,
99 Teuchos::typeName(*this) << " constructor: Map is not uniform.");
100}
101
102template <class LO, class GO, class NT>
103std::string
105 std::ostringstream os;
106 os << "ContiguousUniformDirectory"
107 << "<" << Teuchos::TypeNameTraits<LO>::name()
108 << ", " << Teuchos::TypeNameTraits<GO>::name()
109 << ", " << Teuchos::TypeNameTraits<NT>::name() << ">";
110 return os.str();
111}
112
113template <class LO, class GO, class NT>
117 const Teuchos::ArrayView<const GO>& globalIDs,
118 const Teuchos::ArrayView<int>& nodeIDs,
119 const Teuchos::ArrayView<LO>& localIDs,
120 const bool computeLIDs) const {
121 using Teuchos::Comm;
122 using Teuchos::RCP;
123 typedef typename Teuchos::ArrayView<const GO>::size_type size_type;
124 const LO invalidLid = Teuchos::OrdinalTraits<LO>::invalid();
126
127 RCP<const Comm<int> > comm = map.getComm();
128 const GO g_min = map.getMinAllGlobalIndex();
129
130 // Let N_G be the global number of elements in the Map,
131 // and P be the number of processes in its communicator.
132 // Then, N_G = P * N_L + R = R*(N_L + 1) + (P - R)*N_L.
133 //
134 // The first R processes own N_L+1 elements.
135 // The remaining P-R processes own N_L elements.
136 //
137 // Let g be the current GID, g_min be the global minimum GID,
138 // and g_0 = g - g_min. If g is a valid GID in this Map, then
139 // g_0 is in [0, N_G - 1].
140 //
141 // If g is a valid GID in this Map and g_0 < R*(N_L + 1), then
142 // the rank of the process that owns g is floor(g_0 / (N_L +
143 // 1)), and its corresponding local index on that process is g_0
144 // mod (N_L + 1).
145 //
146 // Let g_R = g_0 - R*(N_L + 1). If g is a valid GID in this Map
147 // and g_0 >= R*(N_L + 1), then the rank of the process that
148 // owns g is then R + floor(g_R / N_L), and its corresponding
149 // local index on that process is g_R mod N_L.
150
151 const size_type N_G =
152 static_cast<size_type>(map.getGlobalNumElements());
153 const size_type P = static_cast<size_type>(comm->getSize());
154 const size_type N_L = N_G / P;
155 const size_type R = N_G - N_L * P; // N_G mod P
156 const size_type N_R = R * (N_L + static_cast<size_type>(1));
157
158#ifdef HAVE_TPETRA_DEBUG
160 N_G != P * N_L + R, std::logic_error,
161 "Tpetra::ContiguousUniformDirectory::getEntriesImpl: "
162 "N_G = "
163 << N_G << " != P*N_L + R = " << P << "*" << N_L << " + " << R
164 << " = " << P * N_L + R << ". "
165 "Please report this bug to the Tpetra developers.");
166#endif // HAVE_TPETRA_DEBUG
167
168 const size_type numGids = globalIDs.size(); // for const loop bound
169 // Avoid signed/unsigned comparisons below, in case GO is
170 // unsigned. (Integer literals are generally signed.)
171 const GO ONE = static_cast<GO>(1);
172
173 if (computeLIDs) {
174 for (size_type k = 0; k < numGids; ++k) {
175 const GO g_0 = globalIDs[k] - g_min;
176
177 // The first test is a little strange just in case GO is
178 // unsigned. Compilers raise a warning on tests like "x <
179 // 0" if x is unsigned, but don't usually raise a warning if
180 // the expression is a bit more complicated than that.
181 if (g_0 + ONE < ONE || g_0 >= static_cast<GO>(N_G)) {
182 nodeIDs[k] = -1;
185 } else if (g_0 < static_cast<GO>(N_R)) {
186 // The GID comes from the initial sequence of R processes.
187 nodeIDs[k] = static_cast<int>(g_0 / static_cast<GO>(N_L + 1));
188 localIDs[k] = static_cast<LO>(g_0 % static_cast<GO>(N_L + 1));
189 } else if (g_0 >= static_cast<GO>(N_R)) {
190 // The GID comes from the remaining P-R processes.
191 const GO g_R = g_0 - static_cast<GO>(N_R);
192 nodeIDs[k] = static_cast<int>(R + g_R / N_L);
193 localIDs[k] = static_cast<int>(g_R % N_L);
194 }
195#ifdef HAVE_TPETRA_DEBUG
196 else {
197 TEUCHOS_TEST_FOR_EXCEPTION(true, std::logic_error,
198 "Tpetra::ContiguousUniformDirectory::getEntriesImpl: "
199 "should never get here. "
200 "Please report this bug to the Tpetra developers.");
201 }
202#endif // HAVE_TPETRA_DEBUG
203 }
204 } else { // don't compute local indices
205 for (size_type k = 0; k < numGids; ++k) {
206 const GO g_0 = globalIDs[k] - g_min;
207 // The first test is a little strange just in case GO is
208 // unsigned. Compilers raise a warning on tests like "x <
209 // 0" if x is unsigned, but don't usually raise a warning if
210 // the expression is a bit more complicated than that.
211 if (g_0 + ONE < ONE || g_0 >= static_cast<GO>(N_G)) {
212 nodeIDs[k] = -1;
214 } else if (g_0 < static_cast<GO>(N_R)) {
215 // The GID comes from the initial sequence of R processes.
216 nodeIDs[k] = static_cast<int>(g_0 / static_cast<GO>(N_L + 1));
217 } else if (g_0 >= static_cast<GO>(N_R)) {
218 // The GID comes from the remaining P-R processes.
219 const GO g_R = g_0 - static_cast<GO>(N_R);
220 nodeIDs[k] = static_cast<int>(R + g_R / N_L);
221 }
222#ifdef HAVE_TPETRA_DEBUG
223 else {
224 TEUCHOS_TEST_FOR_EXCEPTION(true, std::logic_error,
225 "Tpetra::ContiguousUniformDirectory::getEntriesImpl: "
226 "should never get here. "
227 "Please report this bug to the Tpetra developers.");
228 }
229#endif // HAVE_TPETRA_DEBUG
230 }
231 }
232 return res;
233}
234
235template <class LO, class GO, class NT>
237 DistributedContiguousDirectory(const map_type& map) {
238 using Teuchos::arcp;
239 using Teuchos::gatherAll;
240 using Teuchos::RCP;
241
242 RCP<const Teuchos::Comm<int> > comm = map.getComm();
243
244 TEUCHOS_TEST_FOR_EXCEPTION(!map.isDistributed(), std::invalid_argument,
245 Teuchos::typeName(*this) << " constructor: Map is not distributed.");
246 TEUCHOS_TEST_FOR_EXCEPTION(!map.isContiguous(), std::invalid_argument,
247 Teuchos::typeName(*this) << " constructor: Map is not contiguous.");
248
249 const int numProcs = comm->getSize();
250
251 // Make room for the min global ID on each process, plus one
252 // entry at the end for the "max cap."
253 allMinGIDs_ = arcp<GO>(numProcs + 1);
254 // Get my process' min global ID.
255 GO minMyGID = map.getMinGlobalIndex();
256 // Gather all of the min global IDs into the first numProcs
257 // entries of allMinGIDs_.
258
259 // FIXME (mfh 16 Apr 2013) GIANT HACK BELOW
260 //
261 // The purpose of this giant hack is that gatherAll appears to
262 // interpret the "receive count" argument differently than
263 // MPI_Allgather does. Matt Bettencourt reports Valgrind issues
264 // (memcpy with overlapping data) with MpiComm<int>::gatherAll,
265 // which could relate either to this, or to OpenMPI.
266#ifdef HAVE_TPETRACORE_MPI
268 bool useRawMpi = true;
269 if (typeid(GO) == typeid(int)) {
271 } else if (typeid(GO) == typeid(long)) {
273 } else {
274 useRawMpi = false;
275 }
276 if (useRawMpi) {
277 using Teuchos::MpiComm;
278 using Teuchos::rcp_dynamic_cast;
279 RCP<const MpiComm<int> > mpiComm =
280 rcp_dynamic_cast<const MpiComm<int> >(comm);
281 // It could be a SerialComm instead, even in an MPI build, so
282 // be sure to check.
283 if (!comm.is_null()) {
284 MPI_Comm rawMpiComm = *(mpiComm->getRawMpiComm());
285 const int err =
286 MPI_Allgather(&minMyGID, 1, rawMpiType,
287 allMinGIDs_.getRawPtr(), 1, rawMpiType,
288 rawMpiComm);
289 TEUCHOS_TEST_FOR_EXCEPTION(err != MPI_SUCCESS, std::runtime_error,
290 "Tpetra::DistributedContiguousDirectory: MPI_Allgather failed");
291 } else {
292 gatherAll<int, GO>(*comm, 1, &minMyGID, numProcs, allMinGIDs_.getRawPtr());
293 }
294 } else {
295 gatherAll<int, GO>(*comm, 1, &minMyGID, numProcs, allMinGIDs_.getRawPtr());
296 }
297#else // NOT HAVE_TPETRACORE_MPI
298 gatherAll<int, GO>(*comm, 1, &minMyGID, numProcs, allMinGIDs_.getRawPtr());
299#endif // HAVE_TPETRACORE_MPI
300 // FIXME (mfh 16 Apr 2013) GIANT HACK ABOVE
301
302 // gatherAll<int, GO> (*comm, 1, &minMyGID, numProcs, allMinGIDs_.getRawPtr ());
303
304 // Put the max cap at the end. Adding one lets us write loops
305 // over the global IDs with the usual strict less-than bound.
306 allMinGIDs_[numProcs] = map.getMaxAllGlobalIndex() + Teuchos::OrdinalTraits<GO>::one();
307}
308
309template <class LO, class GO, class NT>
310std::string
312 std::ostringstream os;
313 os << "DistributedContiguousDirectory"
314 << "<" << Teuchos::TypeNameTraits<LO>::name()
315 << ", " << Teuchos::TypeNameTraits<GO>::name()
316 << ", " << Teuchos::TypeNameTraits<NT>::name() << ">";
317 return os.str();
318}
319
320template <class LO, class GO, class NT>
324 const Teuchos::ArrayView<const GO>& globalIDs,
325 const Teuchos::ArrayView<int>& nodeIDs,
326 const Teuchos::ArrayView<LO>& localIDs,
327 const bool computeLIDs) const {
328 using Teuchos::Array;
329 using Teuchos::ArrayRCP;
330 using Teuchos::ArrayView;
331 using Teuchos::as;
332 using Teuchos::Comm;
333 using Teuchos::RCP;
334
336 RCP<const Teuchos::Comm<int> > comm = map.getComm();
337 const int myRank = comm->getRank();
338
339 // Map is on one process or is locally replicated.
340 typename ArrayView<int>::iterator procIter = nodeIDs.begin();
341 typename ArrayView<LO>::iterator lidIter = localIDs.begin();
343 for (gidIter = globalIDs.begin(); gidIter != globalIDs.end(); ++gidIter) {
344 if (map.isNodeGlobalElement(*gidIter)) {
345 *procIter++ = myRank;
346 if (computeLIDs) {
347 *lidIter++ = map.getLocalElement(*gidIter);
348 }
349 } else {
350 // Advance the pointers, leaving these values set to invalid
351 procIter++;
352 if (computeLIDs) {
353 lidIter++;
354 }
356 }
357 }
358 return res;
359}
360
361template <class LO, class GO, class NT>
365 const Teuchos::ArrayView<const GO>& globalIDs,
366 const Teuchos::ArrayView<int>& nodeIDs,
367 const Teuchos::ArrayView<LO>& localIDs,
368 const bool computeLIDs) const {
369 using Teuchos::Array;
370 using Teuchos::ArrayRCP;
371 using Teuchos::ArrayView;
372 using Teuchos::as;
373 using Teuchos::Comm;
374 using Teuchos::RCP;
375
376 RCP<const Teuchos::Comm<int> > comm = map.getComm();
377 const int numProcs = comm->getSize();
378 const LO LINVALID = Teuchos::OrdinalTraits<LO>::invalid();
379 const GO noGIDsOnProc = std::numeric_limits<GO>::max();
381
382 // Find the first initialized GID for search below
386 if (allMinGIDs_[firstProcWithGIDs] != noGIDsOnProc) break;
387 }
388
389 // If Map is empty, return invalid values for all requested lookups
390 // This case should not happen, as an empty Map is not considered
391 // Distributed.
393 // No entries in Map
394 res = (globalIDs.size() > 0) ? IDNotPresent : AllIDsPresent;
395 std::fill(nodeIDs.begin(), nodeIDs.end(), -1);
396 if (computeLIDs)
397 std::fill(localIDs.begin(), localIDs.end(), LINVALID);
398 return res;
399 }
400
401 const GO one = as<GO>(1);
402 const GO nOverP = as<GO>(map.getGlobalNumElements() / as<global_size_t>(numProcs - firstProcWithGIDs));
403
404 // Map is distributed but contiguous.
405 typename ArrayView<int>::iterator procIter = nodeIDs.begin();
406 typename ArrayView<LO>::iterator lidIter = localIDs.begin();
408 for (gidIter = globalIDs.begin(); gidIter != globalIDs.end(); ++gidIter) {
409 LO LID = LINVALID; // Assume not found until proven otherwise
410 int image = -1;
411 GO GID = *gidIter;
412 // Guess uniform distribution (TODO: maybe replace by a binary search)
413 // We go through all this trouble to avoid overflow and
414 // signed / unsigned casting mistakes (that were made in
415 // previous versions of this code).
416 int curRank;
417 const GO firstGuess = firstProcWithGIDs + GID / std::max(nOverP, one);
418 curRank = as<int>(std::min(firstGuess, as<GO>(numProcs - 1)));
419
420 // This while loop will stop because
421 // allMinGIDs_[np] == global num elements
422 while (allMinGIDs_[curRank] == noGIDsOnProc) curRank++;
423
424 bool badGID = false;
425 while (curRank >= firstProcWithGIDs && GID < allMinGIDs_[curRank]) {
426 curRank--;
427 while (curRank >= firstProcWithGIDs &&
428 allMinGIDs_[curRank] == noGIDsOnProc) curRank--;
429 }
431 // GID is lower than all GIDs in map
432 badGID = true;
433 } else if (curRank == numProcs) {
434 // GID is higher than all GIDs in map
435 badGID = true;
436 } else {
437 // we have the lower bound; now limit from above
438 int above = curRank + 1;
439 while (allMinGIDs_[above] == noGIDsOnProc) above++;
440
441 while (GID >= allMinGIDs_[above]) {
442 curRank = above;
443 if (curRank == numProcs) {
444 // GID is higher than all GIDs in map
445 badGID = true;
446 break;
447 }
448 above++;
449 while (allMinGIDs_[above] == noGIDsOnProc) above++;
450 }
451 }
452
453 if (!badGID) {
454 image = curRank;
455 LID = as<LO>(GID - allMinGIDs_[image]);
456 } else {
458 }
459 *procIter++ = image;
460 if (computeLIDs) {
461 *lidIter++ = LID;
462 }
463 }
464 return res;
465}
466
467template <class LO, class GO, class NT>
470 : oneToOneResult_(ONE_TO_ONE_NOT_CALLED_YET)
471 , // to be revised below
472 locallyOneToOne_(true)
473 , // to be revised below
474 useHashTables_(false) // to be revised below
475{
476 initialize(map, Teuchos::null);
477}
478
479template <class LO, class GO, class NT>
480DistributedNoncontiguousDirectory<LO, GO, NT>::
481 DistributedNoncontiguousDirectory(const map_type& map,
482 const tie_break_type& tie_break)
483 : oneToOneResult_(ONE_TO_ONE_NOT_CALLED_YET)
484 , // to be revised below
485 locallyOneToOne_(true)
486 , // to be revised below
487 useHashTables_(false) // to be revised below
488{
489 initialize(map, Teuchos::ptrFromRef(tie_break));
490}
491
492template <class LO, class GO, class NT>
493void DistributedNoncontiguousDirectory<LO, GO, NT>::
494 initialize(const map_type& map,
495 Teuchos::Ptr<const tie_break_type> tie_break) {
496 using std::cerr;
497 using std::endl;
498 using Teuchos::arcp;
499 using Teuchos::Array;
500 using Teuchos::ArrayRCP;
501 using Teuchos::ArrayView;
502 using Teuchos::as;
503 using Teuchos::RCP;
504 using Teuchos::rcp;
505 using Teuchos::typeName;
506 using Teuchos::TypeNameTraits;
507 typedef Array<int>::size_type size_type;
508
509 // This class' implementation of getEntriesImpl() currently
510 // encodes the following assumptions:
511 //
512 // 1. global_size_t >= GO
513 // 2. global_size_t >= int
514 // 3. global_size_t >= LO
515 //
516 // We check these assumptions here.
517 TEUCHOS_TEST_FOR_EXCEPTION(sizeof(global_size_t) < sizeof(GO),
518 std::logic_error, typeName(*this) << ": sizeof(Tpetra::"
519 "global_size_t) = "
520 << sizeof(global_size_t) << " < sizeof(Global"
521 "Ordinal = "
522 << TypeNameTraits<LO>::name() << ") = " << sizeof(GO) << ".");
523 TEUCHOS_TEST_FOR_EXCEPTION(sizeof(global_size_t) < sizeof(int),
524 std::logic_error, typeName(*this) << ": sizeof(Tpetra::"
525 "global_size_t) = "
526 << sizeof(global_size_t) << " < sizeof(int) = " << sizeof(int) << ".");
527 TEUCHOS_TEST_FOR_EXCEPTION(sizeof(global_size_t) < sizeof(LO),
528 std::logic_error, typeName(*this) << ": sizeof(Tpetra::"
529 "global_size_t) = "
530 << sizeof(global_size_t) << " < sizeof(Local"
531 "Ordinal = "
532 << TypeNameTraits<LO>::name() << ") = " << sizeof(LO) << ".");
533 TEUCHOS_TEST_FOR_EXCEPTION(!map.haveGlobalConstants(), std::logic_error, "Map needs to have global constants.");
534
535 RCP<const Teuchos::Comm<int> > comm = map.getComm();
536 const LO LINVALID = Teuchos::OrdinalTraits<LO>::invalid();
537 const GO minAllGID = map.getMinAllGlobalIndex();
538 const GO maxAllGID = map.getMaxAllGlobalIndex();
539
540 // The "Directory Map" (see below) will have a range of elements
541 // from the minimum to the maximum GID of the user Map, and a
542 // minimum GID of minAllGID from the user Map. It doesn't
543 // actually have to store all those entries, though do beware of
544 // calling getLocalElementList on it (see Bug 5822).
545 const global_size_t numGlobalEntries = maxAllGID - minAllGID + 1;
546
547 // We can't afford to replicate the whole directory on each
548 // process, so create the "Directory Map", a uniform contiguous
549 // Map that describes how we will distribute the directory over
550 // processes.
551 //
552 // FIXME (mfh 08 May 2012) Here we're setting minAllGID to be
553 // the index base. The index base should be separate from the
554 // minimum GID.
555 directoryMap_ = rcp(new map_type(numGlobalEntries, minAllGID, comm,
556 GloballyDistributed));
557 // The number of Directory elements that my process owns.
558 const size_t dir_numMyEntries = directoryMap_->getLocalNumElements();
559
560 // Fix for Bug 5822: If the input Map is "sparse," that is if
561 // the difference between the global min and global max GID is
562 // much larger than the global number of elements in the input
563 // Map, then it's possible that the Directory Map might have
564 // many more entries than the input Map on this process. This
565 // can cause memory scalability issues. In that case, we switch
566 // from the array-based implementation of Directory storage to
567 // the hash table - based implementation. We don't use hash
568 // tables all the time, because they are slower in the common
569 // case of a nonsparse Map.
570 //
571 // NOTE: This is a per-process decision. Some processes may use
572 // array-based storage, whereas others may use hash table -
573 // based storage.
574
575 // A hash table takes a constant factor more space, more or
576 // less, than an array. Thus, it's not worthwhile, even in
577 // terms of memory usage, always to use a hash table.
578 // Furthermore, array lookups are faster than hash table
579 // lookups, so it may be worthwhile to use an array even if it
580 // takes more space. The "sparsity threshold" governs when to
581 // switch to a hash table - based implementation.
582 const size_t inverseSparsityThreshold = 10;
583 useHashTables_ =
584 (dir_numMyEntries >= inverseSparsityThreshold * map.getLocalNumElements());
585
586 // Get list of process IDs that own the directory entries for the
587 // Map GIDs. These will be the targets of the sends that the
588 // Distributor will do.
589 const int myRank = comm->getRank();
590 const size_t numMyEntries = map.getLocalNumElements();
591 Array<int> sendImageIDs(numMyEntries);
592 ArrayView<const GO> myGlobalEntries = map.getLocalElementList();
593 // An ID not present in this lookup indicates that it lies outside
594 // of the range [minAllGID,maxAllGID] (from map_). this means
595 // something is wrong with map_, our fault.
596 const LookupStatus lookupStatus =
597 directoryMap_->getRemoteIndexList(myGlobalEntries, sendImageIDs);
598 TEUCHOS_TEST_FOR_EXCEPTION(
599 lookupStatus == IDNotPresent, std::logic_error, Teuchos::typeName(*this) << " constructor: the Directory Map could not find out where one or "
600 "more of my Map's indices should go. The input to getRemoteIndexList "
601 "is "
602 << Teuchos::toString(myGlobalEntries) << ", and the output is " << Teuchos::toString(sendImageIDs()) << ". The input Map itself has "
603 "the following entries on the calling process "
604 << map.getComm()->getRank() << ": " << Teuchos::toString(map.getLocalElementList()) << ", and has " << map.getGlobalNumElements() << " total global indices in [" << map.getMinAllGlobalIndex() << "," << map.getMaxAllGlobalIndex() << "]. The Directory Map has " << directoryMap_->getGlobalNumElements() << " total global indices in "
605 "["
606 << directoryMap_->getMinAllGlobalIndex() << "," << directoryMap_->getMaxAllGlobalIndex() << "], and the calling process "
607 "has GIDs ["
608 << directoryMap_->getMinGlobalIndex() << "," << directoryMap_->getMaxGlobalIndex() << "]. "
609 "This probably means there is a bug in Map or Directory. "
610 "Please report this bug to the Tpetra developers.");
611
612 // Initialize the distributor using the list of process IDs to
613 // which to send. We'll use the distributor to send out triples
614 // of (GID, process ID, LID). We're sending the entries to the
615 // processes that the Directory Map says should own them, which is
616 // why we called directoryMap_->getRemoteIndexList() above.
617 Distributor distor(comm);
618 const size_t numReceives = distor.createFromSends(sendImageIDs);
619
620 // NOTE (mfh 21 Mar 2012) The following code assumes that
621 // sizeof(GO) >= sizeof(int) and sizeof(GO) >= sizeof(LO).
622 //
623 // Create and fill buffer of (GID, PID, LID) triples to send
624 // out. We pack the (GID, PID, LID) triples into a single Array
625 // of GO, casting the PID from int to GO and the LID from LO to
626 // GO as we do so.
627 //
628 // FIXME (mfh 23 Mar 2014) This assumes that sizeof(LO) <=
629 // sizeof(GO) and sizeof(int) <= sizeof(GO). The former is
630 // required, and the latter is generally the case, but we should
631 // still check for this.
632 const int packetSize = 3; // We're sending triples, so packet size is 3.
633 Kokkos::View<GO*, Kokkos::HostSpace> exportEntries("exportEntries", packetSize * numMyEntries);
634 {
635 size_t exportIndex = 0;
636 for (size_t i = 0; i < numMyEntries; ++i) {
637 exportEntries[exportIndex++] = myGlobalEntries[i];
638 exportEntries[exportIndex++] = as<GO>(myRank);
639 exportEntries[exportIndex++] = as<GO>(i);
640 }
641 }
642 // Buffer of data to receive. The Distributor figured out for
643 // us how many packets we're receiving, when we called its
644 // createFromSends() method to set up the distribution plan.
645 Kokkos::View<GO*, Kokkos::HostSpace> importElements("importElements", packetSize * distor.getTotalReceiveLength());
646
647 // Distribute the triples of (GID, process ID, LID).
648 distor.doPostsAndWaits(exportEntries, packetSize, importElements);
649
650 // Unpack the redistributed data. Both implementations of
651 // Directory storage map from an LID in the Directory Map (which
652 // is the LID of the GID to store) to either a PID or an LID in
653 // the input Map. Each "packet" (contiguous chunk of
654 // importElements) contains a triple: (GID, PID, LID).
655 if (useHashTables_) {
656 // Create the hash tables. We know exactly how many elements
657 // to expect in each hash table. FixedHashTable's constructor
658 // currently requires all the keys and values at once, so we
659 // have to extract them in temporary arrays. It may be
660 // possible to rewrite FixedHashTable to use a "start fill" /
661 // "end fill" approach that avoids the temporary arrays, but
662 // we won't try that for now.
663
664 // The constructors of Array and ArrayRCP that take a number
665 // of elements all initialize the arrays. Instead, allocate
666 // raw arrays, then hand them off to ArrayRCP, to avoid the
667 // initial unnecessary initialization without losing the
668 // benefit of exception safety (and bounds checking, in a
669 // debug build).
670 LO* tableKeysRaw = NULL;
671 LO* tableLidsRaw = NULL;
672 int* tablePidsRaw = NULL;
673 try {
674 tableKeysRaw = new LO[numReceives];
675 tableLidsRaw = new LO[numReceives];
676 tablePidsRaw = new int[numReceives];
677 } catch (...) {
678 if (tableKeysRaw != NULL) {
679 delete[] tableKeysRaw;
680 }
681 if (tableLidsRaw != NULL) {
682 delete[] tableLidsRaw;
683 }
684 if (tablePidsRaw != NULL) {
685 delete[] tablePidsRaw;
686 }
687 throw;
688 }
689 ArrayRCP<LO> tableKeys(tableKeysRaw, 0, numReceives, true);
690 ArrayRCP<LO> tableLids(tableLidsRaw, 0, numReceives, true);
691 ArrayRCP<int> tablePids(tablePidsRaw, 0, numReceives, true);
692
693 if (tie_break.is_null()) {
694 // Fill the temporary arrays of keys and values.
695 size_type importIndex = 0;
696 for (size_type i = 0; i < static_cast<size_type>(numReceives); ++i) {
697 const GO curGID = importElements[importIndex++];
698 const LO curLID = directoryMap_->getLocalElement(curGID);
699 TEUCHOS_TEST_FOR_EXCEPTION(
700 curLID == LINVALID, std::logic_error,
701 Teuchos::typeName(*this) << " constructor: Incoming global index "
702 << curGID << " does not have a corresponding local index in the "
703 "Directory Map. Please report this bug to the Tpetra developers.");
704 tableKeys[i] = curLID;
705 tablePids[i] = importElements[importIndex++];
706 tableLids[i] = importElements[importIndex++];
707 }
708 // Set up the hash tables. The hash tables' constructor
709 // detects whether there are duplicates, so that we can set
710 // locallyOneToOne_.
711 lidToPidTable_ =
712 rcp(new lidToPidTable_type(tableKeys(), tablePids()));
713 locallyOneToOne_ = !(lidToPidTable_->hasDuplicateKeys());
714 lidToLidTable_ =
715 rcp(new lidToLidTable_type(tableKeys(), tableLids()));
716 } else { // tie_break is NOT null
717
718 // For each directory Map LID received, collect all the
719 // corresponding (PID,LID) pairs. If the input Map is not
720 // one-to-one, corresponding directory Map LIDs will have
721 // more than one pair. In that case, we will use the
722 // TieBreak object to pick exactly one pair.
723 typedef std::map<LO, std::vector<std::pair<int, LO> > > pair_table_type;
724 pair_table_type ownedPidLidPairs;
725
726 // For each directory Map LID received, collect the zero or
727 // more input Map (PID,LID) pairs into ownedPidLidPairs.
728 size_type importIndex = 0;
729 for (size_type i = 0; i < static_cast<size_type>(numReceives); ++i) {
730 const GO curGID = importElements[importIndex++];
731 const LO dirMapLid = directoryMap_->getLocalElement(curGID);
732 TEUCHOS_TEST_FOR_EXCEPTION(
733 dirMapLid == LINVALID, std::logic_error,
734 Teuchos::typeName(*this) << " constructor: Incoming global index "
735 << curGID << " does not have a corresponding local index in the "
736 "Directory Map. Please report this bug to the Tpetra developers.");
737 tableKeys[i] = dirMapLid;
738 const int PID = importElements[importIndex++];
739 const int LID = importElements[importIndex++];
740
741 // These may change below. We fill them in just to ensure
742 // that they won't have invalid values.
743 tablePids[i] = PID;
744 tableLids[i] = LID;
745
746 // For every directory Map LID, we have to remember all
747 // (PID, LID) pairs. The TieBreak object will arbitrate
748 // between them in the loop below.
749 ownedPidLidPairs[dirMapLid].push_back(std::make_pair(PID, LID));
750 }
751
752 // Use TieBreak to arbitrate between (PID,LID) pairs
753 // corresponding to each directory Map LID.
754 //
755 // FIXME (mfh 23 Mar 2014) How do I know that i is the same
756 // as the directory Map LID?
757 // KDD 21 Mar 2018: It isn't, especially if the user's IDs are not
758 // contiguous, but the directory map is. Need to set tablePids[i]
759 // and tableLids[i], so need to loop over numReceives (as that is
760 // how those arrays are allocated). FIXED
761
762 for (size_type i = 0; i < static_cast<size_type>(numReceives); ++i) {
763 const LO dirMapLid = tableKeys[i];
764 const std::vector<std::pair<int, LO> >& pidLidList =
765 ownedPidLidPairs[dirMapLid];
766 const size_t listLen = pidLidList.size();
767 if (listLen == 0) continue; // KDD This will never happen
768 const GO dirMapGid = directoryMap_->getGlobalElement(dirMapLid);
769 if (listLen > 1) {
770 locallyOneToOne_ = false;
771 }
772 // If there is some (PID,LID) pair for the current input
773 // Map LID, then it makes sense to invoke the TieBreak
774 // object to arbitrate between the options. Even if
775 // there is only one (PID,LID) pair, we still want to
776 // give the TieBreak object a chance to do whatever it
777 // likes to do, in terms of side effects (e.g., track
778 // (PID,LID) pairs).
779 const size_type index =
780 static_cast<size_type>(tie_break->selectedIndex(dirMapGid,
781 pidLidList));
782 tablePids[i] = pidLidList[index].first;
783 tableLids[i] = pidLidList[index].second;
784 }
785
786 // Set up the hash tables.
787 lidToPidTable_ =
788 rcp(new lidToPidTable_type(tableKeys(), tablePids()));
789 lidToLidTable_ =
790 rcp(new lidToLidTable_type(tableKeys(), tableLids()));
791 }
792 } else {
793 if (tie_break.is_null()) {
794 // Use array-based implementation of Directory storage.
795 // Allocate these arrays and fill them with invalid values,
796 // in case the input Map's GID list is sparse (i.e., does
797 // not populate all GIDs from minAllGID to maxAllGID).
798 PIDs_ = arcp<int>(dir_numMyEntries);
799 std::fill(PIDs_.begin(), PIDs_.end(), -1);
800 LIDs_ = arcp<LO>(dir_numMyEntries);
801 std::fill(LIDs_.begin(), LIDs_.end(), LINVALID);
802 // Fill in the arrays with PIDs resp. LIDs.
803 size_type importIndex = 0;
804 for (size_type i = 0; i < static_cast<size_type>(numReceives); ++i) {
805 const GO curGID = importElements[importIndex++];
806 const LO curLID = directoryMap_->getLocalElement(curGID);
807 TEUCHOS_TEST_FOR_EXCEPTION(curLID == LINVALID, std::logic_error,
808 Teuchos::typeName(*this) << " constructor: Incoming global index "
809 << curGID << " does not have a corresponding local index in the "
810 "Directory Map. Please report this bug to the Tpetra developers.");
811
812 // If PIDs_[curLID] is not -1, then curGID is a duplicate
813 // on the calling process, so the Directory is not locally
814 // one-to-one.
815 if (PIDs_[curLID] != -1) {
816 locallyOneToOne_ = false;
817 }
818 PIDs_[curLID] = importElements[importIndex++];
819 LIDs_[curLID] = importElements[importIndex++];
820 }
821 } else {
822 PIDs_ = arcp<int>(dir_numMyEntries);
823 LIDs_ = arcp<LO>(dir_numMyEntries);
824 std::fill(PIDs_.begin(), PIDs_.end(), -1);
825
826 // All received (PID, LID) pairs go into ownedPidLidPairs.
827 // This is a map from the directory Map's LID to the (PID,
828 // LID) pair (where the latter LID comes from the input Map,
829 // not the directory Map). If the input Map is not
830 // one-to-one, corresponding LIDs will have
831 // ownedPidLidPairs[curLID].size() > 1. In that case, we
832 // will use the TieBreak object to pick exactly one pair.
833 Array<std::vector<std::pair<int, LO> > > ownedPidLidPairs(dir_numMyEntries);
834 size_t importIndex = 0;
835 for (size_t i = 0; i < numReceives; ++i) {
836 const GO GID = importElements[importIndex++];
837 const int PID = importElements[importIndex++];
838 const LO LID = importElements[importIndex++];
839
840 const LO dirMapLid = directoryMap_->getLocalElement(GID);
841 TEUCHOS_TEST_FOR_EXCEPTION(
842 dirMapLid == LINVALID, std::logic_error,
843 Teuchos::typeName(*this) << " constructor: Incoming global index "
844 << GID << " does not have a corresponding local index in the "
845 "Directory Map. Please report this bug to the Tpetra developers.");
846 ownedPidLidPairs[dirMapLid].push_back(std::make_pair(PID, LID));
847 }
848
849 // Use TieBreak to arbitrate between (PID,LID) pairs
850 // corresponding to each directory Map LID.
851 //
852 // FIXME (mfh 23 Mar 2014) How do I know that i is the same
853 // as the directory Map LID?
854 // KDD 21 Mar 2018: It isn't, especially if the user's IDs are not
855 // contiguous. Loop over all ownedPidLidPairs; skip those that have
856 // empty lists. FIXED
857
858 for (size_t i = 0; i < dir_numMyEntries; ++i) {
859 const std::vector<std::pair<int, LO> >& pidLidList =
860 ownedPidLidPairs[i];
861 const size_t listLen = pidLidList.size();
862 if (listLen == 0) continue; // KDD will happen for GIDs not in
863 // KDD the user's source map
864 const LO dirMapLid = static_cast<LO>(i);
865 const GO dirMapGid = directoryMap_->getGlobalElement(dirMapLid);
866 if (listLen > 1) {
867 locallyOneToOne_ = false;
868 }
869 // If there is some (PID,LID) pair for the current input
870 // Map LID, then it makes sense to invoke the TieBreak
871 // object to arbitrate between the options. Even if
872 // there is only one (PID,LID) pair, we still want to
873 // give the TieBreak object a chance to do whatever it
874 // likes to do, in terms of side effects (e.g., track
875 // (PID,LID) pairs).
876 const size_type index =
877 static_cast<size_type>(tie_break->selectedIndex(dirMapGid,
878 pidLidList));
879 PIDs_[i] = pidLidList[index].first;
880 LIDs_[i] = pidLidList[index].second;
881 }
882 }
883 }
884}
885
886template <class LO, class GO, class NT>
887std::string
889 std::ostringstream os;
890 os << "DistributedNoncontiguousDirectory"
891 << "<" << Teuchos::TypeNameTraits<LO>::name()
892 << ", " << Teuchos::TypeNameTraits<GO>::name()
893 << ", " << Teuchos::TypeNameTraits<NT>::name() << ">";
894 return os.str();
895}
896
897template <class LO, class GO, class NT>
900 getEntriesImpl(const map_type& map,
901 const Teuchos::ArrayView<const GO>& globalIDs,
902 const Teuchos::ArrayView<int>& nodeIDs,
903 const Teuchos::ArrayView<LO>& localIDs,
904 const bool computeLIDs) const {
905 using Details::Behavior;
907 using std::cerr;
908 using std::endl;
909 using Teuchos::Array;
910 using Teuchos::ArrayRCP;
911 using Teuchos::ArrayView;
912 using Teuchos::as;
913 using Teuchos::RCP;
914 using Teuchos::toString;
915 using size_type = typename Array<GO>::size_type;
916 const char funcPrefix[] =
917 "Tpetra::"
918 "DistributedNoncontiguousDirectory::getEntriesImpl: ";
919 const char errSuffix[] =
920 " Please report this bug to the Tpetra developers.";
921
922 RCP<const Teuchos::Comm<int> > comm = map.getComm();
923 const bool verbose = Behavior::verbose("Directory") ||
924 Behavior::verbose("Tpetra::Directory");
925 const size_t maxNumToPrint = verbose ? Behavior::verbosePrintCountThreshold() : size_t(0);
926
927 std::unique_ptr<std::string> procPrefix;
928 if (verbose) {
929 std::ostringstream os;
930 os << "Proc ";
931 if (map.getComm().is_null()) {
932 os << "?";
933 } else {
934 os << map.getComm()->getRank();
935 }
936 os << ": ";
937 procPrefix = std::unique_ptr<std::string>(
938 new std::string(os.str()));
939 os << funcPrefix << "{";
941 os << ", ";
943 os << ", ";
945 os << ", computeLIDs: "
946 << (computeLIDs ? "true" : "false") << "}" << endl;
947 cerr << os.str();
948 }
949
950 const size_t numEntries = globalIDs.size();
951 const LO LINVALID = Teuchos::OrdinalTraits<LO>::invalid();
953
954 //
955 // Set up directory structure.
956 //
957
958 // If we're computing LIDs, we also have to include them in each
959 // packet, along with the GID and process ID.
960 const int packetSize = computeLIDs ? 3 : 2;
961
962 // For data distribution, we use: Surprise! A Distributor!
963 Distributor distor(comm);
964
965 // Get directory locations for the requested list of entries.
966 Array<int> dirImages(numEntries);
967 if (verbose) {
968 std::ostringstream os;
969 os << *procPrefix << "Call directoryMap_->getRemoteIndexList"
970 << endl;
971 cerr << os.str();
972 }
973 res = directoryMap_->getRemoteIndexList(globalIDs, dirImages());
974 if (verbose) {
975 std::ostringstream os;
976 os << *procPrefix << "Director Map getRemoteIndexList out ";
978 os << endl;
979 cerr << os.str();
980 }
981
982 // Check for unfound globalIDs and set corresponding nodeIDs to -1
983 size_t numMissing = 0;
984 if (res == IDNotPresent) {
985 for (size_t i = 0; i < numEntries; ++i) {
986 if (dirImages[i] == -1) {
987 nodeIDs[i] = -1;
988 if (computeLIDs) {
990 }
991 numMissing++;
992 }
993 }
994 }
995
998 if (verbose) {
999 std::ostringstream os;
1000 os << *procPrefix << "Call Distributor::createFromRecvs"
1001 << endl;
1002 cerr << os.str();
1003 }
1004 distor.createFromRecvs(globalIDs, dirImages(), sendGIDs, sendImages);
1005 if (verbose) {
1006 std::ostringstream os;
1007 os << *procPrefix << "Distributor::createFromRecvs result: "
1008 << "{";
1010 os << ", ";
1012 os << "}" << endl;
1013 cerr << os.str();
1014 }
1015 const size_type numSends = sendGIDs.size();
1016
1017 //
1018 // mfh 13 Nov 2012:
1019 //
1020 // The code below temporarily stores LO, GO, and int values in
1021 // an array of global_size_t. If one of the signed types (LO
1022 // and GO should both be signed) happened to be -1 (or some
1023 // negative number, but -1 is the one that came up today), then
1024 // conversion to global_size_t will result in a huge
1025 // global_size_t value, and thus conversion back may overflow.
1026 // (Teuchos::as doesn't know that we meant it to be an LO or GO
1027 // all along.)
1028 //
1029 // The overflow normally would not be a problem, since it would
1030 // just go back to -1 again. However, Teuchos::as does range
1031 // checking on conversions in a debug build, so it throws an
1032 // exception (std::range_error) in this case. Range checking is
1033 // generally useful in debug mode, so we don't want to disable
1034 // this behavior globally.
1035 //
1036 // We solve this problem by forgoing use of Teuchos::as for the
1037 // conversions below from LO, GO, or int to global_size_t, and
1038 // the later conversions back from global_size_t to LO, GO, or
1039 // int.
1040 //
1041 // I've recorded this discussion as Bug 5760.
1042 //
1043
1044 // global_size_t >= GO
1045 // global_size_t >= size_t >= int
1046 // global_size_t >= size_t >= LO
1047 // Therefore, we can safely store all of these in a global_size_t
1048 Kokkos::View<global_size_t*, Kokkos::HostSpace> exports("exports", packetSize * numSends);
1049 {
1050 // Packet format:
1051 // - If computing LIDs: (GID, PID, LID)
1052 // - Otherwise: (GID, PID)
1053 //
1054 // "PID" means "process ID" (a.k.a. "node ID," a.k.a. "rank").
1055
1056 // Current position to which to write in exports array. If
1057 // sending pairs, we pack the (GID, PID) pair for gid =
1058 // sendGIDs[k] in exports[2*k], exports[2*k+1]. If sending
1059 // triples, we pack the (GID, PID, LID) pair for gid =
1060 // sendGIDs[k] in exports[3*k, 3*k+1, 3*k+2].
1061 size_t exportsIndex = 0;
1062
1063 if (useHashTables_) {
1064 if (verbose) {
1065 std::ostringstream os;
1066 os << *procPrefix << "Pack exports (useHashTables_ true)"
1067 << endl;
1068 cerr << os.str();
1069 }
1070 for (size_type gidIndex = 0; gidIndex < numSends; ++gidIndex) {
1071 const GO curGID = sendGIDs[gidIndex];
1072 // Don't use as() here (see above note).
1073 exports[exportsIndex++] = static_cast<global_size_t>(curGID);
1074 const LO curLID = directoryMap_->getLocalElement(curGID);
1075 TEUCHOS_TEST_FOR_EXCEPTION(curLID == LINVALID, std::logic_error, funcPrefix << "Directory Map's global index " << curGID << " lacks "
1076 "a corresponding local index."
1077 << errSuffix);
1078 // Don't use as() here (see above note).
1079 exports[exportsIndex++] =
1080 static_cast<global_size_t>(lidToPidTable_->get(curLID));
1081 if (computeLIDs) {
1082 // Don't use as() here (see above note).
1083 exports[exportsIndex++] =
1084 static_cast<global_size_t>(lidToLidTable_->get(curLID));
1085 }
1086 }
1087 } else {
1088 if (verbose) {
1089 std::ostringstream os;
1090 os << *procPrefix << "Pack exports (useHashTables_ false)"
1091 << endl;
1092 cerr << os.str();
1093 }
1094 for (size_type gidIndex = 0; gidIndex < numSends; ++gidIndex) {
1095 const GO curGID = sendGIDs[gidIndex];
1096 // Don't use as() here (see above note).
1097 exports[exportsIndex++] = static_cast<global_size_t>(curGID);
1098 const LO curLID = directoryMap_->getLocalElement(curGID);
1099 TEUCHOS_TEST_FOR_EXCEPTION(curLID == LINVALID, std::logic_error, funcPrefix << "Directory Map's global index " << curGID << " lacks "
1100 "a corresponding local index."
1101 << errSuffix);
1102 // Don't use as() here (see above note).
1103 exports[exportsIndex++] =
1104 static_cast<global_size_t>(PIDs_[curLID]);
1105 if (computeLIDs) {
1106 // Don't use as() here (see above note).
1107 exports[exportsIndex++] =
1108 static_cast<global_size_t>(LIDs_[curLID]);
1109 }
1110 }
1111 }
1112
1113 TEUCHOS_TEST_FOR_EXCEPTION(exportsIndex > exports.size(), std::logic_error,
1114 funcPrefix << "On Process " << comm->getRank() << ", "
1115 "exportsIndex = "
1116 << exportsIndex << " > exports.size() = "
1117 << exports.size() << "." << errSuffix);
1118 }
1119
1120 TEUCHOS_TEST_FOR_EXCEPTION(numEntries < numMissing, std::logic_error, funcPrefix << "On Process " << comm->getRank() << ", numEntries = " << numEntries << " < numMissing = " << numMissing << "." << errSuffix);
1121
1122 //
1123 // mfh 13 Nov 2012: See note above on conversions between
1124 // global_size_t and LO, GO, or int.
1125 //
1126 const size_t numRecv = numEntries - numMissing;
1127
1128 {
1129 const size_t importLen = packetSize * distor.getTotalReceiveLength();
1130 const size_t requiredImportLen = numRecv * packetSize;
1131 const int myRank = comm->getRank();
1133 importLen < requiredImportLen, std::logic_error,
1134 "Tpetra::Details::DistributedNoncontiguousDirectory::getEntriesImpl: "
1135 "On Process "
1136 << myRank << ": The 'imports' array must have length "
1137 "at least "
1138 << requiredImportLen << ", but its actual length is " << importLen << ". numRecv: " << numRecv << ", packetSize: " << packetSize << ", numEntries (# GIDs): " << numEntries << ", numMissing: " << numMissing << ": distor.getTotalReceiveLength(): "
1139 << distor.getTotalReceiveLength() << ". " << std::endl
1140 << "Distributor description: " << distor.description() << ". "
1141 << std::endl
1142 << "Please report this bug to the Tpetra developers.");
1143 }
1144
1145 Kokkos::View<global_size_t*, Kokkos::HostSpace> imports("imports", packetSize * distor.getTotalReceiveLength());
1146 // FIXME (mfh 20 Mar 2014) One could overlap the sort2() below
1147 // with communication, by splitting this call into doPosts and
1148 // doWaits. The code is still correct in this form, however.
1149 if (verbose) {
1150 std::ostringstream os;
1151 os << *procPrefix << "Call doPostsAndWaits: {"
1152 << "packetSize: " << packetSize << ", ";
1153 verbosePrintArray(os, exports, "exports", maxNumToPrint);
1154 os << "}" << endl;
1155 cerr << os.str();
1156 }
1157 distor.doPostsAndWaits(exports, packetSize, imports);
1158 if (verbose) {
1159 std::ostringstream os;
1160 os << *procPrefix << "doPostsAndWaits result: ";
1161 verbosePrintArray(os, imports, "imports", maxNumToPrint);
1162 os << endl;
1163 cerr << os.str();
1164 }
1165
1166 Array<GO> sortedIDs(globalIDs); // deep copy (for later sorting)
1167 Array<GO> offset(numEntries); // permutation array (sort2 output)
1168 for (GO ii = 0; ii < static_cast<GO>(numEntries); ++ii) {
1169 offset[ii] = ii;
1170 }
1171 sort2(sortedIDs.begin(), sortedIDs.begin() + numEntries, offset.begin());
1172 if (verbose) {
1173 std::ostringstream os;
1174 os << *procPrefix;
1176 os << ", ";
1178 os << endl;
1179 cerr << os.str();
1180 }
1181
1182 size_t importsIndex = 0;
1183
1184 // we know these conversions are in range, because we loaded this data
1185 //
1186 // Don't use as() for conversions here; we know they are in range.
1187 for (size_t i = 0; i < numRecv; ++i) {
1188 const GO curGID = static_cast<GO>(imports[importsIndex++]);
1189 auto p1 = std::equal_range(sortedIDs.begin(),
1190 sortedIDs.end(), curGID);
1191 if (p1.first != p1.second) {
1192 const size_t j = p1.first - sortedIDs.begin();
1193 nodeIDs[offset[j]] =
1194 static_cast<int>(imports[importsIndex++]);
1195 if (computeLIDs) {
1196 localIDs[offset[j]] =
1197 static_cast<LO>(imports[importsIndex++]);
1198 }
1199 if (nodeIDs[offset[j]] == -1) {
1200 res = IDNotPresent;
1201 }
1202 }
1203 }
1204
1205 TEUCHOS_TEST_FOR_EXCEPTION(size_t(importsIndex) > size_t(imports.size()),
1206 std::logic_error, funcPrefix << "On Process " << comm->getRank() << ": importsIndex = " << importsIndex << " > imports.size() = " << imports.size() << ". "
1207 "numRecv: "
1208 << numRecv << ", packetSize: " << packetSize << ", "
1209 "numEntries (# GIDs): "
1210 << numEntries << ", numMissing: " << numMissing << ": distor.getTotalReceiveLength(): " << distor.getTotalReceiveLength() << "." << errSuffix);
1211 if (verbose) {
1212 std::ostringstream os;
1213 os << *procPrefix << funcPrefix << "Done!" << endl;
1214 cerr << os.str();
1215 }
1216 return res;
1217}
1218
1219template <class LO, class GO, class NT>
1221 isOneToOne(const Teuchos::Comm<int>& comm) const {
1222 if (oneToOneResult_ == ONE_TO_ONE_NOT_CALLED_YET) {
1223 const int lcl121 = isLocallyOneToOne() ? 1 : 0;
1224 int gbl121 = 0;
1225 Teuchos::reduceAll<int, int>(comm, Teuchos::REDUCE_MIN, lcl121,
1226 Teuchos::outArg(gbl121));
1227 oneToOneResult_ = (gbl121 == 1) ? ONE_TO_ONE_TRUE : ONE_TO_ONE_FALSE;
1228 }
1229 return (oneToOneResult_ == ONE_TO_ONE_TRUE);
1230}
1231} // namespace Details
1232} // namespace Tpetra
1233
1234//
1235// Explicit instantiation macro
1236//
1237// Must be expanded from within the Tpetra namespace!
1238//
1239#define TPETRA_DIRECTORYIMPL_INSTANT(LO, GO, NODE) \
1240 namespace Details { \
1241 template class Directory<LO, GO, NODE>; \
1242 template class ReplicatedDirectory<LO, GO, NODE>; \
1243 template class ContiguousUniformDirectory<LO, GO, NODE>; \
1244 template class DistributedContiguousDirectory<LO, GO, NODE>; \
1245 template class DistributedNoncontiguousDirectory<LO, GO, NODE>; \
1246 }
1247
1248#endif // TPETRA_DIRECTORYIMPL_DEF_HPP
Interface for breaking ties in ownership.
Stand-alone utility functions and macros.
Struct that holds views of the contents of a CrsMatrix.
Description of Tpetra's behavior.
static bool verbose()
Whether Tpetra is in verbose mode.
static size_t verbosePrintCountThreshold()
Number of entries below which arrays, lists, etc. will be printed in debug mode.
Implementation of Directory for a contiguous, uniformly distributed Map.
std::string description() const override
A one-line human-readable description of this object.
LookupStatus getEntriesImpl(const map_type &map, const Teuchos::ArrayView< const GlobalOrdinal > &globalIDs, const Teuchos::ArrayView< int > &nodeIDs, const Teuchos::ArrayView< LocalOrdinal > &localIDs, const bool computeLIDs) const override
Find process IDs and (optionally) local IDs for the given global IDs.
LookupStatus getEntries(const map_type &map, const Teuchos::ArrayView< const GlobalOrdinal > &globalIDs, const Teuchos::ArrayView< int > &nodeIDs, const Teuchos::ArrayView< LocalOrdinal > &localIDs, const bool computeLIDs) const
Implementation of Directory for a distributed contiguous Map.
LookupStatus getEntriesImpl(const map_type &map, const Teuchos::ArrayView< const GlobalOrdinal > &globalIDs, const Teuchos::ArrayView< int > &nodeIDs, const Teuchos::ArrayView< LocalOrdinal > &localIDs, const bool computeLIDs) const override
Find process IDs and (optionally) local IDs for the given global IDs.
std::string description() const override
A one-line human-readable description of this object.
Implementation of Directory for a distributed noncontiguous Map.
std::string description() const override
A one-line human-readable description of this object.
bool isOneToOne(const Teuchos::Comm< int > &comm) const override
Whether the Directory's input Map is (globally) one to one.
LookupStatus getEntriesImpl(const map_type &map, const Teuchos::ArrayView< const GlobalOrdinal > &globalIDs, const Teuchos::ArrayView< int > &nodeIDs, const Teuchos::ArrayView< LocalOrdinal > &localIDs, const bool computeLIDs) const override
Find process IDs and (optionally) local IDs for the given global IDs.
LookupStatus getEntriesImpl(const map_type &map, const Teuchos::ArrayView< const GlobalOrdinal > &globalIDs, const Teuchos::ArrayView< int > &nodeIDs, const Teuchos::ArrayView< LocalOrdinal > &localIDs, const bool computeLIDs) const override
Find process IDs and (optionally) local IDs for the given global IDs.
bool isOneToOne(const Teuchos::Comm< int > &comm) const override
Whether the Directory's input Map is (globally) one to one.
std::string description() const override
A one-line human-readable description of this object.
ReplicatedDirectory()=default
Constructor (that takes no arguments).
Sets up and executes a communication plan for a Tpetra DistObject.
A parallel distribution of indices over processes.
Implementation details of Tpetra.
void verbosePrintArray(std::ostream &out, const ArrayType &x, const char name[], const size_t maxNumToPrint)
Print min(x.size(), maxNumToPrint) entries of x.
Namespace Tpetra contains the class and methods constituting the Tpetra library.
void initialize(int *argc, char ***argv)
Initialize Tpetra.
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.
LookupStatus
Return status of Map remote index lookup (getRemoteIndexList()).
size_t global_size_t
Global size_t object.