Skip to content
Snippets Groups Projects
adolctest.cc 22.7 KiB
Newer Older
  • Learn to ignore specific revisions
  • #include <config.h>
    
    #define SECOND_ORDER
    
    #include <fenv.h>
    
    // Includes for the ADOL-C automatic differentiation library
    // Need to come before (almost) all others.
    #include <adolc/adouble.h>
    #include <adolc/drivers/drivers.h>    // use of "Easy to Use" drivers
    #include <adolc/taping.h>
    
    #include <dune/gfe/adolcnamespaceinjections.hh>
    
    #include <dune/common/fmatrix.hh>
    
    #include <dune/geometry/quadraturerules.hh>
    
    #include <dune/grid/yaspgrid.hh>
    
    
    #include <dune/istl/io.hh>
    
    #include <dune/fufem/functionspacebases/p2nodalbasis.hh>
    
    #include <dune/gfe/rotation.hh>
    #include <dune/gfe/localgeodesicfestiffness.hh>
    
    #include <dune/gfe/localgeodesicfefunction.hh>
    
    #include <dune/gfe/rotation.hh>
    
    // grid dimension
    const int dim = 2;
    
    // Image space of the geodesic fe functions
    typedef Rotation<double,3> TargetSpace;
    
    using namespace Dune;
    
    
    
    
    template<class GridView, class LocalFiniteElement, int dim, class field_type=double>
    class CosseratEnergyLocalStiffness
        : public LocalGeodesicFEStiffness<GridView,LocalFiniteElement,Rotation<field_type,dim> >
    
        // grid types
        typedef typename GridView::Grid::ctype DT;
        typedef Rotation<field_type,dim> TargetSpace;
        typedef typename TargetSpace::ctype RT;
        typedef typename GridView::template Codim<0>::Entity Entity;
    
        // some other sizes
        enum {gridDim=GridView::dimension};
    
        /** \brief Assemble the energy for a single element */
        RT energy (const Entity& element,
                   const LocalFiniteElement& localFiniteElement,
                   const std::vector<TargetSpace>& localSolution) const
        {
          assert(element.type() == localFiniteElement.type());
          typedef typename GridView::template Codim<0>::Entity::Geometry Geometry;
    
          typedef LocalGeodesicFEFunction<gridDim, DT, LocalFiniteElement, TargetSpace> LocalGFEFunctionType;
          LocalGFEFunctionType localGeodesicFEFunction(localFiniteElement,localSolution);
    
          int quadOrder = (element.type().isSimplex()) ? localFiniteElement.localBasis().order()
                                                       : localFiniteElement.localBasis().order() * gridDim;
    
          const Dune::QuadratureRule<DT, gridDim>& quad
              = Dune::QuadratureRules<DT, gridDim>::rule(element.type(), quadOrder);
    
          for (size_t pt=0; pt<quad.size(); pt++) {
    
            // Local position of the quadrature point
            const Dune::FieldVector<DT,gridDim>& quadPos = quad[pt].position();
    
            const DT integrationElement = element.geometry().integrationElement(quadPos);
    
            const typename Geometry::JacobianInverseTransposed& jacobianInverseTransposed = element.geometry().jacobianInverseTransposed(quadPos);
    
            DT weight = quad[pt].weight() * integrationElement;
    
            // The value of the local function
            Rotation<field_type,dim> value = localGeodesicFEFunction.evaluate(quadPos);
    
            // The derivative of the local function defined on the reference element
            typename LocalGFEFunctionType::DerivativeType referenceDerivative = localGeodesicFEFunction.evaluateDerivative(quadPos,value);
    
            // The derivative of the function defined on the actual element
            typename LocalGFEFunctionType::DerivativeType derivative(0);
    
            for (size_t comp=0; comp<referenceDerivative.N(); comp++)
                jacobianInverseTransposed.umv(referenceDerivative[comp], derivative[comp]);
    
            //////////////////////////////////////////////////////////
            //  Compute the derivative of the rotation
            //  Note: we need it in matrix coordinates
            //////////////////////////////////////////////////////////
    
            Dune::FieldMatrix<field_type,dim,dim> R;
            value.matrix(R);
    
            // Add the local energy density
            energy += 2.5e3*weight *derivative.frobenius_norm2();
    
    /** \brief Assembles energy gradient and Hessian with ADOL-C
     */
    template<class GridView, class LocalFiniteElement>
    class LocalGeodesicFEADOLCStiffness
    {
        // 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};
    
        //! 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 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<Dune::FieldVector<double, 4> >& localGradient,
                             Dune::Matrix<Dune::FieldMatrix<RT,embeddedBlocksize,embeddedBlocksize> >& localHessian,
                             bool vectorMode);
    
        const LocalGeodesicFEStiffness<GridView, LocalFiniteElement, ATargetSpace>* localEnergy_;
    
    template <class GridView, class LocalFiniteElement>
    typename LocalGeodesicFEADOLCStiffness<GridView, LocalFiniteElement>::RT
    LocalGeodesicFEADOLCStiffness<GridView, LocalFiniteElement>::
    energy(const Entity& element,
           const LocalFiniteElement& localFiniteElement,
           const std::vector<TargetSpace>& localSolution) const
    {
        double pureEnergy;
    
        std::vector<ATargetSpace> localASolution(localSolution.size());
    
        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.
        // 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();
        return pureEnergy;
    }
    
    // ///////////////////////////////////////////////////////////
    //   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>
    void LocalGeodesicFEADOLCStiffness<GridType, LocalFiniteElement>::
    assembleGradientAndHessian(const Entity& element,
                    const LocalFiniteElement& localFiniteElement,
                    const std::vector<TargetSpace>& localSolution,
                    std::vector<Dune::FieldVector<double,4> >& localGradient,
                    Dune::Matrix<Dune::FieldMatrix<RT,embeddedBlocksize,embeddedBlocksize> >& localHessian,
                    bool vectorMode)
    {
        // Tape energy computation.  We may not have to do this every time, but it's comparatively cheap.
        energy(element, localFiniteElement, localSolution);
    
        /////////////////////////////////////////////////////////////////
        // Compute the gradient.
        /////////////////////////////////////////////////////////////////
    
        // Copy data from Dune data structures to plain-C ones
        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++)
                localGradient[i][j] = g[idx++];
    
        /////////////////////////////////////////////////////////////////
        // Compute Hessian
        /////////////////////////////////////////////////////////////////
    
        localHessian.setSize(nDofs,nDofs);
    
        double* rawHessian[nDoubles];
        for(size_t i=0; i<nDoubles; i++)
            rawHessian[i] = (double*)malloc((i+1)*sizeof(double));
    
        if (vectorMode)
          hessian2(1,nDoubles,xp.data(),rawHessian);
        else
          hessian(1,nDoubles,xp.data(),rawHessian);
    
        // Copy Hessian into Dune data type
        for(size_t i=0; i<nDoubles; i++)
          for (size_t j=0; j<nDoubles; j++)
          {
            double value = (i>=j) ? rawHessian[i][j] : rawHessian[j][i];
            localHessian[j/embeddedBlocksize][i/embeddedBlocksize][j%embeddedBlocksize][i%embeddedBlocksize] = value;
          }
    
        for(size_t i=0; i<nDoubles; i++)
            free(rawHessian[i]);
    
    /** \brief Assembles energy gradient and Hessian with finite differences
    
    template<class GridView, class LocalFiniteElement, class field_type=double>
    
    class LocalGeodesicFEFDStiffness
    {
        // grid types
        typedef typename GridView::Grid::ctype DT;
        typedef typename GridView::template Codim<0>::Entity Entity;
    
    public:
    
        //! Dimension of a tangent space
        enum { blocksize = TargetSpace::TangentVector::dimension };
    
        //! Dimension of the embedding space
        enum { embeddedBlocksize = TargetSpace::EmbeddedTangentVector::dimension };
    
        LocalGeodesicFEFDStiffness(const LocalGeodesicFEStiffness<GridView, LocalFiniteElement, TargetSpace>* energy)
        : localEnergy_(energy)
        {}
    
        /** \brief Compute the energy at the current configuration */
    
        virtual field_type energy (const Entity& element,
    
                   const LocalFiniteElement& localFiniteElement,
                   const std::vector<TargetSpace>& localSolution) const
        {
          return localEnergy_->energy(element,localFiniteElement,localSolution);
        }
    
        virtual void assembleGradientAndHessian(const Entity& e,
                                     const LocalFiniteElement& localFiniteElement,
                                     const std::vector<TargetSpace>& localSolution,
                                     std::vector<Dune::FieldVector<double,4> >& localGradient,
    
                                     Dune::Matrix<Dune::FieldMatrix<double,embeddedBlocksize,embeddedBlocksize> >& localHessian);
    
    
        const LocalGeodesicFEStiffness<GridView, LocalFiniteElement, TargetSpace>* localEnergy_;
    };
    
    // ///////////////////////////////////////////////////////////
    //   Compute gradient by finite-difference approximation
    // ///////////////////////////////////////////////////////////
    
    template <class GridType, class LocalFiniteElement, class field_type>
    void LocalGeodesicFEFDStiffness<GridType, LocalFiniteElement, field_type>::
    
    assembleGradientAndHessian(const Entity& element,
                    const LocalFiniteElement& localFiniteElement,
                    const std::vector<TargetSpace>& localSolution,
                    std::vector<Dune::FieldVector<double, 4> >& localGradient,
    
                    Dune::Matrix<Dune::FieldMatrix<double,embeddedBlocksize,embeddedBlocksize> >& localHessian)
    
    {
        // Number of degrees of freedom for this element
        size_t nDofs = localSolution.size();
    
        // Clear assemble data
        localHessian.setSize(nDofs, nDofs);
        localHessian = 0;
    
    
        std::vector<Dune::FieldMatrix<field_type,embeddedBlocksize,embeddedBlocksize> > B(localSolution.size());
    
        for (size_t i=0; i<B.size(); i++)
        {
            B[i] = 0;
            for (int j=0; j<embeddedBlocksize; j++)
              B[i][j][j] = 1.0;
        }
    
        // Precompute negative energy at the current configuration
        // (negative because that is how we need it as part of the 2nd-order fd formula)
    
        field_type centerValue   = -energy(element, localFiniteElement, localSolution);
    
    
        // Precompute energy infinitesimal corrections in the directions of the local basis vectors
    
        std::vector<Dune::array<field_type,embeddedBlocksize> > forwardEnergy(nDofs);
        std::vector<Dune::array<field_type,embeddedBlocksize> > backwardEnergy(nDofs);
    
    
        for (size_t i=0; i<localSolution.size(); i++) {
            for (size_t i2=0; i2<embeddedBlocksize; i2++) {
                typename TargetSpace::EmbeddedTangentVector epsXi = B[i][i2];
                epsXi *= eps;
                typename TargetSpace::EmbeddedTangentVector minusEpsXi = epsXi;
                minusEpsXi  *= -1;
    
                std::vector<TargetSpace> forwardSolution  = localSolution;
                std::vector<TargetSpace> backwardSolution = localSolution;
    
                forwardSolution[i]  = TargetSpace(localSolution[i].globalCoordinates() + epsXi);
                backwardSolution[i] = TargetSpace(localSolution[i].globalCoordinates() + minusEpsXi);
    
                forwardEnergy[i][i2]  = energy(element, localFiniteElement, forwardSolution);
                backwardEnergy[i][i2] = energy(element, localFiniteElement, backwardSolution);
    
            }
    
        }
    
        //////////////////////////////////////////////////////////////
        //   Compute gradient by finite-difference approximation
        //////////////////////////////////////////////////////////////
    
        localGradient.resize(localSolution.size());
    
        for (size_t i=0; i<localSolution.size(); i++)
            for (int j=0; j<embeddedBlocksize; j++)
                localGradient[i][j] = (forwardEnergy[i][j] - backwardEnergy[i][j]) / (2*eps);
    
        ///////////////////////////////////////////////////////////////////////////
        //   Compute Riemannian Hesse matrix by finite-difference approximation.
        //   We loop over the lower left triangular half of the matrix.
        //   The other half follows from symmetry.
        ///////////////////////////////////////////////////////////////////////////
        //#pragma omp parallel for schedule (dynamic)
        for (size_t i=0; i<localSolution.size(); i++) {
            for (size_t i2=0; i2<embeddedBlocksize; i2++) {
                for (size_t j=0; j<=i; j++) {
                    for (size_t j2=0; j2<((i==j) ? i2+1 : embeddedBlocksize); j2++) {
    
                        std::vector<TargetSpace> forwardSolutionXiEta  = localSolution;
                        std::vector<TargetSpace> backwardSolutionXiEta  = localSolution;
    
                        typename TargetSpace::EmbeddedTangentVector epsXi  = B[i][i2];    epsXi *= eps;
                        typename TargetSpace::EmbeddedTangentVector epsEta = B[j][j2];   epsEta *= eps;
    
                        typename TargetSpace::EmbeddedTangentVector minusEpsXi  = epsXi;   minusEpsXi  *= -1;
                        typename TargetSpace::EmbeddedTangentVector minusEpsEta = epsEta;  minusEpsEta *= -1;
    
                        if (i==j)
                            forwardSolutionXiEta[i] = TargetSpace(localSolution[i].globalCoordinates() + epsXi+epsEta);
                        else {
                            forwardSolutionXiEta[i] = TargetSpace(localSolution[i].globalCoordinates() + epsXi);
                            forwardSolutionXiEta[j] = TargetSpace(localSolution[j].globalCoordinates() + epsEta);
                        }
    
                        if (i==j)
                            backwardSolutionXiEta[i] = TargetSpace(localSolution[i].globalCoordinates() + minusEpsXi+minusEpsEta);
                        else {
                            backwardSolutionXiEta[i] = TargetSpace(localSolution[i].globalCoordinates() + minusEpsXi);
                            backwardSolutionXiEta[j] = TargetSpace(localSolution[j].globalCoordinates() + minusEpsEta);
                        }
    
    
                        field_type forwardValue  = energy(element, localFiniteElement, forwardSolutionXiEta) - forwardEnergy[i][i2] - forwardEnergy[j][j2];
                        field_type backwardValue = energy(element, localFiniteElement, backwardSolutionXiEta) - backwardEnergy[i][i2] - backwardEnergy[j][j2];
    
    
                        localHessian[i][j][i2][j2] = localHessian[j][i][j2][i2] = 0.5 * (forwardValue - 2*centerValue + backwardValue) / (eps*eps);
    
                    }
                }
            }
        }
    }
    
    // Compare two matrices
    void compareMatrices(const Matrix<FieldMatrix<double,4,4> >& matrixA, std::string nameA,
                         const Matrix<FieldMatrix<double,4,4> >& matrixB, std::string nameB)
    {
      double maxAbsDifference = -1;
      double maxRelDifference = -1;
    
      for(int i=0; i<matrixA.N(); i++) {
    
        for (int j=0; j<matrixA.M(); j++ ) {
    
          for (int ii=0; ii<4; ii++)
            for (int jj=0; jj<4; jj++)
            {
              double valueA = matrixA[i][j][ii][jj];
              double valueB = matrixB[i][j][ii][jj];
    
              double absDifference = valueA - valueB;
              double relDifference = std::abs(absDifference) / std::abs(valueA);
              maxAbsDifference = std::max(maxAbsDifference, std::abs(absDifference));
              if (not isinf(relDifference))
                maxRelDifference = std::max(maxRelDifference, relDifference);
    
              if (relDifference > 1)
                std::cout << i << ", " << j << "   " << ii << ", " << jj
                << ",       " << nameA << ": " << valueA << ",           " << nameB << ": " << valueB << std::endl;
            }
        }
    
      std::cout << nameA << " vs. " << nameB << " -- max absolute / relative difference is " << maxAbsDifference << " / " << maxRelDifference << std::endl;
    
    int main (int argc, char *argv[]) try
    
        typedef std::vector<TargetSpace> SolutionType;
    
        // ///////////////////////////////////////
        //    Create the grid
        // ///////////////////////////////////////
        typedef YaspGrid<dim> GridType;
    
        FieldVector<double,dim> upper = {{0.38, 0.128}};
    
    
        array<int,dim> elements = {{15, 5}};
        GridType grid(upper, elements);
    
    
        typedef GridType::LeafGridView GridView;
    
        GridView gridView = grid.leafGridView();
    
    
        typedef P2NodalBasis<GridView,double> FEBasis;
        FEBasis feBasis(gridView);
    
        // /////////////////////////////////////////
        //   Read Dirichlet values
        // /////////////////////////////////////////
    
        // //////////////////////////
        //   Initial iterate
        // //////////////////////////
    
        SolutionType x(feBasis.size());
    
        //////////////////////////////////////////7
        //  Read initial iterate from file
        //////////////////////////////////////////7
        Dune::BlockVector<FieldVector<double,7> > xEmbedded(x.size());
    
        std::ifstream file("dangerous_iterate", std::ios::in|std::ios::binary);
        if (not(file))
          DUNE_THROW(SolverError, "Couldn't open file 'dangerous_iterate' for reading");
    
        GenericVector::readBinary(file, xEmbedded);
    
        file.close();
    
        for (int ii=0; ii<x.size(); ii++)
        {
          // The first 3 of the 7 entries are irrelevant
          FieldVector<double, 4> rotationEmbedded;
          for (int jj=0; jj<4; jj++)
            rotationEmbedded[jj] = xEmbedded[ii][jj+3];
    
          x[ii] = TargetSpace(rotationEmbedded);
        }
    
        // ////////////////////////////////////////////////////////////
        //   Create an assembler for the energy functional
        // ////////////////////////////////////////////////////////////
    
        // Assembler using ADOL-C
        CosseratEnergyLocalStiffness<GridView,
                                     FEBasis::LocalFiniteElement,
                                     3,adouble> cosseratEnergyADOLCLocalStiffness;
    
        LocalGeodesicFEADOLCStiffness<GridView,
                                      FEBasis::LocalFiniteElement> localGFEADOLCStiffness(&cosseratEnergyADOLCLocalStiffness);
    
        CosseratEnergyLocalStiffness<GridView,
                                     FEBasis::LocalFiniteElement,
                                     3,double> cosseratEnergyFDLocalStiffness;
    
        LocalGeodesicFEFDStiffness<GridView,
                                 FEBasis::LocalFiniteElement> localGFEFDStiffness(&cosseratEnergyFDLocalStiffness);
    
        // Compute and compare matrices
        auto it    = gridView.template begin<0>();
        auto endit = gridView.template end<0>  ();
    
        for( ; it != endit; ++it ) {
    
            std::cout << "  ++++  element " << gridView.indexSet().index(*it) << " ++++" << std::endl;
    
            const int numOfBaseFct = feBasis.getLocalFiniteElement(*it).localBasis().size();
    
            // Extract local solution
            std::vector<TargetSpace> localSolution(numOfBaseFct);
    
            for (int i=0; i<numOfBaseFct; i++)
                localSolution[i] = x[feBasis.index(*it,i)];
    
            std::vector<Dune::FieldVector<double,4> > localADGradient(numOfBaseFct);
            std::vector<Dune::FieldVector<double,4> > localADVMGradient(numOfBaseFct);  // VM: vector-mode
            std::vector<Dune::FieldVector<double,4> > localFDGradient(numOfBaseFct);
    
            Matrix<FieldMatrix<double,4,4> > localADHessian;
            Matrix<FieldMatrix<double,4,4> > localADVMHessian;   // VM: vector-mode
            Matrix<FieldMatrix<double,4,4> > localFDHessian;
    
            // setup local matrix and gradient
            localGFEADOLCStiffness.assembleGradientAndHessian(*it,
                                                              feBasis.getLocalFiniteElement(*it),
                                                              localSolution,
                                                              localADGradient,
                                                              localADHessian,
                                                              false);   // 'true' means 'vector mode'
    
            localGFEADOLCStiffness.assembleGradientAndHessian(*it,
                                                              feBasis.getLocalFiniteElement(*it),
                                                              localSolution,
                                                              localADGradient,
                                                              localADVMHessian,
                                                              true);   // 'true' means 'vector mode'
    
            localGFEFDStiffness.assembleGradientAndHessian(*it, feBasis.getLocalFiniteElement(*it), localSolution, localFDGradient, localFDHessian);
    
            compareMatrices(localADHessian, "AD", localFDHessian, "FD");
            compareMatrices(localADHessian, "AD scalar", localADVMHessian, "AD vector");
        }
    
        // //////////////////////////////
     } catch (Exception e) {
    
        std::cout << e << std::endl;
    
     }