-
Sander, Oliver authoredSander, Oliver authored
localquickanddirtyfefunction.hh 5.75 KiB
#ifndef DUNE_GFE_LOCALQUICKANDDIRTYFEFUNCTION_HH
#define DUNE_GFE_LOCALQUICKANDDIRTYFEFUNCTION_HH
#include <vector>
#include <dune/common/fvector.hh>
#include <dune/geometry/type.hh>
#include <dune/gfe/rotation.hh>
#include <dune/gfe/linearalgebra.hh>
namespace Dune {
namespace GFE {
/** \brief Interpolate on a manifold, as fast as we can
*
* This class implements interpolation of values on a manifold in a 'quick-and-dirty' way.
* No particular interpolation rule is used consistenly for all manifolds. Rather, it is
* used whatever is quickest and 'reasonable' for any given space. The reason to have this
* is to provide initial iterates for the iterative solvers used to solve the local
* geodesic-FE minimization problems.
*
* \note In particular, we currently do not implement the derivative of the interpolation,
* because it is not needed for producing initial iterates!
*
* \tparam dim Dimension of the reference element
* \tparam ctype Type used for coordinates on the reference element
* \tparam LocalFiniteElement A Lagrangian finite element whose shape functions define the interpolation weights
* \tparam TargetSpace The manifold that the function takes its values in
*/
template <int dim, class ctype, class LocalFiniteElement, class TS>
class LocalQuickAndDirtyFEFunction
{
public:
using TargetSpace=TS;
private:
typedef typename TargetSpace::ctype RT;
typedef typename TargetSpace::EmbeddedTangentVector EmbeddedTangentVector;
static const int embeddedDim = EmbeddedTangentVector::dimension;
static const int spaceDim = TargetSpace::TangentVector::dimension;
public:
/** \brief The type used for derivatives */
typedef Dune::FieldMatrix<RT, embeddedDim, dim> DerivativeType;
/** \brief Constructor
* \param localFiniteElement A Lagrangian finite element that provides the interpolation points
* \param coefficients Values of the function at the Lagrange points
*/
LocalQuickAndDirtyFEFunction(const LocalFiniteElement& localFiniteElement,
const std::vector<TargetSpace>& coefficients)
: localFiniteElement_(localFiniteElement),
coefficients_(coefficients)
{
assert(localFiniteElement_.localBasis().size() == coefficients_.size());
}
/** \brief The number of Lagrange points */
unsigned int size() const
{
return localFiniteElement_.localBasis().size();
}
/** \brief The type of the reference element */
Dune::GeometryType type() const
{
return localFiniteElement_.type();
}
/** \brief Evaluate the function */
TargetSpace evaluate(const Dune::FieldVector<ctype, dim>& local) const;
/** \brief Get the i'th base coefficient. */
TargetSpace coefficient(int i) const
{
return coefficients_[i];
}
private:
/** \brief The scalar local finite element, which provides the weighting factors
* \todo We really only need the local basis
*/
const LocalFiniteElement& localFiniteElement_;
/** \brief The coefficient vector */
std::vector<TargetSpace> coefficients_;
};
template <int dim, class ctype, class LocalFiniteElement, class TargetSpace>
TargetSpace LocalQuickAndDirtyFEFunction<dim,ctype,LocalFiniteElement,TargetSpace>::
evaluate(const Dune::FieldVector<ctype, dim>& local) const
{
// Evaluate the weighting factors---these are the Lagrangian shape function values at 'local'
std::vector<Dune::FieldVector<ctype,1> > w;
localFiniteElement_.localBasis().evaluateFunction(local,w);
// Special-casing for the Rotations. This is quite a challenge:
//
// Projection-based interpolation in the
// unit quaternions is not a good idea, here, because you may end up on the
// wrong sheet of the covering of SO(3) by the unit quaternions. The minimization
// solver for the weighted distance functional may then actually converge
// to the wrong local minimizer -- the interpolating functions wraps "the wrong way"
// around SO(3).
//
// Projection-based interpolation in SO(3) is not a good idea, because it is about as
// expensive as the geodesic interpolation itself. That's not good if all we want
// is an initial iterate.
//
// Averaging in R^{3x3} followed by QR decomposition is not a good idea either:
// The averaging can give you singular matrices quite quickly (try two matrices that
// differ by a rotation of pi/2). Then, the QR factorization of the identity may
// not be the identity (but differ in sign)! I tried that with the implementation
// from Numerical recipies.
//
// What do we do instead? We start from the coefficient that produces the lowest
// value of the objective functional.
if constexpr (std::is_same<TargetSpace,Rotation<double,3> >::value)
{
AverageDistanceAssembler averageDistance(coefficients_,w);
auto minDistance = averageDistance.value(coefficients_[0]);
std::size_t minCoefficient = 0;
for (std::size_t i=1; i<coefficients_.size(); i++)
{
auto distance = averageDistance.value(coefficients_[i]);
if (distance < minDistance)
{
minDistance = distance;
minCoefficient = i;
}
}
return coefficients_[minCoefficient];
}
else
{
typename TargetSpace::CoordinateType c(0);
for (size_t i=0; i<coefficients_.size(); i++)
c.axpy(w[i][0], coefficients_[i].globalCoordinates());
return TargetSpace(c);
}
}
} // namespace GFE
} // namespace Dune
#endif