From c47b4d03bf5d3a1b66fcb49f36174f8904f60d5a Mon Sep 17 00:00:00 2001 From: Oliver Sander <oliver.sander@tu-dresden.de> Date: Fri, 16 Oct 2015 17:01:32 +0200 Subject: [PATCH] Implement Projected Finite Elements with values in SO(3) --- dune/gfe/localprojectedfefunction.hh | 282 ++++++++++++++++++++++++++- 1 file changed, 281 insertions(+), 1 deletion(-) diff --git a/dune/gfe/localprojectedfefunction.hh b/dune/gfe/localprojectedfefunction.hh index a653f310..189deb1f 100644 --- a/dune/gfe/localprojectedfefunction.hh +++ b/dune/gfe/localprojectedfefunction.hh @@ -7,8 +7,28 @@ #include <dune/geometry/type.hh> +#include <dune/gfe/rotation.hh> +#include <dune/gfe/svd.hh> +#include <dune/gfe/linearalgebra.hh> + namespace Dune { +template< class K, int m, int n, int p > +Dune::FieldMatrix< K, m, p > operator* ( const Dune::FieldMatrix< K, m, n > &A, const Dune::FieldMatrix< K, n, p > &B) +{ + typedef typename Dune::FieldMatrix< K, m, p > :: size_type size_type; + Dune::FieldMatrix< K, m, p > ret; + + for( size_type i = 0; i < m; ++i ) { + + for( size_type j = 0; j < p; ++j ) { + ret[ i ][ j ] = K( 0 ); + for( size_type k = 0; k < n; ++k ) + ret[ i ][ j ] += A[ i ][ k ] * B[ k ][ j ]; + } + } + return ret; +} namespace GFE { /** \brief Interpolate in an embedding Euclidean space, and project back onto the Riemannian manifold @@ -18,9 +38,12 @@ namespace Dune { * \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 TargetSpace> + template <int dim, class ctype, class LocalFiniteElement, class TS> class LocalProjectedFEFunction { + public: + using TargetSpace=TS; + private: typedef typename TargetSpace::ctype RT; typedef typename TargetSpace::EmbeddedTangentVector EmbeddedTangentVector; @@ -151,6 +174,263 @@ namespace Dune { return result; } + /** \brief Interpolate in an embedding Euclidean space, and project back onto the Riemannian manifold -- specialization for SO(3) + * + * \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 + */ + template <int dim, class ctype, class LocalFiniteElement, class field_type> + class LocalProjectedFEFunction<dim,ctype,LocalFiniteElement,Rotation<field_type,3> > + { + public: + typedef Rotation<field_type,3> TargetSpace; + private: + typedef typename TargetSpace::ctype RT; + + typedef typename TargetSpace::EmbeddedTangentVector EmbeddedTangentVector; + static const int embeddedDim = EmbeddedTangentVector::dimension; + + static const int spaceDim = TargetSpace::TangentVector::dimension; + + static FieldMatrix<field_type,3,3> transpose(const FieldMatrix<field_type,3,3>& matrix) + { + FieldMatrix<field_type,3,3> result; + + for (int i=0; i<3; i++) + for (int j=0; j<3; j++) + result[i][j] = matrix[j][i]; + + return result; + } + + static FieldMatrix<field_type,3,3> polarFactor(const FieldMatrix<field_type,3,3>& matrix) + { + FieldVector<field_type,3> W; + FieldMatrix<field_type,3,3> V, VT; + auto polar = matrix; + + // returns a decomposition U W VT, where U is returned in the first argument + svdcmp<field_type,3,3>(polar, W, V); + + // \todo Merge this transposition with the following multiplication + for (int i=0; i<3; i++) + for (int j=0; j<3; j++) + VT[i][j] = V[j][i]; + + polar.rightmultiply(VT); + return polar; + } + + /** + * \param A The argument of the projection + * \param value The image of the projection + */ + static std::array<std::array<FieldMatrix<field_type,3,3>, 3>, 3> derivativeOfProjection(const FieldMatrix<field_type,3,3> A, + Rotation<field_type,3> value) + { + std::array<std::array<FieldMatrix<field_type,3,3>, 3>, 3> result; + + ///////////////////////////////////////////////////////////////////////////////// + // Step I: Compute the singular value decomposition of A + ///////////////////////////////////////////////////////////////////////////////// + + FieldVector<field_type,3> sigma; + FieldMatrix<field_type,3,3> V, VT; + FieldMatrix<field_type,3,3> U = A; + + // returns a decomposition U diag(sigma) VT, where U is returned in the first argument + svdcmp<field_type,3,3>(U, sigma, V); + + // Compute VT from V (svdcmp returns the latter) + for (int i=0; i<3; i++) + for (int j=0; j<3; j++) + VT[i][j] = V[j][i]; + + ///////////////////////////////////////////////////////////////////////////////// + // Step II: Compute the derivative direction X + ///////////////////////////////////////////////////////////////////////////////// + + for (int dir0=0; dir0<3; dir0++) + { + for (int dir1=0; dir1<3; dir1++) + { + FieldMatrix<field_type,3,3> X(0); + X[dir0][dir1] = 1.0; + + ///////////////////////////////////////////////////////////////////////////////// + // Step ???: Markus' magic formula + ///////////////////////////////////////////////////////////////////////////////// + + FieldMatrix<field_type,3,3> UTXV = transpose(U)*X*V; + + FieldMatrix<field_type,3,3> center; + center[0][0] = 0.0; + center[0][1] = (UTXV[0][1] - UTXV[1][0]) / (sigma[0] + sigma[1]); + center[0][2] = (UTXV[0][2] - UTXV[2][0]) / (sigma[0] + sigma[2]); + + center[1][0] = (UTXV[1][0] - UTXV[0][1]) / (sigma[1] + sigma[0]); + center[1][1] = 0.0; + center[1][2] = (UTXV[1][2] - UTXV[2][1]) / (sigma[1] + sigma[2]); + + center[2][0] = (UTXV[2][0] - UTXV[0][2]) / (sigma[2] + sigma[0]); + center[2][1] = (UTXV[2][1] - UTXV[1][2]) / (sigma[2] + sigma[1]); + center[2][2] = 0.0; + + // Compute U center VT + FieldMatrix<field_type,3,3> PAX = U*center*VT; + + // Copy into result array + for (int i=0; i<3; i++) + for (int j=0; j<3; j++) + result[i][j][dir0][dir1] = PAX[i][j]; + + } + } + + return result; + } + + + 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 + */ + LocalProjectedFEFunction(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_.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 + { + Rotation<field_type,3> result; + + // 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); + + // Interpolate in R^{3x3} + FieldMatrix<field_type,3,3> interpolatedMatrix(0); + for (size_t i=0; i<coefficients_.size(); i++) + { + FieldMatrix<field_type,3,3> coefficientAsMatrix; + coefficients_[i].matrix(coefficientAsMatrix); + interpolatedMatrix.axpy(w[i][0], coefficientAsMatrix); + } + + // Project back onto SO(3) + result.set(polarFactor(interpolatedMatrix)); + + return result; + } + + /** \brief Evaluate the derivative of the function */ + DerivativeType evaluateDerivative(const Dune::FieldVector<ctype, dim>& local) const + { + // the function value at the point where we are evaluating the derivative + TargetSpace q = evaluate(local); + + // Actually compute the derivative + return evaluateDerivative(local,q); + } + + /** \brief Evaluate the derivative of the function, if you happen to know the function value (much faster!) + * \param local Local coordinates in the reference element where to evaluate the derivative + * \param q Value of the local function at 'local'. If you provide something wrong here the result will be wrong, too! + */ + DerivativeType evaluateDerivative(const Dune::FieldVector<ctype, dim>& local, + const TargetSpace& q) 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); + + std::vector<Dune::FieldMatrix<ctype,1,dim> > wDer; + localFiniteElement_.localBasis().evaluateJacobian(local,wDer); + + // Compute matrix representations for all coefficients (we only have them in quaternion representation) + std::vector<Dune::FieldMatrix<field_type,3,3> > coefficientsAsMatrix(coefficients_.size()); + for (size_t i=0; i<coefficients_.size(); i++) + coefficients_[i].matrix(coefficientsAsMatrix[i]); + + // Interpolate in R^{3x3} + FieldMatrix<field_type,3,3> interpolatedMatrix(0); + for (size_t i=0; i<coefficients_.size(); i++) + interpolatedMatrix.axpy(w[i][0], coefficientsAsMatrix[i]); + + auto polarFactor = this->polarFactor(interpolatedMatrix); + + Tensor3<RT,dim,3,3> derivative(0); + + for (size_t dir=0; dir<dim; dir++) + for (size_t i=0; i<3; i++) + for (size_t j=0; j<3; j++) + for (size_t k=0; k<coefficients_.size(); k++) + derivative[dir][i][j] += wDer[k][0][dir] * coefficientsAsMatrix[k][i][j]; + + auto derivativeOfProjection = this->derivativeOfProjection(interpolatedMatrix,q); + + Tensor3<field_type,dim,3,3> intermediateResult(0); + + for (size_t dir=0; dir<dim; dir++) + for (size_t i=0; i<3; i++) + for (size_t j=0; j<3; j++) + for (size_t k=0; k<3; k++) + for (size_t l=0; l<3; l++) + intermediateResult[dir][i][j] += derivativeOfProjection[i][j][k][l]*derivative[dir][k][l]; + + // One more application of the chain rule: we need to go from orthogonal matrices to quaternions + Tensor3<field_type,4,3,3> derivativeOfMatrixToQuaternion = Rotation<field_type,3>::derivativeOfMatrixToQuaternion(polarFactor); + + DerivativeType result(0); + + for (size_t dir0=0; dir0<4; dir0++) + for (size_t dir1=0; dir1<dim; dir1++) + for (size_t i=0; i<3; i++) + for (size_t j=0; j<3; j++) + result[dir0][dir1] += derivativeOfMatrixToQuaternion[dir0][i][j] * intermediateResult[dir1][i][j]; + + return result; + } + + /** \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_; + + }; + } } -- GitLab