1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335
|
// Copyright (C) 2015-2024 Chris Richardson, Garth N. Wells and Igor Baratta
//
// This file is part of DOLFINx (https://www.fenicsproject.org)
//
// SPDX-License-Identifier: LGPL-3.0-or-later
#pragma once
#include "IndexMap.h"
#include "MPI.h"
#include <cstdint>
#include <dolfinx/graph/AdjacencyList.h>
#include <memory>
#include <span>
#include <tuple>
#include <utility>
#include <vector>
namespace dolfinx::common
{
// Forward declaration
class IndexMap;
/// Enum to control preservation of ghost index ordering in
/// sub-IndexMaps.
enum class IndexMapOrder : bool
{
preserve = true, ///< Preserve the ordering of ghost indices
any = false ///< Allow arbitrary ordering of ghost indices in sub-maps
};
/// @brief Given a sorted list of indices (local indexing, owned or
/// ghost) and an index map, this function returns the indices owned by
/// this process, including indices that might have been in the list of
/// indices on another processes.
///
/// @param[in] indices List of indices.
/// @param[in] map The index map.
/// @return Indices owned by the calling process.
std::vector<int32_t>
compute_owned_indices(std::span<const std::int32_t> indices,
const IndexMap& map);
/// @brief Compute layout data and ghost indices for a stacked
/// (concatenated) index map, i.e. 'splice' multiple maps into one.
///
/// The input maps are concatenated, with indices in `maps` and owned by
/// the caller remaining owned by the caller. Ghost data is stored at
/// the end of the local range as normal, with the ghosts in blocks in
/// the order of the index maps in `maps`.
///
/// @note Index maps with a block size are unrolled in the data for the
/// concatenated index map.
/// @note Communication is required to compute the new ghost indices.
///
/// @param[in] maps List of (index map, block size) pairs
/// @returns The (0) global offset of a concatenated map for the calling
/// rank, (1) local offset for the owned indices of each submap in the
/// concatenated map, (2) new indices for the ghosts for each submap,
/// and (3) owner rank of each ghost entry for each submap.
std::tuple<std::int64_t, std::vector<std::int32_t>,
std::vector<std::vector<std::int64_t>>,
std::vector<std::vector<int>>>
stack_index_maps(
const std::vector<std::pair<std::reference_wrapper<const IndexMap>, int>>&
maps);
/// @brief Create a new index map from a subset of indices in an
/// existing index map.
///
/// @param[in] imap Parent map to create a new sub-map from.
/// @param[in] indices Local indices in `imap` (owned and ghost) to
/// include in the new index map.
/// @param[in] order Control the order in which ghost indices appear in
/// the new map.
/// @param[in] allow_owner_change If `true`, indices that are not
/// included in `indices` by their owning process can be included in
/// `indices` by processes that ghost the indices to be included in the
/// new submap. These indices will be owned by one of the sharing
/// processes in the submap. If `false`, and exception is raised if an
/// index is included by a sharing process and not by the owning
/// process.
/// @return The (i) new index map and (ii) a map from local indices in
/// the submap to local indices in the original (this) map.
/// @pre `indices` must be sorted and must not contain duplicates.
std::pair<IndexMap, std::vector<std::int32_t>> create_sub_index_map(
const IndexMap& imap, std::span<const std::int32_t> indices,
IndexMapOrder order = IndexMapOrder::any, bool allow_owner_change = false);
/// This class represents the distribution index arrays across
/// processes. An index array is a contiguous collection of `N+1`
/// indices `[0, 1, . . ., N]` that are distributed across `M`
/// processes. On a given process, the IndexMap stores a portion of the
/// index set using local indices `[0, 1, . . . , n]`, and a map from
/// the local indices to a unique global index.
class IndexMap
{
public:
/// @brief Create an non-overlapping index map.
///
/// @note Collective
///
/// @param[in] comm MPI communicator that the index map is distributed
/// across.
/// @param[in] local_size Local size of the index map, i.e. the number
/// of owned entries.
IndexMap(MPI_Comm comm, std::int32_t local_size);
/// @brief Create an overlapping (ghosted) index map.
///
/// This constructor uses a 'consensus' algorithm to determine the
/// ranks that ghost indices that are owned by the caller. This
/// requires non-trivial MPI communication. If the ranks that ghost
/// indices owned by the caller are known, it more efficient to use
/// the constructor that takes these ranks as an argument.
///
/// @note Collective
///
/// @param[in] comm MPI communicator that the index map is distributed
/// across.
/// @param[in] local_size Local size of the index map, i.e. the number
/// of owned entries
/// @param[in] ghosts The global indices of ghost entries
/// @param[in] owners Owner rank (on `comm`) of each entry in `ghosts`
/// @param[in] tag Tag used in non-blocking MPI calls in the consensus
/// algorithm.
/// @note A tag can sometimes be required when there are a series of
/// calls to this constructor, or other functions that call the
/// consensus algorithm, that are close together. In cases where this
/// constructor is called a second time on rank and another rank has
/// not completed its first consensus algorithm call, communications
/// can be corrupted if each collective call of this constructor does
/// not have its own `tag` value. Each collective call to this
/// constructor must use the same `tag` value. An alternative to
/// passing a tag is to have an implicit or explicit MPI barrier
/// before and after the call to this constructor.
IndexMap(MPI_Comm comm, std::int32_t local_size,
std::span<const std::int64_t> ghosts, std::span<const int> owners,
int tag = static_cast<int>(dolfinx::MPI::tag::consensus_nbx));
/// @brief Create an overlapping (ghosted) index map.
///
/// This constructor is optimised for the case where the 'source'
/// (ranks that own indices ghosted by the caller) and 'destination'
/// ranks (ranks that ghost indices owned by the caller) are already
/// available. It allows the complex computation of the destination
/// ranks from `owners`.
///
/// @note Collective
///
/// @param[in] comm MPI communicator that the index map is distributed
/// across.
/// @param[in] local_size Local size of the index map, i.e. the number
/// @param[in] src_dest Lists of [0] src and [1] dest ranks. The list
/// in each must be sorted and not contain duplicates. `src` ranks are
/// owners of the indices in `ghosts`. `dest` ranks are the rank that
/// ghost indices owned by the caller.
/// @param[in] ghosts The global indices of ghost entries
/// @param[in] owners Owner rank (on `comm`) of each entry in `ghosts`
IndexMap(MPI_Comm comm, std::int32_t local_size,
const std::array<std::vector<int>, 2>& src_dest,
std::span<const std::int64_t> ghosts, std::span<const int> owners);
// Copy constructor
IndexMap(const IndexMap& map) = delete;
/// Move constructor
IndexMap(IndexMap&& map) = default;
/// Destructor
~IndexMap() = default;
/// Move assignment
IndexMap& operator=(IndexMap&& map) = default;
// Copy assignment
IndexMap& operator=(const IndexMap& map) = delete;
/// Range of indices (global) owned by this process
std::array<std::int64_t, 2> local_range() const noexcept;
/// Number of ghost indices on this process
std::int32_t num_ghosts() const noexcept;
/// Number of indices owned by this process
std::int32_t size_local() const noexcept;
/// Number indices across communicator
std::int64_t size_global() const noexcept;
/// Local-to-global map for ghosts (local indexing beyond end of local
/// range)
std::span<const std::int64_t> ghosts() const noexcept;
/// @brief Return the MPI communicator that the map is defined on.
/// @return Communicator
MPI_Comm comm() const;
/// @brief Compute global indices for array of local indices.
/// @param[in] local Local indices
/// @param[out] global The global indices
void local_to_global(std::span<const std::int32_t> local,
std::span<std::int64_t> global) const;
/// @brief Compute local indices for array of global indices.
/// @param[in] global Global indices
/// @param[out] local The local of the corresponding global index in
/// 'global'. Returns -1 if the local index does not exist on this
/// process.
void global_to_local(std::span<const std::int64_t> global,
std::span<std::int32_t> local) const;
/// @brief Build list of indices with global indexing.
/// @return The global index for all local indices `(0, 1, 2, ...)` on
/// this process, including ghosts
std::vector<std::int64_t> global_indices() const;
/// @brief The ranks that own each ghost index.
/// @return List of ghost owners. The owning rank of the ith ghost
/// index is `owners()[i]`.
std::span<const int> owners() const { return _owners; }
/// @todo Aim to remove this function?
///
/// @brief Compute map from each local (owned) index to the set of
/// ranks that have the index as a ghost.
///
/// @note Collective
///
/// @param[in] tag Tag to pass to MPI calls.
/// @note See ::IndexMap(MPI_Comm,std::int32_t,std::span<const
/// std::int64_t>,std::span<const int>,int) for an explanation of when
/// `tag` is required.
/// @return Shared indices.
graph::AdjacencyList<int> index_to_dest_ranks(
int tag = static_cast<int>(dolfinx::MPI::tag::consensus_nbx)) const;
/// @brief Build a list of owned indices that are ghosted by another
/// rank.
/// @return The local index of owned indices that are ghosts on other
/// rank(s). The indices are unique and sorted.
std::vector<std::int32_t> shared_indices() const;
/// @brief Ordered set of MPI ranks that own caller's ghost indices.
///
/// Typically used when creating neighbourhood communicators.
///
/// @return MPI ranks than own ghost indices. The ranks are unique
/// and sorted.
std::span<const int> src() const noexcept;
/// @brief Ordered set of MPI ranks that ghost indices owned by
/// caller.
///
/// Typically used when creating neighbourhood communicators.
///
/// @return MPI ranks than own ghost indices. The ranks are unique
/// and sorted.
std::span<const int> dest() const noexcept;
/// @brief Compute the number of ghost indices owned by each rank in
/// IndexMap::src.
///
/// This is a measure of the amount of data:
///
/// 1. Sent from this rank to other ranks when performing a reverse
/// (owner <- ghost) scatter.
///
/// 2. Received by this rank from other ranks when performing a
/// forward (owner -> ghost) scatter.
///
/// @return A weight vector, where `weight[i]` the the number of
/// ghost indices owned by rank IndexMap::src()`[i]`.
std::vector<std::int32_t> weights_src() const;
/// @brief Compute the number of ghost indices owned by each rank in
/// IndexMap::dest.
///
/// This is a measure of the amount of data:
///
/// 1. Sent from this rank to other ranks when performing a forward
/// (owner -> ghost) scatter.
///
/// 2. Received by this rank from other ranks when performing a
/// reverse forward (owner <- ghost) scatter.
///
/// @return A weight vector, where `weight[i]` the the number of ghost
/// indices owned by rank IndexMap::dest()`[i]`.
std::vector<std::int32_t> weights_dest() const;
/// @brief Destination and source ranks by type, e.g, ranks that are
/// destination/source ranks for the caller and are in a common
/// shared memory region.
///
/// This function is used to group destination and source ranks by
/// 'type'. The type is defined by the MPI `split_type`. Split types
/// include ranks from a common shared memory region
/// (`MPI_COMM_TYPE_SHARED`) or a common NUMA region. Splits types are
/// listed at
/// https://docs.open-mpi.org/en/main/man-openmpi/man3/MPI_Comm_split_type.3.html#split-types.
///
/// @note Collective operation on comm().
///
/// @param[in] split_type MPI split type, as used in the function
/// `MPI_Comm_split_type`. See
/// https://docs.open-mpi.org/en/main/man-openmpi/man3/MPI_Comm_split_type.3.html#split-types.
/// @return (0) Intersection of ranks in `split_type` and in dest(),
/// and (1) intersection of ranks in `split_type` and in src().
/// Returned ranks are on the comm() communicator.
std::array<std::vector<int>, 2> rank_type(int split_type) const;
private:
// Range of indices (global) owned by this process
std::array<std::int64_t, 2> _local_range;
// Number indices across communicator
std::int64_t _size_global;
// MPI communicator that map is defined on
dolfinx::MPI::Comm _comm;
// Local-to-global map for ghost indices
std::vector<std::int64_t> _ghosts;
// Owning rank on _comm for the ith ghost index
std::vector<int> _owners;
// Set of ranks that own ghosts
std::vector<int> _src;
// Set of ranks ghost owned indices
std::vector<int> _dest;
};
} // namespace dolfinx::common
|