Skip to content

Commit 5470ff4

Browse files
garth-wellsschnellerhase
authored andcommitted
Improve Topology construction and fix MPI/NBX bug (FEniCS#3512)
* Work on Topology constructor * Fix compilation * Update * Fixes * Sub-topology fix * Wrapper update * Doc fixes * Continue on error * Updates * Doc fix * Debug * Debug * Doc fix * Update demo * Add barrier * Use PCX * Increase problem size * Parameterise demo * Small edits * Pass tag to nbx function * Test update * Tidy up * Test update * Simplify * Simplification * Tidy * Simplifications * Undo changes * Simplify * Tidy * Wrapper simplification * Update doc * Remove stray line
1 parent e44d3e8 commit 5470ff4

11 files changed

Lines changed: 204 additions & 149 deletions

File tree

cpp/dolfinx/common/IndexMap.cpp

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ namespace
2626
/// @param comm MPI communicator.
2727
/// @param owners List of ranks that own each ghost index.
2828
/// @return (src ranks, destination ranks). Both lists are sorted.
29-
std::array<std::vector<int>, 2> build_src_dest(MPI_Comm comm,
30-
std::span<const int> owners)
29+
std::array<std::vector<int>, 2>
30+
build_src_dest(MPI_Comm comm, std::span<const int> owners, int tag)
3131
{
3232
if (dolfinx::MPI::size(comm) == 1)
3333
{
@@ -40,8 +40,9 @@ std::array<std::vector<int>, 2> build_src_dest(MPI_Comm comm,
4040
auto [unique_end, range_end] = std::ranges::unique(src);
4141
src.erase(unique_end, range_end);
4242
src.shrink_to_fit();
43-
std::vector<int> dest = dolfinx::MPI::compute_graph_edges_nbx(comm, src);
43+
std::vector<int> dest = dolfinx::MPI::compute_graph_edges_nbx(comm, src, tag);
4444
std::ranges::sort(dest);
45+
4546
return {std::move(src), std::move(dest)};
4647
}
4748

@@ -57,9 +58,9 @@ std::array<std::vector<int>, 2> build_src_dest(MPI_Comm comm,
5758
/// @param[in] dest Destination ranks on `comm`.
5859
/// @param[in] ghosts Ghost indices on calling process.
5960
/// @param[in] owners Owning rank for each entry in `ghosts`.
60-
/// @param[in] include_ghost A list of the same length as `ghosts`, whose
61-
/// ith entry must be non-zero (true) to include `ghost[i]`, otherwise
62-
/// the ghost will be excluded
61+
/// @param[in] include_ghost A list of the same length as `ghosts`,
62+
/// whose ith entry must be non-zero (true) to include `ghost[i]`,
63+
/// otherwise the ghost will be excluded
6364
/// @return 1) The ghost indices packed in a buffer for communication
6465
/// 2) The received indices (in receive buffer layout)
6566
/// 3) A map relating the position of a ghost in the packed
@@ -879,8 +880,9 @@ IndexMap::IndexMap(MPI_Comm comm, std::int32_t local_size) : _comm(comm, true)
879880
//-----------------------------------------------------------------------------
880881
IndexMap::IndexMap(MPI_Comm comm, std::int32_t local_size,
881882
std::span<const std::int64_t> ghosts,
882-
std::span<const int> owners)
883-
: IndexMap(comm, local_size, build_src_dest(comm, owners), ghosts, owners)
883+
std::span<const int> owners, int tag)
884+
: IndexMap(comm, local_size, build_src_dest(comm, owners, tag), ghosts,
885+
owners)
884886
{
885887
// Do nothing
886888
}
@@ -1002,7 +1004,7 @@ std::vector<std::int64_t> IndexMap::global_indices() const
10021004
//-----------------------------------------------------------------------------
10031005
MPI_Comm IndexMap::comm() const { return _comm.comm(); }
10041006
//----------------------------------------------------------------------------
1005-
graph::AdjacencyList<int> IndexMap::index_to_dest_ranks() const
1007+
graph::AdjacencyList<int> IndexMap::index_to_dest_ranks(int tag) const
10061008
{
10071009
const std::int64_t offset = _local_range[0];
10081010

@@ -1011,7 +1013,8 @@ graph::AdjacencyList<int> IndexMap::index_to_dest_ranks() const
10111013
std::ranges::sort(src);
10121014
auto [unique_end, range_end] = std::ranges::unique(src);
10131015
src.erase(unique_end, range_end);
1014-
auto dest = dolfinx::MPI::compute_graph_edges_nbx(_comm.comm(), src);
1016+
std::vector<int> dest
1017+
= dolfinx::MPI::compute_graph_edges_nbx(_comm.comm(), src, tag);
10151018
std::ranges::sort(dest);
10161019

10171020
// Array (local idx, ghosting rank) pairs for owned indices

cpp/dolfinx/common/IndexMap.h

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -119,8 +119,21 @@ class IndexMap
119119
/// of owned entries
120120
/// @param[in] ghosts The global indices of ghost entries
121121
/// @param[in] owners Owner rank (on `comm`) of each entry in `ghosts`
122+
/// @param[in] tag Tag used in non-blocking MPI calls in the consensus
123+
/// algorithm.
124+
/// @note A tag can sometimes be required when there are a series of
125+
/// calls to this constructor, or other functions that call the
126+
/// consensus algorithm, that are close together. In cases where this
127+
/// constructor is called a second time on rank and another rank has
128+
/// not completed its first consensus algorithm call, communications
129+
/// can be corrupted if each collective call of this constructor does
130+
/// not have its own `tag` value. Each collective call to this
131+
/// constructor must use the same `tag` value. An alternative to
132+
/// passing a tag is to have an implicit or explicit MPI barrier
133+
/// before and after the call to this constructor.
122134
IndexMap(MPI_Comm comm, std::int32_t local_size,
123-
std::span<const std::int64_t> ghosts, std::span<const int> owners);
135+
std::span<const std::int64_t> ghosts, std::span<const int> owners,
136+
int tag = static_cast<int>(dolfinx::MPI::tag::consensus_nbx));
124137

125138
/// @brief Create an overlapping (ghosted) index map.
126139
///
@@ -208,8 +221,16 @@ class IndexMap
208221
///
209222
/// @brief Compute map from each local (owned) index to the set of
210223
/// ranks that have the index as a ghost.
211-
/// @return shared indices
212-
graph::AdjacencyList<int> index_to_dest_ranks() const;
224+
///
225+
/// @note Collective
226+
///
227+
/// @param[in] tag Tag to pass to MPI calls.
228+
/// @note See ::IndexMap(MPI_Comm,std::int32_t,std::span<const
229+
/// std::int64_t>,std::span<const int>,int) for an explanation of when
230+
/// `tag` is required.
231+
/// @return Shared indices.
232+
graph::AdjacencyList<int> index_to_dest_ranks(
233+
int tag = static_cast<int>(dolfinx::MPI::tag::consensus_nbx)) const;
213234

214235
/// @brief Build a list of owned indices that are ghosted by another
215236
/// rank.

cpp/dolfinx/common/MPI.cpp

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,8 @@ dolfinx::MPI::compute_graph_edges_pcx(MPI_Comm comm, std::span<const int> edges)
159159
}
160160
//-----------------------------------------------------------------------------
161161
std::vector<int>
162-
dolfinx::MPI::compute_graph_edges_nbx(MPI_Comm comm, std::span<const int> edges)
162+
dolfinx::MPI::compute_graph_edges_nbx(MPI_Comm comm, std::span<const int> edges,
163+
int tag)
163164
{
164165
spdlog::info(
165166
"Computing communication graph edges (using NBX algorithm). Number "
@@ -171,9 +172,8 @@ dolfinx::MPI::compute_graph_edges_nbx(MPI_Comm comm, std::span<const int> edges)
171172
std::vector<std::byte> send_buffer(edges.size());
172173
for (std::size_t e = 0; e < edges.size(); ++e)
173174
{
174-
int err = MPI_Issend(send_buffer.data() + e, 1, MPI_BYTE, edges[e],
175-
static_cast<int>(tag::consensus_pex), comm,
176-
&send_requests[e]);
175+
int err = MPI_Issend(send_buffer.data() + e, 1, MPI_BYTE, edges[e], tag,
176+
comm, &send_requests[e]);
177177
dolfinx::MPI::check_error(comm, err);
178178
}
179179

@@ -189,8 +189,7 @@ dolfinx::MPI::compute_graph_edges_nbx(MPI_Comm comm, std::span<const int> edges)
189189
// Check for message
190190
int request_pending;
191191
MPI_Status status;
192-
int err = MPI_Iprobe(MPI_ANY_SOURCE, static_cast<int>(tag::consensus_pex),
193-
comm, &request_pending, &status);
192+
int err = MPI_Iprobe(MPI_ANY_SOURCE, tag, comm, &request_pending, &status);
194193
dolfinx::MPI::check_error(comm, err);
195194

196195
// Check if message is waiting to be processed
@@ -199,8 +198,7 @@ dolfinx::MPI::compute_graph_edges_nbx(MPI_Comm comm, std::span<const int> edges)
199198
// Receive it
200199
int other_rank = status.MPI_SOURCE;
201200
std::byte buffer_recv;
202-
int err = MPI_Recv(&buffer_recv, 1, MPI_BYTE, other_rank,
203-
static_cast<int>(tag::consensus_pex), comm,
201+
int err = MPI_Recv(&buffer_recv, 1, MPI_BYTE, other_rank, tag, comm,
204202
MPI_STATUS_IGNORE);
205203
dolfinx::MPI::check_error(comm, err);
206204
other_ranks.push_back(other_rank);

cpp/dolfinx/common/MPI.h

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,9 @@ namespace dolfinx::MPI
3333
/// MPI communication tags
3434
enum class tag : int
3535
{
36-
consensus_pcx,
37-
consensus_pex
36+
consensus_pcx = 1200,
37+
consensus_pex = 1201,
38+
consensus_nbx = 1202,
3839
};
3940

4041
/// @brief A duplicate MPI communicator and manage lifetime of the
@@ -72,22 +73,21 @@ class Comm
7273
int rank(MPI_Comm comm);
7374

7475
/// Return size of the group (number of processes) associated with the
75-
/// communicator
76+
/// communicator.
7677
int size(MPI_Comm comm);
7778

7879
/// @brief Check MPI error code. If the error code is not equal to
7980
/// MPI_SUCCESS, then std::abort is called.
80-
/// @param[in] comm MPI communicator
81-
/// @param[in] code Error code returned by an MPI function call
81+
/// @param[in] comm MPI communicator.
82+
/// @param[in] code Error code returned by an MPI function call.
8283
void check_error(MPI_Comm comm, int code);
8384

8485
/// @brief Return local range for the calling process, partitioning the
8586
/// global [0, N - 1] range across all ranks into partitions of almost
8687
/// equal size.
8788
/// @param[in] rank MPI rank of the caller
88-
/// @param[in] N The value to partition
89-
/// @param[in] size The number of MPI ranks across which to partition
90-
/// `N`
89+
/// @param[in] N The value to partition.
90+
/// @param[in] size Number of MPI ranks across which to partition `N`.
9191
constexpr std::array<std::int64_t, 2> local_range(int rank, std::int64_t N,
9292
int size)
9393
{
@@ -108,10 +108,10 @@ constexpr std::array<std::int64_t, 2> local_range(int rank, std::int64_t N,
108108

109109
/// @brief Return which rank owns index in global range [0, N - 1]
110110
/// (inverse of MPI::local_range).
111-
/// @param[in] size Number of MPI ranks
112-
/// @param[in] index The index to determine owning rank
113-
/// @param[in] N Total number of indices
114-
/// @return The rank of the owning process
111+
/// @param[in] size Number of MPI ranks.
112+
/// @param[in] index The index to determine the owning rank of.
113+
/// @param[in] N Total number of indices.
114+
/// @return Rank of the owning process.
115115
constexpr int index_owner(int size, std::size_t index, std::size_t N)
116116
{
117117
assert(index < N);
@@ -171,19 +171,21 @@ std::vector<int> compute_graph_edges_pcx(MPI_Comm comm,
171171
/// implements the NBX algorithm presented in
172172
/// https://dx.doi.org/10.1145/1837853.1693476.
173173
///
174-
/// @note For sparse graphs, this function has \f$O(\log p)\f$ cost,
175-
/// where \f$p\f$is the number of MPI ranks. It is suitable for modest
176-
/// MPI rank counts.
177-
///
178174
/// @note The order of the returned ranks is not deterministic.
179175
///
180176
/// @note Collective.
181177
///
182178
/// @param[in] comm MPI communicator
183179
/// @param[in] edges Edges (ranks) from this rank (the caller).
184-
/// @return Ranks that have defined edges from them to this rank.
185-
std::vector<int> compute_graph_edges_nbx(MPI_Comm comm,
186-
std::span<const int> edges);
180+
/// @param[in] tag Tag used in non-blocking MPI calls. A tag can be
181+
/// required when this function is called a second time on some ranks
182+
/// before a previous call has completed on all other ranks. @return
183+
/// Ranks that have defined edges from them to this rank. @note An
184+
/// alternative to passing a tag is to ensure that there is an implicit
185+
/// or explicit barrier before and after the call to this function.
186+
std::vector<int>
187+
compute_graph_edges_nbx(MPI_Comm comm, std::span<const int> edges,
188+
int tag = static_cast<int>(tag::consensus_nbx));
187189

188190
/// @brief Distribute row data to 'post office' ranks.
189191
///

0 commit comments

Comments
 (0)