]> https://gitweb.dealii.org/ - dealii.git/commitdiff
Implement DataTransfer for disabled MPI support
authorPasquale Africa <pafrica@sissa.it>
Sun, 2 Jul 2023 21:18:57 +0000 (21:18 +0000)
committerPasquale Africa <pafrica@sissa.it>
Fri, 7 Jul 2023 23:17:06 +0000 (23:17 +0000)
include/deal.II/grid/data_transfer.h
include/deal.II/grid/tria.h
source/distributed/tria.cc
source/grid/CMakeLists.txt
source/grid/data_transfer.cc [new file with mode: 0644]
source/grid/data_transfer.inst.in [new file with mode: 0644]
source/grid/tria.cc

index 14c2b4ec7cf42a74a9a2ca08abb6b4da042ac222..5eac21a8d495acae3d98b4ab1ae3d2ac78dcd36f 100644 (file)
@@ -28,6 +28,7 @@
 #include <boost/range/iterator_range.hpp>
 
 #include <algorithm>
+#include <fstream>
 #include <functional>
 #include <utility>
 #include <vector>
@@ -88,7 +89,7 @@ public:
    */
   using cell_relation_t = typename std::pair<cell_iterator, CellStatus>;
 
-  DataTransfer(const MPI_Comm mpi_communicator);
+  DataTransfer(const MPI_Comm &mpi_communicator);
 
   /**
    * Prepare data transfer by calling the pack callback functions on each
@@ -146,9 +147,9 @@ public:
    * <tt>_fixed.data</tt> for fixed size data and <tt>_variable.data</tt>
    * for variable size data.
    *
-   * All processors write into these files simultaneously via MPIIO.
-   * Each processor's position to write to will be determined
-   * from the provided input parameters.
+   * If MPI support is enabled, all processors write into these files
+   * simultaneously via MPIIO. Each processor's position to write to will be
+   * determined from the provided input parameters.
    *
    * Data has to be previously packed with pack_data().
    */
@@ -168,9 +169,9 @@ public:
    * parameters are required to gather the memory offsets for each
    * callback.
    *
-   * All processors read from these files simultaneously via MPIIO.
-   * Each processor's position to read from will be determined
-   * from the provided input arguments.
+   * If MPI support is enabled, all processors read from these files
+   * simultaneously via MPIIO. Each processor's position to read from will be
+   * determined from the provided input arguments.
    *
    * After loading, unpack_data() needs to be called to finally
    * distribute data across the associated triangulation.
@@ -211,14 +212,14 @@ public:
 
   /**
    * Consecutive buffers designed for the fixed size transfer
-   * functions of p4est.
+   * functions.
    */
   std::vector<char> src_data_fixed;
   std::vector<char> dest_data_fixed;
 
   /**
    * Consecutive buffers designed for the variable size transfer
-   * functions of p4est.
+   * functions.
    */
   std::vector<int>  src_sizes_variable;
   std::vector<int>  dest_sizes_variable;
@@ -226,900 +227,9 @@ public:
   std::vector<char> dest_data_variable;
 
 private:
-  MPI_Comm mpi_communicator;
+  const MPI_Comm &mpi_communicator;
 };
 
-
-template <int dim, int spacedim>
-DEAL_II_CXX20_REQUIRES((concepts::is_valid_dim_spacedim<dim, spacedim>))
-DataTransfer<dim, spacedim>::DataTransfer(const MPI_Comm mpi_communicator)
-  : variable_size_data_stored(false)
-  , mpi_communicator(mpi_communicator)
-{}
-
-
-template <int dim, int spacedim>
-DEAL_II_CXX20_REQUIRES((concepts::is_valid_dim_spacedim<dim, spacedim>))
-void DataTransfer<dim, spacedim>::pack_data(
-  const std::vector<cell_relation_t> &cell_relations,
-  const std::vector<typename CellAttachedData<dim, spacedim>::pack_callback_t>
-    &pack_callbacks_fixed,
-  const std::vector<typename CellAttachedData<dim, spacedim>::pack_callback_t>
-    &pack_callbacks_variable)
-{
-  Assert(src_data_fixed.size() == 0,
-         ExcMessage("Previously packed data has not been released yet!"));
-  Assert(src_sizes_variable.size() == 0, ExcInternalError());
-
-  const unsigned int n_callbacks_fixed    = pack_callbacks_fixed.size();
-  const unsigned int n_callbacks_variable = pack_callbacks_variable.size();
-
-  // Store information that we packed variable size data in
-  // a member variable for later.
-  variable_size_data_stored = (n_callbacks_variable > 0);
-
-  // If variable transfer is scheduled, we will store the data size that
-  // each variable size callback function writes in this auxiliary
-  // container. The information will be stored by each cell in this vector
-  // temporarily.
-  std::vector<unsigned int> cell_sizes_variable_cumulative(
-    n_callbacks_variable);
-
-  // Prepare the buffer structure, in which each callback function will
-  // store its data for each active cell.
-  // The outmost shell in this container construct corresponds to the
-  // data packed per cell. The next layer resembles the data that
-  // each callback function packs on the corresponding cell. These
-  // buffers are chains of chars stored in an std::vector<char>.
-  // A visualisation of the data structure:
-  /* clang-format off */
-      // |             cell_1                | |             cell_2                | ...
-      // ||  callback_1  ||  callback_2  |...| ||  callback_1  ||  callback_2  |...| ...
-      // |||char|char|...|||char|char|...|...| |||char|char|...|||char|char|...|...| ...
-  /* clang-format on */
-  std::vector<std::vector<std::vector<char>>> packed_fixed_size_data(
-    cell_relations.size());
-  std::vector<std::vector<std::vector<char>>> packed_variable_size_data(
-    variable_size_data_stored ? cell_relations.size() : 0);
-
-  //
-  // --------- Pack data for fixed and variable size transfer ---------
-  //
-  // Iterate over all cells, call all callback functions on each cell,
-  // and store their data in the corresponding buffer scope.
-  {
-    auto cell_rel_it           = cell_relations.cbegin();
-    auto data_cell_fixed_it    = packed_fixed_size_data.begin();
-    auto data_cell_variable_it = packed_variable_size_data.begin();
-    for (; cell_rel_it != cell_relations.cend(); ++cell_rel_it)
-      {
-        const auto &dealii_cell = cell_rel_it->first;
-        const auto &cell_status = cell_rel_it->second;
-
-        // Assertions about the tree structure.
-        switch (cell_status)
-          {
-            case CELL_PERSIST:
-            case CELL_REFINE:
-              // double check the condition that we will only ever attach
-              // data to active cells when we get here
-              Assert(dealii_cell->is_active(), ExcInternalError());
-              break;
-
-            case CELL_COARSEN:
-              // double check the condition that we will only ever attach
-              // data to cells with children when we get here. however, we
-              // can only tolerate one level of coarsening at a time, so
-              // check that the children are all active
-              Assert(dealii_cell->is_active() == false, ExcInternalError());
-              for (unsigned int c = 0;
-                   c < GeometryInfo<dim>::max_children_per_cell;
-                   ++c)
-                Assert(dealii_cell->child(c)->is_active(), ExcInternalError());
-              break;
-
-            case CELL_INVALID:
-              // do nothing on invalid cells
-              break;
-
-            default:
-              Assert(false, ExcInternalError());
-              break;
-          }
-
-        // Reserve memory corresponding to the number of callback
-        // functions that will be called.
-        // If variable size transfer is scheduled, we need to leave
-        // room for an array that holds information about how many
-        // bytes each of the variable size callback functions will
-        // write.
-        // On cells flagged with CELL_INVALID, only its CellStatus
-        // will be stored.
-        const unsigned int n_fixed_size_data_sets_on_cell =
-          1 + ((cell_status == CELL_INVALID) ?
-                 0 :
-                 ((variable_size_data_stored ? 1 : 0) + n_callbacks_fixed));
-        data_cell_fixed_it->resize(n_fixed_size_data_sets_on_cell);
-
-        // We continue with packing all data on this specific cell.
-        auto data_fixed_it = data_cell_fixed_it->begin();
-
-        // First, we pack the CellStatus information.
-        // to get consistent data sizes on each cell for the fixed size
-        // transfer, we won't allow compression
-        *data_fixed_it =
-          Utilities::pack(cell_status, /*allow_compression=*/false);
-        ++data_fixed_it;
-
-        // Proceed with all registered callback functions.
-        // Skip cells with the CELL_INVALID flag.
-        if (cell_status != CELL_INVALID)
-          {
-            // Pack fixed size data.
-            for (auto callback_it = pack_callbacks_fixed.cbegin();
-                 callback_it != pack_callbacks_fixed.cend();
-                 ++callback_it, ++data_fixed_it)
-              {
-                *data_fixed_it = (*callback_it)(dealii_cell, cell_status);
-              }
-
-            // Pack variable size data.
-            // If we store variable size data, we need to transfer
-            // the sizes of each corresponding callback function
-            // via fixed size transfer as well.
-            if (variable_size_data_stored)
-              {
-                const unsigned int n_variable_size_data_sets_on_cell =
-                  ((cell_status == CELL_INVALID) ? 0 : n_callbacks_variable);
-                data_cell_variable_it->resize(
-                  n_variable_size_data_sets_on_cell);
-
-                auto callback_it       = pack_callbacks_variable.cbegin();
-                auto data_variable_it  = data_cell_variable_it->begin();
-                auto sizes_variable_it = cell_sizes_variable_cumulative.begin();
-                for (; callback_it != pack_callbacks_variable.cend();
-                     ++callback_it, ++data_variable_it, ++sizes_variable_it)
-                  {
-                    *data_variable_it =
-                      (*callback_it)(dealii_cell, cell_status);
-
-                    // Store data sizes for each callback function first.
-                    // Make it cumulative below.
-                    *sizes_variable_it = data_variable_it->size();
-                  }
-
-                // Turn size vector into its cumulative representation.
-                std::partial_sum(cell_sizes_variable_cumulative.begin(),
-                                 cell_sizes_variable_cumulative.end(),
-                                 cell_sizes_variable_cumulative.begin());
-
-                // Serialize cumulative variable size vector
-                // value-by-value. This way we can circumvent the overhead
-                // of storing the container object as a whole, since we
-                // know its size by the number of registered callback
-                // functions.
-                data_fixed_it->resize(n_callbacks_variable *
-                                      sizeof(unsigned int));
-                for (unsigned int i = 0; i < n_callbacks_variable; ++i)
-                  std::memcpy(&(data_fixed_it->at(i * sizeof(unsigned int))),
-                              &(cell_sizes_variable_cumulative.at(i)),
-                              sizeof(unsigned int));
-
-                ++data_fixed_it;
-              }
-
-            // Double check that we packed everything we wanted
-            // in the fixed size buffers.
-            Assert(data_fixed_it == data_cell_fixed_it->end(),
-                   ExcInternalError());
-          }
-
-        ++data_cell_fixed_it;
-
-        // Increment the variable size data iterator
-        // only if we actually pack this kind of data
-        // to avoid getting out of bounds.
-        if (variable_size_data_stored)
-          ++data_cell_variable_it;
-      } // loop over cell_relations
-  }
-
-  //
-  // ----------- Gather data sizes for fixed size transfer ------------
-  //
-  // Generate a vector which stores the sizes of each callback function,
-  // including the packed CellStatus transfer.
-  // Find the very first cell that we wrote to with all callback
-  // functions (i.e. a cell that was not flagged with CELL_INVALID)
-  // and store the sizes of each buffer.
-  //
-  // To deal with the case that at least one of the processors does not
-  // own any cell at all, we will exchange the information about the data
-  // sizes among them later. The code in between is still well-defined,
-  // since the following loops will be skipped.
-  std::vector<unsigned int> local_sizes_fixed(
-    1 + n_callbacks_fixed + (variable_size_data_stored ? 1 : 0));
-  for (const auto &data_cell : packed_fixed_size_data)
-    {
-      if (data_cell.size() == local_sizes_fixed.size())
-        {
-          auto sizes_fixed_it = local_sizes_fixed.begin();
-          auto data_fixed_it  = data_cell.cbegin();
-          for (; data_fixed_it != data_cell.cend();
-               ++data_fixed_it, ++sizes_fixed_it)
-            {
-              *sizes_fixed_it = data_fixed_it->size();
-            }
-
-          break;
-        }
-    }
-
-  // Check if all cells have valid sizes.
-  for (auto data_cell_fixed_it = packed_fixed_size_data.cbegin();
-       data_cell_fixed_it != packed_fixed_size_data.cend();
-       ++data_cell_fixed_it)
-    {
-      Assert((data_cell_fixed_it->size() == 1) ||
-               (data_cell_fixed_it->size() == local_sizes_fixed.size()),
-             ExcInternalError());
-    }
-
-  // Share information about the packed data sizes
-  // of all callback functions across all processors, in case one
-  // of them does not own any cells at all.
-  std::vector<unsigned int> global_sizes_fixed(local_sizes_fixed.size());
-  Utilities::MPI::max(local_sizes_fixed,
-                      this->mpi_communicator,
-                      global_sizes_fixed);
-
-  // Construct cumulative sizes, since this is the only information
-  // we need from now on.
-  sizes_fixed_cumulative.resize(global_sizes_fixed.size());
-  std::partial_sum(global_sizes_fixed.begin(),
-                   global_sizes_fixed.end(),
-                   sizes_fixed_cumulative.begin());
-
-  //
-  // ---------- Gather data sizes for variable size transfer ----------
-  //
-  if (variable_size_data_stored)
-    {
-      src_sizes_variable.reserve(packed_variable_size_data.size());
-      for (const auto &data_cell : packed_variable_size_data)
-        {
-          int variable_data_size_on_cell = 0;
-
-          for (const auto &data : data_cell)
-            variable_data_size_on_cell += data.size();
-
-          src_sizes_variable.push_back(variable_data_size_on_cell);
-        }
-    }
-
-  //
-  // ------------------------ Build buffers ---------------------------
-  //
-  const unsigned int expected_size_fixed =
-    cell_relations.size() * sizes_fixed_cumulative.back();
-  const unsigned int expected_size_variable =
-    std::accumulate(src_sizes_variable.begin(),
-                    src_sizes_variable.end(),
-                    std::vector<int>::size_type(0));
-
-  // Move every piece of packed fixed size data into the consecutive
-  // buffer.
-  src_data_fixed.reserve(expected_size_fixed);
-  for (const auto &data_cell_fixed : packed_fixed_size_data)
-    {
-      // Move every fraction of packed data into the buffer
-      // reserved for this particular cell.
-      for (const auto &data_fixed : data_cell_fixed)
-        std::move(data_fixed.begin(),
-                  data_fixed.end(),
-                  std::back_inserter(src_data_fixed));
-
-      // If we only packed the CellStatus information
-      // (i.e. encountered a cell flagged CELL_INVALID),
-      // fill the remaining space with invalid entries.
-      // We can skip this if there is nothing else to pack.
-      if ((data_cell_fixed.size() == 1) && (sizes_fixed_cumulative.size() > 1))
-        {
-          const std::size_t bytes_skipped =
-            sizes_fixed_cumulative.back() - sizes_fixed_cumulative.front();
-
-          src_data_fixed.insert(src_data_fixed.end(),
-                                bytes_skipped,
-                                static_cast<char>(-1)); // invalid_char
-        }
-    }
-
-  // Move every piece of packed variable size data into the consecutive
-  // buffer.
-  if (variable_size_data_stored)
-    {
-      src_data_variable.reserve(expected_size_variable);
-      for (const auto &data_cell : packed_variable_size_data)
-        {
-          // Move every fraction of packed data into the buffer
-          // reserved for this particular cell.
-          for (const auto &data : data_cell)
-            std::move(data.begin(),
-                      data.end(),
-                      std::back_inserter(src_data_variable));
-        }
-    }
-
-  // Double check that we packed everything correctly.
-  Assert(src_data_fixed.size() == expected_size_fixed, ExcInternalError());
-  Assert(src_data_variable.size() == expected_size_variable,
-         ExcInternalError());
-}
-
-
-
-template <int dim, int spacedim>
-DEAL_II_CXX20_REQUIRES((concepts::is_valid_dim_spacedim<dim, spacedim>))
-void DataTransfer<dim, spacedim>::unpack_cell_status(
-  std::vector<typename DataTransfer<dim, spacedim>::cell_relation_t>
-    &cell_relations) const
-{
-  Assert(sizes_fixed_cumulative.size() > 0,
-         ExcMessage("No data has been packed!"));
-  if (cell_relations.size() > 0)
-    {
-      Assert(dest_data_fixed.size() > 0,
-             ExcMessage("No data has been received!"));
-    }
-
-  // Size of CellStatus object that will be unpacked on each cell.
-  const unsigned int size = sizes_fixed_cumulative.front();
-
-  // Iterate over all cells and overwrite the CellStatus
-  // information from the transferred data.
-  // Proceed buffer iterator position to next cell after
-  // each iteration.
-  auto cell_rel_it   = cell_relations.begin();
-  auto dest_fixed_it = dest_data_fixed.cbegin();
-  for (; cell_rel_it != cell_relations.end();
-       ++cell_rel_it, dest_fixed_it += sizes_fixed_cumulative.back())
-    {
-      cell_rel_it->second = // cell_status
-        Utilities::unpack<CellStatus>(dest_fixed_it,
-                                      dest_fixed_it + size,
-                                      /*allow_compression=*/false);
-    }
-}
-
-
-
-template <int dim, int spacedim>
-DEAL_II_CXX20_REQUIRES((concepts::is_valid_dim_spacedim<dim, spacedim>))
-void DataTransfer<dim, spacedim>::unpack_data(
-  const std::vector<typename DataTransfer<dim, spacedim>::cell_relation_t>
-    &                cell_relations,
-  const unsigned int handle,
-  const std::function<
-    void(const cell_iterator &,
-         const CellStatus &,
-         const boost::iterator_range<std::vector<char>::const_iterator> &)>
-    &unpack_callback) const
-{
-  // We decode the handle returned by register_data_attach() back into
-  // a format we can use. All even handles belong to those callback
-  // functions which write/read variable size data, all odd handles
-  // interact with fixed size buffers.
-  const bool         callback_variable_transfer = (handle % 2 == 0);
-  const unsigned int callback_index             = handle / 2;
-
-  // Cells will always receive fixed size data (i.e., CellStatus
-  // information), but not necessarily variable size data (e.g., with a
-  // ParticleHandler a cell might not contain any particle at all).
-  // Thus it is sufficient to check if fixed size data has been received.
-  Assert(sizes_fixed_cumulative.size() > 0,
-         ExcMessage("No data has been packed!"));
-  if (cell_relations.size() > 0)
-    {
-      Assert(dest_data_fixed.size() > 0,
-             ExcMessage("No data has been received!"));
-    }
-
-  std::vector<char>::const_iterator dest_data_it;
-  std::vector<char>::const_iterator dest_sizes_cell_it;
-
-  // Depending on whether our callback function unpacks fixed or
-  // variable size data, we have to pursue different approaches
-  // to localize the correct fraction of the buffer from which
-  // we are allowed to read.
-  unsigned int offset         = numbers::invalid_unsigned_int;
-  unsigned int size           = numbers::invalid_unsigned_int;
-  unsigned int data_increment = numbers::invalid_unsigned_int;
-
-  if (callback_variable_transfer)
-    {
-      // For the variable size data, we need to extract the
-      // data size from the fixed size buffer on each cell.
-      //
-      // We packed this information last, so the last packed
-      // object in the fixed size buffer corresponds to the
-      // variable data sizes.
-      //
-      // The last entry of sizes_fixed_cumulative corresponds
-      // to the size of all fixed size data packed on the cell.
-      // To get the offset for the last packed object, we need
-      // to get the next-to-last entry.
-      const unsigned int offset_variable_data_sizes =
-        sizes_fixed_cumulative[sizes_fixed_cumulative.size() - 2];
-
-      // This iterator points to the data size that the
-      // callback_function packed for each specific cell.
-      // Adjust buffer iterator to the offset of the callback
-      // function so that we only have to advance its position
-      // to the next cell after each iteration.
-      dest_sizes_cell_it = dest_data_fixed.cbegin() +
-                           offset_variable_data_sizes +
-                           callback_index * sizeof(unsigned int);
-
-      // Let the data iterator point to the correct buffer.
-      dest_data_it = dest_data_variable.cbegin();
-    }
-  else
-    {
-      // For the fixed size data, we can get the information about
-      // the buffer location on each cell directly from the
-      // sizes_fixed_cumulative vector.
-      offset         = sizes_fixed_cumulative[callback_index];
-      size           = sizes_fixed_cumulative[callback_index + 1] - offset;
-      data_increment = sizes_fixed_cumulative.back();
-
-      // Let the data iterator point to the correct buffer.
-      // Adjust buffer iterator to the offset of the callback
-      // function so that we only have to advance its position
-      // to the next cell after each iteration.
-      if (cell_relations.begin() != cell_relations.end())
-        dest_data_it = dest_data_fixed.cbegin() + offset;
-    }
-
-  // Iterate over all cells and unpack the transferred data.
-  auto cell_rel_it   = cell_relations.begin();
-  auto dest_sizes_it = dest_sizes_variable.cbegin();
-  for (; cell_rel_it != cell_relations.end(); ++cell_rel_it)
-    {
-      const auto &dealii_cell = cell_rel_it->first;
-      const auto &cell_status = cell_rel_it->second;
-
-      if (callback_variable_transfer)
-        {
-          // Update the increment according to the whole data size
-          // of the current cell.
-          data_increment = *dest_sizes_it;
-
-          if (cell_status != CELL_INVALID)
-            {
-              // Extract the corresponding values for offset and size from
-              // the cumulative sizes array stored in the fixed size
-              // buffer.
-              if (callback_index == 0)
-                offset = 0;
-              else
-                std::memcpy(&offset,
-                            &(*(dest_sizes_cell_it - sizeof(unsigned int))),
-                            sizeof(unsigned int));
-
-              std::memcpy(&size, &(*dest_sizes_cell_it), sizeof(unsigned int));
-
-              size -= offset;
-
-              // Move the data iterator to the corresponding position
-              // of the callback function and adjust the increment
-              // accordingly.
-              dest_data_it += offset;
-              data_increment -= offset;
-            }
-
-          // Advance data size iterators to the next cell, avoid iterating
-          // past the end of dest_sizes_cell_it
-          if (cell_rel_it != cell_relations.end() - 1)
-            dest_sizes_cell_it += sizes_fixed_cumulative.back();
-          ++dest_sizes_it;
-        }
-
-      switch (cell_status)
-        {
-          case CELL_PERSIST:
-          case CELL_COARSEN:
-            unpack_callback(dealii_cell,
-                            cell_status,
-                            boost::make_iterator_range(dest_data_it,
-                                                       dest_data_it + size));
-            break;
-
-          case CELL_REFINE:
-            unpack_callback(dealii_cell->parent(),
-                            cell_status,
-                            boost::make_iterator_range(dest_data_it,
-                                                       dest_data_it + size));
-            break;
-
-          case CELL_INVALID:
-            // Skip this cell.
-            break;
-
-          default:
-            Assert(false, ExcInternalError());
-            break;
-        }
-
-      if (cell_rel_it != cell_relations.end() - 1)
-        dest_data_it += data_increment;
-    }
-}
-
-
-
-template <int dim, int spacedim>
-DEAL_II_CXX20_REQUIRES((concepts::is_valid_dim_spacedim<dim, spacedim>))
-void DataTransfer<dim, spacedim>::save(const unsigned int global_first_cell,
-                                       const unsigned int global_num_cells,
-                                       const std::string &filename) const
-{
-#ifdef DEAL_II_WITH_MPI
-  // Large fractions of this function have been copied from
-  // DataOutInterface::write_vtu_in_parallel.
-  // TODO: Write general MPIIO interface.
-
-  Assert(sizes_fixed_cumulative.size() > 0,
-         ExcMessage("No data has been packed!"));
-
-  const int myrank = Utilities::MPI::this_mpi_process(mpi_communicator);
-
-  const unsigned int bytes_per_cell = sizes_fixed_cumulative.back();
-
-  //
-  // ---------- Fixed size data ----------
-  //
-  {
-    const std::string fname_fixed = std::string(filename) + "_fixed.data";
-
-    MPI_Info info;
-    int      ierr = MPI_Info_create(&info);
-    AssertThrowMPI(ierr);
-
-    MPI_File fh;
-    ierr = MPI_File_open(mpi_communicator,
-                         fname_fixed.c_str(),
-                         MPI_MODE_CREATE | MPI_MODE_WRONLY,
-                         info,
-                         &fh);
-    AssertThrowMPI(ierr);
-
-    ierr = MPI_File_set_size(fh, 0); // delete the file contents
-    AssertThrowMPI(ierr);
-    // this barrier is necessary, because otherwise others might already
-    // write while one core is still setting the size to zero.
-    ierr = MPI_Barrier(mpi_communicator);
-    AssertThrowMPI(ierr);
-    ierr = MPI_Info_free(&info);
-    AssertThrowMPI(ierr);
-    // ------------------
-
-    // Write cumulative sizes to file.
-    // Since each processor owns the same information about the data
-    // sizes, it is sufficient to let only the first processor perform
-    // this task.
-    if (myrank == 0)
-      {
-        ierr = Utilities::MPI::LargeCount::File_write_at_c(
-          fh,
-          0,
-          sizes_fixed_cumulative.data(),
-          sizes_fixed_cumulative.size(),
-          MPI_UNSIGNED,
-          MPI_STATUS_IGNORE);
-        AssertThrowMPI(ierr);
-      }
-
-    // Write packed data to file simultaneously.
-    const MPI_Offset size_header =
-      sizes_fixed_cumulative.size() * sizeof(unsigned int);
-
-    // Make sure we do the following computation in 64bit integers to be
-    // able to handle 4GB+ files:
-    const MPI_Offset my_global_file_position =
-      size_header + static_cast<MPI_Offset>(global_first_cell) * bytes_per_cell;
-
-    ierr = Utilities::MPI::LargeCount::File_write_at_c(fh,
-                                                       my_global_file_position,
-                                                       src_data_fixed.data(),
-                                                       src_data_fixed.size(),
-                                                       MPI_BYTE,
-                                                       MPI_STATUS_IGNORE);
-    AssertThrowMPI(ierr);
-
-    ierr = MPI_File_close(&fh);
-    AssertThrowMPI(ierr);
-  }
-
-
-
-  //
-  // ---------- Variable size data ----------
-  //
-  if (variable_size_data_stored)
-    {
-      const std::string fname_variable =
-        std::string(filename) + "_variable.data";
-
-      MPI_Info info;
-      int      ierr = MPI_Info_create(&info);
-      AssertThrowMPI(ierr);
-
-      MPI_File fh;
-      ierr = MPI_File_open(mpi_communicator,
-                           fname_variable.c_str(),
-                           MPI_MODE_CREATE | MPI_MODE_WRONLY,
-                           info,
-                           &fh);
-      AssertThrowMPI(ierr);
-
-      ierr = MPI_File_set_size(fh, 0); // delete the file contents
-      AssertThrowMPI(ierr);
-      // this barrier is necessary, because otherwise others might already
-      // write while one core is still setting the size to zero.
-      ierr = MPI_Barrier(mpi_communicator);
-      AssertThrowMPI(ierr);
-      ierr = MPI_Info_free(&info);
-      AssertThrowMPI(ierr);
-
-      // Write sizes of each cell into file simultaneously.
-      {
-        const MPI_Offset my_global_file_position =
-          static_cast<MPI_Offset>(global_first_cell) * sizeof(unsigned int);
-
-        // It is very unlikely that a single process has more than
-        // 2 billion cells, but we might as well check.
-        AssertThrow(src_sizes_variable.size() <
-                      static_cast<std::size_t>(std::numeric_limits<int>::max()),
-                    ExcNotImplemented());
-
-        ierr =
-          Utilities::MPI::LargeCount::File_write_at_c(fh,
-                                                      my_global_file_position,
-                                                      src_sizes_variable.data(),
-                                                      src_sizes_variable.size(),
-                                                      MPI_INT,
-                                                      MPI_STATUS_IGNORE);
-        AssertThrowMPI(ierr);
-      }
-
-      // Gather size of data in bytes we want to store from this
-      // processor and compute the prefix sum. We do this in 64 bit
-      // to avoid overflow for files larger than 4GB:
-      const std::uint64_t size_on_proc = src_data_variable.size();
-      std::uint64_t       prefix_sum   = 0;
-      ierr                             = MPI_Exscan(
-        &size_on_proc, &prefix_sum, 1, MPI_UINT64_T, MPI_SUM, mpi_communicator);
-      AssertThrowMPI(ierr);
-
-      const MPI_Offset my_global_file_position =
-        static_cast<MPI_Offset>(global_num_cells) * sizeof(unsigned int) +
-        prefix_sum;
-
-      // Write data consecutively into file.
-      ierr =
-        Utilities::MPI::LargeCount::File_write_at_c(fh,
-                                                    my_global_file_position,
-                                                    src_data_variable.data(),
-                                                    src_data_variable.size(),
-                                                    MPI_BYTE,
-                                                    MPI_STATUS_IGNORE);
-      AssertThrowMPI(ierr);
-
-
-      ierr = MPI_File_close(&fh);
-      AssertThrowMPI(ierr);
-    }
-#else
-  (void)global_first_cell;
-  (void)global_num_cells;
-  (void)filename;
-
-  AssertThrow(false, ExcNeedsMPI());
-#endif
-}
-
-
-
-template <int dim, int spacedim>
-DEAL_II_CXX20_REQUIRES((concepts::is_valid_dim_spacedim<dim, spacedim>))
-void DataTransfer<dim, spacedim>::load(
-  const unsigned int global_first_cell,
-  const unsigned int global_num_cells,
-  const unsigned int local_num_cells,
-  const std::string &filename,
-  const unsigned int n_attached_deserialize_fixed,
-  const unsigned int n_attached_deserialize_variable)
-{
-#ifdef DEAL_II_WITH_MPI
-  // Large fractions of this function have been copied from
-  // DataOutInterface::write_vtu_in_parallel.
-  // TODO: Write general MPIIO interface.
-
-  Assert(dest_data_fixed.size() == 0,
-         ExcMessage("Previously loaded data has not been released yet!"));
-
-  variable_size_data_stored = (n_attached_deserialize_variable > 0);
-
-  //
-  // ---------- Fixed size data ----------
-  //
-  {
-    const std::string fname_fixed = std::string(filename) + "_fixed.data";
-
-    MPI_Info info;
-    int      ierr = MPI_Info_create(&info);
-    AssertThrowMPI(ierr);
-
-    MPI_File fh;
-    ierr = MPI_File_open(
-      mpi_communicator, fname_fixed.c_str(), MPI_MODE_RDONLY, info, &fh);
-    AssertThrowMPI(ierr);
-
-    ierr = MPI_Info_free(&info);
-    AssertThrowMPI(ierr);
-
-    // Read cumulative sizes from file.
-    // Since all processors need the same information about the data
-    // sizes, let each of them retrieve it by reading from the same
-    // location in the file.
-    sizes_fixed_cumulative.resize(1 + n_attached_deserialize_fixed +
-                                  (variable_size_data_stored ? 1 : 0));
-    ierr =
-      Utilities::MPI::LargeCount::File_read_at_c(fh,
-                                                 0,
-                                                 sizes_fixed_cumulative.data(),
-                                                 sizes_fixed_cumulative.size(),
-                                                 MPI_UNSIGNED,
-                                                 MPI_STATUS_IGNORE);
-    AssertThrowMPI(ierr);
-
-    // Allocate sufficient memory.
-    const unsigned int bytes_per_cell = sizes_fixed_cumulative.back();
-    dest_data_fixed.resize(static_cast<size_t>(local_num_cells) *
-                           bytes_per_cell);
-
-    // Read packed data from file simultaneously.
-    const MPI_Offset size_header =
-      sizes_fixed_cumulative.size() * sizeof(unsigned int);
-
-    // Make sure we do the following computation in 64bit integers to be
-    // able to handle 4GB+ files:
-    const MPI_Offset my_global_file_position =
-      size_header + static_cast<MPI_Offset>(global_first_cell) * bytes_per_cell;
-
-    ierr = Utilities::MPI::LargeCount::File_read_at_c(fh,
-                                                      my_global_file_position,
-                                                      dest_data_fixed.data(),
-                                                      dest_data_fixed.size(),
-                                                      MPI_BYTE,
-                                                      MPI_STATUS_IGNORE);
-    AssertThrowMPI(ierr);
-
-
-    ierr = MPI_File_close(&fh);
-    AssertThrowMPI(ierr);
-  }
-
-  //
-  // ---------- Variable size data ----------
-  //
-  if (variable_size_data_stored)
-    {
-      const std::string fname_variable =
-        std::string(filename) + "_variable.data";
-
-      MPI_Info info;
-      int      ierr = MPI_Info_create(&info);
-      AssertThrowMPI(ierr);
-
-      MPI_File fh;
-      ierr = MPI_File_open(
-        mpi_communicator, fname_variable.c_str(), MPI_MODE_RDONLY, info, &fh);
-      AssertThrowMPI(ierr);
-
-      ierr = MPI_Info_free(&info);
-      AssertThrowMPI(ierr);
-
-      // Read sizes of all locally owned cells.
-      dest_sizes_variable.resize(local_num_cells);
-
-      const MPI_Offset my_global_file_position_sizes =
-        static_cast<MPI_Offset>(global_first_cell) * sizeof(unsigned int);
-
-      ierr = Utilities::MPI::LargeCount::File_read_at_c(
-        fh,
-        my_global_file_position_sizes,
-        dest_sizes_variable.data(),
-        dest_sizes_variable.size(),
-        MPI_INT,
-        MPI_STATUS_IGNORE);
-      AssertThrowMPI(ierr);
-
-
-      // Compute my data size in bytes and compute prefix sum. We do this
-      // in 64 bit to avoid overflow for files larger than 4 GB:
-      const std::uint64_t size_on_proc =
-        std::accumulate(dest_sizes_variable.begin(),
-                        dest_sizes_variable.end(),
-                        0ULL);
-
-      std::uint64_t prefix_sum = 0;
-      ierr                     = MPI_Exscan(
-        &size_on_proc, &prefix_sum, 1, MPI_UINT64_T, MPI_SUM, mpi_communicator);
-      AssertThrowMPI(ierr);
-
-      const MPI_Offset my_global_file_position =
-        static_cast<MPI_Offset>(global_num_cells) * sizeof(unsigned int) +
-        prefix_sum;
-
-      dest_data_variable.resize(size_on_proc);
-
-      ierr =
-        Utilities::MPI::LargeCount::File_read_at_c(fh,
-                                                   my_global_file_position,
-                                                   dest_data_variable.data(),
-                                                   dest_data_variable.size(),
-                                                   MPI_BYTE,
-                                                   MPI_STATUS_IGNORE);
-      AssertThrowMPI(ierr);
-
-      ierr = MPI_File_close(&fh);
-      AssertThrowMPI(ierr);
-    }
-#else
-  (void)global_first_cell;
-  (void)global_num_cells;
-  (void)local_num_cells;
-  (void)filename;
-  (void)n_attached_deserialize_fixed;
-  (void)n_attached_deserialize_variable;
-
-  AssertThrow(false, ExcNeedsMPI());
-#endif
-}
-
-
-template <int dim, int spacedim>
-DEAL_II_CXX20_REQUIRES((concepts::is_valid_dim_spacedim<dim, spacedim>))
-void DataTransfer<dim, spacedim>::clear()
-{
-  variable_size_data_stored = false;
-
-  // free information about data sizes
-  sizes_fixed_cumulative.clear();
-  sizes_fixed_cumulative.shrink_to_fit();
-
-  // free fixed size transfer data
-  src_data_fixed.clear();
-  src_data_fixed.shrink_to_fit();
-
-  dest_data_fixed.clear();
-  dest_data_fixed.shrink_to_fit();
-
-  // free variable size transfer data
-  src_sizes_variable.clear();
-  src_sizes_variable.shrink_to_fit();
-
-  src_data_variable.clear();
-  src_data_variable.shrink_to_fit();
-
-  dest_sizes_variable.clear();
-  dest_sizes_variable.shrink_to_fit();
-
-  dest_data_variable.clear();
-  dest_data_variable.shrink_to_fit();
-}
-
 DEAL_II_NAMESPACE_CLOSE
 
 #endif
index 81bc4a19a5ffad5ff1e45ff7bb26f1a8fc765bcc..a828d99bb01ef223fa7a712031b1f2c9e05c6d4d 100644 (file)
@@ -2033,6 +2033,7 @@ public:
    * @{
    */
 
+  using CellStatus = ::dealii::CellStatus;
 
   /**
    * A structure used to accumulate the results of the `weight` signal slot
@@ -3685,7 +3686,7 @@ protected:
   std::vector<typename DataTransfer<dim, spacedim>::cell_relation_t>
     local_cell_relations;
 
-  std::unique_ptr<DataTransfer<dim, spacedim>> data_transfer;
+  DataTransfer<dim, spacedim> data_transfer;
   /**
    * @}
    */
index 9640ba8d0de9ab8234124f5d793aba751e05ce3e..0d36591536f2a62c0b7b0bf439a3376c7625942a 100644 (file)
@@ -1868,13 +1868,13 @@ namespace parallel
       const typename dealii::internal::p4est::types<dim>::gloidx
         *previous_global_first_quadrant)
     {
-      Assert(this->data_transfer->sizes_fixed_cumulative.size() > 0,
+      Assert(this->data_transfer.sizes_fixed_cumulative.size() > 0,
              ExcMessage("No data has been packed!"));
 
       // Resize memory according to the data that we will receive.
-      this->data_transfer->dest_data_fixed.resize(
+      this->data_transfer.dest_data_fixed.resize(
         parallel_forest->local_num_quadrants *
-        this->data_transfer->sizes_fixed_cumulative.back());
+        this->data_transfer.sizes_fixed_cumulative.back());
 
       // Execute non-blocking fixed size transfer.
       typename dealii::internal::p4est::types<dim>::transfer_context
@@ -1885,14 +1885,14 @@ namespace parallel
           previous_global_first_quadrant,
           parallel_forest->mpicomm,
           0,
-          this->data_transfer->dest_data_fixed.data(),
-          this->data_transfer->src_data_fixed.data(),
-          this->data_transfer->sizes_fixed_cumulative.back());
+          this->data_transfer.dest_data_fixed.data(),
+          this->data_transfer.src_data_fixed.data(),
+          this->data_transfer.sizes_fixed_cumulative.back());
 
-      if (this->data_transfer->variable_size_data_stored)
+      if (this->data_transfer.variable_size_data_stored)
         {
           // Resize memory according to the data that we will receive.
-          this->data_transfer->dest_sizes_variable.resize(
+          this->data_transfer.dest_sizes_variable.resize(
             parallel_forest->local_num_quadrants);
 
           // Execute fixed size transfer of data sizes for variable size
@@ -1902,23 +1902,23 @@ namespace parallel
             previous_global_first_quadrant,
             parallel_forest->mpicomm,
             1,
-            this->data_transfer->dest_sizes_variable.data(),
-            this->data_transfer->src_sizes_variable.data(),
+            this->data_transfer.dest_sizes_variable.data(),
+            this->data_transfer.src_sizes_variable.data(),
             sizeof(unsigned int));
         }
 
       dealii::internal::p4est::functions<dim>::transfer_fixed_end(tf_context);
 
       // Release memory of previously packed data.
-      this->data_transfer->src_data_fixed.clear();
-      this->data_transfer->src_data_fixed.shrink_to_fit();
+      this->data_transfer.src_data_fixed.clear();
+      this->data_transfer.src_data_fixed.shrink_to_fit();
 
-      if (this->data_transfer->variable_size_data_stored)
+      if (this->data_transfer.variable_size_data_stored)
         {
           // Resize memory according to the data that we will receive.
-          this->data_transfer->dest_data_variable.resize(
-            std::accumulate(this->data_transfer->dest_sizes_variable.begin(),
-                            this->data_transfer->dest_sizes_variable.end(),
+          this->data_transfer.dest_data_variable.resize(
+            std::accumulate(this->data_transfer.dest_sizes_variable.begin(),
+                            this->data_transfer.dest_sizes_variable.end(),
                             std::vector<int>::size_type(0)));
 
 #  if DEAL_II_P4EST_VERSION_GTE(2, 0, 65, 0)
@@ -1928,10 +1928,10 @@ namespace parallel
           // at all, which is mandatory if one of our processes does not own
           // any quadrant. This bypasses the assertion from being triggered.
           //   - see: https://github.com/cburstedde/p4est/issues/48
-          if (this->data_transfer->src_sizes_variable.size() == 0)
-            this->data_transfer->src_sizes_variable.resize(1);
-          if (this->data_transfer->dest_sizes_variable.size() == 0)
-            this->data_transfer->dest_sizes_variable.resize(1);
+          if (this->data_transfer.src_sizes_variable.size() == 0)
+            this->data_transfer.src_sizes_variable.resize(1);
+          if (this->data_transfer.dest_sizes_variable.size() == 0)
+            this->data_transfer.dest_sizes_variable.resize(1);
 #  endif
 
           // Execute variable size transfer.
@@ -1940,16 +1940,16 @@ namespace parallel
             previous_global_first_quadrant,
             parallel_forest->mpicomm,
             1,
-            this->data_transfer->dest_data_variable.data(),
-            this->data_transfer->dest_sizes_variable.data(),
-            this->data_transfer->src_data_variable.data(),
-            this->data_transfer->src_sizes_variable.data());
+            this->data_transfer.dest_data_variable.data(),
+            this->data_transfer.dest_sizes_variable.data(),
+            this->data_transfer.src_data_variable.data(),
+            this->data_transfer.src_sizes_variable.data());
 
           // Release memory of previously packed data.
-          this->data_transfer->src_sizes_variable.clear();
-          this->data_transfer->src_sizes_variable.shrink_to_fit();
-          this->data_transfer->src_data_variable.clear();
-          this->data_transfer->src_data_variable.shrink_to_fit();
+          this->data_transfer.src_sizes_variable.clear();
+          this->data_transfer.src_sizes_variable.shrink_to_fit();
+          this->data_transfer.src_data_variable.clear();
+          this->data_transfer.src_data_variable.shrink_to_fit();
         }
     }
 
@@ -3385,7 +3385,7 @@ namespace parallel
       // pack data before triangulation gets updated
       if (this->cell_attached_data.n_attached_data_sets > 0)
         {
-          this->data_transfer->pack_data(
+          this->data_transfer.pack_data(
             this->local_cell_relations,
             this->cell_attached_data.pack_callbacks_fixed,
             this->cell_attached_data.pack_callbacks_variable);
@@ -3418,7 +3418,7 @@ namespace parallel
                                  previous_global_first_quadrant.data());
 
           // also update the CellStatus information on the new mesh
-          this->data_transfer->unpack_cell_status(this->local_cell_relations);
+          this->data_transfer.unpack_cell_status(this->local_cell_relations);
         }
 
 #  ifdef DEBUG
@@ -3558,7 +3558,7 @@ namespace parallel
       // pack data before triangulation gets updated
       if (this->cell_attached_data.n_attached_data_sets > 0)
         {
-          this->data_transfer->pack_data(
+          this->data_transfer.pack_data(
             this->local_cell_relations,
             this->cell_attached_data.pack_callbacks_fixed,
             this->cell_attached_data.pack_callbacks_variable);
index 3d70ba8095710f43f9019e8eb33da461829b2e92..fb2c6554b6fb3f2f19f29c6747ead0374f2c0bf6 100644 (file)
@@ -32,6 +32,7 @@ endif()
 
 set(_unity_include_src
   cell_id.cc
+  data_transfer.cc
   grid_refinement.cc
   intergrid_map.cc
   manifold.cc
@@ -70,6 +71,7 @@ setup_source_list("${_unity_include_src}"
 
 set(_inst
   cell_id.inst.in
+  data_transfer.inst.in
   grid_generator_cgal.inst.in
   grid_generator_from_name.inst.in
   grid_generator.inst.in
diff --git a/source/grid/data_transfer.cc b/source/grid/data_transfer.cc
new file mode 100644 (file)
index 0000000..0f9c6c8
--- /dev/null
@@ -0,0 +1,1029 @@
+// ---------------------------------------------------------------------
+//
+// Copyright (C) 1998 - 2023 by the deal.II authors
+//
+// This file is part of the deal.II library.
+//
+// The deal.II library is free software; you can use it, redistribute
+// it, and/or modify it under the terms of the GNU Lesser General
+// Public License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+// The full text of the license can be found in the file LICENSE.md at
+// the top level directory of deal.II.
+//
+// ---------------------------------------------------------------------
+
+#include <deal.II/base/config.h>
+
+#include <deal.II/base/geometry_info.h>
+
+#include <deal.II/grid/data_transfer.h>
+#include <deal.II/grid/tria_accessor.h>
+
+#include <utility>
+#include <vector>
+
+DEAL_II_NAMESPACE_OPEN
+
+template <int dim, int spacedim>
+DEAL_II_CXX20_REQUIRES((concepts::is_valid_dim_spacedim<dim, spacedim>))
+DataTransfer<dim, spacedim>::DataTransfer(const MPI_Comm &mpi_communicator)
+  : variable_size_data_stored(false)
+  , mpi_communicator(mpi_communicator)
+{}
+
+
+template <int dim, int spacedim>
+DEAL_II_CXX20_REQUIRES((concepts::is_valid_dim_spacedim<dim, spacedim>))
+void DataTransfer<dim, spacedim>::pack_data(
+  const std::vector<cell_relation_t> &cell_relations,
+  const std::vector<typename CellAttachedData<dim, spacedim>::pack_callback_t>
+    &pack_callbacks_fixed,
+  const std::vector<typename CellAttachedData<dim, spacedim>::pack_callback_t>
+    &pack_callbacks_variable)
+{
+  Assert(src_data_fixed.size() == 0,
+         ExcMessage("Previously packed data has not been released yet!"));
+  Assert(src_sizes_variable.size() == 0, ExcInternalError());
+
+  const unsigned int n_callbacks_fixed    = pack_callbacks_fixed.size();
+  const unsigned int n_callbacks_variable = pack_callbacks_variable.size();
+
+  // Store information that we packed variable size data in
+  // a member variable for later.
+  variable_size_data_stored = (n_callbacks_variable > 0);
+
+  // If variable transfer is scheduled, we will store the data size that
+  // each variable size callback function writes in this auxiliary
+  // container. The information will be stored by each cell in this vector
+  // temporarily.
+  std::vector<unsigned int> cell_sizes_variable_cumulative(
+    n_callbacks_variable);
+
+  // Prepare the buffer structure, in which each callback function will
+  // store its data for each active cell.
+  // The outmost shell in this container construct corresponds to the
+  // data packed per cell. The next layer resembles the data that
+  // each callback function packs on the corresponding cell. These
+  // buffers are chains of chars stored in an std::vector<char>.
+  // A visualisation of the data structure:
+  /* clang-format off */
+      // |             cell_1                | |             cell_2                | ...
+      // ||  callback_1  ||  callback_2  |...| ||  callback_1  ||  callback_2  |...| ...
+      // |||char|char|...|||char|char|...|...| |||char|char|...|||char|char|...|...| ...
+  /* clang-format on */
+  std::vector<std::vector<std::vector<char>>> packed_fixed_size_data(
+    cell_relations.size());
+  std::vector<std::vector<std::vector<char>>> packed_variable_size_data(
+    variable_size_data_stored ? cell_relations.size() : 0);
+
+  //
+  // --------- Pack data for fixed and variable size transfer ---------
+  //
+  // Iterate over all cells, call all callback functions on each cell,
+  // and store their data in the corresponding buffer scope.
+  {
+    auto cell_rel_it           = cell_relations.cbegin();
+    auto data_cell_fixed_it    = packed_fixed_size_data.begin();
+    auto data_cell_variable_it = packed_variable_size_data.begin();
+    for (; cell_rel_it != cell_relations.cend(); ++cell_rel_it)
+      {
+        const auto &dealii_cell = cell_rel_it->first;
+        const auto &cell_status = cell_rel_it->second;
+
+        // Assertions about the tree structure.
+        switch (cell_status)
+          {
+            case CELL_PERSIST:
+            case CELL_REFINE:
+              // double check the condition that we will only ever attach
+              // data to active cells when we get here
+              Assert(dealii_cell->is_active(), ExcInternalError());
+              break;
+
+            case CELL_COARSEN:
+              // double check the condition that we will only ever attach
+              // data to cells with children when we get here. however, we
+              // can only tolerate one level of coarsening at a time, so
+              // check that the children are all active
+              Assert(dealii_cell->is_active() == false, ExcInternalError());
+              for (unsigned int c = 0;
+                   c < GeometryInfo<dim>::max_children_per_cell;
+                   ++c)
+                Assert(dealii_cell->child(c)->is_active(), ExcInternalError());
+              break;
+
+            case CELL_INVALID:
+              // do nothing on invalid cells
+              break;
+
+            default:
+              Assert(false, ExcInternalError());
+              break;
+          }
+
+        // Reserve memory corresponding to the number of callback
+        // functions that will be called.
+        // If variable size transfer is scheduled, we need to leave
+        // room for an array that holds information about how many
+        // bytes each of the variable size callback functions will
+        // write.
+        // On cells flagged with CELL_INVALID, only its CellStatus
+        // will be stored.
+        const unsigned int n_fixed_size_data_sets_on_cell =
+          1 + ((cell_status == CELL_INVALID) ?
+                 0 :
+                 ((variable_size_data_stored ? 1 : 0) + n_callbacks_fixed));
+        data_cell_fixed_it->resize(n_fixed_size_data_sets_on_cell);
+
+        // We continue with packing all data on this specific cell.
+        auto data_fixed_it = data_cell_fixed_it->begin();
+
+        // First, we pack the CellStatus information.
+        // to get consistent data sizes on each cell for the fixed size
+        // transfer, we won't allow compression
+        *data_fixed_it =
+          Utilities::pack(cell_status, /*allow_compression=*/false);
+        ++data_fixed_it;
+
+        // Proceed with all registered callback functions.
+        // Skip cells with the CELL_INVALID flag.
+        if (cell_status != CELL_INVALID)
+          {
+            // Pack fixed size data.
+            for (auto callback_it = pack_callbacks_fixed.cbegin();
+                 callback_it != pack_callbacks_fixed.cend();
+                 ++callback_it, ++data_fixed_it)
+              {
+                *data_fixed_it = (*callback_it)(dealii_cell, cell_status);
+              }
+
+            // Pack variable size data.
+            // If we store variable size data, we need to transfer
+            // the sizes of each corresponding callback function
+            // via fixed size transfer as well.
+            if (variable_size_data_stored)
+              {
+                const unsigned int n_variable_size_data_sets_on_cell =
+                  ((cell_status == CELL_INVALID) ? 0 : n_callbacks_variable);
+                data_cell_variable_it->resize(
+                  n_variable_size_data_sets_on_cell);
+
+                auto callback_it       = pack_callbacks_variable.cbegin();
+                auto data_variable_it  = data_cell_variable_it->begin();
+                auto sizes_variable_it = cell_sizes_variable_cumulative.begin();
+                for (; callback_it != pack_callbacks_variable.cend();
+                     ++callback_it, ++data_variable_it, ++sizes_variable_it)
+                  {
+                    *data_variable_it =
+                      (*callback_it)(dealii_cell, cell_status);
+
+                    // Store data sizes for each callback function first.
+                    // Make it cumulative below.
+                    *sizes_variable_it = data_variable_it->size();
+                  }
+
+                // Turn size vector into its cumulative representation.
+                std::partial_sum(cell_sizes_variable_cumulative.begin(),
+                                 cell_sizes_variable_cumulative.end(),
+                                 cell_sizes_variable_cumulative.begin());
+
+                // Serialize cumulative variable size vector
+                // value-by-value. This way we can circumvent the overhead
+                // of storing the container object as a whole, since we
+                // know its size by the number of registered callback
+                // functions.
+                data_fixed_it->resize(n_callbacks_variable *
+                                      sizeof(unsigned int));
+                for (unsigned int i = 0; i < n_callbacks_variable; ++i)
+                  std::memcpy(&(data_fixed_it->at(i * sizeof(unsigned int))),
+                              &(cell_sizes_variable_cumulative.at(i)),
+                              sizeof(unsigned int));
+
+                ++data_fixed_it;
+              }
+
+            // Double check that we packed everything we wanted
+            // in the fixed size buffers.
+            Assert(data_fixed_it == data_cell_fixed_it->end(),
+                   ExcInternalError());
+          }
+
+        ++data_cell_fixed_it;
+
+        // Increment the variable size data iterator
+        // only if we actually pack this kind of data
+        // to avoid getting out of bounds.
+        if (variable_size_data_stored)
+          ++data_cell_variable_it;
+      } // loop over cell_relations
+  }
+
+  //
+  // ----------- Gather data sizes for fixed size transfer ------------
+  //
+  // Generate a vector which stores the sizes of each callback function,
+  // including the packed CellStatus transfer.
+  // Find the very first cell that we wrote to with all callback
+  // functions (i.e. a cell that was not flagged with CELL_INVALID)
+  // and store the sizes of each buffer.
+  //
+  // To deal with the case that at least one of the processors does not
+  // own any cell at all, we will exchange the information about the data
+  // sizes among them later. The code in between is still well-defined,
+  // since the following loops will be skipped.
+  std::vector<unsigned int> local_sizes_fixed(
+    1 + n_callbacks_fixed + (variable_size_data_stored ? 1 : 0));
+  for (const auto &data_cell : packed_fixed_size_data)
+    {
+      if (data_cell.size() == local_sizes_fixed.size())
+        {
+          auto sizes_fixed_it = local_sizes_fixed.begin();
+          auto data_fixed_it  = data_cell.cbegin();
+          for (; data_fixed_it != data_cell.cend();
+               ++data_fixed_it, ++sizes_fixed_it)
+            {
+              *sizes_fixed_it = data_fixed_it->size();
+            }
+
+          break;
+        }
+    }
+
+  // Check if all cells have valid sizes.
+  for (auto data_cell_fixed_it = packed_fixed_size_data.cbegin();
+       data_cell_fixed_it != packed_fixed_size_data.cend();
+       ++data_cell_fixed_it)
+    {
+      Assert((data_cell_fixed_it->size() == 1) ||
+               (data_cell_fixed_it->size() == local_sizes_fixed.size()),
+             ExcInternalError());
+    }
+
+  // Share information about the packed data sizes
+  // of all callback functions across all processors, in case one
+  // of them does not own any cells at all.
+  std::vector<unsigned int> global_sizes_fixed(local_sizes_fixed.size());
+  Utilities::MPI::max(local_sizes_fixed,
+                      this->mpi_communicator,
+                      global_sizes_fixed);
+
+  // Construct cumulative sizes, since this is the only information
+  // we need from now on.
+  sizes_fixed_cumulative.resize(global_sizes_fixed.size());
+  std::partial_sum(global_sizes_fixed.begin(),
+                   global_sizes_fixed.end(),
+                   sizes_fixed_cumulative.begin());
+
+  //
+  // ---------- Gather data sizes for variable size transfer ----------
+  //
+  if (variable_size_data_stored)
+    {
+      src_sizes_variable.reserve(packed_variable_size_data.size());
+      for (const auto &data_cell : packed_variable_size_data)
+        {
+          int variable_data_size_on_cell = 0;
+
+          for (const auto &data : data_cell)
+            variable_data_size_on_cell += data.size();
+
+          src_sizes_variable.push_back(variable_data_size_on_cell);
+        }
+    }
+
+  //
+  // ------------------------ Build buffers ---------------------------
+  //
+  const unsigned int expected_size_fixed =
+    cell_relations.size() * sizes_fixed_cumulative.back();
+  const unsigned int expected_size_variable =
+    std::accumulate(src_sizes_variable.begin(),
+                    src_sizes_variable.end(),
+                    std::vector<int>::size_type(0));
+
+  // Move every piece of packed fixed size data into the consecutive
+  // buffer.
+  src_data_fixed.reserve(expected_size_fixed);
+  for (const auto &data_cell_fixed : packed_fixed_size_data)
+    {
+      // Move every fraction of packed data into the buffer
+      // reserved for this particular cell.
+      for (const auto &data_fixed : data_cell_fixed)
+        std::move(data_fixed.begin(),
+                  data_fixed.end(),
+                  std::back_inserter(src_data_fixed));
+
+      // If we only packed the CellStatus information
+      // (i.e. encountered a cell flagged CELL_INVALID),
+      // fill the remaining space with invalid entries.
+      // We can skip this if there is nothing else to pack.
+      if ((data_cell_fixed.size() == 1) && (sizes_fixed_cumulative.size() > 1))
+        {
+          const std::size_t bytes_skipped =
+            sizes_fixed_cumulative.back() - sizes_fixed_cumulative.front();
+
+          src_data_fixed.insert(src_data_fixed.end(),
+                                bytes_skipped,
+                                static_cast<char>(-1)); // invalid_char
+        }
+    }
+
+  // Move every piece of packed variable size data into the consecutive
+  // buffer.
+  if (variable_size_data_stored)
+    {
+      src_data_variable.reserve(expected_size_variable);
+      for (const auto &data_cell : packed_variable_size_data)
+        {
+          // Move every fraction of packed data into the buffer
+          // reserved for this particular cell.
+          for (const auto &data : data_cell)
+            std::move(data.begin(),
+                      data.end(),
+                      std::back_inserter(src_data_variable));
+        }
+    }
+
+  // Double check that we packed everything correctly.
+  Assert(src_data_fixed.size() == expected_size_fixed, ExcInternalError());
+  Assert(src_data_variable.size() == expected_size_variable,
+         ExcInternalError());
+}
+
+
+
+template <int dim, int spacedim>
+DEAL_II_CXX20_REQUIRES((concepts::is_valid_dim_spacedim<dim, spacedim>))
+void DataTransfer<dim, spacedim>::unpack_cell_status(
+  std::vector<typename DataTransfer<dim, spacedim>::cell_relation_t>
+    &cell_relations) const
+{
+  Assert(sizes_fixed_cumulative.size() > 0,
+         ExcMessage("No data has been packed!"));
+  if (cell_relations.size() > 0)
+    {
+      Assert(dest_data_fixed.size() > 0,
+             ExcMessage("No data has been received!"));
+    }
+
+  // Size of CellStatus object that will be unpacked on each cell.
+  const unsigned int size = sizes_fixed_cumulative.front();
+
+  // Iterate over all cells and overwrite the CellStatus
+  // information from the transferred data.
+  // Proceed buffer iterator position to next cell after
+  // each iteration.
+  auto cell_rel_it   = cell_relations.begin();
+  auto dest_fixed_it = dest_data_fixed.cbegin();
+  for (; cell_rel_it != cell_relations.end();
+       ++cell_rel_it, dest_fixed_it += sizes_fixed_cumulative.back())
+    {
+      cell_rel_it->second = // cell_status
+        Utilities::unpack<CellStatus>(dest_fixed_it,
+                                      dest_fixed_it + size,
+                                      /*allow_compression=*/false);
+    }
+}
+
+
+
+template <int dim, int spacedim>
+DEAL_II_CXX20_REQUIRES((concepts::is_valid_dim_spacedim<dim, spacedim>))
+void DataTransfer<dim, spacedim>::unpack_data(
+  const std::vector<typename DataTransfer<dim, spacedim>::cell_relation_t>
+    &                cell_relations,
+  const unsigned int handle,
+  const std::function<
+    void(const cell_iterator &,
+         const CellStatus &,
+         const boost::iterator_range<std::vector<char>::const_iterator> &)>
+    &unpack_callback) const
+{
+  // We decode the handle returned by register_data_attach() back into
+  // a format we can use. All even handles belong to those callback
+  // functions which write/read variable size data, all odd handles
+  // interact with fixed size buffers.
+  const bool         callback_variable_transfer = (handle % 2 == 0);
+  const unsigned int callback_index             = handle / 2;
+
+  // Cells will always receive fixed size data (i.e., CellStatus
+  // information), but not necessarily variable size data (e.g., with a
+  // ParticleHandler a cell might not contain any particle at all).
+  // Thus it is sufficient to check if fixed size data has been received.
+  Assert(sizes_fixed_cumulative.size() > 0,
+         ExcMessage("No data has been packed!"));
+  if (cell_relations.size() > 0)
+    {
+      Assert(dest_data_fixed.size() > 0,
+             ExcMessage("No data has been received!"));
+    }
+
+  std::vector<char>::const_iterator dest_data_it;
+  std::vector<char>::const_iterator dest_sizes_cell_it;
+
+  // Depending on whether our callback function unpacks fixed or
+  // variable size data, we have to pursue different approaches
+  // to localize the correct fraction of the buffer from which
+  // we are allowed to read.
+  unsigned int offset         = numbers::invalid_unsigned_int;
+  unsigned int size           = numbers::invalid_unsigned_int;
+  unsigned int data_increment = numbers::invalid_unsigned_int;
+
+  if (callback_variable_transfer)
+    {
+      // For the variable size data, we need to extract the
+      // data size from the fixed size buffer on each cell.
+      //
+      // We packed this information last, so the last packed
+      // object in the fixed size buffer corresponds to the
+      // variable data sizes.
+      //
+      // The last entry of sizes_fixed_cumulative corresponds
+      // to the size of all fixed size data packed on the cell.
+      // To get the offset for the last packed object, we need
+      // to get the next-to-last entry.
+      const unsigned int offset_variable_data_sizes =
+        sizes_fixed_cumulative[sizes_fixed_cumulative.size() - 2];
+
+      // This iterator points to the data size that the
+      // callback_function packed for each specific cell.
+      // Adjust buffer iterator to the offset of the callback
+      // function so that we only have to advance its position
+      // to the next cell after each iteration.
+      dest_sizes_cell_it = dest_data_fixed.cbegin() +
+                           offset_variable_data_sizes +
+                           callback_index * sizeof(unsigned int);
+
+      // Let the data iterator point to the correct buffer.
+      dest_data_it = dest_data_variable.cbegin();
+    }
+  else
+    {
+      // For the fixed size data, we can get the information about
+      // the buffer location on each cell directly from the
+      // sizes_fixed_cumulative vector.
+      offset         = sizes_fixed_cumulative[callback_index];
+      size           = sizes_fixed_cumulative[callback_index + 1] - offset;
+      data_increment = sizes_fixed_cumulative.back();
+
+      // Let the data iterator point to the correct buffer.
+      // Adjust buffer iterator to the offset of the callback
+      // function so that we only have to advance its position
+      // to the next cell after each iteration.
+      if (cell_relations.begin() != cell_relations.end())
+        dest_data_it = dest_data_fixed.cbegin() + offset;
+    }
+
+  // Iterate over all cells and unpack the transferred data.
+  auto cell_rel_it   = cell_relations.begin();
+  auto dest_sizes_it = dest_sizes_variable.cbegin();
+  for (; cell_rel_it != cell_relations.end(); ++cell_rel_it)
+    {
+      const auto &dealii_cell = cell_rel_it->first;
+      const auto &cell_status = cell_rel_it->second;
+
+      if (callback_variable_transfer)
+        {
+          // Update the increment according to the whole data size
+          // of the current cell.
+          data_increment = *dest_sizes_it;
+
+          if (cell_status != CELL_INVALID)
+            {
+              // Extract the corresponding values for offset and size from
+              // the cumulative sizes array stored in the fixed size
+              // buffer.
+              if (callback_index == 0)
+                offset = 0;
+              else
+                std::memcpy(&offset,
+                            &(*(dest_sizes_cell_it - sizeof(unsigned int))),
+                            sizeof(unsigned int));
+
+              std::memcpy(&size, &(*dest_sizes_cell_it), sizeof(unsigned int));
+
+              size -= offset;
+
+              // Move the data iterator to the corresponding position
+              // of the callback function and adjust the increment
+              // accordingly.
+              dest_data_it += offset;
+              data_increment -= offset;
+            }
+
+          // Advance data size iterators to the next cell, avoid iterating
+          // past the end of dest_sizes_cell_it
+          if (cell_rel_it != cell_relations.end() - 1)
+            dest_sizes_cell_it += sizes_fixed_cumulative.back();
+          ++dest_sizes_it;
+        }
+
+      switch (cell_status)
+        {
+          case CELL_PERSIST:
+          case CELL_COARSEN:
+            unpack_callback(dealii_cell,
+                            cell_status,
+                            boost::make_iterator_range(dest_data_it,
+                                                       dest_data_it + size));
+            break;
+
+          case CELL_REFINE:
+            unpack_callback(dealii_cell->parent(),
+                            cell_status,
+                            boost::make_iterator_range(dest_data_it,
+                                                       dest_data_it + size));
+            break;
+
+          case CELL_INVALID:
+            // Skip this cell.
+            break;
+
+          default:
+            Assert(false, ExcInternalError());
+            break;
+        }
+
+      if (cell_rel_it != cell_relations.end() - 1)
+        dest_data_it += data_increment;
+    }
+}
+
+
+
+template <int dim, int spacedim>
+DEAL_II_CXX20_REQUIRES((concepts::is_valid_dim_spacedim<dim, spacedim>))
+void DataTransfer<dim, spacedim>::save(const unsigned int global_first_cell,
+                                       const unsigned int global_num_cells,
+                                       const std::string &filename) const
+{
+  Assert(sizes_fixed_cumulative.size() > 0,
+         ExcMessage("No data has been packed!"));
+
+#ifdef DEAL_II_WITH_MPI
+  // Large fractions of this function have been copied from
+  // DataOutInterface::write_vtu_in_parallel.
+  // TODO: Write general MPIIO interface.
+
+  const int myrank  = Utilities::MPI::this_mpi_process(mpi_communicator);
+  const int mpisize = Utilities::MPI::n_mpi_processes(mpi_communicator);
+
+  if (mpisize > 1)
+    {
+      const unsigned int bytes_per_cell = sizes_fixed_cumulative.back();
+
+      //
+      // ---------- Fixed size data ----------
+      //
+      {
+        const std::string fname_fixed = std::string(filename) + "_fixed.data";
+
+        MPI_Info info;
+        int      ierr = MPI_Info_create(&info);
+        AssertThrowMPI(ierr);
+
+        MPI_File fh;
+        ierr = MPI_File_open(mpi_communicator,
+                             fname_fixed.c_str(),
+                             MPI_MODE_CREATE | MPI_MODE_WRONLY,
+                             info,
+                             &fh);
+        AssertThrowMPI(ierr);
+
+        ierr = MPI_File_set_size(fh, 0); // delete the file contents
+        AssertThrowMPI(ierr);
+        // this barrier is necessary, because otherwise others might already
+        // write while one core is still setting the size to zero.
+        ierr = MPI_Barrier(mpi_communicator);
+        AssertThrowMPI(ierr);
+        ierr = MPI_Info_free(&info);
+        AssertThrowMPI(ierr);
+        // ------------------
+
+        // Write cumulative sizes to file.
+        // Since each processor owns the same information about the data
+        // sizes, it is sufficient to let only the first processor perform
+        // this task.
+        if (myrank == 0)
+          {
+            ierr = Utilities::MPI::LargeCount::File_write_at_c(
+              fh,
+              0,
+              sizes_fixed_cumulative.data(),
+              sizes_fixed_cumulative.size(),
+              MPI_UNSIGNED,
+              MPI_STATUS_IGNORE);
+            AssertThrowMPI(ierr);
+          }
+
+        // Write packed data to file simultaneously.
+        const MPI_Offset size_header =
+          sizes_fixed_cumulative.size() * sizeof(unsigned int);
+
+        // Make sure we do the following computation in 64bit integers to be
+        // able to handle 4GB+ files:
+        const MPI_Offset my_global_file_position =
+          size_header +
+          static_cast<MPI_Offset>(global_first_cell) * bytes_per_cell;
+
+        ierr =
+          Utilities::MPI::LargeCount::File_write_at_c(fh,
+                                                      my_global_file_position,
+                                                      src_data_fixed.data(),
+                                                      src_data_fixed.size(),
+                                                      MPI_BYTE,
+                                                      MPI_STATUS_IGNORE);
+        AssertThrowMPI(ierr);
+
+        ierr = MPI_File_close(&fh);
+        AssertThrowMPI(ierr);
+      }
+
+
+
+      //
+      // ---------- Variable size data ----------
+      //
+      if (variable_size_data_stored)
+        {
+          const std::string fname_variable =
+            std::string(filename) + "_variable.data";
+
+          MPI_Info info;
+          int      ierr = MPI_Info_create(&info);
+          AssertThrowMPI(ierr);
+
+          MPI_File fh;
+          ierr = MPI_File_open(mpi_communicator,
+                               fname_variable.c_str(),
+                               MPI_MODE_CREATE | MPI_MODE_WRONLY,
+                               info,
+                               &fh);
+          AssertThrowMPI(ierr);
+
+          ierr = MPI_File_set_size(fh, 0); // delete the file contents
+          AssertThrowMPI(ierr);
+          // this barrier is necessary, because otherwise others might already
+          // write while one core is still setting the size to zero.
+          ierr = MPI_Barrier(mpi_communicator);
+          AssertThrowMPI(ierr);
+          ierr = MPI_Info_free(&info);
+          AssertThrowMPI(ierr);
+
+          // Write sizes of each cell into file simultaneously.
+          {
+            const MPI_Offset my_global_file_position =
+              static_cast<MPI_Offset>(global_first_cell) * sizeof(unsigned int);
+
+            // It is very unlikely that a single process has more than
+            // 2 billion cells, but we might as well check.
+            AssertThrow(src_sizes_variable.size() <
+                          static_cast<std::size_t>(
+                            std::numeric_limits<int>::max()),
+                        ExcNotImplemented());
+
+            ierr = Utilities::MPI::LargeCount::File_write_at_c(
+              fh,
+              my_global_file_position,
+              src_sizes_variable.data(),
+              src_sizes_variable.size(),
+              MPI_INT,
+              MPI_STATUS_IGNORE);
+            AssertThrowMPI(ierr);
+          }
+
+          // Gather size of data in bytes we want to store from this
+          // processor and compute the prefix sum. We do this in 64 bit
+          // to avoid overflow for files larger than 4GB:
+          const std::uint64_t size_on_proc = src_data_variable.size();
+          std::uint64_t       prefix_sum   = 0;
+          ierr                             = MPI_Exscan(&size_on_proc,
+                            &prefix_sum,
+                            1,
+                            MPI_UINT64_T,
+                            MPI_SUM,
+                            mpi_communicator);
+          AssertThrowMPI(ierr);
+
+          const MPI_Offset my_global_file_position =
+            static_cast<MPI_Offset>(global_num_cells) * sizeof(unsigned int) +
+            prefix_sum;
+
+          // Write data consecutively into file.
+          ierr = Utilities::MPI::LargeCount::File_write_at_c(
+            fh,
+            my_global_file_position,
+            src_data_variable.data(),
+            src_data_variable.size(),
+            MPI_BYTE,
+            MPI_STATUS_IGNORE);
+          AssertThrowMPI(ierr);
+
+
+          ierr = MPI_File_close(&fh);
+          AssertThrowMPI(ierr);
+        }
+    } // if (mpisize > 1)
+  else
+#endif
+    {
+      (void)global_first_cell;
+      (void)global_num_cells;
+
+      //
+      // ---------- Fixed size data ----------
+      //
+      {
+        const std::string fname_fixed = std::string(filename) + "_fixed.data";
+
+        std::ofstream file(fname_fixed, std::ios::binary | std::ios::out);
+
+        // Write header data.
+        file.write(reinterpret_cast<const char *>(
+                     sizes_fixed_cumulative.data()),
+                   sizes_fixed_cumulative.size() * sizeof(unsigned int));
+
+        // Write packed data.
+        file.write(reinterpret_cast<const char *>(src_data_fixed.data()),
+                   src_data_fixed.size() * sizeof(char));
+
+        file.close();
+      }
+
+      //
+      // ---------- Variable size data ----------
+      //
+      if (variable_size_data_stored)
+        {
+          const std::string fname_variable =
+            std::string(filename) + "_variable.data";
+
+          std::ofstream file(fname_variable, std::ios::binary | std::ios::out);
+
+          // Write header data.
+          file.write(reinterpret_cast<const char *>(src_sizes_variable.data()),
+                     src_sizes_variable.size() * sizeof(int));
+
+          // Write packed data.
+          file.write(reinterpret_cast<const char *>(src_data_variable.data()),
+                     src_data_variable.size() * sizeof(char));
+
+          file.close();
+        }
+    }
+}
+
+
+
+template <int dim, int spacedim>
+DEAL_II_CXX20_REQUIRES((concepts::is_valid_dim_spacedim<dim, spacedim>))
+void DataTransfer<dim, spacedim>::load(
+  const unsigned int global_first_cell,
+  const unsigned int global_num_cells,
+  const unsigned int local_num_cells,
+  const std::string &filename,
+  const unsigned int n_attached_deserialize_fixed,
+  const unsigned int n_attached_deserialize_variable)
+{
+  Assert(dest_data_fixed.size() == 0,
+         ExcMessage("Previously loaded data has not been released yet!"));
+
+  variable_size_data_stored = (n_attached_deserialize_variable > 0);
+
+#ifdef DEAL_II_WITH_MPI
+  // Large fractions of this function have been copied from
+  // DataOutInterface::write_vtu_in_parallel.
+  // TODO: Write general MPIIO interface.
+
+  const int mpisize = Utilities::MPI::n_mpi_processes(mpi_communicator);
+
+  if (mpisize > 1)
+    {
+      //
+      // ---------- Fixed size data ----------
+      //
+      {
+        const std::string fname_fixed = std::string(filename) + "_fixed.data";
+
+        MPI_Info info;
+        int      ierr = MPI_Info_create(&info);
+        AssertThrowMPI(ierr);
+
+        MPI_File fh;
+        ierr = MPI_File_open(
+          mpi_communicator, fname_fixed.c_str(), MPI_MODE_RDONLY, info, &fh);
+        AssertThrowMPI(ierr);
+
+        ierr = MPI_Info_free(&info);
+        AssertThrowMPI(ierr);
+
+        // Read cumulative sizes from file.
+        // Since all processors need the same information about the data
+        // sizes, let each of them retrieve it by reading from the same
+        // location in the file.
+        sizes_fixed_cumulative.resize(1 + n_attached_deserialize_fixed +
+                                      (variable_size_data_stored ? 1 : 0));
+        ierr = Utilities::MPI::LargeCount::File_read_at_c(
+          fh,
+          0,
+          sizes_fixed_cumulative.data(),
+          sizes_fixed_cumulative.size(),
+          MPI_UNSIGNED,
+          MPI_STATUS_IGNORE);
+        AssertThrowMPI(ierr);
+
+        // Allocate sufficient memory.
+        const unsigned int bytes_per_cell = sizes_fixed_cumulative.back();
+        dest_data_fixed.resize(static_cast<size_t>(local_num_cells) *
+                               bytes_per_cell);
+
+        // Read packed data from file simultaneously.
+        const MPI_Offset size_header =
+          sizes_fixed_cumulative.size() * sizeof(unsigned int);
+
+        // Make sure we do the following computation in 64bit integers to be
+        // able to handle 4GB+ files:
+        const MPI_Offset my_global_file_position =
+          size_header +
+          static_cast<MPI_Offset>(global_first_cell) * bytes_per_cell;
+
+        ierr =
+          Utilities::MPI::LargeCount::File_read_at_c(fh,
+                                                     my_global_file_position,
+                                                     dest_data_fixed.data(),
+                                                     dest_data_fixed.size(),
+                                                     MPI_BYTE,
+                                                     MPI_STATUS_IGNORE);
+        AssertThrowMPI(ierr);
+
+
+        ierr = MPI_File_close(&fh);
+        AssertThrowMPI(ierr);
+      }
+
+      //
+      // ---------- Variable size data ----------
+      //
+      if (variable_size_data_stored)
+        {
+          const std::string fname_variable =
+            std::string(filename) + "_variable.data";
+
+          MPI_Info info;
+          int      ierr = MPI_Info_create(&info);
+          AssertThrowMPI(ierr);
+
+          MPI_File fh;
+          ierr = MPI_File_open(mpi_communicator,
+                               fname_variable.c_str(),
+                               MPI_MODE_RDONLY,
+                               info,
+                               &fh);
+          AssertThrowMPI(ierr);
+
+          ierr = MPI_Info_free(&info);
+          AssertThrowMPI(ierr);
+
+          // Read sizes of all locally owned cells.
+          dest_sizes_variable.resize(local_num_cells);
+
+          const MPI_Offset my_global_file_position_sizes =
+            static_cast<MPI_Offset>(global_first_cell) * sizeof(unsigned int);
+
+          ierr = Utilities::MPI::LargeCount::File_read_at_c(
+            fh,
+            my_global_file_position_sizes,
+            dest_sizes_variable.data(),
+            dest_sizes_variable.size(),
+            MPI_INT,
+            MPI_STATUS_IGNORE);
+          AssertThrowMPI(ierr);
+
+
+          // Compute my data size in bytes and compute prefix sum. We do this
+          // in 64 bit to avoid overflow for files larger than 4 GB:
+          const std::uint64_t size_on_proc =
+            std::accumulate(dest_sizes_variable.begin(),
+                            dest_sizes_variable.end(),
+                            0ULL);
+
+          std::uint64_t prefix_sum = 0;
+          ierr                     = MPI_Exscan(&size_on_proc,
+                            &prefix_sum,
+                            1,
+                            MPI_UINT64_T,
+                            MPI_SUM,
+                            mpi_communicator);
+          AssertThrowMPI(ierr);
+
+          const MPI_Offset my_global_file_position =
+            static_cast<MPI_Offset>(global_num_cells) * sizeof(unsigned int) +
+            prefix_sum;
+
+          dest_data_variable.resize(size_on_proc);
+
+          ierr = Utilities::MPI::LargeCount::File_read_at_c(
+            fh,
+            my_global_file_position,
+            dest_data_variable.data(),
+            dest_data_variable.size(),
+            MPI_BYTE,
+            MPI_STATUS_IGNORE);
+          AssertThrowMPI(ierr);
+
+          ierr = MPI_File_close(&fh);
+          AssertThrowMPI(ierr);
+        }
+    }
+  else // if (mpisize > 1)
+#endif
+    {
+      (void)global_first_cell;
+      (void)global_num_cells;
+      (void)local_num_cells;
+
+      //
+      // ---------- Fixed size data ----------
+      //
+      {
+        const std::string fname_fixed = std::string(filename) + "_fixed.data";
+
+        std::ifstream file(fname_fixed, std::ios::binary | std::ios::in);
+        sizes_fixed_cumulative.resize(1 + n_attached_deserialize_fixed +
+                                      (variable_size_data_stored ? 1 : 0));
+
+        // Read header data.
+        file.read(reinterpret_cast<char *>(sizes_fixed_cumulative.data()),
+                  sizes_fixed_cumulative.size() * sizeof(unsigned int));
+
+        const unsigned int bytes_per_cell = sizes_fixed_cumulative.back();
+        dest_data_fixed.resize(static_cast<size_t>(local_num_cells) *
+                               bytes_per_cell);
+
+        // Read packed data.
+        file.read(reinterpret_cast<char *>(dest_data_fixed.data()),
+                  dest_data_fixed.size() * sizeof(char));
+
+        file.close();
+      }
+
+      //
+      // ---------- Variable size data ----------
+      //
+      if (variable_size_data_stored)
+        {
+          const std::string fname_variable =
+            std::string(filename) + "_variable.data";
+
+          std::ifstream file(fname_variable, std::ios::binary | std::ios::in);
+
+          // Read header data.
+          file.read(reinterpret_cast<char *>(dest_sizes_variable.data()),
+                    dest_sizes_variable.size() * sizeof(int));
+
+          // Read packed data.
+          file.read(reinterpret_cast<char *>(dest_data_variable.data()),
+                    dest_data_variable.size() * sizeof(char));
+
+          file.close();
+        }
+    }
+}
+
+
+template <int dim, int spacedim>
+DEAL_II_CXX20_REQUIRES((concepts::is_valid_dim_spacedim<dim, spacedim>))
+void DataTransfer<dim, spacedim>::clear()
+{
+  variable_size_data_stored = false;
+
+  // free information about data sizes
+  sizes_fixed_cumulative.clear();
+  sizes_fixed_cumulative.shrink_to_fit();
+
+  // free fixed size transfer data
+  src_data_fixed.clear();
+  src_data_fixed.shrink_to_fit();
+
+  dest_data_fixed.clear();
+  dest_data_fixed.shrink_to_fit();
+
+  // free variable size transfer data
+  src_sizes_variable.clear();
+  src_sizes_variable.shrink_to_fit();
+
+  src_data_variable.clear();
+  src_data_variable.shrink_to_fit();
+
+  dest_sizes_variable.clear();
+  dest_sizes_variable.shrink_to_fit();
+
+  dest_data_variable.clear();
+  dest_data_variable.shrink_to_fit();
+}
+
+// explicit instantiations
+#include "data_transfer.inst"
+
+DEAL_II_NAMESPACE_CLOSE
diff --git a/source/grid/data_transfer.inst.in b/source/grid/data_transfer.inst.in
new file mode 100644 (file)
index 0000000..d4eb339
--- /dev/null
@@ -0,0 +1,23 @@
+// ---------------------------------------------------------------------
+//
+// Copyright (C) 2010 - 2023 by the deal.II authors
+//
+// This file is part of the deal.II library.
+//
+// The deal.II library is free software; you can use it, redistribute
+// it, and/or modify it under the terms of the GNU Lesser General
+// Public License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+// The full text of the license can be found in the file LICENSE.md at
+// the top level directory of deal.II.
+//
+// ---------------------------------------------------------------------
+
+
+
+for (deal_II_dimension : DIMENSIONS; deal_II_space_dimension : SPACE_DIMENSIONS)
+  {
+#if deal_II_dimension <= deal_II_space_dimension
+    template class DataTransfer<deal_II_dimension, deal_II_space_dimension>;
+#endif
+  }
index 22695f336fb384c4539a58876b4bfa450c998c7c..21fcd7fa437ce420b1d816759d9515551a82ca68 100644 (file)
@@ -11064,7 +11064,9 @@ DEAL_II_CXX20_REQUIRES((concepts::is_valid_dim_spacedim<dim, spacedim>))
 Triangulation<dim, spacedim>::Triangulation(
   const MeshSmoothing smooth_grid,
   const bool          check_for_distorted_cells)
-  : smooth_grid(smooth_grid)
+  : cell_attached_data({0, 0, {}, {}})
+  , data_transfer(get_communicator())
+  , smooth_grid(smooth_grid)
   , anisotropic_refinement(false)
   , check_for_distorted_cells(check_for_distorted_cells)
 {
@@ -11081,10 +11083,6 @@ Triangulation<dim, spacedim>::Triangulation(
   signals.post_refinement.connect(signals.any_change);
   signals.clear.connect(signals.any_change);
   signals.mesh_movement.connect(signals.any_change);
-
-  cell_attached_data = {0, 0, {}, {}};
-  data_transfer =
-    std::make_unique<DataTransfer<dim, spacedim>>(get_communicator());
 }
 
 
@@ -11094,6 +11092,7 @@ DEAL_II_CXX20_REQUIRES((concepts::is_valid_dim_spacedim<dim, spacedim>))
 Triangulation<dim, spacedim>::Triangulation(
   Triangulation<dim, spacedim> &&tria) noexcept
   : Subscriptor(std::move(tria))
+  , data_transfer(get_communicator())
   , smooth_grid(tria.smooth_grid)
   , reference_cells(std::move(tria.reference_cells))
   , periodic_face_pairs_level_0(std::move(tria.periodic_face_pairs_level_0))
@@ -11190,7 +11189,7 @@ void Triangulation<dim, spacedim>::clear()
   reference_cells.clear();
 
   cell_attached_data = {0, 0, {}, {}};
-  data_transfer->clear();
+  data_transfer.clear();
 }
 
 
@@ -14864,9 +14863,9 @@ void Triangulation<dim, spacedim>::notify_ready_to_unpack(
     &unpack_callback)
 {
   // perform unpacking
-  this->data_transfer->unpack_data(this->local_cell_relations,
-                                   handle,
-                                   unpack_callback);
+  this->data_transfer.unpack_data(this->local_cell_relations,
+                                  handle,
+                                  unpack_callback);
 
   // decrease counters
   --this->cell_attached_data.n_attached_data_sets;
@@ -14886,7 +14885,7 @@ void Triangulation<dim, spacedim>::notify_ready_to_unpack(
       // everybody got their data, time for cleanup!
       this->cell_attached_data.pack_callbacks_fixed.clear();
       this->cell_attached_data.pack_callbacks_variable.clear();
-      this->data_transfer->clear();
+      this->data_transfer.clear();
 
       // reset all cell_status entries after coarsening/refinement
       for (auto &cell_rel : this->local_cell_relations)
@@ -14909,16 +14908,16 @@ void Triangulation<dim, spacedim>::save_attached_data(
   if (this->cell_attached_data.n_attached_data_sets > 0)
     {
       // pack attached data first
-      tria->data_transfer->pack_data(
+      tria->data_transfer.pack_data(
         tria->local_cell_relations,
         tria->cell_attached_data.pack_callbacks_fixed,
         tria->cell_attached_data.pack_callbacks_variable);
 
       // then store buffers in file
-      tria->data_transfer->save(global_first_cell, global_num_cells, filename);
+      tria->data_transfer.save(global_first_cell, global_num_cells, filename);
 
       // and release the memory afterwards
-      tria->data_transfer->clear();
+      tria->data_transfer.clear();
     }
 
   // clear all of the callback data, as explained in the documentation of
@@ -14944,14 +14943,14 @@ void Triangulation<dim, spacedim>::load_attached_data(
   // load saved data, if any was stored
   if (this->cell_attached_data.n_attached_deserialize > 0)
     {
-      this->data_transfer->load(global_first_cell,
-                                global_num_cells,
-                                local_num_cells,
-                                filename,
-                                n_attached_deserialize_fixed,
-                                n_attached_deserialize_variable);
-
-      this->data_transfer->unpack_cell_status(this->local_cell_relations);
+      this->data_transfer.load(global_first_cell,
+                               global_num_cells,
+                               local_num_cells,
+                               filename,
+                               n_attached_deserialize_fixed,
+                               n_attached_deserialize_variable);
+
+      this->data_transfer.unpack_cell_status(this->local_cell_relations);
 
       // the CellStatus of all stored cells should always be CELL_PERSIST.
       for (const auto &cell_rel : this->local_cell_relations)

In the beginning the Universe was created. This has made a lot of people very angry and has been widely regarded as a bad move.

Douglas Adams


Typeset in Trocchi and Trocchi Bold Sans Serif.