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_consensus_algorithms.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) 2020 - 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_consensus_algorithm_h
14#define dealii_mpi_consensus_algorithm_h
15
16#include <deal.II/base/config.h>
17
18#include <deal.II/base/mpi.h>
19#include <deal.II/base/mpi.templates.h>
21
22#include <set>
23#include <vector>
24
26
27
28namespace Utilities
29{
30 namespace MPI
31 {
131 {
161 template <typename RequestType, typename AnswerType>
163 {
164 public:
169 virtual ~Process() = default;
170
177 virtual std::vector<unsigned int>
179
189 virtual void
190 create_request(const unsigned int other_rank, RequestType &send_buffer);
191
204 virtual void
205 answer_request(const unsigned int other_rank,
206 const RequestType &buffer_recv,
207 AnswerType &request_buffer);
208
216 virtual void
217 read_answer(const unsigned int other_rank,
218 const AnswerType &recv_buffer);
219 };
220
221
222
236 template <typename RequestType, typename AnswerType>
238 {
239 public:
243 Interface() = default;
244
249 virtual ~Interface() = default;
250
266 std::vector<unsigned int>
269
293 virtual std::vector<unsigned int>
295 const std::vector<unsigned int> &targets,
296 const std::function<RequestType(const unsigned int)> &create_request,
297 const std::function<AnswerType(const unsigned int,
298 const RequestType &)> &answer_request,
299 const std::function<void(const unsigned int, const AnswerType &)>
300 &process_answer,
301 const MPI_Comm comm) = 0;
302 };
303
304
318 template <typename RequestType, typename AnswerType>
319 class NBX : public Interface<RequestType, AnswerType>
320 {
321 public:
325 NBX() = default;
326
330 virtual ~NBX() = default;
331
333 std::vector<unsigned int>
338
342 virtual std::vector<unsigned int>
344 const std::vector<unsigned int> &targets,
345 const std::function<RequestType(const unsigned int)> &create_request,
346 const std::function<AnswerType(const unsigned int,
347 const RequestType &)> &answer_request,
348 const std::function<void(const unsigned int, const AnswerType &)>
349 &process_answer,
350 const MPI_Comm comm) override;
351
352 private:
353#ifdef DEAL_II_WITH_MPI
357 std::vector<std::vector<char>> send_buffers;
358
362 std::vector<MPI_Request> send_requests;
363
371 std::vector<std::unique_ptr<std::vector<char>>> request_buffers;
372
376 std::vector<std::unique_ptr<MPI_Request>> request_requests;
377
382
383 // request for barrier
384 MPI_Request barrier_request;
385#endif
386
390 std::set<unsigned int> requesting_processes;
391
397 bool
399 const std::function<void(const unsigned int, const AnswerType &)>
400 &process_answer,
401 const MPI_Comm comm);
402
407 void
409
415 bool
417
423 void
425 const std::function<AnswerType(const unsigned int,
426 const RequestType &)> &answer_request,
427 const MPI_Comm comm);
428
433 void
435 const std::vector<unsigned int> &targets,
436 const std::function<RequestType(const unsigned int)> &create_request,
437 const MPI_Comm comm);
438
443 void
445 };
446
447
492 template <typename RequestType, typename AnswerType>
493 std::vector<unsigned int>
494 nbx(const std::vector<unsigned int> &targets,
495 const std::function<RequestType(const unsigned int)> &create_request,
496 const std::function<AnswerType(const unsigned int,
497 const RequestType &)> &answer_request,
498 const std::function<void(const unsigned int, const AnswerType &)>
499 &process_answer,
500 const MPI_Comm comm);
501
539 template <typename RequestType>
540 std::vector<unsigned int>
541 nbx(const std::vector<unsigned int> &targets,
542 const std::function<RequestType(const unsigned int)> &create_request,
543 const std::function<void(const unsigned int, const RequestType &)>
544 &process_request,
545 const MPI_Comm comm);
546
572 template <typename RequestType, typename AnswerType>
573 class PEX : public Interface<RequestType, AnswerType>
574 {
575 public:
579 PEX() = default;
580
584 virtual ~PEX() = default;
585
587 std::vector<unsigned int>
592
596 virtual std::vector<unsigned int>
598 const std::vector<unsigned int> &targets,
599 const std::function<RequestType(const unsigned int)> &create_request,
600 const std::function<AnswerType(const unsigned int,
601 const RequestType &)> &answer_request,
602 const std::function<void(const unsigned int, const AnswerType &)>
603 &process_answer,
604 const MPI_Comm comm) override;
605
606 private:
607#ifdef DEAL_II_WITH_MPI
611 std::vector<std::vector<char>> send_buffers;
612
616 std::vector<std::vector<char>> recv_buffers;
617
621 std::vector<MPI_Request> send_request_requests;
622
626 std::vector<std::vector<char>> requests_buffers;
627
631 std::vector<MPI_Request> send_answer_requests;
632#endif
636 std::set<unsigned int> requesting_processes;
637
642 unsigned int
644 const std::vector<unsigned int> &targets,
645 const std::function<RequestType(const unsigned int)> &create_request,
646 const MPI_Comm comm);
647
652 void
654 const unsigned int index,
655 const std::function<AnswerType(const unsigned int,
656 const RequestType &)> &answer_request,
657 const MPI_Comm comm);
658
663 void
665 const unsigned int n_targets,
666 const std::function<void(const unsigned int, const AnswerType &)>
667 &process_answer,
668 const MPI_Comm comm);
669
674 void
676 };
677
678
679
736 template <typename RequestType, typename AnswerType>
737 std::vector<unsigned int>
738 pex(const std::vector<unsigned int> &targets,
739 const std::function<RequestType(const unsigned int)> &create_request,
740 const std::function<AnswerType(const unsigned int,
741 const RequestType &)> &answer_request,
742 const std::function<void(const unsigned int, const AnswerType &)>
743 &process_answer,
744 const MPI_Comm comm);
745
783 template <typename RequestType>
784 std::vector<unsigned int>
785 pex(const std::vector<unsigned int> &targets,
786 const std::function<RequestType(const unsigned int)> &create_request,
787 const std::function<void(const unsigned int, const RequestType &)>
788 &process_request,
789 const MPI_Comm comm);
790
791
796 template <typename RequestType, typename AnswerType>
797 class Serial : public Interface<RequestType, AnswerType>
798 {
799 public:
803 Serial() = default;
804
806 std::vector<unsigned int>
811
815 virtual std::vector<unsigned int>
817 const std::vector<unsigned int> &targets,
818 const std::function<RequestType(const unsigned int)> &create_request,
819 const std::function<AnswerType(const unsigned int,
820 const RequestType &)> &answer_request,
821 const std::function<void(const unsigned int, const AnswerType &)>
822 &process_answer,
823 const MPI_Comm comm) override;
824 };
825
826
827
860 template <typename RequestType, typename AnswerType>
861 std::vector<unsigned int>
863 const std::vector<unsigned int> &targets,
864 const std::function<RequestType(const unsigned int)> &create_request,
865 const std::function<AnswerType(const unsigned int, const RequestType &)>
866 &answer_request,
867 const std::function<void(const unsigned int, const AnswerType &)>
868 &process_answer,
869 const MPI_Comm comm);
870
900 template <typename RequestType>
901 std::vector<unsigned int>
903 const std::vector<unsigned int> &targets,
904 const std::function<RequestType(const unsigned int)> &create_request,
905 const std::function<void(const unsigned int, const RequestType &)>
906 &process_request,
907 const MPI_Comm comm);
908
909
910
923 template <typename RequestType, typename AnswerType>
924 class Selector : public Interface<RequestType, AnswerType>
925 {
926 public:
930 Selector() = default;
931
935 virtual ~Selector() = default;
936
938 std::vector<unsigned int>
943
949 virtual std::vector<unsigned int>
951 const std::vector<unsigned int> &targets,
952 const std::function<RequestType(const unsigned int)> &create_request,
953 const std::function<AnswerType(const unsigned int,
954 const RequestType &)> &answer_request,
955 const std::function<void(const unsigned int, const AnswerType &)>
956 &process_answer,
957 const MPI_Comm comm) override;
958
959 private:
960 // Pointer to the actual ConsensusAlgorithms::Interface implementation.
961 std::shared_ptr<Interface<RequestType, AnswerType>> consensus_algo;
962 };
963
964
965
1010 template <typename RequestType, typename AnswerType>
1011 std::vector<unsigned int>
1013 const std::vector<unsigned int> &targets,
1014 const std::function<RequestType(const unsigned int)> &create_request,
1015 const std::function<AnswerType(const unsigned int, const RequestType &)>
1016 &answer_request,
1017 const std::function<void(const unsigned int, const AnswerType &)>
1018 &process_answer,
1019 const MPI_Comm comm);
1020
1058 template <typename RequestType>
1059 std::vector<unsigned int>
1061 const std::vector<unsigned int> &targets,
1062 const std::function<RequestType(const unsigned int)> &create_request,
1063 const std::function<void(const unsigned int, const RequestType &)>
1064 &process_request,
1065 const MPI_Comm comm);
1066
1067
1068
1069#ifndef DOXYGEN
1070 // Implementation of the functions in this namespace.
1071
1072 template <typename RequestType, typename AnswerType>
1073 std::vector<unsigned int>
1074 nbx(const std::vector<unsigned int> &targets,
1075 const std::function<RequestType(const unsigned int)> &create_request,
1076 const std::function<AnswerType(const unsigned int,
1077 const RequestType &)> &answer_request,
1078 const std::function<void(const unsigned int, const AnswerType &)>
1079 &process_answer,
1080 const MPI_Comm comm)
1081 {
1083 targets, create_request, answer_request, process_answer, comm);
1084 }
1085
1086
1087
1088 template <typename RequestType>
1089 std::vector<unsigned int>
1090 nbx(const std::vector<unsigned int> &targets,
1091 const std::function<RequestType(const unsigned int)> &create_request,
1092 const std::function<void(const unsigned int, const RequestType &)>
1093 &process_request,
1094 const MPI_Comm comm)
1095 {
1096 // TODO: For the moment, simply implement this special case by
1097 // forwarding to the other function with rewritten function
1098 // objects and using an empty type as answer type. This way,
1099 // we have the interface in place and can provide a more
1100 // efficient implementation later on.
1101 using EmptyType = std::tuple<>;
1102
1104 targets,
1105 create_request,
1106 // answer_request:
1107 [&process_request](const unsigned int source_rank,
1108 const RequestType &request) -> EmptyType {
1109 process_request(source_rank, request);
1110 // Return something. What it is is arbitrary here, except that
1111 // we want it to be as small an object as possible. Using
1112 // std::tuple<> is interpreted as an empty object that is packed
1113 // down to a zero-length char array.
1114 return {};
1115 },
1116 // process_answer:
1117 [](const unsigned int /*target_rank */,
1118 const EmptyType & /*answer*/) {},
1119 comm);
1120 }
1121
1122
1123
1124 template <typename RequestType, typename AnswerType>
1125 std::vector<unsigned int>
1126 pex(const std::vector<unsigned int> &targets,
1127 const std::function<RequestType(const unsigned int)> &create_request,
1128 const std::function<AnswerType(const unsigned int,
1129 const RequestType &)> &answer_request,
1130 const std::function<void(const unsigned int, const AnswerType &)>
1131 &process_answer,
1132 const MPI_Comm comm)
1133 {
1135 targets, create_request, answer_request, process_answer, comm);
1136 }
1137
1138
1139
1140 template <typename RequestType>
1141 std::vector<unsigned int>
1142 pex(const std::vector<unsigned int> &targets,
1143 const std::function<RequestType(const unsigned int)> &create_request,
1144 const std::function<void(const unsigned int, const RequestType &)>
1145 &process_request,
1146 const MPI_Comm comm)
1147 {
1148 // TODO: For the moment, simply implement this special case by
1149 // forwarding to the other function with rewritten function
1150 // objects and using an empty type as answer type. This way,
1151 // we have the interface in place and can provide a more
1152 // efficient implementation later on.
1153 using EmptyType = std::tuple<>;
1154
1156 targets,
1157 create_request,
1158 // answer_request:
1159 [&process_request](const unsigned int source_rank,
1160 const RequestType &request) -> EmptyType {
1161 process_request(source_rank, request);
1162 // Return something. What it is is arbitrary here, except that
1163 // we want it to be as small an object as possible. Using
1164 // std::tuple<> is interpreted as an empty object that is packed
1165 // down to a zero-length char array.
1166 return {};
1167 },
1168 // process_answer:
1169 [](const unsigned int /*target_rank */,
1170 const EmptyType & /*answer*/) {},
1171 comm);
1172 }
1173
1174
1175
1176 template <typename RequestType, typename AnswerType>
1177 std::vector<unsigned int>
1178 serial(
1179 const std::vector<unsigned int> &targets,
1180 const std::function<RequestType(const unsigned int)> &create_request,
1181 const std::function<AnswerType(const unsigned int, const RequestType &)>
1182 &answer_request,
1183 const std::function<void(const unsigned int, const AnswerType &)>
1184 &process_answer,
1185 const MPI_Comm comm)
1186 {
1188 targets, create_request, answer_request, process_answer, comm);
1189 }
1190
1191
1192
1193 template <typename RequestType>
1194 std::vector<unsigned int>
1195 serial(
1196 const std::vector<unsigned int> &targets,
1197 const std::function<RequestType(const unsigned int)> &create_request,
1198 const std::function<void(const unsigned int, const RequestType &)>
1199 &process_request,
1200 const MPI_Comm comm)
1201 {
1202 // TODO: For the moment, simply implement this special case by
1203 // forwarding to the other function with rewritten function
1204 // objects and using an empty type as answer type. This way,
1205 // we have the interface in place and can provide a more
1206 // efficient implementation later on.
1207 using EmptyType = std::tuple<>;
1208
1210 targets,
1211 create_request,
1212 // answer_request:
1213 [&process_request](const unsigned int source_rank,
1214 const RequestType &request) -> EmptyType {
1215 process_request(source_rank, request);
1216 // Return something. What it is is arbitrary here, except that
1217 // we want it to be as small an object as possible. Using
1218 // std::tuple<> is interpreted as an empty object that is packed
1219 // down to a zero-length char array.
1220 return {};
1221 },
1222 // process_answer:
1223 [](const unsigned int /*target_rank */,
1224 const EmptyType & /*answer*/) {},
1225 comm);
1226 }
1227
1228
1229
1230 template <typename RequestType, typename AnswerType>
1231 std::vector<unsigned int>
1232 selector(
1233 const std::vector<unsigned int> &targets,
1234 const std::function<RequestType(const unsigned int)> &create_request,
1235 const std::function<AnswerType(const unsigned int, const RequestType &)>
1236 &answer_request,
1237 const std::function<void(const unsigned int, const AnswerType &)>
1238 &process_answer,
1239 const MPI_Comm comm)
1240 {
1242 targets, create_request, answer_request, process_answer, comm);
1243 }
1244
1245
1246
1247 template <typename RequestType>
1248 std::vector<unsigned int>
1249 selector(
1250 const std::vector<unsigned int> &targets,
1251 const std::function<RequestType(const unsigned int)> &create_request,
1252 const std::function<void(const unsigned int, const RequestType &)>
1253 &process_request,
1254 const MPI_Comm comm)
1255 {
1256 // TODO: For the moment, simply implement this special case by
1257 // forwarding to the other function with rewritten function
1258 // objects and using an empty type as answer type. This way,
1259 // we have the interface in place and can provide a more
1260 // efficient implementation later on.
1261 using EmptyType = std::tuple<>;
1262
1264 targets,
1265 create_request,
1266 // answer_request:
1267 [&process_request](const unsigned int source_rank,
1268 const RequestType &request) -> EmptyType {
1269 process_request(source_rank, request);
1270 // Return something. What it is is arbitrary here, except that
1271 // we want it to be as small an object as possible. Using
1272 // std::tuple<> is interpreted as an empty object that is packed
1273 // down to a zero-length char array.
1274 return {};
1275 },
1276 // process_answer:
1277 [](const unsigned int /*target_rank */,
1278 const EmptyType & /*answer*/) {},
1279 comm);
1280 }
1281
1282#endif
1283
1284
1285 } // namespace ConsensusAlgorithms
1286 } // end of namespace MPI
1287} // end of namespace Utilities
1288
1289
1290
1291#ifndef DOXYGEN
1292
1293// ----------------- Implementation of template functions
1294
1295namespace Utilities
1296{
1297 namespace MPI
1298 {
1299 namespace ConsensusAlgorithms
1300 {
1301 namespace internal
1302 {
1307 inline bool
1308 has_unique_elements(const std::vector<unsigned int> &targets)
1309 {
1310 std::vector<unsigned int> my_destinations = targets;
1311 std::sort(my_destinations.begin(), my_destinations.end());
1312 return (std::adjacent_find(my_destinations.begin(),
1313 my_destinations.end()) ==
1314 my_destinations.end());
1315 }
1316
1317
1318
1322 inline void
1323 handle_exception(std::exception_ptr &&exception, const MPI_Comm comm)
1324 {
1325# ifdef DEAL_II_WITH_MPI
1326 // an exception within a ConsensusAlgorithm likely causes an
1327 // MPI deadlock. Abort with a reasonable error message instead.
1328 try
1329 {
1330 std::rethrow_exception(exception);
1331 }
1332 catch (ExceptionBase &exc)
1333 {
1334 // report name of the deal.II exception:
1335 std::cerr
1336 << std::endl
1337 << std::endl
1338 << "----------------------------------------------------"
1339 << std::endl;
1340 std::cerr
1341 << "Exception '" << exc.get_exc_name() << "'"
1342 << " on rank " << Utilities::MPI::this_mpi_process(comm)
1343 << " on processing: " << std::endl
1344 << exc.what() << std::endl
1345 << "Aborting!" << std::endl
1346 << "----------------------------------------------------"
1347 << std::endl;
1348
1349 // Then bring down the whole MPI world
1350 MPI_Abort(comm, 255);
1351 }
1352 catch (std::exception &exc)
1353 {
1354 std::cerr
1355 << std::endl
1356 << std::endl
1357 << "----------------------------------------------------"
1358 << std::endl;
1359 std::cerr
1360 << "Exception within ConsensusAlgorithm"
1361 << " on rank " << Utilities::MPI::this_mpi_process(comm)
1362 << " on processing: " << std::endl
1363 << exc.what() << std::endl
1364 << "Aborting!" << std::endl
1365 << "----------------------------------------------------"
1366 << std::endl;
1367
1368 // Then bring down the whole MPI world
1369 MPI_Abort(comm, 255);
1370 }
1371 catch (...)
1372 {
1373 std::cerr
1374 << std::endl
1375 << std::endl
1376 << "----------------------------------------------------"
1377 << std::endl;
1378 std::cerr
1379 << "Unknown exception within ConsensusAlgorithm!" << std::endl
1380 << "Aborting!" << std::endl
1381 << "----------------------------------------------------"
1382 << std::endl;
1383
1384 // Then bring down the whole MPI world
1385 MPI_Abort(comm, 255);
1386 }
1387# else
1388 (void)comm;
1389
1390 // No need to be concerned about deadlocks without MPI.
1391 // Defer to exception handling further up the callstack.
1392 std::rethrow_exception(exception);
1393# endif
1394 }
1395 } // namespace internal
1396
1397
1398
1399 template <typename RequestType, typename AnswerType>
1400 void
1402 const RequestType &,
1403 AnswerType &)
1404 {
1405 // nothing to do
1406 }
1407
1408
1409
1410 template <typename RequestType, typename AnswerType>
1411 void
1413 RequestType &)
1414 {
1415 // nothing to do
1416 }
1417
1418
1419
1420 template <typename RequestType, typename AnswerType>
1421 void
1423 const AnswerType &)
1424 {
1425 // nothing to do
1426 }
1427
1428
1429
1430 template <typename RequestType, typename AnswerType>
1431 std::vector<unsigned int>
1434 const MPI_Comm comm)
1435 {
1436 // Unpack the 'process' object and call the function that takes
1437 // function objects for all operations.
1438 return run(
1439 process.compute_targets(),
1440 /* create_request: */
1441 [&process](const unsigned int target) {
1442 RequestType request;
1443 process.create_request(target, request);
1444 return request;
1445 },
1446 /* answer_request: */
1447 [&process](const unsigned int source, const RequestType &request) {
1448 AnswerType answer;
1449 process.answer_request(source, request, answer);
1450 return answer;
1451 },
1452 /* process_answer: */
1453 [&process](const unsigned int target, const AnswerType &answer) {
1454 process.read_answer(target, answer);
1455 },
1456 comm);
1457 }
1458
1459
1460
1461 template <typename RequestType, typename AnswerType>
1462 std::vector<unsigned int>
1464 const std::vector<unsigned int> &targets,
1465 const std::function<RequestType(const unsigned int)> &create_request,
1466 const std::function<AnswerType(const unsigned int, const RequestType &)>
1467 &answer_request,
1468 const std::function<void(const unsigned int, const AnswerType &)>
1469 &process_answer,
1470 const MPI_Comm comm)
1471 {
1472 Assert(internal::has_unique_elements(targets),
1473 ExcMessage("The consensus algorithms expect that each process "
1474 "only sends a single message to another process, "
1475 "but the targets provided include duplicates."));
1476
1477 static CollectiveMutex mutex;
1478 CollectiveMutex::ScopedLock lock(mutex, comm);
1479
1480 try
1481 {
1482 // 1) Send data to identified targets and start receiving
1483 // the answers from these very same processes.
1484 start_communication(targets, create_request, comm);
1485
1486 // 2) Until all posted receive operations are known to have
1487 // completed, answer requests and keep checking whether all
1488 // requests of this process have been answered.
1489 //
1490 // The requests that we catch in the answer_requests()
1491 // function originate elsewhere, that is, they are not in
1492 // response to our own messages
1493 //
1494 // Note also that we may not catch all incoming requests in
1495 // the following two lines: our own requests may have been
1496 // satisfied before we've dealt with all incoming requests.
1497 // That's ok: We will get around to dealing with all
1498 // remaining message later. We just want to move on to the
1499 // next step as early as possible.
1500 while (all_locally_originated_receives_are_completed(process_answer,
1501 comm) == false)
1502 maybe_answer_one_request(answer_request, comm);
1503
1504 // 3) Signal to all other processes that all requests of this
1505 // process have been answered
1506 signal_finish(comm);
1507
1508 // 4) Nevertheless, this process has to keep on answering
1509 // (potential) incoming requests until all processes have
1510 // received the answer to all requests
1511 while (all_remotely_originated_receives_are_completed() == false)
1512 maybe_answer_one_request(answer_request, comm);
1513
1514 // 5) process the answer to all requests
1515 clean_up_and_end_communication(comm);
1516 }
1517 catch (...)
1518 {
1519 internal::handle_exception(std::current_exception(), comm);
1520 }
1521
1522 return std::vector<unsigned int>(requesting_processes.begin(),
1523 requesting_processes.end());
1524 }
1525
1526
1527
1528 template <typename RequestType, typename AnswerType>
1529 void
1531 const std::vector<unsigned int> &targets,
1532 const std::function<RequestType(const unsigned int)> &create_request,
1533 const MPI_Comm comm)
1534 {
1535# ifdef DEAL_II_WITH_MPI
1536 // 1)
1537 const auto n_targets = targets.size();
1538
1539 const int tag_request = Utilities::MPI::internal::Tags::
1540 consensus_algorithm_nbx_answer_request;
1541
1542 // 2) allocate memory
1543 send_requests.resize(n_targets);
1544 send_buffers.resize(n_targets);
1545
1546 {
1547 // 4) send and receive
1548 for (unsigned int index = 0; index < n_targets; ++index)
1549 {
1550 const unsigned int rank = targets[index];
1552
1553 auto &send_buffer = send_buffers[index];
1554 send_buffer =
1555 (create_request ? Utilities::pack(create_request(rank), false) :
1556 std::vector<char>());
1557
1558 // Post a request to send data
1559 auto ierr = MPI_Isend(send_buffer.data(),
1560 send_buffer.size(),
1561 MPI_CHAR,
1562 rank,
1563 tag_request,
1564 comm,
1565 &send_requests[index]);
1566 AssertThrowMPI(ierr);
1567 }
1568
1569 // Also record that we expect an answer from each target we sent
1570 // a request to:
1571 n_outstanding_answers = n_targets;
1572 }
1573# else
1574 (void)targets;
1575 (void)create_request;
1576 (void)comm;
1577# endif
1578 }
1579
1580
1581
1582 template <typename RequestType, typename AnswerType>
1583 bool
1586 const std::function<void(const unsigned int, const AnswerType &)>
1587 &process_answer,
1588 const MPI_Comm comm)
1589 {
1590# ifdef DEAL_II_WITH_MPI
1591 // We know that all requests have come in when we have pending
1592 // messages from all targets with the right tag (some of which we may
1593 // have already taken care of below, after discovering their existence).
1594 // We can check for pending messages with MPI_IProbe, which returns
1595 // immediately with a return code that indicates whether
1596 // it has found a message from any process with a given
1597 // tag.
1598 if (n_outstanding_answers == 0)
1599 return true;
1600 else
1601 {
1602 const int tag_deliver = Utilities::MPI::internal::Tags::
1603 consensus_algorithm_nbx_process_deliver;
1604
1605 int request_is_pending;
1606 MPI_Status status;
1607 const auto ierr = MPI_Iprobe(
1608 MPI_ANY_SOURCE, tag_deliver, comm, &request_is_pending, &status);
1609 AssertThrowMPI(ierr);
1610
1611 // If there is no pending message with this tag,
1612 // then we are clearly not done receiving everything
1613 // yet -- so return false.
1614 if (request_is_pending == 0)
1615 return false;
1616 else
1617 {
1618 // OK, so we have gotten a reply to our request from
1619 // one rank. Let us process it.
1620 const auto target = status.MPI_SOURCE;
1621
1622 // Then query the size of the message, allocate enough memory,
1623 // receive the data, and process it.
1624 int message_size;
1625 {
1626 const int ierr =
1627 MPI_Get_count(&status, MPI_CHAR, &message_size);
1628 AssertThrowMPI(ierr);
1629 }
1630 std::vector<char> recv_buffer(message_size);
1631
1632 {
1633 const int tag_deliver = Utilities::MPI::internal::Tags::
1634 consensus_algorithm_nbx_process_deliver;
1635
1636 const int ierr = MPI_Recv(recv_buffer.data(),
1637 recv_buffer.size(),
1638 MPI_CHAR,
1639 target,
1640 tag_deliver,
1641 comm,
1642 MPI_STATUS_IGNORE);
1643 AssertThrowMPI(ierr);
1644 }
1645
1646 if (process_answer)
1647 process_answer(target,
1649 false));
1650
1651 // Finally, remove this rank from the list of outstanding
1652 // targets:
1653 --n_outstanding_answers;
1654
1655 // We could do another go-around from the top of this
1656 // else-branch to see whether there are actually other messages
1657 // that are currently pending. But that would mean spending
1658 // substantial time in receiving answers while we should also be
1659 // sending answers to requests we have received from other
1660 // places. So let it be enough for now. If there are outstanding
1661 // answers, we will get back to this function before long and
1662 // can take care of them then.
1663 return (n_outstanding_answers == 0);
1664 }
1665 }
1666
1667# else
1668 (void)process_answer;
1669 (void)comm;
1670
1671 return true;
1672# endif
1673 }
1674
1675
1676
1677 template <typename RequestType, typename AnswerType>
1678 void
1680 const std::function<AnswerType(const unsigned int, const RequestType &)>
1681 &answer_request,
1682 const MPI_Comm comm)
1683 {
1684# ifdef DEAL_II_WITH_MPI
1685
1686 const int tag_request = Utilities::MPI::internal::Tags::
1687 consensus_algorithm_nbx_answer_request;
1688 const int tag_deliver = Utilities::MPI::internal::Tags::
1689 consensus_algorithm_nbx_process_deliver;
1690
1691 // Check if there is a request pending. By selecting the
1692 // tag_request tag, these are other processes asking for
1693 // our own replies, not these other processes' replies
1694 // to our own requests.
1695 //
1696 // There may be multiple such pending messages. We
1697 // only answer one.
1698 MPI_Status status;
1699 int request_is_pending;
1700 const auto ierr = MPI_Iprobe(
1701 MPI_ANY_SOURCE, tag_request, comm, &request_is_pending, &status);
1702 AssertThrowMPI(ierr);
1703
1704 if (request_is_pending != 0)
1705 {
1706 // Get the rank of the requesting process and add it to the
1707 // list of requesting processes (which may contain duplicates).
1708 const auto other_rank = status.MPI_SOURCE;
1709
1710 Assert(requesting_processes.find(other_rank) ==
1711 requesting_processes.end(),
1712 ExcMessage("Process is requesting a second time!"));
1713 requesting_processes.insert(other_rank);
1714
1715 // get size of incoming message
1716 int number_amount;
1717 auto ierr = MPI_Get_count(&status, MPI_CHAR, &number_amount);
1718 AssertThrowMPI(ierr);
1719
1720 // allocate memory for incoming message
1721 std::vector<char> buffer_recv(number_amount);
1722 ierr = MPI_Recv(buffer_recv.data(),
1723 number_amount,
1724 MPI_CHAR,
1725 other_rank,
1726 tag_request,
1727 comm,
1728 MPI_STATUS_IGNORE);
1729 AssertThrowMPI(ierr);
1730
1731 // Allocate memory for an answer message to the current request,
1732 // and ask the 'process' object to produce an answer:
1733 request_buffers.emplace_back(std::make_unique<std::vector<char>>());
1734 auto &request_buffer = *request_buffers.back();
1735 if (answer_request)
1736 request_buffer =
1737 Utilities::pack(answer_request(other_rank,
1739 buffer_recv, false)),
1740 false);
1741
1742 // Then initiate sending the answer back to the requester.
1743 request_requests.emplace_back(std::make_unique<MPI_Request>());
1744 ierr = MPI_Isend(request_buffer.data(),
1745 request_buffer.size(),
1746 MPI_CHAR,
1747 other_rank,
1748 tag_deliver,
1749 comm,
1750 request_requests.back().get());
1751 AssertThrowMPI(ierr);
1752 }
1753# else
1754 (void)answer_request;
1755 (void)comm;
1756# endif
1757 }
1758
1759
1760
1761 template <typename RequestType, typename AnswerType>
1762 void
1764 {
1765# ifdef DEAL_II_WITH_MPI
1766 const auto ierr = MPI_Ibarrier(comm, &barrier_request);
1767 AssertThrowMPI(ierr);
1768# else
1769 (void)comm;
1770# endif
1771 }
1772
1773
1774
1775 template <typename RequestType, typename AnswerType>
1776 bool
1777 NBX<RequestType,
1778 AnswerType>::all_remotely_originated_receives_are_completed()
1779 {
1780# ifdef DEAL_II_WITH_MPI
1781 int all_ranks_reached_barrier;
1782 const auto ierr = MPI_Test(&barrier_request,
1783 &all_ranks_reached_barrier,
1784 MPI_STATUS_IGNORE);
1785 AssertThrowMPI(ierr);
1786 return all_ranks_reached_barrier != 0;
1787# else
1788 return true;
1789# endif
1790 }
1791
1792
1793
1794 template <typename RequestType, typename AnswerType>
1795 void
1797 const MPI_Comm comm)
1798 {
1799 (void)comm;
1800# ifdef DEAL_II_WITH_MPI
1801 // clean up
1802 {
1803 if (send_requests.size() > 0)
1804 {
1805 const int ierr = MPI_Waitall(send_requests.size(),
1806 send_requests.data(),
1807 MPI_STATUSES_IGNORE);
1808 AssertThrowMPI(ierr);
1809 }
1810
1811 int ierr = MPI_Wait(&barrier_request, MPI_STATUS_IGNORE);
1812 AssertThrowMPI(ierr);
1813
1814 for (auto &i : request_requests)
1815 {
1816 ierr = MPI_Wait(i.get(), MPI_STATUS_IGNORE);
1817 AssertThrowMPI(ierr);
1818 }
1819
1820 if constexpr (running_in_debug_mode())
1821 {
1822 // note: IBarrier seems to make problem during testing, this
1823 // additional Barrier seems to help
1824 ierr = MPI_Barrier(comm);
1825 AssertThrowMPI(ierr);
1826 }
1827 }
1828# endif
1829 }
1830
1831
1832
1833 template <typename RequestType, typename AnswerType>
1834 std::vector<unsigned int>
1836 const std::vector<unsigned int> &targets,
1837 const std::function<RequestType(const unsigned int)> &create_request,
1838 const std::function<AnswerType(const unsigned int, const RequestType &)>
1839 &answer_request,
1840 const std::function<void(const unsigned int, const AnswerType &)>
1841 &process_answer,
1842 const MPI_Comm comm)
1843 {
1844 Assert(internal::has_unique_elements(targets),
1845 ExcMessage("The consensus algorithms expect that each process "
1846 "only sends a single message to another process, "
1847 "but the targets provided include duplicates."));
1848
1849 static CollectiveMutex mutex;
1850 CollectiveMutex::ScopedLock lock(mutex, comm);
1851
1852 try
1853 {
1854 // 1) Send requests and start receiving the answers.
1855 // In particular, determine how many requests we should expect
1856 // on the current process.
1857 const unsigned int n_requests =
1858 start_communication(targets, create_request, comm);
1859
1860 // 2) Answer requests:
1861 for (unsigned int request = 0; request < n_requests; ++request)
1862 answer_one_request(request, answer_request, comm);
1863
1864 // 3) Process answers:
1865 process_incoming_answers(targets.size(), process_answer, comm);
1866
1867 // 4) Make sure all sends have successfully terminated:
1868 clean_up_and_end_communication();
1869 }
1870 catch (...)
1871 {
1872 internal::handle_exception(std::current_exception(), comm);
1873 }
1874
1875 return std::vector<unsigned int>(requesting_processes.begin(),
1876 requesting_processes.end());
1877 }
1878
1879
1880
1881 template <typename RequestType, typename AnswerType>
1882 unsigned int
1884 const std::vector<unsigned int> &targets,
1885 const std::function<RequestType(const unsigned int)> &create_request,
1886 const MPI_Comm comm)
1887 {
1888# ifdef DEAL_II_WITH_MPI
1889 const int tag_request = Utilities::MPI::internal::Tags::
1890 consensus_algorithm_pex_answer_request;
1891
1892 // 1) determine with which processes this process wants to communicate
1893 // with
1894 const unsigned int n_targets = targets.size();
1895
1896 // 2) determine who wants to communicate with this process
1897 const unsigned int n_sources =
1899
1900 // 2) allocate memory
1901 recv_buffers.resize(n_targets);
1902 send_buffers.resize(n_targets);
1903 send_request_requests.resize(n_targets);
1904
1905 send_answer_requests.resize(n_sources);
1906 requests_buffers.resize(n_sources);
1907
1908 // 4) send and receive
1909 for (unsigned int i = 0; i < n_targets; ++i)
1910 {
1911 const unsigned int rank = targets[i];
1913
1914 // pack data which should be sent
1915 auto &send_buffer = send_buffers[i];
1916 if (create_request)
1917 send_buffer = Utilities::pack(create_request(rank), false);
1918
1919 // start to send data
1920 auto ierr = MPI_Isend(send_buffer.data(),
1921 send_buffer.size(),
1922 MPI_CHAR,
1923 rank,
1924 tag_request,
1925 comm,
1926 &send_request_requests[i]);
1927 AssertThrowMPI(ierr);
1928 }
1929
1930 return n_sources;
1931# else
1932 (void)targets;
1933 (void)create_request;
1934 (void)comm;
1935 return 0;
1936# endif
1937 }
1938
1939
1940
1941 template <typename RequestType, typename AnswerType>
1942 void
1944 const unsigned int index,
1945 const std::function<AnswerType(const unsigned int, const RequestType &)>
1946 &answer_request,
1947 const MPI_Comm comm)
1948 {
1949# ifdef DEAL_II_WITH_MPI
1950 const int tag_request = Utilities::MPI::internal::Tags::
1951 consensus_algorithm_pex_answer_request;
1952 const int tag_deliver = Utilities::MPI::internal::Tags::
1953 consensus_algorithm_pex_process_deliver;
1954
1955 // Wait until we have a message ready for retrieval, though we don't
1956 // care which process it is from.
1957 MPI_Status status;
1958 int ierr = MPI_Probe(MPI_ANY_SOURCE, tag_request, comm, &status);
1959 AssertThrowMPI(ierr);
1960
1961 // Get rank of incoming message and verify that it makes sense
1962 const unsigned int other_rank = status.MPI_SOURCE;
1963
1964 Assert(requesting_processes.find(other_rank) ==
1965 requesting_processes.end(),
1966 ExcMessage(
1967 "A process is sending a request after a request from "
1968 "the same process has previously already been "
1969 "received. This algorithm does not expect this to happen."));
1970 requesting_processes.insert(other_rank);
1971
1972 // Actually get the incoming message:
1973 int number_amount;
1974 ierr = MPI_Get_count(&status, MPI_CHAR, &number_amount);
1975 AssertThrowMPI(ierr);
1976
1977 std::vector<char> buffer_recv(number_amount);
1978 ierr = MPI_Recv(buffer_recv.data(),
1979 number_amount,
1980 MPI_CHAR,
1981 other_rank,
1982 tag_request,
1983 comm,
1984 &status);
1985 AssertThrowMPI(ierr);
1986
1987 // Process request by asking the user-provided function for
1988 // the answer and post a send for it.
1989 auto &request_buffer = requests_buffers[index];
1990 request_buffer =
1991 (answer_request ?
1992 Utilities::pack(answer_request(other_rank,
1994 buffer_recv, false)),
1995 false) :
1996 std::vector<char>());
1997
1998 ierr = MPI_Isend(request_buffer.data(),
1999 request_buffer.size(),
2000 MPI_CHAR,
2001 other_rank,
2002 tag_deliver,
2003 comm,
2004 &send_answer_requests[index]);
2005 AssertThrowMPI(ierr);
2006# else
2007 (void)answer_request;
2008 (void)comm;
2009 (void)index;
2010# endif
2011 }
2012
2013
2014
2015 template <typename RequestType, typename AnswerType>
2016 void
2018 const unsigned int n_targets,
2019 const std::function<void(const unsigned int, const AnswerType &)>
2020 &process_answer,
2021 const MPI_Comm comm)
2022 {
2023# ifdef DEAL_II_WITH_MPI
2024 const int tag_deliver = Utilities::MPI::internal::Tags::
2025 consensus_algorithm_pex_process_deliver;
2026
2027 // We know how many targets we have sent requests to. These
2028 // targets will all eventually send us their responses, but
2029 // we need not process them in order -- rather, just see what
2030 // comes in and then look at message originators' ranks and
2031 // message sizes
2032 for (unsigned int i = 0; i < n_targets; ++i)
2033 {
2034 MPI_Status status;
2035 {
2036 const int ierr =
2037 MPI_Probe(MPI_ANY_SOURCE, tag_deliver, comm, &status);
2038 AssertThrowMPI(ierr);
2039 }
2040
2041 const auto other_rank = status.MPI_SOURCE;
2042 int message_size;
2043 {
2044 const int ierr = MPI_Get_count(&status, MPI_CHAR, &message_size);
2045 AssertThrowMPI(ierr);
2046 }
2047 std::vector<char> recv_buffer(message_size);
2048
2049 // Now actually receive the answer. Because the MPI_Probe
2050 // above blocks until we have a message, we know that the
2051 // following MPI_Recv call will immediately succeed.
2052 {
2053 const int ierr = MPI_Recv(recv_buffer.data(),
2054 recv_buffer.size(),
2055 MPI_CHAR,
2056 other_rank,
2057 tag_deliver,
2058 comm,
2059 MPI_STATUS_IGNORE);
2060 AssertThrowMPI(ierr);
2061 }
2062
2063 if (process_answer)
2064 process_answer(other_rank,
2065 Utilities::unpack<AnswerType>(recv_buffer, false));
2066 }
2067# else
2068 (void)n_targets;
2069 (void)process_answer;
2070 (void)comm;
2071# endif
2072 }
2073
2074
2075
2076 template <typename RequestType, typename AnswerType>
2077 void
2079 {
2080# ifdef DEAL_II_WITH_MPI
2081 // Finalize all MPI_Request objects for both the
2082 // send-request and receive-answer operations.
2083 if (send_request_requests.size() > 0)
2084 {
2085 const int ierr = MPI_Waitall(send_request_requests.size(),
2086 send_request_requests.data(),
2087 MPI_STATUSES_IGNORE);
2088 AssertThrowMPI(ierr);
2089 }
2090
2091 // Then also check the send-answer requests.
2092 if (send_answer_requests.size() > 0)
2093 {
2094 const int ierr = MPI_Waitall(send_answer_requests.size(),
2095 send_answer_requests.data(),
2096 MPI_STATUSES_IGNORE);
2097 AssertThrowMPI(ierr);
2098 }
2099# endif
2100 }
2101
2102
2103
2104 template <typename RequestType, typename AnswerType>
2105 std::vector<unsigned int>
2107 const std::vector<unsigned int> &targets,
2108 const std::function<RequestType(const unsigned int)> &create_request,
2109 const std::function<AnswerType(const unsigned int, const RequestType &)>
2110 &answer_request,
2111 const std::function<void(const unsigned int, const AnswerType &)>
2112 &process_answer,
2113 const MPI_Comm comm)
2114 {
2116 ExcMessage("You shouldn't use the 'Serial' class on "
2117 "communicators that have more than one process "
2118 "associated with it."));
2119
2120 // The only valid target for a serial program is itself.
2121 if (targets.size() != 0)
2122 {
2123 Assert(targets.size() == 1,
2124 ExcMessage(
2125 "On a single process, the only valid target "
2126 "is process zero (the process itself), which can only be "
2127 "listed once."));
2128 AssertDimension(targets[0], 0);
2129
2130 // Since the caller indicates that there is a target, and since we
2131 // know that it is the current process, let the process send
2132 // something to itself.
2133 const RequestType request =
2134 (create_request ? create_request(0) : RequestType());
2135 const AnswerType answer =
2136 (answer_request ? answer_request(0, request) : AnswerType());
2137
2138 if (process_answer)
2139 process_answer(0, answer);
2140 }
2141
2142 return targets; // nothing to do
2143 }
2144
2145
2146
2147 template <typename RequestType, typename AnswerType>
2148 std::vector<unsigned int>
2150 const std::vector<unsigned int> &targets,
2151 const std::function<RequestType(const unsigned int)> &create_request,
2152 const std::function<AnswerType(const unsigned int, const RequestType &)>
2153 &answer_request,
2154 const std::function<void(const unsigned int, const AnswerType &)>
2155 &process_answer,
2156 const MPI_Comm comm)
2157 {
2158 // Depending on the number of processes we switch between
2159 // implementations. We reduce the threshold for debug mode to be
2160 // able to test also the non-blocking implementation. This feature
2161 // is tested by:
2162 // tests/multigrid/transfer_matrix_free_06.with_mpi=true.with_p4est=true.with_trilinos=true.mpirun=10.output
2163
2164 const unsigned int n_procs = (Utilities::MPI::job_supports_mpi() ?
2166 1);
2167# ifdef DEAL_II_WITH_MPI
2168# ifdef DEBUG
2169 if (n_procs > 10)
2170# else
2171 if (n_procs > 99)
2172# endif
2173 consensus_algo.reset(new NBX<RequestType, AnswerType>());
2174 else
2175# endif
2176 if (n_procs > 1)
2177 consensus_algo.reset(new PEX<RequestType, AnswerType>());
2178 else
2179 consensus_algo.reset(new Serial<RequestType, AnswerType>());
2180
2181 return consensus_algo->run(
2182 targets, create_request, answer_request, process_answer, comm);
2183 }
2184
2185
2186 } // namespace ConsensusAlgorithms
2187 } // end of namespace MPI
2188} // end of namespace Utilities
2189
2190#endif // DOXYGEN
2191
2192
2194
2195#endif
const char * get_exc_name() const
virtual const char * what() const noexcept override
std::vector< unsigned int > run(Process< RequestType, AnswerType > &process, const MPI_Comm comm)
virtual std::vector< unsigned int > run(const std::vector< unsigned int > &targets, const std::function< RequestType(const unsigned int)> &create_request, const std::function< AnswerType(const unsigned int, const RequestType &)> &answer_request, const std::function< void(const unsigned int, const AnswerType &)> &process_answer, const MPI_Comm comm)=0
void start_communication(const std::vector< unsigned int > &targets, const std::function< RequestType(const unsigned int)> &create_request, const MPI_Comm comm)
void maybe_answer_one_request(const std::function< AnswerType(const unsigned int, const RequestType &)> &answer_request, const MPI_Comm comm)
std::vector< std::unique_ptr< std::vector< char > > > request_buffers
std::vector< unsigned int > run(Process< RequestType, AnswerType > &process, const MPI_Comm comm)
virtual std::vector< unsigned int > run(const std::vector< unsigned int > &targets, const std::function< RequestType(const unsigned int)> &create_request, const std::function< AnswerType(const unsigned int, const RequestType &)> &answer_request, const std::function< void(const unsigned int, const AnswerType &)> &process_answer, const MPI_Comm comm) override
void clean_up_and_end_communication(const MPI_Comm comm)
bool all_locally_originated_receives_are_completed(const std::function< void(const unsigned int, const AnswerType &)> &process_answer, const MPI_Comm comm)
std::vector< std::unique_ptr< MPI_Request > > request_requests
std::vector< std::vector< char > > send_buffers
void signal_finish(const MPI_Comm comm)
unsigned int start_communication(const std::vector< unsigned int > &targets, const std::function< RequestType(const unsigned int)> &create_request, const MPI_Comm comm)
std::vector< std::vector< char > > requests_buffers
virtual std::vector< unsigned int > run(const std::vector< unsigned int > &targets, const std::function< RequestType(const unsigned int)> &create_request, const std::function< AnswerType(const unsigned int, const RequestType &)> &answer_request, const std::function< void(const unsigned int, const AnswerType &)> &process_answer, const MPI_Comm comm) override
std::vector< std::vector< char > > send_buffers
void answer_one_request(const unsigned int index, const std::function< AnswerType(const unsigned int, const RequestType &)> &answer_request, const MPI_Comm comm)
std::vector< unsigned int > run(Process< RequestType, AnswerType > &process, const MPI_Comm comm)
std::vector< std::vector< char > > recv_buffers
void process_incoming_answers(const unsigned int n_targets, const std::function< void(const unsigned int, const AnswerType &)> &process_answer, const MPI_Comm comm)
virtual std::vector< unsigned int > compute_targets()=0
virtual void answer_request(const unsigned int other_rank, const RequestType &buffer_recv, AnswerType &request_buffer)
virtual void read_answer(const unsigned int other_rank, const AnswerType &recv_buffer)
virtual void create_request(const unsigned int other_rank, RequestType &send_buffer)
virtual std::vector< unsigned int > run(const std::vector< unsigned int > &targets, const std::function< RequestType(const unsigned int)> &create_request, const std::function< AnswerType(const unsigned int, const RequestType &)> &answer_request, const std::function< void(const unsigned int, const AnswerType &)> &process_answer, const MPI_Comm comm) override
std::vector< unsigned int > run(Process< RequestType, AnswerType > &process, const MPI_Comm comm)
std::shared_ptr< Interface< RequestType, AnswerType > > consensus_algo
std::vector< unsigned int > run(Process< RequestType, AnswerType > &process, const MPI_Comm comm)
virtual std::vector< unsigned int > run(const std::vector< unsigned int > &targets, const std::function< RequestType(const unsigned int)> &create_request, const std::function< AnswerType(const unsigned int, const RequestType &)> &answer_request, const std::function< void(const unsigned int, const AnswerType &)> &process_answer, const MPI_Comm comm) override
#define DEAL_II_DEPRECATED
Definition config.h:286
#define DEAL_II_NAMESPACE_OPEN
Definition config.h:38
#define DEAL_II_DISABLE_EXTRA_DIAGNOSTICS
Definition config.h:628
constexpr bool running_in_debug_mode()
Definition config.h:76
#define DEAL_II_NAMESPACE_CLOSE
Definition config.h:39
#define DEAL_II_ENABLE_EXTRA_DIAGNOSTICS
Definition config.h:672
#define Assert(cond, exc)
#define AssertDimension(dim1, dim2)
#define AssertThrowMPI(error_code)
#define AssertIndexRange(index, range)
static ::ExceptionBase & ExcMessage(std::string arg1)
*  *  if(update_pressure &update_flags) *  compute_pressure(constitutive_request
std::vector< unsigned int > selector(const std::vector< unsigned int > &targets, const std::function< RequestType(const unsigned int)> &create_request, const std::function< AnswerType(const unsigned int, const RequestType &)> &answer_request, const std::function< void(const unsigned int, const AnswerType &)> &process_answer, const MPI_Comm comm)
std::vector< unsigned int > serial(const std::vector< unsigned int > &targets, const std::function< RequestType(const unsigned int)> &create_request, const std::function< AnswerType(const unsigned int, const RequestType &)> &answer_request, const std::function< void(const unsigned int, const AnswerType &)> &process_answer, const MPI_Comm comm)
std::vector< unsigned int > nbx(const std::vector< unsigned int > &targets, const std::function< RequestType(const unsigned int)> &create_request, const std::function< AnswerType(const unsigned int, const RequestType &)> &answer_request, const std::function< void(const unsigned int, const AnswerType &)> &process_answer, const MPI_Comm comm)
std::vector< unsigned int > pex(const std::vector< unsigned int > &targets, const std::function< RequestType(const unsigned int)> &create_request, const std::function< AnswerType(const unsigned int, const RequestType &)> &answer_request, const std::function< void(const unsigned int, const AnswerType &)> &process_answer, const MPI_Comm comm)
unsigned int n_mpi_processes(const MPI_Comm mpi_communicator)
Definition mpi.cc:103
unsigned int this_mpi_process(const MPI_Comm mpi_communicator)
Definition mpi.cc:118
bool job_supports_mpi()
Definition mpi.cc:680
unsigned int compute_n_point_to_point_communications(const MPI_Comm mpi_comm, const std::vector< unsigned int > &destinations)
Definition mpi.cc:371
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
void run(const Iterator &begin, const std_cxx20::type_identity_t< Iterator > &end, Worker worker, Copier copier, const ScratchData &sample_scratch_data, const CopyData &sample_copy_data, const unsigned int queue_length, const unsigned int chunk_size)
*braid_SplitCommworld & comm