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