139 if constexpr (BS0 != -1 or BS1 != -1)
141 if (bs[0] != BS0 or bs[1] != BS1)
142 throw std::invalid_argument(
"Invalid template parameters/block size");
148 const std::int32_t n_row = row_map_A->size_local();
149 const std::int32_t n_col = col_map_A->size_local();
154 std::span<const int> src = col_map_A->src();
155 std::span<const int> dest = col_map_A->dest();
159 MPI_Comm comm = row_map_A->comm();
164 auto at_col_map = std::make_shared<dolfinx::common::IndexMap>(comm, n_row);
165 auto at_row_map = std::make_shared<dolfinx::common::IndexMap>(comm, n_col);
167 auto [at_cols, at_rp, at_vals] = impl::local_transpose<T, BS0, BS1>(A);
170 std::vector<std::int32_t> off_diag_offsets(n_col);
171 for (
int j = 0; j < n_col; ++j)
172 off_diag_offsets[j] = at_rp[j + 1] - at_rp[j];
175 at_rp, off_diag_offsets, {bs[1], bs[0]}};
177 std::ranges::copy(at_vals, At.
values().begin());
183 auto cols_A = A.
cols();
186 std::span<const std::int64_t> ghost_col_globals = col_map_A->ghosts();
190 auto rank_to_nbr = [&src](
int r) ->
int
192 auto it = std::lower_bound(src.begin(), src.end(), r);
193 assert(it != src.end() && *it == r);
194 return static_cast<int>(std::ranges::distance(src.begin(), it));
196 std::vector<int> ghost_col_owners(col_map_A->owners().size());
197 std::transform(col_map_A->owners().begin(), col_map_A->owners().end(),
198 ghost_col_owners.begin(), rank_to_nbr);
203 MPI_Dist_graph_create_adjacent(
204 comm,
static_cast<int>(dest.size()), dest.data(), MPI_UNWEIGHTED,
205 static_cast<int>(src.size()), src.data(), MPI_UNWEIGHTED, MPI_INFO_NULL,
211 std::vector<int> send_count(src.size(), 0);
212 send_count.reserve(1);
213 for (std::int32_t i = 0; i < n_row; ++i)
215 for (std::int64_t k = offset_row_A[i]; k < row_ptr_A[i + 1]; ++k)
217 const std::int32_t j = cols_A[k] - n_col;
218 ++send_count[ghost_col_owners[j]];
222 std::vector<int> recv_count(dest.size(), 0);
223 recv_count.reserve(1);
224 MPI_Neighbor_alltoall(send_count.data(), 1, MPI_INT, recv_count.data(), 1,
225 MPI_INT, neigh_comm);
227 std::vector<int> send_disp(src.size() + 1, 0);
228 std::partial_sum(send_count.begin(), send_count.end(),
229 std::next(send_disp.begin()));
230 std::vector<int> recv_disp(dest.size() + 1, 0);
231 std::partial_sum(recv_count.begin(), recv_count.end(),
232 std::next(recv_disp.begin()));
233 const int total_send = send_disp.back();
234 const int total_recv = recv_disp.back();
240 auto [row_start, row_end] = row_map_A->local_range();
246 std::vector<std::int64_t> send_row_gidx(total_send);
247 std::vector<std::int64_t> send_col_gidx(total_send);
248 std::vector<T> send_vals(total_send * bs[0] * bs[1]);
250 int nbs = bs[0] * bs[1];
251 std::vector<int> pos = send_disp;
252 for (std::int32_t i = 0; i < n_row; ++i)
254 const std::int64_t global_i = row_start + i;
255 for (std::int64_t k = offset_row_A[i]; k < row_ptr_A[i + 1]; ++k)
257 const std::int32_t j = cols_A[k] - n_col;
258 const int sidx = ghost_col_owners[j];
259 const int p = pos[sidx]++;
260 send_row_gidx[p] = global_i;
261 send_col_gidx[p] = ghost_col_globals[j];
262 std::copy_n(std::next(vals_A.begin(), k * nbs), nbs,
263 std::next(send_vals.begin(), p * nbs));
271 std::vector<std::int64_t> recv_row_gidx(total_recv);
272 std::vector<std::int64_t> recv_col_gidx(total_recv);
273 std::vector<T> recv_vals(total_recv * bs[0] * bs[1]);
275 MPI_Request data_reqs[3];
277 MPI_Datatype mpi_T = block.
type();
279 MPI_Ineighbor_alltoallv(send_row_gidx.data(), send_count.data(),
280 send_disp.data(), MPI_INT64_T, recv_row_gidx.data(),
281 recv_count.data(), recv_disp.data(), MPI_INT64_T,
282 neigh_comm, &data_reqs[0]);
283 MPI_Ineighbor_alltoallv(send_col_gidx.data(), send_count.data(),
284 send_disp.data(), MPI_INT64_T, recv_col_gidx.data(),
285 recv_count.data(), recv_disp.data(), MPI_INT64_T,
286 neigh_comm, &data_reqs[1]);
287 MPI_Ineighbor_alltoallv(send_vals.data(), send_count.data(), send_disp.data(),
288 mpi_T, recv_vals.data(), recv_count.data(),
289 recv_disp.data(), mpi_T, neigh_comm, &data_reqs[2]);
292 auto [at0_cols, at0_rp, at0_vals] = impl::local_transpose<T, BS0, BS1>(A);
297 MPI_Waitall(3, data_reqs, MPI_STATUSES_IGNORE);
298 MPI_Comm_free(&neigh_comm);
303 auto [col_start, col_end] = col_map_A->local_range();
306 std::vector<std::int64_t> new_row_count(n_col);
307 std::adjacent_difference(std::next(at0_rp.begin()), at0_rp.end(),
308 new_row_count.begin());
309 for (std::int64_t c : recv_col_gidx)
311 const std::int32_t local_col =
static_cast<std::int32_t
>(c - col_start);
312 assert(local_col >= 0 && local_col < n_col);
313 ++new_row_count[local_col];
315 std::vector<std::int64_t> at_row_ptr(new_row_count.size() + 1, 0);
316 std::partial_sum(new_row_count.begin(), new_row_count.end(),
317 std::next(at_row_ptr.begin()));
320 std::vector<std::int64_t> ghost_row_globals;
321 std::vector<int> ghost_row_owners;
324 std::vector<std::pair<std::int64_t, int>> ghost_row_pairs;
325 ghost_row_pairs.reserve(recv_disp.back());
326 for (std::size_t p = 0; p < dest.size(); ++p)
328 for (
int k = recv_disp[p]; k < recv_disp[p + 1]; ++k)
330 const std::int64_t gidx = recv_row_gidx[k];
331 assert(gidx < row_start || gidx >= row_end);
332 ghost_row_pairs.emplace_back(gidx,
static_cast<int>(dest[p]));
339 std::sort(ghost_row_pairs.begin(), ghost_row_pairs.end());
340 ghost_row_pairs.erase(
341 std::unique(ghost_row_pairs.begin(), ghost_row_pairs.end()),
342 ghost_row_pairs.end());
344 ghost_row_globals.reserve(ghost_row_pairs.size());
345 ghost_row_owners.reserve(ghost_row_pairs.size());
346 for (
auto& [g, o] : ghost_row_pairs)
348 ghost_row_globals.push_back(g);
349 ghost_row_owners.push_back(o);
353 auto at_col_map = std::make_shared<dolfinx::common::IndexMap>(
354 comm, n_row, ghost_row_globals, ghost_row_owners);
357 std::vector<std::int32_t> new_col_local(recv_disp.back());
358 at_col_map->global_to_local(recv_row_gidx, new_col_local);
361 std::vector<std::int32_t> at_cols(at_row_ptr.back());
362 std::vector<T> at_vals(at_row_ptr.back() * bs[0] * bs[1]);
363 for (std::int32_t i = 0; i < n_col; ++i)
365 std::copy(std::next(at0_cols.begin(), at0_rp[i]),
366 std::next(at0_cols.begin(), at0_rp[i + 1]),
367 std::next(at_cols.begin(), at_row_ptr[i]));
368 std::copy(std::next(at0_vals.begin(), at0_rp[i] * bs[0] * bs[1]),
369 std::next(at0_vals.begin(), at0_rp[i + 1] * bs[0] * bs[1]),
370 std::next(at_vals.begin(), at_row_ptr[i] * bs[0] * bs[1]));
376 std::vector<std::int32_t> cursor(n_col);
377 std::vector<std::int32_t> off_diag_offsets(n_col);
378 for (
int j = 0; j < n_col; ++j)
380 off_diag_offsets[j] = at0_rp[j + 1] - at0_rp[j];
381 cursor[j] =
static_cast<std::int32_t
>(at_row_ptr[j]) + off_diag_offsets[j];
384 for (std::size_t p = 0; p < dest.size(); ++p)
386 for (
int k = recv_disp[p]; k < recv_disp[p + 1]; ++k)
388 const std::int32_t local_col
389 =
static_cast<std::int32_t
>(recv_col_gidx[k] - col_start);
390 assert(local_col >= 0 && local_col < n_col);
391 const int pos = cursor[local_col]++;
392 at_cols[pos] = new_col_local[k];
394 if constexpr (BS0 < 0 or BS1 < 0)
396 for (
int k0 = 0; k0 < bs[0]; ++k0)
397 for (
int k1 = 0; k1 < bs[1]; ++k1)
399 at_vals[pos * bs[0] * bs[1] + k1 * bs[0] + k0]
400 = recv_vals[k * bs[0] * bs[1] + k0 * bs[1] + k1];
405 for (
int k0 = 0; k0 < BS0; ++k0)
406 for (
int k1 = 0; k1 < BS1; ++k1)
408 at_vals[pos * BS0 * BS1 + k1 * BS0 + k0]
409 = recv_vals[k * BS0 * BS1 + k0 * BS1 + k1];
419 auto at_row_map = std::make_shared<dolfinx::common::IndexMap>(comm, n_col);
422 at_row_ptr, off_diag_offsets, {bs[1], bs[0]}};
425 std::ranges::copy(at_vals, At.
values().begin());
429 logger().info(
"Transpose: ({} x {}) -> ({} x {}), nnz {}",
430 row_map_A->size_global(), col_map_A->size_global(),
431 col_map_A->size_global(), row_map_A->size_global(),