Tpetra parallel linear algebra  Version of the Day
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
Tpetra_Core.cpp
1 // @HEADER
2 // ***********************************************************************
3 //
4 // Tpetra: Templated Linear Algebra Services Package
5 // Copyright (2008) Sandia Corporation
6 //
7 // Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
8 // the U.S. Government retains certain rights in this software.
9 //
10 // Redistribution and use in source and binary forms, with or without
11 // modification, are permitted provided that the following conditions are
12 // met:
13 //
14 // 1. Redistributions of source code must retain the above copyright
15 // notice, this list of conditions and the following disclaimer.
16 //
17 // 2. Redistributions in binary form must reproduce the above copyright
18 // notice, this list of conditions and the following disclaimer in the
19 // documentation and/or other materials provided with the distribution.
20 //
21 // 3. Neither the name of the Corporation nor the names of the
22 // contributors may be used to endorse or promote products derived from
23 // this software without specific prior written permission.
24 //
25 // THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "AS IS" AND ANY
26 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SANDIA CORPORATION OR THE
29 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
30 // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
31 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
32 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
33 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
34 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
35 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 //
37 // ************************************************************************
38 // @HEADER
39 
40 #include "Tpetra_Core.hpp"
41 #include "Tpetra_Details_mpiIsInitialized.hpp"
42 
43 #ifdef HAVE_TPETRACORE_MPI
44 # include <Teuchos_DefaultMpiComm.hpp> // this includes mpi.h too
45 #endif // HAVE_TPETRACORE_MPI
46 #include <Teuchos_DefaultSerialComm.hpp>
47 
48 #include <Kokkos_Core.hpp>
49 
50 namespace Tpetra {
51  namespace { // (anonymous)
52 
53  class HideOutputExceptOnProcess0 {
54  public:
55  HideOutputExceptOnProcess0 (std::ostream& stream,
56  const int myRank) :
57  stream_ (stream),
58  originalBuffer_ (stream.rdbuf ())
59  {
60  if (myRank != 0) {
61  stream.rdbuf (blackHole_.rdbuf ());
62  }
63  }
64 
65  ~HideOutputExceptOnProcess0 () {
66  stream_.rdbuf (originalBuffer_);
67  }
68  private:
69  std::ostream& stream_;
70  decltype (std::cout.rdbuf ()) originalBuffer_;
71  Teuchos::oblackholestream blackHole_;
72  };
73 
74 #if defined(HAVE_TPETRACORE_MPI)
75  bool mpiIsInitializedAndNotFinalized ()
76  {
77  int isInitialized = 0;
78  int isFinalized = 0;
79  // Not sure if MPI_Initialized or MPI_Finalized meet the strong
80  // exception guarantee.
81  try {
82  (void) MPI_Initialized (&isInitialized);
83  }
84  catch (...) {
85  isInitialized = 0;
86  }
87  try {
88  (void) MPI_Finalized (&isFinalized);
89  }
90  catch (...) {
91  isFinalized = 0;
92  }
93  return isInitialized != 0 && isFinalized == 0;
94  }
95 
96  int getRankHarmlessly (MPI_Comm comm)
97  {
98  int myRank = 0;
99  if (mpiIsInitializedAndNotFinalized ()) {
100  try {
101  (void) MPI_Comm_rank (comm, &myRank);
102  }
103  catch (...) {
104  // Not sure if MPI_Comm_rank meets strong exception guarantee
105  myRank = 0;
106  }
107  }
108  return myRank;
109  }
110 #endif // defined(HAVE_TPETRACORE_MPI)
111 
112 
113  // Whether one of the Tpetra::initialize() functions has been called before.
114  bool tpetraIsInitialized_ = false;
115 
116  // Whether Tpetra initialized Kokkos. Tpetra::finalize only
117  // finalizes Kokkos if it initialized Kokkos. Otherwise,
118  // something else initialized Kokkos and is responsible for
119  // finalizing it.
120  bool tpetraInitializedKokkos_ = false;
121 
122 #ifdef HAVE_TPETRACORE_MPI
123  // Whether Tpetra initialized MPI. Tpetra::finalize only
124  // finalizes MPI if it initialized MPI. Otherwise, something else
125  // initialized MPI and is responsible for finalizing it.
126  bool tpetraInitializedMpi_ = false;
127 #endif // HAVE_TPETRACORE_MPI
128 
129  // Tpetra's default communicator, wrapped in a Teuchos wrapper.
130  // After Tpetra::finalize() is called, this GOES AWAY (is set to null).
131  Teuchos::RCP<const Teuchos::Comm<int> > wrappedDefaultComm_;
132 
133  // This takes the same arguments as (the first two of) initialize().
134  void initKokkosIfNeeded (int* argc, char*** argv, const int myRank)
135  {
136  if (! tpetraInitializedKokkos_) {
137  // Kokkos doesn't have a global is_initialized(). However,
138  // Kokkos::initialize() always initializes the default execution
139  // space, so it suffices to check whether that was initialized.
140  const bool kokkosIsInitialized =
141  Kokkos::is_initialized ();
142  if (! kokkosIsInitialized) {
143  HideOutputExceptOnProcess0 hideCerr (std::cerr, myRank);
144  HideOutputExceptOnProcess0 hideCout (std::cout, myRank);
145 
146  // Unlike MPI_Init, Kokkos promises not to modify argc and argv.
147  Kokkos::initialize (*argc, *argv);
148  tpetraInitializedKokkos_ = true;
149  }
150  }
151 
152  const bool kokkosIsInitialized =
153  Kokkos::is_initialized ();
154  TEUCHOS_TEST_FOR_EXCEPTION
155  (! kokkosIsInitialized, std::logic_error, "At the end of "
156  "initKokkosIfNeeded, Kokkos is not initialized. "
157  "Please report this bug to the Tpetra developers.");
158  }
159 
160 #ifdef HAVE_TPETRACORE_MPI
161  // This takes the same arguments as MPI_Init and the first two
162  // arguments of initialize().
163  void initMpiIfNeeded (int* argc, char*** argv)
164  {
165  // Both MPI_Initialized and MPI_Finalized report true after
166  // MPI_Finalize has been called. It's not legal to call
167  // MPI_Init after MPI_Finalize has been called (see MPI 3.0
168  // Standard, Section 8.7). It would be unusual for users to
169  // want to use Tpetra after MPI_Finalize has been called, but
170  // there's no reason why we should forbid it. It just means
171  // that Tpetra will need to run without MPI.
172 
173  const bool mpiReady = mpiIsInitializedAndNotFinalized ();
174  if (! mpiReady) {
175  // Tpetra doesn't currently need to call MPI_Init_thread,
176  // since with Tpetra, only one thread ever calls MPI
177  // functions. If we ever want to explore
178  // MPI_THREAD_MULTIPLE, here would be the place to call
179  // MPI_Init_thread.
180  const int err = MPI_Init (argc, argv);
181  TEUCHOS_TEST_FOR_EXCEPTION
182  (err != MPI_SUCCESS, std::runtime_error, "MPI_Init failed with "
183  "error code " << err << " != MPI_SUCCESS. If MPI was set up "
184  "correctly, then this should not happen, since we have already "
185  "checked that MPI_Init (or MPI_Init_thread) has not yet been "
186  "called. This may indicate that your MPI library is corrupted "
187  "or that it is incorrectly linked to your program.");
188  tpetraInitializedMpi_ = true;
189  }
190  }
191 #endif // HAVE_TPETRACORE_MPI
192 
193  } // namespace (anonymous)
194 
195  bool isInitialized () {
196  return tpetraIsInitialized_;
197  }
198 
199  Teuchos::RCP<const Teuchos::Comm<int> > getDefaultComm ()
200  {
201  // It's technically not correct to call this function if Tpetra
202  // has not yet been initialized, but requiring that may break some
203  // downstream tests.
204  //
205  // This function initializes wrappedDefaultComm_ lazily.
206  // Tpetra::initialize should not set it up.
207  if (wrappedDefaultComm_.is_null ()) {
208  Teuchos::RCP<const Teuchos::Comm<int> > comm;
209 #ifdef HAVE_TPETRACORE_MPI
210  // Teuchos::MpiComm's constructor used to invoke MPI collectives.
211  // It still reserves the right to do so. This means MPI must be
212  // initialized and not finalized.
213  const bool mpiReady = mpiIsInitializedAndNotFinalized ();
214  if (mpiReady) {
215  comm = Teuchos::rcp (new Teuchos::MpiComm<int> (MPI_COMM_WORLD));
216  }
217  else {
218  comm = Teuchos::rcp (new Teuchos::SerialComm<int> ());
219  }
220 #else
221  comm = Teuchos::rcp (new Teuchos::SerialComm<int> ());
222 #endif // HAVE_TPETRACORE_MPI
223  wrappedDefaultComm_ = comm;
224  }
225  return wrappedDefaultComm_;
226  }
227 
228  void initialize (int* argc, char*** argv)
229  {
230  if (! tpetraIsInitialized_) {
231 #if defined(HAVE_TPETRACORE_MPI)
232  initMpiIfNeeded (argc, argv);
233  // It's technically legal to initialize Tpetra after
234  // MPI_Finalize has been called. This means that we can't call
235  // MPI_Comm_rank without first checking MPI_Finalized.
236  const int myRank = getRankHarmlessly (MPI_COMM_WORLD);
237 #else
238  const int myRank = 0;
239 #endif // defined(HAVE_TPETRACORE_MPI)
240  initKokkosIfNeeded (argc, argv, myRank);
241  }
242  tpetraIsInitialized_ = true;
243  }
244 
245 #ifdef HAVE_TPETRACORE_MPI
246  void initialize (int* argc, char*** argv, MPI_Comm comm)
247  {
248  if (! tpetraIsInitialized_) {
249 #if defined(HAVE_TPETRACORE_MPI)
250  initMpiIfNeeded (argc, argv);
251  // It's technically legal to initialize Tpetra after
252  // MPI_Finalize has been called. This means that we can't call
253  // MPI_Comm_rank without first checking MPI_Finalized.
254  const int myRank = getRankHarmlessly (comm);
255 #else
256  const int myRank = 0;
257 #endif // defined(HAVE_TPETRACORE_MPI)
258  initKokkosIfNeeded (argc, argv, myRank);
259  }
260  tpetraIsInitialized_ = true;
261 
262  // Set the default communicator. We set it here, after the above
263  // initialize() call, just in case users have not yet initialized
264  // MPI. (This is legal if users pass in a predefined
265  // communicator, like MPI_COMM_WORLD or MPI_COMM_SELF.)
266  //
267  // What if users have already called initialize() before, but with
268  // a different default communicator? There are two possible
269  // things we could do here:
270  //
271  // 1. Test via MPI_Comm_compare whether comm differs from the
272  // raw MPI communicator in wrappedDefaultComm_ (if indeed it
273  // is an MpiComm).
274  // 2. Accept that the user might want to change the default
275  // communicator, and let them do it.
276  //
277  // I prefer #2. Perhaps it would be sensible to print a warning
278  // here, but on which process? Would we use the old or the new
279  // communicator to find that process' rank? We don't want to use
280  // MPI_COMM_WORLD's Process 0, since neither communicator might
281  // include that process. Furthermore, in some environments, only
282  // Process 0 in MPI_COMM_WORLD is allowed to do I/O. Thus, we
283  // just let the change go without a warning.
284  wrappedDefaultComm_ = Teuchos::rcp (new Teuchos::MpiComm<int> (comm));
285  }
286 #endif // HAVE_TPETRACORE_MPI
287 
288  void
289  initialize (int* argc, char*** argv,
290  const Teuchos::RCP<const Teuchos::Comm<int> >& comm)
291  {
292  if (! tpetraIsInitialized_) {
293 #if defined(HAVE_TPETRACORE_MPI)
294  initMpiIfNeeded (argc, argv);
295 #endif // defined(HAVE_TPETRACORE_MPI)
296  // It's technically legal to initialize Tpetra after
297  // MPI_Finalize has been called. This means that we can't call
298  // MPI_Comm_rank without first checking MPI_Finalized.
299  const int myRank = comm->getRank ();
300  initKokkosIfNeeded (argc, argv, myRank);
301  }
302  tpetraIsInitialized_ = true;
303  wrappedDefaultComm_ = comm;
304  }
305 
306  void finalize ()
307  {
308  if (! tpetraIsInitialized_) {
309  return; // user didn't call initialize(), so do nothing at all
310  }
311 
312  // Tpetra should only finalize Kokkos if it initialized Kokkos.
313  // See Github Issue #434.
314  if (tpetraInitializedKokkos_) {
315  Kokkos::finalize ();
316  }
317 
318  // Make sure that no outstanding references to the communicator
319  // remain. If users gave initialize() an MPI_Comm, _they_ are
320  // responsible for freeing it before calling finalize().
321  wrappedDefaultComm_ = Teuchos::null;
322 
323 #ifdef HAVE_TPETRACORE_MPI
324  // Tpetra should only finalize MPI if it initialized MPI.
325  // See Github Issue #434.
326  if (tpetraInitializedMpi_) {
327  // finalize() is a kind of destructor, so it's a bad idea to
328  // throw an exception on error. MPI implementations do have
329  // the option to throw on error, so let's catch that here.
330  try {
331  if (Details::mpiIsInitialized ()) {
332  // This must be called by the same thread that called
333  // MPI_Init or MPI_Init_thread (possibly, but not
334  // necessarily, in Tpetra::initialize()).
335  (void) MPI_Finalize ();
336  }
337  }
338  catch (...) {}
339  }
340 #endif // HAVE_TPETRACORE_MPI
341 
342  tpetraIsInitialized_ = false; // it's not anymore.
343  }
344 
345  ScopeGuard::ScopeGuard (int* argc, char*** argv)
346  {
347  ::Tpetra::initialize (argc, argv);
348  }
349 
350 #ifdef HAVE_TPETRA_MPI
351  ScopeGuard::ScopeGuard (int* argc, char*** argv, MPI_Comm comm)
352  {
353  ::Tpetra::initialize (argc, argv, comm);
354  }
355 #endif // HAVE_TPETRA_MPI
356 
358  {
359  ::Tpetra::finalize ();
360  }
361 
362 } // namespace Tpetra
~ScopeGuard()
Finalize Tpetra.
bool isInitialized()
Whether Tpetra is in an initialized state.
void initialize(int *argc, char ***argv)
Initialize Tpetra.
void finalize()
Finalize Tpetra.
Functions for initializing and finalizing Tpetra.
bool mpiIsInitialized()
Has MPI_Init been called (on this process)?
Teuchos::RCP< const Teuchos::Comm< int > > getDefaultComm()
Get Tpetra&#39;s default communicator.
ScopeGuard()=delete
Default constructor (FORBIDDEN)