From 0ca3410793d785fb13492c0a92e0111f27b68bf3 Mon Sep 17 00:00:00 2001
From: Felix Schindler <felix.schindler@wwu.de>
Date: Sun, 10 Feb 2019 22:43:12 +0100
Subject: [PATCH] [operators.ipdg-flux-reconstruction] add order 0

---
 .../gdt/operators/ipdg-flux-reconstruction.hh | 346 ++++++++++++++++++
 1 file changed, 346 insertions(+)
 create mode 100644 dune/gdt/operators/ipdg-flux-reconstruction.hh

diff --git a/dune/gdt/operators/ipdg-flux-reconstruction.hh b/dune/gdt/operators/ipdg-flux-reconstruction.hh
new file mode 100644
index 000000000..1e0721d9a
--- /dev/null
+++ b/dune/gdt/operators/ipdg-flux-reconstruction.hh
@@ -0,0 +1,346 @@
+// 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_IPDG_FLUX_RECONSTRUCTION_HH
+#define DUNE_GDT_OPERATORS_IPDG_FLUX_RECONSTRUCTION_HH
+
+#include <dune/geometry/referenceelements.hh>
+#include <dune/grid/common/rangegenerators.hh>
+
+#include <dune/xt/la/container/common.hh>
+#include <dune/xt/la/solver.hh>
+#include <dune/xt/grid/type_traits.hh>
+#include <dune/xt/functions/constant.hh>
+#include <dune/xt/functions/interfaces/grid-function.hh>
+
+#include <dune/gdt/exceptions.hh>
+#include <dune/gdt/local/finite-elements/orthonormal.hh>
+#include <dune/gdt/local/integrands/elliptic-ipdg.hh>
+#include <dune/gdt/operators/interfaces.hh>
+#include <dune/gdt/spaces/mapper/finite-volume.hh>
+
+namespace Dune {
+namespace GDT {
+
+
+/**
+ * \todo This is just the old implementation copied over. Update analoggously to the RT interpolation!
+ */
+template <class M,
+          class AssemblyGridView,
+          LocalEllipticIpdgIntegrands::Method ipdg = LocalEllipticIpdgIntegrands::default_method,
+          class SGV = AssemblyGridView,
+          class RGV = AssemblyGridView>
+class IpdgFluxReconstructionOperator : public OperatorInterface<M, SGV, 1, 1, RGV::dimension, 1, RGV>
+{
+  static_assert(XT::Grid::is_view<AssemblyGridView>::value, "");
+  using BaseType = OperatorInterface<M, SGV, 1, 1, RGV::dimension, 1, RGV>;
+  using ThisType = IpdgFluxReconstructionOperator<M, AssemblyGridView, ipdg, SGV, RGV>;
+
+public:
+  using typename BaseType::F;
+  using typename BaseType::RangeSpaceType;
+  using typename BaseType::SourceSpaceType;
+  using typename BaseType::VectorType;
+
+  using AGV = AssemblyGridView;
+  using AssemblyGridViewType = AGV;
+  using E = XT::Grid::extract_entity_t<AssemblyGridViewType>;
+  static const constexpr size_t d = RGV::dimension;
+
+  IpdgFluxReconstructionOperator(const AssemblyGridViewType& assembly_grid_view,
+                                 const SourceSpaceType& src_spc,
+                                 const RangeSpaceType& rng_spc,
+                                 const XT::Functions::GridFunctionInterface<E>& diffusion_factor,
+                                 const XT::Functions::GridFunctionInterface<E, d, d>& diffusion_tensor)
+    : assembly_grid_view_(assembly_grid_view)
+    , source_space_(src_spc)
+    , range_space_(rng_spc)
+    , diffusion_factor_(diffusion_factor)
+    , diffusion_tensor_(diffusion_tensor)
+    , element_mapper_(assembly_grid_view_)
+  {
+    DUNE_THROW_IF(range_space_.type() != SpaceType::raviart_thomas, Exceptions::operator_error, "");
+    DUNE_THROW_IF(range_space_.max_polorder() != 0, 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_;
+  }
+
+  using BaseType::apply;
+
+  void apply(const VectorType& source, VectorType& range, const XT::Common::Parameter& param = {}) const override final
+  {
+    using D = typename AssemblyGridView::ctype;
+    using I = XT::Grid::extract_intersection_t<AssemblyGridViewType>;
+    // some checks
+    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 range_function = make_discrete_function(range_space_, range);
+    // some preparations
+    auto local_range = range_function.local_discrete_function();
+    auto local_source_element = source_function.local_function();
+    auto local_source_neighbor = source_function.local_function();
+    auto local_df_element = diffusion_factor_.local_function();
+    auto local_df_neighbor = diffusion_factor_.local_function();
+    auto local_dt_element = diffusion_tensor_.local_function();
+    auto local_dt_neighbor = diffusion_tensor_.local_function();
+    auto rt_basis = range_space_.basis().localize();
+    for (auto&& element : elements(assembly_grid_view_)) {
+      local_range->bind(element);
+      local_source_element->bind(element);
+      local_df_element->bind(element);
+      local_dt_element->bind(element);
+      rt_basis->bind(element);
+      const auto& rt_fe = range_space_.finite_element(element.geometry().type());
+      // prepare
+      const size_t sz = rt_basis->size();
+      std::vector<bool> local_key_was_handled(sz, false);
+      // determine the face dofs, therefore walk the intersections
+      for (auto&& intersection : intersections(assembly_grid_view_, element)) {
+        const auto intersection_to_local_key_map = rt_fe.coefficients().local_key_indices(1);
+        const auto intersection_index = intersection.indexInInside();
+        const auto& local_keys_assosiated_with_intersection = intersection_to_local_key_map[intersection_index];
+        if (local_keys_assosiated_with_intersection.size() > 0) {
+          const auto intersection_fe =
+              make_local_orthonormal_finite_element<D, d - 1, F>(intersection.geometry().type(), rt_fe.order());
+          const auto& intersection_Pk_basis = intersection_fe->basis();
+          DUNE_THROW_IF(intersection_Pk_basis.size() != local_keys_assosiated_with_intersection.size(),
+                        Exceptions::interpolation_error,
+                        "intersection_Pk_basis.size() = " << intersection_Pk_basis.size()
+                                                          << "\n   local_keys_assosiated_with_intersection.size() = "
+                                                          << local_keys_assosiated_with_intersection.size());
+          XT::LA::CommonDenseMatrix<F> lhs(
+              local_keys_assosiated_with_intersection.size(), intersection_Pk_basis.size(), 0);
+          XT::LA::CommonDenseVector<F> rhs(intersection_Pk_basis.size(), 0);
+          bool there_are_intersection_dofs_to_determine = false;
+          if (intersection.neighbor()) {
+            const auto neighbor = intersection.outside();
+            // only look at each intersection once
+            if (element_mapper_.global_index(element, 0) < element_mapper_.global_index(neighbor, 0)) {
+              there_are_intersection_dofs_to_determine = true;
+              local_source_neighbor->bind(neighbor);
+              local_df_neighbor->bind(neighbor);
+              local_dt_neighbor->bind(neighbor);
+              // do a face quadrature
+              const int max_polorder =
+                  std::max(intersection_Pk_basis.order(),
+                           std::max(local_source_element->order(param),
+                                    std::max(intersection_Pk_basis.order(), local_source_neighbor->order(param))));
+              for (auto&& quadrature_point : QuadratureRules<D, d - 1>::rule(
+                       intersection.geometry().type(), 2 * std::max(max_polorder, rt_basis->order()))) {
+                const auto point_on_reference_intersection = quadrature_point.position();
+                const auto point_in_reference_element =
+                    intersection.geometryInInside().global(point_on_reference_intersection);
+                const auto point_in_reference_neighbor =
+                    intersection.geometryInOutside().global(point_on_reference_intersection);
+                const auto quadrature_weight = quadrature_point.weight();
+                const auto normal = intersection.unitOuterNormal(point_on_reference_intersection);
+                const auto integration_factor =
+                    intersection.geometry().integrationElement(point_on_reference_intersection);
+                // evaluate bases ...
+                const auto rt_basis_values = rt_basis->evaluate_set(point_in_reference_element);
+                const auto intersection_Pk_basis_values =
+                    intersection_Pk_basis.evaluate(point_on_reference_intersection);
+                // ... and data functions
+                const auto source_value_element = local_source_element->evaluate(point_in_reference_element, param);
+                const auto source_value_neighbor = local_source_neighbor->evaluate(point_in_reference_neighbor, param);
+                const auto source_grad_element = local_source_element->jacobian(point_in_reference_element, param)[0];
+                const auto source_grad_neighbor =
+                    local_source_neighbor->jacobian(point_in_reference_neighbor, param)[0];
+                const auto df_value_element = local_df_element->evaluate(point_in_reference_element, param);
+                const auto df_value_neighbor = local_df_neighbor->evaluate(point_in_reference_neighbor, param);
+                const auto dt_value_element = local_dt_element->evaluate(point_in_reference_element, param);
+                const auto dt_value_neighbor = local_dt_neighbor->evaluate(point_in_reference_neighbor, param);
+                const auto diffusion_element = dt_value_element * df_value_element;
+                const auto diffusion_neighbor = dt_value_neighbor * df_value_neighbor;
+                // compute penalty factor (see Epshteyn, Riviere, 2007)
+                const F sigma = LocalEllipticIpdgIntegrands::internal::inner_sigma(max_polorder);
+                const double beta = LocalEllipticIpdgIntegrands::internal::default_beta(d);
+                // compute weighting (see Ern, Stephansen, Zunino 2007)
+                using IpdgHelper = typename LocalEllipticIpdgIntegrands::Inner<I, F, ipdg>::template IPDG<ipdg>;
+                const F delta_plus =
+                    IpdgHelper::delta_plus(df_value_neighbor, dt_value_neighbor, diffusion_neighbor, normal);
+                const F delta_minus =
+                    IpdgHelper::delta_minus(df_value_element, dt_value_element, diffusion_element, normal);
+                const F gamma = IpdgHelper::gamma(delta_plus, delta_minus);
+                const F penalty = IpdgHelper::penalty(df_value_element,
+                                                      dt_value_neighbor,
+                                                      df_value_neighbor,
+                                                      dt_value_element,
+                                                      normal,
+                                                      sigma,
+                                                      gamma,
+                                                      intersection.geometry().volume(),
+                                                      beta);
+                const F weight_plus = IpdgHelper::weight_plus(delta_plus, delta_minus);
+                const F weight_minus = IpdgHelper::weight_minus(delta_plus, delta_minus);
+                for (size_t ii = 0; ii < local_keys_assosiated_with_intersection.size(); ++ii) {
+                  const size_t local_key_index = local_keys_assosiated_with_intersection[ii];
+                  for (size_t jj = 0; jj < intersection_Pk_basis.size(); ++jj)
+                    lhs.add_to_entry(ii,
+                                     jj,
+                                     quadrature_weight * integration_factor
+                                         * (rt_basis_values[local_key_index] * normal)
+                                         * intersection_Pk_basis_values[jj]);
+                }
+                for (size_t jj = 0; jj < intersection_Pk_basis.size(); ++jj)
+                  rhs[jj] += quadrature_weight * integration_factor
+                             * (penalty * (source_value_element - source_value_neighbor)
+                                - normal
+                                      * (weight_minus * (diffusion_element * source_grad_element)
+                                         + weight_plus * (diffusion_neighbor * source_grad_neighbor)))
+                             * intersection_Pk_basis_values[jj];
+              }
+            } else {
+              there_are_intersection_dofs_to_determine = false;
+              // we still need to mark the local DoFs as handled, since they were determined earlier when looking at
+              // this intersection from the other side
+              for (size_t ii = 0; ii < local_keys_assosiated_with_intersection.size(); ++ii) {
+                const size_t local_key_index = local_keys_assosiated_with_intersection[ii];
+                assert(!local_key_was_handled[local_key_index]);
+                local_key_was_handled[local_key_index] = true;
+              }
+            }
+          } else {
+            there_are_intersection_dofs_to_determine = true;
+            // do a face quadrature
+            const int max_polorder = std::max(intersection_Pk_basis.order(), local_source_element->order(param));
+            for (auto&& quadrature_point : QuadratureRules<D, d - 1>::rule(
+                     intersection.geometry().type(), 2 * std::max(max_polorder, rt_basis->order()))) {
+              const auto point_on_reference_intersection = quadrature_point.position();
+              const auto point_in_reference_element =
+                  intersection.geometryInInside().global(point_on_reference_intersection);
+              const auto quadrature_weight = quadrature_point.weight();
+              const auto normal = intersection.unitOuterNormal(point_on_reference_intersection);
+              const auto integration_factor =
+                  intersection.geometry().integrationElement(point_on_reference_intersection);
+              // evaluate bases ...
+              const auto rt_basis_values = rt_basis->evaluate_set(point_in_reference_element);
+              const auto intersection_Pk_basis_values = intersection_Pk_basis.evaluate(point_on_reference_intersection);
+              // ... and data functions ...
+              const auto source_value_element = local_source_element->evaluate(point_in_reference_element, param);
+              const auto source_grad_element = local_source_element->jacobian(point_in_reference_element, param)[0];
+              const auto df_value_element = local_df_element->evaluate(point_in_reference_element, param);
+              const auto dt_value_element = local_dt_element->evaluate(point_in_reference_element, param);
+              const auto diffusion_element = dt_value_element * df_value_element;
+              // compute penalty (see Epshteyn, Riviere, 2007)
+              const F sigma = LocalEllipticIpdgIntegrands::internal::boundary_sigma(max_polorder);
+              const double beta = LocalEllipticIpdgIntegrands::internal::default_beta(d);
+              // compute weighting (see Ern, Stephansen, Zunino 2007)
+              using IpdgHelper =
+                  typename LocalEllipticIpdgIntegrands::DirichletBoundaryLhs<I, F, ipdg>::template IPDG<ipdg>;
+              const F gamma = IpdgHelper::gamma(diffusion_element, normal);
+              const F penalty = IpdgHelper::penalty(sigma, gamma, intersection.geometry().volume(), beta);
+              for (size_t ii = 0; ii < local_keys_assosiated_with_intersection.size(); ++ii) {
+                const size_t local_key_index = local_keys_assosiated_with_intersection[ii];
+                for (size_t jj = 0; jj < intersection_Pk_basis.size(); ++jj)
+                  lhs.add_to_entry(ii,
+                                   jj,
+                                   quadrature_weight * integration_factor * (rt_basis_values[local_key_index] * normal)
+                                       * intersection_Pk_basis_values[jj]);
+              }
+              for (size_t jj = 0; jj < intersection_Pk_basis.size(); ++jj)
+                rhs[jj] += quadrature_weight * integration_factor
+                           * (penalty * source_value_element - normal * (diffusion_element * source_grad_element))
+                           * intersection_Pk_basis_values[jj];
+            }
+          }
+          if (there_are_intersection_dofs_to_determine) {
+            XT::LA::CommonDenseVector<F> intersection_dofs(local_keys_assosiated_with_intersection.size(), 0);
+            try {
+              intersection_dofs = XT::LA::solve(lhs, rhs);
+            } catch (const XT::LA::Exceptions::linear_solver_failed& ee) {
+              DUNE_THROW(Exceptions::interpolation_error,
+                         "Failed to solve for DoFs associated with intersection "
+                             << intersection_index << ", this was the original error:\n   " << ee.what());
+            }
+            for (size_t ii = 0; ii < local_keys_assosiated_with_intersection.size(); ++ii) {
+              const size_t local_key_index = local_keys_assosiated_with_intersection[ii];
+              assert(!local_key_was_handled[local_key_index]);
+              local_range->dofs()[local_key_index] = intersection_dofs[ii];
+              local_key_was_handled[local_key_index] = true;
+            }
+          }
+        }
+      }
+      // determine the volume dofs
+      // ... to be added ...
+      // final checks that there are no other dofs left
+      for (size_t ii = 0; ii < sz; ++ii)
+        DUNE_THROW_IF(!local_key_was_handled[ii],
+                      Exceptions::interpolation_error,
+                      "The following DoF is neither associated with an intersection, nor with the element!"
+                          << "\n   local DoF index: " << ii
+                          << "\n   associated local_key: " << rt_fe.coefficients().local_key(ii));
+    }
+  } // ... apply(...)
+
+private:
+  const AssemblyGridViewType& assembly_grid_view_;
+  const SourceSpaceType& source_space_;
+  const RangeSpaceType& range_space_;
+  const XT::Functions::GridFunctionInterface<E>& diffusion_factor_;
+  const XT::Functions::GridFunctionInterface<E, d, d>& diffusion_tensor_;
+  const FiniteVolumeMapper<AssemblyGridViewType> element_mapper_;
+}; // class IpdgFluxReconstructionOperator
+
+
+template <class MatrixType,
+          class AssemblyGridViewType,
+          class SGV,
+          class RGV,
+          LocalEllipticIpdgIntegrands::Method ipdg = LocalEllipticIpdgIntegrands::default_method>
+IpdgFluxReconstructionOperator<MatrixType, AssemblyGridViewType, ipdg, SGV, RGV> make_ipdg_flux_reconstruction_operator(
+    const AssemblyGridViewType& assembly_grid_view,
+    const SpaceInterface<SGV>& source_space,
+    const SpaceInterface<RGV, RGV::dimension>& range_space,
+    const XT::Functions::GridFunctionInterface<XT::Grid::extract_entity_t<AssemblyGridViewType>>& diffusion_factor,
+    const XT::Functions::GridFunctionInterface<XT::Grid::extract_entity_t<AssemblyGridViewType>,
+                                               RGV::dimension,
+                                               RGV::dimension>& diffusion_tensor)
+{
+  return IpdgFluxReconstructionOperator<MatrixType, AssemblyGridViewType, ipdg, SGV, RGV>(
+      assembly_grid_view, source_space, range_space, diffusion_factor, diffusion_tensor);
+}
+
+
+template <class MatrixType, LocalEllipticIpdgIntegrands::Method ipdg, class AssemblyGridViewType, class SGV, class RGV>
+IpdgFluxReconstructionOperator<MatrixType, AssemblyGridViewType, ipdg, SGV, RGV> make_ipdg_flux_reconstruction_operator(
+    const AssemblyGridViewType& assembly_grid_view,
+    const SpaceInterface<SGV>& source_space,
+    const SpaceInterface<RGV, RGV::dimension>& range_space,
+    const XT::Functions::GridFunctionInterface<XT::Grid::extract_entity_t<AssemblyGridViewType>>& diffusion_factor,
+    const XT::Functions::GridFunctionInterface<XT::Grid::extract_entity_t<AssemblyGridViewType>,
+                                               RGV::dimension,
+                                               RGV::dimension>& diffusion_tensor)
+{
+  return IpdgFluxReconstructionOperator<MatrixType, AssemblyGridViewType, ipdg, SGV, RGV>(
+      assembly_grid_view, source_space, range_space, diffusion_factor, diffusion_tensor);
+}
+
+
+} // namespace GDT
+} // namespace Dune
+
+#endif // DUNE_GDT_OPERATORS_IPDG_FLUX_RECONSTRUCTION_HH
-- 
GitLab