Tpetra parallel linear algebra  Version of the Day
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
Tpetra_Distributor.cpp
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 #include "Tpetra_Distributor.hpp"
13 #include "Tpetra_Util.hpp"
14 #include "Tpetra_Details_makeValidVerboseStream.hpp"
15 #include "Teuchos_StandardParameterEntryValidators.hpp"
16 #include "Teuchos_VerboseObjectParameterListHelpers.hpp"
17 #include <numeric>
18 
19 namespace Tpetra {
20 // We set default values of Distributor's Boolean parameters here,
21 // in this one place. That way, if we want to change the default
22 // value of a parameter, we don't have to search the whole file to
23 // ensure a consistent setting.
24 namespace {
25 // Default value of the "Debug" parameter.
26 const bool tpetraDistributorDebugDefault = false;
27 } // namespace
28 
29 #if defined(TPETRA_ENABLE_DEPRECATED_CODE)
30 Teuchos::Array<std::string>
33 }
34 #endif
35 
37  Distributor(const Teuchos::RCP<const Teuchos::Comm<int> >& comm,
38  const Teuchos::RCP<Teuchos::FancyOStream>& /* out */,
39  const Teuchos::RCP<Teuchos::ParameterList>& plist)
40  : plan_(comm) {
41  this->setParameterList(plist);
42 }
43 
45  Distributor(const Teuchos::RCP<const Teuchos::Comm<int> >& comm)
46  : Distributor(comm, Teuchos::null, Teuchos::null) {}
47 
49  Distributor(const Teuchos::RCP<const Teuchos::Comm<int> >& comm,
50  const Teuchos::RCP<Teuchos::FancyOStream>& out)
51  : Distributor(comm, out, Teuchos::null) {}
52 
54  Distributor(const Teuchos::RCP<const Teuchos::Comm<int> >& comm,
55  const Teuchos::RCP<Teuchos::ParameterList>& plist)
56  : Distributor(comm, Teuchos::null, plist) {}
57 
59  Distributor(const Distributor& distributor)
60  : plan_(distributor.plan_)
61  , actor_(distributor.actor_)
62  , verbose_(distributor.verbose_)
63  , reverseDistributor_(distributor.reverseDistributor_) {
64  using Teuchos::ParameterList;
65  using Teuchos::RCP;
66  using Teuchos::rcp;
67 
68  RCP<const ParameterList> rhsList = distributor.getParameterList();
69  RCP<ParameterList> newList = rhsList.is_null() ? Teuchos::null : Teuchos::parameterList(*rhsList);
70  this->setParameterList(newList);
71 }
72 
74  using Teuchos::ParameterList;
75  using Teuchos::parameterList;
76  using Teuchos::RCP;
77 
78  std::swap(plan_, rhs.plan_);
79  std::swap(actor_, rhs.actor_);
80  std::swap(verbose_, rhs.verbose_);
81  std::swap(reverseDistributor_, rhs.reverseDistributor_);
82 
83  // Swap parameter lists. If they are the same object, make a deep
84  // copy first, so that modifying one won't modify the other one.
85  RCP<ParameterList> lhsList = this->getNonconstParameterList();
86  RCP<ParameterList> rhsList = rhs.getNonconstParameterList();
87  if (lhsList.getRawPtr() == rhsList.getRawPtr() && !rhsList.is_null()) {
88  rhsList = parameterList(*rhsList);
89  }
90  if (!rhsList.is_null()) {
91  this->setMyParamList(rhsList);
92  }
93  if (!lhsList.is_null()) {
94  rhs.setMyParamList(lhsList);
95  }
96 
97  // We don't need to swap timers, because all instances of
98  // Distributor use the same timers.
99 }
100 
101 bool Distributor::getVerbose() {
102  return Details::Behavior::verbose("Distributor") ||
103  Details::Behavior::verbose("Tpetra::Distributor");
104 }
105 
106 std::unique_ptr<std::string>
107 Distributor::
108  createPrefix(const char methodName[]) const {
109  return Details::createPrefix(
110  plan_.getComm().getRawPtr(), "Distributor", methodName);
111 }
112 
113 void Distributor::
114  setParameterList(const Teuchos::RCP<Teuchos::ParameterList>& plist) {
115  using std::endl;
116  using Teuchos::FancyOStream;
117  using Teuchos::getIntegralValue;
118  using Teuchos::includesVerbLevel;
119  using Teuchos::ParameterList;
120  using Teuchos::parameterList;
121  using Teuchos::RCP;
122  using ::Tpetra::Details::Behavior;
123 
124  if (!plist.is_null()) {
125  RCP<const ParameterList> validParams = getValidParameters();
126  plist->validateParametersAndSetDefaults(*validParams);
127 
128  // ParameterListAcceptor semantics require pointer identity of the
129  // sublist passed to setParameterList(), so we save the pointer.
130  this->setMyParamList(plist);
131 
132  RCP<ParameterList> planParams(plist);
133  planParams->remove("Debug", false);
134  planParams->remove("VerboseObject", false);
135  plan_.setParameterList(planParams);
136  }
137 }
138 
139 Teuchos::RCP<const Teuchos::ParameterList>
141  using Teuchos::Array;
142  using Teuchos::ParameterList;
143  using Teuchos::parameterList;
144  using Teuchos::RCP;
145  using Teuchos::setStringToIntegralParameter;
146 
147  const bool debug = tpetraDistributorDebugDefault;
148 
149  Array<std::string> sendTypes = Details::distributorSendTypes();
150  const Array<Details::EDistributorSendType> sendTypeEnums = Details::distributorSendTypeEnums();
151  const std::string validatedSendType = Details::validSendTypeOrThrow(Details::Behavior::defaultSendType());
152 
153  RCP<ParameterList> plist = parameterList("Tpetra::Distributor");
154  setStringToIntegralParameter<Details::EDistributorSendType>("Send type",
155  validatedSendType,
156  "When using MPI, the variant of send to use in "
157  "do[Reverse]Posts()",
158  sendTypes(), sendTypeEnums(), plist.getRawPtr());
159  plist->set("Debug", debug,
160  "Whether to print copious debugging output on "
161  "all processes.");
162  plist->set("Timer Label", "", "Label for Time Monitor output");
163 
164  // mfh 24 Dec 2015: Tpetra no longer inherits from
165  // Teuchos::VerboseObject, so it doesn't need the "VerboseObject"
166  // sublist. However, we retain the "VerboseObject" sublist
167  // anyway, for backwards compatibility (otherwise the above
168  // validation would fail with an invalid parameter name, should
169  // the user still want to provide this list).
170  Teuchos::setupVerboseObjectSublist(&*plist);
171  return Teuchos::rcp_const_cast<const ParameterList>(plist);
172 }
173 
174 size_t Distributor::getTotalReceiveLength() const { return plan_.getTotalReceiveLength(); }
175 
176 size_t Distributor::getNumReceives() const { return plan_.getNumReceives(); }
177 
178 bool Distributor::hasSelfMessage() const { return plan_.hasSelfMessage(); }
179 
180 size_t Distributor::getNumSends() const { return plan_.getNumSends(); }
181 
182 size_t Distributor::getMaxSendLength() const { return plan_.getMaxSendLength(); }
183 
184 Teuchos::ArrayView<const int> Distributor::getProcsFrom() const { return plan_.getProcsFrom(); }
185 
186 Teuchos::ArrayView<const size_t> Distributor::getLengthsFrom() const { return plan_.getLengthsFrom(); }
187 
188 Teuchos::ArrayView<const int> Distributor::getProcsTo() const { return plan_.getProcsTo(); }
189 
190 Teuchos::ArrayView<const size_t> Distributor::getLengthsTo() const { return plan_.getLengthsTo(); }
191 
192 Teuchos::RCP<Distributor>
193 Distributor::getReverse(bool create) const {
194  if (reverseDistributor_.is_null() && create) {
195  createReverseDistributor();
196  }
197  TEUCHOS_TEST_FOR_EXCEPTION(reverseDistributor_.is_null() && create, std::logic_error,
198  "The reverse "
199  "Distributor is null after createReverseDistributor returned. "
200  "Please report this bug to the Tpetra developers.");
201  return reverseDistributor_;
202 }
203 
204 void Distributor::createReverseDistributor() const {
205  reverseDistributor_ = Teuchos::rcp(new Distributor(plan_.getComm()));
206  reverseDistributor_->plan_ = *plan_.getReversePlan();
207  reverseDistributor_->verbose_ = verbose_;
208 
209  // requests_: Allocated on demand.
210  // reverseDistributor_: See note below
211 
212  // I am my reverse Distributor's reverse Distributor.
213  // Thus, it would be legit to do the following:
214  //
215  // reverseDistributor_->reverseDistributor_ = Teuchos::rcp (this, false);
216  //
217  // (Note use of a "weak reference" to avoid a circular RCP
218  // dependency.) The only issue is that if users hold on to the
219  // reverse Distributor but let go of the forward one, this
220  // reference won't be valid anymore. However, the reverse
221  // Distributor is really an implementation detail of Distributor
222  // and not meant to be used directly, so we don't need to do this.
223  reverseDistributor_->reverseDistributor_ = Teuchos::null;
224 }
225 
227  actor_.doWaits(plan_);
228 }
229 
231  // call doWaits() on the reverse Distributor, if it exists
232  if (!reverseDistributor_.is_null()) {
233  reverseDistributor_->doWaits();
234  }
235 }
236 
237 std::string Distributor::description() const {
238  std::ostringstream out;
239 
240  out << "\"Tpetra::Distributor\": {";
241  const std::string label = this->getObjectLabel();
242  if (label != "") {
243  out << "Label: " << label << ", ";
244  }
245  out << "How initialized: "
246  << Details::DistributorHowInitializedEnumToString(plan_.howInitialized())
247  << ", Parameters: {"
248  << "Send type: "
249  << DistributorSendTypeEnumToString(plan_.getSendType())
250  << ", Debug: " << (verbose_ ? "true" : "false")
251  << "}}";
252  return out.str();
253 }
254 
255 std::string
256 Distributor::
257  localDescribeToString(const Teuchos::EVerbosityLevel vl) const {
258  using std::endl;
259  using Teuchos::toString;
260  using Teuchos::VERB_EXTREME;
261  using Teuchos::VERB_HIGH;
262 
263  // This preserves current behavior of Distributor.
264  if (vl <= Teuchos::VERB_LOW || plan_.getComm().is_null()) {
265  return std::string();
266  }
267 
268  auto outStringP = Teuchos::rcp(new std::ostringstream());
269  auto outp = Teuchos::getFancyOStream(outStringP); // returns RCP
270  Teuchos::FancyOStream& out = *outp;
271 
272  const int myRank = plan_.getComm()->getRank();
273  const int numProcs = plan_.getComm()->getSize();
274  out << "Process " << myRank << " of " << numProcs << ":" << endl;
275  Teuchos::OSTab tab1(out);
276 
277  out << "selfMessage: " << hasSelfMessage() << endl;
278  out << "numSends: " << getNumSends() << endl;
279  if (vl == VERB_HIGH || vl == VERB_EXTREME) {
280  out << "procsTo: " << toString(plan_.getProcsTo()) << endl;
281  out << "lengthsTo: " << toString(plan_.getLengthsTo()) << endl;
282  out << "maxSendLength: " << getMaxSendLength() << endl;
283  }
284  if (vl == VERB_EXTREME) {
285  out << "startsTo: " << toString(plan_.getStartsTo()) << endl;
286  out << "indicesTo: " << toString(plan_.getIndicesTo()) << endl;
287  }
288  if (vl == VERB_HIGH || vl == VERB_EXTREME) {
289  out << "numReceives: " << getNumReceives() << endl;
290  out << "totalReceiveLength: " << getTotalReceiveLength() << endl;
291  out << "lengthsFrom: " << toString(plan_.getLengthsFrom()) << endl;
292  out << "procsFrom: " << toString(plan_.getProcsFrom()) << endl;
293  }
294 
295  out.flush(); // make sure the ostringstream got everything
296  return outStringP->str();
297 }
298 
299 void Distributor::
300  describe(Teuchos::FancyOStream& out,
301  const Teuchos::EVerbosityLevel verbLevel) const {
302  using std::endl;
303  using Teuchos::VERB_DEFAULT;
304  using Teuchos::VERB_EXTREME;
305  using Teuchos::VERB_HIGH;
306  using Teuchos::VERB_LOW;
307  using Teuchos::VERB_MEDIUM;
308  using Teuchos::VERB_NONE;
309  const Teuchos::EVerbosityLevel vl =
310  (verbLevel == VERB_DEFAULT) ? VERB_LOW : verbLevel;
311 
312  if (vl == VERB_NONE) {
313  return; // don't print anything
314  }
315  // If this Distributor's Comm is null, then the the calling
316  // process does not participate in Distributor-related collective
317  // operations with the other processes. In that case, it is not
318  // even legal to call this method. The reasonable thing to do in
319  // that case is nothing.
320  if (plan_.getComm().is_null()) {
321  return;
322  }
323  const int myRank = plan_.getComm()->getRank();
324  const int numProcs = plan_.getComm()->getSize();
325 
326  // Only Process 0 should touch the output stream, but this method
327  // in general may need to do communication. Thus, we may need to
328  // preserve the current tab level across multiple "if (myRank ==
329  // 0) { ... }" inner scopes. This is why we sometimes create
330  // OSTab instances by pointer, instead of by value. We only need
331  // to create them by pointer if the tab level must persist through
332  // multiple inner scopes.
333  Teuchos::RCP<Teuchos::OSTab> tab0, tab1;
334 
335  if (myRank == 0) {
336  // At every verbosity level but VERB_NONE, Process 0 prints.
337  // By convention, describe() always begins with a tab before
338  // printing.
339  tab0 = Teuchos::rcp(new Teuchos::OSTab(out));
340  // We quote the class name because it contains colons.
341  // This makes the output valid YAML.
342  out << "\"Tpetra::Distributor\":" << endl;
343  tab1 = Teuchos::rcp(new Teuchos::OSTab(out));
344 
345  const std::string label = this->getObjectLabel();
346  if (label != "") {
347  out << "Label: " << label << endl;
348  }
349  out << "Number of processes: " << numProcs << endl
350  << "How initialized: "
351  << Details::DistributorHowInitializedEnumToString(plan_.howInitialized())
352  << endl;
353  {
354  out << "Parameters: " << endl;
355  Teuchos::OSTab tab2(out);
356  out << "\"Send type\": "
357  << DistributorSendTypeEnumToString(plan_.getSendType()) << endl
358  << "\"Debug\": " << (verbose_ ? "true" : "false") << endl;
359  }
360  } // if myRank == 0
361 
362  // This is collective over the Map's communicator.
363  if (vl > VERB_LOW) {
364  const std::string lclStr = this->localDescribeToString(vl);
365  Tpetra::Details::gathervPrint(out, lclStr, *plan_.getComm());
366  }
367 
368  out << "Reverse Distributor:";
369  if (reverseDistributor_.is_null()) {
370  out << " null" << endl;
371  } else {
372  out << endl;
373  reverseDistributor_->describe(out, vl);
374  }
375 }
376 
377 size_t
379  createFromSends(const Teuchos::ArrayView<const int>& exportProcIDs) {
380  return plan_.createFromSends(exportProcIDs);
381 }
382 
383 void Distributor::
384  createFromSendsAndRecvs(const Teuchos::ArrayView<const int>& exportProcIDs,
385  const Teuchos::ArrayView<const int>& remoteProcIDs) {
386  plan_.createFromSendsAndRecvs(exportProcIDs, remoteProcIDs);
387 }
388 
389 } // namespace Tpetra
size_t getNumReceives() const
The number of processes from which we will receive data.
std::string description() const
Return a one-line description of this object.
Teuchos::RCP< Distributor > getReverse(bool create=true) const
A reverse communication plan Distributor.
Declaration of a function that prints strings from each process.
Teuchos::RCP< const Teuchos::ParameterList > getValidParameters() const
List of valid Distributor parameters.
const std::string & validSendTypeOrThrow(const std::string &s)
Valid enum values of distributor send types.
void swap(Distributor &rhs)
Swap the contents of rhs with those of *this.
Teuchos::ArrayView< const size_t > getLengthsFrom() const
Number of values this process will receive from each process.
Teuchos::ArrayView< const int > getProcsFrom() const
Ranks of the processes sending values to this process.
size_t createFromSends(const Teuchos::ArrayView< const int > &exportProcIDs)
Set up Distributor using list of process ranks to which this process will send.
void gathervPrint(std::ostream &out, const std::string &s, const Teuchos::Comm< int > &comm)
On Process 0 in the given communicator, print strings from each process in that communicator, in rank order.
Teuchos::Array< EDistributorSendType > distributorSendTypeEnums()
Valid enum values of distributor send types.
Teuchos::ArrayView< const int > getProcsTo() const
Ranks of the processes to which this process will send values.
void createFromSendsAndRecvs(const Teuchos::ArrayView< const int > &exportProcIDs, const Teuchos::ArrayView< const int > &remoteProcIDs)
Set up Distributor using list of process ranks to which to send, and list of process ranks from which...
bool hasSelfMessage() const
Whether the calling process will send or receive messages to itself.
Sets up and executes a communication plan for a Tpetra DistObject.
static bool verbose()
Whether Tpetra is in verbose mode.
size_t getTotalReceiveLength() const
Total number of values this process will receive from other processes.
void setParameterList(const Teuchos::RCP< Teuchos::ParameterList > &plist)
Set Distributor parameters.
Teuchos::ArrayView< const size_t > getLengthsTo() const
Number of values this process will send to each process.
std::string DistributorHowInitializedEnumToString(EDistributorHowInitialized how)
Convert an EDistributorHowInitialized enum value to a string.
Stand-alone utility functions and macros.
Teuchos::Array< std::string > distributorSendTypes()
Valid string values for Distributor&#39;s &quot;Send type&quot; parameter.
size_t getNumSends() const
The number of processes to which we will send data.
void describe(Teuchos::FancyOStream &out, const Teuchos::EVerbosityLevel verbLevel=Teuchos::Describable::verbLevel_default) const
Describe this object in a human-readable way to the given output stream.
size_t getMaxSendLength() const
Maximum number of values this process will send to another single process.
static std::string defaultSendType()
Default send type.
std::unique_ptr< std::string > createPrefix(const int myRank, const char prefix[])
Create string prefix for each line of verbose output.
Definition: Tpetra_Util.cpp:69
Distributor(const Teuchos::RCP< const Teuchos::Comm< int > > &comm)
Construct using the specified communicator and default parameters.
Declaration of Tpetra::Details::Behavior, a class that describes Tpetra&#39;s behavior.