Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
oswald-interpolation.hh 11.26 KiB
// This file is part of the dune-gdt project:
//   https://github.com/dune-community/dune-gdt
// Copyright 2010-2018 dune-gdt developers and contributors. All rights reserved.
// License: Dual licensed as BSD 2-Clause License (http://opensource.org/licenses/BSD-2-Clause)
//      or  GPL-2.0+ (http://opensource.org/licenses/gpl-license)
//          with "runtime exception" (http://www.dune-project.org/license.html)
// Authors:
//   Felix Schindler (2019)

#ifndef DUNE_GDT_OPERATORS_OSWALD_INTERPOLATION_HH
#define DUNE_GDT_OPERATORS_OSWALD_INTERPOLATION_HH

#include <map>
#include <set>
#include <vector>

#include <dune/grid/common/rangegenerators.hh>

#include <dune/xt/common/memory.hh>
#include <dune/xt/grid/boundaryinfo/allneumann.hh>
#include <dune/xt/grid/boundaryinfo/interfaces.hh>
#include <dune/xt/grid/walker.hh>
#include <dune/xt/grid/type_traits.hh>

#include <dune/gdt/discretefunction/default.hh>
#include <dune/gdt/exceptions.hh>
#include <dune/gdt/spaces/h1/continuous-lagrange.hh>
#include <dune/gdt/tools/dirichlet-constraints.hh>

#include "interfaces.hh"

namespace Dune {
namespace GDT {


template <class M,
          class AssemblyGridView,
          size_t dim = 1,
          size_t dim_cols = 1,
          class SGV = AssemblyGridView,
          class RGV = AssemblyGridView>
class OswaldInterpolationOperator : public OperatorInterface<M, SGV, dim, dim_cols, dim, dim_cols, RGV>
{
  static_assert(XT::Grid::is_view<AssemblyGridView>::value, "");
  static_assert(dim == 1, "I did not think about this yet, feel free to implement!");
  static_assert(dim_cols == 1, "I did not think about this yet, feel free to implement!");
  using BaseType = OperatorInterface<M, SGV, dim, dim_cols, dim, dim_cols, RGV>;
  using ThisType = OswaldInterpolationOperator<M, AssemblyGridView, dim, dim_cols, SGV, RGV>;

public:
  using typename BaseType::RangeSpaceType;
  using typename BaseType::SourceSpaceType;
  using typename BaseType::VectorType;

  using AGV = AssemblyGridView;
  using AssemblyGridViewType = AGV;
  using I = XT::Grid::extract_intersection_t<AssemblyGridViewType>;

  /**
   * \param boundary_info To determine the Dirichlet boundary DoFs on which to set the range to zero.
   */
  OswaldInterpolationOperator(AssemblyGridViewType assembly_grid_view,
                              const SourceSpaceType& src_spc,
                              const RangeSpaceType& rng_spc,
                              const XT::Grid::BoundaryInfo<I>& boundary_info)
    : assembly_grid_view_(assembly_grid_view)
    , source_space_(src_spc)
    , range_space_(rng_spc)
    , boundary_info_(boundary_info)
    , assembled_(false)
    , global_DoF_id_to_global_LP_id_map_()
  {
    DUNE_THROW_IF(!range_space_.is_lagrangian(), Exceptions::operator_error, "This does not make any sense!");
    DUNE_THROW_IF(range_space_.continuous(0), Exceptions::operator_error, "This does not make any sense!");
    DUNE_THROW_IF(
        range_space_.min_polorder() != range_space_.max_polorder(), Exceptions::operator_error, "Not implemented yet!");
  }

  /**
   * Does not set any boundary DoFs to zero.
   */
  OswaldInterpolationOperator(AssemblyGridViewType assembly_grid_view,
                              const SourceSpaceType& src_spc,
                              const RangeSpaceType& rng_spc)
    : assembly_grid_view_(assembly_grid_view)
    , source_space_(src_spc)
    , range_space_(rng_spc)
    , boundary_info_(new XT::Grid::AllNeumannBoundaryInfo<I>()) // Anything without Dirichlet
    , assembled_(false)
  {
    DUNE_THROW_IF(!range_space_.is_lagrangian(), Exceptions::operator_error, "This does not make any sense!");
    DUNE_THROW_IF(range_space_.continuous(0), Exceptions::operator_error, "This does not make any sense!");
    DUNE_THROW_IF(
        range_space_.min_polorder() != range_space_.max_polorder(), Exceptions::operator_error, "Not implemented yet!");
  }

  bool linear() const override final
  {
    return true;
  }

  const SourceSpaceType& source_space() const override final
  {
    return source_space_;
  }

  const RangeSpaceType& range_space() const override final
  {
    return range_space_;
  }

  ThisType& assemble(const bool use_tbb = false) override final
  {
    if (assembled_)
      return *this;
    // create conforming space of same order to be used as mapper for the global Lagrange points
    auto cg_space = make_continuous_lagrange_space(assembly_grid_view_, range_space_.min_polorder());
    // determine Dirichlet DoFs
    DirichletConstraints<I, decltype(cg_space)> dirichlet_constraints(boundary_info_.access(), cg_space);
    auto walker = XT::Grid::make_walker(assembly_grid_view_);
    walker.append(dirichlet_constraints);
    walker.walk(use_tbb);
    boundary_LPs_ = std::move(dirichlet_constraints.dirichlet_DoFs());
    global_LP_id_to_global_DoF_id_map_.resize(cg_space.mapper().size());
    global_DoF_id_to_global_LP_id_map_.resize(range_space_.mapper().size(), std::numeric_limits<size_t>::max());
    DynamicVector<size_t> global_lagrange_point_indices(cg_space.mapper().max_local_size());
    DynamicVector<size_t> global_DoF_indices(range_space_.mapper().max_local_size());
    // walk the grid
    for (auto&& element : elements(assembly_grid_view_)) {
      const auto& lagrange_points = cg_space.finite_element(element.geometry().type()).lagrange_points();
      DUNE_THROW_IF(range_space_.finite_element(element.geometry().type()).lagrange_points().size()
                        != lagrange_points.size(),
                    Exceptions::operator_error,
                    "This should not happen, the Lagrange points should coincide for Lagrange spaces of same order!\n"
                        << "range_space_.finite_element(element.geometry().type()).lagrange_points().size() = "
                        << range_space_.finite_element(element.geometry().type()).lagrange_points().size() << "\n"
                        << "lagrange_points.size() = " << lagrange_points.size());
      cg_space.mapper().global_indices(element, global_lagrange_point_indices);
      range_space_.mapper().global_indices(element, global_DoF_indices);
      for (size_t ii = 0; ii < lagrange_points.size(); ++ii) {
        const auto global_LP_id = global_lagrange_point_indices[ii];
        const auto global_DoF_id = global_DoF_indices[ii];
        global_LP_id_to_global_DoF_id_map_[global_LP_id].insert(global_DoF_id);
        global_DoF_id_to_global_LP_id_map_[global_DoF_id] = global_LP_id;
      }
    }
    assembled_ = true;
    return *this;
  } // ... assemble(...)

  using BaseType::apply;

  void
  apply(const VectorType& source, VectorType& range, const XT::Common::Parameter& /*param*/ = {}) const override final
  {
    // some checks
    DUNE_THROW_IF(!assembled_, Exceptions::operator_error, "You need to call assemble() first!");
    DUNE_THROW_IF(!source.valid(), Exceptions::operator_error, "source contains inf or nan!");
    DUNE_THROW_IF(!source_space_.contains(source), Exceptions::operator_error, "");
    DUNE_THROW_IF(!range_space_.contains(range), Exceptions::operator_error, "");
    const auto source_function = make_discrete_function(source_space_, source);
    auto local_source = source_function.local_function();
    DynamicVector<size_t> global_DoF_indices(range_space_.mapper().max_local_size());
    // clear range on those DoFs associated with assembly_grid_view_ individually
    // (might only be a subset, range *= 0 would clear too much)
    for (const auto& DoF_id : global_DoF_id_to_global_LP_id_map_)
      if (DoF_id != std::numeric_limits<size_t>::max())
        range[DoF_id] = 0;
    // walk the grid to average on all inner Lagrange points
    for (auto&& element : elements(assembly_grid_view_)) {
      local_source->bind(element);
      const auto& lagrange_points = range_space_.finite_element(element.geometry().type()).lagrange_points();
      range_space_.mapper().global_indices(element, global_DoF_indices);
      DUNE_THROW_IF(global_DoF_indices.size() < lagrange_points.size(),
                    Exceptions::operator_error,
                    "This should not happen, the range_space is broken:\n"
                        << "global_DoF_indices.size() = " << global_DoF_indices.size() << "\n"
                        << "lagrange_points.size() = " << lagrange_points.size());
      for (size_t ii = 0; ii < lagrange_points.size(); ++ii) {
        const auto& lagrange_point = lagrange_points[ii];
        const auto global_DoF_id = global_DoF_indices[ii];
        const auto global_LP_id = global_DoF_id_to_global_LP_id_map_.at(global_DoF_id);
        const auto& DoFs_per_global_LP = global_LP_id_to_global_DoF_id_map_.at(global_LP_id);
        const auto source_value = local_source->evaluate(lagrange_point)[0] / DoFs_per_global_LP.size();
        for (const auto& DoF_id : DoFs_per_global_LP) {
          range[DoF_id] += source_value;
        }
      }
    }
    // set Dirichlet DoFs to zero
    for (const auto& global_LP_id : boundary_LPs_)
      for (const auto& global_DoF_id : global_LP_id_to_global_DoF_id_map_.at(global_LP_id))
        range[global_DoF_id] = 0;
  } // ... apply(...)

private:
  const AssemblyGridViewType assembly_grid_view_;
  const SourceSpaceType& source_space_;
  const RangeSpaceType& range_space_;
  const XT::Common::ConstStorageProvider<XT::Grid::BoundaryInfo<I>> boundary_info_;
  bool assembled_;
  std::vector<size_t> global_DoF_id_to_global_LP_id_map_;
  std::vector<std::set<size_t>> global_LP_id_to_global_DoF_id_map_;
  std::set<size_t> boundary_LPs_;
}; // class OswaldInterpolationOperator


template <class MatrixType, class AssemblyGridView, class SGV, size_t dim, size_t dim_cols, class RGV>
OswaldInterpolationOperator<MatrixType, AssemblyGridView, dim, dim_cols, SGV, RGV> make_oswald_interpolation_operator(
    const AssemblyGridView& assembly_grid_view,
    const SpaceInterface<SGV, dim, dim_cols>& source_space,
    const SpaceInterface<RGV, dim, dim_cols>& range_space,
    const XT::Grid::BoundaryInfo<XT::Grid::extract_intersection_t<AssemblyGridView>>& boundary_info)
{
  return OswaldInterpolationOperator<MatrixType, AssemblyGridView, dim, dim_cols, SGV, RGV>(
      assembly_grid_view, source_space, range_space, boundary_info);
}


template <class AssemblyGridView, class V, class SGV, size_t dim, size_t dim_cols, class RGV>
DiscreteFunction<V, RGV, dim, dim_cols> apply_oswald_interpolation(
    const AssemblyGridView& assembly_grid_view,
    const DiscreteFunction<V, SGV, dim, dim_cols>& source,
    const SpaceInterface<RGV, dim, dim_cols>& range_space,
    const XT::Grid::BoundaryInfo<XT::Grid::extract_intersection_t<AssemblyGridView>>& boundary_info)
{
  using M = typename XT::LA::Container<typename V::ScalarType, V::sparse_matrix_type>::MatrixType;
  auto op = make_oswald_interpolation_operator<M>(assembly_grid_view, source.space(), range_space, boundary_info);
  op.assemble(/*parallel=*/true);
  return op.apply(source);
}


template <class V, class GV, size_t dim, size_t dim_cols>
DiscreteFunction<V, GV, dim, dim_cols>
apply_oswald_interpolation(const DiscreteFunction<V, GV, dim, dim_cols>& source,
                           const SpaceInterface<GV, dim, dim_cols>& range_space,
                           const XT::Grid::BoundaryInfo<XT::Grid::extract_intersection_t<GV>>& boundary_info)
{
  return apply_oswald_interpolation(source.space().grid_view(), source, range_space, boundary_info);
}


} // namespace GDT
} // namespace Dune

#endif // DUNE_GDT_OPERATORS_OSWALD_INTERPOLATION_HH