deal.II version 9.8.0
\(\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 {
360 mutex.unlock(comm);
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
1225 template <typename T>
1226 [[nodiscard]] std::vector<T>
1228 const T &object_to_send,
1229 const unsigned int root_process = 0);
1230
1245 template <typename T>
1246 [[nodiscard]] T
1248 const std::vector<T> &objects_to_send,
1249 const unsigned int root_process = 0);
1250
1289 template <typename T>
1290 [[nodiscard]] T
1292 const T &object_to_send,
1293 const unsigned int root_process = 0);
1294
1311 template <typename T>
1312 void
1313 broadcast(T *buffer,
1314 const std::size_t count,
1315 const unsigned int root,
1316 const MPI_Comm comm);
1317
1330 template <typename T>
1331 [[nodiscard]] T
1332 reduce(const T &local_value,
1333 const MPI_Comm comm,
1334 const std::function<T(const T &, const T &)> &combiner,
1335 const unsigned int root_process = 0);
1336
1337
1349 template <typename T, typename = std::enable_if_t<is_mpi_type<T> == true>>
1350 [[nodiscard]] std::pair<T, T>
1352
1353
1363 template <typename T>
1364 [[nodiscard]] T
1365 all_reduce(const T &local_value,
1366 const MPI_Comm comm,
1367 const std::function<T(const T &, const T &)> &combiner);
1368
1369
1390 template <typename T>
1392 isend(const T &object,
1393 MPI_Comm communicator,
1394 const unsigned int target_rank,
1395 const unsigned int mpi_tag = 0);
1396
1397
1414 template <typename T>
1415 Future<T>
1416 irecv(MPI_Comm communicator,
1417 const unsigned int source_rank,
1418 const unsigned int mpi_tag = 0);
1419
1420
1464 std::vector<unsigned int>
1465 compute_index_owner(const IndexSet &owned_indices,
1466 const IndexSet &indices_to_look_up,
1467 const MPI_Comm comm);
1468
1479 std::pair<std::vector<unsigned int>, std::map<unsigned int, IndexSet>>
1480 compute_index_owner_and_requesters(const IndexSet &owned_indices,
1481 const IndexSet &indices_to_look_up,
1482 const MPI_Comm &comm);
1483
1491 template <typename T>
1492 std::vector<T>
1493 compute_set_union(const std::vector<T> &vec, const MPI_Comm comm);
1494
1498 template <typename T>
1499 std::set<T>
1500 compute_set_union(const std::set<T> &set, const MPI_Comm comm);
1501
1502
1503
1504 /* --------------------------- inline functions ------------------------- */
1505
1506 namespace internal
1507 {
1514 {
1515#ifdef DEAL_II_WITH_MPI
1516 inline MPI_Datatype
1517 mpi_type_id(const bool *)
1518 {
1519 return MPI_CXX_BOOL;
1520 }
1521
1522
1523
1524 inline MPI_Datatype
1525 mpi_type_id(const char *)
1526 {
1527 return MPI_CHAR;
1528 }
1529
1530
1531
1532 inline MPI_Datatype
1533 mpi_type_id(const signed char *)
1534 {
1535 return MPI_SIGNED_CHAR;
1536 }
1537
1538
1539
1540 inline MPI_Datatype
1541 mpi_type_id(const wchar_t *)
1542 {
1543 return MPI_WCHAR;
1544 }
1545
1546
1547
1548 inline MPI_Datatype
1549 mpi_type_id(const short *)
1550 {
1551 return MPI_SHORT;
1552 }
1553
1554
1555
1556 inline MPI_Datatype
1557 mpi_type_id(const int *)
1558 {
1559 return MPI_INT;
1560 }
1561
1562
1563
1564 inline MPI_Datatype
1565 mpi_type_id(const long int *)
1566 {
1567 return MPI_LONG;
1568 }
1569
1570
1571
1572 inline MPI_Datatype
1573 mpi_type_id(const long long int *)
1574 {
1575 return MPI_LONG_LONG;
1576 }
1577
1578
1579
1580 inline MPI_Datatype
1581 mpi_type_id(const unsigned char *)
1582 {
1583 return MPI_UNSIGNED_CHAR;
1584 }
1585
1586
1587
1588 inline MPI_Datatype
1589 mpi_type_id(const unsigned short *)
1590 {
1591 return MPI_UNSIGNED_SHORT;
1592 }
1593
1594
1595
1596 inline MPI_Datatype
1597 mpi_type_id(const unsigned int *)
1598 {
1599 return MPI_UNSIGNED;
1600 }
1601
1602
1603
1604 inline MPI_Datatype
1605 mpi_type_id(const unsigned long int *)
1606 {
1607 return MPI_UNSIGNED_LONG;
1608 }
1609
1610
1611
1612 inline MPI_Datatype
1613 mpi_type_id(const unsigned long long int *)
1614 {
1615 return MPI_UNSIGNED_LONG_LONG;
1616 }
1617
1618
1619
1620 inline MPI_Datatype
1621 mpi_type_id(const float *)
1622 {
1623 return MPI_FLOAT;
1624 }
1625
1626
1627
1628 inline MPI_Datatype
1629 mpi_type_id(const double *)
1630 {
1631 return MPI_DOUBLE;
1632 }
1633
1634
1635
1636 inline MPI_Datatype
1637 mpi_type_id(const long double *)
1638 {
1639 return MPI_LONG_DOUBLE;
1640 }
1641
1642
1643
1644 inline MPI_Datatype
1645 mpi_type_id(const std::complex<float> *)
1646 {
1647 return MPI_COMPLEX;
1648 }
1649
1650
1651
1652 inline MPI_Datatype
1653 mpi_type_id(const std::complex<double> *)
1654 {
1655 return MPI_DOUBLE_COMPLEX;
1656 }
1657#endif
1658 } // namespace MPIDataTypes
1659 } // namespace internal
1660
1661
1662
1663#ifdef DEAL_II_WITH_MPI
1681 template <typename T>
1682 inline const MPI_Datatype mpi_type_id_for_type =
1684 static_cast<std::remove_cv_t<std::remove_reference_t<T>> *>(nullptr));
1685#endif
1686
1687#ifndef DOXYGEN
1688 namespace internal
1689 {
1690 // declaration for an internal function that lives in mpi.templates.h
1691 template <typename T>
1692 void
1693 all_reduce(const MPI_Op &mpi_op,
1694 const ArrayView<const T> &values,
1695 const MPI_Comm mpi_communicator,
1696 const ArrayView<T> &output);
1697 } // namespace internal
1698
1699
1700 template <typename T>
1701 template <typename W, typename G>
1702 Future<T>::Future(W &&wait_operation, G &&get_and_cleanup_operation)
1703 : wait_function(wait_operation)
1704 , get_and_cleanup_function(get_and_cleanup_operation)
1705 , is_done(false)
1706 , get_was_called(false)
1707 {}
1708
1709
1710
1711 template <typename T>
1712 Future<T>::~Future()
1713 {
1714 // If there is a clean-up function, and if it has not been
1715 // called yet, then do so. Note that we may not have a
1716 // clean-up function (not even an empty one) if the current
1717 // object has been moved from, into another object, and as
1718 // a consequence the std::function objects are now empty
1719 // even though they were initialized in the constructor.
1720 // (A std::function object whose object is a an empty lambda
1721 // function, [](){}, is not an empty std::function object.)
1722 if ((get_was_called == false) && get_and_cleanup_function)
1723 get();
1724 }
1725
1726
1727
1728 template <typename T>
1729 void
1730 Future<T>::wait()
1731 {
1732 if (is_done == false)
1733 {
1734 wait_function();
1735
1736 is_done = true;
1737 }
1738 }
1739
1740
1741 template <typename T>
1742 T
1743 Future<T>::get()
1744 {
1745 Assert(get_was_called == false,
1746 ExcMessage(
1747 "You can't call get() more than once on a Future object."));
1748 get_was_called = true;
1749
1750 wait();
1751 return get_and_cleanup_function();
1752 }
1753
1754
1755
1756 template <typename T, unsigned int N>
1757 void
1758 sum(const T (&values)[N], const MPI_Comm mpi_communicator, T (&sums)[N])
1759 {
1760 internal::all_reduce(MPI_SUM,
1761 ArrayView<const T>(values, N),
1762 mpi_communicator,
1763 ArrayView<T>(sums, N));
1764 }
1765
1766
1767
1768 template <typename T, unsigned int N>
1769 void
1770 max(const T (&values)[N], const MPI_Comm mpi_communicator, T (&maxima)[N])
1771 {
1772 internal::all_reduce(MPI_MAX,
1773 ArrayView<const T>(values, N),
1774 mpi_communicator,
1775 ArrayView<T>(maxima, N));
1776 }
1777
1778
1779
1780 template <typename T, unsigned int N>
1781 void
1782 min(const T (&values)[N], const MPI_Comm mpi_communicator, T (&minima)[N])
1783 {
1784 internal::all_reduce(MPI_MIN,
1785 ArrayView<const T>(values, N),
1786 mpi_communicator,
1787 ArrayView<T>(minima, N));
1788 }
1789
1790
1791
1792 template <typename T, unsigned int N>
1793 void
1794 logical_or(const T (&values)[N],
1795 const MPI_Comm mpi_communicator,
1796 T (&results)[N])
1797 {
1798 static_assert(std::is_integral_v<T>,
1799 "The MPI_LOR operation only allows integral data types.");
1800
1801 internal::all_reduce(MPI_LOR,
1802 ArrayView<const T>(values, N),
1803 mpi_communicator,
1804 ArrayView<T>(results, N));
1805 }
1806
1807
1808
1809 inline bool
1810 logical_and(const bool t, const MPI_Comm mpi_communicator)
1811 {
1812 bool return_value = false;
1813 internal::all_reduce(MPI_LAND,
1814 ArrayView<const bool>(&t, 1),
1815 mpi_communicator,
1816 ArrayView<bool>(&return_value, 1));
1817 return return_value;
1818 }
1819
1820
1821
1822 template <typename T>
1823 std::map<unsigned int, T>
1824 some_to_some(const MPI_Comm comm,
1825 const std::map<unsigned int, T> &objects_to_send)
1826 {
1827# ifndef DEAL_II_WITH_MPI
1828 (void)comm;
1829 Assert(objects_to_send.size() < 2,
1830 ExcMessage("Cannot send to more than one processor."));
1831 Assert(objects_to_send.find(0) != objects_to_send.end() ||
1832 objects_to_send.empty(),
1833 ExcMessage("Can only send to myself or to nobody."));
1834 return objects_to_send;
1835# else
1836 const auto my_proc = this_mpi_process(comm);
1837
1838 std::map<unsigned int, T> received_objects;
1839
1840 std::vector<unsigned int> send_to;
1841 send_to.reserve(objects_to_send.size());
1842 for (const auto &m : objects_to_send)
1843 if (m.first == my_proc)
1844 received_objects[my_proc] = m.second;
1845 else
1846 send_to.emplace_back(m.first);
1847
1848 const unsigned int n_expected_incoming_messages =
1850
1851 // Protect the following communication:
1852 static CollectiveMutex mutex;
1853 CollectiveMutex::ScopedLock lock(mutex, comm);
1854
1855 // If we have something to send, or we expect something from other
1856 // processors, we need to visit one of the two scopes below. Otherwise,
1857 // no other action is required by this mpi process, and we can safely
1858 // return.
1859 if (send_to.empty() && n_expected_incoming_messages == 0)
1860 return received_objects;
1861
1862 const int mpi_tag =
1863 internal::Tags::compute_point_to_point_communication_pattern;
1864
1865 // Sending buffers
1866 std::vector<std::vector<char>> buffers_to_send(send_to.size());
1867 std::vector<MPI_Request> buffer_send_requests(send_to.size());
1868 {
1869 unsigned int i = 0;
1870 for (const auto &rank_obj : objects_to_send)
1871 if (rank_obj.first != my_proc)
1872 {
1873 const auto &rank = rank_obj.first;
1874 buffers_to_send[i] =
1875 Utilities::pack(rank_obj.second, /*compress=*/false);
1876 const int ierr = MPI_Isend(buffers_to_send[i].data(),
1877 buffers_to_send[i].size(),
1878 MPI_CHAR,
1879 rank,
1880 mpi_tag,
1881 comm,
1882 &buffer_send_requests[i]);
1883 AssertThrowMPI(ierr);
1884 ++i;
1885 }
1886 }
1887
1888 // Fill the output map
1889 {
1890 std::vector<char> buffer;
1891 // We do this on a first come/first served basis
1892 for (unsigned int i = 0; i < n_expected_incoming_messages; ++i)
1893 {
1894 // Probe what's going on. Take data from the first available sender
1895 MPI_Status status;
1896 int ierr = MPI_Probe(MPI_ANY_SOURCE, mpi_tag, comm, &status);
1897 AssertThrowMPI(ierr);
1898
1899 // Length of the message
1900 int len;
1901 ierr = MPI_Get_count(&status, MPI_CHAR, &len);
1902 AssertThrowMPI(ierr);
1903 buffer.resize(len);
1904
1905 // Source rank
1906 const unsigned int rank = status.MPI_SOURCE;
1907
1908 // Actually receive the message
1909 ierr = MPI_Recv(buffer.data(),
1910 len,
1911 MPI_CHAR,
1912 status.MPI_SOURCE,
1913 status.MPI_TAG,
1914 comm,
1915 MPI_STATUS_IGNORE);
1916 AssertThrowMPI(ierr);
1917 Assert(received_objects.find(rank) == received_objects.end(),
1919 "I should not receive again from this rank"));
1920 received_objects[rank] =
1921 Utilities::unpack<T>(buffer, /*compress=*/false);
1922 }
1923 }
1924
1925 // Wait to have sent all objects.
1926 const int ierr = MPI_Waitall(send_to.size(),
1927 buffer_send_requests.data(),
1928 MPI_STATUSES_IGNORE);
1929 AssertThrowMPI(ierr);
1930
1931 return received_objects;
1932# endif // deal.II with MPI
1933 }
1934
1935
1936
1937 template <typename T, typename>
1938 std::pair<T, T>
1939 partial_and_total_sum(const T &value, const MPI_Comm comm)
1940 {
1941# ifndef DEAL_II_WITH_MPI
1942 (void)comm;
1943 return {0, value};
1944# else
1946 return {0, value};
1947 else
1948 {
1949 T prefix = {};
1950
1951 // First obtain every process's prefix sum:
1952 int ierr =
1953 MPI_Exscan(&value,
1954 &prefix,
1955 1,
1957 MPI_SUM,
1958 comm);
1959 AssertThrowMPI(ierr);
1960
1961 // Then we also need the total sum. We could obtain it by
1962 // calling Utilities::MPI::sum(), but it is cheaper if we
1963 // broadcast it from the last process, which can compute it
1964 // from its own prefix sum plus its own value.
1967
1968 return {prefix, sum};
1969 }
1970# endif
1971 }
1972
1973
1974
1975 template <typename T>
1976 std::vector<T>
1977 all_gather(const MPI_Comm comm, const T &object)
1978 {
1979 if (job_supports_mpi() == false)
1980 return {object};
1981
1982# ifndef DEAL_II_WITH_MPI
1983 (void)comm;
1984 std::vector<T> v(1, object);
1985 return v;
1986# else
1987 const auto n_procs = ::Utilities::MPI::n_mpi_processes(comm);
1988
1989 std::vector<char> buffer = Utilities::pack(object, /*compress=*/false);
1990
1991 int n_local_data = buffer.size();
1992
1993 // Vector to store the size of loc_data_array for every process
1994 std::vector<int> size_all_data(n_procs, 0);
1995
1996 // Exchanging the size of each buffer
1997 int ierr = MPI_Allgather(
1998 &n_local_data, 1, MPI_INT, size_all_data.data(), 1, MPI_INT, comm);
1999 AssertThrowMPI(ierr);
2000
2001 // Now computing the displacement, relative to recvbuf,
2002 // at which to store the incoming buffer
2003 std::vector<int> rdispls(n_procs);
2004 rdispls[0] = 0;
2005 for (unsigned int i = 1; i < n_procs; ++i)
2006 rdispls[i] = rdispls[i - 1] + size_all_data[i - 1];
2007
2008 // Step 3: exchange the buffer:
2009 std::vector<char> received_unrolled_buffer(rdispls.back() +
2010 size_all_data.back());
2011
2012 ierr = MPI_Allgatherv(buffer.data(),
2013 n_local_data,
2014 MPI_CHAR,
2015 received_unrolled_buffer.data(),
2016 size_all_data.data(),
2017 rdispls.data(),
2018 MPI_CHAR,
2019 comm);
2020 AssertThrowMPI(ierr);
2021
2022 std::vector<T> received_objects(n_procs);
2023 for (unsigned int i = 0; i < n_procs; ++i)
2024 {
2025 std::vector<char> local_buffer(received_unrolled_buffer.begin() +
2026 rdispls[i],
2027 received_unrolled_buffer.begin() +
2028 rdispls[i] + size_all_data[i]);
2029 received_objects[i] =
2030 Utilities::unpack<T>(local_buffer, /*compress=*/false);
2031 }
2032
2033 return received_objects;
2034# endif
2035 }
2036
2037
2038
2039 template <typename T>
2040 std::vector<T>
2041 gather(const MPI_Comm comm,
2042 const T &object_to_send,
2043 const unsigned int root_process)
2044 {
2045# ifndef DEAL_II_WITH_MPI
2046 (void)comm;
2047 (void)root_process;
2048 std::vector<T> v(1, object_to_send);
2049 return v;
2050# else
2051 const auto n_procs = ::Utilities::MPI::n_mpi_processes(comm);
2052 const auto my_rank = ::Utilities::MPI::this_mpi_process(comm);
2053
2054 AssertIndexRange(root_process, n_procs);
2055
2056 std::vector<char> buffer =
2057 Utilities::pack(object_to_send, /*compress=*/false);
2058 int n_local_data = buffer.size();
2059
2060 // Vector to store the size of loc_data_array for every process
2061 // only the root process needs to allocate memory for that purpose
2062 std::vector<int> size_all_data;
2063 if (my_rank == root_process)
2064 size_all_data.resize(n_procs, 0);
2065
2066 // Exchanging the size of each buffer
2067 int ierr = MPI_Gather(&n_local_data,
2068 1,
2069 MPI_INT,
2070 size_all_data.data(),
2071 1,
2072 MPI_INT,
2073 root_process,
2074 comm);
2075 AssertThrowMPI(ierr);
2076
2077 // Now computing the displacement, relative to recvbuf,
2078 // at which to store the incoming buffer; only for root
2079 std::vector<int> rdispls;
2080 if (my_rank == root_process)
2081 {
2082 rdispls.resize(n_procs, 0);
2083 for (unsigned int i = 1; i < n_procs; ++i)
2084 rdispls[i] = rdispls[i - 1] + size_all_data[i - 1];
2085 }
2086 // exchange the buffer:
2087 std::vector<char> received_unrolled_buffer;
2088 if (my_rank == root_process)
2089 received_unrolled_buffer.resize(rdispls.back() + size_all_data.back());
2090
2091 ierr = MPI_Gatherv(buffer.data(),
2092 n_local_data,
2093 MPI_CHAR,
2094 received_unrolled_buffer.data(),
2095 size_all_data.data(),
2096 rdispls.data(),
2097 MPI_CHAR,
2098 root_process,
2099 comm);
2100 AssertThrowMPI(ierr);
2101
2102 std::vector<T> received_objects;
2103
2104 if (my_rank == root_process)
2105 {
2106 received_objects.resize(n_procs);
2107
2108 for (unsigned int i = 0; i < n_procs; ++i)
2109 received_objects[i] = Utilities::unpack<T>(
2110 received_unrolled_buffer.cbegin() + rdispls[i],
2111 received_unrolled_buffer.cbegin() + rdispls[i] + size_all_data[i],
2112 /*compress=*/false);
2113 }
2114 return received_objects;
2115# endif
2116 }
2117
2118
2119
2120 template <typename T>
2121 T
2122 scatter(const MPI_Comm comm,
2123 const std::vector<T> &objects_to_send,
2124 const unsigned int root_process)
2125 {
2126# ifndef DEAL_II_WITH_MPI
2127 (void)comm;
2128 (void)root_process;
2129
2130 AssertDimension(objects_to_send.size(), 1);
2131
2132 return objects_to_send[0];
2133# else
2134 const auto n_procs = ::Utilities::MPI::n_mpi_processes(comm);
2135 const auto my_rank = ::Utilities::MPI::this_mpi_process(comm);
2136
2137 AssertIndexRange(root_process, n_procs);
2139 (my_rank != root_process && objects_to_send.empty()) ||
2140 objects_to_send.size() == n_procs,
2141 ExcMessage(
2142 "The number of objects to be scattered must correspond to the number processes."));
2143
2144 std::vector<char> send_buffer;
2145 std::vector<int> send_counts;
2146 std::vector<int> send_displacements;
2147
2148 if (my_rank == root_process)
2149 {
2150 send_counts.resize(n_procs, 0);
2151 send_displacements.resize(n_procs + 1, 0);
2152
2153 for (unsigned int i = 0; i < n_procs; ++i)
2154 {
2155 const auto packed_data =
2156 Utilities::pack(objects_to_send[i], /*compress=*/false);
2157 send_buffer.insert(send_buffer.end(),
2158 packed_data.begin(),
2159 packed_data.end());
2160 send_counts[i] = packed_data.size();
2161 }
2162
2163 for (unsigned int i = 0; i < n_procs; ++i)
2164 send_displacements[i + 1] = send_displacements[i] + send_counts[i];
2165 }
2166
2167 int n_local_data;
2168 int ierr = MPI_Scatter(send_counts.data(),
2169 1,
2170 MPI_INT,
2171 &n_local_data,
2172 1,
2173 MPI_INT,
2174 root_process,
2175 comm);
2176 AssertThrowMPI(ierr);
2177
2178 std::vector<char> recv_buffer(n_local_data);
2179
2180 ierr = MPI_Scatterv(send_buffer.data(),
2181 send_counts.data(),
2182 send_displacements.data(),
2183 MPI_CHAR,
2184 recv_buffer.data(),
2185 n_local_data,
2186 MPI_CHAR,
2187 root_process,
2188 comm);
2189 AssertThrowMPI(ierr);
2190
2191 return Utilities::unpack<T>(recv_buffer, /*compress=*/false);
2192# endif
2193 }
2194
2195
2196 template <typename T>
2197 void
2198 broadcast(T *buffer,
2199 const std::size_t count,
2200 const unsigned int root,
2201 const MPI_Comm comm)
2202 {
2203 static_assert(is_mpi_type<T>,
2204 "This function is currently only implemented for "
2205 "MPI-supported data types.");
2206
2207# ifndef DEAL_II_WITH_MPI
2208 (void)buffer;
2209 (void)count;
2210 (void)root;
2211 (void)comm;
2212# else
2213 Assert(root < n_mpi_processes(comm),
2214 ExcMessage("Invalid root rank specified."));
2215
2216 // MPI_Bcast's count is a signed int, so send at most 2^31 in each
2217 // iteration:
2218 const size_t max_send_count = std::numeric_limits<signed int>::max();
2219
2220 size_t total_sent_count = 0;
2221 while (total_sent_count < count)
2222 {
2223 const size_t current_count =
2224 std::min(count - total_sent_count, max_send_count);
2225
2226 const int ierr = MPI_Bcast(buffer + total_sent_count,
2227 current_count,
2228 mpi_type_id_for_type<decltype(*buffer)>,
2229 root,
2230 comm);
2231 AssertThrowMPI(ierr);
2232 total_sent_count += current_count;
2233 }
2234# endif
2235 }
2236
2237
2238
2239 template <typename T>
2240 T
2241 broadcast(const MPI_Comm comm,
2242 const T &object_to_send,
2243 const unsigned int root_process)
2244 {
2245# ifndef DEAL_II_WITH_MPI
2246 (void)comm;
2247 (void)root_process;
2248 return object_to_send;
2249# else
2250 const auto n_procs = ::Utilities::MPI::n_mpi_processes(comm);
2251 AssertIndexRange(root_process, n_procs);
2252 (void)n_procs;
2253
2254 if constexpr (is_mpi_type<T>)
2255 {
2256 T object = object_to_send;
2257 const int ierr =
2258 MPI_Bcast(&object, 1, mpi_type_id_for_type<T>, root_process, comm);
2259 AssertThrowMPI(ierr);
2260
2261 return object;
2262 }
2263 else
2264 {
2265 std::vector<char> buffer;
2266 std::size_t buffer_size = numbers::invalid_size_type;
2267
2268 // On the root process, pack the data and determine what the
2269 // buffer size needs to be.
2270 if (this_mpi_process(comm) == root_process)
2271 {
2272 buffer = Utilities::pack(object_to_send, /*compress=*/false);
2273 buffer_size = buffer.size();
2274 }
2275
2276 // Exchange the size of buffer
2277 int ierr = MPI_Bcast(&buffer_size,
2278 1,
2279 mpi_type_id_for_type<decltype(buffer_size)>,
2280 root_process,
2281 comm);
2282 AssertThrowMPI(ierr);
2283
2284 // If not on the root process, correctly size the buffer to
2285 // receive the data, then do exactly that.
2286 if (this_mpi_process(comm) != root_process)
2287 buffer.resize(buffer_size);
2288
2289 broadcast(buffer.data(), buffer_size, root_process, comm);
2290
2291 if (Utilities::MPI::this_mpi_process(comm) == root_process)
2292 return object_to_send;
2293 else
2294 return Utilities::unpack<T>(buffer, /*compress=*/false);
2295 }
2296# endif
2297 }
2298
2299
2300
2301 template <typename T>
2302 Future<void>
2303 isend(const T &object,
2304 MPI_Comm communicator,
2305 const unsigned int target_rank,
2306 const unsigned int mpi_tag)
2307 {
2308# ifndef DEAL_II_WITH_MPI
2309 Assert(false, ExcNeedsMPI());
2310 (void)object;
2311 (void)communicator;
2312 (void)target_rank;
2313 (void)mpi_tag;
2314 return Future<void>([]() {}, []() {});
2315# else
2316 // Create a pointer to a send buffer into which we pack the object
2317 // to be sent. The buffer will be released by the Future object once
2318 // the send has been verified to have succeeded.
2319 //
2320 // Conceptually, we would like this send buffer to be a
2321 // std::unique_ptr object whose ownership is later handed over
2322 // to the cleanup function. That has the disadvantage that the
2323 // cleanup object is a non-copyable lambda capture, leading to
2324 // awkward semantics. Instead, we use a std::shared_ptr; we move
2325 // this shared pointer into the cleanup function, which means
2326 // that there is exactly one shared pointer who owns the buffer
2327 // at any given time, though the latter is not an important
2328 // optimization.
2329 std::shared_ptr<std::vector<char>> send_buffer =
2330 std::make_unique<std::vector<char>>(
2331 Utilities::pack(object, /*compress=*/false));
2332
2333 // Now start the send, and store the result in a request object that
2334 // we can then wait for later:
2335 MPI_Request request;
2336 const int ierr =
2337 MPI_Isend(send_buffer->data(),
2338 send_buffer->size(),
2339 mpi_type_id_for_type<decltype(*send_buffer->data())>,
2340 target_rank,
2341 mpi_tag,
2342 communicator,
2343 &request);
2344 AssertThrowMPI(ierr);
2345
2346 // Then return a std::future-like object that has a wait()
2347 // function one can use to wait for the communication to finish,
2348 // and that has a cleanup function to be called at some point
2349 // after that makes sure the send buffer gets deallocated. This
2350 // cleanup function takes over ownership of the send buffer.
2351 //
2352 // Note that the body of the lambda function of the clean-up
2353 // function could be left empty. If that were so, once the
2354 // lambda function object goes out of scope, the 'send_buffer'
2355 // member of the closure object goes out of scope as well and so
2356 // the send_buffer is destroyed. But we may want to release the
2357 // buffer itself as early as possible, and so we clear the
2358 // buffer when the Future::get() function is called.
2359 auto wait = [request]() mutable {
2360 const int ierr = MPI_Wait(&request, MPI_STATUS_IGNORE);
2361 AssertThrowMPI(ierr);
2362 };
2363 auto cleanup = [send_buffer = std::move(send_buffer)]() {
2364 send_buffer->clear();
2365 };
2366 return Future<void>(wait, cleanup);
2367# endif
2368 }
2369
2370
2371
2372 template <typename T>
2373 Future<T>
2374 irecv(MPI_Comm communicator,
2375 const unsigned int source_rank,
2376 const unsigned int mpi_tag)
2377 {
2378# ifndef DEAL_II_WITH_MPI
2379 Assert(false, ExcNeedsMPI());
2380 (void)communicator;
2381 (void)source_rank;
2382 (void)mpi_tag;
2383 return Future<void>([]() {}, []() { return T{}; });
2384# else
2385 // Use a 'probe' operation for the 'wait' operation of the
2386 // Future this function returns. It will trigger whenever we get
2387 // the incoming message. Later, once we have received the message, we
2388 // can query its size and allocate a receiver buffer.
2389 //
2390 // Since we may be waiting for multiple messages from the same
2391 // incoming process (with possibly the same tag -- we can't
2392 // know), we must make sure that the 'probe' operation we have
2393 // here (and which we use to determine the buffer size) matches
2394 // the 'recv' operation with which we actually get the data
2395 // later on. This is exactly what the 'MPI_Mprobe' function and
2396 // its 'I'mmediate variant is there for, coupled with the
2397 // 'MPI_Mrecv' call that would put into the clean-up function
2398 // below.
2399 std::shared_ptr<MPI_Message> message = std::make_shared<MPI_Message>();
2400 std::shared_ptr<MPI_Status> status = std::make_shared<MPI_Status>();
2401
2402 auto wait = [source_rank, mpi_tag, communicator, message, status]() {
2403 const int ierr = MPI_Mprobe(
2404 source_rank, mpi_tag, communicator, message.get(), status.get());
2405 AssertThrowMPI(ierr);
2406 };
2407
2408
2409 // Now also define the function that actually gets the data:
2410 auto get = [status, message]() {
2411 int number_amount;
2412 int ierr;
2413 ierr = MPI_Get_count(status.get(), MPI_CHAR, &number_amount);
2414 AssertThrowMPI(ierr);
2415
2416 std::vector<char> receive_buffer(number_amount);
2417
2418 // Then actually get the data, using the matching MPI_Mrecv to the above
2419 // MPI_Mprobe:
2420 ierr = MPI_Mrecv(receive_buffer.data(),
2421 number_amount,
2422 mpi_type_id_for_type<decltype(*receive_buffer.data())>,
2423 message.get(),
2424 status.get());
2425 AssertThrowMPI(ierr);
2426
2427 // Return the unpacked object:
2428 return Utilities::unpack<T>(receive_buffer, /*compress=*/false);
2429 };
2430
2431 return Future<T>(wait, get);
2432# endif
2433 }
2434
2435
2436
2437# ifdef DEAL_II_WITH_MPI
2438 template <class Iterator, typename Number>
2439 std::pair<Number, typename numbers::NumberTraits<Number>::real_type>
2440 mean_and_standard_deviation(const Iterator begin,
2441 const Iterator end,
2442 const MPI_Comm comm)
2443 {
2444 // below we do simple and straight-forward implementation. More elaborate
2445 // options are:
2446 // http://dx.doi.org/10.1145/2807591.2807644 section 3.1.2
2447 // https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Welford's_online_algorithm
2448 // https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Online
2449 using Std = typename numbers::NumberTraits<Number>::real_type;
2450 const Number sum = std::accumulate(begin, end, Number(0.));
2451
2452 const auto size = Utilities::MPI::sum(std::distance(begin, end), comm);
2453 Assert(size > 0, ExcDivideByZero());
2454 const Number mean =
2455 Utilities::MPI::sum(sum, comm) / static_cast<Std>(size);
2456 Std sq_sum = 0.;
2457 std::for_each(begin, end, [&mean, &sq_sum](const Number &v) {
2459 });
2460 sq_sum = Utilities::MPI::sum(sq_sum, comm);
2461 return std::make_pair(mean,
2462 std::sqrt(sq_sum / static_cast<Std>(size - 1)));
2463 }
2464# endif
2465
2466#endif
2467 } // end of namespace MPI
2468} // end of namespace Utilities
2469
2470
2472
2473#endif
*  iterator end()
*  *  iterator begin()
*  x_component_mask set(0, true)
InitFinalize(int &argc, char **&argv, const InitializeLibrary &libraries, const unsigned int max_num_threads=numbers::invalid_unsigned_int)
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)
MPI_InitFinalize(int &argc, char **&argv, const unsigned int max_num_threads=numbers::invalid_unsigned_int)
Definition mpi.cc:665
#define DEAL_II_NAMESPACE_OPEN
Definition config.h:38
#define DEAL_II_DISABLE_EXTRA_DIAGNOSTICS
Definition config.h:628
#define DEAL_II_NAMESPACE_CLOSE
Definition config.h:39
#define DEAL_II_ENABLE_EXTRA_DIAGNOSTICS
Definition config.h:672
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 bool IsBlockVector< VectorType >::value
constexpr char T
*  *  *  RotationFunction< dim, Number >::RotationFunction Number(dim)
MPI_Datatype mpi_type_id(const bool *)
Definition mpi.h:1517
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:1682
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
T unpack(const std::vector< char > &buffer, const bool allow_compression=true)
Definition utilities.h:1509
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:248
constexpr unsigned int invalid_unsigned_int
Definition types.h:236
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 global_dof_index
Definition types.h:92
*braid_SplitCommworld & comm
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)