deal.II version GIT relicensing-6834-g5b78e6bcdf 2026-10-01 11:20:01+00:00
\(\newcommand{\dealvcentcolon}{\mathrel{\mathop{:}}}\) \(\newcommand{\dealcoloneq}{\dealvcentcolon\mathrel{\mkern-1.2mu}=}\) \(\newcommand{\jump}[1]{\left[\!\left[ #1 \right]\!\right]}\) \(\newcommand{\average}[1]{\left\{\!\left\{ #1 \right\}\!\right\}}\)
Loading...
Searching...
No Matches
init_finalize.cc
Go to the documentation of this file.
1// ---------------------------------------------------------------------
2//
3// Copyright (C) 2023 - 2026 by the deal.II authors
4//
5// This file is part of the deal.II library.
6//
7// The deal.II library is free software; you can use it, redistribute
8// it, and/or modify it under the terms of the GNU Lesser General
9// Public License as published by the Free Software Foundation; either
10// version 2.1 of the License, or (at your option) any later version.
11// The full text of the license can be found in the file LICENSE.md at
12// the top level directory of deal.II.
13//
14// ---------------------------------------------------------------------
15
17#include <deal.II/base/mpi.h>
19
23
24#include <Kokkos_Core.hpp>
25
26#ifdef DEAL_II_GMSH_WITH_API
27# include <gmsh.h>
28#endif
29
30#ifdef DEAL_II_WITH_TRILINOS
31# ifdef DEAL_II_WITH_MPI
34
36# ifdef DEAL_II_TRILINOS_WITH_EPETRA
37# include <Epetra_MpiComm.h>
38# endif
40# endif
41#endif
42
43#ifdef DEAL_II_WITH_PETSC
46
47# include <petscsys.h>
48#endif
49
50#ifdef DEAL_II_WITH_SLEPC
52
53# include <slepcsys.h>
54#endif
55
56#ifdef DEAL_II_WITH_P4EST
57# include <p4est_bits.h>
58#endif
59
60#ifdef DEAL_II_TRILINOS_WITH_ZOLTAN
61# include <zoltan_cpp.h>
62#endif
63
64#ifdef DEAL_II_WITH_PSBLAS
65# include <psb_c_base.h>
66#endif
67
68#ifdef DEAL_II_HAVE_FP_EXCEPTIONS
69# include <cfenv>
70#endif
71
72
73#include <set>
74#include <string>
75
76
78
79
80/* Force initialization of static struct: */
82
83
84InitFinalize::InitFinalize([[maybe_unused]] int &argc,
85 [[maybe_unused]] char **&argv,
86 const InitializeLibrary &libraries,
87 const unsigned int max_num_threads)
88 : libraries(libraries)
89{
90 static bool constructor_has_already_run = false;
91 Assert(constructor_has_already_run == false,
92 ExcMessage("You can only create a single object of this class "
93 "in a program since it initializes the MPI system."));
94
95
96 [[maybe_unused]] int ierr = 0;
97#ifdef DEAL_II_WITH_MPI
98 if (static_cast<bool>(libraries & InitializeLibrary::MPI))
99 {
100# ifdef DEAL_II_HAVE_FP_EXCEPTIONS
101 // Some MPI installations, during initializing the MPI system,
102 // read configuration files via libxml that computes some NaN
103 // constants at run time. This triggers floating point exceptions
104 // if these have been requested by the application program (or
105 // as part of our own test suite). To avoid this, save FP flags,
106 // reset these flags to allow such computations, and at the end
107 // of the block re-set the flags to the saved state.
108 //
109 // References:
110 // https://stackoverflow.com/questions/79739650/libxml2-throws-sigfpe-in-debian-13
111 fenv_t fe_state;
112 ierr = fegetenv(&fe_state);
113 AssertThrow(ierr == 0, ExcInternalError());
114
115 fedisableexcept(FE_DIVBYZERO | FE_INVALID);
116# endif
117
118 // if we have PETSc, we will initialize it and let it handle MPI.
119 // Otherwise, we will do it.
120 int MPI_has_been_started = 0;
121 ierr = MPI_Initialized(&MPI_has_been_started);
122 AssertThrowMPI(ierr);
123 AssertThrow(MPI_has_been_started == 0,
124 ExcMessage("MPI error. You can only start MPI once!"));
125
126 int provided;
127 // this works like ierr = MPI_Init (&argc, &argv); but tells MPI that
128 // we might use several threads but never call two MPI functions at the
129 // same time. For an explanation see on why we do this see
130 // http://www.open-mpi.org/community/lists/users/2010/03/12244.php
131 int wanted = MPI_THREAD_SERIALIZED;
132 ierr = MPI_Init_thread(&argc, &argv, wanted, &provided);
133 AssertThrowMPI(ierr);
134
135 // disable for now because at least some implementations always return
136 // MPI_THREAD_SINGLE.
137 // Assert(max_num_threads==1 || provided != MPI_THREAD_SINGLE,
138 // ExcMessage("MPI reports that we are not allowed to use multiple
139 // threads."));
140
141# ifdef DEAL_II_HAVE_FP_EXCEPTIONS
142 ierr = fesetenv(&fe_state);
143 AssertThrow(ierr == 0, ExcInternalError());
144# endif
145 }
146#endif
147
148 // we are allowed to call MPI_Init ourselves and PETScInitialize will
149 // detect this. This allows us to use MPI_Init_thread instead.
150#ifdef DEAL_II_WITH_PETSC
151 PetscErrorCode pierr;
152# ifdef DEAL_II_WITH_SLEPC
153 // Initialize SLEPc (with PETSc):
154 if (static_cast<bool>(libraries & InitializeLibrary::SLEPc))
155 {
156 finalize_petscslepc = SlepcInitializeCalled ? false : true;
157 pierr = SlepcInitialize(&argc, &argv, nullptr, nullptr);
159 }
160# else
161 // or just initialize PETSc alone:
162 if (static_cast<bool>(libraries & InitializeLibrary::PETSc))
163 {
164 finalize_petscslepc = PetscInitializeCalled ? false : true;
165 pierr = PetscInitialize(&argc, &argv, nullptr, nullptr);
166 AssertThrow(pierr == 0, ExcPETScError(pierr));
167 }
168# endif
169
170 // Disable PETSc exception handling. This just prints a large wall
171 // of text that is not particularly helpful for what we do:
172 if (static_cast<bool>(libraries & InitializeLibrary::SLEPc) ||
173 static_cast<bool>(libraries & InitializeLibrary::PETSc))
174 {
175 pierr = PetscPopSignalHandler();
176 AssertThrow(pierr == 0, ExcPETScError(pierr));
177 }
178#endif
179
180 // Initialize zoltan
181#ifdef DEAL_II_TRILINOS_WITH_ZOLTAN
182 if (static_cast<bool>(libraries & InitializeLibrary::Zoltan))
183 {
184 float version;
185 Zoltan_Initialize(argc, argv, &version);
186 }
187#endif
188
189 // Initialize p4est and libsc components
190#ifdef DEAL_II_WITH_P4EST
191 if (static_cast<bool>(libraries & InitializeLibrary::P4EST))
192 {
193# if DEAL_II_P4EST_VERSION_GTE(2, 5, 0, 0)
194 // This feature is broken in version 2.0.0 for calls to
195 // MPI_Comm_create_group (see cburstedde/p4est#30).
196 // Disabling it leads to more verbose p4est error messages
197 // which should be fine.
198 sc_init(MPI_COMM_WORLD, 0, 0, nullptr, SC_LP_SILENT);
199# endif
200 p4est_init(nullptr, SC_LP_SILENT);
201 }
202#endif
203
204 // Initialize PSBLAS
205#ifdef DEAL_II_WITH_PSBLAS
206 if (static_cast<bool>(libraries & InitializeLibrary::PSBLAS))
207 {
208 cctxt = psb_c_new_ctxt();
209 Assert(cctxt != nullptr,
211 "Failed to create PSBLAS context during MPI initialization."));
212 psb_c_init(cctxt);
213 psb_c_set_index_base(0); // Set index base to 0 (PSBLAS default is 1)
214 }
215#endif
216
217 // initialize GMSH
218#ifdef DEAL_II_GMSH_WITH_API
219 if (static_cast<bool>(libraries & InitializeLibrary::GMSH))
220 gmsh::initialize();
221#endif
222
223 constructor_has_already_run = true;
224
225
226 // Now also see how many threads we'd like to run
227 if (max_num_threads != numbers::invalid_unsigned_int)
228 {
229 // set maximum number of threads (also respecting the environment
230 // variable that the called function evaluates) based on what the
231 // user asked
232 MultithreadInfo::set_thread_limit(max_num_threads);
233 }
234 else
235 // user wants automatic choice
236 {
237 unsigned int n_threads = MultithreadInfo::n_cores();
238#ifdef DEAL_II_WITH_MPI
239 if (static_cast<bool>(libraries & InitializeLibrary::MPI))
240 {
241 int MPI_has_been_started = 0;
242 int ierr = MPI_Initialized(&MPI_has_been_started);
243 AssertThrowMPI(ierr);
244
245 // we need to figure out how many MPI processes there are on the
246 // current node, as well as how many CPU cores we have. for the
247 // first task, check what get_hostname() returns and then do an
248 // allgather so each processor gets the answer
249 //
250 // in calculating the length of the string, don't forget the
251 // terminating \0 on C-style strings
252 const std::string hostname = Utilities::System::get_hostname();
253
254 int my_hostname_size = hostname.size() + 1;
255 int max_hostname_size = -1;
256 ierr = MPI_Allreduce(&my_hostname_size,
257 &max_hostname_size,
258 1,
259 MPI_INT,
260 MPI_MAX,
261 MPI_COMM_WORLD);
262 AssertThrowMPI(ierr);
263 std::vector<char> hostname_array(max_hostname_size);
264 std::copy(hostname.c_str(),
265 hostname.c_str() + hostname.size() + 1,
266 hostname_array.begin());
267
268 int n_mpi_processes = 1;
269 if (MPI_has_been_started)
270 {
271 ierr = MPI_Comm_size(MPI_COMM_WORLD, &n_mpi_processes);
272 AssertThrowMPI(ierr);
273 }
274 std::vector<char> all_hostnames(max_hostname_size * n_mpi_processes);
275 ierr = MPI_Allgather(hostname_array.data(),
276 max_hostname_size,
277 MPI_CHAR,
278 all_hostnames.data(),
279 max_hostname_size,
280 MPI_CHAR,
281 MPI_COMM_WORLD);
282 AssertThrowMPI(ierr);
283
284 // search how often our own hostname appears and the how-manyth
285 // instance the current process represents
286 unsigned int n_local_processes = 0;
287 unsigned int nth_process_on_host = 0;
288 int rank = 0;
289 if (MPI_has_been_started)
290 {
291 ierr = MPI_Comm_rank(MPI_COMM_WORLD, &rank);
292 AssertThrowMPI(ierr);
293 }
294 for (int i = 0; i < n_mpi_processes; ++i)
295 if (std::string(all_hostnames.data() + i * max_hostname_size) ==
296 hostname)
297 {
298 ++n_local_processes;
299 if (i <= rank)
300 ++nth_process_on_host;
301 }
302 Assert(nth_process_on_host > 0, ExcInternalError());
303
304
305 // compute how many cores each process gets. if the number does not
306 // divide evenly, then we get one more core if we are among the
307 // first few processes
308 //
309 // if the number would be zero, round up to one since every process
310 // needs to have at least one thread
311 n_threads =
312 std::max(MultithreadInfo::n_cores() / n_local_processes +
313 (nth_process_on_host <=
314 MultithreadInfo::n_cores() % n_local_processes ?
315 1 :
316 0),
317 1U);
318 }
319#endif
320
321 // finally set this number of threads
323 }
324
325 // Initialize Kokkos
326 if (static_cast<bool>(libraries & InitializeLibrary::Kokkos))
327 {
328 // argv has argc+1 elements and the last one is a nullptr. For appending
329 // one element we thus create a new argv by copying the first argc
330 // elements, append the new option, and then a nullptr.
331 //
332 // We do get in trouble, though, if a user program is called with
333 // '--help' as a command line argument. This '--help' gets passed on to
334 // Kokkos, which promptly responds with a lengthy message that the user
335 // likely did not intend. As a consequence, filter out this specific
336 // flag.
337 std::vector<char *> argv_new;
338 for (auto *const arg : make_array_view(&argv[0], &argv[0] + argc))
339 if (std::strcmp(arg, "--help") != 0)
340 argv_new.push_back(arg);
341
342 std::stringstream threads_flag;
343#if DEAL_II_KOKKOS_VERSION_GTE(3, 7, 0)
344 threads_flag << "--kokkos-num-threads=" << MultithreadInfo::n_threads();
345#else
346 threads_flag << "--kokkos-threads=" << MultithreadInfo::n_threads();
347#endif
348 const std::string threads_flag_string = threads_flag.str();
349 argv_new.push_back(const_cast<char *>(threads_flag_string.c_str()));
350 argv_new.push_back(nullptr);
351
352 // The first argument in Kokkos::initialize is of type int&. Hence, we
353 // need to define a new variable to pass to it (instead of using argc+1
354 // inline).
355 int argc_new = argv_new.size() - 1;
356 Kokkos::initialize(argc_new, argv_new.data());
357 }
358
359 // As a final step call the at_mpi_init() signal handler.
361}
362
363
364
365void
367{
368 // insert if it is not in the set already:
369 requests.insert(&request);
370}
371
372
373
374void
376{
377 Assert(requests.find(&request) != requests.end(),
379 "You tried to call unregister_request() with an invalid request."));
380
381 requests.erase(&request);
382}
383
384
385
386std::set<MPI_Request *> InitFinalize::requests;
387
388#ifdef DEAL_II_WITH_PSBLAS
389
390psb_c_ctxt *InitFinalize::cctxt;
391
392psb_c_ctxt *
393InitFinalize::get_psblas_context()
394{
395 Assert(cctxt != nullptr, ExcMessage("PSBLAS context was not initialized."));
396 return cctxt;
397}
398#endif
399
400void
402{
403 if (!is_finalized)
404 {
405 // First, call the at_mpi_finalize() signal handler.
407
408 // make memory pool release all PETSc/Trilinos/MPI-based vectors that
409 // are no longer used at this point. this is relevant because the static
410 // object destructors run for these vectors at the end of the program
411 // would run after MPI_Finalize is called, leading to errors
412
413#ifdef DEAL_II_WITH_MPI
414 // Before exiting, wait for nonblocking communication to complete:
415 for (auto *request : requests)
416 {
417 const int ierr = MPI_Wait(request, MPI_STATUS_IGNORE);
418 AssertThrowMPI(ierr);
419 }
420
421 // Start with deal.II MPI vectors and delete vectors from the pools:
423 LinearAlgebra::distributed::Vector<double>>::release_unused_memory();
425 release_unused_memory();
427 LinearAlgebra::distributed::Vector<float>>::release_unused_memory();
429 release_unused_memory();
430
431 // Next with Trilinos:
432# ifdef DEAL_II_TRILINOS_WITH_EPETRA
434 TrilinosWrappers::MPI::Vector>::release_unused_memory();
436 TrilinosWrappers::MPI::BlockVector>::release_unused_memory();
437# endif
438#endif
439
440
441 // Now deal with PETSc (with or without MPI). Only delete the vectors if
442 // finalize hasn't been called yet, otherwise this will lead to errors.
443#ifdef DEAL_II_WITH_PETSC
444 if (!PetscFinalizeCalled)
445 {
447 PETScWrappers::MPI::Vector>::release_unused_memory();
449 PETScWrappers::MPI::BlockVector>::release_unused_memory();
450 }
451# ifdef DEAL_II_WITH_SLEPC
452 // and now end SLEPc with PETSc if we did so
453 if (static_cast<bool>(libraries & InitializeLibrary::SLEPc) &&
455 {
456 PetscErrorCode ierr = SlepcFinalize();
457 AssertThrow(ierr == 0,
459 }
460# else
461 // or just end PETSc if we did so
462 if (static_cast<bool>(libraries & InitializeLibrary::PETSc) &&
464 {
465 PetscErrorCode ierr = PetscFinalize();
466 AssertThrow(ierr == 0, ExcPETScError(ierr));
467 }
468# endif
469#endif
470
471#ifdef DEAL_II_WITH_P4EST
472 // now end p4est and libsc
473 // Note: p4est has no finalize function
474 if (static_cast<bool>(libraries & InitializeLibrary::P4EST))
475 sc_finalize();
476#endif
477
478#ifdef DEAL_II_WITH_PSBLAS
479 if (static_cast<bool>(libraries & InitializeLibrary::PSBLAS))
480 {
481 psb_c_exit_ctxt(*cctxt);
482 free(cctxt);
483 }
484#endif
485
486 // Finalize Kokkos
487 if (static_cast<bool>(libraries & InitializeLibrary::Kokkos))
488 Kokkos::finalize();
489
490 // only MPI_Finalize if we are running with MPI. We also need to do this
491 // when running PETSc, because we initialize MPI ourselves before
492 // calling PetscInitialize
493#ifdef DEAL_II_WITH_MPI
494 int MPI_has_been_started = 0;
495 const int ierr = MPI_Initialized(&MPI_has_been_started);
496 AssertThrowMPI(ierr);
497 if (static_cast<bool>(libraries & InitializeLibrary::MPI) &&
498 (MPI_has_been_started))
499 {
500 if (std::uncaught_exceptions() > 0)
501 {
502 // do not try to call MPI_Finalize to avoid a deadlock.
503 }
504 else
505 {
506 const int ierr = MPI_Finalize();
507 AssertNothrow(ierr == MPI_SUCCESS, ::ExcMPI(ierr));
508 }
509 }
510#endif
511 is_finalized = true;
512 }
513
514 // GMSH may attempt to finalize MPI so ensure this is run last:
515#ifdef DEAL_II_GMSH_WITH_API
516 if (static_cast<bool>(libraries & InitializeLibrary::GMSH))
517 gmsh::finalize();
518#endif
519}
520
521
522
527
528
ArrayView< std::remove_reference_t< typename std::iterator_traits< Iterator >::reference >, MemorySpaceType > make_array_view(const Iterator begin, const Iterator end)
InitializeLibrary libraries
static void unregister_request(MPI_Request &request)
static void register_request(MPI_Request &request)
static std::set< MPI_Request * > requests
bool finalize_petscslepc
InitFinalize(int &argc, char **&argv, const InitializeLibrary &libraries, const unsigned int max_num_threads=numbers::invalid_unsigned_int)
static Signals signals
static unsigned int n_cores()
static unsigned int n_threads()
static void set_thread_limit(const unsigned int max_threads=numbers::invalid_unsigned_int)
#define DEAL_II_NAMESPACE_OPEN
Definition config.h:38
#define DEAL_II_DISABLE_EXTRA_DIAGNOSTICS
Definition config.h:636
#define DEAL_II_NAMESPACE_CLOSE
Definition config.h:39
#define DEAL_II_ENABLE_EXTRA_DIAGNOSTICS
Definition config.h:680
static ::ExceptionBase & ExcSLEPcError(int arg1)
#define Assert(cond, exc)
#define AssertThrowMPI(error_code)
#define AssertNothrow(cond, exc)
static ::ExceptionBase & ExcInternalError()
static ::ExceptionBase & ExcMessage(std::string arg1)
#define AssertThrow(cond, exc)
InitializeLibrary
std::string get_hostname()
Definition utilities.cc:982
constexpr unsigned int invalid_unsigned_int
Definition types.h:228
::VectorizedArray< Number, width > max(const ::VectorizedArray< Number, width > &, const ::VectorizedArray< Number, width > &)
****code *  *  MPI_Finalize()
boost::signals2::signal< void()> at_mpi_init
boost::signals2::signal< void()> at_mpi_finalize