// Distributed under the MIT License.
// See LICENSE.txt for details.

#pragma once

#include <algorithm>
#include <array>
#include <boost/functional/hash.hpp>
#include <cstddef>
#include <iterator>
#include <limits>
#include <map>
#include <tuple>
#include <utility>
#include <vector>

#include "DataStructures/DataBox/DataBox.hpp"
#include "DataStructures/DataBox/Prefixes.hpp"
#include "DataStructures/DataVector.hpp"
#include "DataStructures/FixedHashMap.hpp"
#include "DataStructures/Index.hpp"
#include "DataStructures/Tensor/Tensor.hpp"
#include "DataStructures/Variables.hpp"
#include "DataStructures/VariablesTag.hpp"
#include "Domain/Structure/Direction.hpp"
#include "Domain/Structure/Element.hpp"
#include "Domain/Structure/ElementId.hpp"
#include "Domain/Structure/MaxNumberOfNeighbors.hpp"
#include "Domain/Structure/OrientationMapHelpers.hpp"
#include "Domain/Tags.hpp"
#include "Evolution/DgSubcell/ActiveGrid.hpp"
#include "Evolution/DgSubcell/NeighborData.hpp"
#include "Evolution/DgSubcell/Projection.hpp"
#include "Evolution/DgSubcell/RdmpTci.hpp"
#include "Evolution/DgSubcell/SliceData.hpp"
#include "Evolution/DgSubcell/Tags/ActiveGrid.hpp"
#include "Evolution/DgSubcell/Tags/Inactive.hpp"
#include "Evolution/DgSubcell/Tags/Mesh.hpp"
#include "Evolution/DgSubcell/Tags/NeighborData.hpp"
#include "Evolution/DiscontinuousGalerkin/InboxTags.hpp"
#include "Evolution/DiscontinuousGalerkin/MortarData.hpp"
#include "Evolution/DiscontinuousGalerkin/MortarTags.hpp"
#include "NumericalAlgorithms/Spectral/Mesh.hpp"
#include "NumericalAlgorithms/Spectral/Spectral.hpp"
#include "Parallel/AlgorithmMetafunctions.hpp"
#include "Parallel/GlobalCache.hpp"
#include "Time/Tags.hpp"
#include "Time/TimeStepId.hpp"
#include "Utilities/ErrorHandling/Assert.hpp"
#include "Utilities/Gsl.hpp"
#include "Utilities/MakeArray.hpp"
#include "Utilities/TMPL.hpp"

namespace evolution::dg::subcell::Actions {
/*!
 * \brief Sets the local data from the relaxed discrete maximum principle
 * troubled-cell indicator and sends ghost zone data to neighboring elements.
 *
 * The action proceeds as follows:
 *
 * 1. Computes the maximum and minimum of each evolved variable, which is used
 *    by the relaxed discrete maximum principle troubled-cell indicator.
 * 2. Determine in which directions we have neighbors
 * 3. Slice the variables provided by GhostDataMutator to send to our neighbors
 *    for ghost zones
 * 4. Send the ghost zone data, appending the max/min for the TCI at the end of
 *    the `std::vector<double>` we are sending.
 *
 * Some notes:
 * - In the future we will need to send the cell-centered fluxes to do
 *   high-order FD without additional reconstruction being necessary.
 *
 * GlobalCache:
 * - Uses:
 *   - `ParallelComponent` proxy
 *
 * DataBox:
 * - Uses:
 *   - `domain::Tags::Mesh<Dim>`
 *   - `subcell::Tags::Mesh<Dim>`
 *   - `domain::Tags::Element<Dim>`
 *   - `Tags::TimeStepId`
 *   - `Tags::Next<Tags::TimeStepId>`
 *   - `subcell::Tags::ActiveGrid`
 *   - `System::variables_tag`
 * - Adds: nothing
 * - Removes: nothing
 * - Modifies:
 *   - `subcell::Tags::NeighborDataForReconstructionAndRdmpTci<Dim>`
 */
template <size_t Dim, typename GhostDataMutator>
struct SendDataForReconstruction {
  using inbox_tags = tmpl::list<
      evolution::dg::Tags::BoundaryCorrectionAndGhostCellsInbox<Dim>>;

  template <typename DbTags, typename... InboxTags, typename ArrayIndex,
            typename ActionList, typename ParallelComponent,
            typename Metavariables>
  static std::tuple<db::DataBox<DbTags>&&> apply(
      db::DataBox<DbTags>& box, tuples::TaggedTuple<InboxTags...>& /*inboxes*/,
      Parallel::GlobalCache<Metavariables>& cache,
      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,
      const ParallelComponent* const /*meta*/) {
    using variables_tag = typename Metavariables::system::variables_tag;

    ASSERT(db::get<Tags::ActiveGrid>(box) == ActiveGrid::Subcell,
           "The SendDataForReconstruction action can only be called when "
           "Subcell is the active scheme.");

    db::mutate<Tags::NeighborDataForReconstructionAndRdmpTci<Dim>>(
        make_not_null(&box),
        [](const auto neighbor_data_ptr, const auto& active_vars) {
          auto [max_of_vars, min_of_vars] =
              rdmp_max_min(active_vars, {}, false);

          // Clear the previous neighbor data and add current local data
          neighbor_data_ptr->clear();
          (*neighbor_data_ptr)[std::pair{
              Direction<Dim>::lower_xi(),
              ElementId<Dim>::external_boundary_id()}] =
              NeighborData{{}, std::move(max_of_vars), std::move(min_of_vars)};
        },
        db::get<variables_tag>(box));

    const Mesh<Dim>& dg_mesh = db::get<::domain::Tags::Mesh<Dim>>(box);
    const Mesh<Dim>& subcell_mesh = db::get<Tags::Mesh<Dim>>(box);
    const Element<Dim>& element = db::get<::domain::Tags::Element<Dim>>(box);
    const size_t ghost_zone_size =
        Metavariables::SubcellOptions::ghost_zone_size(box);
    DirectionMap<Dim, bool> directions_to_slice{};
    for (const auto& direction_neighbors : element.neighbors()) {
      if (direction_neighbors.second.size() == 0) {
        directions_to_slice[direction_neighbors.first] = false;
      } else {
        directions_to_slice[direction_neighbors.first] = true;
      }
    }
    // Optimization note: could save a copy+allocation if we moved
    // all_sliced_data when possible before sending.
    const DirectionMap<Dim, std::vector<double>> all_sliced_data = slice_data(
        db::mutate_apply(GhostDataMutator{}, make_not_null(&box)),
        subcell_mesh.extents(), ghost_zone_size, directions_to_slice);

    auto& receiver_proxy =
        Parallel::get_parallel_component<ParallelComponent>(cache);
    const NeighborData& local_neighbor_data =
        db::get<Tags::NeighborDataForReconstructionAndRdmpTci<Dim>>(box).at(
            std::pair{Direction<Dim>::lower_xi(),
                      ElementId<Dim>::external_boundary_id()});
    const TimeStepId& time_step_id = db::get<::Tags::TimeStepId>(box);
    const TimeStepId& next_time_step_id = [&box]() {
      if (Metavariables::local_time_stepping) {
        return db::get<::Tags::Next<::Tags::TimeStepId>>(box);
      } else {
        return db::get<::Tags::TimeStepId>(box);
      }
    }();

    // Compute and send actual variables
    for (const auto& [direction, neighbors_in_direction] :
         element.neighbors()) {
      const auto& orientation = neighbors_in_direction.orientation();
      const auto direction_from_neighbor = orientation(direction.opposite());
      ASSERT(neighbors_in_direction.size() == 1,
             "AMR is not yet supported when using DG-subcell. Note that this "
             "condition could be relaxed to support AMR only where the "
             "evolution is using DG without any changes to subcell.");

      for (const ElementId<Dim>& neighbor : neighbors_in_direction) {
        std::vector<double> subcell_data_to_send{};
        if (not orientation.is_aligned()) {
          std::array<size_t, Dim> slice_extents{};
          for (size_t d = 0; d < Dim; ++d) {
            gsl::at(slice_extents, d) = subcell_mesh.extents(d);
          }
          gsl::at(slice_extents, direction.dimension()) = ghost_zone_size;

          subcell_data_to_send =
              orient_variables(all_sliced_data.at(direction),
                               Index<Dim>{slice_extents}, orientation);
        } else {
          subcell_data_to_send = all_sliced_data.at(direction);
        }
        subcell_data_to_send.insert(
            subcell_data_to_send.end(),
            local_neighbor_data.max_variables_values.cbegin(),
            local_neighbor_data.max_variables_values.cend());
        subcell_data_to_send.insert(
            subcell_data_to_send.end(),
            local_neighbor_data.min_variables_values.cbegin(),
            local_neighbor_data.min_variables_values.cend());

        std::tuple<Mesh<Dim - 1>, std::optional<std::vector<double>>,
                   std::optional<std::vector<double>>, ::TimeStepId>
            data{dg_mesh.slice_away(direction.dimension()),
                 std::move(subcell_data_to_send), std::nullopt,
                 next_time_step_id};

        Parallel::receive_data<
            evolution::dg::Tags::BoundaryCorrectionAndGhostCellsInbox<Dim>>(
            receiver_proxy[neighbor], time_step_id,
            std::pair{std::pair{direction_from_neighbor, element.id()},
                      std::move(data)});
      }
    }
    return {std::move(box)};
  }
};

/*!
 * \brief Receive the subcell data from our neighbor, and accumulate the data
 * from the relaxed discrete maximum principle troubled-cell indicator.
 *
 * Note:
 * - Since we only care about the min/max over all neighbors and ourself at the
 *   past time, we accumulate all data immediately into the self `NeighborData`.
 * - If the neighbor is using DG and therefore sends boundary correction data
 *   then that is added into the `evolution::dg::Tags::MortarData` tag
 * - The next `TimeStepId` is recorded, but we do not yet support local time
 *   stepping.
 * - This action will never care about what variables are sent for
 *   reconstruction. It is only responsible for receiving the data and storing
 *   it in the `NeighborData`.
 *
 * GlobalCache:
 * -Uses: nothing
 *
 * DataBox:
 * - Uses:
 *   - `domain::Tags::Element<Dim>`
 *   - `Tags::TimeStepId`
 *   - `domain::Tags::Mesh<Dim>`
 *   - `subcell::Tags::Mesh<Dim>`
 *   - `domain::Tags::Element<Dim>`
 *   - `Tags::Next<Tags::TimeStepId>`
 *   - `subcell::Tags::ActiveGrid`
 *   - `System::variables_tag`
 * - Adds: nothing
 * - Removes: nothing
 * - Modifies:
 *   - `subcell::Tags::NeighborDataForReconstructionAndRdmpTci<Dim>`
 *   - `evolution::dg::Tags::MortarData`
 *   - `evolution::dg::Tags::MortarNextTemporalId`
 */
template <size_t Dim>
struct ReceiveDataForReconstruction {
  template <typename DbTags, typename... InboxTags, typename ArrayIndex,
            typename ActionList, typename ParallelComponent,
            typename Metavariables>
  static std::tuple<db::DataBox<DbTags>&&, Parallel::AlgorithmExecution> apply(
      db::DataBox<DbTags>& box, tuples::TaggedTuple<InboxTags...>& inboxes,
      const Parallel::GlobalCache<Metavariables>& /*cache*/,
      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,
      const ParallelComponent* const /*meta*/) {
    static_assert(
        not Metavariables::local_time_stepping,
        "DG-subcell does not yet support local time stepping. The "
        "reconstruction data must be sent using dense output sometimes, and "
        "not at all other times. However, the data for the RDMP TCI should be "
        "sent along with the data for reconstruction each time.");
    const Element<Dim>& element = db::get<::domain::Tags::Element<Dim>>(box);
    const auto number_of_expected_messages = element.neighbors().size();
    if (UNLIKELY(number_of_expected_messages == 0)) {
      // We have no neighbors, so just continue without doing any work
      return {std::move(box), Parallel::AlgorithmExecution::Continue};
    }

    using ::operator<<;
    using Key = std::pair<Direction<Dim>, ElementId<Dim>>;
    const auto& current_time_step_id = db::get<::Tags::TimeStepId>(box);
    std::map<TimeStepId,
             FixedHashMap<
                 maximum_number_of_neighbors(Dim), Key,
                 std::tuple<Mesh<Dim - 1>, std::optional<std::vector<double>>,
                            std::optional<std::vector<double>>, ::TimeStepId>,
                 boost::hash<Key>>>& inbox =
        tuples::get<evolution::dg::Tags::BoundaryCorrectionAndGhostCellsInbox<
            Metavariables::volume_dim>>(inboxes);
    const auto& received = inbox.find(current_time_step_id);
    // Check we have at least some data from correct time, and then check that
    // we have received all data
    if (received == inbox.end() or
        received->second.size() != number_of_expected_messages) {
      return {std::move(box), Parallel::AlgorithmExecution::Retry};
    }

    // Now that we have received all the data, copy it over as needed.
    FixedHashMap<maximum_number_of_neighbors(Dim), Key,
                 std::tuple<Mesh<Dim - 1>, std::optional<std::vector<double>>,
                            std::optional<std::vector<double>>, ::TimeStepId>,
                 boost::hash<Key>>
        received_data = std::move(inbox[current_time_step_id]);
    inbox.erase(current_time_step_id);
    db::mutate<Tags::NeighborDataForReconstructionAndRdmpTci<Dim>,
               evolution::dg::Tags::MortarData<Dim>,
               evolution::dg::Tags::MortarNextTemporalId<Dim>>(
        make_not_null(&box),
        [&current_time_step_id, &element, &received_data](
            const gsl::not_null<FixedHashMap<
                maximum_number_of_neighbors(Dim) + 1,
                std::pair<Direction<Dim>, ElementId<Dim>>, NeighborData,
                boost::hash<std::pair<Direction<Dim>, ElementId<Dim>>>>*>
                neighbor_data_ptr,
            const gsl::not_null<std::unordered_map<
                Key, evolution::dg::MortarData<Dim>, boost::hash<Key>>*>
                mortar_data,
            const gsl::not_null<
                std::unordered_map<Key, TimeStepId, boost::hash<Key>>*>
                mortar_next_time_step_id) {
          // Get the next time step id, and also the fluxes data if the neighbor
          // is doing DG.
          for (auto& received_mortar_data : received_data) {
            const auto& mortar_id = received_mortar_data.first;
            try {
              mortar_next_time_step_id->at(mortar_id) =
                  std::get<3>(received_mortar_data.second);
            } catch (std::exception& e) {
              ERROR("Failed retrieving the MortarId: ("
                    << mortar_id.first << ',' << mortar_id.second
                    << ") from the mortar_next_time_step_id. Got exception: "
                    << e.what());
            }
            if (std::get<2>(received_mortar_data.second).has_value()) {
              mortar_data->at(mortar_id).insert_neighbor_mortar_data(
                  current_time_step_id,
                  std::get<0>(received_mortar_data.second),
                  std::move(*std::get<2>(received_mortar_data.second)));
            }
          }

          ASSERT(neighbor_data_ptr->size() == 1,
                 "Should only have one element in the neighbor data when "
                 "receiving neighbor data");
          ASSERT(
              neighbor_data_ptr->count(
                  std::pair{Direction<Dim>::lower_xi(),
                            element.id().external_boundary_id()}) == 1,
              "The self data for the RDMP TCI should have been inserted but it "
              "wasn't, despite there being one entry in the neighbor data.");
          NeighborData& self_neighbor_data = (*neighbor_data_ptr)[std::pair{
              Direction<Dim>::lower_xi(),
              ElementId<Dim>::external_boundary_id()}];
          const size_t number_of_evolved_vars =
              self_neighbor_data.max_variables_values.size();
          ASSERT(self_neighbor_data.min_variables_values.size() ==
                     number_of_evolved_vars,
                 "The number of evolved variables for which we have a maximum "
                 "and minimum should be the same, but we have "
                     << number_of_evolved_vars << " for the max and "
                     << self_neighbor_data.min_variables_values.size()
                     << " for the min.");

          for (const auto& [direction, neighbors_in_direction] :
               element.neighbors()) {
            for (const auto& neighbor : neighbors_in_direction) {
              std::pair directional_element_id{direction, neighbor};
              ASSERT(neighbor_data_ptr->count(directional_element_id) == 0,
                     "Found neighbor already inserted in direction "
                         << direction << " with ElementId " << neighbor);
              ASSERT(std::get<1>(received_data[directional_element_id])
                         .has_value(),
                     "Received subcell data message that does not contain any "
                     "actual subcell data for reconstruction.");
              // Collect the max/min of u(t^n) for the RDMP as we receive data.
              // This reduces the memory footprint.
              std::vector<double>& received_neighbor_subcell_data =
                  *std::get<1>(received_data[directional_element_id]);
              const size_t max_offset = received_neighbor_subcell_data.size() -
                                        2 * number_of_evolved_vars;
              const size_t min_offset = received_neighbor_subcell_data.size() -
                                        number_of_evolved_vars;
              for (size_t var_index = 0; var_index < number_of_evolved_vars;
                   ++var_index) {
                self_neighbor_data.max_variables_values[var_index] = std::max(
                    self_neighbor_data.max_variables_values[var_index],
                    received_neighbor_subcell_data[max_offset + var_index]);
                self_neighbor_data.min_variables_values[var_index] = std::min(
                    self_neighbor_data.min_variables_values[var_index],
                    received_neighbor_subcell_data[min_offset + var_index]);
              }
              // Copy over the ghost cell data for subcell reconstruction.
              [[maybe_unused]] const auto insert_result =
                  neighbor_data_ptr->insert(std::pair{
                      directional_element_id,
                      NeighborData{std::vector<double>{
                          received_neighbor_subcell_data.begin(),
                          std::prev(
                              received_neighbor_subcell_data.end(),
                              2 * static_cast<typename std::iterator_traits<
                                      typename std::vector<double>::iterator>::
                                                  difference_type>(
                                      number_of_evolved_vars))}}});
              ASSERT(insert_result.second,
                     "Failed to insert the neighbor data in direction "
                         << direction << " from neighbor " << neighbor);
            }
          }
        });
    return {std::move(box), Parallel::AlgorithmExecution::Continue};
  }
};
}  // namespace evolution::dg::subcell::Actions


// Distributed under the MIT License.
// See LICENSE.txt for details.

#pragma once

#include <algorithm>
#include <boost/functional/hash.hpp>
#include <cstddef>
#include <unordered_map>
#include <utility>

#include "DataStructures/DataVector.hpp"
#include "DataStructures/Index.hpp"
#include "DataStructures/SliceIterator.hpp"
#include "DataStructures/Tensor/Tensor.hpp"
#include "DataStructures/Variables.hpp"
#include "Domain/Structure/Direction.hpp"
#include "Domain/Structure/Element.hpp"
#include "Domain/Structure/ElementId.hpp"
#include "Evolution/DgSubcell/Projection.hpp"
#include "Evolution/DiscontinuousGalerkin/MortarData.hpp"
#include "NumericalAlgorithms/Spectral/Mesh.hpp"
#include "Utilities/ErrorHandling/Assert.hpp"
#include "Utilities/Gsl.hpp"
#include "Utilities/TMPL.hpp"

namespace evolution::dg::subcell {
/*!
 * \brief Project the DG package data to the subcells. Data received from a
 * neighboring element doing DG is always projected, while the data we sent to
 * our neighbors before doing a rollback from DG to subcell is only projected if
 * `OverwriteInternalMortarData` is `true`.
 *
 * In order for the hybrid DG-FD/FV scheme to be conservative between elements
 * using DG and elements using subcell, the boundary terms must be the same on
 * both elements. In practice this means the boundary corrections \f$G+D\f$ must
 * be computed on the same grid. Consider the element doing subcell which
 * receives data from an element doing DG. In this case the DG element's
 * ingredients going into \f$G+D\f$ are projected to the subcells and then
 * \f$G+D\f$ are computed on the subcells. Similarly, for strict conservation
 * the element doing DG must first project the data it sent to the neighbor to
 * the subcells, then compute \f$G+D\f$ on the subcells, and finally reconstrct
 * \f$G+D\f$ back to the DG grid before lifting \f$G+D\f$ to the volume.
 *
 * This function updates the `packaged_data` (ingredients into \f$G+D\f$)
 * received by an element doing subcell by projecting the neighbor's DG data
 * onto the subcells. Note that this is only half of what is required for strict
 * conservation, the DG element must also compute \f$G+D\f$ on the subcells.
 * Note that we currently do not perform the other half of the correction
 * needed to be strictly conservative.
 *
 * If we are retaking a time step after the DG step failed then maintaining
 * conservation requires additional care. If `OverwriteInternalMortarData` is
 * `true` then the local (the element switching from DG to subcell) ingredients
 * into \f$G+D\f$ are projected and overwrite the data computed from
 * the FD reconstruction to the interface. However, even this is insufficient to
 * guarantee conservation. To guarantee conservation (which we do not currently
 * do) the correction \f$G+D\f$ must be computed on the DG grid and then
 * projected to the subcells.
 *
 * Note that our practical experience shows that since the DG-subcell hybrid
 * scheme switches to the subcell solver _before_ the local solution contains
 * discontinuities, strict conservation is not necessary between DG and FD/FV
 * regions. This was also observed with a block-adaptive finite difference AMR
 * code \cite CHEN2016604
 */
template <bool OverwriteInternalMortarData, size_t Dim,
          typename DgPackageFieldTags>
void correct_package_data(
    const gsl::not_null<Variables<DgPackageFieldTags>*> lower_packaged_data,
    const gsl::not_null<Variables<DgPackageFieldTags>*> upper_packaged_data,
    const size_t logical_dimension_to_operate_in, const Element<Dim>& element,
    const Mesh<Dim>& subcell_volume_mesh,
    const std::unordered_map<
        std::pair<Direction<Dim>, ElementId<Dim>>,
        evolution::dg::MortarData<Dim>,
        boost::hash<std::pair<Direction<Dim>, ElementId<Dim>>>>& mortar_data) {
  const Direction<Dim> upper_direction{logical_dimension_to_operate_in,
                                       Side::Upper};
  const Direction<Dim> lower_direction{logical_dimension_to_operate_in,
                                       Side::Lower};
  const bool has_upper_neighbor = element.neighbors().contains(upper_direction);
  const bool has_lower_neighbor = element.neighbors().contains(lower_direction);
  const std::pair upper_mortar_id =
      has_upper_neighbor
          ? std::pair{upper_direction,
                      *element.neighbors().at(upper_direction).begin()}
          : std::pair<Direction<Dim>, ElementId<Dim>>{};
  const std::pair lower_mortar_id =
      has_lower_neighbor
          ? std::pair{lower_direction,
                      *element.neighbors().at(lower_direction).begin()}
          : std::pair<Direction<Dim>, ElementId<Dim>>{};

  Index<Dim> subcell_extents_with_faces = subcell_volume_mesh.extents();
  ++subcell_extents_with_faces[logical_dimension_to_operate_in];
  const Mesh<Dim - 1>& subcell_face_mesh =
      subcell_volume_mesh.slice_away(logical_dimension_to_operate_in);

  const auto project_dg_data_to_subcells =
      [logical_dimension_to_operate_in, &subcell_extents_with_faces,
       &subcell_face_mesh](const gsl::not_null<Variables<DgPackageFieldTags>*>
                               subcell_packaged_data,
                           const size_t subcell_index,
                           const Mesh<Dim - 1>& neighbor_face_mesh,
                           const std::vector<double>& neighbor_data) {
        const double* slice_data = neighbor_data.data();
        // Warning: projected_data can't be inside the `if constexpr` since that
        // would lead to a dangling pointer.
        std::vector<double> projected_data{};
        if constexpr (Dim > 1) {
          projected_data.resize(
              Variables<DgPackageFieldTags>::number_of_independent_components *
              subcell_face_mesh.number_of_grid_points());
          evolution::dg::subcell::fd::detail::project_impl(
              gsl::make_span(projected_data.data(), projected_data.size()),
              gsl::make_span(neighbor_data.data(), neighbor_data.size()),
              neighbor_face_mesh, subcell_face_mesh.extents());
          slice_data = projected_data.data();
        } else {
          (void)subcell_face_mesh;
          (void)projected_data;
        }
        const size_t volume_grid_points = subcell_extents_with_faces.product();
        const size_t slice_grid_points =
            subcell_extents_with_faces
                .slice_away(logical_dimension_to_operate_in)
                .product();
        double* const volume_data = subcell_packaged_data->data();
        for (SliceIterator si(subcell_extents_with_faces,
                              logical_dimension_to_operate_in, subcell_index);
             si; ++si) {
          for (size_t i = 0;
               i <
               Variables<DgPackageFieldTags>::number_of_independent_components;
               ++i) {
            // clang-tidy: do not use pointer arithmetic
            volume_data[si.volume_offset() +
                        i * volume_grid_points] =  // NOLINT
                slice_data[si.slice_offset() +
                           i * slice_grid_points];  // NOLINT
          }
        }
      };

  // Project DG data to the subcells
  if (auto neighbor_mortar_data_it = mortar_data.find(upper_mortar_id);
      has_upper_neighbor and neighbor_mortar_data_it != mortar_data.end()) {
    if (neighbor_mortar_data_it->second.neighbor_mortar_data().has_value()) {
      project_dg_data_to_subcells(
          upper_packaged_data,
          subcell_extents_with_faces[logical_dimension_to_operate_in] - 1,
          neighbor_mortar_data_it->second.neighbor_mortar_data()->first,
          neighbor_mortar_data_it->second.neighbor_mortar_data()->second);
    }
    if constexpr (OverwriteInternalMortarData) {
      if (neighbor_mortar_data_it->second.local_mortar_data().has_value()) {
        project_dg_data_to_subcells(
            lower_packaged_data,
            subcell_extents_with_faces[logical_dimension_to_operate_in] - 1,
            neighbor_mortar_data_it->second.local_mortar_data()->first,
            neighbor_mortar_data_it->second.local_mortar_data()->second);
      }
    }
  }
  if (auto neighbor_mortar_data_it = mortar_data.find(lower_mortar_id);
      has_lower_neighbor and neighbor_mortar_data_it != mortar_data.end()) {
    if (neighbor_mortar_data_it->second.neighbor_mortar_data().has_value()) {
      project_dg_data_to_subcells(
          lower_packaged_data, 0,
          neighbor_mortar_data_it->second.neighbor_mortar_data()->first,
          neighbor_mortar_data_it->second.neighbor_mortar_data()->second);
    }
    if constexpr (OverwriteInternalMortarData) {
      if (neighbor_mortar_data_it->second.local_mortar_data().has_value()) {
        project_dg_data_to_subcells(
            upper_packaged_data, 0,
            neighbor_mortar_data_it->second.local_mortar_data()->first,
            neighbor_mortar_data_it->second.local_mortar_data()->second);
      }
    }
  }
}
}  // namespace evolution::dg::subcell


/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*                                                                           */
/*  This file is part of the library KASKADE 7                               */
/*    see http://www.zib.de/projects/kaskade7-finite-element-toolbox         */
/*                                                                           */
/*  Copyright (C) 2002-2015 Zuse Institute Berlin                            */
/*                                                                           */
/*  KASKADE 7 is distributed under the terms of the ZIB Academic License.    */
/*    see $KASKADE/academic.txt                                              */
/*                                                                           */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef GRIDMANAGER_HH_
#define GRIDMANAGER_HH_

#include <ctime>
#include <iostream>
#include <utility>

#include <boost/signals2.hpp>
#include <boost/range/iterator_range.hpp>
#include <boost/timer/timer.hpp>

#ifdef MULTITHREAD
#include <boost/thread.hpp>
#endif

#include <dune/grid/common/capabilities.hh>
#include <dune/grid/common/gridview.hh>
#include <dune/grid/io/file/dgfparser/gridptr.hh>

#ifdef MULTITHREAD
#include "utilities/threadpool/threadpool-0_2_5-src/threadpool/boost/threadpool.hpp"
#endif

#include "utilities/threading.hh"

namespace Kaskade
{
  template<class G, class T> class CellData;

  /**
   * \ingroup grid
   * \brief A class that provides access to signals that are emitted from the grid manager on various occasions.
   */
  struct GridSignals
  {
    /**
     * \ingroup grid
     * Signal group names for use with the informAboutRefinement
     * signal. Freeing resources should occur before allocating new
     * resources.
     */
    enum { freeResources=0, allocResources=1 };

    /**
     * \ingroup grid
     * \brief The argument type of the signal that is emitted before and after grid adaptation.
     */
    enum Status { BeforeRefinement, AfterRefinement, TransferCompleted };

    /**
     * \ingroup grid
     * \brief A signal that is emitted thrice on grid modifications, once before
     * adaptation takes place and twice after it is completed.
     *
     * The current state of grid refinement and FE coefficient transfer is signaled by the status argument.
     */
    boost::signals2::signal<void (Status)> informAboutRefinement;
  };

  /**
   * \ingroup grid
   * \brief A class storing cell ranges of roughly equal size for multithreaded mesh traversal.
   */
  template <class GridView>
  class CellRanges
  {
  public:
    /**
     * \brief The type of iterator to step through the cells of the given view.
     */
    typedef typename GridView::template Codim<0>::Iterator CellIterator;

    /**
     * \brief Default constructor.
     * This creates a pretty useless empty cell ranges object.
     */
    CellRanges() = default;

    /**
     * \brief Constructs cell ranges.
     * \param nmax Compute and store cell ranges for up to nmax groups
     * \param gridView the grid view to use (usually a leaf view). The iterators returned by the grid view
     *                 have to be valid for the lifetime of the CellRanges object - usually this means the
     *                 grid shall not be modified/adapted.
     */
    CellRanges(int nmax, GridView const& gridView): cellRanges(nmax+1)
    {
      // We know how many iterators we will insert - allocate space beforehand.
      for (int n=1; n<=nmax; ++n)
        cellRanges[n].reserve(n+1);

      // number of cells
      size_t const size = gridView.size(0);

      // Step through all cells, storing the iterator in case it is at a boundary of cell ranges.
      // The iteration through the cells is monotone, hence the ranges we construct cannot overlap. If few
      // cells exist, some ranges may be empty, however.
      // The following postconditions hold:
      // (i) cellRanges[n].size() <= n.
      // Proof: Assume cellRanges[n].size()==n. Then there is no cell with j >= size (it is at most size-1), where a further push_back could happen.
      // (ii) cellRanges[n][0] == begin.
      // Proof: In the first iterations j==0 and cellRanges[n].size()==0 holds, such that 0>=0 evaluates true and triggers the push_back(begin).
      // (iii) any range contains less than size/n + 1 cells.
      // Proof: Assume a range [r,s[. Then there is k with r>=k*size/n and t<(k+1)*size/n for all t<s. Thus s-1-r < ((k+1)-k)*size/n = size/n,
      // such that the length is s-r < size/n + 1.
      size_t j = 0; // cell counter
      for (CellIterator i=gridView.template begin<0>(); i!=gridView.template end<0>(); ++i, ++j)
        for (int n=1; n<=nmax; ++n)
          if (j >= cellRanges[n].size()*size/n)
            cellRanges[n].push_back(i);

      // Now we have to make sure that every range vector contains the number of ranges it's supposed to hold. Add empty ranges as needed.
      // As up to here the size of cellRanges[n] is at most n (not n+1), we automatically take care of terminating the last range with
      // the end iterator.
      for (int n=1; n<=nmax; ++n)
        while (cellRanges[n].size()<n+1)
          cellRanges[n].push_back(gridView.template end<0>());
    }

    /**
     * \brief Obtain cell range.
     * \param n the number of cell ranges (1<=n<=maxRanges())
     * \param k the k-th range will be returned (0<=k<n)
     */
    boost::iterator_range<CellIterator> range(int n, int k) const
    {
      assert(1<=n && n<cellRanges.size());
      assert(0<=k && k<n);
      return boost::iterator_range<CellIterator>(cellRanges[n][k],cellRanges[n][k+1]);
    }

    /**
     * \brief Obtain the maximum number of ranges.
     * Default constructed "empty" cell ranges return a negative value.
     */
    int maxRanges() const
    {
      return static_cast<int>(cellRanges.size())-1;
    }

  private:
    std::vector<std::vector<CellIterator>> cellRanges;
  };

  //----------------------------------------------------------------------------------------

  /**
   * \ingroup grid
   * \brief Basic functionality for managing grids and their refinement.
   */
  template<class Grd>
  class GridManagerBase
  {
    typedef Grd Grid;
  public:

    typedef GridManagerBase<Grid> Self;

    GridManagerBase(Dune::GridPtr<Grid> grid_, bool verbose_, bool enforceConcurrentReads = false)
    : GridManagerBase(grid_.release(),verbose_,enforceConcurrentReads)
    {}

    GridManagerBase(std::unique_ptr<Grid>&& grid_, bool verbose_, bool enforceConcurrentReads = false)
    : GridManagerBase(grid_.release(),verbose_,enforceConcurrentReads)
    {}

    GridManagerBase(Grid*&& grid_, bool verbose_, bool enforceConcurrentReads = false):
      markedAny(false), gridptr(grid_), measureTime(false),
      gridIsThreadSafe_(Dune::Capabilities::viewThreadSafe<Grid>::v), enforceConcurrentReads_(enforceConcurrentReads),
      levelRanges(gridptr->maxLevel()+1), verbose(verbose_)
    {}

    virtual ~GridManagerBase() {};

    /// Returns a const reference on the owned grid
    Grid const& grid() const
    {
      return *gridptr;
    }

    /// Returns a non-const reference on the owned grid
    Grid& grid_non_const()
    {
      return *gridptr;
    }

    /**
     * \brief Provides shared ownership management.
     *
     * Note that only an immutable grid is provided, such that the responsibility for
     * grid modifications remains with the GridManager object. This is necessary for the
     * GridManager to trigger prolongation/interpolation on grid modification.
     */
    std::shared_ptr<Grid const> gridShared() const
    {
      return gridptr;
    }

    bool adaptAtOnce()
    {
      this->preAdapt();
      bool result = adapt();
#if !defined(NDEBUG)
      if(!result) std::cout << "GRIDMANAGER: Nothing has been refined!" << std::endl;
#endif
      this->postAdapt();
      return result;
    }

    // will be removed after 2014-10-31
    /// Calls grid.mark(...): Marks an Entity T by an integer refCount
    bool mark(int refCount, typename Grid::template Codim<0>::EntityPointer e) __attribute((deprecated))
    {
      if(refCount != 0)
      {
        markedAny = true;
        return gridptr->mark(refCount,*e);
      }
      return false;
    }

    /// Calls grid.mark(...): Marks an Entity T by an integer refCount
    bool mark(int refCount, typename Grid::template Codim<0>::Entity const& e)
    {
      if(refCount != 0)
      {
        markedAny = true;
        return gridptr->mark(refCount,e);
      }
      return false;
    }

    /// Marks all Entities of a grid according to the corresponding entries in a CellData
    template<class S>
    void mark(CellData<Grid,S> const& cellData)
    {
      this->flushMarks();
      typename CellData<Grid,S>::CellDataVector::const_iterator imark;
      typename CellData<Grid,S>::CellDataVector::const_iterator imark_end = cellData.end();
      for(imark=cellData.begin(); imark != imark_end; ++imark)
        if(!this->mark(imark->first,imark->second) && imark->first!=0)
          std::cout << "GRIDMANAGER: Warning: Cannot mark element!" << std::endl;
      this->markedAny = true;
    }

    /**
     * \brief Marks a cell by its index.
     *
     * \warning The computational complexity of this method is the number of cells in the grid, i.e., calling this
     *          is dead slow.
     */
    void markByIndex(int refCount, int index)
    {
      auto cend = gridptr->leafIndexSet().template end<0, Dune::All_Partition>();
      for (auto ci=gridptr->leafIndexSet().template begin<0,Dune::All_Partition>(); ci!=cend; ++ci) {
        if(gridptr->leafIndexSet().index(*ci)==index)
          mark(refCount,ci);
      }
    }

    /**
     * \brief Reports the number of marked elements.
     *
     * Elements may be marked for multiple refinement. This is reflected in the sum. The exact
     * number returned is \f[ \sum_i r_i, \f] where \f$ r_i \f$ is the refinement count for
     * cell \f$ i \f$.
     */
    size_t countMarked() const
    {
      size_t marks(0);
      auto cend = gridptr->template leafend<0>();
      for (auto ci=gridptr->template leafbegin<0>(); ci!=cend; ++ci)
        marks += abs(gridptr->getMark(*ci));
      std::cout << "GRIDMANAGER: " << marks << " marked elements!" << std::endl;
      return marks;
    }

    /// Calls grid.preAdapt().
    bool preAdapt() {
      if(markedAny)
        return gridptr->preAdapt();
      else
        return false;
    };

    /// Calls grid.postAdapt().
    void postAdapt()
    {
      gridptr->postAdapt();

      if(verbose)
        std::cout << "after refinement: " << gridptr->size(0) << " cells, " << gridptr->size(Grid::dimension) << " vertices." << std::endl;
    };

    /// Remove all marks from the grid
    void flushMarks()
    {
      auto lend = gridptr->template leafend<0>();
      for(auto li=gridptr->template leafbegin<0>(); li !=lend; ++li)
        mark(0,*li);
      markedAny = false;
    }

    /**
     * \brief Tells the grid manager that concurrent reads of the grid are safe.
     */
    Self& enforceConcurrentReads(bool enforceConcurrentReads)
    {
      enforceConcurrentReads_ = enforceConcurrentReads;
      return *this;
    }

    /**
     * \brief Tells the grid manager whether to report timing statistics to standard output.
     */
    Self& setMeasureTime(bool measureTime_) { measureTime = measureTime_; return *this; }

    /**
     * \brief sets the verbosity level of the grid manager
     *
     * The grid manager prints status and progress information to std::cout. The amount of
     * information depends on the verbosity:
     * - 0: no messages are printed
     * - 1: mesh size change on refinement is reported
     */
    Self& setVerbosity(const bool verbosity) { verbose = verbosity; return *this; }

    /**
     * \brief Returns true if concurrent read accesses to the grid do not lead to data races.
     *
     * This is either the case if the grid implementation itself reports that it's thread-safe, or if the
     * user has explicitly stated that concurrent reads are safe (using enforceConcurrentReads).
     *
     * The thread-safety reported by this method is used e.g. by the assembler to decide how many threads
     * to use during assembly.
     */
    bool gridIsThreadSafe() const
    {
#ifdef NDEBUG
      return gridIsThreadSafe_ || enforceConcurrentReads_;
#else
      std::cout << "GRIDMANAGER: No Multithreading in DEBUG mode!" << std::endl;
      return false;
#endif
    }

    template<class TM, class FSE>
    struct TransFSE
    {
      TransFSE(TM const& mat_, FSE& fse_) : mat(mat_), fse(fse_) {}

      void operator()()
      {
        fse.transfer(mat);
      }

      TM const& mat;
      FSE& fse;
    };

    template<class TM, class FSE>
    void transferFSE(TM const& mat, FSE& fse) const
    {
#ifdef MULTITHREAD
      pool->schedule(TransFSE<TM, FSE>(mat,fse));
#else
      fse.transfer(mat);
#endif
    }

    template<class TD, class Space>
    struct TransBefore
    {
      TransBefore(TD* td_, Space const& space_) : td(td_), space(space_) {}

      void operator()()
      {
        td = new TD(space);
      }

      TD* td;
      Space const& space;
    };

    template<class TransferD, class Space>
    void getTransferData(TransferD*& result, Space const& space)
    {
#ifdef MULTITHREAD
      pool->schedule(TransBefore<TransferD, Space>(result,space));
#else
      result = (new TransferD(space));
#endif
    }

    template<class TD, class TM, class Space>
    struct TransAfter
    {
      TransAfter(TD* td_, TM* result_, Space const& space_, GridManagerBase<Grd>const & gm_) : td(td_), result(result_), space(space_), gm(gm_) {}

      void operator()()
      {
        result=td->transferMatrix().release();
        space.requestToRefine(*result,gm);
      }

      TD* td;
      TM* result;
      Space const& space;
      GridManagerBase<Grd> const& gm;
    };

    template<class TransferD, class Space>
    void getTransferMatrix(TransferD* transferData, typename TransferD::TransferMatrix*& result, Space const& space)
    {
#ifdef MULTITHREAD
      pool->schedule(TransAfter<TransferD,typename TransferD::TransferMatrix, Space>(transferData,result,space,*this));
#else
      result = transferData->transferMatrix().release();
      space.requestToRefine(*result,*this);
#endif
    }

    // TODO: docme
    bool adapt()
    {
      bool result;
      if(this->markedAny) {

        boost::timer::cpu_timer timer;
        if(measureTime) std::cout << "GRIDMANAGER: Preref.:" << std::flush;
        this->beforeRefinement();
        if(measureTime) std::cout << boost::timer::format(timer.elapsed()) << std::endl;

        if (verbose)  std::cout << std::endl << "GRIDMANAGER: mesh is refined from " << this->gridptr->size(0) << " cells " << std::flush;

        timer.start();
        if(measureTime) std::cout << "GRIDMANAGER: Refinement: " << std::flush;
        result = adaptGrid();
        update();
        if(measureTime) std::cout << boost::timer::format(timer.elapsed()) << std::endl;

        if(verbose) std::cout << "to " << this->gridptr->size(0) << " cells" << std::endl;

#if !defined(NDEBUG)
        if(!result) std::cout << "GRIDMANAGER: Warning: no refinement made!" << std::endl;
#endif

        timer.start();
        if(measureTime) std::cout << "GRIDMANAGER: Postref.: " << std::flush;
        this->afterRefinement();
        if(measureTime) std::cout << boost::timer::format(timer.elapsed()) << std::endl;

        this->markedAny = false;
      } else
        result = false;
      return result;
    }

    /// Refines the grid and transfers the data
    /** Calls grid.globalRefine(...) and performs the transfer of data
     * between the old and the new grid. If measureTime is true, then some
     * output on time needed the for the steps is given.
     */
    void globalRefine(int refCount) {
      if(refCount==0) return;
      if(refCount == -1) refCount=0;

      boost::timer::cpu_timer timer;
      if(measureTime) std::cout << "GRIDMANAGER: Preref.:" << std::flush;
      this->beforeRefinement();
      if(measureTime) std::cout << boost::timer::format(timer.elapsed()) << std::endl;

      timer.start();
      if(measureTime) std::cout << "GRIDMANAGER: Refinement: " << std::flush;
      refineGrid(refCount);
      update();
      if(measureTime) std::cout << boost::timer::format(timer.elapsed()) << std::endl;

      timer.start();
      if(measureTime) std::cout << "GRIDMANAGER: Postref.: " << std::flush;
      this->afterRefinement();
      if(measureTime) std::cout << boost::timer::format(timer.elapsed()) << std::endl;

      this->markedAny=false;
    };

    /**
     * \brief Must be overloaded by derived classes to adjust internal state immediately after mesh refinement.
     */
    virtual void update() = 0;

    /**
     * \brief DEPRECATED, use cellRanges(gridView) instead
     */
    CellRanges<typename Grid::LeafGridView> const& leafCellRanges() const
    {
      if (!leafRanges)
        leafRanges.reset(new CellRanges<typename Grid::LeafGridView>(NumaThreadPool::instance().cpus(),grid().leafGridView()));
      return *leafRanges;
    }

    /**
     * \brief Returns a CellRanges object for the given grid view.
     * The cell ranges are created on demand. The reference is valid up to the next mesh modification. This method is *not* thread-safe.
     * \todo implement locking just to make sure
     */
    CellRanges<typename Grid::LeafGridView> const& cellRanges(typename Grid::LeafGridView const&) const
    {
      if (!leafRanges)
        leafRanges.reset(new CellRanges<typename Grid::LeafGridView>(2*NumaThreadPool::instance().cpus(),grid().leafGridView()));
      return *leafRanges;
    }

    /**
     * \brief Returns a CellRanges object for the given grid view.
     * \param gridView a level grid view of the grid managed by this grid manager
     *
     * The cell ranges are created on demand. The reference is valid up to the next mesh modification. This method is *not* thread-safe.
     * \todo implement locking just to make sure
     */
    CellRanges<typename Grid::LevelGridView> const& cellRanges(typename Grid::LevelGridView const& gridView) const
    {
      int const level = gridView.template begin<0>()->level(); // cells have the level of their level view (inferred from the Dune tutorial)
      assert(level<=grid().maxLevel());

      if (levelRanges[level].maxRanges()<0)
        levelRanges[level] = CellRanges<typename Grid::LevelGridView>(2*NumaThreadPool::instance().cpus(),grid().levelGridView(level));
        // we could have used gridView directly here. But can we make sure this is a level view of the grid we keep?
      return levelRanges[level];
    }

    // TODO: docme
    bool markedAny;

  private:
    // TODO: docme!
    virtual void refineGrid(int refcount) = 0;
    virtual bool adaptGrid() = 0;

  protected:
    void beforeRefinement()
    {
      // trigger creation of transfer data structures
#ifdef MULTITHREAD
      pool.reset(new boost::threadpool::pool(16));
#endif
      signals.informAboutRefinement(GridSignals::BeforeRefinement);
#ifdef MULTITHREAD
      pool->wait(0);
#endif

      // invalidate the cell range data structures
      leafRanges.reset();
      levelRanges.clear();
    }

    void afterRefinement()
    {
      levelRanges.resize(grid().maxLevel()+1);
#ifdef MULTITHREAD
      std::cout << "multithreading enabled" << std::endl;
#endif
      signals.informAboutRefinement(GridSignals::AfterRefinement);
#ifdef MULTITHREAD
      pool->wait(0);
#endif
      signals.informAboutRefinement(GridSignals::TransferCompleted);
#ifdef MULTITHREAD
      pool->wait(0);
      pool.reset();
#endif
    }

    template<class LocalToGlobalMapper> friend class FEFunctionSpace;
    template<class GridMan, class ErrEst> friend class AdaptiveGrid;

    /**
     * \brief The grid itself.
     */
    std::shared_ptr<Grid> gridptr;

#ifdef MULTITHREAD
    std::unique_ptr<boost::threadpool::pool> pool;
#endif

  public:
    GridSignals signals;

  private:
    /// true: Print timing information, false: don't
    bool measureTime;

    const bool gridIsThreadSafe_;

    /// enforce concurrent reads for grids that possibly do not support concurrency
    bool enforceConcurrentReads_;

    // cell ranges are constructed on demand
    mutable std::unique_ptr<CellRanges<typename Grid::LeafGridView>> leafRanges;
    mutable std::vector<CellRanges<typename Grid::LevelGridView>> levelRanges;

  protected:
    bool verbose;
  };

  // -------------------------------------------------------------------

  /**
   * \ingroup grid
   * \brief Class that takes care of the transfer of data (of each FunctionSpaceElement) after modification of the grid
   *
   * Class that owns a grid. It takes care that all grid functions stay
   * consistent, when the grid is refined. A Gridmanager<Grid> is constructed with a
   * GridPointer<Grid>,  a std::unique_ptr<Grid>&& or an r-value reference on a Grid*. After this, all
   * modifications on the grid have to be done via the GridManager, in
   * particular refinement. If the grid is refined, the GridManager sends a
   * boost::signal to each FEFunctionSpace, which themselves send a
   * boost::signal to each FunctionSpaceElement, such that their transfer
   * from one grid to the other is organized.
   *
   * The grid manager can print status and progress information to std::cout, see \ref setVerbosity.

   * <b> Examples for usage </b>

 - Construction

 \verbatim
 Dune::GridPtr<Grid> gridptr("MyDomain.dgf");
 GridManager<Grid> gm(gridptr);
 \endverbatim

 - Alternative construction

 \verbatim
 std::unique_ptr<Grid> gridptr(new ...);
 GridManager<Grid> gm(gridptr);
 \endverbatim

 - Construction of FEFunctionSpaces with GridManager:

 \verbatim
 MySpace mySpace(gm,gm.grid().leafIndexSet(),order) //instead of: MySpace mySpace(grid,grid().leafIndexSet(),order)
 \endverbatim

 - Uniform Refinement:

 \verbatim
 gm.globalRefine(nTimes) //instead of: grid.globalRefine(nTimes)
 \endverbatim

 - Adaptivity

 Cf. detailed description of averaging_errorest.hh
   */
  template <class Grd>
  class GridManager : public GridManagerBase<Grd>{
  public:
    typedef Grd Grid;
    /// Construction from a GridPtr<Grid>
    /** Construct a GridManager<Grid> from a GridPointer<Grid>. The
     * ownership of the grid is transferred to the GridManager. Afterwards
     * the grid can only be modified via the GridManager.
     */
    explicit GridManager(Dune::GridPtr<Grid> grid_, bool verbose=false) : GridManagerBase<Grid>(grid_,verbose)
    {}

    /// Construction from an unique_ptr<Grid>
    /** Construct a GridManager<Grid> from a std::unique_ptr<Grid>. The
     * ownership of the grid is transferred to the GridManager. Afterwards
     * the grid can only be modified via the GridManager.
     */
    explicit GridManager(std::unique_ptr<Grid>&& grid_, bool verbose=false) : GridManagerBase<Grid>( std::move(grid_),verbose)
    {}

    /// Construction from a Grid*
    /** Construct a GridManager<Grid> from a std::unique_ptr<Grid>. The
     * ownership of the grid is transferred to the GridManager. Afterwards
     * the grid can only be modified via the GridManager.
     */
    explicit GridManager(Grid*&& grid_, bool verbose=false) : GridManagerBase<Grid>( std::move(grid_),verbose)
    {}

    virtual void update() {}

  private:
    virtual void refineGrid(int refCount)
    {
      this->gridptr->globalRefine(refCount);
    };

    virtual bool adaptGrid()
    {
      return this->gridptr->adapt();
    };
  };

  //---------------------------------------------------------------------

  /**
   * \ingroup grid
   * \brief A convenience routine that refines all cells incident to a boundary face.
   *
   * This is useful to create a priori boundary-refined meshes.
   */
  template <class Grid>
  void refineAtBoundary(GridManager<Grid>& gridManager)
  {
    auto lend = gridManager.grid().template leafend<0>();
    for (auto i=gridManager.grid().template leafbegin<0>(); i!=lend; ++i)
      if (i->hasBoundaryIntersections())
        gridManager.mark(1,i);
    gridManager.adaptAtOnce();
  }
}

#endif /* GRIDMANAGER_HH_ */


// Copyright (C) 2003-2011 Anders Logg
//
// This file is part of DOLFIN.
//
// DOLFIN is free software: you can 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 3 of the License, or
// (at your option) any later version.
//
// DOLFIN is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with DOLFIN. If not, see <http://www.gnu.org/licenses/>.
//
// Modified by Garth N. Wells 2005-2010
// Modified by Martin Sandve Alnes 2008
// Modified by Andre Massing 2009
//
// First added:  2003-11-28
// Last changed: 2011-11-14

#include <algorithm>
#include <map>
#include <utility>
#include <vector>
#include <boost/assign/list_of.hpp>

#include <dolfin/adaptivity/Extrapolation.h>
#include <dolfin/common/utils.h>
#include <dolfin/common/Array.h>
#include <dolfin/common/NoDeleter.h>
#include <dolfin/fem/FiniteElement.h>
#include <dolfin/fem/GenericDofMap.h>
#include <dolfin/fem/DirichletBC.h>
#include <dolfin/fem/UFC.h>
#include <dolfin/io/File.h>
#include <dolfin/io/XMLFile.h>
#include <dolfin/la/GenericVector.h>
#include <dolfin/la/DefaultFactory.h>
#include <dolfin/log/log.h>
#include <dolfin/mesh/Mesh.h>
#include <dolfin/mesh/Vertex.h>
#include <dolfin/mesh/ParallelData.h>
#include <dolfin/mesh/Point.h>
#include <dolfin/parameter/GlobalParameters.h>
#include "Expression.h"
#include "FunctionSpace.h"
#include "Function.h"

using namespace dolfin;

//-----------------------------------------------------------------------------
Function::Function(const FunctionSpace& V)
  : Hierarchical<Function>(*this),
    _function_space(reference_to_no_delete_pointer(V)),
    allow_extrapolation(dolfin::parameters["allow_extrapolation"])
{
  // Check that we don't have a subspace
  if (V.component().size() > 0)
  {
    dolfin_error("Function.cpp",
                 "create function",
                 "Cannot be created from subspace. Consider collapsing the function space");
  }

  // Initialize vector
  init_vector();
}
//-----------------------------------------------------------------------------
Function::Function(boost::shared_ptr<const FunctionSpace> V)
  : Hierarchical<Function>(*this), _function_space(V),
    allow_extrapolation(dolfin::parameters["allow_extrapolation"])
{
  // Check that we don't have a subspace
  if (V->component().size() > 0)
  {
    dolfin_error("Function.cpp",
                 "create function",
                 "Cannot be created from subspace. Consider collapsing the function space");
  }

  // Initialize vector
  init_vector();
}
//-----------------------------------------------------------------------------
Function::Function(boost::shared_ptr<const FunctionSpace> V,
                   boost::shared_ptr<GenericVector> x)
  : Hierarchical<Function>(*this), _function_space(V), _vector(x),
    allow_extrapolation(dolfin::parameters["allow_extrapolation"])
{
  // We do not check for a subspace since this constructor is used for creating
  // subfunctions

  // Assertion uses '<=' to deal with sub-functions
  dolfin_assert(V->dofmap());
  dolfin_assert(V->dofmap()->global_dimension() <= x->size());
}
//-----------------------------------------------------------------------------
Function::Function(const FunctionSpace& V, std::string filename)
  : Hierarchical<Function>(*this),
    _function_space(reference_to_no_delete_pointer(V)),
    allow_extrapolation(dolfin::parameters["allow_extrapolation"])
{
  // Check that we don't have a subspace
  if (V.component().size() > 0)
  {
    dolfin_error("Function.cpp",
                 "create function",
                 "Cannot be created from subspace. Consider collapsing the function space");
  }

  // Initialize vector
  init_vector();

  // Check size of vector
  if (_vector->size() != _function_space->dim())
  {
    dolfin_error("Function.cpp",
                 "read function from file",
                 "The number of degrees of freedom (%d) does not match dimension of function space (%d)",
                 _vector->size(), _function_space->dim());
  }

  // Read function data from file
  File file(filename);
  file >> *this;
}
//-----------------------------------------------------------------------------
Function::Function(boost::shared_ptr<const FunctionSpace> V,
                   std::string filename)
  : Hierarchical<Function>(*this), _function_space(V),
    allow_extrapolation(dolfin::parameters["allow_extrapolation"])
{
  // Check that we don't have a subspace
  if (V->component().size() > 0)
  {
    dolfin_error("Function.cpp",
                 "create function",
                 "Cannot be created from subspace. Consider collapsing the function space");
  }

  // Initialize vector
  init_vector();

  // Check size of vector
  if (_vector->size() != _function_space->dim())
  {
    dolfin_error("Function.cpp",
                 "read function from file",
                 "The number of degrees of freedom (%d) does not match dimension of function space (%d)",
                 _vector->size(), _function_space->dim());
  }

  // Read function data from file
  File file(filename);
  file >> *this;
}
//-----------------------------------------------------------------------------
Function::Function(const Function& v)
  : Hierarchical<Function>(*this),
    allow_extrapolation(dolfin::parameters["allow_extrapolation"])
{
  // Assign data
  *this = v;
}
//-----------------------------------------------------------------------------
Function::Function(const Function& v, uint i)
  : Hierarchical<Function>(*this),
    allow_extrapolation(dolfin::parameters["allow_extrapolation"])
{
  // Copy function space pointer
  this->_function_space = v[i]._function_space;

  // Copy vector pointer
  this->_vector = v[i]._vector;
}
//-----------------------------------------------------------------------------
Function::~Function()
{
  // Do nothing
}
//-----------------------------------------------------------------------------
const Function& Function::operator= (const Function& v)
{
  dolfin_assert(v._vector);

  // Make a copy of all the data, or if v is a sub-function, then we collapse
  // the dof map and copy only the relevant entries from the vector of v.
  if (v._vector->size() == v._function_space->dim())
  {
    // Copy function space
    _function_space = v._function_space;

    // Copy vector
    _vector.reset(v._vector->copy());

    // Clear subfunction cache
    sub_functions.clear();
  }
  else
  {
    // Create new collapsed FunctionSpace
    boost::unordered_map<uint, uint> collapsed_map;
    _function_space = v._function_space->collapse(collapsed_map);

    // FIXME: This dolfin_assertion doesn't work in parallel
    //dolfin_assert(collapsed_map.size() == _function_space->dofmap()->global_dimension());
    //dolfin_assert(collapsed_map.size() == _function_space->dofmap()->local_dimension());

    // Get row indices of original and new vectors
    boost::unordered_map<uint, uint>::const_iterator entry;
    std::vector<uint> new_rows(collapsed_map.size());
    Array<uint> old_rows(collapsed_map.size());
    uint i = 0;
    for (entry = collapsed_map.begin(); entry != collapsed_map.end(); ++entry)
    {
      new_rows[i]   = entry->first;
      old_rows[i++] = entry->second;
    }

    // Gather values into an Array
    Array<double> gathered_values;
    dolfin_assert(v.vector());
    v.vector()->gather(gathered_values, old_rows);

    // Initial new vector (global)
    init_vector();
    dolfin_assert(_function_space->dofmap());
    dolfin_assert(_vector->size() == _function_space->dofmap()->global_dimension());

    // Set values in vector
    this->_vector->set(&gathered_values[0], collapsed_map.size(), &new_rows[0]);
    this->_vector->apply("insert");
  }

  // Call assignment operator for base class
  Hierarchical<Function>::operator=(v);

  return *this;
}
//-----------------------------------------------------------------------------
const Function& Function::operator= (const Expression& v)
{
  interpolate(v);
  return *this;
}
//-----------------------------------------------------------------------------
Function& Function::operator[] (uint i) const
{
  // Check if sub-Function is in the cache, otherwise create and add to cache
  boost::ptr_map<uint, Function>::iterator sub_function = sub_functions.find(i);
  if (sub_function != sub_functions.end())
    return *(sub_function->second);
  else
  {
    // Extract function subspace
    std::vector<uint> component = boost::assign::list_of(i);
    boost::shared_ptr<const FunctionSpace> sub_space(_function_space->extract_sub_space(component));

    // Insert sub-Function into map and return reference
    sub_functions.insert(i, new Function(sub_space, _vector));
    return *(sub_functions.find(i)->second);
  }
}
//-----------------------------------------------------------------------------
boost::shared_ptr<const FunctionSpace> Function::function_space() const
{
  dolfin_assert(_function_space);
  return _function_space;
}
//-----------------------------------------------------------------------------
boost::shared_ptr<GenericVector> Function::vector()
{
  dolfin_assert(_vector);
  dolfin_assert(_function_space->dofmap());

  // Check that this is not a sub function.
  if (_vector->size() != _function_space->dofmap()->global_dimension())
  {
    cout << "Size of vector: " << _vector->size() << endl;
    cout << "Size of function space: " << _function_space->dofmap()->global_dimension() << endl;
    dolfin_error("Function.cpp",
                 "access vector of degrees of freedom fro function",
                 "Cannot access a non-const vector from a subfunction");
  }
  return _vector;
}
//-----------------------------------------------------------------------------
boost::shared_ptr<const GenericVector> Function::vector() const
{
  dolfin_assert(_vector);
  return _vector;
}
//-----------------------------------------------------------------------------
bool Function::in(const FunctionSpace& V) const
{
  dolfin_assert(_function_space);
  return *_function_space == V;
}
//-----------------------------------------------------------------------------
dolfin::uint Function::geometric_dimension() const
{
  dolfin_assert(_function_space);
  dolfin_assert(_function_space->mesh());
  return _function_space->mesh()->geometry().dim();
}
//-----------------------------------------------------------------------------
void Function::eval(Array<double>& values, const Array<double>& x) const
{
  dolfin_assert(_function_space);
  dolfin_assert(_function_space->mesh());
  const Mesh& mesh = *_function_space->mesh();

  // Find the cell that contains x
  const double* _x = x.data().get();
  const Point point(mesh.geometry().dim(), _x);
  int id = mesh.intersected_cell(point);

  // If not found, use the closest cell
  if (id == -1)
  {
    if (allow_extrapolation)
    {
      id = mesh.closest_cell(point);
      cout << "Extrapolating function value at x = " << point << " (not inside domain)." << endl;
    }
    else
    {
      cout << "Evaluating at x = " << point << endl;
      dolfin_error("Function.cpp",
                   "evaluate function at point",
                   "The point is not inside the domain. Consider setting \"allow_extrapolation\" to allow extrapolation");
    }
  }

  // Create cell that contains point
  const Cell cell(mesh, id);
  const UFCCell ufc_cell(cell, false);

  // Call evaluate function
  eval(values, x, cell, ufc_cell);
}
//-----------------------------------------------------------------------------
void Function::eval(Array<double>& values,
                    const Array<double>& x,
                    const Cell& dolfin_cell,
                    const ufc::cell& ufc_cell) const
{
  // Developer note: work arrays/vectors are re-created each time this function
  //                 is called for thread-safety

  dolfin_assert(_function_space->element());
  const FiniteElement& element = *_function_space->element();

  // FIXME: Rather than computing num_tensor_entries, we could just use
  //        values.size()

  // Compute in tensor (one for scalar function, . . .)
  uint num_tensor_entries = 1;
  for (uint i = 0; i < element.value_rank(); i++)
    num_tensor_entries *= element.value_dimension(i);

  dolfin_assert(values.size() == num_tensor_entries);

  // Create work vector for expansion coefficients
  std::vector<double> coefficients(element.space_dimension());

  // Restrict function to cell
  restrict(&coefficients[0], element, dolfin_cell, ufc_cell);

  // Create work vector for basis
  std::vector<double> basis(num_tensor_entries);

  // Initialise values
  for (uint j = 0; j < num_tensor_entries; ++j)
    values[j] = 0.0;

  // Compute linear combination
  for (uint i = 0; i < element.space_dimension(); ++i)
  {
    element.evaluate_basis(i, &basis[0], &x[0], ufc_cell);
    for (uint j = 0; j < num_tensor_entries; ++j)
      values[j] += coefficients[i]*basis[j];
  }
}
//-----------------------------------------------------------------------------
void Function::interpolate(const GenericFunction& v)
{
  // Gather off-process dofs
  v.gather();

  // Initialise vector
  init_vector();

  // Interpolate
  dolfin_assert(_function_space);
  _function_space->interpolate(*_vector, v);
}
//-----------------------------------------------------------------------------
void Function::extrapolate(const Function& v)
{
  Extrapolation::extrapolate(*this, v);
}
//-----------------------------------------------------------------------------
dolfin::uint Function::value_rank() const
{
  dolfin_assert(_function_space);
  dolfin_assert(_function_space->element());
  return _function_space->element()->value_rank();
}
//-----------------------------------------------------------------------------
dolfin::uint Function::value_dimension(uint i) const
{
  dolfin_assert(_function_space);
  dolfin_assert(_function_space->element());
  return _function_space->element()->value_dimension(i);
}
//-----------------------------------------------------------------------------
void Function::eval(Array<double>& values, const Array<double>& x,
                    const ufc::cell& ufc_cell) const
{
  dolfin_assert(_function_space);
  dolfin_assert(_function_space->mesh());
  const Mesh& mesh = *_function_space->mesh();

  // Check if UFC cell comes from mesh, otherwise redirect to
  // evaluate on non-matching cell
  if (ufc_cell.mesh_identifier == (int) mesh.id())
  {
    const Cell cell(mesh, ufc_cell.index);
    eval(values, x, cell, ufc_cell);
  }
  else
    non_matching_eval(values, x, ufc_cell);
}
//-----------------------------------------------------------------------------
void Function::non_matching_eval(Array<double>& values,
                                 const Array<double>& x,
                                 const ufc::cell& ufc_cell) const
{
  dolfin_assert(_function_space);
  dolfin_assert(_function_space->mesh());
  const Mesh& mesh = *_function_space->mesh();

  const double* _x = x.data().get();
  const uint dim = mesh.geometry().dim();
  const Point point(dim, _x);

  // Alternative 1: Find cell that point (x) intersects
  int id = mesh.intersected_cell(point);

  if (id == -1 && !allow_extrapolation)
  {
    dolfin_error("Function.cpp",
                 "evaluate function at point",
                 "The point is not inside the domain. Consider setting \"allow_extrapolation\" to allow extrapolation");
  }

  // Alternative 2: Compute closest cell to point (x)
  if (id == -1 && allow_extrapolation && dim == 2)
    id = mesh.closest_cell(point);

  // Alternative 3: Compute cell that contains barycenter of ufc_cell
  // NB: This is slightly heuristic, but should work well for
  // evaluation of points on refined meshes
  if (id == -1 && allow_extrapolation)
  {
    // Extract vertices of ufc_cell
    const double * const * vertices = ufc_cell.coordinates;

    Point barycenter;
    for (uint i = 0; i <= dim; i++)
    {
      Point vertex(dim, vertices[i]);
      barycenter += vertex;
    }
    barycenter /= (dim + 1);
    id = mesh.intersected_cell(barycenter);
  }

  // Throw error if all alternatives failed.
  if (id == -1)
  {
    dolfin_error("Function.cpp",
                 "evaluate function at point",
                 "No matching cell found");
  }

  // Create cell that contains point
  const Cell cell(mesh, id);
  const UFCCell new_ufc_cell(cell, false);

  // Call evaluate function
  eval(values, x, cell, new_ufc_cell);
}
//-----------------------------------------------------------------------------
void Function::restrict(double* w,
                        const FiniteElement& element,
                        const Cell& dolfin_cell,
                        const ufc::cell& ufc_cell) const
{
  dolfin_assert(w);
  dolfin_assert(_function_space);
  dolfin_assert(_function_space->dofmap());

  // Check if we are restricting to an element of this function space
  if (_function_space->has_element(element)
      && _function_space->has_cell(dolfin_cell))
  {
    // Get dofmap for cell
    const GenericDofMap& dofmap = *_function_space->dofmap();
    const std::vector<uint>& dofs = dofmap.cell_dofs(dolfin_cell.index());

    // Pick values from vector(s)
    _vector->get_local(w, dofs.size(), &dofs[0]);
  }
  else
  {
    // Restrict as UFC function (by calling eval)
    restrict_as_ufc_function(w, element, dolfin_cell, ufc_cell);
  }
}
//-----------------------------------------------------------------------------
void Function::compute_vertex_values(Array<double>& vertex_values,
                                     const Mesh& mesh) const
{
  dolfin_assert(_function_space);
  dolfin_assert(_function_space->mesh());

  // Check that the mesh matches
  if (&mesh != _function_space->mesh().get())
  {
    dolfin_error("Function.cpp",
                 "interpolate function values at vertices",
                 "Non-matching mesh");
  }

  // Gather ghosts dofs
  gather();

  // Get finite element
  dolfin_assert(_function_space->element());
  const FiniteElement& element = *_function_space->element();

  // Local data for interpolation on each cell
  const uint num_cell_vertices = mesh.type().num_vertices(mesh.topology().dim());

  // Compute in tensor (one for scalar function, . . .)
  uint num_tensor_entries = 1;
  for (uint i = 0; i < element.value_rank(); i++)
    num_tensor_entries *= element.value_dimension(i);

  // Resize Array for holding vertex values
  vertex_values.resize(num_tensor_entries*(_function_space->mesh()->num_vertices()));

  // Create vector to hold cell vertex values
  std::vector<double> cell_vertex_values(num_tensor_entries*num_cell_vertices);

  // Create vector for expansion coefficients
  std::vector<double> coefficients(element.space_dimension());

  // Interpolate vertex values on each cell (using last computed value if not
  // continuous, e.g. discontinuous Galerkin methods)
  UFCCell ufc_cell(mesh, false);
  for (CellIterator cell(mesh); !cell.end(); ++cell)
  {
    // Update to current cell
    ufc_cell.update(*cell);

    // Pick values from global vector
    restrict(&coefficients[0], element, *cell, ufc_cell);

    // Interpolate values at the vertices
    element.interpolate_vertex_values(&cell_vertex_values[0],
                                      &coefficients[0], ufc_cell);

    // Copy values to array of vertex values
    for (VertexIterator vertex(*cell); !vertex.end(); ++vertex)
    {
      for (uint i = 0; i < num_tensor_entries; ++i)
      {
        const uint local_index  = vertex.pos()*num_tensor_entries + i;
        const uint global_index = i*mesh.num_vertices() + vertex->index();
        vertex_values[global_index] = cell_vertex_values[local_index];
      }
    }
  }
}
//-----------------------------------------------------------------------------
void Function::gather() const
{
  if (MPI::num_processes() > 1)
    _vector->update_ghost_values();
}
//-----------------------------------------------------------------------------
void Function::init_vector()
{
  // Check that function space is not a subspace (view)
  dolfin_assert(_function_space);
  if (_function_space->dofmap()->is_view())
  {
    dolfin_error("Function.cpp",
                 "initialize vector of degrees of freedom for function",
                 "Cannot be created from subspace. Consider collapsing the function space");
  }

  // Get global size
  const uint N = _function_space->dofmap()->global_dimension();

  // Get local range
  const std::pair<uint, uint> range = _function_space->dofmap()->ownership_range();
  const uint local_size = range.second - range.first;

  // Determine ghost vertices if dof map is distributed
  std::vector<uint> ghost_indices;
  if (N  > local_size)
    compute_ghost_indices(range, ghost_indices);

  // Create vector of dofs
  if (!_vector)
  {
    DefaultFactory factory;
    _vector.reset(factory.create_vector());
  }
  dolfin_assert(_vector);

  // Initialize vector of dofs
  _vector->resize(range, ghost_indices);
  _vector->zero();
}
//-----------------------------------------------------------------------------
void Function::compute_ghost_indices(std::pair<uint, uint> range,
                                     std::vector<uint>& ghost_indices) const
{
  // Clear data
  ghost_indices.clear();

  // Get mesh
  dolfin_assert(_function_space);
  dolfin_assert(_function_space->mesh());
  const Mesh& mesh = *_function_space->mesh();

  // Get dof map
  dolfin_assert(_function_space->dofmap());
  const GenericDofMap& dofmap = *_function_space->dofmap();

  // Dofs per cell
  dolfin_assert(_function_space->element());
  const uint num_dofs_per_cell = _function_space->element()->space_dimension();

  // Get local range
  const uint n0 = range.first;
  const uint n1 = range.second;

  // Iterate over local mesh and check which dofs are needed
  for (CellIterator cell(mesh); !cell.end(); ++cell)
  {
    // Get dofs on cell
    const std::vector<uint>& dofs = dofmap.cell_dofs(cell->index());

    for (uint d = 0; d < num_dofs_per_cell; ++d)
    {
      const uint dof = dofs[d];
      if (dof < n0 || dof >= n1)
      {
        // FIXME: Could we use dolfin::Set here? Or unordered_set?
        if (std::find(ghost_indices.begin(), ghost_indices.end(), dof) == ghost_indices.end())
          ghost_indices.push_back(dof);
      }
    }
  }
}
//-----------------------------------------------------------------------------