From 79e02a3c32cc1441584a4e5e6ae5e81c1757cd99 Mon Sep 17 00:00:00 2001
From: Elias Pipping <elias.pipping@fu-berlin.de>
Date: Sat, 31 May 2014 01:48:47 +0200
Subject: [PATCH] [Algorit] Carsten: Use eigenvalue approximation

---
 dune/tectonic/ellipticenergy.hh  | 63 --------------------------------
 dune/tectonic/minimisation.hh    | 54 +++++++--------------------
 dune/tectonic/myblockproblem.hh  | 52 ++++++++++++++++----------
 dune/tectonic/quadraticenergy.hh | 19 ++++++++++
 src/sand-wedge-data/parset.cfg   |  3 --
 5 files changed, 66 insertions(+), 125 deletions(-)
 delete mode 100644 dune/tectonic/ellipticenergy.hh
 create mode 100644 dune/tectonic/quadraticenergy.hh

diff --git a/dune/tectonic/ellipticenergy.hh b/dune/tectonic/ellipticenergy.hh
deleted file mode 100644
index e3405a1c..00000000
--- a/dune/tectonic/ellipticenergy.hh
+++ /dev/null
@@ -1,63 +0,0 @@
-#ifndef DUNE_TECTONIC_ELLIPTICENERGY_HH
-#define DUNE_TECTONIC_ELLIPTICENERGY_HH
-
-#include <dune/common/fmatrix.hh>
-#include <dune/common/fvector.hh>
-#include <dune/common/stdstreams.hh>
-
-#include <dune/fufem/arithmetic.hh>
-#include <dune/solvers/common/interval.hh>
-
-#include "localfriction.hh"
-
-template <size_t dim> class EllipticEnergy {
-public:
-  using LocalVector = Dune::FieldVector<double, dim>;
-  using LocalMatrix = Dune::FieldMatrix<double, dim, dim>;
-
-  using Nonlinearity = LocalFriction<dim>;
-
-  EllipticEnergy(LocalMatrix const &A, LocalVector const &b,
-                 std::shared_ptr<Nonlinearity const> phi,
-                 typename Dune::BitSetVector<dim>::const_reference ignore)
-      : A(A), b(b), phi(phi), ignore(ignore) {}
-
-  double operator()(LocalVector const &v) const {
-    return computeEnergy(A, v, b) + (*phi)(v);
-  }
-
-  LocalMatrix const &A;
-  LocalVector const &b;
-  std::shared_ptr<Nonlinearity const> const phi;
-  typename Dune::BitSetVector<dim>::const_reference const ignore;
-
-  void descentDirection(LocalVector const &x, LocalVector &y) const {
-    if (x.two_norm() >= 0.0) {
-      A.mv(x, y);
-      y -= b;
-      phi->addGradient(x, y);
-
-      for (size_t i = 0; i < dim; ++i)
-        if (ignore[i])
-          y[i] = 0;
-      y *= -1;
-    } else {
-      A.mv(x, y);
-      y -= b;
-
-      for (size_t i = 0; i < dim; ++i)
-        if (ignore[i])
-          y[i] = 0;
-      y *= -1;
-
-      // The contribution of phi to the directional derivative is the
-      // same for all directions here! Choose the one that is best of
-      // the smooth part and check if we have overall decline
-      Dune::Solvers::Interval<double> D;
-      phi->directionalSubDiff(x, y, D);
-      if (D[1] - y.two_norm2() >= 0.0)
-        y = 0.0;
-    }
-  }
-};
-#endif
diff --git a/dune/tectonic/minimisation.hh b/dune/tectonic/minimisation.hh
index 3ae1464a..4d4b172b 100644
--- a/dune/tectonic/minimisation.hh
+++ b/dune/tectonic/minimisation.hh
@@ -11,57 +11,31 @@
 
 #include "mydirectionalconvexfunction.hh"
 
+// Warning: this exploits the property v*x = 0
 template <class Functional>
 double lineSearch(Functional const &J,
                   typename Functional::LocalVector const &x,
                   typename Functional::LocalVector const &v,
                   Bisection const &bisection) {
   MyDirectionalConvexFunction<typename Functional::Nonlinearity> const JRest(
-      computeDirectionalA(J.A, v), computeDirectionalb(J.A, J.b, x, v), *J.phi,
-      x, v);
-
+      J.alpha * v.two_norm2(), J.b * v, *J.phi, x, v);
   int count;
   return bisection.minimize(JRest, 0.0, 0.0, count);
 }
 
+/** Minimise a quadratic problem, for which both the quadratic and the
+    nonlinear term have gradients which point in the direction of
+    their arguments */
 template <class Functional>
 void minimise(Functional const &J, typename Functional::LocalVector &x,
-              size_t steps, Bisection const &bisection) {
-  using LocalVector = typename Functional::LocalVector;
-  auto const diff = [](LocalVector const &a, LocalVector const &b) {
-    LocalVector tmp = a;
-    tmp -= b;
-    return tmp.two_norm();
-  };
-
-  LocalVector x_initial = x;
-  LocalVector x_o = x;
-
-  for (size_t step = 0; step < steps; ++step) {
-    LocalVector v;
-    J.descentDirection(x, v);
-
-    double const vnorm = v.two_norm();
-    if (vnorm <= 0.0)
-      return;
-    v /= vnorm;
-
-    double const alpha = lineSearch(J, x, v, bisection);
-    Arithmetic::addProduct(x, alpha, v);
-
-    if (alpha < 1e-14) // TODO
-      break;
-
-    double const correction = diff(x, x_o);
-    double const overallCorrection = diff(x, x_initial);
-    if (overallCorrection <= 0.0)
-      return;
-
-    double const correctionQuotient = correction / overallCorrection;
-    if (correctionQuotient < 0.1) // enough descent; TODO
-      break;
-
-    x_o = x;
-  }
+              Bisection const &bisection) {
+  auto v = J.b;
+  double const vnorm = v.two_norm();
+  if (vnorm <= 0.0)
+    return;
+  v /= vnorm;
+
+  double const alpha = lineSearch(J, x, v, bisection);
+  Arithmetic::addProduct(x, alpha, v);
 }
 #endif
diff --git a/dune/tectonic/myblockproblem.hh b/dune/tectonic/myblockproblem.hh
index 9770d244..0f43db0b 100644
--- a/dune/tectonic/myblockproblem.hh
+++ b/dune/tectonic/myblockproblem.hh
@@ -6,6 +6,7 @@
 #include <dune/common/bitsetvector.hh>
 #include <dune/common/nullptr.hh>
 #include <dune/common/parametertree.hh>
+#include <dune/common/fmatrixev.hh>
 
 #include <dune/fufem/arithmetic.hh>
 #include <dune/solvers/common/interval.hh>
@@ -13,10 +14,10 @@
 #include <dune/tnnmg/problem-classes/bisection.hh>
 #include <dune/tnnmg/problem-classes/blocknonlineargsproblem.hh>
 
-#include "ellipticenergy.hh"
 #include "globalnonlinearity.hh"
 #include "minimisation.hh"
 #include "mydirectionalconvexfunction.hh"
+#include "quadraticenergy.hh"
 
 /** \brief Base class for problems where each block can be solved with a
  * modified gradient method */
@@ -56,8 +57,16 @@ class MyBlockProblem : /* NOT PUBLIC */ BlockNonlinearGSProblem<ConvexProblem> {
 
   MyBlockProblem(Dune::ParameterTree const &parset, ConvexProblem &problem)
       : BNGSP(parset, problem),
+        maxEigenvalues_(problem.f.size()),
         parset_(parset),
-        localBisection(0.0, 1.0, 1e-12, false) {}
+        localBisection(0.0, 1.0, 1e-12, false) {
+    for (size_t i = 0; i < problem.f.size(); ++i) {
+      LocalVectorType eigenvalues;
+      Dune::FMatrixHelp::eigenValues(problem.A[i][i], eigenvalues);
+      maxEigenvalues_[i] =
+          *std::max_element(std::begin(eigenvalues), std::end(eigenvalues));
+    }
+  }
 
   std::string getOutput(bool header = false) const {
     if (header) {
@@ -188,10 +197,12 @@ class MyBlockProblem : /* NOT PUBLIC */ BlockNonlinearGSProblem<ConvexProblem> {
 
   /** \brief Constructs and returns an iterate object */
   IterateObject getIterateObject() {
-    return IterateObject(parset_, localBisection, problem_);
+    return IterateObject(parset_, localBisection, problem_, maxEigenvalues_);
   }
 
 private:
+  std::vector<double> maxEigenvalues_;
+
   Dune::ParameterTree const &parset_;
 
   // problem data
@@ -213,10 +224,11 @@ class MyBlockProblem<ConvexProblem>::IterateObject {
    * \param problem The problem including quadratic part and nonlinear part
    */
   IterateObject(Dune::ParameterTree const &parset, Bisection const &bisection,
-                ConvexProblem const &problem)
+                ConvexProblem const &problem,
+                std::vector<double> const &maxEigenvalues)
       : problem(problem),
-        bisection_(bisection),
-        localsteps(parset.get<size_t>("localsolver.steps")) {}
+        maxEigenvalues_(maxEigenvalues),
+        bisection_(bisection) {}
 
 public:
   /** \brief Set the current iterate */
@@ -240,22 +252,24 @@ class MyBlockProblem<ConvexProblem>::IterateObject {
       LocalVectorType &ui, size_t m,
       typename Dune::BitSetVector<block_size>::const_reference ignore) {
     {
-      LocalMatrixType const *localA = nullptr;
-      LocalVectorType localb(problem.f[m]);
-
+      LocalVectorType localb = problem.f[m];
       auto const end = std::end(problem.A[m]);
       for (auto it = std::begin(problem.A[m]); it != end; ++it) {
         size_t const j = it.index();
-        if (j == m)
-          localA = &(*it); // localA = A[m][m]
-        else
-          Arithmetic::subtractProduct(localb, *it, u[j]);
+        Arithmetic::subtractProduct(localb, *it, u[j]); // also the diagonal!
       }
-      assert(localA != nullptr);
+      Arithmetic::addProduct(localb, maxEigenvalues_[m], u[m]);
 
-      auto const phi = problem.phi.restriction(m);
-      EllipticEnergy<block_size> localJ(*localA, localb, phi, ignore);
-      minimise(localJ, ui, localsteps, bisection_);
+      // We minimise over an affine subspace
+      for (size_t j = 0; j < block_size; ++j)
+        if (ignore[j])
+          localb[j] = 0;
+        else
+          ui[j] = 0;
+
+      QuadraticEnergy<typename ConvexProblem::NonlinearityType::Friction>
+      localJ(maxEigenvalues_[m], localb, problem.phi.restriction(m));
+      minimise(localJ, ui, bisection_);
     }
   }
 
@@ -263,12 +277,12 @@ class MyBlockProblem<ConvexProblem>::IterateObject {
   // problem data
   ConvexProblem const &problem;
 
+  std::vector<double> maxEigenvalues_;
+
   Bisection const bisection_;
 
   // state data for smoothing procedure used by:
   // setIterate, updateIterate, solveLocalProblem
   VectorType u;
-
-  size_t const localsteps;
 };
 #endif
diff --git a/dune/tectonic/quadraticenergy.hh b/dune/tectonic/quadraticenergy.hh
new file mode 100644
index 00000000..f724a1e8
--- /dev/null
+++ b/dune/tectonic/quadraticenergy.hh
@@ -0,0 +1,19 @@
+#ifndef DUNE_TECTONIC_QUADRATICENERGY_HH
+#define DUNE_TECTONIC_QUADRATICENERGY_HH
+
+#include <memory>
+
+template <class NonlinearityTEMPLATE> class QuadraticEnergy {
+public:
+  using Nonlinearity = NonlinearityTEMPLATE;
+  using LocalVector = typename Nonlinearity::VectorType;
+
+  QuadraticEnergy(double alpha, LocalVector const &b,
+                  std::shared_ptr<Nonlinearity const> phi)
+      : alpha(alpha), b(b), phi(phi) {}
+
+  double const alpha;
+  LocalVector const &b;
+  std::shared_ptr<Nonlinearity const> const phi;
+};
+#endif
diff --git a/src/sand-wedge-data/parset.cfg b/src/sand-wedge-data/parset.cfg
index 4287864a..f6ad4f34 100644
--- a/src/sand-wedge-data/parset.cfg
+++ b/src/sand-wedge-data/parset.cfg
@@ -73,6 +73,3 @@ post               = 3
 pre   = 1
 multi = 5 # number of multigrid steps
 post  = 0
-
-[localsolver]
-steps = 100
-- 
GitLab