703 class FlexibleIndexStorage
706 using index_type =
unsigned int;
707 static const index_type invalid_index_value =
710 FlexibleIndexStorage(
const bool use_vector =
true);
713 reinit(
const bool use_vector,
714 const bool index_range_contiguous,
715 const std::size_t size);
718 fill(
const std::size_t start,
719 const std::size_t
end,
720 const index_type &
value);
723 operator[](
const std::size_t index);
726 operator[](
const std::size_t index)
const;
728 [[maybe_unused]]
bool
729 entry_has_been_set(
const std::size_t index)
const;
734 std::vector<index_type> data;
735 std::map<std::size_t, index_type> data_map;
768 static constexpr unsigned int range_minimum_grain_size = 64;
776 static constexpr unsigned int sparsity_factor = 4;
792 FlexibleIndexStorage actually_owning_ranks;
798 std::vector<unsigned int> actually_owning_rank_list;
813 std::pair<types::global_dof_index, types::global_dof_index>
832 unsigned int n_dict_procs_in_owned_indices;
839 unsigned int stride_small_size;
854 get_index_offset(
const unsigned int rank);
862 get_owning_rank_index(
const unsigned int rank_in_owned_indices,
863 const unsigned int guess = 0);
882 class ConsensusAlgorithmsPayload
885 using RequestType = std::vector<
886 std::pair<types::global_dof_index, types::global_dof_index>>;
887 using AnswerType = std::vector<unsigned int>;
892 ConsensusAlgorithmsPayload(
const IndexSet &owned_indices,
895 std::vector<unsigned int> &owning_ranks,
896 const bool track_index_requesters =
false);
917 const unsigned int my_rank;
923 const unsigned int n_procs;
931 const bool track_index_requesters;
938 std::vector<unsigned int> &owning_ranks;
954 std::vector<std::vector<
955 std::pair<
unsigned int,
965 std::map<
unsigned int,
966 std::pair<std::vector<types::global_dof_index>,
967 std::vector<unsigned int>>>
968 indices_to_look_up_by_dict_rank;
973 std::vector<unsigned int>
980 std::pair<types::global_dof_index, types::global_dof_index>>
981 create_request(
const unsigned int other_rank);
986 std::vector<unsigned int>
988 const unsigned int other_rank,
997 process_answer(
const unsigned int other_rank,
998 const std::vector<unsigned int> &recv_buffer);
1015 std::map<unsigned int, IndexSet>
1033 append_index_origin(
1035 const unsigned int rank_of_request,
1036 const unsigned int rank_of_owner,
1037 unsigned int &owner_index_guess);
1047 return (i / dofs_per_process) * stride_small_size;
1052 Dictionary::get_index_offset(
const unsigned int rank)
1056 (rank + stride_small_size - 1) /
1064 Dictionary::get_owning_rank_index(
1065 const unsigned int rank_in_owned_indices,
1066 const unsigned int guess)
1069 if (actually_owning_rank_list[guess] == rank_in_owned_indices)
1073 auto it = std::lower_bound(actually_owning_rank_list.begin(),
1074 actually_owning_rank_list.end(),
1075 rank_in_owned_indices);
1078 return it - actually_owning_rank_list.begin();
1083 const FlexibleIndexStorage::index_type
1084 FlexibleIndexStorage::invalid_index_value;
1088 FlexibleIndexStorage::FlexibleIndexStorage(
const bool use_vector)
1089 : use_vector(use_vector)
1096 FlexibleIndexStorage::reinit(
const bool use_vector,
1097 const bool index_range_contiguous,
1098 const std::size_t size)
1100 this->use_vector = use_vector;
1108 if (use_vector && !index_range_contiguous)
1109 data.resize(size, invalid_index_value);
1115 FlexibleIndexStorage::fill(
1116 const std::size_t start,
1117 const std::size_t
end,
1118 const FlexibleIndexStorage::index_type &
value)
1125 if (data.empty() &&
end > start)
1136 data.resize(size, invalid_index_value);
1137 std::fill(data.begin() + start,
1143 data.resize(size,
value);
1149 std::fill(data.begin() + start, data.begin() +
end,
value);
1154 for (
auto i = start; i <
end; ++i)
1155 data_map[i] =
value;
1161 FlexibleIndexStorage::index_type &
1162 FlexibleIndexStorage::operator[](
const std::size_t index)
1173 return data_map.try_emplace(index, invalid_index_value)
1180 [[maybe_unused]]
bool
1181 FlexibleIndexStorage::entry_has_been_set(
const std::size_t index)
const
1191 return data[index] != invalid_index_value;
1194 return data_map.find(index) != data_map.end();
1199 Dictionary::Dictionary(
const IndexSet &owned_indices,
1203 this->partition(owned_indices,
comm);
1208 std::map<
unsigned int,
1213 const auto owned_indices_size_actual =
1216 actually_owning_ranks.reinit((owned_indices_size_actual *
1217 sparsity_factor) > owned_indices.
size(),
1218 owned_indices_size_actual ==
1219 owned_indices.
size(),
1220 locally_owned_size);
1231 std::pair<types::global_dof_index, types::global_dof_index>
1232 index_range(*interval->begin(), interval->last() + 1);
1236 while (index_range.first != index_range.second)
1238 Assert(index_range.first < index_range.second,
1241 const unsigned int owner =
1242 dof_to_dict_rank(index_range.first);
1247 std::min(get_index_offset(owner + 1), index_range.second);
1263 if (owner == my_rank)
1265 actually_owning_ranks.fill(index_range.first -
1267 next_index - local_range.first,
1269 dic_local_received += next_index - index_range.first;
1270 if (actually_owning_rank_list.empty())
1271 actually_owning_rank_list.push_back(my_rank);
1274 buffers[owner].emplace_back(index_range.first, next_index);
1276 index_range.first = next_index;
1280#ifdef DEAL_II_WITH_MPI
1281 n_dict_procs_in_owned_indices = buffers.size();
1282 std::vector<MPI_Request> request;
1285 if (owned_indices_size_actual == owned_indices.
size())
1293 request.reserve(n_dict_procs_in_owned_indices);
1304 for (
const auto &rank_pair : buffers)
1306 request.push_back(MPI_Request());
1308 MPI_Isend(rank_pair.second.data(),
1309 rank_pair.second.size() * 2,
1320 while (this->locally_owned_size != dic_local_received)
1324 int ierr = MPI_Probe(MPI_ANY_SOURCE, mpi_tag,
comm, &status);
1329 ierr = MPI_Get_count(&status,
1335 const auto other_rank = status.MPI_SOURCE;
1336 actually_owning_rank_list.push_back(other_rank);
1341 std::pair<types::global_dof_index, types::global_dof_index>>
1342 buffer(number_amount / 2);
1343 ierr = MPI_Recv(buffer.data(),
1353 for (
auto interval : buffer)
1359 i < interval.second;
1361 Assert(actually_owning_ranks.entry_has_been_set(
1362 i - local_range.first) ==
false,
1364 Assert(interval.first >= local_range.first &&
1365 interval.first < local_range.second,
1367 Assert(interval.second > local_range.first &&
1368 interval.second <= local_range.second,
1372 actually_owning_ranks.fill(interval.first -
1377 dic_local_received += interval.second - interval.first;
1389 using RequestType = std::vector<
1390 std::pair<types::global_dof_index, types::global_dof_index>>;
1392 std::vector<unsigned int> targets;
1393 targets.reserve(buffers.size());
1394 for (
const auto &rank_pair : buffers)
1395 targets.emplace_back(rank_pair.first);
1401 [&buffers](
const unsigned int target_rank) -> RequestType {
1402 return buffers.at(target_rank);
1406 [&](
const unsigned int source_rank,
1407 const RequestType &request) ->
void {
1409 for (
auto interval : request)
1415 i < interval.second;
1418 actually_owning_ranks.entry_has_been_set(
1419 i - local_range.first) ==
false,
1421 "Multiple processes seem to own the same global index. "
1422 "A possible reason is that the sets of locally owned "
1423 "indices are not distinct."));
1424 Assert(interval.first < interval.second,
1427 local_range.first <= interval.first &&
1428 interval.second <= local_range.second,
1430 "The specified interval is not handled by the current process."));
1432 actually_owning_ranks.fill(interval.first -
1438 actually_owning_rank_list.push_back(source_rank);
1444 std::sort(actually_owning_rank_list.begin(),
1445 actually_owning_rank_list.end());
1447 for (
unsigned int i = 1; i < actually_owning_rank_list.size(); ++i)
1448 Assert(actually_owning_rank_list[i] >
1449 actually_owning_rank_list[i - 1],
1453 if (request.size() > 0)
1455 const int ierr = MPI_Waitall(request.size(),
1457 MPI_STATUSES_IGNORE);
1464 (void)dic_local_received;
1471 Dictionary::partition(
const IndexSet &owned_indices,
1477 size = owned_indices.
size();
1483 range_minimum_grain_size);
1488 local_range.first = get_index_offset(my_rank);
1489 local_range.second = get_index_offset(my_rank + 1);
1491 locally_owned_size = local_range.second - local_range.first;
1495 ConsensusAlgorithmsPayload::ConsensusAlgorithmsPayload(
1497 const IndexSet &indices_to_look_up,
1499 std::vector<unsigned int> &owning_ranks,
1500 const bool track_index_requesters)
1501 : owned_indices(owned_indices)
1502 , indices_to_look_up(indices_to_look_up)
1506 , track_index_requesters(track_index_requesters)
1507 , owning_ranks(owning_ranks)
1508 , dict(owned_indices,
comm)
1509 , requesters(dict.actually_owning_rank_list.size())
1514 std::vector<unsigned int>
1515 ConsensusAlgorithmsPayload::compute_targets()
1517 std::vector<unsigned int> targets;
1519 indices_to_look_up_by_dict_rank.clear();
1520 unsigned int index = 0;
1521 unsigned int owner_index_guess = 0;
1522 for (
auto i : indices_to_look_up)
1524 unsigned int other_rank = dict.dof_to_dict_rank(i);
1525 if (other_rank == my_rank)
1527 owning_ranks[index] =
1528 dict.actually_owning_ranks[i - dict.local_range.first];
1529 if (track_index_requesters)
1530 append_index_origin(i - dict.local_range.first,
1532 owning_ranks[index],
1537 if (targets.empty() || targets.back() != other_rank)
1538 targets.push_back(other_rank);
1539 auto &indices = indices_to_look_up_by_dict_rank[other_rank];
1540 indices.first.push_back(i);
1541 indices.second.push_back(index);
1546 Assert(targets.size() == indices_to_look_up_by_dict_rank.size(),
1554 std::vector<std::pair<types::global_dof_index, types::global_dof_index>>
1555 ConsensusAlgorithmsPayload::create_request(
1556 const unsigned int other_rank)
1559 std::pair<types::global_dof_index, types::global_dof_index>>
1563 auto &indices_i = indices_to_look_up_by_dict_rank[other_rank].first;
1565 is.
add_indices(indices_i.begin(), indices_i.end());
1571 send_buffer.emplace_back(*interval->begin(), interval->last() + 1);
1578 std::vector<unsigned int>
1579 ConsensusAlgorithmsPayload::answer_request(
1580 const unsigned int other_rank,
1584 std::vector<unsigned int> request_buffer;
1586 unsigned int owner_index_guess = 0;
1587 for (
const auto &interval : buffer_recv)
1588 for (
auto i = interval.first; i < interval.second; ++i)
1590 const unsigned int actual_owner =
1591 dict.actually_owning_ranks[i - dict.local_range.first];
1592 request_buffer.push_back(actual_owner);
1594 if (track_index_requesters)
1595 append_index_origin(i - dict.local_range.first,
1601 return request_buffer;
1607 ConsensusAlgorithmsPayload::process_answer(
1608 const unsigned int other_rank,
1609 const std::vector<unsigned int> &recv_buffer)
1611 const auto &recv_indices =
1612 indices_to_look_up_by_dict_rank[other_rank].second;
1614 for (
unsigned int j = 0; j < recv_indices.size(); ++j)
1615 owning_ranks[recv_indices[j]] = recv_buffer[j];
1620 std::map<unsigned int, IndexSet>
1621 ConsensusAlgorithmsPayload::get_requesters()
1623 Assert(track_index_requesters,
1624 ExcMessage(
"Must enable index range tracking in "
1625 "constructor of ConsensusAlgorithmProcess"));
1627 std::map<unsigned int, ::IndexSet> requested_indices;
1629#ifdef DEAL_II_WITH_MPI
1634 const int mpi_tag = Utilities::MPI::internal::Tags::
1635 consensus_algorithm_payload_get_requesters;
1641 std::vector<MPI_Request> send_requests;
1642 send_requests.reserve(requesters.size());
1652 std::vector<std::vector<types::global_dof_index>> send_data(
1654 for (
unsigned int i = 0; i < requesters.size(); ++i)
1657 if (dict.actually_owning_rank_list[i] == my_rank)
1659 for (
const auto &j : requesters[i])
1662 dict.get_index_offset(my_rank);
1663 IndexSet &my_index_set = requested_indices[j.first];
1665 for (
const auto &interval : j.second)
1666 my_index_set.
add_range(index_offset + interval.first,
1667 index_offset + interval.second);
1672 for (
const auto &j : requesters[i])
1674 send_data[i].push_back(j.first);
1675 send_data[i].push_back(j.second.size());
1676 for (
const auto &interval : j.second)
1678 send_data[i].push_back(interval.first);
1679 send_data[i].push_back(interval.second);
1682 send_requests.push_back(MPI_Request());
1684 MPI_Isend(send_data[i].data(),
1685 send_data[i].size(),
1688 dict.actually_owning_rank_list[i],
1691 &send_requests.back());
1697 for (
unsigned int c = 0; c < dict.n_dict_procs_in_owned_indices; ++c)
1701 int ierr = MPI_Probe(MPI_ANY_SOURCE, mpi_tag,
comm, &status);
1706 ierr = MPI_Get_count(
1715 std::pair<types::global_dof_index, types::global_dof_index>>
1716 buffer(number_amount / 2);
1730 dict.get_index_offset(status.MPI_SOURCE);
1731 unsigned int offset = 0;
1732 while (offset < buffer.size())
1738 for (
unsigned int i = offset + 1;
1739 i < offset + buffer[offset].second + 1;
1741 my_index_set.
add_range(index_offset + buffer[i].first,
1742 index_offset + buffer[i].second);
1747 IndexSet &index_set = requested_indices[buffer[offset].first];
1748 if (index_set.
size() == 0)
1752 offset += buffer[offset].second + 1;
1757 if (send_requests.size() > 0)
1759 const auto ierr = MPI_Waitall(send_requests.size(),
1760 send_requests.data(),
1761 MPI_STATUSES_IGNORE);
1768 for (
const auto &it : requested_indices)
1774 "The indices requested from the current "
1775 "MPI rank should be locally owned here!"));
1781 return requested_indices;
1787 ConsensusAlgorithmsPayload::append_index_origin(
1789 const unsigned int rank_of_request,
1790 const unsigned int rank_of_owner,
1791 unsigned int &owner_index_guess)
1798 dict.get_owning_rank_index(rank_of_owner, owner_index_guess);
1800 auto &request = requesters[owner_index_guess];
1801 if (request.empty() || request.back().first != rank_of_request)
1802 request.emplace_back(
1805 std::pair<types::global_dof_index, types::global_dof_index>>());
1807 auto &intervals = request.back().second;
1808 if (intervals.empty() || intervals.back().second != index_within_dict)
1809 intervals.emplace_back(index_within_dict, index_within_dict + 1);
1811 ++intervals.back().second;