61    const mesh::Topology& topology, std::span<const std::int64_t> nodes_g,
 
   63    md::mdspan<
const std::int32_t, md::dextents<std::size_t, 2>> xdofmap,
 
   65    md::mdspan<
const std::int64_t, md::dextents<std::size_t, 2>> entities,
 
   66    std::span<const T> data)
 
   68  assert(entities.extent(0) == data.size());
 
   70  spdlog::info(
"XDMF distribute entity data");
 
   74  std::vector<int> cell_vertex_dofs;
 
   77    const std::vector<int>& local_index = cmap_dof_layout.
entity_dofs(0, i);
 
   78    assert(local_index.size() == 1);
 
   79    cell_vertex_dofs.push_back(local_index[0]);
 
   84  auto to_vertex_entities
 
   92    const std::vector<int>& entity_layout
 
   94    std::vector<int> entity_vertex_dofs;
 
   95    for (std::size_t i = 0; i < cell_vertex_dofs.size(); ++i)
 
   97      auto it = std::find(entity_layout.begin(), entity_layout.end(),
 
   99      if (it != entity_layout.end())
 
  100        entity_vertex_dofs.push_back(std::distance(entity_layout.begin(), it));
 
  106    assert(entities.extent(1) == entity_layout.size());
 
  107    std::vector<std::int64_t> entities_v(entities.extent(0) * num_vert_per_e);
 
  108    for (std::size_t e = 0; e < entities.extent(0); ++e)
 
  110      std::span entity(entities_v.data() + e * num_vert_per_e, num_vert_per_e);
 
  111      for (std::size_t i = 0; i < num_vert_per_e; ++i)
 
  112        entity[i] = entities(e, entity_vertex_dofs[i]);
 
  113      std::ranges::sort(entity);
 
  116    std::array shape{entities.extent(0), num_vert_per_e};
 
  117    return std::pair(std::move(entities_v), shape);
 
  119  const auto [entities_v_b, shapev] = to_vertex_entities(
 
  120      cmap_dof_layout, entity_dim, cell_vertex_dofs, cell_type, entities);
 
  122  md::mdspan<const std::int64_t, md::dextents<std::size_t, 2>> entities_v(
 
  123      entities_v_b.data(), shapev);
 
  125  MPI_Comm comm = topology.
comm();
 
  126  MPI_Datatype compound_type;
 
  127  MPI_Type_contiguous(entities_v.extent(1), MPI_INT64_T, &compound_type);
 
  128  MPI_Type_commit(&compound_type);
 
  131  auto send_entities_to_postmaster
 
  132      = [](MPI_Comm comm, MPI_Datatype compound_type, std::int64_t num_nodes_g,
 
  133           auto entities, std::span<const T> data)
 
  138    std::vector<int> dest0;
 
  139    dest0.reserve(entities.extent(0));
 
  140    for (std::size_t e = 0; e < entities.extent(0); ++e)
 
  145    std::vector<int> perm(dest0.size());
 
  146    std::iota(perm.begin(), perm.end(), 0);
 
  147    std::ranges::sort(perm, [&dest0](
auto x0, 
auto x1)
 
  148                      { 
return dest0[x0] < dest0[x1]; });
 
  153    std::vector<int> dest;
 
  154    std::vector<std::int32_t> num_items_send;
 
  156      auto it = perm.begin();
 
  157      while (it != perm.end())
 
  159        dest.push_back(dest0[*it]);
 
  161            = std::find_if(it, perm.end(), [&dest0, r = dest.back()](
auto idx)
 
  162                           { return dest0[idx] != r; });
 
  163        num_items_send.push_back(std::distance(it, it1));
 
  169    std::vector<int> send_disp(num_items_send.size() + 1, 0);
 
  170    std::partial_sum(num_items_send.begin(), num_items_send.end(),
 
  171                     std::next(send_disp.begin()));
 
  176    std::ranges::sort(src);
 
  181    int err = MPI_Dist_graph_create_adjacent(
 
  182        comm, src.size(), src.data(), MPI_UNWEIGHTED, dest.size(), dest.data(),
 
  183        MPI_UNWEIGHTED, MPI_INFO_NULL, 
false, &comm0);
 
  187    std::vector<int> num_items_recv(src.size());
 
  188    num_items_send.reserve(1);
 
  189    num_items_recv.reserve(1);
 
  190    MPI_Neighbor_alltoall(num_items_send.data(), 1, MPI_INT,
 
  191                          num_items_recv.data(), 1, MPI_INT, comm0);
 
  195    std::vector<int> recv_disp(num_items_recv.size() + 1, 0);
 
  196    std::partial_sum(num_items_recv.begin(), num_items_recv.end(),
 
  197                     std::next(recv_disp.begin()));
 
  200    std::vector<std::int64_t> send_buffer;
 
  201    std::vector<T> send_values_buffer;
 
  202    send_buffer.reserve(entities.size());
 
  203    send_values_buffer.reserve(data.size());
 
  204    for (std::size_t e = 0; e < entities.extent(0); ++e)
 
  207      auto it = std::next(entities.data_handle(), idx * entities.extent(1));
 
  208      send_buffer.insert(send_buffer.end(), it, it + entities.extent(1));
 
  209      send_values_buffer.push_back(data[idx]);
 
  212    std::vector<std::int64_t> recv_buffer(recv_disp.back()
 
  213                                          * entities.extent(1));
 
  214    err = MPI_Neighbor_alltoallv(send_buffer.data(), num_items_send.data(),
 
  215                                 send_disp.data(), compound_type,
 
  216                                 recv_buffer.data(), num_items_recv.data(),
 
  217                                 recv_disp.data(), compound_type, comm0);
 
  219    std::vector<T> recv_values_buffer(recv_disp.back());
 
  220    err = MPI_Neighbor_alltoallv(
 
  221        send_values_buffer.data(), num_items_send.data(), send_disp.data(),
 
  225    err = MPI_Comm_free(&comm0);
 
  228    std::array shape{recv_buffer.size() / (entities.extent(1)),
 
  229                     (entities.extent(1))};
 
  230    return std::tuple<std::vector<std::int64_t>, std::vector<T>,
 
  231                      std::array<std::size_t, 2>>(
 
  232        std::move(recv_buffer), std::move(recv_values_buffer), shape);
 
  234  const auto [entitiesp_b, entitiesp_v, shapep] = send_entities_to_postmaster(
 
  235      comm, compound_type, num_nodes_g, entities_v, data);
 
  236  md::mdspan<const std::int64_t, md::dextents<std::size_t, 2>> entitiesp(
 
  237      entitiesp_b.data(), shapep);
 
  240  auto indices_to_postoffice = [](MPI_Comm comm, std::int64_t num_nodes,
 
  241                                  std::span<const std::int64_t> indices)
 
  244    std::vector<std::pair<int, std::int64_t>> dest_to_index;
 
  245    std::ranges::transform(
 
  246        indices, std::back_inserter(dest_to_index),
 
  247        [size, num_nodes](
auto n)
 
  251    std::ranges::sort(dest_to_index);
 
  255    std::vector<int> dest;
 
  256    std::vector<std::int32_t> num_items_send;
 
  258      auto it = dest_to_index.begin();
 
  259      while (it != dest_to_index.end())
 
  261        dest.push_back(it->first);
 
  263            = std::find_if(it, dest_to_index.end(), [r = dest.back()](
auto idx)
 
  264                           { return idx.first != r; });
 
  265        num_items_send.push_back(std::distance(it, it1));
 
  271    std::vector<int> send_disp(num_items_send.size() + 1, 0);
 
  272    std::partial_sum(num_items_send.begin(), num_items_send.end(),
 
  273                     std::next(send_disp.begin()));
 
  278    std::ranges::sort(src);
 
  282    int err = MPI_Dist_graph_create_adjacent(
 
  283        comm, src.size(), src.data(), MPI_UNWEIGHTED, dest.size(), dest.data(),
 
  284        MPI_UNWEIGHTED, MPI_INFO_NULL, 
false, &comm0);
 
  289    std::vector<int> num_items_recv(src.size());
 
  290    num_items_send.reserve(1);
 
  291    num_items_recv.reserve(1);
 
  292    MPI_Neighbor_alltoall(num_items_send.data(), 1, MPI_INT,
 
  293                          num_items_recv.data(), 1, MPI_INT, comm0);
 
  297    std::vector<int> recv_disp(num_items_recv.size() + 1, 0);
 
  298    std::partial_sum(num_items_recv.begin(), num_items_recv.end(),
 
  299                     std::next(recv_disp.begin()));
 
  302    std::vector<std::int64_t> send_buffer;
 
  303    send_buffer.reserve(indices.size());
 
  304    std::ranges::transform(dest_to_index, std::back_inserter(send_buffer),
 
  305                           [](
auto x) { 
return x.second; });
 
  307    std::vector<std::int64_t> recv_buffer(recv_disp.back());
 
  308    err = MPI_Neighbor_alltoallv(send_buffer.data(), num_items_send.data(),
 
  309                                 send_disp.data(), MPI_INT64_T,
 
  310                                 recv_buffer.data(), num_items_recv.data(),
 
  311                                 recv_disp.data(), MPI_INT64_T, comm0);
 
  313    err = MPI_Comm_free(&comm0);
 
  315    return std::tuple(std::move(recv_buffer), std::move(recv_disp),
 
  316                      std::move(src), std::move(dest));
 
  318  const auto [nodes_g_p, recv_disp, src, dest]
 
  319      = indices_to_postoffice(comm, num_nodes_g, nodes_g);
 
  323      = [](MPI_Comm comm, MPI_Datatype compound_type,
 
  324           std::span<const std::int64_t> indices_recv,
 
  325           std::span<const int> indices_recv_disp, std::span<const int> src,
 
  326           std::span<const int> dest, 
auto entities, std::span<const T> data)
 
  330    std::multimap<std::int64_t, int> node_to_rank;
 
  331    for (std::size_t i = 0; i < indices_recv_disp.size() - 1; ++i)
 
  332      for (
int j = indices_recv_disp[i]; j < indices_recv_disp[i + 1]; ++j)
 
  333        node_to_rank.insert({indices_recv[j], i});
 
  335    std::vector<std::vector<std::int64_t>> send_data(dest.size());
 
  336    std::vector<std::vector<T>> send_values(dest.size());
 
  337    for (std::size_t e = 0; e < entities.extent(0); ++e)
 
  339      std::span e_recv(entities.data_handle() + e * entities.extent(1),
 
  341      auto [it0, it1] = node_to_rank.equal_range(entities(e, 0));
 
  342      for (
auto it = it0; it != it1; ++it)
 
  345        send_data[p].insert(send_data[p].end(), e_recv.begin(), e_recv.end());
 
  346        send_values[p].push_back(data[e]);
 
  351    int err = MPI_Dist_graph_create_adjacent(
 
  352        comm, src.size(), src.data(), MPI_UNWEIGHTED, dest.size(), dest.data(),
 
  353        MPI_UNWEIGHTED, MPI_INFO_NULL, 
false, &comm0);
 
  356    std::vector<int> num_items_send;
 
  357    num_items_send.reserve(send_data.size());
 
  358    for (
auto& x : send_data)
 
  359      num_items_send.push_back(x.size() / entities.extent(1));
 
  361    std::vector<int> num_items_recv(src.size());
 
  362    num_items_send.reserve(1);
 
  363    num_items_recv.reserve(1);
 
  364    err = MPI_Neighbor_alltoall(num_items_send.data(), 1, MPI_INT,
 
  365                                num_items_recv.data(), 1, MPI_INT, comm0);
 
  369    std::vector<std::int32_t> send_disp(num_items_send.size() + 1, 0);
 
  370    std::partial_sum(num_items_send.begin(), num_items_send.end(),
 
  371                     std::next(send_disp.begin()));
 
  374    std::vector<std::int32_t> recv_disp(num_items_recv.size() + 1, 0);
 
  375    std::partial_sum(num_items_recv.begin(), num_items_recv.end(),
 
  376                     std::next(recv_disp.begin()));
 
  379    std::vector<std::int64_t> send_buffer;
 
  380    std::vector<T> send_values_buffer;
 
  381    for (
auto& x : send_data)
 
  382      send_buffer.insert(send_buffer.end(), x.begin(), x.end());
 
  383    for (
auto& v : send_values)
 
  384      send_values_buffer.insert(send_values_buffer.end(), v.begin(), v.end());
 
  385    std::vector<std::int64_t> recv_buffer(entities.extent(1)
 
  387    err = MPI_Neighbor_alltoallv(send_buffer.data(), num_items_send.data(),
 
  388                                 send_disp.data(), compound_type,
 
  389                                 recv_buffer.data(), num_items_recv.data(),
 
  390                                 recv_disp.data(), compound_type, comm0);
 
  394    std::vector<T> recv_values_buffer(recv_disp.back());
 
  395    err = MPI_Neighbor_alltoallv(
 
  396        send_values_buffer.data(), num_items_send.data(), send_disp.data(),
 
  402    err = MPI_Comm_free(&comm0);
 
  405    std::array shape{recv_buffer.size() / entities.extent(1),
 
  407    return std::tuple<std::vector<std::int64_t>, std::vector<T>,
 
  408                      std::array<std::size_t, 2>>(
 
  409        std::move(recv_buffer), std::move(recv_values_buffer), shape);
 
  413  const auto [entities_data_b, entities_values, shape_eb]
 
  414      = candidate_ranks(comm, compound_type, nodes_g_p, recv_disp, dest, src,
 
  415                        entitiesp, std::span(entitiesp_v));
 
  416  md::mdspan<const std::int64_t, md::dextents<std::size_t, 2>> entities_data(
 
  417      entities_data_b.data(), shape_eb);
 
  428           std::span<const std::int64_t> nodes_g,
 
  429           std::span<const int> cell_vertex_dofs, 
auto entities_data,
 
  430           std::span<const T> entities_values)
 
  432    spdlog::info(
"XDMF build map");
 
  435      throw std::runtime_error(
"Missing cell-vertex connectivity.");
 
  437    std::map<std::int64_t, std::int32_t> input_idx_to_vertex;
 
  438    for (
int c = 0; c < c_to_v->num_nodes(); ++c)
 
  440      auto vertices = c_to_v->links(c);
 
  441      std::span xdofs(xdofmap.data_handle() + c * xdofmap.extent(1),
 
  443      for (std::size_t v = 0; v < vertices.size(); ++v)
 
  444        input_idx_to_vertex[nodes_g[xdofs[cell_vertex_dofs[v]]]] = vertices[v];
 
  447    std::vector<std::int32_t> entities;
 
  449    std::vector<std::int32_t> entity(entities_data.extent(1));
 
  450    for (std::size_t e = 0; e < entities_data.extent(0); ++e)
 
  452      bool entity_found = 
true;
 
  453      for (std::size_t i = 0; i < entities_data.extent(1); ++i)
 
  455        if (
auto it = input_idx_to_vertex.find(entities_data(e, i));
 
  456            it == input_idx_to_vertex.end())
 
  460          entity_found = 
false;
 
  464          entity[i] = it->second;
 
  469        entities.insert(entities.end(), entity.begin(), entity.end());
 
  470        data.push_back(entities_values[e]);
 
  474    return std::pair(std::move(entities), std::move(data));
 
  477  MPI_Type_free(&compound_type);
 
  479  return select_entities(topology, xdofmap, nodes_g, cell_vertex_dofs,
 
  480                         entities_data, std::span(entities_values));