#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. // 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 ///////////////////////////////////////////////////////////////// double* rawHessian[nDoubles]; for(size_t i=0; i<nDoubles; i++) 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; } } 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; this->A_.setSize(nDofs,nDofs); 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