Skip to content
Snippets Groups Projects
localgeodesicfeadolcstiffness.hh 11.7 KiB
Newer Older
  • Learn to ignore specific revisions
  • #ifndef DUNE_GFE_LOCAL_GEODESIC_FE_ADOL_C_STIFFNESS_HH
    #define DUNE_GFE_LOCAL_GEODESIC_FE_ADOL_C_STIFFNESS_HH
    
    #include <adolc/adouble.h>            // use of active doubles
    #include <adolc/drivers/drivers.h>    // use of "Easy to Use" drivers
    // gradient(.) and hessian(.)
    #include <adolc/taping.h>             // use of taping
    
    #undef overwrite  // stupid: ADOL-C sets this to 1, so the name cannot be used
    
    #include <dune/gfe/adolcnamespaceinjections.hh>
    
    #include <dune/common/fmatrix.hh>
    #include <dune/istl/matrix.hh>
    
    #include <dune/gfe/localgeodesicfestiffness.hh>
    
    /** \brief Assembles energy gradient and Hessian with ADOL-C (automatic differentiation)
     */
    template<class GridView, class LocalFiniteElement, class TargetSpace>
    class LocalGeodesicFEADOLCStiffness
        : public LocalGeodesicFEStiffness<GridView,LocalFiniteElement,TargetSpace>
    {
        // grid types
        typedef typename GridView::Grid::ctype DT;
        typedef typename TargetSpace::ctype RT;
        typedef typename GridView::template Codim<0>::Entity Entity;
    
        typedef typename TargetSpace::template rebind<adouble>::other ATargetSpace;
    
        // some other sizes
        enum {gridDim=GridView::dimension};
    
    public:
    
        //! Dimension of a tangent space
        enum { blocksize = TargetSpace::TangentVector::dimension };
    
        //! Dimension of the embedding space
        enum { embeddedBlocksize = TargetSpace::EmbeddedTangentVector::dimension };
    
        LocalGeodesicFEADOLCStiffness(const LocalGeodesicFEStiffness<GridView, LocalFiniteElement, ATargetSpace>* energy)
        : localEnergy_(energy)
        {}
    
        /** \brief Compute the energy at the current configuration */
        virtual RT energy (const Entity& e,
                   const LocalFiniteElement& localFiniteElement,
                   const std::vector<TargetSpace>& localSolution) const;
    
        /** \brief Assemble the element gradient of the energy functional
    
        This uses the automatic differentiation toolbox ADOL_C.
        */
        virtual void assembleGradient(const Entity& element,
                              const LocalFiniteElement& localFiniteElement,
                              const std::vector<TargetSpace>& solution,
                              std::vector<typename TargetSpace::TangentVector>& gradient) const;
    
        /** \brief Assemble the local stiffness matrix at the current position
    
        This uses the automatic differentiation toolbox ADOL_C.
        */
    
        virtual void assembleGradientAndHessian(const Entity& e,
    
                             const LocalFiniteElement& localFiniteElement,
    
                             const std::vector<TargetSpace>& localSolution,
                             std::vector<typename TargetSpace::TangentVector>& localGradient);
    
    
        const LocalGeodesicFEStiffness<GridView, LocalFiniteElement, ATargetSpace>* localEnergy_;
    
    };
    
    
    template <class GridView, class LocalFiniteElement, class TargetSpace>
    typename LocalGeodesicFEADOLCStiffness<GridView, LocalFiniteElement, TargetSpace>::RT
    LocalGeodesicFEADOLCStiffness<GridView, LocalFiniteElement, TargetSpace>::
    energy(const Entity& element,
           const LocalFiniteElement& localFiniteElement,
           const std::vector<TargetSpace>& localSolution) const
    {
        double pureEnergy;
    
        std::vector<ATargetSpace> localASolution(localSolution.size());
    
        trace_on(1);
    
        adouble energy = 0;
    
    
        // The following loop is not quite intuitive: we copy the localSolution into an
        // array of FieldVector<double>, go from there to FieldVector<adouble> and
        // only then to ATargetSpace.
        // Rationale: The constructor/assignment-from-vector of TargetSpace frequently
        // contains a projection onto the manifold from the surrounding Euclidean space.
    
    Oliver Sander's avatar
    Oliver Sander committed
        // ADOL-C needs a function on the whole Euclidean space, hence that projection
    
        // is part of the function and needs to be taped.
    
        // The following variable cannot be declared inside of the loop, or ADOL-C will report wrong results
        // (Presumably because several independent variables use the same memory location.)
        std::vector<typename ATargetSpace::CoordinateType> aRaw(localSolution.size());
        for (size_t i=0; i<localSolution.size(); i++) {
          typename TargetSpace::CoordinateType raw = localSolution[i].globalCoordinates();
          for (size_t j=0; j<raw.size(); j++)
            aRaw[i][j] <<= raw[j];
          localASolution[i] = aRaw[i];  // may contain a projection onto M -- needs to be done in adouble
        }
    
    
        energy = localEnergy_->energy(element,localFiniteElement,localASolution);
    
        energy >>= pureEnergy;
    
        trace_off();
    #if 0
        size_t tape_stats[STAT_SIZE];
        tapestats(1,tape_stats);             // reading of tape statistics
        cout<<"maxlive "<<tape_stats[NUM_MAX_LIVES]<<"\n";
        // ..... print other tape stats
    #endif
        return pureEnergy;
    }
    
    
    template <class GridView, class LocalFiniteElement, class TargetSpace>
    void LocalGeodesicFEADOLCStiffness<GridView, LocalFiniteElement, TargetSpace>::
    assembleGradient(const Entity& element,
                     const LocalFiniteElement& localFiniteElement,
                     const std::vector<TargetSpace>& localSolution,
                     std::vector<typename TargetSpace::TangentVector>& localGradient) const
    {
    
        // Tape energy computation.  We may not have to do this every time, but it's comparatively cheap.
        energy(element, localFiniteElement, localSolution);
    
    
        // Compute the actual gradient
        size_t nDofs = localSolution.size();
        size_t nDoubles = nDofs*embeddedBlocksize;
        std::vector<double> xp(nDoubles);
        int idx=0;
        for (size_t i=0; i<nDofs; i++)
            for (size_t j=0; j<embeddedBlocksize; j++)
                xp[idx++] = localSolution[i].globalCoordinates()[j];
    
      // Compute gradient
        std::vector<double> g(nDoubles);
        gradient(1,nDoubles,xp.data(),g.data());                  // gradient evaluation
    
        // Copy into Dune type
        std::vector<typename TargetSpace::EmbeddedTangentVector> localEmbeddedGradient(localSolution.size());
    
        idx=0;
        for (size_t i=0; i<nDofs; i++)
            for (size_t j=0; j<embeddedBlocksize; j++)
                localEmbeddedGradient[i][j] = g[idx++];
    
    //     std::cout << "localEmbeddedGradient:\n";
    //     for (size_t i=0; i<nDofs; i++)
    //       std::cout << localEmbeddedGradient[i] << std::endl;
    
        // Express gradient in local coordinate system
        for (size_t i=0; i<nDofs; i++) {
            Dune::FieldMatrix<RT,blocksize,embeddedBlocksize> orthonormalFrame = localSolution[i].orthonormalFrame();
            orthonormalFrame.mv(localEmbeddedGradient[i],localGradient[i]);
        }
    }
    
    
    // ///////////////////////////////////////////////////////////
    
    //   Compute gradient and Hessian together
    //   To compute the Hessian we need to compute the gradient anyway, so we may
    //   as well return it.  This saves assembly time.
    
    // ///////////////////////////////////////////////////////////
    template <class GridType, class LocalFiniteElement, class TargetSpace>
    void LocalGeodesicFEADOLCStiffness<GridType, LocalFiniteElement, TargetSpace>::
    
    assembleGradientAndHessian(const Entity& element,
    
                    const LocalFiniteElement& localFiniteElement,
    
                    const std::vector<TargetSpace>& localSolution,
                    std::vector<typename TargetSpace::TangentVector>& localGradient)
    
        // Tape energy computation.  We may not have to do this every time, but it's comparatively cheap.
        energy(element, localFiniteElement, localSolution);
    
        /////////////////////////////////////////////////////////////////
        // Compute the gradient.  It is needed to transform the Hessian
        // into the correct coordinates.
        /////////////////////////////////////////////////////////////////
    
        // Compute the actual gradient
    
        size_t nDofs = localSolution.size();
        size_t nDoubles = nDofs*embeddedBlocksize;
        std::vector<double> xp(nDoubles);
        int idx=0;
        for (size_t i=0; i<nDofs; i++)
            for (size_t j=0; j<embeddedBlocksize; j++)
                xp[idx++] = localSolution[i].globalCoordinates()[j];
    
    
      // Compute gradient
        std::vector<double> g(nDoubles);
        gradient(1,nDoubles,xp.data(),g.data());                  // gradient evaluation
    
        // Copy into Dune type
        std::vector<typename TargetSpace::EmbeddedTangentVector> localEmbeddedGradient(localSolution.size());
    
        idx=0;
        for (size_t i=0; i<nDofs; i++)
            for (size_t j=0; j<embeddedBlocksize; j++)
                localEmbeddedGradient[i][j] = g[idx++];
    
    
        // Express gradient in local coordinate system
        for (size_t i=0; i<nDofs; i++) {
            Dune::FieldMatrix<RT,blocksize,embeddedBlocksize> orthonormalFrame = localSolution[i].orthonormalFrame();
            orthonormalFrame.mv(localEmbeddedGradient[i],localGradient[i]);
        }
    
    
        /////////////////////////////////////////////////////////////////
        // Compute Hessian
        /////////////////////////////////////////////////////////////////
    
    Oliver Sander's avatar
    Oliver Sander committed
        double* rawHessian[nDoubles];
    
            rawHessian[i] = (double*)malloc((i+1)*sizeof(double));
        hessian(1,nDoubles,xp.data(),rawHessian);
    
    
        // Copy Hessian into Dune data type
        Dune::Matrix<Dune::FieldMatrix<double,embeddedBlocksize, embeddedBlocksize> > embeddedHessian(nDofs,nDofs);
        for(size_t i=0; i<nDoubles; i++) {
          for (size_t j=0; j<nDoubles; j++) {
    
            double value = (j<=i) ? rawHessian[i][j] : rawHessian[j][i];
    
            embeddedHessian[i/embeddedBlocksize][j/embeddedBlocksize][i%embeddedBlocksize][j%embeddedBlocksize] = value;
          }
        }
    
    
    Oliver Sander's avatar
    Oliver Sander committed
        for(size_t i=0; i<nDoubles; i++)
            free(rawHessian[i]);
    
    
        // 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".
    
        typedef typename TargetSpace::EmbeddedTangentVector EmbeddedTangentVector;
        typedef typename TargetSpace::TangentVector         TangentVector;
    
    
        std::vector<Dune::FieldMatrix<RT,blocksize,embeddedBlocksize> > orthonormalFrame(nDofs);
    
        for (size_t i=0; i<nDofs; i++)
            orthonormalFrame[i] = localSolution[i].orthonormalFrame();
    
    
        for (size_t col=0; col<nDofs; col++) {
    
            for (size_t subCol=0; subCol<blocksize; subCol++) {
    
                EmbeddedTangentVector z = orthonormalFrame[col][subCol];
    
                // P_x \partial^2 f z
                std::vector<EmbeddedTangentVector> semiEmbeddedProduct(nDofs);
    
                for (size_t row=0; row<nDofs; row++) {
                    embeddedHessian[row][col].mv(z,semiEmbeddedProduct[row]);
                    //tmp1 = localSolution[row].projectOntoTangentSpace(tmp1);
                    TangentVector tmp2;
                    orthonormalFrame[row].mv(semiEmbeddedProduct[row],tmp2);
    
                    for (int subRow=0; subRow<blocksize; subRow++)
                        this->A_[row][col][subRow][subCol] = tmp2[subRow];
                }
    
    
        //////////////////////////////////////////////////////////////////////////////////////
        //  Further correction due to non-planar configuration space
        //  + \mathfrak{A}_x(z,P^\orth_x \partial f)
        //////////////////////////////////////////////////////////////////////////////////////
    
        // Project embedded gradient onto normal space
        std::vector<typename TargetSpace::EmbeddedTangentVector> projectedGradient(localSolution.size());
        for (size_t i=0; i<localSolution.size(); i++)
          projectedGradient[i] = localSolution[i].projectOntoNormalSpace(localEmbeddedGradient[i]);
    
        for (size_t row=0; row<nDofs; row++) {
    
          for (size_t subRow=0; subRow<blocksize; subRow++) {
    
            EmbeddedTangentVector z = orthonormalFrame[row][subRow];
            EmbeddedTangentVector tmp1 = localSolution[row].weingarten(z,projectedGradient[row]);
    
            TangentVector tmp2;
            orthonormalFrame[row].mv(tmp1,tmp2);
    
            this->A_[row][row][subRow] += tmp2;
          }
    
        }
    
    //     std::cout << "ADOL-C stiffness:\n";
    //     printmatrix(std::cout, this->A_, "foo", "--");
    }
    
    #endif