diff --git a/dune/gfe/assemblers/discretekirchhoffbendingenergy.hh b/dune/gfe/assemblers/discretekirchhoffbendingenergy.hh new file mode 100644 index 0000000000000000000000000000000000000000..542cca43532a1b4c07b64d92e97ea0a9013dc145 --- /dev/null +++ b/dune/gfe/assemblers/discretekirchhoffbendingenergy.hh @@ -0,0 +1,94 @@ +#ifndef DUNE_GFE_ASSEMBLERS_DISCRETEKIRCHHOFFBENDINGENERGY_HH +#define DUNE_GFE_ASSEMBLERS_DISCRETEKIRCHHOFFBENDINGENERGY_HH + +#include <dune/common/fmatrix.hh> +#include <dune/geometry/quadraturerules.hh> + +#include <dune/gfe/assemblers/localenergy.hh> +#include <dune/gfe/bendingisometryhelper.hh> +#include <dune/gfe/tensor3.hh> + +#include <dune/localfunctions/lagrange/lagrangesimplex.hh> + +/** + * \brief Assemble the discrete Kirchhoff bending energy for a single element. + * + * The energy is essentially the harmonic energy of a + * discrete Jacobian operator which is represented as a + * linear combination in a P2 finite element space. + * The gradient of that linear combination serves as an + * discrete Hessian in the energy. + */ +namespace Dune::GFE +{ + template<class Basis, class DiscreteKirchhoffFunction, class LocalForce, class TargetSpace> + class DiscreteKirchhoffBendingEnergy + : public Dune::GFE::LocalEnergy<Basis,TargetSpace> + { + // grid types + typedef typename Basis::GridView GridView; + typedef typename GridView::ctype DT; + typedef typename TargetSpace::ctype RT; + typedef typename DiscreteKirchhoffFunction::DerivativeType DerivativeType; + + // Grid dimension + constexpr static int gridDim = GridView::dimension; + + // P2-LocalFiniteElement used to represent the discrete Jacobian on the current element. + typedef typename Dune::LagrangeSimplexLocalFiniteElement<DT, double, gridDim, 2> P2LocalFiniteElement; + + public: + DiscreteKirchhoffBendingEnergy(DiscreteKirchhoffFunction &localFunction) + : localFunction_(localFunction) + {} + + /** \brief Assemble the energy for a single element */ + virtual RT energy (const typename Basis::LocalView& localView, + const typename Impl::LocalEnergyTypes<TargetSpace>::CompositeCoefficients& coefficients) const override + { + DUNE_THROW(NotImplemented, "!"); + } + + + /** \brief Assemble the energy for a single element */ + virtual RT energy (const typename Basis::LocalView& localView, + const std::vector<TargetSpace>& localConfiguration) const override + { + + const auto element = localView.element(); + + int quadOrder = 3; + const auto &quad = QuadratureRules<double, gridDim>::rule(element.type(), quadOrder); + + /** + bind the underlying Hermite basis to the current element. + Note: The DiscreteKirchhoffFunction object stores a vector of global + and local basis coefficients that are accessed for evaluation + methods (linear combinations of hermite basis function and local coefficients). + Therefore the local configuration is passed to the bind method as well since + these coefficient vectors need to be updated with the new local configuration + previous to evaluation. + */ + localFunction_.bind(element,localConfiguration); + + RT bendingEnergy = 0; + + for (auto&& quadPoint : quad) + { + auto discreteHessian= localFunction_.evaluateDiscreteHessian(quadPoint.position()); + + auto weight = quadPoint.weight() * element.geometry().integrationElement(quadPoint.position()); + bendingEnergy += weight * discreteHessian.frobenius_norm2(); + } + + return 0.5 * bendingEnergy; + } + + private: + DiscreteKirchhoffFunction& localFunction_; + + P2LocalFiniteElement lagrangeLFE_; + }; + +} // namespace Dune::GFE +#endif diff --git a/dune/gfe/assemblers/forceenergy.hh b/dune/gfe/assemblers/forceenergy.hh new file mode 100644 index 0000000000000000000000000000000000000000..916068c9b676a6a020fc287e35a0f5bcc4103183 --- /dev/null +++ b/dune/gfe/assemblers/forceenergy.hh @@ -0,0 +1,97 @@ +#ifndef DUNE_GFE_FORCEENERGY_HH +#define DUNE_GFE_FORCEENERGY_HH + +#include <dune/common/fmatrix.hh> +#include <dune/geometry/quadraturerules.hh> + +#include <dune/gfe/assemblers/localenergy.hh> +#include <dune/gfe/bendingisometryhelper.hh> + + +/** + * \brief Assemble the energy of a force term (force times deformation) for a single element. + */ +namespace Dune::GFE +{ + template<class Basis, class LocalDiscreteKirchhoffFunction, class LocalForce, class TargetSpace> + class ForceEnergy + : public Dune::GFE::LocalEnergy<Basis,TargetSpace> + { + // grid types + typedef typename Basis::GridView GridView; + typedef typename GridView::ctype DT; + typedef typename TargetSpace::ctype RT; + + // Grid dimension + constexpr static int gridDim = GridView::dimension; + + public: + ForceEnergy(LocalDiscreteKirchhoffFunction &localFunction, + LocalForce &localForce) + : localFunction_(localFunction), + localForce_(localForce) + {} + + /** \brief Assemble the energy for a single element */ + virtual RT energy (const typename Basis::LocalView& localView, + const typename Impl::LocalEnergyTypes<TargetSpace>::CompositeCoefficients& coefficients) const override + { + DUNE_THROW(NotImplemented, "!"); + } + + + /** \brief Assemble the energy for a single element */ + virtual RT energy (const typename Basis::LocalView& localView, + const std::vector<TargetSpace>& localConfiguration) const override + { + RT energy = 0; + + int quadOrder = 3; + + const auto &quad = QuadratureRules<double, gridDim>::rule(GeometryTypes::simplex(gridDim), quadOrder); + + const auto element = localView.element(); + + /** bind the local force to the current element */ + localForce_.bind(element); + + /** + bind the underlying Hermite basis to the current element. + Note: The DiscreteKirchhoffFunction object stores a vector of global + and local basis coefficients that are accessed for evaluation + methods (linear comibnations of hermite basis function and local coefficients). + Therefore the local configuration is passed to the bind method as well since + these coefficient vectors need to be updated with the new local configuration + previous to evaluation. + */ + localFunction_.bind(element,localConfiguration); + + /** + * @brief Compute contribution of the force Term + * + * Integrate the scalar product of the force + * with the local deformation function 'localFunction_' + * over the current element. + */ + RT forceTermEnergy = 0; + + for (auto&& quadPoint : quad) + { + auto deformationValue = localFunction_.evaluate(quadPoint.position()); + auto forceValue = localForce_(quadPoint.position()); + auto weight = quadPoint.weight() * element.geometry().integrationElement(quadPoint.position()); + forceTermEnergy += deformationValue.globalCoordinates() * forceValue * weight; + } + + return forceTermEnergy; + } + + private: + LocalDiscreteKirchhoffFunction& localFunction_; + + LocalForce& localForce_; + + }; + +} // namespace Dune::GFE +#endif diff --git a/dune/gfe/bendingisometryhelper.hh b/dune/gfe/bendingisometryhelper.hh new file mode 100644 index 0000000000000000000000000000000000000000..c3075959de53fb93281317b0e5702c6a055506c7 --- /dev/null +++ b/dune/gfe/bendingisometryhelper.hh @@ -0,0 +1,399 @@ +#ifndef DUNE_GFE_BENDINGISOMETRYHELPER_HH +#define DUNE_GFE_BENDINGISOMETRYHELPER_HH + +#include <dune/common/parametertree.hh> +#include <dune/common/parametertreeparser.hh> + +#include <dune/geometry/quadraturerules.hh> + +#include <dune/functions/functionspacebases/cubichermitebasis.hh> +#include <dune/functions/common/differentiablefunction.hh> +#include <dune/functions/common/differentiablefunctionfromcallables.hh> +#include <dune/functions/common/functionconcepts.hh> + + +#include <dune/gfe/spaces/productmanifold.hh> +#include <dune/gfe/spaces/realtuple.hh> +#include <dune/gfe/spaces/rotation.hh> +#include <dune/gfe/functions/embeddedglobalgfefunction.hh> +#include <dune/gfe/functions/localprojectedfefunction.hh> + +#include <dune/matrix-vector/crossproduct.hh> + +/** \file + * \brief Various helper functions related to bending isometries. + */ + + +namespace Dune::GFE::Impl +{ + + + /** \brief The identity function in R^d, and its derivative + * + * This is needed to compute the displacement from the deformation (for visualization). + * Since we want to represent it in a Hermite finite element space, we need + * the derivative as well. + * + * Is there no shorter way to implement this? + */ + template<class K> + class IdentityGridEmbedding + { + public: + //! Evaluate identity + Dune::FieldVector<K,3> operator() (const Dune::FieldVector<K,2>& x) const + { + return {x[0], x[1], 0.0}; + } + + /** + * \brief Obtain derivative of identity function + */ + friend auto derivative(const IdentityGridEmbedding& p) + { + return [](const Dune::FieldVector<K,2>& x) { return Dune::FieldMatrix<K,3,2>({{1,0}, {0,1}, {0,0}}); }; + } + }; + + + /** + Define some Generic lambda expressions (C++ 20) + */ + auto cancelLastMatrixColumn = []<typename RT>(Dune::FieldMatrix<RT,3,3> matrix) -> Dune::FieldMatrix<RT,3,2> + { + return Dune::FieldMatrix<RT,3,2>{{matrix[0][0],matrix[0][1]}, + {matrix[1][0],matrix[1][1]}, + {matrix[2][0],matrix[2][1]} + }; + }; + + auto rotationExtraction = []<typename RT>(Dune::FieldVector<RT,7> vec) -> Rotation<RT,3> + { + std::array<RT,4> quaternion {vec[3],vec[4],vec[5],vec[6]}; + return Rotation<RT,3>(quaternion); + }; + + auto deformationExtraction = []<typename RT>(Dune::FieldVector<RT,7> vec) -> Dune::FieldVector<RT,3> + { + return Dune::FieldVector<RT,3>{vec[0],vec[1],vec[2]}; + }; + + + + + + + + /** + * @brief Data structure conversion from 'VectorSpaceCoefficients' to 'IsometryCoefficients'. + */ + template<class DiscreteKirchhoffBasis, class CoefficientBasis, class Coefficients, class IsometryCoefficients> + void vectorToIsometryCoefficientMap(const DiscreteKirchhoffBasis& discreteKirchhoffBasis, + const CoefficientBasis& coefficientBasis, + const Coefficients& vectorCoefficients, + IsometryCoefficients& isometryCoefficients) + { + isometryCoefficients.resize(coefficientBasis.size()); + auto localView = discreteKirchhoffBasis.localView(); + auto localViewCoefficients = coefficientBasis.localView(); + + using RT = typename IsometryCoefficients::value_type::ctype; + + for (const auto &element : elements(coefficientBasis.gridView())) + { + localView.bind(element); + localViewCoefficients.bind(element); + + /** + Create data structures to store deformation values and the deformation gradient (Dofs) + on the current element. We store the values in an std::vector where each entry + contains the deformation vector (resp. x,y derivative vectors) at one node of the element. + */ + std::vector<Dune::FieldVector<RT,3> > deformationDofs(3); + std::vector<Dune::FieldVector<RT,3> > xDerivativeDofs(3); + std::vector<Dune::FieldVector<RT,3> > yDerivativeDofs(3); + + // Loop over components of power basis + for(std::size_t k=0; k<3 ; k++) + { + // This could be placed out of the loop if we assume a power basis. + const auto& hermiteLFE = localView.tree().child(k).finiteElement(); + + for(std::size_t i=0; i<hermiteLFE.size(); i++) + { + auto localIdx = localView.tree().child(k).localIndex(i); + auto globalIdx = localView.index(localIdx); + // Get current node index (Caution: This assumes that the hermite LocalFiniteElement only contains vertex dofs.) + auto nodeIdx = hermiteLFE.localCoefficients().localKey(i).subEntity(); + + /** + We use the functionalDescriptor of the (discreteKirchhoffBasis) hermite LocalFiniteELement + to determine whether a current DOF corresponds to a function evaluation or partial derivative w.r.t + the first or second variable. + (Alternatively to the - experimental - FunctionalDescriptor: use localKey(i).index(): 0~value, 1~xDerivative, 2~yDerivative.) + */ + if(std::array<unsigned int,2> val {0,0} ; hermiteLFE.localInterpolation().functionalDescriptor(i).partialDerivativeOrder() == val ) + { + deformationDofs[nodeIdx][k] = vectorCoefficients[globalIdx[0]][globalIdx[1]]; + } + if(std::array<unsigned int,2> val {1,0} ; hermiteLFE.localInterpolation().functionalDescriptor(i).partialDerivativeOrder() == val ) + { + xDerivativeDofs[nodeIdx][k] = vectorCoefficients[globalIdx[0]][globalIdx[1]]; + } + if(std::array<unsigned int,2> val {0,1} ; hermiteLFE.localInterpolation().functionalDescriptor(i).partialDerivativeOrder() == val ) + { + yDerivativeDofs[nodeIdx][k] = vectorCoefficients[globalIdx[0]][globalIdx[1]]; + } + } + } + + // Loop over vertices of the current element + for(std::size_t c=0; c<3 ; c++) + { + /** Normalize derivative vectors (this is necessary due to the h-dependent scaling used in the hermite-Basis). */ + xDerivativeDofs[c] /= xDerivativeDofs[c].two_norm(); + yDerivativeDofs[c] /= yDerivativeDofs[c].two_norm(); + + /** + cross product to get last column of rotation matrix. + */ + Dune::FieldVector<RT,3> cross = Dune::MatrixVector::crossProduct(xDerivativeDofs[c],yDerivativeDofs[c]); + + Dune::FieldMatrix<RT,3,3> rotMatrix(0); + + for(std::size_t k=0; k<3; k++) + { + rotMatrix[k][0] = xDerivativeDofs[c][k]; + rotMatrix[k][1] = yDerivativeDofs[c][k]; + rotMatrix[k][2] = cross[k]; + } + + /** + Check if derivative vectors are orthonormal. + */ + Dune::FieldMatrix<double,3,3> I = Dune::ScaledIdentityMatrix<double,3>(1); + + if(((rotMatrix.transposed()*rotMatrix) - I).frobenius_norm()>1e-8) + DUNE_THROW(Dune::Exception, "Error: input rotation is not orthonormal!"<< ((rotMatrix.transposed()*rotMatrix) - I).frobenius_norm()); + + size_t localIdxOut = localViewCoefficients.tree().localIndex(c); + size_t globalIdxOut = localViewCoefficients.index(localIdxOut); + + using namespace Dune::Indices; + isometryCoefficients[globalIdxOut][_1].set(rotMatrix); + isometryCoefficients[globalIdxOut][_0] = (RealTuple<RT, 3>)deformationDofs[c]; + } + } + } + + + /** + * @brief Data structure conversion from 'IsometryCoefficients' to 'VectorSpaceCoefficients'. + */ + template<class DiscreteKirchhoffBasis, class CoefficientBasis, class Coefficients, class IsometryCoefficients> + void isometryToVectorCoefficientMap(const DiscreteKirchhoffBasis& discreteKirchhoffBasis, + const CoefficientBasis& coefficientBasis, + Coefficients& vectorCoefficients, + const IsometryCoefficients& isometryCoefficients) + { + auto localView = discreteKirchhoffBasis.localView(); + auto localViewCoefficients = coefficientBasis.localView(); + + using GridView = typename DiscreteKirchhoffBasis::GridView; + auto gridView = discreteKirchhoffBasis.gridView(); + + /** Create an EmbeddedGlobalGFEFunction that serves as a GridViewFunction later on. + We need a function to be passed into the interpolation method of the hermite basis + (although the values in between grid nodes do not matter). + + The range type of EmbeddedGlobalFEFunction is a FieldVector<double,7> + where the first three entries correspond to the deformation and the + last four entries correspond to a (quaternion) rotation. + */ + typedef Dune::GFE::LocalProjectedFEFunction<GridView::dimension, double, typename CoefficientBasis::LocalView::Tree::FiniteElement, Dune::GFE::ProductManifold<RealTuple<double,3>, Rotation<double,3> > > LocalInterpolationRule; + Dune::GFE::EmbeddedGlobalGFEFunction<CoefficientBasis, LocalInterpolationRule, Dune::GFE::ProductManifold<RealTuple<double,3>, Rotation<double,3> > > embeddedGlobalFunction(coefficientBasis, isometryCoefficients); + + auto productSpaceGridViewFunction = Dune::Functions::makeAnalyticGridViewFunction(embeddedGlobalFunction, gridView); + + /** Extract the deformation and rotation into separate functions. */ + auto deformationGlobalFunction = Dune::Functions::makeComposedGridFunction(deformationExtraction,productSpaceGridViewFunction); + + + + auto rotationGlobalFunction = Dune::Functions::makeComposedGridFunction(cancelLastMatrixColumn, + Dune::Functions::makeComposedGridFunction(Rotation<double,3>::quaternionToMatrix, + Dune::Functions::makeComposedGridFunction(rotationExtraction,productSpaceGridViewFunction))); + + using Domain = const Dune::FieldVector<double,2>&; + using Range = Dune::FieldVector<double,3>; + auto globalIsometryFunction = makeDifferentiableFunctionFromCallables(Dune::Functions::SignatureTag<Range(Domain)>(),deformationGlobalFunction,rotationGlobalFunction); + + /** Interpolate into the hermite basis to get the coefficient vector. */ + interpolate(discreteKirchhoffBasis, vectorCoefficients, globalIsometryFunction); + } + + + template<class LocalDiscreteKirchhoffFunction, class NormalBasis> + auto computeDiscreteSurfaceNormal(LocalDiscreteKirchhoffFunction& deformationFunction, + const NormalBasis& normalBasis) + { + std::vector<Dune::FieldVector<double,3> > discreteNormalVectorCoefficients(normalBasis.size()); + + auto localView = normalBasis.localView(); + for (auto&& element : elements(normalBasis.gridView())) + { + auto geometry = element.geometry(); + + localView.bind(element); + const auto nSf = localView.tree().child(0).finiteElement().localBasis().size(); + + deformationFunction.bind(element); + + for (int i=0; i<geometry.corners(); i++) + { + auto numDir = deformationFunction.evaluateDerivative(geometry.local(geometry.corner(i))); + + /** + Get the normal vector via cross-product. + */ + Dune::FieldVector<double,3> normal = {numDir[1][0]*numDir[2][1] - numDir[1][1]*numDir[2][0], + numDir[2][0]*numDir[0][1] - numDir[0][0]*numDir[2][1], + numDir[0][0]*numDir[1][1] - numDir[1][0]*numDir[0][1]}; + // printvector(std::cout, normal, "discrete normal:" , "--"); + for (size_t k=0; k < 3; k++) + for (size_t i=0; i < nSf; i++) + { + size_t localIdx = localView.tree().child(k).localIndex(i); // hier i:leafIdx + size_t globalIdx = localView.index(localIdx); + discreteNormalVectorCoefficients[globalIdx] = normal[k]; + } + } + } + + return discreteNormalVectorCoefficients; + } + + + /** + * @brief Compute Coefficients of the local discrete gradient operator. + * + * The discrete Gradient is a special linear combination represented in a [P2]^2 space (locally by a representation local finite element) + * The coefficients of this linear combination correspond to certain linear combinations of the Gradients of localfunction_ . + * The coefficients are stored in the form [Basisfunctions x components x gridDim] + * in a BlockVector<FieldMatrix<RT, 3, gridDim>> . + */ + template<class Coefficient,class LocalP2Element, class DeformationFunction > + auto discreteGradientCoefficients(Coefficient& discreteGradientCoefficients, + LocalP2Element& representationLFE, + DeformationFunction& deformationFunction, + auto& element) + { + using RT = typename DeformationFunction::RT; + + auto geometry = element.geometry(); + constexpr static int gridDim = DeformationFunction::gridDim; + auto gridView = deformationFunction.gridView(); + const auto &indexSet = gridView.indexSet(); + discreteGradientCoefficients.resize(representationLFE.size()); + /** + * @brief On the current element we need to assign the coefficients of the discrete Gradient to the right P2-Lagrange-Basisfunctions. + * this is done by iterating over all P2-Lagrange-Basisfunctions and determine the corresponding coefficients. + * We distinguish two situations: + * - [1] If the Lagrange-basisfunction is assigned to a node: the corresponding coefficient is just the evaluation of the deformation gradient at that specific node. + * - [2] If the Lagrange-basisfunction is assigned to an edge center: the corresponding coefficient is decomposed into a normal-& tangential part, + * where the normal-part: is given by the affine combination of the deformation gradient values at the edge-vertices + * and the tangential-part: is given by the tangential part of the deformation gradient at the edge-center. + * + * * We do this for all (k=3) components of the deformation-function simulatneously. + */ + for(std::size_t i=0; i<representationLFE.size(); i++) + { + // [1] Determine coefficients for basis functions assigned to a vertex + if (representationLFE.localCoefficients().localKey(i).codim() == 2) + { + size_t nodeIndex = representationLFE.localCoefficients().localKey(i).subEntity(); // This gives index on reference Element ? + // Alternative: use subEntity to get (local) node position + + typename DeformationFunction::DerivativeType whJacobianValue = deformationFunction.evaluateDerivative(geometry.local(geometry.corner(nodeIndex))); + discreteGradientCoefficients[i] = whJacobianValue; + } + + // [2] Determine coefficients for basis functions assigned to an edge + if (representationLFE.localCoefficients().localKey(i).codim() == 1) + { + /** + * @brief On each edge the (componentwise) value of the discete jacobian on the edge midpoint + * is decomposed into a normal and tangent component. + * The normal component at the edge center is given as the affine combination of the + * normal component of the jacobian of localfunctions_ at the two vertices on each edge. + * The tangential component is the tangent component of the jacobian of localfunctions_ on + * the edge center. + */ + size_t edgeIndex = representationLFE.localCoefficients().localKey(i).subEntity(); + auto edgeGeometry = element.template subEntity<1>(edgeIndex).geometry(); + auto edgeEntity = element.template subEntity<1>(edgeIndex); + + /** + * @brief Get the right orientation for tangent and normal vectors on current edge. + */ + const auto &refElement = referenceElement(element); + // Local vertex indices within the element + auto localV0 = refElement.subEntity(edgeIndex, gridDim-1, 0, gridDim); + auto localV1 = refElement.subEntity(edgeIndex, gridDim-1, 1, gridDim); + + // Global vertex indices within the grid + auto globalV0 = indexSet.subIndex(element, localV0, gridDim); + auto globalV1 = indexSet.subIndex(element, localV1, gridDim); + + double edgeOrientation = 1.0; + + if ((localV0<localV1 && globalV0>globalV1) || (localV0>localV1 && globalV0<globalV1)) + edgeOrientation = -1.0; + + // Get tangent vector on current edge + Dune::FieldVector<RT,2> tmp = (geometry.corner(localV1) - geometry.corner(localV0)) / abs(edgeGeometry.volume()); + + //convert to FieldMatrix + Dune::FieldMatrix<RT,2,1> edgeTangent = {tmp[0], tmp[1]}; + edgeTangent *= edgeOrientation; + + /** + * @brief Get edge normal by rotating the edge-tangent by by pi/2 clockwise + */ + Dune::FieldMatrix<RT,2,1> edgeNormal = {edgeTangent[1], -1.0*edgeTangent[0]}; + + /** + * @brief Evaluate Jacobian of localfunctions_ at edge-vertices and edge-midpoints. + */ + typename DeformationFunction::DerivativeType whJacobianValueCenter = deformationFunction.evaluateDerivative(geometry.local(edgeGeometry.center())); // Q: Are these transformations correct? + typename DeformationFunction::DerivativeType whJacobianValueFirstVertex = deformationFunction.evaluateDerivative(geometry.local(geometry.corner(localV0))); + typename DeformationFunction::DerivativeType whJacobianValueSecondVertex = deformationFunction.evaluateDerivative(geometry.local(geometry.corner(localV1))); + + // @OS: Is there a elementwise/hadamard matrix multiplication in Dune? + Dune::FieldMatrix<RT,3,gridDim> normalComponent(0); + Dune::FieldMatrix<RT,3,gridDim> tangentialComponent(0); + + auto normalComponentFactor = 0.5 * (whJacobianValueFirstVertex + whJacobianValueSecondVertex) * edgeNormal; + auto tangentialComponentFactor = whJacobianValueCenter * edgeTangent; + + /** + * @brief Compute normal and tangential component on current edge. + */ + for (int k=0; k<3; k++) + for (int l=0; l<gridDim; l++) + { + normalComponent[k][l] = normalComponentFactor[k][0]*edgeNormal[l]; + tangentialComponent[k][l] = tangentialComponentFactor[k][0]*edgeTangent[l]; + } + discreteGradientCoefficients[i] = normalComponent + tangentialComponent; + + } + } //end of P2-indices + } + + + +} // end namespace Dune::GFE::Impl + +#endif diff --git a/dune/gfe/functions/discretekirchhoffbendingisometry.hh b/dune/gfe/functions/discretekirchhoffbendingisometry.hh new file mode 100644 index 0000000000000000000000000000000000000000..a7296ae5869fe5b81df2b661b0a2d5b4b8b314e6 --- /dev/null +++ b/dune/gfe/functions/discretekirchhoffbendingisometry.hh @@ -0,0 +1,315 @@ +#ifndef DUNE_GFE_FUNCTIONS_DISCRETEKIRCHHOFFBENDINGISOMETRY_HH +#define DUNE_GFE_FUNCTIONS_DISCRETEKIRCHHOFFBENDINGISOMETRY_HH + +#include <vector> + +#include <dune/common/fvector.hh> + +#include <dune/gfe/spaces/productmanifold.hh> +#include <dune/gfe/spaces/realtuple.hh> +#include <dune/gfe/spaces/rotation.hh> +#include <dune/gfe/functions/localisometrycomponentfunction.hh> +#include <dune/gfe/functions/localprojectedfefunction.hh> +#include <dune/gfe/bendingisometryhelper.hh> +#include <dune/gfe/tensor3.hh> + +#include <dune/functions/functionspacebases/cubichermitebasis.hh> + +namespace Dune::GFE +{ + /** \brief Interpolate a Discrete Kirchhoff finite element function from a set of RealTuple x Rotation coefficients + * + * The Discrete Kirchhoff finite element is a Hermite-type element. + * The Rotation component of the coefficient set represents the deformation gradient. + * + * The class stores both a global coefficient vector 'coefficients_' + * as well as a local coefficient vector 'localHermiteCoefficients_' + * that is used for local evaluation after binding to an element. + * + * + * \tparam DiscreteKirchhoffBasis The basis used to compute function values + * \tparam CoefficientBasis The basis used to index the coefficient vector. + * \tparam Coefficients The container of global coefficients + */ + template <class DiscreteKirchhoffBasis, class CoefficientBasis, class Coefficients> + class DiscreteKirchhoffBendingIsometry + { + using GridView = typename DiscreteKirchhoffBasis::GridView; + using ctype = typename GridView::ctype; + + public: + constexpr static int gridDim = GridView::dimension; + using Coefficient = typename Coefficients::value_type; + using RT = typename Coefficient::ctype; + + static constexpr int components_ = 3; + + typedef Dune::GFE::LocalProjectedFEFunction<GridView::dimension, ctype, typename CoefficientBasis::LocalView::Tree::FiniteElement, Dune::GFE::ProductManifold<RealTuple<RT,components_>, Rotation<RT,components_> > > LocalInterpolationRule; + + /** \brief The type used for derivatives */ + typedef Dune::FieldMatrix<RT, components_, gridDim> DerivativeType; + + /** \brief The type used for discrete Hessian */ + typedef Tensor3<RT, components_, gridDim, gridDim> discreteHessianType; + + + /** \brief Constructor + * \param basis An object of Hermite basis type + * \param coefficientBasis An object of type LagrangeBasis<GridView,1> + * \param globalIsometryCoefficients Values and derivatives of the function at the Lagrange points + */ + DiscreteKirchhoffBendingIsometry(const DiscreteKirchhoffBasis& discreteKirchhoffBasis, + const CoefficientBasis& coefficientBasis, + Coefficients& globalIsometryCoefficients) + : basis_(discreteKirchhoffBasis), + coefficientBasis_(coefficientBasis), + localView_(basis_), + localViewCoefficient_(coefficientBasis_), + globalIsometryCoefficients_(globalIsometryCoefficients) + {} + + void bind(const typename GridView::template Codim<0>::Entity& element) + { + localView_.bind(element); + + /** extract the local coefficients from the global coefficient vector */ + std::vector<Coefficient> localIsometryCoefficients; + getLocalIsometryCoefficients(localIsometryCoefficients,element); + updateLocalHermiteCoefficients(localIsometryCoefficients,element); + + /** + * @brief Get Coefficients of the discrete Gradient + * + * The discrete Gradient is a special linear combination represented in a [P2]^2 - (Lagrange) space + * The coefficients of this linear combination correspond to certain linear combinations of the Gradients of + * the deformation (hermite) basis. The coefficients are stored in the form [Basisfunctions x components x gridDim] + * in a BlockVector<FieldMatrix<RT, 3, gridDim>>. + */ + Impl::discreteGradientCoefficients(discreteJacobianCoefficients_,rotationLFE_,*this,element); + } + + /** bind to an element and update the coefficient member variables for a set of new local coefficients */ + void bind(const typename GridView::template Codim<0>::Entity& element,const Coefficients& newLocalCoefficients) + { + this->bind(element); + + // Update the global isometry coefficients. + for (unsigned int vertex=0; vertex<3; ++vertex) + { + size_t localIdx = localViewCoefficient_.tree().localIndex(vertex); + size_t globalIdx = localViewCoefficient_.index(localIdx); + globalIsometryCoefficients_[globalIdx] = newLocalCoefficients[vertex]; + } + + // Update the local hermite coefficients. + updateLocalHermiteCoefficients(newLocalCoefficients,element); + + // After setting a new local configurarion, the coefficients of the discrete Gradient need to be updated as well. + Impl::discreteGradientCoefficients(discreteJacobianCoefficients_,rotationLFE_,*this,element); + } + + /** \brief The type of the element we are bound to */ + Dune::GeometryType type() const + { + return localView_.element().type(); + } + + + /** \brief Evaluate the function */ + auto evaluate(const Dune::FieldVector<ctype, gridDim>& local) const + { + const auto &localFiniteElement = localView_.tree().child(0).finiteElement(); + + // Evaluate the shape functions + std::vector<FieldVector<double, 1> > values; + localFiniteElement.localBasis().evaluateFunction(local, values); + + FieldVector<RT,components_> result(0); + + for(size_t i=0; i<localFiniteElement.size(); i++) + result.axpy(values[i][0], localHermiteCoefficients_[i]); + + return (RealTuple<RT, components_>)result; + } + + /** \brief Evaluate the derivative of the function */ + DerivativeType evaluateDerivative(const Dune::FieldVector<ctype, gridDim>& local) const + { + const auto &localFiniteElement = localView_.tree().child(0).finiteElement(); + + const auto jacobianInverse = localView_.element().geometry().jacobianInverse(local); + std::vector<FieldMatrix<double,1,gridDim> > jacobianValues; + localFiniteElement.localBasis().evaluateJacobian(local, jacobianValues); + for (size_t i = 0; i<jacobianValues.size(); i++) + jacobianValues[i] = jacobianValues[i] * jacobianInverse; + + DerivativeType result(0); + + for(size_t i=0; i<localFiniteElement.size(); i++) + for (unsigned int k = 0; k<components_; k++) + for (unsigned int j = 0; j<gridDim; ++j) + result[k][j] += (jacobianValues[i][0][j] * localHermiteCoefficients_[i][k]); + + return result; + } + + /** \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 gfe function at 'local'. If you provide something wrong here the result will be wrong, too! + */ + DerivativeType evaluateDerivative(const Dune::FieldVector<ctype, gridDim>& local, + const Coefficient& q) const + { + return evaluateDerivative(local); + } + + + + /** \brief Evaluate the discrete gradient operator (This quantity lives in the P2-rotation space) */ + DerivativeType evaluateDiscreteGradient(const Dune::FieldVector<ctype, gridDim>& local) const + { + //Get values of the P2-Basis functions on local point + std::vector<FieldVector<double,1> > basisValues; + rotationLFE_.localBasis().evaluateFunction(local, basisValues); + + DerivativeType discreteGradient(0); + + for (int k=0; k<components_; k++) + for (int l=0; l<gridDim; l++) + for (std::size_t i=0; i<rotationLFE_.size(); i++) + discreteGradient[k][l] += discreteJacobianCoefficients_[i][k][l]*basisValues[i]; + + return discreteGradient; + } + + + /** \brief Evaluate the discrete hessian operator (This quantity lives in the P2-rotation space) */ + discreteHessianType evaluateDiscreteHessian(const Dune::FieldVector<ctype, gridDim>& local) const + { + // Get gradient values of the P2-Basis functions on local point + const auto geometryJacobianInverse = localView_.element().geometry().jacobianInverse(local); + std::vector<FieldMatrix<double,1,gridDim> > gradients; + rotationLFE_.localBasis().evaluateJacobian(local, gradients); + + for (size_t i=0; i< gradients.size(); i++) + gradients[i] = gradients[i] * geometryJacobianInverse; + + discreteHessianType discreteHessian(0); + + for (int k=0; k<components_; k++) + for (int l=0; l<gridDim; l++) + for (std::size_t i=0; i<rotationLFE_.size(); i++) + { + discreteHessian[k][l][0] += discreteJacobianCoefficients_[i][k][l]*gradients[i][0][0]; + discreteHessian[k][l][1] += discreteJacobianCoefficients_[i][k][l]*gradients[i][0][1]; + } + + return discreteHessian; + } + + + //! Obtain the grid view that the basis is defined on + const GridView &gridView() const + { + return basis_.gridView(); + } + + void updateglobalIsometryCoefficients(const Coefficients& newCoefficients) + { + globalIsometryCoefficients_ = newCoefficients; + } + + auto getDiscreteJacobianCoefficients() + { + return discreteJacobianCoefficients_; + } + + /** + Update the local hermite basis coefficient vector + */ + void updateLocalHermiteCoefficients(const Coefficients& localIsometryCoefficients,const typename GridView::template Codim<0>::Entity& element) + { + localViewCoefficient_.bind(element); + + //Create a LocalProjected-Finite element from the local coefficients used for interpolation. + auto P1LagrangeLFE = localViewCoefficient_.tree().finiteElement(); + LocalInterpolationRule localPBfunction(P1LagrangeLFE,localIsometryCoefficients); + + /** + Interpolate into the local hermite space for each component. + */ + const auto &localHermiteFiniteElement = localView_.tree().child(0).finiteElement(); + + for(size_t k=0; k<components_; k++) + { + std::vector<RT> hermiteComponentCoefficients; + Dune::GFE::Impl::LocalIsometryComponentFunction<double,LocalInterpolationRule> localIsometryComponentFunction(localPBfunction,k); + + localHermiteFiniteElement.localInterpolation().interpolate(localIsometryComponentFunction,hermiteComponentCoefficients); + + for(size_t i=0; i<localHermiteFiniteElement.size(); i++) + localHermiteCoefficients_[i][k] = hermiteComponentCoefficients[i]; + } + } + + /** Extract the local isometry coefficients. */ + void getLocalIsometryCoefficients(std::vector<Coefficient>& in,const typename GridView::template Codim<0>::Entity& element) + { + localViewCoefficient_.bind(element); + + in.resize(3); + for (unsigned int vertex = 0; vertex<3; ++vertex) + { + size_t localIdx = localViewCoefficient_.tree().localIndex(vertex); + size_t globalIdx = localViewCoefficient_.index(localIdx); + in[vertex] = globalIsometryCoefficients_[globalIdx]; + } + } + + /** \brief Return the representation LocalFiniteElement for the rotation space */ + auto const &getRotationFiniteElement() const + { + return rotationLFE_; + } + + /** \brief Return the discrete Jacobian coefficients */ + BlockVector<FieldMatrix<RT, components_, gridDim> > getDiscreteJacobianCoefficients() const + { + return discreteJacobianCoefficients_; + } + + /** \brief Get the i'th base coefficient. */ + Coefficients coefficient(int i) const + { + return globalIsometryCoefficients_[i]; + } + + private: + + /** \brief The Lagrange basis used to assign manifold-valued degrees of freedom */ + const CoefficientBasis& coefficientBasis_; + + /** \brief The hermite basis used to represent deformations */ + const DiscreteKirchhoffBasis& basis_; + + /** \brief LocalFiniteElement (P2-Lagrange) used to represent the discrete Jacobian (rotation) on the current element. */ + Dune::LagrangeSimplexLocalFiniteElement<ctype, double, gridDim, 2> rotationLFE_; + + /** \brief The coefficients of the discrete Gradient/Hessian operator */ + BlockVector<FieldMatrix<RT, components_, gridDim> > discreteJacobianCoefficients_; + + mutable typename DiscreteKirchhoffBasis::LocalView localView_; + mutable typename CoefficientBasis::LocalView localViewCoefficient_; + + /** \brief The global coefficient vector */ + Coefficients& globalIsometryCoefficients_; + + static constexpr int localHermiteBasisSize_ = Dune::Functions::Impl::CubicHermiteLocalCoefficients<gridDim,true>::size(); + + /** \brief The local coefficient vector of the hermite basis. */ + std::array<Dune::FieldVector<RT,components_>,localHermiteBasisSize_> localHermiteCoefficients_; + + }; + +} // end namespace Dune::GFE +#endif // DUNE_GFE_FUNCTIONS_DISCRETEKIRCHHOFFBENDINGISOMETRY_HH diff --git a/dune/gfe/functions/localisometrycomponentfunction.hh b/dune/gfe/functions/localisometrycomponentfunction.hh new file mode 100644 index 0000000000000000000000000000000000000000..558bbab6d6cef2d84ddc533767c7ecaeb4eaba9a --- /dev/null +++ b/dune/gfe/functions/localisometrycomponentfunction.hh @@ -0,0 +1,48 @@ +#ifndef DUNE_GFE_FUNCTIONS_LOCALISOMETRYCOMPONENTFUNCTION_HH +#define DUNE_GFE_FUNCTIONS_LOCALISOMETRYCOMPONENTFUNCTION_HH + +#include <dune/gfe/bendingisometryhelper.hh> + +namespace Dune::GFE::Impl +{ + /** + * \brief Local isometry component function that can be used as a Dune::Functions::DifferentiableFunction + * + * \tparam ctype Type used for coordinates on the reference element + * \tparam LocalInterpolation Local function mapping into a product manifold of type RealTuple x Rotations + */ + template<class ctype, class LocalInterpolation> + class LocalIsometryComponentFunction + { + public: + LocalIsometryComponentFunction(LocalInterpolation& localInterpolation, + const int component) + : localInterpolation_(localInterpolation), + component_(component) + {} + + auto operator() (const Dune::FieldVector<ctype,2>& x) const + { + return localInterpolation_.evaluate(x)[Dune::Indices::_0].globalCoordinates()[component_]; + } + + /** + * \brief Obtain derivative function + */ + friend auto derivative(const LocalIsometryComponentFunction& p) + { + return [&p](const Dune::FieldVector<ctype,2>& x) + { + auto derivativeQuaternion = p.localInterpolation_.evaluate(x)[Dune::Indices::_1]; + auto derivativeMatrix = Rotation<ctype,3>::quaternionToMatrix(derivativeQuaternion); + auto reducedDerivativeMatrix = cancelLastMatrixColumn(derivativeMatrix); + return reducedDerivativeMatrix[p.component_]; + }; + } + LocalInterpolation& localInterpolation_; + const int component_; + }; + +} // end namespace Dune::GFE::Impl + +#endif //#ifndef DUNE_GFE_FUNCTIONS_LOCALISOMETRYCOMPONENTFUNCTION_HH diff --git a/problems/L-clamped-Plate.py b/problems/L-clamped-Plate.py new file mode 100644 index 0000000000000000000000000000000000000000..52d9bf820f710f5f0b71d091833ce8d8f6ae5bf9 --- /dev/null +++ b/problems/L-clamped-Plate.py @@ -0,0 +1,96 @@ +import math + +class ParameterSet(dict): + def __init__(self, *args, **kwargs): + super(ParameterSet, self).__init__(*args, **kwargs) + self.__dict__ = self + +parameterSet = ParameterSet() + +############################################# +# Paths +############################################# +parameterSet.resultPath = '/home/klaus/Desktop/Dune-Hermite/dune-gfe/outputs_L-clamped-Plate' +parameterSet.instrumentedPath = '/home/klaus/Desktop/Dune-Hermite/dune-gfe/outputs_L-clamped-Plate/instrumented' +parameterSet.executablePath = '/home/klaus/Desktop/Dune-Hermite/dune-gfe/build-cmake/src' +parameterSet.baseName= 'L-clamped-Plate' + +############################################# +# Grid parameters +############################################# +nX=1 +nY=1 + +parameterSet.structuredGrid = 'simplex' +parameterSet.lower = '0 0' +parameterSet.upper = '4 4' +parameterSet.elements = str(nX)+' '+ str(nY) + +parameterSet.macroGridLevel = 3 +############################################# +# Solver parameters +############################################# +# Choose solver: "RNHM" (Default:Riemannian Newton with Hessian modification), "RiemannianTR" (Riemannain Trust-region method) +parameterSet.Solver = "RNHM" +# Tolerance of the multigrid solver +parameterSet.tolerance = 1e-12 +# Maximum number of multigrid iterations +parameterSet.maxProximalNewtonSteps = 100 +# Initial regularization +parameterSet.initialRegularization = 1 +# Measure convergence +parameterSet.instrumented = 0 + +############################ +# Problem specifications +############################ +# Dimension of the domain (only used for numerical-test python files) +parameterSet.dim = 2 + +# Dimension of the target space +parameterSet.targetDim = 3 + +parameterSet.targetSpace = 'BendingIsometry' +############################################# +# Options +############################################# +# Write discrete solution as .vtk-File +parameterSet.writeVTK = 1 + +# Write Dof-Vector to .txt-file +parameterSet.writeDOFvector = 0 + +############################################# +# Dirichlet boundary indicator +############################################# +def dirichlet_indicator(x) : + if( (x[0] <= 0.001) or (x[1]<=0.001)): + return True + else: + return False + + +############################################# +# Initial iterate function +############################################# +def f(x): + return [x[0], x[1], 0] + + +def df(x): + return ((1,0), + (0,1), + (0,0)) + + +fdf = (f, df) + +############################################# +# Force +############################################ +def force(x): + return [0, 0, 0.025] + + + + diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 658ef54c6c2c21aa88a712195bef103147ea1fa9..68deff572ca4f35264eba4b8d19b385b7c9c945b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -44,6 +44,11 @@ add_executable("cosserat-continuum-3d-in-3d-2-1" cosserat-continuum.cc) set_property(TARGET "cosserat-continuum-3d-in-3d-2-1" APPEND PROPERTY COMPILE_DEFINITIONS "GRID_DIM=3; WORLD_DIM=3; LFE_ORDER=2; GFE_ORDER=1") set(programs cosserat-continuum-3d-in-3d-2-1 ${programs}) +if(dune-functions_VERSION VERSION_GREATER_EQUAL 2.11.0) + add_executable("bending-plate" bending-plate.cc) + set(programs bending-plate ${programs}) +endif() + foreach(_program ${programs}) target_link_dune_default_libraries(${_program}) add_dune_pythonlibs_flags(${_program}) diff --git a/src/bending-plate.cc b/src/bending-plate.cc new file mode 100644 index 0000000000000000000000000000000000000000..6edfea3ada4f781068ac87e8da0248b4907b83da --- /dev/null +++ b/src/bending-plate.cc @@ -0,0 +1,337 @@ +#include <config.h> +#include <memory> +#include <array> +#include <math.h> + +// Includes for the ADOL-C automatic differentiation library +// Need to come before (almost) all others. +#include <adolc/adouble.h> +#include <dune/fufem/utilities/adolcnamespaceinjections.hh> + +#include <dune/common/typetraits.hh> +#include <dune/common/bitsetvector.hh> +#include <dune/common/parametertree.hh> +#include <dune/common/parametertreeparser.hh> + +#include <dune/grid/uggrid.hh> +#include <dune/grid/utility/structuredgridfactory.hh> + +#include <dune/functions/gridfunctions/discreteglobalbasisfunction.hh> +#include <dune/functions/gridfunctions/composedgridfunction.hh> +#include <dune/functions/functionspacebases/cubichermitebasis.hh> +#include <dune/functions/functionspacebases/lagrangebasis.hh> +#include <dune/functions/functionspacebases/powerbasis.hh> +#include <dune/functions/functionspacebases/interpolate.hh> + +#include <dune/fufem/boundarypatch.hh> +#include <dune/fufem/functiontools/boundarydofs.hh> +#include <dune/fufem/dunepython.hh> +#include <dune/fufem/discretizationerror.hh> + +#include <dune/solvers/solvers/iterativesolver.hh> +#include <dune/solvers/norms/energynorm.hh> + +#include <dune/gfe/spaces/productmanifold.hh> +#include <dune/gfe/spaces/realtuple.hh> +#include <dune/gfe/spaces/rotation.hh> +#include <dune/gfe/functions/discretekirchhoffbendingisometry.hh> +#include <dune/gfe/functions/embeddedglobalgfefunction.hh> +#include <dune/gfe/functions/localprojectedfefunction.hh> +#include <dune/gfe/assemblers/localgeodesicfeadolcstiffness.hh> +#include <dune/gfe/assemblers/geodesicfeassembler.hh> +#include <dune/gfe/assemblers/discretekirchhoffbendingenergy.hh> +#include <dune/gfe/assemblers/forceenergy.hh> +#include <dune/gfe/assemblers/sumenergy.hh> +#include <dune/gfe/bendingisometryhelper.hh> +#include <dune/gfe/riemannianpnsolver.hh> +#include <dune/gfe/riemanniantrsolver.hh> + +#include <dune/gmsh4/gmsh4reader.hh> +#include <dune/gmsh4/gridcreators/lagrangegridcreator.hh> + +#include <dune/vtk/vtkwriter.hh> +#include <dune/vtk/writers/unstructuredgridwriter.hh> +#include <dune/vtk/datacollectors/continuousdatacollector.hh> +#include <dune/vtk/datacollectors/discontinuousdatacollector.hh> +#include <dune/vtk/datacollectors/quadraticdatacollector.hh> +#include <dune/vtk/datacollectors/lagrangedatacollector.hh> + +const int dim = 2; + +using namespace Dune; + +/** + Source file for the computation of bending isometries. + + Usage: + ./bending-isometries <python path> <python module without extension> + */ + +int main(int argc, char *argv[]) +{ + MPIHelper::instance(argc, argv); + Dune::Timer globalTimer; + + if (argc < 3) + DUNE_THROW(Exception, "Usage: ./bending-isometries <python path> <python module without extension>"); + + // Start Python interpreter + Python::start(); + auto pyMain = Python::main(); + pyMain.runStream() + << std::endl << "import math" + << std::endl << "import sys" + << std::endl << "sys.path.append('" << argv[1] << "')" << std::endl; + auto pyModule = pyMain.import(argv[2]); + + std::cout << "Current path is " << std::filesystem::current_path() << '\n'; + std::filesystem::path file_path = (__FILE__); + std::cout<< "File path: " << file_path<<std::endl; + std::cout << "dir_path: " << file_path.parent_path() << std::endl; + std::string dir_path = file_path.parent_path(); + + // parse data file + ParameterTree parameterSet; + pyModule.get("parameterSet").toC(parameterSet); + + // read possible further parameters from the command line + ParameterTreeParser::readOptions(argc, argv, parameterSet); + + // Print all parameters, to make them appear in the log file + std::cout << "Executable: bending-isometries, with parameters:" << std::endl; + parameterSet.report(); + + bool PRINT_DEBUG = parameterSet.get<bool>("print_debug", 0); + + ///////////////////////////////////////// + // Create the grid + ///////////////////////////////////////// + using GridType = UGGrid<dim>; + + std::shared_ptr<GridType> grid; + FieldVector<double,dim> lower(0), upper(1); + std::array<unsigned int,dim> elementsArray; + + std::string structuredGridType = parameterSet["structuredGrid"]; + if (structuredGridType != "false" ) + { + lower = parameterSet.get<FieldVector<double,dim> >("lower"); + upper = parameterSet.get<FieldVector<double,dim> >("upper"); + elementsArray = parameterSet.get<std::array<unsigned int,dim> >("elements"); + + if (structuredGridType == "simplex") + grid = StructuredGridFactory<GridType>::createSimplexGrid(lower, upper, elementsArray); + else if (structuredGridType == "cube") + grid = StructuredGridFactory<GridType>::createCubeGrid(lower, upper, elementsArray); + else + DUNE_THROW(Exception, "Unknown structured grid type '" << structuredGridType << "' found!"); + } else { + std::cout << "Read GMSH grid." << std::endl; + std::string gridPath = parameterSet.get<std::string>("gridPath"); + std::string gridFile = parameterSet.get<std::string>("gridFile"); + GridFactory<GridType> factory; + Gmsh4::LagrangeGridCreator creator{factory}; + Gmsh4Reader reader{creator}; + reader.read(gridPath + "/" + gridFile); + grid = factory.createGrid(); + } + + const int macroGridLevel = parameterSet.get<int>("macroGridLevel"); + grid->globalRefine(macroGridLevel-1); + + using GridView = typename GridType::LeafGridView; + GridView gridView = grid->leafGridView(); + + /////////////////////////////////////////////////////// + // Set up the function spaces + /////////////////////////////////////////////////////// + // General coefficient vector of a Discrete Kirchhoff deformation function + using VectorSpaceCoefficients = BlockVector<FieldVector<double,3> >; + + // Coefficient vector of a Discrete Kirchhoff deformation function that is constrained to be an isometry. + // we need both 'double' and 'adouble' versions. + using Coefficient = GFE::ProductManifold<GFE::RealTuple<double,3>, GFE::Rotation<double,3> >; + using IsometryCoefficients = std::vector<Coefficient>; + using ACoefficient = typename Coefficient::template rebind<adouble>::other; + using AIsometryCoefficients = std::vector<ACoefficient>; + + using namespace Functions::BasisFactory; + auto deformationBasis = makeBasis(gridView, + power<3>(reducedCubicHermite(), + blockedInterleaved())); + using DeformationBasis = decltype(deformationBasis); + + + // The next basis is used to assign (nonlinear) degrees of freedom to the grid vertices. + // The actual basis function values are never used. + using CoefficientBasis = Functions::LagrangeBasis<GridView, 1>; + CoefficientBasis coefficientBasis(gridView); + + // A basis for the tangent space (used to set DirichletNodes) + auto tangentBasis = makeBasis(gridView, + power<Coefficient::TangentVector::dimension>( + lagrange<1>(), + blockedInterleaved())); + + + // Print some information on the grid and degrees of freedom. + std::cout << "Coefficient::TangentVector::dimension: " << Coefficient::TangentVector::dimension<< std::endl; + std::cout << "Number of Elements in the grid: " << gridView.size(0)<< std::endl; + std::cout << "Number of Nodes in the grid: " << gridView.size(dim)<< std::endl; + std::cout << "deformationBasis.size(): " << deformationBasis.size() << std::endl; + std::cout << "deformationBasis.dimension(): " << deformationBasis.dimension() << std::endl; + std::cout << "Degrees of Freedom: " << deformationBasis.dimension() << std::endl; + + /////////////////////////////////////////// + // Read Dirichlet values + /////////////////////////////////////////// + BitSetVector<1> dirichletVertices(gridView.size(dim), false); + const typename GridView::IndexSet &indexSet = gridView.indexSet(); + BitSetVector<Coefficient::TangentVector::dimension> dirichletNodes(tangentBasis.size(), false); //tangentBasis.size()=coefficientBasis.size() + + // Make Python function that computes which vertices are on the Dirichlet boundary, + // based on the vertex positions. + auto dirichletIndicatorFunction = Python::make_function<bool>(pyModule.get("dirichlet_indicator")); + + // If we want to clamp DOFs inside the domain, we cannot use 'BoundaryPatch' + // and 'constructBoundaryDofs'. This is a workaround for now. + for (auto &&vertex : vertices(gridView)) + { + dirichletVertices[indexSet.index(vertex)] = dirichletIndicatorFunction(vertex.geometry().corner(0)); + + if(dirichletIndicatorFunction(vertex.geometry().corner(0))) + { + dirichletNodes[indexSet.index(vertex)] = true; + if(PRINT_DEBUG) + std::cout << "Dirichlet Vertex with coordinates:" << vertex.geometry().corner(0) << std::endl; + } + } + + /////////////////////////////////////////// + // Get initial Iterate + /////////////////////////////////////////// + auto pythonInitialIterate = Python::makeDifferentiableFunction<FieldVector<double,3>(FieldVector<double,2>)>(pyModule.get("f"), pyModule.get("df")); + VectorSpaceCoefficients x(deformationBasis.size()); + interpolate(deformationBasis, x, pythonInitialIterate); + + + + // We need to setup DiscreteKirchhoffBendingIsometry with a coefficient + // vector of ctype 'adouble' while the solver gets a coefficient vector + // of ctype 'double'. + IsometryCoefficients isometryCoefficients(coefficientBasis.size()); + AIsometryCoefficients isometryCoefficients_adouble(coefficientBasis.size()); + + using namespace Dune::GFE::Impl; + // Copy the current iterate into a data type that encapsulates the isometry constraint + // i.e. convert coefficient data structure from 'VectorSpaceCoefficients' to 'IsometryCoefficients' + vectorToIsometryCoefficientMap(deformationBasis,coefficientBasis,x,isometryCoefficients); + vectorToIsometryCoefficientMap(deformationBasis,coefficientBasis,x,isometryCoefficients_adouble); + + // Create a DiscreteKirchhoffBendingIsometry. + // This serves as the deformation function. + using LocalDKFunction = GFE::DiscreteKirchhoffBendingIsometry<DeformationBasis, CoefficientBasis, AIsometryCoefficients>; + LocalDKFunction localDKFunction(deformationBasis, coefficientBasis, isometryCoefficients_adouble); + + // Read the force term. + auto pythonForce = Python::make_function<FieldVector<double,3> >(pyModule.get("force")); + auto forceGVF = Dune::Functions::makeGridViewFunction(pythonForce, gridView); + auto localForce = localFunction(forceGVF); + + //////////////////////////////////////////////////////////////////////// + // Create an assembler for the Discrete Kirchhoff Energy Functional (using ADOL-C) + //////////////////////////////////////////////////////////////////////// + + // Setup nonconforming energy and assembler - only option for this minimal example. + auto forceEnergy = std::make_shared<GFE::ForceEnergy<CoefficientBasis, LocalDKFunction, decltype(localForce), ACoefficient> >(localDKFunction, localForce); + auto localEnergy_nonconforming = std::make_shared<GFE::DiscreteKirchhoffBendingEnergy<CoefficientBasis, LocalDKFunction, decltype(localForce), ACoefficient> >(localDKFunction); + + auto sumEnergy = std::make_shared<GFE::SumEnergy<CoefficientBasis, GFE::RealTuple<adouble,3>, GFE::Rotation<adouble,3> > >(); + sumEnergy->addLocalEnergy(localEnergy_nonconforming); + sumEnergy->addLocalEnergy(forceEnergy); + + auto localGFEADOLCStiffness_nonconforming= std::make_shared<Dune::GFE::LocalGeodesicFEADOLCStiffness<CoefficientBasis, Coefficient> >(sumEnergy); + std::shared_ptr<Dune::GFE::GeodesicFEAssembler<CoefficientBasis, Coefficient> > assembler_nonconforming; + assembler_nonconforming = std::make_shared<Dune::GFE::GeodesicFEAssembler<CoefficientBasis, Coefficient> >(coefficientBasis, localGFEADOLCStiffness_nonconforming); + + // Create a solver: + // * Riemannian Newton with Hessian modification + // * Riemannian Trust-region + Dune::GFE::RiemannianProximalNewtonSolver<CoefficientBasis, Coefficient> RNHMsolver; + Dune::GFE::RiemannianTrustRegionSolver<CoefficientBasis, Coefficient> RTRsolver; + + std::string Solver_name = parameterSet.get<std::string>("Solver", "RNHM"); + double numerical_energy; //final discrete energy + + if(Solver_name == "RNHM") + { + RNHMsolver.setup(*grid, + &(*assembler_nonconforming), + isometryCoefficients, + dirichletNodes, + parameterSet); + + RNHMsolver.solve(); + isometryCoefficients = RNHMsolver.getSol(); + numerical_energy = RNHMsolver.getStatistics().finalEnergy; + } else if (Solver_name =="RiemannianTR") + { + std::cout << "Using Riemannian Trust-region method for energy minimization." << std::endl; + RTRsolver.setup(*grid, + &(*assembler_nonconforming), + isometryCoefficients, + dirichletNodes, + parameterSet); + + RTRsolver.solve(); + isometryCoefficients = RTRsolver.getSol(); + numerical_energy = RTRsolver.getStatistics().finalEnergy; + } else + DUNE_THROW(Dune::Exception, "Unknown Solver type for bending isometries."); + + + /** Convert coefficient data structure from 'IsometryCoefficients' back to 'VectorSpaceCoefficients' */ + VectorSpaceCoefficients x_out(deformationBasis.size()); + isometryToVectorCoefficientMap(deformationBasis,coefficientBasis,x_out,isometryCoefficients); + + //////////////////////////////// + // Output result + //////////////////////////////// + std::string baseNameDefault = "bending-isometries-"; + std::string baseName = parameterSet.get("baseName", baseNameDefault); + std::string resultFileName = parameterSet.get("resultPath", "") + + "/" + baseName + + "-level" + std::to_string(parameterSet.get<int>("macroGridLevel")); + + + if (parameterSet.get<bool>("writeVTK", 1)) + { + std::cout << "write VTK to Filename: " << resultFileName << std::endl; + + // Compute the displacement from the deformation. + // interpolate the identity and subtract from the coefficient vector. + VectorSpaceCoefficients identity(deformationBasis.size()); + IdentityGridEmbedding<double> identityGridEmbedding; + interpolate(deformationBasis, identity, identityGridEmbedding); + + // Compute the displacement + auto displacement = x_out; + displacement -= identity; + + auto deformationFunction = Functions::makeDiscreteGlobalBasisFunction<FieldVector<double,3> >(deformationBasis, x_out); + auto displacementFunction = Functions::makeDiscreteGlobalBasisFunction<FieldVector<double,3> >(deformationBasis, displacement); + + // Use VTK writer from dune-vtk. + // Setup a DataCollector of order 3. + Dune::Vtk::LagrangeDataCollector<GridView, 3> lagrangeDataCollector(gridView); + Dune::Vtk::UnstructuredGridWriter duneVTKwriter(lagrangeDataCollector, Dune::Vtk::FormatTypes::ASCII, Vtk::DataTypes::FLOAT32); + + // Write discrete displacement + duneVTKwriter.addPointData(displacementFunction, Dune::Vtk::FieldInfo{"Displacement",3, Vtk::RangeTypes::VECTOR}); + + duneVTKwriter.write(resultFileName); + } + + std::cout << "Total time elapsed: " << globalTimer.elapsed() << std::endl; + return 0; +}