From b4f8b75008bfb9be3662e9ddc456823b4d38b306 Mon Sep 17 00:00:00 2001
From: Oliver Sander <sander@igpm.rwth-aachen.de>
Date: Fri, 19 Feb 2010 11:20:58 +0000
Subject: [PATCH] implement the analytical gradient.  Not tested yet

[[Imported from SVN: r5593]]
---
 src/localgeodesicfefunction.hh | 62 +++++++++++++++++++++++++++++++---
 1 file changed, 58 insertions(+), 4 deletions(-)

diff --git a/src/localgeodesicfefunction.hh b/src/localgeodesicfefunction.hh
index aaf8af6e..6b63c877 100644
--- a/src/localgeodesicfefunction.hh
+++ b/src/localgeodesicfefunction.hh
@@ -39,6 +39,16 @@ public:
 
 private:
 
+    static Dune::FieldVector<ctype,dim+1> barycentricCoordinates(const Dune::FieldVector<ctype,dim>& local) {
+        Dune::FieldVector<ctype,dim+1> result;
+        result[0] = 1;
+        for (int i=0; i<dim; i++) {
+            result[0]  -= local[i];
+            result[i+1] = local[i];
+        }
+    }
+        
+
     /** \brief The coefficient vector */
     std::vector<TargetSpace> coefficients_;
 
@@ -81,6 +91,7 @@ evaluateDerivative(const Dune::FieldVector<ctype, dim>& local)
 {
     Dune::FieldMatrix<ctype, EmbeddedTangentVector::size, dim> result;
 
+#if 0  // this is probably faster than the general implementation, but we leave it out for testing purposes
     if (dim==1) {
 
         EmbeddedTangentVector tmp = TargetSpace::interpolateDerivative(coefficients_[0], coefficients_[1], local[0]);
@@ -89,14 +100,57 @@ evaluateDerivative(const Dune::FieldVector<ctype, dim>& local)
             result[i][0] = tmp[i];
 
     }
+#endif
 
-    if (dim==2) {
+    // ////////////////////////////////////////////////////////////////////////
+    //  The derivative is evaluated using the implicit function theorem.
+    //  Hence we need to solve a small system of linear equations.
+    // ////////////////////////////////////////////////////////////////////////
 
-        DUNE_THROW(Dune::NotImplemented, "evaluateDerivative");
+    // the function value at the point where we are evaluation the derivative
+    TargetSpace q = evaluate(local);
 
-    }
+    // the matrix that turns coordinates on the reference simplex into coordinates on the standard simplex
+    Dune::FieldMatrix<ctype,dim+1,dim> B;
+    B[0] = -1;
+    for (int i=0; i<dim; i++)
+        for (int j=0; j<dim; j++)
+            B[i+1][j] = (i==j);
+
+    // compute negative derivate of F(w,q) (the derivative of the weighted distance fctl) wrt to w
+    Dune::FieldMatrix<ctype,dim+1,dim+1> dFdw;
+    for (int i=0; i<dim+1; i++)
+        dFdw[i] = TargetSpace::derivativeOfDistanceSquaredWRTSecondArgument(coefficients_[i], q);
+
+    dFdw *= -1;
+
+    // multiply the two previous matrices: the result is the right hand side
+    Dune::FieldMatrix<ctype,dim+1,dim> RHS;
+    Dune::FMatrixHelp::multMatrix(dFdw,B, RHS);
+
+    // the actual system matrix
+    Dune::FieldVector<ctype,dim+1> w = barycentricCoordinates(local);
 
-    assert(dim==1 || dim==2);
+    Dune::FieldMatrix<ctype,dim+1,dim+1> dFdq(0);
+    for (int i=0; i<dim+1; i++)
+        dFdq.axpy(w[i], TargetSpace::secondDerivativeOfDistanceSquaredWRTSecondArgument(coefficients_[i], q));
+
+    // ////////////////////////////////////
+    //   solve the system
+    // ////////////////////////////////////
+
+    for (int i=0; i<dim; i++) {
+
+        Dune::FieldVector<ctype,dim+1> rhs, x;
+        for (int j=0; j<dim+1; j++)
+            rhs[j] = RHS[j][i];
+
+        dFdq.solve(x, rhs);
+
+        for (int j=0; j<dim+1; j++)
+            result[j][i] = x[j];
+
+    }
 
     return result;
 }
-- 
GitLab