deal.II version GIT relicensing-6809-ge913b9bb34 2026-09-25 17: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
mpi.h
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) 2012 - 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
13#ifndef dealii_mpi_h
14#define dealii_mpi_h
15
16#include <deal.II/base/config.h>
17
25#include <deal.II/base/types.h>
27
28#include <complex>
29#include <limits>
30#include <map>
31#include <numeric>
32#include <set>
33#include <vector>
34
35#ifdef DEAL_II_WITH_MPI
37# include <mpi.h>
39#endif
40
41
55#ifdef DEAL_II_WITH_MPI
56# define DEAL_II_MPI_CONST_CAST(expr) (expr)
57#endif
58
59
60
62
63
64// Forward type declarations to allow MPI sums over tensorial types
65#ifndef DOXYGEN
66template <int rank, int dim, typename Number>
67class Tensor;
68template <int rank, int dim, typename Number>
69class SymmetricTensor;
70template <typename Number>
71class SparseMatrix;
72class IndexSet;
73#endif
74
75namespace Utilities
76{
91 const unsigned int my_partition_id,
92 const unsigned int n_partitions,
93 const types::global_dof_index total_size);
94
102 namespace MPI
103 {
112 template <typename T>
113 constexpr bool is_mpi_type = is_same_as_any_of<T,
114 char,
115 signed short,
116 signed int,
117 signed long,
118 signed long long,
119 signed char,
120 unsigned char,
121 unsigned short,
122 unsigned int,
123 unsigned long int,
124 unsigned long long,
125 float,
126 double,
127 long double,
128 bool,
129 std::complex<float>,
130 std::complex<double>,
131 std::complex<long double>,
132 wchar_t>::value;
133
142 unsigned int
143 n_mpi_processes(const MPI_Comm mpi_communicator);
144
153 unsigned int
154 this_mpi_process(const MPI_Comm mpi_communicator);
155
160 std::vector<unsigned int>
162 const MPI_Comm comm_small);
163
185 std::vector<unsigned int>
187 const MPI_Comm mpi_comm,
188 const std::vector<unsigned int> &destinations);
189
209 unsigned int
211 const MPI_Comm mpi_comm,
212 const std::vector<unsigned int> &destinations);
213
231 duplicate_communicator(const MPI_Comm mpi_communicator);
232
242 void
243 free_communicator(MPI_Comm mpi_communicator);
244
258 {
259 public:
263 explicit DuplicatedCommunicator(const MPI_Comm communicator)
264 : comm(duplicate_communicator(communicator))
265 {}
266
271
279
284 operator*() const
285 {
286 return comm;
287 }
288
289
295
296 private:
301 };
302
303
304
335 {
336 public:
343 {
344 public:
349 : mutex(mutex)
350 , comm(comm)
351 {
352 mutex.lock(comm);
353 }
354
359 {
361 }
362
363 private:
372 };
373
378
383
390 void
391 lock(const MPI_Comm comm);
392
399 void
400 unlock(const MPI_Comm comm);
401
402 private:
406 bool locked;
407
411 MPI_Request request;
412 };
413
414
415
462 template <typename T>
463 class Future
464 {
465 public:
470 template <typename W, typename G>
471 Future(W &&wait_operation, G &&get_and_cleanup_operation);
472
478 Future(const Future &) = delete;
479
483 Future(Future &&) noexcept = default;
484
517
523 Future &
524 operator=(const Future &) = delete;
525
529 Future &
530 operator=(Future &&) noexcept = default;
531
539 void
541
553 T
555
556 private:
560 std::function<void()> wait_function;
562
567
572 };
573
582 std::vector<IndexSet>
584 const MPI_Comm comm,
585 const types::global_dof_index locally_owned_size);
586
596 const MPI_Comm comm,
597 const types::global_dof_index total_size);
598
599#ifdef DEAL_II_WITH_MPI
615 template <class Iterator, typename Number = long double>
616 std::pair<Number, typename numbers::NumberTraits<Number>::real_type>
618 const Iterator end,
619 const MPI_Comm comm);
620#endif
621
622
670 std::unique_ptr<MPI_Datatype, void (*)(MPI_Datatype *)>
671 create_mpi_data_type_n_bytes(const std::size_t n_bytes);
672
692 template <typename T>
693 T
694 sum(const T &t, const MPI_Comm mpi_communicator);
695
705 template <typename T, typename U>
706 void
707 sum(const T &values, const MPI_Comm mpi_communicator, U &sums);
708
718 template <typename T>
719 void
720 sum(const ArrayView<const T> &values,
721 const MPI_Comm mpi_communicator,
722 const ArrayView<T> &sums);
723
729 template <int rank, int dim, typename Number>
732 const MPI_Comm mpi_communicator);
733
739 template <int rank, int dim, typename Number>
742 const MPI_Comm mpi_communicator);
743
752 template <typename Number>
753 void
755 const MPI_Comm mpi_communicator,
756 SparseMatrix<Number> &global);
757
777 template <typename T>
778 T
779 max(const T &t, const MPI_Comm mpi_communicator);
780
790 template <typename T, typename U>
791 void
792 max(const T &values, const MPI_Comm mpi_communicator, U &maxima);
793
803 template <typename T>
804 void
805 max(const ArrayView<const T> &values,
806 const MPI_Comm mpi_communicator,
807 const ArrayView<T> &maxima);
808
828 template <typename T>
829 T
830 min(const T &t, const MPI_Comm mpi_communicator);
831
841 template <typename T, typename U>
842 void
843 min(const T &values, const MPI_Comm mpi_communicator, U &minima);
844
854 template <typename T>
855 void
856 min(const ArrayView<const T> &values,
857 const MPI_Comm mpi_communicator,
858 const ArrayView<T> &minima);
859
883 template <typename T>
884 T
885 logical_or(const T &t, const MPI_Comm mpi_communicator);
886
901 template <typename T, typename U>
902 void
903 logical_or(const T &values, const MPI_Comm mpi_communicator, U &results);
904
914 template <typename T>
915 void
917 const MPI_Comm mpi_communicator,
918 const ArrayView<T> &results);
919
939 bool
940 logical_and(const bool t, const MPI_Comm mpi_communicator);
941
957 {
962 double sum;
963
968 double min;
969
974 double max;
975
984 unsigned int min_index;
985
994 unsigned int max_index;
995
1000 double avg;
1001 };
1002
1017 MinMaxAvg
1018 min_max_avg(const double my_value, const MPI_Comm mpi_communicator);
1019
1031 std::vector<MinMaxAvg>
1032 min_max_avg(const std::vector<double> &my_value,
1033 const MPI_Comm mpi_communicator);
1034
1035
1048 void
1049 min_max_avg(const ArrayView<const double> &my_values,
1050 const ArrayView<MinMaxAvg> &result,
1051 const MPI_Comm mpi_communicator);
1052
1053
1098 {
1099 public:
1146 int &argc,
1147 char **&argv,
1148 const unsigned int max_num_threads = numbers::invalid_unsigned_int);
1149
1155 };
1156
1168 bool
1170
1188 template <typename T>
1189 std::map<unsigned int, T>
1191 const std::map<unsigned int, T> &objects_to_send);
1192
1206 template <typename T>
1207 [[nodiscard]] std::vector<T>
1208 all_gather(const MPI_Comm comm, const T &object_to_send);
1209
1228 template <typename T>
1229 [[nodiscard]] std::vector<T>
1231 const T &object_to_send,
1232 const unsigned int root_process = 0);
1233
1248 template <typename T>
1249 [[nodiscard]] T
1251 const std::vector<T> &objects_to_send,
1252 const unsigned int root_process = 0);
1253
1292 template <typename T>
1293 [[nodiscard]] T
1295 const T &object_to_send,
1296 const unsigned int root_process = 0);
1297
1314 template <typename T>
1315 void
1316 broadcast(T *buffer,
1317 const std::size_t count,
1318 const unsigned int root,
1319 const MPI_Comm comm);
1320
1333 template <typename T>
1334 [[nodiscard]] T
1335 reduce(const T &local_value,
1336 const MPI_Comm comm,
1337 const std::function<T(const T &, const T &)> &combiner,
1338 const unsigned int root_process = 0);
1339
1340
1352 template <typename T, typename = std::enable_if_t<is_mpi_type<T> == true>>
1353 [[nodiscard]] std::pair<T, T>
1354 partial_and_total_sum(const T &value, const MPI_Comm comm);
1355
1356
1366 template <typename T>
1367 [[nodiscard]] T
1368 all_reduce(const T &local_value,
1369 const MPI_Comm comm,
1370 const std::function<T(const T &, const T &)> &combiner);
1371
1372
1393 template <typename T>
1395 isend(const T &object,
1396 MPI_Comm communicator,
1397 const unsigned int target_rank,
1398 const unsigned int mpi_tag = 0);
1399
1400
1417 template <typename T>
1418 Future<T>
1419 irecv(MPI_Comm communicator,
1420 const unsigned int source_rank,
1421 const unsigned int mpi_tag = 0);
1422
1423
1467 std::vector<unsigned int>
1470 const MPI_Comm comm);
1471
1482 std::pair<std::vector<unsigned int>, std::map<unsigned int, IndexSet>>
1485 const MPI_Comm &comm);
1486
1494 template <typename T>
1495 std::vector<T>
1496 compute_set_union(const std::vector<T> &vec, const MPI_Comm comm);
1497
1501 template <typename T>
1502 std::set<T>
1503 compute_set_union(const std::set<T> &set, const MPI_Comm comm);
1504
1505
1506
1507 /* --------------------------- inline functions ------------------------- */
1508
1509 namespace internal
1510 {
1516 namespace MPIDataTypes
1517 {
1518#ifdef DEAL_II_WITH_MPI
1519 inline MPI_Datatype
1520 mpi_type_id(const bool *)
1521 {
1522 return MPI_CXX_BOOL;
1523 }
1524
1525
1526
1527 inline MPI_Datatype
1528 mpi_type_id(const char *)
1529 {
1530 return MPI_CHAR;
1531 }
1532
1533
1534
1535 inline MPI_Datatype
1536 mpi_type_id(const signed char *)
1537 {
1538 return MPI_SIGNED_CHAR;
1539 }
1540
1541
1542
1543 inline MPI_Datatype
1544 mpi_type_id(const wchar_t *)
1545 {
1546 return MPI_WCHAR;
1547 }
1548
1549
1550
1551 inline MPI_Datatype
1552 mpi_type_id(const short *)
1553 {
1554 return MPI_SHORT;
1555 }
1556
1557
1558
1559 inline MPI_Datatype
1560 mpi_type_id(const int *)
1561 {
1562 return MPI_INT;
1563 }
1564
1565
1566
1567 inline MPI_Datatype
1568 mpi_type_id(const long int *)
1569 {
1570 return MPI_LONG;
1571 }
1572
1573
1574
1575 inline MPI_Datatype
1576 mpi_type_id(const long long int *)
1577 {
1578 return MPI_LONG_LONG;
1579 }
1580
1581
1582
1583 inline MPI_Datatype
1584 mpi_type_id(const unsigned char *)
1585 {
1586 return MPI_UNSIGNED_CHAR;
1587 }
1588
1589
1590
1591 inline MPI_Datatype
1592 mpi_type_id(const unsigned short *)
1593 {
1594 return MPI_UNSIGNED_SHORT;
1595 }
1596
1597
1598
1599 inline MPI_Datatype
1600 mpi_type_id(const unsigned int *)
1601 {
1602 return MPI_UNSIGNED;
1603 }
1604
1605
1606
1607 inline MPI_Datatype
1608 mpi_type_id(const unsigned long int *)
1609 {
1610 return MPI_UNSIGNED_LONG;
1611 }
1612
1613
1614
1615 inline MPI_Datatype
1616 mpi_type_id(const unsigned long long int *)
1617 {
1618 return MPI_UNSIGNED_LONG_LONG;
1619 }
1620
1621
1622
1623 inline MPI_Datatype
1624 mpi_type_id(const float *)
1625 {
1626 return MPI_FLOAT;
1627 }
1628
1629
1630
1631 inline MPI_Datatype
1632 mpi_type_id(const double *)
1633 {
1634 return MPI_DOUBLE;
1635 }
1636
1637
1638
1639 inline MPI_Datatype
1640 mpi_type_id(const long double *)
1641 {
1642 return MPI_LONG_DOUBLE;
1643 }
1644
1645
1646
1647 inline MPI_Datatype
1648 mpi_type_id(const std::complex<float> *)
1649 {
1650 return MPI_COMPLEX;
1651 }
1652
1653
1654
1655 inline MPI_Datatype
1656 mpi_type_id(const std::complex<double> *)
1657 {
1658 return MPI_DOUBLE_COMPLEX;
1659 }
1660#endif
1661 } // namespace MPIDataTypes
1662 } // namespace internal
1663
1664
1665
1666#ifdef DEAL_II_WITH_MPI
1684 template <typename T>
1685 inline const MPI_Datatype mpi_type_id_for_type =
1687 static_cast<std::remove_cv_t<std::remove_reference_t<T>> *>(nullptr));
1688#endif
1689
1690#ifndef DOXYGEN
1691 namespace internal
1692 {
1693 // declaration for an internal function that lives in mpi.templates.h
1694 template <typename T>
1695 void
1696 all_reduce(const MPI_Op &mpi_op,
1697 const ArrayView<const T> &values,
1698 const MPI_Comm mpi_communicator,
1699 const ArrayView<T> &output);
1700 } // namespace internal
1701
1702
1703 template <typename T>
1704 template <typename W, typename G>
1705 Future<T>::Future(W &&wait_operation, G &&get_and_cleanup_operation)
1706 : wait_function(wait_operation)
1707 , get_and_cleanup_function(get_and_cleanup_operation)
1708 , is_done(false)
1709 , get_was_called(false)
1710 {}
1711
1712
1713
1714 template <typename T>
1715 Future<T>::~Future()
1716 {
1717 // If there is a clean-up function, and if it has not been
1718 // called yet, then do so. Note that we may not have a
1719 // clean-up function (not even an empty one) if the current
1720 // object has been moved from, into another object, and as
1721 // a consequence the std::function objects are now empty
1722 // even though they were initialized in the constructor.
1723 // (A std::function object whose object is a an empty lambda
1724 // function, [](){}, is not an empty std::function object.)
1725 if ((get_was_called == false) && get_and_cleanup_function)
1726 get();
1727 }
1728
1729
1730
1731 template <typename T>
1732 void
1733 Future<T>::wait()
1734 {
1735 if (is_done == false)
1736 {
1737 wait_function();
1738
1739 is_done = true;
1740 }
1741 }
1742
1743
1744 template <typename T>
1745 T
1746 Future<T>::get()
1747 {
1748 Assert(get_was_called == false,
1749 ExcMessage(
1750 "You can't call get() more than once on a Future object."));
1751 get_was_called = true;
1752
1753 wait();
1754 return get_and_cleanup_function();
1755 }
1756
1757
1758
1759 template <typename T, unsigned int N>
1760 void
1761 sum(const T (&values)[N], const MPI_Comm mpi_communicator, T (&sums)[N])
1762 {
1763 internal::all_reduce(MPI_SUM,
1764 ArrayView<const T>(values, N),
1765 mpi_communicator,
1766 ArrayView<T>(sums, N));
1767 }
1768
1769
1770
1771 template <typename T, unsigned int N>
1772 void
1773 max(const T (&values)[N], const MPI_Comm mpi_communicator, T (&maxima)[N])
1774 {
1775 internal::all_reduce(MPI_MAX,
1776 ArrayView<const T>(values, N),
1777 mpi_communicator,
1778 ArrayView<T>(maxima, N));
1779 }
1780
1781
1782
1783 template <typename T, unsigned int N>
1784 void
1785 min(const T (&values)[N], const MPI_Comm mpi_communicator, T (&minima)[N])
1786 {
1787 internal::all_reduce(MPI_MIN,
1788 ArrayView<const T>(values, N),
1789 mpi_communicator,
1790 ArrayView<T>(minima, N));
1791 }
1792
1793
1794
1795 template <typename T, unsigned int N>
1796 void
1797 logical_or(const T (&values)[N],
1798 const MPI_Comm mpi_communicator,
1799 T (&results)[N])
1800 {
1801 static_assert(std::is_integral_v<T>,
1802 "The MPI_LOR operation only allows integral data types.");
1803
1804 internal::all_reduce(MPI_LOR,
1805 ArrayView<const T>(values, N),
1806 mpi_communicator,
1807 ArrayView<T>(results, N));
1808 }
1809
1810
1811
1812 inline bool
1813 logical_and(const bool t, const MPI_Comm mpi_communicator)
1814 {
1815 bool return_value = false;
1816 internal::all_reduce(MPI_LAND,
1817 ArrayView<const bool>(&t, 1),
1818 mpi_communicator,
1819 ArrayView<bool>(&return_value, 1));
1820 return return_value;
1821 }
1822
1823
1824
1825 template <typename T>
1826 std::map<unsigned int, T>
1828 const std::map<unsigned int, T> &objects_to_send)
1829 {
1830# ifndef DEAL_II_WITH_MPI
1831 (void)comm;
1832 Assert(objects_to_send.size() < 2,
1833 ExcMessage("Cannot send to more than one processor."));
1834 Assert(objects_to_send.find(0) != objects_to_send.end() ||
1835 objects_to_send.empty(),
1836 ExcMessage("Can only send to myself or to nobody."));
1837 return objects_to_send;
1838# else
1839 const auto my_proc = this_mpi_process(comm);
1840
1841 std::map<unsigned int, T> received_objects;
1842
1843 std::vector<unsigned int> send_to;
1844 send_to.reserve(objects_to_send.size());
1845 for (const auto &m : objects_to_send)
1846 if (m.first == my_proc)
1847 received_objects[my_proc] = m.second;
1848 else
1849 send_to.emplace_back(m.first);
1850
1851 const unsigned int n_expected_incoming_messages =
1853
1854 // Protect the following communication:
1855 static CollectiveMutex mutex;
1856 CollectiveMutex::ScopedLock lock(mutex, comm);
1857
1858 // If we have something to send, or we expect something from other
1859 // processors, we need to visit one of the two scopes below. Otherwise,
1860 // no other action is required by this mpi process, and we can safely
1861 // return.
1862 if (send_to.empty() && n_expected_incoming_messages == 0)
1863 return received_objects;
1864
1865 const int mpi_tag =
1866 internal::Tags::compute_point_to_point_communication_pattern;
1867
1868 // Sending buffers
1869 std::vector<std::vector<char>> buffers_to_send(send_to.size());
1870 std::vector<MPI_Request> buffer_send_requests(send_to.size());
1871 {
1872 unsigned int i = 0;
1873 for (const auto &rank_obj : objects_to_send)
1874 if (rank_obj.first != my_proc)
1875 {
1876 const auto &rank = rank_obj.first;
1877 buffers_to_send[i] =
1878 Utilities::pack(rank_obj.second, /*compress=*/false);
1879 const int ierr = MPI_Isend(buffers_to_send[i].data(),
1880 buffers_to_send[i].size(),
1881 MPI_CHAR,
1882 rank,
1883 mpi_tag,
1884 comm,
1885 &buffer_send_requests[i]);
1886 AssertThrowMPI(ierr);
1887 ++i;
1888 }
1889 }
1890
1891 // Fill the output map
1892 {
1893 std::vector<char> buffer;
1894 // We do this on a first come/first served basis
1895 for (unsigned int i = 0; i < n_expected_incoming_messages; ++i)
1896 {
1897 // Probe what's going on. Take data from the first available sender
1898 MPI_Status status;
1899 int ierr = MPI_Probe(MPI_ANY_SOURCE, mpi_tag, comm, &status);
1900 AssertThrowMPI(ierr);
1901
1902 // Length of the message
1903 int len;
1904 ierr = MPI_Get_count(&status, MPI_CHAR, &len);
1905 AssertThrowMPI(ierr);
1906 buffer.resize(len);
1907
1908 // Source rank
1909 const unsigned int rank = status.MPI_SOURCE;
1910
1911 // Actually receive the message
1912 ierr = MPI_Recv(buffer.data(),
1913 len,
1914 MPI_CHAR,
1915 status.MPI_SOURCE,
1916 status.MPI_TAG,
1917 comm,
1918 MPI_STATUS_IGNORE);
1919 AssertThrowMPI(ierr);
1920 Assert(received_objects.find(rank) == received_objects.end(),
1922 "I should not receive again from this rank"));
1923 received_objects[rank] =
1924 Utilities::unpack<T>(buffer, /*compress=*/false);
1925 }
1926 }
1927
1928 // Wait to have sent all objects.
1929 const int ierr = MPI_Waitall(send_to.size(),
1930 buffer_send_requests.data(),
1931 MPI_STATUSES_IGNORE);
1932 AssertThrowMPI(ierr);
1933
1934 return received_objects;
1935# endif // deal.II with MPI
1936 }
1937
1938
1939
1940 template <typename T, typename>
1941 std::pair<T, T>
1942 partial_and_total_sum(const T &value, const MPI_Comm comm)
1943 {
1944# ifndef DEAL_II_WITH_MPI
1945 (void)comm;
1946 return {0, value};
1947# else
1949 return {0, value};
1950 else
1951 {
1952 T prefix = {};
1953
1954 // First obtain every process's prefix sum:
1955 int ierr =
1956 MPI_Exscan(&value,
1957 &prefix,
1958 1,
1959 Utilities::MPI::mpi_type_id_for_type<decltype(value)>,
1960 MPI_SUM,
1961 comm);
1962 AssertThrowMPI(ierr);
1963
1964 // Then we also need the total sum. We could obtain it by
1965 // calling Utilities::MPI::sum(), but it is cheaper if we
1966 // broadcast it from the last process, which can compute it
1967 // from its own prefix sum plus its own value.
1969 comm, prefix + value, Utilities::MPI::n_mpi_processes(comm) - 1);
1970
1971 return {prefix, sum};
1972 }
1973# endif
1974 }
1975
1976
1977
1978 template <typename T>
1979 std::vector<T>
1980 all_gather(const MPI_Comm comm, const T &object)
1981 {
1982 if (job_supports_mpi() == false)
1983 return {object};
1984
1985# ifndef DEAL_II_WITH_MPI
1986 (void)comm;
1987 std::vector<T> v(1, object);
1988 return v;
1989# else
1991
1992 std::vector<char> buffer = Utilities::pack(object, /*compress=*/false);
1993
1994 int n_local_data = buffer.size();
1995
1996 // Vector to store the size of loc_data_array for every process
1997 std::vector<int> size_all_data(n_procs, 0);
1998
1999 // Exchanging the size of each buffer
2000 int ierr = MPI_Allgather(
2001 &n_local_data, 1, MPI_INT, size_all_data.data(), 1, MPI_INT, comm);
2002 AssertThrowMPI(ierr);
2003
2004 // Now computing the displacement, relative to recvbuf,
2005 // at which to store the incoming buffer
2006 std::vector<int> rdispls(n_procs);
2007 rdispls[0] = 0;
2008 for (unsigned int i = 1; i < n_procs; ++i)
2009 rdispls[i] = rdispls[i - 1] + size_all_data[i - 1];
2010
2011 // Step 3: exchange the buffer:
2012 std::vector<char> received_unrolled_buffer(rdispls.back() +
2013 size_all_data.back());
2014
2015 ierr = MPI_Allgatherv(buffer.data(),
2016 n_local_data,
2017 MPI_CHAR,
2018 received_unrolled_buffer.data(),
2019 size_all_data.data(),
2020 rdispls.data(),
2021 MPI_CHAR,
2022 comm);
2023 AssertThrowMPI(ierr);
2024
2025 std::vector<T> received_objects(n_procs);
2026 for (unsigned int i = 0; i < n_procs; ++i)
2027 {
2028 std::vector<char> local_buffer(received_unrolled_buffer.begin() +
2029 rdispls[i],
2030 received_unrolled_buffer.begin() +
2031 rdispls[i] + size_all_data[i]);
2032 received_objects[i] =
2033 Utilities::unpack<T>(local_buffer, /*compress=*/false);
2034 }
2035
2036 return received_objects;
2037# endif
2038 }
2039
2040
2041
2042 template <typename T>
2043 std::vector<T>
2044 gather(const MPI_Comm comm,
2045 const T &object_to_send,
2046 const unsigned int root_process)
2047 {
2048# ifndef DEAL_II_WITH_MPI
2049 (void)comm;
2050 (void)root_process;
2051 std::vector<T> v(1, object_to_send);
2052 return v;
2053# else
2056
2057 AssertIndexRange(root_process, n_procs);
2058
2059 std::vector<char> buffer =
2060 Utilities::pack(object_to_send, /*compress=*/false);
2061 int n_local_data = buffer.size();
2062
2063 // Vector to store the size of loc_data_array for every process
2064 // only the root process needs to allocate memory for that purpose
2065 std::vector<int> size_all_data;
2066 if (my_rank == root_process)
2067 size_all_data.resize(n_procs, 0);
2068
2069 // Exchanging the size of each buffer
2070 int ierr = MPI_Gather(&n_local_data,
2071 1,
2072 MPI_INT,
2073 size_all_data.data(),
2074 1,
2075 MPI_INT,
2076 root_process,
2077 comm);
2078 AssertThrowMPI(ierr);
2079
2080 // Now computing the displacement, relative to recvbuf,
2081 // at which to store the incoming buffer; only for root
2082 std::vector<int> rdispls;
2083 if (my_rank == root_process)
2084 {
2085 rdispls.resize(n_procs, 0);
2086 for (unsigned int i = 1; i < n_procs; ++i)
2087 rdispls[i] = rdispls[i - 1] + size_all_data[i - 1];
2088 }
2089 // exchange the buffer:
2090 std::vector<char> received_unrolled_buffer;
2091 if (my_rank == root_process)
2092 received_unrolled_buffer.resize(rdispls.back() + size_all_data.back());
2093
2094 ierr = MPI_Gatherv(buffer.data(),
2095 n_local_data,
2096 MPI_CHAR,
2097 received_unrolled_buffer.data(),
2098 size_all_data.data(),
2099 rdispls.data(),
2100 MPI_CHAR,
2101 root_process,
2102 comm);
2103 AssertThrowMPI(ierr);
2104
2105 std::vector<T> received_objects;
2106
2107 if (my_rank == root_process)
2108 {
2109 received_objects.resize(n_procs);
2110
2111 for (unsigned int i = 0; i < n_procs; ++i)
2112 received_objects[i] = Utilities::unpack<T>(
2113 received_unrolled_buffer.cbegin() + rdispls[i],
2114 received_unrolled_buffer.cbegin() + rdispls[i] + size_all_data[i],
2115 /*compress=*/false);
2116 }
2117 return received_objects;
2118# endif
2119 }
2120
2121
2122
2123 template <typename T>
2124 T
2125 scatter(const MPI_Comm comm,
2126 const std::vector<T> &objects_to_send,
2127 const unsigned int root_process)
2128 {
2129# ifndef DEAL_II_WITH_MPI
2130 (void)comm;
2131 (void)root_process;
2132
2133 AssertDimension(objects_to_send.size(), 1);
2134
2135 return objects_to_send[0];
2136# else
2139
2140 AssertIndexRange(root_process, n_procs);
2142 (my_rank != root_process && objects_to_send.empty()) ||
2143 objects_to_send.size() == n_procs,
2144 ExcMessage(
2145 "The number of objects to be scattered must correspond to the number processes."));
2146
2147 std::vector<char> send_buffer;
2148 std::vector<int> send_counts;
2149 std::vector<int> send_displacements;
2150
2151 if (my_rank == root_process)
2152 {
2153 send_counts.resize(n_procs, 0);
2154 send_displacements.resize(n_procs + 1, 0);
2155
2156 for (unsigned int i = 0; i < n_procs; ++i)
2157 {
2158 const auto packed_data =
2159 Utilities::pack(objects_to_send[i], /*compress=*/false);
2160 send_buffer.insert(send_buffer.end(),
2161 packed_data.begin(),
2162 packed_data.end());
2163 send_counts[i] = packed_data.size();
2164 }
2165
2166 for (unsigned int i = 0; i < n_procs; ++i)
2167 send_displacements[i + 1] = send_displacements[i] + send_counts[i];
2168 }
2169
2170 int n_local_data;
2171 int ierr = MPI_Scatter(send_counts.data(),
2172 1,
2173 MPI_INT,
2174 &n_local_data,
2175 1,
2176 MPI_INT,
2177 root_process,
2178 comm);
2179 AssertThrowMPI(ierr);
2180
2181 std::vector<char> recv_buffer(n_local_data);
2182
2183 ierr = MPI_Scatterv(send_buffer.data(),
2184 send_counts.data(),
2185 send_displacements.data(),
2186 MPI_CHAR,
2187 recv_buffer.data(),
2188 n_local_data,
2189 MPI_CHAR,
2190 root_process,
2191 comm);
2192 AssertThrowMPI(ierr);
2193
2194 return Utilities::unpack<T>(recv_buffer, /*compress=*/false);
2195# endif
2196 }
2197
2198
2199 template <typename T>
2200 void
2201 broadcast(T *buffer,
2202 const std::size_t count,
2203 const unsigned int root,
2204 const MPI_Comm comm)
2205 {
2206 static_assert(is_mpi_type<T>,
2207 "This function is currently only implemented for "
2208 "MPI-supported data types.");
2209
2210# ifndef DEAL_II_WITH_MPI
2211 (void)buffer;
2212 (void)count;
2213 (void)root;
2214 (void)comm;
2215# else
2216 Assert(root < n_mpi_processes(comm),
2217 ExcMessage("Invalid root rank specified."));
2218
2219 // MPI_Bcast's count is a signed int, so send at most 2^31 in each
2220 // iteration:
2221 const size_t max_send_count = std::numeric_limits<signed int>::max();
2222
2223 size_t total_sent_count = 0;
2224 while (total_sent_count < count)
2225 {
2226 const size_t current_count =
2227 std::min(count - total_sent_count, max_send_count);
2228
2229 const int ierr = MPI_Bcast(buffer + total_sent_count,
2230 current_count,
2231 mpi_type_id_for_type<decltype(*buffer)>,
2232 root,
2233 comm);
2234 AssertThrowMPI(ierr);
2235 total_sent_count += current_count;
2236 }
2237# endif
2238 }
2239
2240
2241
2242 template <typename T>
2243 T
2244 broadcast(const MPI_Comm comm,
2245 const T &object_to_send,
2246 const unsigned int root_process)
2247 {
2248# ifndef DEAL_II_WITH_MPI
2249 (void)comm;
2250 (void)root_process;
2251 return object_to_send;
2252# else
2254 AssertIndexRange(root_process, n_procs);
2255 (void)n_procs;
2256
2257 if constexpr (is_mpi_type<T>)
2258 {
2259 T object = object_to_send;
2260 const int ierr =
2261 MPI_Bcast(&object, 1, mpi_type_id_for_type<T>, root_process, comm);
2262 AssertThrowMPI(ierr);
2263
2264 return object;
2265 }
2266 else
2267 {
2268 std::vector<char> buffer;
2269 std::size_t buffer_size = numbers::invalid_size_type;
2270
2271 // On the root process, pack the data and determine what the
2272 // buffer size needs to be.
2273 if (this_mpi_process(comm) == root_process)
2274 {
2275 buffer = Utilities::pack(object_to_send, /*compress=*/false);
2276 buffer_size = buffer.size();
2277 }
2278
2279 // Exchange the size of buffer
2280 int ierr = MPI_Bcast(&buffer_size,
2281 1,
2282 mpi_type_id_for_type<decltype(buffer_size)>,
2283 root_process,
2284 comm);
2285 AssertThrowMPI(ierr);
2286
2287 // If not on the root process, correctly size the buffer to
2288 // receive the data, then do exactly that.
2289 if (this_mpi_process(comm) != root_process)
2290 buffer.resize(buffer_size);
2291
2292 broadcast(buffer.data(), buffer_size, root_process, comm);
2293
2294 if (Utilities::MPI::this_mpi_process(comm) == root_process)
2295 return object_to_send;
2296 else
2297 return Utilities::unpack<T>(buffer, /*compress=*/false);
2298 }
2299# endif
2300 }
2301
2302
2303
2304 template <typename T>
2305 Future<void>
2306 isend(const T &object,
2307 MPI_Comm communicator,
2308 const unsigned int target_rank,
2309 const unsigned int mpi_tag)
2310 {
2311# ifndef DEAL_II_WITH_MPI
2312 Assert(false, ExcNeedsMPI());
2313 (void)object;
2314 (void)communicator;
2315 (void)target_rank;
2316 (void)mpi_tag;
2317 return Future<void>([]() {}, []() {});
2318# else
2319 // Create a pointer to a send buffer into which we pack the object
2320 // to be sent. The buffer will be released by the Future object once
2321 // the send has been verified to have succeeded.
2322 //
2323 // Conceptually, we would like this send buffer to be a
2324 // std::unique_ptr object whose ownership is later handed over
2325 // to the cleanup function. That has the disadvantage that the
2326 // cleanup object is a non-copyable lambda capture, leading to
2327 // awkward semantics. Instead, we use a std::shared_ptr; we move
2328 // this shared pointer into the cleanup function, which means
2329 // that there is exactly one shared pointer who owns the buffer
2330 // at any given time, though the latter is not an important
2331 // optimization.
2332 std::shared_ptr<std::vector<char>> send_buffer =
2333 std::make_unique<std::vector<char>>(
2334 Utilities::pack(object, /*compress=*/false));
2335
2336 // Now start the send, and store the result in a request object that
2337 // we can then wait for later:
2338 MPI_Request request;
2339 const int ierr =
2340 MPI_Isend(send_buffer->data(),
2341 send_buffer->size(),
2342 mpi_type_id_for_type<decltype(*send_buffer->data())>,
2343 target_rank,
2344 mpi_tag,
2345 communicator,
2346 &request);
2347 AssertThrowMPI(ierr);
2348
2349 // Then return a std::future-like object that has a wait()
2350 // function one can use to wait for the communication to finish,
2351 // and that has a cleanup function to be called at some point
2352 // after that makes sure the send buffer gets deallocated. This
2353 // cleanup function takes over ownership of the send buffer.
2354 //
2355 // Note that the body of the lambda function of the clean-up
2356 // function could be left empty. If that were so, once the
2357 // lambda function object goes out of scope, the 'send_buffer'
2358 // member of the closure object goes out of scope as well and so
2359 // the send_buffer is destroyed. But we may want to release the
2360 // buffer itself as early as possible, and so we clear the
2361 // buffer when the Future::get() function is called.
2362 auto wait = [request]() mutable {
2363 const int ierr = MPI_Wait(&request, MPI_STATUS_IGNORE);
2364 AssertThrowMPI(ierr);
2365 };
2366 auto cleanup = [send_buffer = std::move(send_buffer)]() {
2367 send_buffer->clear();
2368 };
2369 return Future<void>(wait, cleanup);
2370# endif
2371 }
2372
2373
2374
2375 template <typename T>
2376 Future<T>
2377 irecv(MPI_Comm communicator,
2378 const unsigned int source_rank,
2379 const unsigned int mpi_tag)
2380 {
2381# ifndef DEAL_II_WITH_MPI
2382 Assert(false, ExcNeedsMPI());
2383 (void)communicator;
2384 (void)source_rank;
2385 (void)mpi_tag;
2386 return Future<void>([]() {}, []() { return T{}; });
2387# else
2388 // Use a 'probe' operation for the 'wait' operation of the
2389 // Future this function returns. It will trigger whenever we get
2390 // the incoming message. Later, once we have received the message, we
2391 // can query its size and allocate a receiver buffer.
2392 //
2393 // Since we may be waiting for multiple messages from the same
2394 // incoming process (with possibly the same tag -- we can't
2395 // know), we must make sure that the 'probe' operation we have
2396 // here (and which we use to determine the buffer size) matches
2397 // the 'recv' operation with which we actually get the data
2398 // later on. This is exactly what the 'MPI_Mprobe' function and
2399 // its 'I'mmediate variant is there for, coupled with the
2400 // 'MPI_Mrecv' call that would put into the clean-up function
2401 // below.
2402 std::shared_ptr<MPI_Message> message = std::make_shared<MPI_Message>();
2403 std::shared_ptr<MPI_Status> status = std::make_shared<MPI_Status>();
2404
2405 auto wait = [source_rank, mpi_tag, communicator, message, status]() {
2406 const int ierr = MPI_Mprobe(
2407 source_rank, mpi_tag, communicator, message.get(), status.get());
2408 AssertThrowMPI(ierr);
2409 };
2410
2411
2412 // Now also define the function that actually gets the data:
2413 auto get = [status, message]() {
2414 int number_amount;
2415 int ierr;
2416 ierr = MPI_Get_count(status.get(), MPI_CHAR, &number_amount);
2417 AssertThrowMPI(ierr);
2418
2419 std::vector<char> receive_buffer(number_amount);
2420
2421 // Then actually get the data, using the matching MPI_Mrecv to the above
2422 // MPI_Mprobe:
2423 ierr = MPI_Mrecv(receive_buffer.data(),
2424 number_amount,
2425 mpi_type_id_for_type<decltype(*receive_buffer.data())>,
2426 message.get(),
2427 status.get());
2428 AssertThrowMPI(ierr);
2429
2430 // Return the unpacked object:
2431 return Utilities::unpack<T>(receive_buffer, /*compress=*/false);
2432 };
2433
2434 return Future<T>(wait, get);
2435# endif
2436 }
2437
2438
2439
2440# ifdef DEAL_II_WITH_MPI
2441 template <class Iterator, typename Number>
2442 std::pair<Number, typename numbers::NumberTraits<Number>::real_type>
2443 mean_and_standard_deviation(const Iterator begin,
2444 const Iterator end,
2445 const MPI_Comm comm)
2446 {
2447 // below we do simple and straight-forward implementation. More elaborate
2448 // options are:
2449 // http://dx.doi.org/10.1145/2807591.2807644 section 3.1.2
2450 // https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Welford's_online_algorithm
2451 // https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Online
2452 using Std = typename numbers::NumberTraits<Number>::real_type;
2453 const Number sum = std::accumulate(begin, end, Number(0.));
2454
2455 const auto size = Utilities::MPI::sum(std::distance(begin, end), comm);
2456 Assert(size > 0, ExcDivideByZero());
2457 const Number mean =
2458 Utilities::MPI::sum(sum, comm) / static_cast<Std>(size);
2459 Std sq_sum = 0.;
2460 std::for_each(begin, end, [&mean, &sq_sum](const Number &v) {
2462 });
2463 sq_sum = Utilities::MPI::sum(sq_sum, comm);
2464 return std::make_pair(mean,
2465 std::sqrt(sq_sum / static_cast<Std>(size - 1)));
2466 }
2467# endif
2468
2469#endif
2470 } // end of namespace MPI
2471} // end of namespace Utilities
2472
2473
2475
2476#endif
*  iterator end()
*  *  iterator begin()
*  x_component_mask set(0, true)
void sum(const SparseMatrix< Number > &local, const MPI_Comm mpi_communicator, SparseMatrix< Number > &global)
SymmetricTensor< rank, dim, Number > sum(const SymmetricTensor< rank, dim, Number > &local, const MPI_Comm mpi_communicator)
Tensor< rank, dim, Number > sum(const Tensor< rank, dim, Number > &local, const MPI_Comm mpi_communicator)
ScopedLock(CollectiveMutex &mutex, const MPI_Comm comm)
Definition mpi.h:348
void lock(const MPI_Comm comm)
Definition mpi.cc:1978
void unlock(const MPI_Comm comm)
Definition mpi.cc:2015
DuplicatedCommunicator(const DuplicatedCommunicator &)=delete
DuplicatedCommunicator & operator=(const DuplicatedCommunicator &)=delete
DuplicatedCommunicator(const MPI_Comm communicator)
Definition mpi.h:263
Future(const Future &)=delete
Future(Future &&) noexcept=default
std::function< void()> wait_function
Definition mpi.h:560
std::function< T()> get_and_cleanup_function
Definition mpi.h:561
Future(W &&wait_operation, G &&get_and_cleanup_operation)
#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
Point< 2 > second
Definition grid_out.cc:4640
Point< 2 > first
Definition grid_out.cc:4639
static ::ExceptionBase & ExcNeedsMPI()
#define Assert(cond, exc)
#define AssertDimension(dim1, dim2)
#define AssertThrowMPI(error_code)
#define AssertIndexRange(index, range)
static ::ExceptionBase & ExcInternalError()
static ::ExceptionBase & ExcDivideByZero()
static ::ExceptionBase & ExcMessage(std::string arg1)
#define AssertThrow(cond, exc)
const unsigned int my_rank
Definition mpi.cc:917
const IndexSet & indices_to_look_up
Definition mpi.cc:907
std::vector< index_type > data
Definition mpi.cc:734
std::size_t size
Definition mpi.cc:733
const MPI_Comm comm
Definition mpi.cc:912
const IndexSet & owned_indices
Definition mpi.cc:901
types::global_dof_index locally_owned_size
Definition mpi.cc:821
const unsigned int n_procs
Definition mpi.cc:923
constexpr char T
*  *  if(update_pressure &update_flags) *  compute_pressure(constitutive_request
*  *  *  RotationFunction< dim, Number >::RotationFunction Number(dim)
MPI_Datatype mpi_type_id(const bool *)
Definition mpi.h:1520
std::vector< IndexSet > create_ascending_partitioning(const MPI_Comm comm, const types::global_dof_index locally_owned_size)
Definition mpi.cc:175
std::pair< std::vector< unsigned int >, std::map< unsigned int, IndexSet > > compute_index_owner_and_requesters(const IndexSet &owned_indices, const IndexSet &indices_to_look_up, const MPI_Comm &comm)
Definition mpi.cc:1871
std::pair< T, T > partial_and_total_sum(const T &value, const MPI_Comm comm)
T sum(const T &t, const MPI_Comm mpi_communicator)
bool logical_and(const bool t, const MPI_Comm mpi_communicator)
T logical_or(const T &t, const MPI_Comm mpi_communicator)
std::unique_ptr< MPI_Datatype, void(*)(MPI_Datatype *)> create_mpi_data_type_n_bytes(const std::size_t n_bytes)
Definition mpi.cc:219
Future< T > irecv(MPI_Comm communicator, const unsigned int source_rank, const unsigned int mpi_tag=0)
std::map< unsigned int, T > some_to_some(const MPI_Comm comm, const std::map< unsigned int, T > &objects_to_send)
std::pair< Number, typename numbers::NumberTraits< Number >::real_type > mean_and_standard_deviation(const Iterator begin, const Iterator end, const MPI_Comm comm)
unsigned int n_mpi_processes(const MPI_Comm mpi_communicator)
Definition mpi.cc:103
T max(const T &t, const MPI_Comm mpi_communicator)
T min(const T &t, const MPI_Comm mpi_communicator)
std::vector< T > all_gather(const MPI_Comm comm, const T &object_to_send)
std::vector< unsigned int > compute_index_owner(const IndexSet &owned_indices, const IndexSet &indices_to_look_up, const MPI_Comm comm)
Definition mpi.cc:1820
std::vector< T > compute_set_union(const std::vector< T > &vec, const MPI_Comm comm)
unsigned int this_mpi_process(const MPI_Comm mpi_communicator)
Definition mpi.cc:118
T all_reduce(const T &local_value, const MPI_Comm comm, const std::function< T(const T &, const T &)> &combiner)
IndexSet create_evenly_distributed_partitioning(const MPI_Comm comm, const types::global_dof_index total_size)
Definition mpi.cc:204
std::vector< unsigned int > compute_point_to_point_communication_pattern(const MPI_Comm mpi_comm, const std::vector< unsigned int > &destinations)
Definition mpi.cc:278
MPI_Comm duplicate_communicator(const MPI_Comm mpi_communicator)
Definition mpi.cc:154
T reduce(const T &local_value, const MPI_Comm comm, const std::function< T(const T &, const T &)> &combiner, const unsigned int root_process=0)
bool job_supports_mpi()
Definition mpi.cc:680
const MPI_Datatype mpi_type_id_for_type
Definition mpi.h:1685
void free_communicator(MPI_Comm mpi_communicator)
Definition mpi.cc:165
std::vector< unsigned int > mpi_processes_within_communicator(const MPI_Comm comm_large, const MPI_Comm comm_small)
Definition mpi.cc:134
unsigned int compute_n_point_to_point_communications(const MPI_Comm mpi_comm, const std::vector< unsigned int > &destinations)
Definition mpi.cc:371
std::vector< T > gather(const MPI_Comm comm, const T &object_to_send, const unsigned int root_process=0)
MinMaxAvg min_max_avg(const double my_value, const MPI_Comm mpi_communicator)
Definition mpi.cc:77
constexpr bool is_mpi_type
Definition mpi.h:113
T scatter(const MPI_Comm comm, const std::vector< T > &objects_to_send, const unsigned int root_process=0)
Future< void > isend(const T &object, MPI_Comm communicator, const unsigned int target_rank, const unsigned int mpi_tag=0)
T broadcast(const MPI_Comm comm, const T &object_to_send, const unsigned int root_process=0)
std::size_t pack(const T &object, std::vector< char > &dest_buffer, const bool allow_compression=true)
Definition utilities.h:1352
IndexSet create_evenly_distributed_partitioning(const unsigned int my_partition_id, const unsigned int n_partitions, const types::global_dof_index total_size)
Definition mpi.cc:53
constexpr types::global_dof_index invalid_size_type
Definition types.h:240
constexpr unsigned int invalid_unsigned_int
Definition types.h:228
STL namespace.
::VectorizedArray< Number, width > min(const ::VectorizedArray< Number, width > &, const ::VectorizedArray< Number, width > &)
::VectorizedArray< Number, width > sqrt(const ::VectorizedArray< Number, width > &)
Definition types.h:30
unsigned int max_index
Definition mpi.h:994
unsigned int min_index
Definition mpi.h:984
static constexpr real_type abs_square(const number &x)
Definition numbers.h:551
void gather(VectorizedArray< Number, width > &out, const std::array< const Number *, width > &ptrs, const unsigned int offset)