diff --git a/dune/gfe/interpolationderivatives.hh b/dune/gfe/interpolationderivatives.hh
index 9e4002eb3a1445e62fe998642a9d6be6cddfa26d..70465837bff75d86b236260392ae96484761be7d 100644
--- a/dune/gfe/interpolationderivatives.hh
+++ b/dune/gfe/interpolationderivatives.hh
@@ -698,6 +698,327 @@ namespace Dune::GFE
     }
   };
 
+  /** \brief Compute derivatives of nonconforming interpolation with respect to the coefficients
+   *
+   * This is the specialization of the InterpolationDerivatives class for the nonconforming
+   * interpolation.  No matter what the target space is, the interpolation is always
+   * Euclidean in the surrounding space.
+   */
+  template <int gridDim, typename ctype, typename LocalFiniteElement, typename TargetSpace>
+  class InterpolationDerivatives<LocalProjectedFEFunction<gridDim, ctype, LocalFiniteElement, TargetSpace, false> >
+  {
+    using LocalInterpolationRule = LocalProjectedFEFunction<gridDim, ctype, LocalFiniteElement, TargetSpace, false>;
+    using CoordinateType = typename TargetSpace::CoordinateType;
+
+    constexpr static auto blocksize = TargetSpace::TangentVector::dimension;
+    constexpr static auto embeddedBlocksize = TargetSpace::EmbeddedTangentVector::dimension;
+
+    //////////////////////////////////////////////////////////////////////
+    //  Data members
+    //////////////////////////////////////////////////////////////////////
+
+    const LocalInterpolationRule& localInterpolationRule_;
+
+    // Whether derivatives of the interpolation value are to be computed
+    const bool doValue_;
+
+    // Whether derivatives of the derivative of the interpolation
+    // with respect to space are to be computed
+    const bool doDerivative_;
+
+    // Values of all scalar shape functions at the point we are bound to
+    std::vector<FieldVector<double,1> > shapeFunctionValues_;
+
+    // Gradients of all scalar shape functions at the point we are bound to
+    // TODO: The second dimension must be WorldDim
+    std::vector<FieldMatrix<double,1,gridDim> > shapeFunctionGradients_;
+
+    // TODO: Don't hardcode FieldMatrix
+    std::vector<FieldMatrix<double,blocksize,embeddedBlocksize> > orthonormalFrames_;
+
+  public:
+    InterpolationDerivatives(const LocalInterpolationRule& localInterpolationRule,
+                             bool doValue, bool doDerivative)
+      : localInterpolationRule_(localInterpolationRule)
+      , doValue_(doValue)
+      , doDerivative_(doDerivative)
+    {
+      // Precompute the orthonormal frames
+      orthonormalFrames_.resize(localInterpolationRule_.size());
+
+      for (size_t i=0; i<localInterpolationRule_.size(); ++i)
+        orthonormalFrames_[i] = localInterpolationRule_.coefficient(i).orthonormalFrame();
+    }
+
+    /** \brief Bind the objects to a particular evaluation point
+     *
+     * In particular, this computes the value of the interpolation function at that point,
+     * and the derivative at that point with respect to space.  The default implementation
+     * uses ADOL-C to tape these evaluations.  That is required for the evaluateDerivatives
+     * method below to be able to compute the derivatives with respect to the coefficients.
+     *
+     *  \param[in]  tapeNumber      Number of the ADOL-C tape, not used by this specialization
+     *  \param[in]  localPos        Local position where the FE function is evaluated
+     *  \param[out] value           The function value at the local configuration
+     *  \param[out] derivative      The derivative of the interpolation function
+     *                              with respect to the evaluation point
+     */
+    template <typename Element>
+    void bind(short tapeNumber,
+              const Element& element,
+              const typename Element::Geometry::LocalCoordinate& localPos,
+              typename TargetSpace::CoordinateType& valueGlobalCoordinates,
+              typename LocalInterpolationRule::DerivativeType& derivative)
+    {
+      const auto geometryJacobianInverse = element.geometry().jacobianInverse(localPos);
+
+      const auto& scalarFiniteElement = localInterpolationRule_.localFiniteElement();
+      const auto& localBasis = scalarFiniteElement.localBasis();
+
+      // Get shape function values
+      localBasis.evaluateFunction(localPos, shapeFunctionValues_);
+
+      // Get shape function Jacobians
+      localBasis.evaluateJacobian(localPos, shapeFunctionGradients_);
+
+      for (auto& gradient : shapeFunctionGradients_)
+        gradient = gradient * geometryJacobianInverse;
+
+      std::fill(valueGlobalCoordinates.begin(), valueGlobalCoordinates.end(), 0.0);
+      for (size_t i=0; i<shapeFunctionValues_.size(); i++)
+        valueGlobalCoordinates.axpy(shapeFunctionValues_[i][0],
+                                    localInterpolationRule_.coefficient(i).globalCoordinates());
+
+      // Derivatives
+      for (size_t i=0; i<localInterpolationRule_.size(); i++)
+        for (int j=0; j<TargetSpace::CoordinateType::dimension; j++)
+          derivative[j].axpy(localInterpolationRule_.coefficient(i).globalCoordinates()[j],
+                             shapeFunctionGradients_[i][0]);
+    }
+
+    /** \brief Compute first and second derivatives of the FE interpolation
+     *
+     * This code assumes that `bind` has been called before.
+     *
+     *  \param[in]  tapeNumber            The tape number to be used by ADOL-C. Not used by this specialization
+     *  \param[in]  weights               Vector of weights that the second derivative is contracted with
+     *  \param[out] embeddedFirstDerivative       Derivative of the FE interpolation wrt the coefficients
+     *  \param[out] firstDerivative       Derivative of the FE interpolation wrt the coefficients
+     *  \param[out] secondDerivative      Second derivative of the FE interpolation,
+     *                                    contracted with the weight vector
+     */
+    void evaluateDerivatives(short tapeNumber,
+                             const double* weights,
+                             Matrix<double>& embeddedFirstDerivative,
+                             Matrix<double>& firstDerivative,
+                             Matrix<FieldMatrix<double,blocksize,blocksize> >& secondDerivative) const
+    {
+      const size_t nDofs = localInterpolationRule_.size();
+
+      ////////////////////////////////////////////////////////////////////
+      //  The first derivative of the finite element interpolation
+      ////////////////////////////////////////////////////////////////////
+
+      Matrix<double> partialDerivative(embeddedFirstDerivative.N(), embeddedFirstDerivative.M());
+      partialDerivative = 0.0;
+
+      // First derivatives of the function value wrt to the FE coefficients
+      for (size_t i=0; i<nDofs; ++i)
+        for (int j=0; j<embeddedBlocksize; ++j)
+          partialDerivative[j][i*embeddedBlocksize+j] = shapeFunctionValues_[i][0];
+
+      // First derivatives of the function gradient wrt to the FE coefficients
+      for (size_t i=0; i<nDofs; ++i)
+        for (int j=0; j<embeddedBlocksize; ++j)
+          for (int k=0; k<gridDim; ++k)
+            partialDerivative[embeddedBlocksize + j*gridDim + k][i*embeddedBlocksize+j] = shapeFunctionGradients_[i][0][k];
+
+      // Euclidean derivative: Derivatives in the direction of the projected canonical vectors
+      for (std::size_t j=0; j<nDofs; ++j)
+      {
+        for (int k=0; k<embeddedBlocksize; k++)
+        {
+          // k-th canonical unit vector
+          FieldVector<double,embeddedBlocksize> direction(0);
+          direction[k] = 1.0;
+
+          // Project it onto tangent space at the j-th coefficient
+          auto projectedDirection = localInterpolationRule_.coefficient(j).projectOntoTangentSpace(direction);
+
+          // The interpolation value
+          if (doValue_)
+          {
+            for (size_t i=0; i<CoordinateType::size(); ++i)
+            {
+              // Alias name, for shorter notation
+              auto& entry = embeddedFirstDerivative[i][j*embeddedBlocksize+k];
+
+              entry = 0;
+
+              for (int l=0; l<embeddedBlocksize; l++)
+                entry += projectedDirection[l] * partialDerivative[i][j*embeddedBlocksize+l];
+            }
+          }
+
+          // The interpolation derivative with respect to space
+          if (doDerivative_)
+          {
+            for (size_t alpha=0; alpha<CoordinateType::size(); alpha++)
+            {
+              for (size_t beta=0; beta<gridDim; beta++)
+              {
+                // Alias name, for shorter notation
+                auto& entry = embeddedFirstDerivative[CoordinateType::size() + alpha*gridDim+beta][j*embeddedBlocksize+k];
+
+                entry = 0;
+
+                for (int l=0; l<embeddedBlocksize; l++)
+                  entry += projectedDirection[l] * partialDerivative[CoordinateType::size() + alpha*gridDim+beta][j*embeddedBlocksize+l];
+              }
+            }
+          }
+        }
+      }
+
+      // Riemannian derivative: Derivatives in the directions of the orthonormal frame vectors
+      for (std::size_t j=0; j<nDofs; ++j)
+      {
+        for (int k=0; k<blocksize; k++)
+        {
+          // k-th canonical unit tangent vector
+          FieldVector<double,embeddedBlocksize> direction = orthonormalFrames_[j][k];
+
+          // The interpolation value
+          if (doValue_)
+          {
+            for (size_t i=0; i<CoordinateType::size(); ++i)
+            {
+              // Alias name, for shorter notation
+              auto& entry = firstDerivative[i][j*blocksize+k];
+
+              entry = 0;
+
+              for (int l=0; l<embeddedBlocksize; l++)
+                entry += direction[l] * partialDerivative[i][j*embeddedBlocksize+l];
+            }
+          }
+
+          // The interpolation derivative with respect to space
+          if (doDerivative_)
+          {
+            for (size_t alpha=0; alpha<CoordinateType::size(); alpha++)
+            {
+              for (size_t beta=0; beta<gridDim; beta++)
+              {
+                // Alias name, for shorter notation
+                auto& entry = firstDerivative[CoordinateType::size() + alpha*gridDim+beta][j*blocksize+k];
+
+                entry = 0;
+
+                for (int l=0; l<embeddedBlocksize; l++)
+                  entry += direction[l] * partialDerivative[CoordinateType::size() + alpha*gridDim+beta][j*embeddedBlocksize+l];
+              }
+            }
+          }
+        }
+      }
+
+      ////////////////////////////////////////////////////////////////////
+      //  The second derivative of the finite element interpolation
+      ////////////////////////////////////////////////////////////////////
+
+      // From this, compute the Hessian with respect to the manifold (which we assume here is embedded
+      // isometrically in a Euclidean space.
+      // For the detailed explanation of the following see: Absil, Mahoney, Trumpf, "An extrinsic look
+      // at the Riemannian Hessian".
+
+      if (!doDerivative_)
+        return;
+
+      secondDerivative = 0;
+
+
+      //////////////////////////////////////////////////////////////////////////
+      //  The projection of the Euclidean first derivative of the finite element interpolation
+      //  onto the normal space of the unit sphere.
+      //////////////////////////////////////////////////////////////////////////
+
+      Matrix<double> normalFirstDerivative(embeddedFirstDerivative.N(), nDofs);
+
+      for (std::size_t j=0; j<nDofs; ++j)
+      {
+        // The space is a sphere.  Therefore the normal at a point x is x itself.
+        const auto& direction = localInterpolationRule_.coefficient(j).globalCoordinates();
+
+        // The interpolation value
+        if (doValue_)
+        {
+          for (size_t i=0; i<CoordinateType::size(); ++i)
+          {
+            // Alias name, for shorter notation
+            auto& entry = normalFirstDerivative[i][j];
+
+            entry = 0;
+
+            for (int l=0; l<embeddedBlocksize; l++)
+              entry += direction[l] * partialDerivative[i][j*embeddedBlocksize+l];
+          }
+        }
+
+        // The interpolation derivative with respect to space
+        if (doDerivative_)
+        {
+          for (size_t alpha=0; alpha<CoordinateType::size(); alpha++)
+          {
+            for (size_t beta=0; beta<gridDim; beta++)
+            {
+              // Alias name, for shorter notation
+              auto& entry = normalFirstDerivative[CoordinateType::size() + alpha*gridDim+beta][j];
+
+              entry = 0;
+
+              for (int l=0; l<embeddedBlocksize; l++)
+                entry += direction[l] * partialDerivative[CoordinateType::size() + alpha*gridDim+beta][j*embeddedBlocksize+l];
+            }
+          }
+        }
+      }
+
+      // Project Euclidean gradient onto the normal space
+      Matrix<FieldMatrix<double,1,embeddedBlocksize> > projectedGradient(embeddedFirstDerivative.N(),nDofs);
+
+      for (size_t i=0; i<embeddedFirstDerivative.N(); i++)
+        for (size_t j=0; j<nDofs; j++)
+          projectedGradient[i][j][0] = normalFirstDerivative[i][j] * localInterpolationRule_.coefficient(j).globalCoordinates();
+
+      //////////////////////////////////////////////////////////////////////////////////////
+      //  Further correction due to non-planar configuration space
+      //  + \mathfrak{A}_x(z,P^\orth_x \partial f)
+      //////////////////////////////////////////////////////////////////////////////////////
+
+      // The configuration space is a product of spheres.  Therefore the Weingarten map
+      // has only block-diagonal entries.
+      for (size_t row=0; row<nDofs; row++)
+      {
+        for (size_t subRow=0; subRow<blocksize; subRow++)
+        {
+          typename TargetSpace::EmbeddedTangentVector z = orthonormalFrames_[row][subRow];
+
+          for (size_t i=0; i<embeddedFirstDerivative.N(); i++)
+          {
+            auto tmp1 = localInterpolationRule_.coefficient(row).weingarten(z,projectedGradient[i][row][0]);
+
+            typename TargetSpace::TangentVector tmp2;
+            orthonormalFrames_[row].mv(tmp1,tmp2);
+
+            for (size_t subCol=0; subCol<blocksize; subCol++)
+              secondDerivative[row][row][subRow][subCol] += weights[i]* tmp2[subCol];
+          }
+        }
+      }
+    }
+  };
+
   /** \brief Compute derivatives of projection-based interpolation to UnitVector with respect to the coefficients
    *
    * This is the specialization of the InterpolationDerivatives class for the UnitVector target space
diff --git a/test/localintegralstiffnesstest.cc b/test/localintegralstiffnesstest.cc
index 8c5c450453f0850b326d5da04c9cd5255aec9bfc..57ca582cc8c6a61e8cd269bed1135989371a8e40 100644
--- a/test/localintegralstiffnesstest.cc
+++ b/test/localintegralstiffnesstest.cc
@@ -39,7 +39,7 @@ using namespace Dune;
 using namespace Dune::Indices;
 using namespace Functions::BasisFactory;
 
-enum InterpolationType {Geodesic, ProjectionBased};
+enum InterpolationType {Geodesic, ProjectionBased, Nonconforming};
 
 
 template <class GridView, InterpolationType interpolationType>
@@ -128,16 +128,20 @@ int testHarmonicMapIntoSphere(TestSuite& test, const GridView& gridView)
   }
   else
   {
-    std::cout << "Using projection-based interpolation" << std::endl;
+    if (interpolationType==ProjectionBased)
+      std::cout << "Using projection-based interpolation" << std::endl;
+    else
+      std::cout << "Using nonconforming interpolation" << std::endl;
+
     using LocalInterpolationRule = GFE::LocalProjectedFEFunction<dim,
         typename GridView::ctype,
         decltype(feBasis.localView().tree().finiteElement()),
-        TargetSpace>;
+        TargetSpace, interpolationType!=Nonconforming>;
 
     using ALocalInterpolationRule = GFE::LocalProjectedFEFunction<dim,
         typename GridView::ctype,
         decltype(feBasis.localView().tree().finiteElement()),
-        ATargetSpace>;
+        ATargetSpace, interpolationType!=Nonconforming>;
 
     // Assemble using the old assembler
     auto energy = std::make_shared<GFE::LocalIntegralEnergy<FEBasis, ALocalInterpolationRule,ATargetSpace> >(harmonicDensityA);
@@ -523,6 +527,7 @@ int main (int argc, char *argv[])
   // TODO: Use test framework
   testHarmonicMapIntoSphere<GridView,Geodesic>(test, gridView);
   testHarmonicMapIntoSphere<GridView,ProjectionBased>(test, gridView);
+  testHarmonicMapIntoSphere<GridView,Nonconforming>(test, gridView);
 
   testCosseratBulkModel<GridView,Geodesic>(test, gridView);
   testCosseratBulkModel<GridView,ProjectionBased>(test, gridView);